<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[2244] 2013/codebykat/post-by-email/trunk: added autoloading for Horde dependencies</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://gsoc.trac.wordpress.org/changeset/2244">2244</a></dd>
<dt>Author</dt> <dd>codebykat</dd>
<dt>Date</dt> <dd>2013-08-28 06:43:06 +0000 (Wed, 28 Aug 2013)</dd>
</dl>

<h3>Log Message</h3>
<pre>added autoloading for Horde dependencies</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#2013codebykatpostbyemailtrunkclasspostbyemailadminphp">2013/codebykat/post-by-email/trunk/class-post-by-email-admin.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkclasspostbyemailphp">2013/codebykat/post-by-email/trunk/class-post-by-email.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkpostbyemailphp">2013/codebykat/post-by-email/trunk/post-by-email.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkreadmetxt">2013/codebykat/post-by-email/trunk/readme.txt</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li>2013/codebykat/post-by-email/trunk/include/Horde/Exception/</li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeExceptionWrappedphp">2013/codebykat/post-by-email/trunk/include/Horde/Exception/Wrapped.php</a></li>
<li>2013/codebykat/post-by-email/trunk/include/Horde/Imap/</li>
<li>2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/</li>
<li>2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Auth/</li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientAuthDigestMD5php">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Auth/DigestMD5.php</a></li>
<li>2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Base/</li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientBaseConnectionphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Base/Connection.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientBaseDebugphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Base/Debug.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientBaseDeprecatedphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Base/Deprecated.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientBaseMailboxphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Base/Mailbox.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientBasephp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Base.php</a></li>
<li>2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Cache/</li>
<li>2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Cache/Backend/</li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientCacheBackendCachephp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Cache/Backend/Cache.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientCacheBackendDbphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Cache/Backend/Db.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientCacheBackendMongophp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Cache/Backend/Mongo.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientCacheBackendNullphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Cache/Backend/Null.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientCacheBackendphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Cache/Backend.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientCachephp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Cache.php</a></li>
<li>2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/</li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientDataAclphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Acl.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientDataAclCommonphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/AclCommon.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientDataAclNegativephp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/AclNegative.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientDataAclRightsphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/AclRights.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientDataBaseSubjectphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/BaseSubject.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientDataEnvelopephp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Envelope.php</a></li>
<li>2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Fetch/</li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientDataFetchPop3php">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Fetch/Pop3.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientDataFetchphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Fetch.php</a></li>
<li>2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/</li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientDataFormatAstringphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Astring.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientDataFormatAtomphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Atom.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientDataFormatDatephp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Date.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientDataFormatDateTimephp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/DateTime.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientDataFormatExceptionphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Exception.php</a></li>
<li>2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Filter/</li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientDataFormatFilterQuotephp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Filter/Quote.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientDataFormatFilterStringphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Filter/String.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientDataFormatListphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/List.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientDataFormatListMailboxphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/ListMailbox.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientDataFormatMailboxphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Mailbox.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientDataFormatNilphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Nil.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientDataFormatNstringphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Nstring.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientDataFormatNumberphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Number.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientDataFormatStringphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/String.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientDataFormatphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientDataSyncphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Sync.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientDataThreadphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Thread.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientDateTimephp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/DateTime.php</a></li>
<li>2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Exception/</li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientExceptionNoSupportExtensionphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Exception/NoSupportExtension.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientExceptionNoSupportPop3php">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Exception/NoSupportPop3.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientExceptionSearchCharsetphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Exception/SearchCharset.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientExceptionServerResponsephp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Exception/ServerResponse.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientExceptionSyncphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Exception/Sync.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientExceptionphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Exception.php</a></li>
<li>2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Fetch/</li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientFetchQueryphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Fetch/Query.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientFetchResultsphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Fetch/Results.php</a></li>
<li>2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Ids/</li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientIdsMapphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Ids/Map.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientIdsPop3php">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Ids/Pop3.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientIdsphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Ids.php</a></li>
<li>2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Interaction/</li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientInteractionClientphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Interaction/Client.php</a></li>
<li>2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Interaction/Command/</li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientInteractionCommandContinuationphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Interaction/Command/Continuation.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientInteractionCommandphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Interaction/Command.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientInteractionPipelinephp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Interaction/Pipeline.php</a></li>
<li>2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Interaction/Server/</li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientInteractionServerContinuationphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Interaction/Server/Continuation.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientInteractionServerTaggedphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Interaction/Server/Tagged.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientInteractionServerUntaggedphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Interaction/Server/Untagged.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientInteractionServerphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Interaction/Server.php</a></li>
<li>2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Mailbox/</li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientMailboxListphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Mailbox/List.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientMailboxphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Mailbox.php</a></li>
<li>2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Search/</li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientSearchQueryphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Search/Query.php</a></li>
<li>2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Socket/</li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientSocketCatenatephp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Socket/Catenate.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientSocketClientSortphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Socket/ClientSort.php</a></li>
<li>2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Socket/Connection/</li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientSocketConnectionPop3php">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Socket/Connection/Pop3.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientSocketConnectionSocketphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Socket/Connection/Socket.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientSocketConnectionphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Socket/Connection.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientSocketPop3php">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Socket/Pop3.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientSocketphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Socket.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientTokenizephp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Tokenize.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientTranslationphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Translation.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientUrlphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Url.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientUtf7imapphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Utf7imap.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeImapClientphp">2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client.php</a></li>
<li>2013/codebykat/post-by-email/trunk/include/Horde/Mail/</li>
<li>2013/codebykat/post-by-email/trunk/include/Horde/Mail/Rfc822/</li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeMailRfc822Addressphp">2013/codebykat/post-by-email/trunk/include/Horde/Mail/Rfc822/Address.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeMailRfc822Listphp">2013/codebykat/post-by-email/trunk/include/Horde/Mail/Rfc822/List.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeMailRfc822Objectphp">2013/codebykat/post-by-email/trunk/include/Horde/Mail/Rfc822/Object.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeMailRfc822php">2013/codebykat/post-by-email/trunk/include/Horde/Mail/Rfc822.php</a></li>
<li>2013/codebykat/post-by-email/trunk/include/Horde/Mime/</li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeMimeHeadersphp">2013/codebykat/post-by-email/trunk/include/Horde/Mime/Headers.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeMimePartphp">2013/codebykat/post-by-email/trunk/include/Horde/Mime/Part.php</a></li>
<li>2013/codebykat/post-by-email/trunk/include/Horde/Stream/</li>
<li>2013/codebykat/post-by-email/trunk/include/Horde/Stream/Filter/</li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeStreamFilterEolphp">2013/codebykat/post-by-email/trunk/include/Horde/Stream/Filter/Eol.php</a></li>
<li>2013/codebykat/post-by-email/trunk/include/Horde/Support/</li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeSupportCaseInsensitiveArrayphp">2013/codebykat/post-by-email/trunk/include/Horde/Support/CaseInsensitiveArray.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeSupportRandomidphp">2013/codebykat/post-by-email/trunk/include/Horde/Support/Randomid.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeSupportStubphp">2013/codebykat/post-by-email/trunk/include/Horde/Support/Stub.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeUtilphp">2013/codebykat/post-by-email/trunk/include/Horde/Util.php</a></li>
</ul>

<h3>Removed Paths</h3>
<ul>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientAuthDigestMD5php">2013/codebykat/post-by-email/trunk/include/Horde/Client/Auth/DigestMD5.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientBaseConnectionphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Base/Connection.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientBaseDebugphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Base/Debug.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientBaseDeprecatedphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Base/Deprecated.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientBaseMailboxphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Base/Mailbox.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientBasephp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Base.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientCacheBackendCachephp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Cache/Backend/Cache.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientCacheBackendDbphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Cache/Backend/Db.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientCacheBackendMongophp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Cache/Backend/Mongo.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientCacheBackendNullphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Cache/Backend/Null.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientCacheBackendphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Cache/Backend.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientCachephp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Cache.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientDataAclphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Acl.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientDataAclCommonphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/AclCommon.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientDataAclNegativephp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/AclNegative.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientDataAclRightsphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/AclRights.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientDataBaseSubjectphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/BaseSubject.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientDataEnvelopephp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Envelope.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientDataFetchPop3php">2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Fetch/Pop3.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientDataFetchphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Fetch.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientDataFormatAstringphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Astring.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientDataFormatAtomphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Atom.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientDataFormatDatephp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Date.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientDataFormatDateTimephp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/DateTime.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientDataFormatExceptionphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Exception.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientDataFormatFilterQuotephp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Filter/Quote.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientDataFormatFilterStringphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Filter/String.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientDataFormatListphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/List.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientDataFormatListMailboxphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/ListMailbox.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientDataFormatMailboxphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Mailbox.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientDataFormatNilphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Nil.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientDataFormatNstringphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Nstring.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientDataFormatNumberphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Number.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientDataFormatStringphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/String.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientDataFormatphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientDataSyncphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Sync.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientDataThreadphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Thread.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientDateTimephp">2013/codebykat/post-by-email/trunk/include/Horde/Client/DateTime.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientExceptionNoSupportExtensionphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Exception/NoSupportExtension.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientExceptionNoSupportPop3php">2013/codebykat/post-by-email/trunk/include/Horde/Client/Exception/NoSupportPop3.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientExceptionSearchCharsetphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Exception/SearchCharset.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientExceptionServerResponsephp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Exception/ServerResponse.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientExceptionSyncphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Exception/Sync.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientExceptionphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Exception.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientFetchQueryphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Fetch/Query.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientFetchResultsphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Fetch/Results.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientIdsMapphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Ids/Map.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientIdsPop3php">2013/codebykat/post-by-email/trunk/include/Horde/Client/Ids/Pop3.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientIdsphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Ids.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientInteractionClientphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Client.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientInteractionCommandContinuationphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Command/Continuation.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientInteractionCommandphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Command.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientInteractionPipelinephp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Pipeline.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientInteractionServerContinuationphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Server/Continuation.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientInteractionServerTaggedphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Server/Tagged.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientInteractionServerUntaggedphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Server/Untagged.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientInteractionServerphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Server.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientMailboxListphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Mailbox/List.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientMailboxphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Mailbox.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientSearchQueryphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Search/Query.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientSocketCatenatephp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Socket/Catenate.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientSocketClientSortphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Socket/ClientSort.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientSocketConnectionPop3php">2013/codebykat/post-by-email/trunk/include/Horde/Client/Socket/Connection/Pop3.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientSocketConnectionSocketphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Socket/Connection/Socket.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientSocketConnectionphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Socket/Connection.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientSocketPop3php">2013/codebykat/post-by-email/trunk/include/Horde/Client/Socket/Pop3.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientSocketphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Socket.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientTokenizephp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Tokenize.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientUrlphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Url.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientUtf7imapphp">2013/codebykat/post-by-email/trunk/include/Horde/Client/Utf7imap.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeClientphp">2013/codebykat/post-by-email/trunk/include/Horde/Client.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeExceptionWrappedphp">2013/codebykat/post-by-email/trunk/include/Horde/Exception-Wrapped.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeHordeUtilphp">2013/codebykat/post-by-email/trunk/include/Horde/Horde-Util.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeMailRfc822Addressphp">2013/codebykat/post-by-email/trunk/include/Horde/Mail-Rfc822-Address.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeMailRfc822Listphp">2013/codebykat/post-by-email/trunk/include/Horde/Mail-Rfc822-List.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeMailRfc822Objectphp">2013/codebykat/post-by-email/trunk/include/Horde/Mail-Rfc822-Object.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeMailRfc822php">2013/codebykat/post-by-email/trunk/include/Horde/Mail-Rfc822.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeMimeHeadersphp">2013/codebykat/post-by-email/trunk/include/Horde/Mime-Headers.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeMimePartphp">2013/codebykat/post-by-email/trunk/include/Horde/Mime-Part.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeStreamFilterEolphp">2013/codebykat/post-by-email/trunk/include/Horde/Stream-Filter-Eol.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeStubphp">2013/codebykat/post-by-email/trunk/include/Horde/Stub.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeSupportCaseInsensitiveArrayphp">2013/codebykat/post-by-email/trunk/include/Horde/Support-CaseInsensitiveArray.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeHordeSupportRandomidphp">2013/codebykat/post-by-email/trunk/include/Horde/Support-Randomid.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludeclasshordeimapclienttranslationphp">2013/codebykat/post-by-email/trunk/include/class-horde-imap-client-translation.php</a></li>
<li><a href="#2013codebykatpostbyemailtrunkincludehordewrapperphp">2013/codebykat/post-by-email/trunk/include/horde-wrapper.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="2013codebykatpostbyemailtrunkclasspostbyemailadminphp"></a>
<div class="modfile"><h4>Modified: 2013/codebykat/post-by-email/trunk/class-post-by-email-admin.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/class-post-by-email-admin.php 2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/class-post-by-email-admin.php    2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -8,17 +8,14 @@
</span><span class="cx"> class Post_By_Email_Admin {
</span><span class="cx">  protected static $instance = null;
</span><span class="cx"> 
</span><del>-       private function __construct() {
-               // Add the options page and menu item.
-               add_action( 'admin_init', array( $this, 'add_plugin_settings' ) );
-               add_action( 'admin_menu', array( $this, 'add_plugin_admin_menu' ) );
-
-               // disable post by email settings on Settings->Writing page
-               add_filter( 'enable_post_by_email_configuration', '__return_false' );
-       }
-
</del><ins>+        /**
+        * Instance of this class.
+        *
+        * @since    0.9.6
+        *
+        * @var      object
+        */
</ins><span class="cx">   public static function get_instance() {
</span><del>-
</del><span class="cx">           // If the single instance hasn't been set, set it now.
</span><span class="cx">          if ( null == self::$instance ) {
</span><span class="cx">                  self::$instance = new self;
</span><span class="lines">@@ -28,6 +25,20 @@
</span><span class="cx">  }
</span><span class="cx"> 
</span><span class="cx">  /**
</span><ins>+        * Hook up our functions to the admin menus.
+        *
+        * @since     0.9.6
+        */
+       private function __construct() {
+               // Add the options page and menu item.
+               add_action( 'admin_init', array( $this, 'add_plugin_settings' ) );
+               add_action( 'admin_menu', array( $this, 'add_plugin_admin_menu' ) );
+
+               // disable post by email settings on Settings->Writing page
+               add_filter( 'enable_post_by_email_configuration', '__return_false' );
+       }
+
+       /**
</ins><span class="cx">    * Register the settings.
</span><span class="cx">   *
</span><span class="cx">   * @since    0.9.0
</span><span class="lines">@@ -36,6 +47,13 @@
</span><span class="cx">          register_setting( 'post_by_email_options', 'post_by_email_options', array( $this, 'post_by_email_validate' ) );
</span><span class="cx">  }
</span><span class="cx"> 
</span><ins>+       /**
+        * Validate saved options.
+        *
+        * @since    0.9.5
+        *
+        * @param   array    $input    Form fields submitted from the settings page.
+        */
</ins><span class="cx">   public function post_by_email_validate($input) {
</span><span class="cx">          // load all the options so we don't wipe out the log
</span><span class="cx">          $options = get_option( 'post_by_email_options' );
</span></span></pre></div>
<a id="2013codebykatpostbyemailtrunkclasspostbyemailphp"></a>
<div class="modfile"><h4>Modified: 2013/codebykat/post-by-email/trunk/class-post-by-email.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/class-post-by-email.php       2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/class-post-by-email.php  2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -24,7 +24,7 @@
</span><span class="cx">   *
</span><span class="cx">   * @var     string
</span><span class="cx">   */
</span><del>-       protected $version = '0.9.6';
</del><ins>+        protected $version = '0.9.7';
</ins><span class="cx"> 
</span><span class="cx">  /**
</span><span class="cx">   * Unique identifier for your plugin.
</span><span class="lines">@@ -48,6 +48,15 @@
</span><span class="cx">  protected static $instance = null;
</span><span class="cx"> 
</span><span class="cx">  /**
</span><ins>+        * Plugin include path (used for autoloading libraries).
+        *
+        * @since    0.9.7
+        *
+        * @var      object
+        */
+       public static $path;
+
+       /**
</ins><span class="cx">    * Initialize the plugin by setting localization, filters, and administration functions.
</span><span class="cx">   *
</span><span class="cx">   * @since     0.9.0
</span><span class="lines">@@ -56,6 +65,9 @@
</span><span class="cx">          // Load plugin text domain
</span><span class="cx">          add_action( 'plugins_loaded', array( $this, 'load_plugin_textdomain' ) );
</span><span class="cx"> 
</span><ins>+               // Enable autoloading
+               add_action( 'plugins_loaded', array( $this, 'load' ) );
+
</ins><span class="cx">           // add hook to check for mail
</span><span class="cx">          add_action( 'wp-mail.php', array( 'Post_By_Email', 'check_email' ) );
</span><span class="cx">  }
</span><span class="lines">@@ -134,7 +146,7 @@
</span><span class="cx">  public function check_email() {
</span><span class="cx"> 
</span><span class="cx">          /** include the Horde IMAP client class */
</span><del>-               require_once( plugin_dir_path( __FILE__ ) . 'include/horde-wrapper.php' );
</del><ins>+                // require_once( plugin_dir_path( __FILE__ ) . 'include/horde-wrapper.php' );
</ins><span class="cx"> 
</span><span class="cx">          /** Only check at this interval for new messages. */
</span><span class="cx">          if ( ! defined( 'WP_MAIL_INTERVAL' ) )
</span><span class="lines">@@ -142,7 +154,7 @@
</span><span class="cx"> 
</span><span class="cx">          $last_checked = get_transient( 'mailserver_last_checked' );
</span><span class="cx"> 
</span><del>-               if ( $last_checked )
</del><ins>+                if ( $last_checked && ! WP_DEBUG )
</ins><span class="cx">                   wp_die( __( 'Slow down cowboy, no need to check for new mails so often!', 'post-by-email' ) );
</span><span class="cx"> 
</span><span class="cx">          set_transient( 'mailserver_last_checked', true, WP_MAIL_INTERVAL );
</span><span class="lines">@@ -358,6 +370,14 @@
</span><span class="cx">          update_option( 'post_by_email_options', $options );
</span><span class="cx">  }
</span><span class="cx"> 
</span><ins>+       /**
+        * Save a message to the log file and wp_die()
+        *
+        * @since    0.9.0
+        *
+        * @param    string    $error    Error message to save to the log.
+        * @param    array     $log      The log file to save back to the plugin options.
+        */
</ins><span class="cx">   protected function save_log_and_die( $error, $log ) {
</span><span class="cx">          $log['messages'][] = $error;
</span><span class="cx"> 
</span><span class="lines">@@ -367,4 +387,36 @@
</span><span class="cx"> 
</span><span class="cx">          wp_die( $error );
</span><span class="cx">  }
</span><ins>+
+       /**
+        * Set the plugin include path and register the autoloader.
+        *
+        * @since 0.9.7
+        */
+       public static function load() {
+               self::$path = __DIR__;
+               spl_autoload_register( array( get_called_class(), 'autoload' ) );
+       }
+
+       /**
+        * Autoload class libraries as needed.
+        *
+        * @since 0.9.7
+        *
+        * @param    string    $class    Class name of requested object.
+        */
+       public static function autoload( $class ) {
+               // We're only interested in autoloading Horde includes.
+               if( 0 !== strpos( $class, 'Horde' ) ) {
+                       return;
+               }
+
+               // Replace all underscores in the class name with slashes.
+               // ex: we expect the class Horde_Imap_Client to be defined in Horde/Imap/Client.php.
+               $filename = str_replace( array( '_', '\\' ), '/', $class );
+               $filename = self::$path . '/include/' . $filename . '.php';
+               if( file_exists( $filename ) ) {
+                       require_once( $filename );
+               }
+       }
</ins><span class="cx"> }
</span><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientAuthDigestMD5php"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Auth/DigestMD5.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Auth/DigestMD5.php       2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Auth/DigestMD5.php  2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,186 +0,0 @@
</span><del>-<?php
-/**
- * Copyright (c) 2002-2003 Richard Heyes
- * Copyright 2011-2013 Horde LLC (http://www.horde.org/)
- *
- * This code is based on the original code contained in the PEAR Auth_SASL
- * package (v0.5.1):
- *   $Id: DigestMD5.php 294702 2010-02-07 16:03:55Z cweiske $
- *
- * That code is covered by the BSD 3-Clause license, as set forth below:
- * +-----------------------------------------------------------------------+
- * | 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.  |
- * +-----------------------------------------------------------------------+
- *
- * @category  Horde
- * @copyright 2002-2003 Richard Heyes
- * @copyright 2011-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Provides the code needed to authenticate via the DIGEST-MD5 SASL mechanism
- * (defined in RFC 2831). This method has been obsoleted by RFC 6331, but
- * still is in use on legacy servers.
- *
- * @author    Richard Heyes <richard@php.net>
- * @author    Michael Slusarz <slusarz@horde.org>
- * @copyright 2002-2003 Richard Heyes
- * @copyright 2011-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Auth_DigestMD5
-{
-    /**
-     * Digest response components.
-     *
-     * @var string
-     */
-    protected $_response;
-
-    /**
-     * Generate the Digest-MD5 response.
-     *
-     * @param string $id         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 connecting to.
-     * @param string $service    The service name (e.g. 'imap', 'pop3').
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function __construct($id, $pass, $challenge, $hostname, $service)
-    {
-        $challenge = $this->_parseChallenge($challenge);
-        $cnonce = $this->_getCnonce();
-        $digest_uri = sprintf('%s/%s', $service, $hostname);
-
-        /* Get response value. */
-        $A1 = sprintf('%s:%s:%s', pack('H32', hash('md5', sprintf('%s:%s:%s', $id, $challenge['realm'], $pass))), $challenge['nonce'], $cnonce);
-        $A2 = 'AUTHENTICATE:' . $digest_uri;
-        $response_value = hash('md5', sprintf('%s:%s:00000001:%s:auth:%s', hash('md5', $A1), $challenge['nonce'], $cnonce, hash('md5', $A2)));
-
-        $this->_response = array(
-            'cnonce' => '"' . $cnonce . '"',
-            'digest-uri' => '"' . $digest_uri . '"',
-            'maxbuf' => $challenge['maxbuf'],
-            'nc' => '00000001',
-            'nonce' => '"' . $challenge['nonce'] . '"',
-            'qop' => 'auth',
-            'response' => $response_value,
-            'username' => '"' . $id . '"'
-        );
-
-        if (strlen($challenge['realm'])) {
-            $this->_response['realm'] = '"' . $challenge['realm'] . '"';
-        }
-    }
-
-    /**
-     * Cooerce to string.
-     *
-     * @return string  The digest response (not base64 encoded).
-     */
-    public function __toString()
-    {
-        $out = array();
-        foreach ($this->_response as $key => $val) {
-            $out[] = $key . '=' . $val;
-        }
-        return implode(',', $out);
-    }
-
-    /**
-     * Return specific digest response directive.
-     *
-     * @return mixed  Requested directive, or null if it does not exist.
-     */
-    public function __get($name)
-    {
-        return isset($this->_response[$name])
-            ? $this->_response[$name]
-            : null;
-    }
-
-    /**
-    * Parses and verifies the digest challenge.
-    *
-    * @param string $challenge  The digest challenge
-    *
-    * @return array  The parsed challenge as an array with directives as keys.
-    *
-    * @throws Horde_Imap_Client_Exception
-    */
-    protected function _parseChallenge($challenge)
-    {
-        $tokens = array(
-            'maxbuf' => 65536,
-            'realm' => ''
-        );
-
-        preg_match_all('/([a-z-]+)=("[^"]+(?<!\\\)"|[^,]+)/i', $challenge, $matches, PREG_SET_ORDER);
-
-        foreach ($matches as $val) {
-            $tokens[$val[1]] = trim($val[2], '"');
-        }
-
-        // Required directives.
-        if (!isset($tokens['nonce']) || !isset($tokens['algorithm'])) {
-            throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::t("Authentication failure."), 'SERVER_CONNECT');
-        }
-
-        return $tokens;
-    }
-
-    /**
-     * Creates the client nonce for the response
-     *
-     * @return string  The cnonce value.
-     */
-    protected function _getCnonce()
-    {
-        if ((@is_readable('/dev/urandom') &&
-             ($fd = @fopen('/dev/urandom', 'r'))) ||
-            (@is_readable('/dev/random') &&
-             ($fd = @fopen('/dev/random', 'r')))) {
-            $str = fread($fd, 32);
-            fclose($fd);
-        } else {
-            $str = '';
-            for ($i = 0; $i < 32; ++$i) {
-                $str .= chr(mt_rand(0, 255));
-            }
-        }
-
-        return base64_encode($str);
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientBaseConnectionphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Base/Connection.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Base/Connection.php      2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Base/Connection.php 2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,112 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Abstract interface for the object used to connect to remote mail server.
- *
- * NOTE: This class is NOT intended to be accessed outside of the package.
- * There is NO guarantees that the API of this class will not change across
- * versions.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2013 Horde LLC
- * @internal
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- *
- * @property-read boolean $connected  Is there a connection to the server?
- * @property-read boolean $secure  Is the connection secure?
- */
-abstract class Horde_Imap_Client_Base_Connection
-{
-    /**
-     * Is there a connection to the server?
-     *
-     * @var boolean
-     */
-    protected $_connected = false;
-
-    /**
-     * Debug object.
-     *
-     * @var object
-     */
-    protected $_debug;
-
-    /**
-     * Is the connection secure?
-     *
-     * @var boolean
-     */
-    protected $_secure = false;
-
-    /**
-     * Constructor.
-     *
-     * @param Horde_Imap_Client_Base $base  The base client object.
-     * @param object $debug                 The debug handler.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function __construct(Horde_Imap_Client_Base $base, $debug)
-    {
-        if ($base->getParam('secure') && !extension_loaded('openssl')) {
-            throw new InvalidArgumentException('Secure connections require the PHP openssl extension.');
-        }
-
-        $this->_debug = $debug;
-    }
-
-    /**
-     */
-    public function __get($name)
-    {
-        switch ($name) {
-        case 'connected':
-            return $this->_connected;
-
-        case 'secure':
-            return $this->_secure;
-        }
-    }
-
-    /**
-     * This object can not be cloned.
-     */
-    public function __clone()
-    {
-        throw new LogicException('Object cannot be cloned.');
-    }
-
-    /**
-     * This object can not be serialized.
-     */
-    public function __sleep()
-    {
-        throw new LogicException('Object can not be serialized.');
-    }
-
-    /**
-     * Start a TLS connection to the server.
-     *
-     * @return boolean  Whether TLS was successfully started.
-     */
-    abstract public function startTls();
-
-    /**
-     * Close the connection to the server.
-     */
-    abstract public function close();
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientBaseDebugphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Base/Debug.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Base/Debug.php   2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Base/Debug.php      2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,152 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * An object allowing management of debugging output within a
- * Horde_Imap_Client_Base object.
- *
- * NOTE: This class is NOT intended to be accessed outside of a Base object.
- * There is NO guarantees that the API of this class will not change across
- * versions.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @internal
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Base_Debug
-{
-    /* Time, in seconds, to be labeled a slow command. */
-    const SLOW_CMD = 5;
-
-    /**
-     * Is debugging active?
-     *
-     * @var boolean
-     */
-    public $debug = true;
-
-    /**
-     * The debug stream.
-     *
-     * @var resource
-     */
-    protected $_stream;
-
-    /**
-     * Timestamp of last command.
-     *
-     * @var integer
-     */
-    protected $_time = null;
-
-    /**
-     * Constructor.
-     *
-     * @param mixed $debug  The debug target.
-     */
-    public function __construct($debug)
-    {
-        $this->_stream = is_resource($debug)
-            ? $debug
-            : @fopen($debug, 'a');
-        register_shutdown_function(array($this, 'shutdown'));
-    }
-
-    /**
-     * Shutdown function.
-     */
-    public function shutdown()
-    {
-        if (is_resource($this->_stream)) {
-            fflush($this->_stream);
-            fclose($this->_stream);
-            $this->_stream = null;
-        }
-    }
-
-    /**
-     * Write client output to debug log.
-     *
-     * @param string $msg  Debug message.
-     */
-    public function client($msg)
-    {
-        $this->_write($msg . "\n", 'C: ');
-    }
-
-    /**
-     * Write informational message to debug log.
-     *
-     * @param string $msg  Debug message.
-     */
-    public function info($msg)
-    {
-        $this->_write($msg . "\n", '>> ');
-    }
-
-    /**
-     * Write server output to debug log.
-     *
-     * @param string $msg  Debug message.
-     */
-    public function raw($msg)
-    {
-        $this->_write($msg);
-    }
-
-    /**
-     * Write server output to debug log.
-     *
-     * @param string $msg  Debug message.
-     */
-    public function server($msg)
-    {
-        $this->_write($msg . "\n", 'S: ');
-    }
-
-    /**
-     * Write debug information to the output stream.
-     *
-     * @param string $msg  Debug data.
-     */
-    protected function _write($msg, $pre = null)
-    {
-        if (!$this->debug || !$this->_stream) {
-            return;
-        }
-
-        if (!is_null($pre)) {
-            $new_time = microtime(true);
-
-            if (is_null($this->_time)) {
-                fwrite(
-                    $this->_stream,
-                    str_repeat('-', 30) . "\n" . '>> ' . date('r') . "\n"
-                );
-            } elseif (($diff = ($new_time - $this->_time)) > self::SLOW_CMD) {
-                fwrite(
-                    $this->_stream,
-                    '>> Slow Command: ' . round($diff, 3) . " seconds\n"
-                );
-            }
-
-            $this->_time = $new_time;
-        }
-
-        fwrite($this->_stream, $pre . $msg);
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientBaseDeprecatedphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Base/Deprecated.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Base/Deprecated.php      2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Base/Deprecated.php 2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,109 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Class containing deprecated Horde_Imap_Client_Base methods that will be
- * removed in version 3.0.
- *
- * NOTE: This class is NOT intended to be accessed outside of a Base object.
- * There is NO guarantees that the API of this class will not change across
- * versions.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @internal
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Base_Deprecated
-{
-    /**
-     * Returns a unique identifier for the current mailbox status.
-     *
-     * @param Horde_Imap_Client_Base $base_ob  The base driver object.
-     * @param mixed $mailbox                   A mailbox. Either a
-     *                                         Horde_Imap_Client_Mailbox
-     *                                         object or a string (UTF-8).
-     * @param boolean $condstore               Is CONDSTORE enabled?
-     * @param array $addl                      Additional cache info to add to
-     *                                         the cache ID string.
-     *
-     * @return string  The cache ID string, which will change when the
-     *                 composition of the mailbox changes. The uidvalidity
-     *                 will always be the first element, and will be delimited
-     *                 by the '|' character.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    static public function getCacheId($base_ob, $mailbox, $condstore,
-                                      array $addl = array())
-    {
-        $query = Horde_Imap_Client::STATUS_UIDVALIDITY | Horde_Imap_Client::STATUS_MESSAGES | Horde_Imap_Client::STATUS_UIDNEXT;
-
-        /* Use MODSEQ as cache ID if CONDSTORE extension is available. */
-        if ($condstore) {
-            $query |= Horde_Imap_Client::STATUS_HIGHESTMODSEQ;
-        } else {
-            $query |= Horde_Imap_Client::STATUS_UIDNEXT_FORCE;
-        }
-
-        $status = $base_ob->status($mailbox, $query);
-
-        if (empty($status['highestmodseq'])) {
-            $parts = array(
-                'V' . $status['uidvalidity'],
-                'U' . $status['uidnext'],
-                'M' . $status['messages']
-            );
-        } else {
-            $parts = array(
-                'V' . $status['uidvalidity'],
-                'H' . $status['highestmodseq']
-            );
-        }
-
-        return implode('|', array_merge($parts, $addl));
-    }
-
-    /**
-     * Parses a cacheID created by getCacheId().
-     *
-     * @param string $id  The cache ID.
-     *
-     * @return array  An array with the following information:
-     *   - highestmodseq: (integer)
-     *   - messages: (integer)
-     *   - uidnext: (integer)
-     *   - uidvalidity: (integer) Always present
-     */
-    static public function parseCacheId($id)
-    {
-        $data = array(
-            'H' => 'highestmodseq',
-            'M' => 'messages',
-            'U' => 'uidnext',
-            'V' => 'uidvalidity'
-        );
-        $info = array();
-
-        foreach (explode('|', $id) as $part) {
-            if (isset($data[$part[0]])) {
-                $info[$data[$part[0]]] = intval(substr($part, 1));
-            }
-        }
-
-        return $info;
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientBaseMailboxphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Base/Mailbox.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Base/Mailbox.php 2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Base/Mailbox.php    2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,176 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * An object allowing management of mailbox state within a
- * Horde_Imap_Client_Base object.
- *
- * NOTE: This class is NOT intended to be accessed outside of a Base object.
- * There is NO guarantees that the API of this class will not change across
- * versions.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @internal
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Base_Mailbox
-{
-    /**
-     * Mapping object.
-     *
-     * @var Horde_Imap_Client_Ids_Map
-     */
-    public $map;
-
-    /**
-     * Is mailbox opened?
-     *
-     * @var boolean
-     */
-    public $open;
-
-    /**
-     * Is mailbox sync'd with remote server (via CONDSTORE/QRESYNC)?
-     *
-     * @var boolean
-     */
-    public $sync;
-
-    /**
-     * Status information.
-     *
-     * @var array
-     */
-    protected $_status = array();
-
-    /**
-     * Constructor.
-     */
-    public function __construct()
-    {
-        $this->reset();
-    }
-
-    /**
-     * Get status information for the mailbox.
-     *
-     * @param integer $entry  STATUS_* constant.
-     *
-     * @return mixed  Status information.
-     */
-    public function getStatus($entry)
-    {
-        if (isset($this->_status[$entry])) {
-            return $this->_status[$entry];
-        }
-
-        switch ($entry) {
-        case Horde_Imap_Client::STATUS_FIRSTUNSEEN:
-            /* If we know there are no messages in the current mailbox, we
-             * know there are no unseen messages. */
-            return empty($this->_status[Horde_Imap_Client::STATUS_MESSAGES])
-                ? false
-                : null;
-
-        case Horde_Imap_Client::STATUS_RECENT_TOTAL:
-        case Horde_Imap_Client::STATUS_SYNCMODSEQ:
-            return 0;
-
-        case Horde_Imap_Client::STATUS_SYNCFLAGUIDS:
-        case Horde_Imap_Client::STATUS_SYNCVANISHED:
-            return array();
-
-        case Horde_Imap_Client::STATUS_PERMFLAGS:
-            /* If PERMFLAGS is not returned by server, must assume that all
-             * flags can be change permanently (RFC 3501 [6.3.1]). */
-            $flags = isset($this->_status[Horde_Imap_Client::STATUS_FLAGS])
-                ? $this->_status[Horde_Imap_Client::STATUS_FLAGS]
-                : array();
-            $flags[] = "\\*";
-            return $flags;
-
-        case Horde_Imap_Client::STATUS_UIDNOTSTICKY:
-            /* In the absence of explicit uidnotsticky identification, assume
-             * that UIDs are sticky. */
-            return false;
-
-        case Horde_Imap_Client::STATUS_UNSEEN:
-            /* If we know there are no messages in the current mailbox, we
-             * know there are no unseen messages . */
-            return empty($this->_status[Horde_Imap_Client::STATUS_MESSAGES])
-                ? 0
-                : null;
-
-        default:
-            return null;
-        }
-    }
-
-    /**
-     * Set status information for the mailbox.
-     *
-     * @param integer $entry  STATUS_* constant.
-     * @param mixed $value    Status information.
-     */
-    public function setStatus($entry, $value)
-    {
-        switch ($entry) {
-        case Horde_Imap_Client::STATUS_RECENT:
-            /* Keep track of RECENT_TOTAL information. */
-            $this->_status[Horde_Imap_Client::STATUS_RECENT_TOTAL] = isset($this->_status[Horde_Imap_Client::STATUS_RECENT_TOTAL])
-                ? ($this->_status[Horde_Imap_Client::STATUS_RECENT_TOTAL] + $value)
-                : $value;
-            break;
-
-        case Horde_Imap_Client::STATUS_SYNCMODSEQ:
-            /* This is only set once per access. */
-            if (isset($this->_status[$entry])) {
-                return;
-            }
-            break;
-
-        case Horde_Imap_Client::STATUS_SYNCFLAGUIDS:
-        case Horde_Imap_Client::STATUS_SYNCVANISHED:
-            if (!isset($this->_status[$entry])) {
-                $this->_status[$entry] = array();
-            }
-            $this->_status[$entry] = array_merge($this->_status[$entry], $value);
-            return;
-        }
-
-        $this->_status[$entry] = $value;
-    }
-
-    /**
-     * Reset the mailbox information.
-     */
-    public function reset()
-    {
-        $keep = array(
-            Horde_Imap_Client::STATUS_SYNCFLAGUIDS,
-            Horde_Imap_Client::STATUS_SYNCMODSEQ,
-            Horde_Imap_Client::STATUS_SYNCVANISHED
-        );
-
-        foreach (array_diff(array_keys($this->_status), $keep) as $val) {
-            unset($this->_status[$val]);
-        }
-
-        $this->map = new Horde_Imap_Client_Ids_Map();
-        $this->open = $this->sync = false;
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientBasephp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Base.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Base.php 2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Base.php    2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,4103 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2008-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2008-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * An abstracted API interface to IMAP backends supporting the IMAP4rev1
- * protocol (RFC 3501).
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2008-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-abstract class Horde_Imap_Client_Base implements Serializable
-{
-    /* Serialized version. */
-    const VERSION = 2;
-
-    /* Cache names for miscellaneous data. */
-    const CACHE_MODSEQ = '_m';
-    const CACHE_SEARCH = '_s';
-    /* @since 2.9.0 */
-    const CACHE_SEARCHID = '_i';
-
-    /* Cache names used exclusively within this class. @since 2.11.0 */
-    const CACHE_DOWNGRADED = 'HICdg';
-
-    /**
-     * The list of fetch fields that can be cached, and their cache names.
-     *
-     * @var array
-     */
-    public $cacheFields = array(
-        Horde_Imap_Client::FETCH_ENVELOPE => 'HICenv',
-        Horde_Imap_Client::FETCH_FLAGS => 'HICflags',
-        Horde_Imap_Client::FETCH_HEADERS => 'HIChdrs',
-        Horde_Imap_Client::FETCH_IMAPDATE => 'HICdate',
-        Horde_Imap_Client::FETCH_SIZE => 'HICsize',
-        Horde_Imap_Client::FETCH_STRUCTURE => 'HICstruct'
-    );
-
-    /**
-     * Has the internal configuration changed?
-     *
-     * @var boolean
-     */
-    public $changed = false;
-
-    /**
-     * The Horde_Imap_Client_Cache object.
-     *
-     * @var Horde_Imap_Client_Cache
-     */
-    protected $_cache = null;
-
-    /**
-     * Connection to the IMAP server.
-     *
-     * @var Horde_Imap_Client_Base_Connection
-     */
-    protected $_connection = null;
-
-    /**
-     * The debug object.
-     *
-     * @var Horde_Imap_Client_Base_Debug
-     */
-    protected $_debug = null;
-
-    /**
-     * The fetch data object type to return.
-     *
-     * @var string
-     */
-    protected $_fetchDataClass = 'Horde_Imap_Client_Data_Fetch';
-
-    /**
-     * Cached server data.
-     *
-     * @var array
-     */
-    protected $_init;
-
-    /**
-     * Is there an active authenticated connection to the IMAP Server?
-     *
-     * @var boolean
-     */
-    protected $_isAuthenticated = false;
-
-    /**
-     * The current mailbox selection mode.
-     *
-     * @var integer
-     */
-    protected $_mode = 0;
-
-    /**
-     * Hash containing connection parameters.
-     * This hash never changes.
-     *
-     * @var array
-     */
-    protected $_params = array();
-
-    /**
-     * The currently selected mailbox.
-     *
-     * @var Horde_Imap_Client_Mailbox
-     */
-    protected $_selected = null;
-
-    /**
-     * Temp array (destroyed at end of process).
-     *
-     * @var array
-     */
-    protected $_temp = array(
-        'enabled' => array()
-    );
-
-    /**
-     * Constructor.
-     *
-     * @param array $params   Configuration parameters:
-     * <ul>
-     *  <li>REQUIRED Parameters
-     *   <ul>
-     *    <li>password: (string) The IMAP user password.</li>
-     *    <li>username: (string) The IMAP username.</li>
-     *   </ul>
-     *  </li>
-     *  <li>Optional Parameters
-     *   <ul>
-     *    <li>
-     *     cache: (array) If set, caches data from fetch(), search(), and
-     *            thread() calls. Requires the horde/Cache package to be
-     *            installed. The array can contain the following keys (see
-     *            Horde_Imap_Client_Cache for default values):
-     *     <ul>
-     *      <li>
-     *       backend: [REQUIRED (or cacheob)] (Horde_Imap_Client_Cache_Backend)
-     *                Backend cache driver (as of 2.9.0).
-     *      </li>
-     *      <li>
-     *       cacheob: [REQUIRED (or backend)] (Horde_Cache) The cache object to
-     *                use (DEPRECATED).
-     *      </li>
-     *      <li>
-     *       fetch_ignore: (array) A list of mailboxes to ignore when storing
-     *                     fetch data.
-     *      </li>
-     *      <li>
-     *       fields: (array) The fetch criteria to cache. If not defined, all
-     *               cacheable data is cached. The following is a list of
-     *               criteria that can be cached:
-     *       <ul>
-     *        <li>Horde_Imap_Client::FETCH_ENVELOPE</li>
-     *        <li>Horde_Imap_Client::FETCH_FLAGS
-     *         <ul>
-     *          <li>
-     *           Only if server supports CONDSTORE extension
-     *          </li>
-     *         </ul>
-     *        </li>
-     *        <li>Horde_Imap_Client::FETCH_HEADERS
-     *         <ul>
-     *          <li>
-     *           Only for queries that specifically request caching
-     *          </li>
-     *         </ul>
-     *        </li>
-     *        <li>Horde_Imap_Client::FETCH_IMAPDATE</li>
-     *        <li>Horde_Imap_Client::FETCH_SIZE</li>
-     *        <li>Horde_Imap_Client::FETCH_STRUCTURE</li>
-     *       </ul>
-     *      </li>
-     *     </ul>
-     *    </li>
-     *    <li>
-     *     capability_ignore: (array) A list of IMAP capabilites to ignore,
-     *                        even if they are supported on the server.
-     *                        DEFAULT: No supported capabilities are ignored.
-     *    </li>
-     *    <li>
-     *     comparator: (string) The search comparator to use instead of the
-     *                 default IMAP server comparator. See
-     *                 Horde_Imap_Client_Base#setComparator() for format.
-     *                 DEFAULT: Use the server default
-     *    </li>
-     *    <li>
-     *     debug: (string) If set, will output debug information to the stream
-     *            provided. The value can be any PHP supported wrapper that
-     *            can be opened via fopen().
-     *            DEFAULT: No debug output
-     *    </li>
-     *    <li>
-     *     encryptKey: (array) A callback to a function that returns the key
-     *                 used to encrypt the password. This function MUST be
-     *                 static.
-     *                 DEFAULT: No encryption
-     *    </li>
-     *    <li>
-     *     hostspec: (string) The hostname or IP address of the server.
-     *               DEFAULT: 'localhost'
-     *    </li>
-     *    <li>
-     *     id: (array) Send ID information to the IMAP server (only if server
-     *         supports the ID extension). An array with the keys as the
-     *         fields to send and the values being the associated values. See
-     *         RFC 2971 [3.3] for a list of defined standard field values.
-     *         DEFAULT: No info sent to server
-     *    </li>
-     *    <li>
-     *     lang: (array) A list of languages (in priority order) to be used to
-     *           display human readable messages.
-     *           DEFAULT: Messages output in IMAP server default language
-     *    </li>
-     *    <li>
-     *     port: (integer) The server port to which we will connect.
-     *           DEFAULT: 143 (imap or imap w/TLS) or 993 (imaps)
-     *    </li>
-     *    <li>
-     *     secure: (string) Use SSL or TLS to connect.
-     *             VALUES:
-     *     <ul>
-     *      <li>false</li>
-     *      <li>'ssl' (Auto-detect SSL version)</li>
-     *      <li>'sslv2' (Force SSL version 3)</li>
-     *      <li>'sslv3' (Force SSL version 2)</li>
-     *      <li>'tls'</li>
-     *     </ul>
-     *             DEFAULT: No encryption</li>
-     *    </li>
-     *    <li>
-     *     timeout: (integer)  Connection timeout, in seconds.
-     *              DEFAULT: 30 seconds
-     *    </li>
-     *   </ul>
-     *  </li>
-     * </ul>
-     */
-    public function __construct(array $params = array())
-    {
-        if (!isset($params['username']) || !isset($params['password'])) {
-            throw new InvalidArgumentException('Horde_Imap_Client requires a username and password.');
-        }
-
-        $this->_setInit();
-
-        // Default values.
-        $params = array_merge(array(
-            'encryptKey' => null,
-            'hostspec' => 'localhost',
-            'secure' => false,
-            'timeout' => 30
-        ), array_filter($params));
-
-        if ($params['secure'] === true) {
-            $params['secure'] = 'tls';
-        }
-
-        if (!isset($params['port'])) {
-            $params['port'] = (isset($params['secure']) && in_array($params['secure'], array('ssl', 'sslv2', 'sslv3')))
-                ? 993
-                : 143;
-        }
-
-        if (empty($params['cache'])) {
-            $params['cache'] = array('fields' => array());
-        } elseif (empty($params['cache']['fields'])) {
-            $params['cache']['fields'] = $this->cacheFields;
-        } else {
-            $params['cache']['fields'] = array_flip($params['cache']['fields']);
-        }
-
-        if (empty($params['cache']['fetch_ignore'])) {
-            $params['cache']['fetch_ignore'] = array();
-        }
-
-        $this->_params = $params;
-        $this->setParam('password', $params['password']);
-
-        $this->changed = true;
-        $this->_initOb();
-    }
-
-    /**
-     * Get encryption key.
-     *
-     * @return string  The encryption key.
-     */
-    protected function _getEncryptKey()
-    {
-        if (is_callable($ekey = $this->getParam('encryptKey'))) {
-            return call_user_func($ekey);
-        }
-
-        throw new InvalidArgumentException('encryptKey parameter is not a valid callback.');
-    }
-
-    /**
-     * Do initialization tasks.
-     */
-    protected function _initOb()
-    {
-        register_shutdown_function(array($this, 'shutdown'));
-        $this->_debug = ($debug = $this->getParam('debug'))
-            ? new Horde_Imap_Client_Base_Debug($debug)
-            : new Horde_Support_Stub();
-    }
-
-    /**
-     * Shutdown actions.
-     */
-    public function shutdown()
-    {
-        $this->logout();
-    }
-
-    /**
-     * This object can not be cloned.
-     */
-    public function __clone()
-    {
-        throw new LogicException('Object cannot be cloned.');
-    }
-
-    /**
-     */
-    public function serialize()
-    {
-        return serialize(array(
-            'i' => $this->_init,
-            'p' => $this->_params,
-            'v' => self::VERSION
-        ));
-    }
-
-    /**
-     */
-    public function unserialize($data)
-    {
-        $data = @unserialize($data);
-        if (!is_array($data) ||
-            !isset($data['v']) ||
-            ($data['v'] != self::VERSION)) {
-            throw new Exception('Cache version change');
-        }
-
-        $this->_init = $data['i'];
-        $this->_params = $data['p'];
-
-        $this->_initOb();
-    }
-
-    /**
-     * Set an initialization value.
-     *
-     * @param string $key  The initialization key. If null, resets all keys.
-     * @param mixed $val   The cached value. If null, removes the key.
-     */
-    public function _setInit($key = null, $val = null)
-    {
-        if (is_null($key)) {
-            $this->_init = array(
-                'namespace' => array(),
-                's_charset' => array()
-            );
-        } elseif (is_null($val)) {
-            unset($this->_init[$key]);
-        } else {
-            switch ($key) {
-            case 'capability':
-                if ($ci = $this->getParam('capability_ignore')) {
-                    if ($this->_debug->debug &&
-                        ($ignored = array_intersect_key($val, array_flip($ci)))) {
-                        $this->_debug->info(sprintf("CONFIG: IGNORING these IMAP capabilities: %s", implode(', ', array_keys($ignored))));
-                    }
-
-                    $val = array_diff_key($val, array_flip($ci));
-                }
-
-                /* RFC 5162 [1] - QRESYNC implies CONDSTORE and ENABLE, even
-                 * if not listed as a capability. */
-                if (!empty($val['QRESYNC'])) {
-                    $val['CONDSTORE'] = true;
-                    $val['ENABLE'] = true;
-                }
-                break;
-            }
-
-            /* Nothing has changed. */
-            if (isset($this->_init[$key]) && ($this->_init[$key] == $val)) {
-                return;
-            }
-
-            $this->_init[$key] = $val;
-        }
-
-        $this->changed = true;
-    }
-
-    /**
-     * Set the list of enabled extensions.
-     *
-     * @param array $exts      The list of extensions.
-     * @param integer $status  1 means to report as ENABLED, although it has
-     *                         not been formally enabled on server yet. 2 is
-     *                         verified enabled on the server.
-     */
-    protected function _enabled($exts, $status)
-    {
-        /* RFC 5162 [1] - Enabling QRESYNC also implies enabling of
-         * CONDSTORE. */
-        if (in_array('QRESYNC', $exts)) {
-            $exts[] = 'CONDSTORE';
-        }
-
-        switch ($status) {
-        case 2:
-            $enabled_list = array_intersect(array(2), $this->_temp['enabled']);
-            break;
-
-        case 1:
-        default:
-            $enabled_list = $this->_temp['enabled'];
-            $status = 1;
-            break;
-        }
-
-        $this->_temp['enabled'] = array_merge(
-            $enabled_list,
-            array_fill_keys($exts, $status)
-        );
-    }
-
-    /**
-     * Initialize the Horde_Imap_Client_Cache object, if necessary.
-     *
-     * @param boolean $current  If true, we are going to update the currently
-     *                          selected mailbox. Add an additional check to
-     *                          see if caching is available in current
-     *                          mailbox.
-     *
-     * @return boolean  Returns true if caching is enabled.
-     */
-    protected function _initCache($current = false)
-    {
-        $c = $this->getParam('cache');
-
-        if (empty($c['fields'])) {
-            return false;
-        }
-
-        if (is_null($this->_cache)) {
-            if (isset($c['backend']) &&
-                ($c['backend'] instanceof Horde_Imap_Client_Cache_Backend)) {
-                $backend = $c['backend'];
-            } elseif (isset($c['cacheob'])) {
-                /* Deprecated */
-                $backend = new Horde_Imap_Client_Cache_Backend_Cache($c);
-            } else {
-                return false;
-            }
-
-            $this->_cache = new Horde_Imap_Client_Cache(array(
-                'backend' => $backend,
-                'baseob' => $this,
-                'debug' => $this->_debug
-            ));
-        }
-
-        return $current
-            /* If UIDs are labeled as not sticky, don't cache since UIDs will
-             * change on every access. */
-            ? !($this->_mailboxOb()->getStatus(Horde_Imap_Client::STATUS_UIDNOTSTICKY))
-            : true;
-    }
-
-    /**
-     * Returns a value from the internal params array.
-     *
-     * @param string $key  The param key.
-     *
-     * @return mixed  The param value, or null if not found.
-     */
-    public function getParam($key)
-    {
-        /* Passwords may be stored encrypted. */
-        if (($key == 'password') && !empty($this->_params['_passencrypt'])) {
-            try {
-                $secret = new Horde_Secret();
-                return $secret->read($this->_getEncryptKey(), $this->_params['password']);
-            } catch (Exception $e) {
-                return null;
-            }
-        }
-
-        return isset($this->_params[$key])
-            ? $this->_params[$key]
-            : null;
-    }
-
-    /**
-     * Sets a configuration parameter value.
-     *
-     * @param string $key  The param key.
-     * @param mixed $val   The param value.
-     */
-    public function setParam($key, $val)
-    {
-        switch ($key) {
-        case 'password':
-            // Encrypt password.
-            try {
-                $encrypt_key = $this->_getEncryptKey();
-                if (strlen($encrypt_key)) {
-                    $secret = new Horde_Secret();
-                    $val = $secret->write($encrypt_key, $val);
-                    $this->_params['_passencrypt'] = true;
-                }
-            } catch (Exception $e) {
-                $this->_params['_passencrypt'] = false;
-            }
-            break;
-        }
-
-        $this->_params[$key] = $val;
-        $this->changed = true;
-    }
-
-    /**
-     * Returns the Horde_Imap_Client_Cache object used, if available.
-     *
-     * @return mixed  Either the cache object or null.
-     */
-    public function getCache()
-    {
-        $this->_initCache();
-        return $this->_cache;
-    }
-
-    /**
-     * Returns the correct IDs object for use with this driver.
-     *
-     * @param mixed $ids         See add().
-     * @param boolean $sequence  Are $ids message sequence numbers?
-     *
-     * @return Horde_Imap_Client_Ids  The IDs object.
-     */
-    public function getIdsOb($ids = null, $sequence = false)
-    {
-        return new Horde_Imap_Client_Ids($ids, $sequence);
-    }
-
-    /**
-     * Returns whether the IMAP server supports the given capability
-     * (See RFC 3501 [6.1.1]).
-     *
-     * @param string $capability  The capability string to query.
-     *
-     * @return mixed  True if the server supports the queried capability,
-     *                false if it doesn't, or an array if the capability can
-     *                contain multiple values.
-     */
-    public function queryCapability($capability)
-    {
-        try {
-            $this->capability();
-        } catch (Horde_Imap_Client_Exception $e) {
-            return false;
-        }
-
-        $capability = strtoupper($capability);
-
-        if (!isset($this->_init['capability'][$capability])) {
-            return false;
-        }
-
-        /* Check for capability requirements. */
-        if (isset(Horde_Imap_Client::$capability_deps[$capability])) {
-            foreach (Horde_Imap_Client::$capability_deps[$capability] as $val) {
-                if (!$this->queryCapability($val)) {
-                    return false;
-                }
-            }
-        }
-
-        return $this->_init['capability'][$capability];
-    }
-
-    /**
-     * Get CAPABILITY information from the IMAP server.
-     *
-     * @return array  The capability array.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function capability()
-    {
-        if (!isset($this->_init['capability'])) {
-            $this->_capability();
-        }
-
-        return $this->_init['capability'];
-    }
-
-    /**
-     * Retrieve CAPABILITY information from the IMAP server.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    abstract protected function _capability();
-
-    /**
-     * Send a NOOP command (RFC 3501 [6.1.2]).
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function noop()
-    {
-        // NOOP only useful if we are already authenticated.
-        if ($this->_isAuthenticated) {
-            $this->_noop();
-        }
-    }
-
-    /**
-     * Send a NOOP command.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    abstract protected function _noop();
-
-    /**
-     * Get the NAMESPACE information from the IMAP server (RFC 2342).
-     *
-     * @param array $additional  If the server supports namespaces, any
-     *                           additional namespaces to add to the
-     *                           namespace list that are not broadcast by
-     *                           the server. The namespaces must be UTF-8
-     *                           strings.
-     *
-     * @return array  An array of namespace information with the name as the
-     *                key (UTF-8) and the following values:
-     * <ul>
-     *  <li>delimiter: (string) The namespace delimiter.</li>
-     *  <li>hidden: (boolean) Is this a hidden namespace?</li>
-     *  <li>name: (string) The namespace name (UTF-8).</li>
-     *  <li>
-     *   translation: (string) Returns the translated name of the namespace
-     *   (UTF-8). Requires RFC 5255 and a previous call to setLanguage().
-     *  </li>
-     *  <li>
-     *   type: (integer) The namespace type. Either:
-     *   <ul>
-     *    <li>Horde_Imap_Client::NS_PERSONAL</li>
-     *    <li>Horde_Imap_Client::NS_OTHER</li>
-     *    <li>Horde_Imap_Client::NS_SHARED</li>
-     *   </ul>
-     *  </li>
-     * </ul>
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function getNamespaces(array $additional = array())
-    {
-        $this->login();
-
-        $additional = array_map('strval', $additional);
-        $sig = hash('md5', serialize($additional));
-
-        if (isset($this->_init['namespace'][$sig])) {
-            return $this->_init['namespace'][$sig];
-        }
-
-        $ns = $this->_getNamespaces();
-
-        /* Skip namespaces if we have already auto-detected them. Also, hidden
-         * namespaces cannot be empty. */
-        $to_process = array_diff(array_filter($additional, 'strlen'), array_keys($ns));;
-        if (!empty($to_process)) {
-            foreach ($this->listMailboxes($to_process, Horde_Imap_Client::MBOX_ALL, array('delimiter' => true)) as $val) {
-                $ns[$val] = array(
-                    'delimiter' => $first['delimiter'],
-                    'hidden' => true,
-                    'name' => $val,
-                    'translation' => '',
-                    'type' => Horde_Imap_Client::NS_SHARED
-                );
-            }
-        }
-
-        if (empty($ns)) {
-            /* This accurately determines the namespace information of the
-             * base namespace if the NAMESPACE command is not supported.
-             * See: RFC 3501 [6.3.8] */
-            $mbox = $this->listMailboxes('', Horde_Imap_Client::MBOX_ALL, array('delimiter' => true));
-            $first = reset($mbox);
-            $ns[''] = array(
-                'delimiter' => $first['delimiter'],
-                'hidden' => false,
-                'name' => '',
-                'translation' => '',
-                'type' => Horde_Imap_Client::NS_PERSONAL
-            );
-        }
-
-        $this->_setInit('namespace', array_merge($this->_init['namespace'], array($sig => $ns)));
-
-        return $ns;
-    }
-
-    /**
-     * Get the NAMESPACE information from the IMAP server.
-     *
-     * @return array  An array of namespace information. See getNamespaces()
-     *                for format.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    abstract protected function _getNamespaces();
-
-    /**
-     * Display if connection to the server has been secured via TLS or SSL.
-     *
-     * @return boolean  True if the IMAP connection is secured.
-     */
-    public function isSecureConnection()
-    {
-        return ($this->_connection && $this->_connection->secure);
-    }
-
-    /**
-     * Connect to the remote server.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    abstract protected function _connect();
-
-    /**
-     * Return a list of alerts that MUST be presented to the user (RFC 3501
-     * [7.1]).
-     *
-     * @return array  An array of alert messages.
-     */
-    abstract public function alerts();
-
-    /**
-     * Login to the IMAP server.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function login()
-    {
-        if (!$this->_isAuthenticated && $this->_login()) {
-            if ($this->getParam('id')) {
-                try {
-                    $this->sendID();
-                } catch (Horde_Imap_Client_Exception_NoSupportExtension $e) {
-                    // Ignore if server doesn't support ID extension.
-                }
-            }
-
-            if ($this->getParam('comparator')) {
-                try {
-                    $this->setComparator();
-                } catch (Horde_Imap_Client_Exception_NoSupportExtension $e) {
-                    // Ignore if server doesn't support I18NLEVEL=2
-                }
-            }
-        }
-
-        $this->_isAuthenticated = true;
-    }
-
-    /**
-     * Login to the IMAP server.
-     *
-     * @return boolean  Return true if global login tasks should be run.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    abstract protected function _login();
-
-    /**
-     * Logout from the IMAP server (see RFC 3501 [6.1.3]).
-     */
-    public function logout()
-    {
-        if ($this->_isAuthenticated && $this->_connection->connected) {
-            $this->_logout();
-            $this->_connection->close();
-        }
-
-        $this->_connection = $this->_selected = null;
-        $this->_isAuthenticated = false;
-        $this->_mode = 0;
-    }
-
-    /**
-     * Logout from the IMAP server (see RFC 3501 [6.1.3]).
-     */
-    abstract protected function _logout();
-
-    /**
-     * Send ID information to the IMAP server (RFC 2971).
-     *
-     * @param array $info  Overrides the value of the 'id' param and sends
-     *                     this information instead.
-     *
-     * @throws Horde_Imap_Client_Exception
-     * @throws Horde_Imap_Client_Exception_NoSupportExtension
-     */
-    public function sendID($info = null)
-    {
-        if (!$this->queryCapability('ID')) {
-            throw new Horde_Imap_Client_Exception_NoSupportExtension('ID');
-        }
-
-        /* Modified for WordPress inclusion: The shorthand ternary operator ?: was introduced in PHP 5.3. */
-        $this->_sendID(is_null($info) ? ($this->getParam('id') ? $this->getParam('id') : array()) : $info);
-    }
-
-    /**
-     * Send ID information to the IMAP server (RFC 2971).
-     *
-     * @param array $info  The information to send to the server.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    abstract protected function _sendID($info);
-
-    /**
-     * Return ID information from the IMAP server (RFC 2971).
-     *
-     * @return array  An array of information returned, with the keys as the
-     *                'field' and the values as the 'value'.
-     *
-     * @throws Horde_Imap_Client_Exception
-     * @throws Horde_Imap_Client_Exception_NoSupportExtension
-     */
-    public function getID()
-    {
-        if (!$this->queryCapability('ID')) {
-            throw new Horde_Imap_Client_Exception_NoSupportExtension('ID');
-        }
-
-        return $this->_getID();
-    }
-
-    /**
-     * Return ID information from the IMAP server (RFC 2971).
-     *
-     * @return array  An array of information returned, with the keys as the
-     *                'field' and the values as the 'value'.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    abstract protected function _getID();
-
-    /**
-     * Sets the preferred language for server response messages (RFC 5255).
-     *
-     * @param array $langs  Overrides the value of the 'lang' param and sends
-     *                      this list of preferred languages instead. The
-     *                      special string 'i-default' can be used to restore
-     *                      the language to the server default.
-     *
-     * @return string  The language accepted by the server, or null if the
-     *                 default language is used.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function setLanguage($langs = null)
-    {
-        $lang = null;
-
-        if ($this->queryCapability('LANGUAGE')) {
-            $lang = is_null($langs)
-                ? $this->getParam('lang')
-                : $langs;
-        }
-
-        return is_null($lang)
-            ? null
-            : $this->_setLanguage($lang);
-    }
-
-    /**
-     * Sets the preferred language for server response messages (RFC 5255).
-     *
-     * @param array $langs  The preferred list of languages.
-     *
-     * @return string  The language accepted by the server, or null if the
-     *                 default language is used.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    abstract protected function _setLanguage($langs);
-
-    /**
-     * Gets the preferred language for server response messages (RFC 5255).
-     *
-     * @param array $list  If true, return the list of available languages.
-     *
-     * @return mixed  If $list is true, the list of languages available on the
-     *                server (may be empty). If false, the language used by
-     *                the server, or null if the default language is used.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function getLanguage($list = false)
-    {
-        if (!$this->queryCapability('LANGUAGE')) {
-            return $list ? array() : null;
-        }
-
-        return $this->_getLanguage($list);
-    }
-
-    /**
-     * Gets the preferred language for server response messages (RFC 5255).
-     *
-     * @param array $list  If true, return the list of available languages.
-     *
-     * @return mixed  If $list is true, the list of languages available on the
-     *                server (may be empty). If false, the language used by
-     *                the server, or null if the default language is used.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    abstract protected function _getLanguage($list);
-
-    /**
-     * Open a mailbox.
-     *
-     * @param mixed $mailbox  The mailbox to open. Either a
-     *                        Horde_Imap_Client_Mailbox object or a string
-     *                        (UTF-8).
-     * @param integer $mode   The access mode. Either
-     *   - Horde_Imap_Client::OPEN_READONLY
-     *   - Horde_Imap_Client::OPEN_READWRITE
-     *   - Horde_Imap_Client::OPEN_AUTO
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function openMailbox($mailbox, $mode = Horde_Imap_Client::OPEN_AUTO)
-    {
-        $this->login();
-
-        $change = false;
-        $mailbox = Horde_Imap_Client_Mailbox::get($mailbox);
-
-        if ($mode == Horde_Imap_Client::OPEN_AUTO) {
-            if (is_null($this->_selected) ||
-                !$mailbox->equals($this->_selected)) {
-                $mode = Horde_Imap_Client::OPEN_READONLY;
-                $change = true;
-            }
-        } else {
-            $change = (is_null($this->_selected) ||
-                       !$mailbox->equals($this->_selected) ||
-                       ($mode != $this->_mode));
-        }
-
-        if ($change) {
-            $this->_openMailbox($mailbox, $mode);
-            $this->_mailboxOb()->open = true;
-            if ($this->_initCache(true)) {
-                $this->_condstoreSync();
-            }
-        }
-    }
-
-    /**
-     * Open a mailbox.
-     *
-     * @param Horde_Imap_Client_Mailbox $mailbox  The mailbox to open.
-     * @param integer $mode                       The access mode.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    abstract protected function _openMailbox(Horde_Imap_Client_Mailbox $mailbox,
-                                             $mode);
-
-    /**
-     * Called when the selected mailbox is changed.
-     *
-     * @param mixed $mailbox  The selected mailbox or null.
-     * @param integer $mode   The access mode.
-     */
-    protected function _changeSelected($mailbox = null, $mode = null)
-    {
-        $this->_mode = $mode;
-        if (is_null($mailbox)) {
-            $this->_selected = null;
-        } else {
-            $this->_selected = clone $mailbox;
-            $this->_mailboxOb()->reset();
-        }
-    }
-
-    /**
-     * Return the Horde_Imap_Client_Base_Mailbox object.
-     *
-     * @param string $mailbox  The mailbox name. Defaults to currently
-     *                         selected mailbox.
-     *
-     * @return Horde_Imap_Client_Base_Mailbox  Mailbox object.
-     */
-    protected function _mailboxOb($mailbox = null)
-    {
-        $name = is_null($mailbox)
-            ? strval($this->_selected)
-            : strval($mailbox);
-
-        if (!isset($this->_temp['mailbox_ob'][$name])) {
-            $this->_temp['mailbox_ob'][$name] = new Horde_Imap_Client_Base_Mailbox();
-        }
-
-        return $this->_temp['mailbox_ob'][$name];
-    }
-
-    /**
-     * Return the currently opened mailbox and access mode.
-     *
-     * @return mixed  Null if no mailbox selected, or an array with two
-     *                elements:
-     *   - mailbox: (Horde_Imap_Client_Mailbox) The mailbox object.
-     *   - mode: (integer) Current mode.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function currentMailbox()
-    {
-        return is_null($this->_selected)
-            ? null
-            : array(
-                'mailbox' => clone $this->_selected,
-                'mode' => $this->_mode
-            );
-    }
-
-    /**
-     * Create a mailbox.
-     *
-     * @param mixed $mailbox  The mailbox to create. Either a
-     *                        Horde_Imap_Client_Mailbox object or a string
-     *                        (UTF-8).
-     * @param array $opts     Additional options:
-     *   - special_use: (array) An array of special-use flags to mark the
-     *                  mailbox with. The server MUST support RFC 6154.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function createMailbox($mailbox, array $opts = array())
-    {
-        $this->login();
-
-        if (!$this->queryCapability('CREATE-SPECIAL-USE')) {
-            unset($opts['special_use']);
-        }
-
-        $this->_createMailbox(Horde_Imap_Client_Mailbox::get($mailbox), $opts);
-    }
-
-    /**
-     * Create a mailbox.
-     *
-     * @param Horde_Imap_Client_Mailbox $mailbox  The mailbox to create.
-     * @param array $opts                         Additional options. See
-     *                                            createMailbox().
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    abstract protected function _createMailbox(Horde_Imap_Client_Mailbox $mailbox,
-                                               $opts);
-
-    /**
-     * Delete a mailbox.
-     *
-     * @param mixed $mailbox  The mailbox to delete. Either a
-     *                        Horde_Imap_Client_Mailbox object or a string
-     *                        (UTF-8).
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function deleteMailbox($mailbox)
-    {
-        $this->login();
-
-        $mailbox = Horde_Imap_Client_Mailbox::get($mailbox);
-
-        $this->_deleteMailbox($mailbox);
-        $this->_deleteMailboxPost($mailbox);
-    }
-
-    /**
-     * Delete a mailbox.
-     *
-     * @param Horde_Imap_Client_Mailbox $mailbox  The mailbox to delete.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    abstract protected function _deleteMailbox(Horde_Imap_Client_Mailbox $mailbox);
-
-    /**
-     * Actions to perform after a mailbox delete.
-     *
-     * @param Horde_Imap_Client_Mailbox $mailbox  The deleted mailbox.
-     */
-    protected function _deleteMailboxPost(Horde_Imap_Client_Mailbox $mailbox)
-    {
-        /* Delete mailbox caches. */
-        if ($this->_initCache()) {
-            $this->_cache->deleteMailbox($mailbox);
-        }
-        unset($this->_temp['mailbox_ob'][strval($mailbox)]);
-
-        /* Unsubscribe from mailbox. */
-        try {
-            $this->subscribeMailbox($mailbox, false);
-        } catch (Horde_Imap_Client_Exception $e) {
-            // Ignore failed unsubscribe request
-        }
-    }
-
-    /**
-     * Rename a mailbox.
-     *
-     * @param mixed $old  The old mailbox name. Either a
-     *                    Horde_Imap_Client_Mailbox object or a string (UTF-8).
-     * @param mixed $new  The new mailbox name. Either a
-     *                    Horde_Imap_Client_Mailbox object or a string (UTF-8).
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function renameMailbox($old, $new)
-    {
-        // Login will be handled by first listMailboxes() call.
-
-        $old = Horde_Imap_Client_Mailbox::get($old);
-        $new = Horde_Imap_Client_Mailbox::get($new);
-
-        /* Check if old mailbox(es) were subscribed to. */
-        $base = $this->listMailboxes($old, Horde_Imap_Client::MBOX_SUBSCRIBED, array('delimiter' => true));
-        if (empty($base)) {
-            $base = $this->listMailboxes($old, Horde_Imap_Client::MBOX_ALL, array('delimiter' => true));
-            $base = reset($base);
-            $subscribed = array();
-        } else {
-            $base = reset($base);
-            $subscribed = array($base['mailbox']);
-        }
-
-        $all_mboxes = array($base['mailbox']);
-        if (strlen($base['delimiter'])) {
-            $search = $old->list_escape . $base['delimiter'] . '*';
-            $all_mboxes = array_merge($all_mboxes, $this->listMailboxes($search, Horde_Imap_Client::MBOX_ALL, array('flat' => true)));
-            $subscribed = array_merge($subscribed, $this->listMailboxes($search, Horde_Imap_Client::MBOX_SUBSCRIBED, array('flat' => true)));
-        }
-
-        $this->_renameMailbox($old, $new);
-
-        /* Delete mailbox actions. */
-        foreach ($all_mboxes as $val) {
-            $this->_deleteMailboxPost($val);
-        }
-
-        foreach ($subscribed as $val) {
-            try {
-                $this->subscribeMailbox(new Horde_Imap_Client_Mailbox(substr_replace($val, $new, 0, strlen($old))));
-            } catch (Horde_Imap_Client_Exception $e) {
-                // Ignore failed subscription requests
-            }
-        }
-    }
-
-    /**
-     * Rename a mailbox.
-     *
-     * @param Horde_Imap_Client_Mailbox $old  The old mailbox name.
-     * @param Horde_Imap_Client_Mailbox $new  The new mailbox name.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    abstract protected function _renameMailbox(Horde_Imap_Client_Mailbox $old,
-                                               Horde_Imap_Client_Mailbox $new);
-
-    /**
-     * Manage subscription status for a mailbox.
-     *
-     * @param mixed $mailbox      The mailbox to [un]subscribe to. Either a
-     *                            Horde_Imap_Client_Mailbox object or a string
-     *                            (UTF-8).
-     * @param boolean $subscribe  True to subscribe, false to unsubscribe.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function subscribeMailbox($mailbox, $subscribe = true)
-    {
-        $this->login();
-        $this->_subscribeMailbox(Horde_Imap_Client_Mailbox::get($mailbox), (bool)$subscribe);
-    }
-
-    /**
-     * Manage subscription status for a mailbox.
-     *
-     * @param Horde_Imap_Client_Mailbox $mailbox  The mailbox to [un]subscribe
-     *                                            to.
-     * @param boolean $subscribe                  True to subscribe, false to
-     *                                            unsubscribe.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    abstract protected function _subscribeMailbox(Horde_Imap_Client_Mailbox $mailbox,
-                                                  $subscribe);
-
-    /**
-     * Obtain a list of mailboxes matching a pattern.
-     *
-     * @param mixed $pattern   The mailbox search pattern(s) (see RFC 3501
-     *                         [6.3.8] for the format). A UTF-8 string or an
-     *                         array of strings. If a Horde_Imap_Client_Mailbox
-     *                         object is given, it is escaped (i.e. wildcard
-     *                         patterns are converted to return the miminal
-     *                         number of matches possible).
-     * @param integer $mode    Which mailboxes to return.  Either:
-     *   - Horde_Imap_Client::MBOX_SUBSCRIBED
-     *   - Horde_Imap_Client::MBOX_SUBSCRIBED_EXISTS
-     *   - Horde_Imap_Client::MBOX_UNSUBSCRIBED
-     *   - Horde_Imap_Client::MBOX_ALL
-     * @param array $options   Additional options:
-     * <ul>
-     *  <li>
-     *   attributes: (boolean) If true, return attribute information under
-     *   the 'attributes' key.
-     *   DEFAULT: Do not return this information.
-     *  </li>
-     *  <li>
-     *   children: (boolean) Tell server to return children attribute
-     *   information (\HasChildren, \HasNoChildren). Requires the
-     *   LIST-EXTENDED extension to guarantee this information is returned.
-     *   Server MAY return this attribute without this option, or if the
-     *   CHILDREN extension is available, but it is not guaranteed.
-     *   DEFAULT: false
-     *  </li>
-     *  <li>
-     *   delimiter: (boolean) If true, return delimiter information under the
-     *   'delimiter' key.
-     *   DEFAULT: Do not return this information.
-     *  </li>
-     *  <li>
-     *   flat: (boolean) If true, return a flat list of mailbox names only.
-     *   Overrides both the 'attributes' and 'delimiter' options.
-     *   DEFAULT: Do not return flat list.
-     *  </li>
-     *  <li>
-     *   recursivematch: (boolean) Force the server to return information
-     *   about parent mailboxes that don't match other selection options, but
-     *   have some submailboxes that do. Information about children is
-     *   returned in the CHILDINFO extended data item ('extended'). Requires
-     *   the LIST-EXTENDED extension.
-     *   DEFAULT: false
-     *  </li>
-     *  <li>
-     *   remote: (boolean) Tell server to return mailboxes that reside on
-     *   another server. Requires the LIST-EXTENDED extension.
-     *   DEFAULT: false
-     *  </li>
-     *  <li>
-     *   special_use: (boolean) Tell server to return special-use attribute
-     *   information (\Drafts, \Flagged, \Junk, \Sent, \Trash, \All,
-     *   \Archive). Server must support the SPECIAL-USE return option for this
-     *   setting to have any effect. Server MAY return this attribute without
-     *   this option.
-     *   DEFAULT: false
-     *  <li>
-     *   status: (integer) Tell server to return status information. The
-     *   value is a bitmask that may contain any of:
-     *   <ul>
-     *    <li>Horde_Imap_Client::STATUS_MESSAGES</li>
-     *    <li>Horde_Imap_Client::STATUS_RECENT</li>
-     *    <li>Horde_Imap_Client::STATUS_UIDNEXT</li>
-     *    <li>Horde_Imap_Client::STATUS_UIDVALIDITY</li>
-     *    <li>Horde_Imap_Client::STATUS_UNSEEN</li>
-     *    <li>Horde_Imap_Client::STATUS_HIGHESTMODSEQ</li>
-     *   </ul>
-     *   DEFAULT: 0
-     *  </li>
-     *  <li>
-     *   sort: (boolean) If true, return a sorted list of mailboxes?
-     *   DEFAULT: Do not sort the list.
-     *  </li>
-     *  <li>
-     *   sort_delimiter: (string) If 'sort' is true, this is the delimiter
-     *   used to sort the mailboxes.
-     *   DEFAULT: '.'
-     *  </li>
-     * </ul>
-     *
-     * @return array  If 'flat' option is true, the array values are a list
-     *                of Horde_Imap_Client_Mailbox objects. Otherwise, the
-     *                keys are UTF-8 mailbox names and the values are arrays
-     *                with these keys:
-     *   - attributes: (array) List of lower-cased attributes [only if
-     *                 'attributes' option is true].
-     *   - delimiter: (string) The delimiter for the mailbox [only if
-     *                'delimiter' option is true].
-     *   - extended: (TODO) TODO [only if 'recursivematch' option is true and
-     *               LIST-EXTENDED extension is supported on the server].
-     *   - mailbox: (Horde_Imap_Client_Mailbox) The mailbox object.
-     *   - status: (array) See status() [only if 'status' option is true].
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function listMailboxes($pattern,
-                                  $mode = Horde_Imap_Client::MBOX_ALL,
-                                  array $options = array())
-    {
-        $this->login();
-
-        $pattern = is_array($pattern)
-            ? array_unique($pattern)
-            : array($pattern);
-
-        /* Prepare patterns. */
-        $plist = array();
-        foreach ($pattern as $val) {
-            if ($val instanceof Horde_Imap_Client_Mailbox) {
-                $val = $val->list_escape;
-            }
-            $plist[] = Horde_Imap_Client_Mailbox::get(preg_replace(
-                array("/\*{2,}/", "/\%{2,}/"),
-                array('*', '%'),
-                Horde_Imap_Client_Utf7imap::Utf8ToUtf7Imap($val)
-            ), true);
-        }
-
-        if (isset($options['special_use']) &&
-            !$this->queryCapability('SPECIAL-USE')) {
-            unset($options['special_use']);
-        }
-
-        $ret = $this->_listMailboxes($plist, $mode, $options);
-
-        if (!empty($options['status']) &&
-            !$this->queryCapability('LIST-STATUS')) {
-            $status = $this->statusMultiple($this->_selected, $options['status']);
-            foreach ($status as $key => $val) {
-                $ret[$key]['status'] = $val;
-            }
-        }
-
-        if (empty($options['sort'])) {
-            return $ret;
-        }
-
-        $list_ob = new Horde_Imap_Client_Mailbox_List(empty($options['flat']) ? array_keys($ret) : $ret);
-        $sorted = $list_ob->sort(array(
-            'delimiter' => empty($options['sort_delimiter']) ? '.' : $options['sort_delimiter']
-        ));
-
-        if (!empty($options['flat'])) {
-            return $sorted;
-        }
-
-        $out = array();
-        foreach ($sorted as $val) {
-            $out[$val] = $ret[$val];
-        }
-
-        return $out;
-    }
-
-    /**
-     * Obtain a list of mailboxes matching a pattern.
-     *
-     * @param array $pattern  The mailbox search patterns
-     *                        (Horde_Imap_Client_Mailbox objects).
-     * @param integer $mode   Which mailboxes to return.
-     * @param array $options  Additional options.
-     *
-     * @return array  See listMailboxes().
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    abstract protected function _listMailboxes($pattern, $mode, $options);
-
-    /**
-     * Obtain status information for a mailbox.
-     *
-     * @param mixed $mailbox  The mailbox(es) to query. Either a
-     *                        Horde_Imap_Client_Mailbox object, a string
-     *                        (UTF-8), or an array of objects/strings (since
-     *                        2.10.0).
-     * @param integer $flags  A bitmask of information requested from the
-     *                        server. Allowed flags:
-     * <ul>
-     *  <li>
-     *   Horde_Imap_Client::STATUS_MESSAGES
-     *   <ul>
-     *    <li>
-     *     Return key: messages
-     *    </li>
-     *    <li>
-     *     Return format: (integer) The number of messages in the mailbox.
-     *    </li>
-     *   </ul>
-     *  </li>
-     *  <li>
-     *   Horde_Imap_Client::STATUS_RECENT
-     *   <ul>
-     *    <li>
-     *     Return key: recent
-     *    </li>
-     *    <li>
-     *     Return format: (integer) The number of messages with the \Recent
-     *     flag set as currently reported in the mailbox
-     *    </li>
-     *   </ul>
-     *  </li>
-     *  <li>
-     *   Horde_Imap_Client::STATUS_RECENT_TOTAL
-     *   <ul>
-     *    <li>
-     *     Return key: recent_total
-     *    </li>
-     *    <li>
-     *     Return format: (integer) The number of messages with the \Recent
-     *     flag set. This returns the total number of messages that have
-     *     been marked as recent in this mailbox since the PHP process began.
-     *     (since 2.12.0)
-     *    </li>
-     *   </ul>
-     *  </li>
-     *  <li>
-     *   Horde_Imap_Client::STATUS_UIDNEXT
-     *   <ul>
-     *    <li>
-     *     Return key: uidnext
-     *    </li>
-     *    <li>
-     *     Return format: (integer) The next UID to be assigned in the
-     *     mailbox. Only returned if the server automatically provides the
-     *     data.
-     *    </li>
-     *   </ul>
-     *  </li>
-     *  <li>
-     *   Horde_Imap_Client::STATUS_UIDNEXT_FORCE
-     *   <ul>
-     *    <li>
-     *     Return key: uidnext
-     *    </li>
-     *    <li>
-     *     Return format: (integer) The next UID to be assigned in the
-     *     mailbox. This option will always determine this value, even if the
-     *     server does not automatically provide this data.
-     *    </li>
-     *   </ul>
-     *  </li>
-     *  <li>
-     *   Horde_Imap_Client::STATUS_UIDVALIDITY
-     *   <ul>
-     *    <li>
-     *     Return key: uidvalidity
-     *    </li>
-     *    <li>
-     *     Return format: (integer) The unique identifier validity of the
-     *     mailbox.
-     *    </li>
-     *   </ul>
-     *  </li>
-     *  <li>
-     *   Horde_Imap_Client::STATUS_UNSEEN
-     *   <ul>
-     *    <li>
-     *     Return key: unseen
-     *    </li>
-     *    <li>
-     *     Return format: (integer) The number of messages which do not have
-     *     the \Seen flag set.
-     *    </li>
-     *   </ul>
-     *  </li>
-     *  <li>
-     *   Horde_Imap_Client::STATUS_FIRSTUNSEEN
-     *   <ul>
-     *    <li>
-     *     Return key: firstunseen
-     *    </li>
-     *    <li>
-     *     Return format: (integer) The sequence number of the first unseen
-     *     message in the mailbox.
-     *    </li>
-     *   </ul>
-     *  </li>
-     *  <li>
-     *   Horde_Imap_Client::STATUS_FLAGS
-     *   <ul>
-     *    <li>
-     *     Return key: flags
-     *    </li>
-     *    <li>
-     *     Return format: (array) The list of defined flags in the mailbox
-     *     (all flags are in lowercase).
-     *    </li>
-     *   </ul>
-     *  </li>
-     *  <li>
-     *   Horde_Imap_Client::STATUS_PERMFLAGS
-     *   <ul>
-     *    <li>
-     *     Return key: permflags
-     *    </li>
-     *    <li>
-     *     Return format: (array) The list of flags that a client can change
-     *     permanently (all flags are in lowercase).
-     *    </li>
-     *   </ul>
-     *  </li>
-     *  <li>
-     *   Horde_Imap_Client::STATUS_HIGHESTMODSEQ
-     *   <ul>
-     *    <li>
-     *     Return key: highestmodseq
-     *    </li>
-     *    <li>
-     *     Return format: (integer) If the server supports the CONDSTORE
-     *     IMAP extension, this will be the highest mod-sequence value of all
-     *     messages in the mailbox. Else 0 if CONDSTORE not available or the
-     *     mailbox does not support mod-sequences.
-     *    </li>
-     *   </ul>
-     *  </li>
-     *  <li>
-     *   Horde_Imap_Client::STATUS_SYNCMODSEQ
-     *   <ul>
-     *    <li>
-     *     Return key: syncmodseq
-     *    </li>
-     *    <li>
-     *     Return format: (integer) If caching, and the server supports the
-     *     CONDSTORE IMAP extension, this is the cached mod-sequence value of
-     *     the mailbox when it was opened for the first time in this access.
-     *     Will be null if not caching, CONDSTORE not available, or the
-     *     mailbox does not support mod-sequences.
-     *    </li>
-     *   </ul>
-     *  </li>
-     *  <li>
-     *   Horde_Imap_Client::STATUS_SYNCFLAGUIDS
-     *   <ul>
-     *    <li>
-     *     Return key: syncflaguids
-     *    </li>
-     *    <li>
-     *     Return format: (Horde_Imap_Client_Ids) If caching, the server
-     *     supports the CONDSTORE IMAP extension, and the mailbox contained
-     *     cached data when opened for the first time in this access, this is
-     *     the list of UIDs in which flags have changed since
-     *     STATUS_SYNCMODSEQ.
-     *    </li>
-     *   </ul>
-     *  </li>
-     *  <li>
-     *   Horde_Imap_Client::STATUS_SYNCVANISHED
-     *   <ul>
-     *    <li>
-     *     Return key: syncvanished
-     *    </li>
-     *    <li>
-     *     Return format: (Horde_Imap_Client_Ids) If caching, the server
-     *     supports the CONDSTORE IMAP extension, and the mailbox contained
-     *     cached data when opened for the first time in this access, this is
-     *     the list of UIDs which have been deleted since STATUS_SYNCMODSEQ.
-     *    </li>
-     *   </ul>
-     *  </li>
-     *  <li>
-     *   Horde_Imap_Client::STATUS_UIDNOTSTICKY
-     *   <ul>
-     *    <li>
-     *     Return key: uidnotsticky
-     *    </li>
-     *    <li>
-     *     Return format: (boolean) If the server supports the UIDPLUS IMAP
-     *     extension, and the queried mailbox does not support persistent
-     *     UIDs, this value will be true. In all other cases, this value will
-     *     be false.
-     *    </li>
-     *   </ul>
-     *  </li>
-     *  <li>
-     *   Horde_Imap_Client::STATUS_ALL (DEFAULT)
-     *   <ul>
-     *    <li>
-     *     Shortcut to return 'messages', 'recent', 'uidnext', 'uidvalidity',
-     *     and 'unseen' values.
-     *    </li>
-     *   </ul>
-     *  </li>
-     * </ul>
-     * @param array $opts     Additional options:
-     *   - sort: (boolean) If true, sort the list of mailboxes? (since 2.10.0)
-     *           DEFAULT: Do not sort the list.
-     *   - sort_delimiter: (string) If 'sort' is true, this is the delimiter
-     *                     used to sort the mailboxes. (since 2.10.0)
-     *                     DEFAULT: '.'
-     *
-     * @return array  If $mailbox contains multiple mailboxes, an array with
-     *                keys being the UTF-8 mailbox name and values as arrays
-     *                containing the requested keys (see above).
-     *                Otherwise, an array with keys as the requested keys (see
-     *                above) and values as the key data.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function status($mailbox, $flags = Horde_Imap_Client::STATUS_ALL,
-                           array $opts = array())
-    {
-        $opts = array_merge(array(
-            'sort' => false,
-            'sort_delimiter' => '.'
-        ), $opts);
-
-        $this->login();
-
-        if (is_array($mailbox)) {
-            if (empty($mailbox)) {
-                return array();
-            }
-            $ret_array = true;
-        } else {
-            $mailbox = array($mailbox);
-            $ret_array = false;
-        }
-
-        $mlist = array_map(array('Horde_Imap_Client_Mailbox', 'get'), $mailbox);
-
-        $unselected_flags = array(
-            'messages' => Horde_Imap_Client::STATUS_MESSAGES,
-            'recent' => Horde_Imap_Client::STATUS_RECENT,
-            'uidnext' => Horde_Imap_Client::STATUS_UIDNEXT,
-            'uidvalidity' => Horde_Imap_Client::STATUS_UIDVALIDITY,
-            'unseen' => Horde_Imap_Client::STATUS_UNSEEN
-        );
-
-        if ($flags & Horde_Imap_Client::STATUS_ALL) {
-            foreach ($unselected_flags as $val) {
-                $flags |= $val;
-            }
-        }
-
-        $master = $ret = array();
-
-        /* Catch flags that are not supported. */
-        if (($flags & Horde_Imap_Client::STATUS_HIGHESTMODSEQ) &&
-            !isset($this->_temp['enabled']['CONDSTORE'])) {
-            $master['highestmodseq'] = 0;
-            $flags &= ~Horde_Imap_Client::STATUS_HIGHESTMODSEQ;
-        }
-
-        if (($flags & Horde_Imap_Client::STATUS_UIDNOTSTICKY) &&
-            !$this->queryCapability('UIDPLUS')) {
-            $master['uidnotsticky'] = false;
-            $flags &= ~Horde_Imap_Client::STATUS_UIDNOTSTICKY;
-        }
-
-        /* UIDNEXT return options. */
-        if ($flags & Horde_Imap_Client::STATUS_UIDNEXT_FORCE) {
-            $flags |= Horde_Imap_Client::STATUS_UIDNEXT;
-        }
-
-        foreach ($mlist as $val) {
-            $name = strval($val);
-            $tmp_flags = $flags;
-
-            /* A list of STATUS options (other than those handled directly
-             * below) that require the mailbox to be explicitly opened. */
-            $opened = ($flags & Horde_Imap_Client::STATUS_FIRSTUNSEEN) ||
-                ($flags & Horde_Imap_Client::STATUS_FLAGS) ||
-                ($flags & Horde_Imap_Client::STATUS_PERMFLAGS) ||
-                ($flags & Horde_Imap_Client::STATUS_UIDNOTSTICKY) ||
-                /* Check if already in mailbox. */
-                $val->equals($this->_selected) ||
-                /* Force mailboxes containing wildcards to be accessed via
-                 * STATUS so that wildcards do not return a bunch of
-                 * mailboxes in the LIST-STATUS response. */
-                (strpbrk($name, '*%') !== false);
-
-            $ret[$name] = $master;
-            $ptr = &$ret[$name];
-
-            /* STATUS_PERMFLAGS requires a read/write mailbox. */
-            if ($flags & Horde_Imap_Client::STATUS_PERMFLAGS) {
-                $this->openMailbox($val, Horde_Imap_Client::OPEN_READWRITE);
-                $opened = true;
-            }
-
-            /* Handle SYNC related return options. These require the mailbox
-             * to be opened at least once. */
-            if ($flags & Horde_Imap_Client::STATUS_SYNCMODSEQ) {
-                $this->openMailbox($val);
-                $ptr['syncmodseq'] = $this->_mailboxOb($val)->getStatus(Horde_Imap_Client::STATUS_SYNCMODSEQ);
-                $tmp_flags &= ~Horde_Imap_Client::STATUS_SYNCMODSEQ;
-                $opened = true;
-            }
-
-            if ($flags & Horde_Imap_Client::STATUS_SYNCFLAGUIDS) {
-                $this->openMailbox($val);
-                $ptr['syncflaguids'] = $this->getIdsOb($this->_mailboxOb($val)->getStatus(Horde_Imap_Client::STATUS_SYNCFLAGUIDS));
-                $tmp_flags &= ~Horde_Imap_Client::STATUS_SYNCFLAGUIDS;
-                $opened = true;
-            }
-
-            if ($flags & Horde_Imap_Client::STATUS_SYNCVANISHED) {
-                $this->openMailbox($val);
-                $ptr['syncvanished'] = $this->getIdsOb($this->_mailboxOb($val)->getStatus(Horde_Imap_Client::STATUS_SYNCVANISHED));
-                $tmp_flags &= ~Horde_Imap_Client::STATUS_SYNCVANISHED;
-                $opened = true;
-            }
-
-            /* Handle RECENT_TOTAL option. */
-            if ($flags & Horde_Imap_Client::STATUS_RECENT_TOTAL) {
-                $this->openMailbox($val);
-                $ptr['recent_total'] = $this->_mailboxOb($val)->getStatus(Horde_Imap_Client::STATUS_RECENT_TOTAL);
-                $tmp_flags &= ~Horde_Imap_Client::STATUS_RECENT_TOTAL;
-                $opened = true;
-            }
-
-            if ($opened) {
-                if ($tmp_flags) {
-                    $tmp = $this->_status(array($val), $tmp_flags);
-                    $ptr += reset($tmp);
-                }
-            } else {
-                $to_process[] = $val;
-            }
-        }
-
-        if ($flags && !empty($to_process)) {
-            if ((count($to_process) > 1) &&
-                $this->queryCapability('LIST-STATUS')) {
-                foreach ($this->listMailboxes($to_process, Horde_Imap_Client::MBOX_ALL, array('status' => $flags)) as $key => $val) {
-                    if (isset($val['status'])) {
-                        $ret[$key] += $val['status'];
-                    }
-                }
-            } else {
-                foreach ($this->_status($to_process, $flags) as $key => $val) {
-                    $ret[$key] += $val;
-                }
-            }
-        }
-
-        if (!$opts['sort'] || (count($ret) == 1)) {
-            return $ret_array
-                ? $ret
-                : reset($ret);
-        }
-
-        $list_ob = new Horde_Imap_Client_Mailbox_List(array_keys($ret));
-        $sorted = $list_ob->sort(array(
-            'delimiter' => $opts['sort_delimiter']
-        ));
-
-        $out = array();
-        foreach ($sorted as $val) {
-            $out[$val] = $ret[$val];
-        }
-
-        return $out;
-    }
-
-    /**
-     * Obtain status information for mailboxes.
-     *
-     * @param array $mboxes   The list of mailbox objects to query.
-     * @param integer $flags  A bitmask of information requested from the
-     *                        server.
-     *
-     * @return array  See array return for status().
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    abstract protected function _status($mboxes, $flags);
-
-    /**
-     * Perform a STATUS call on multiple mailboxes at the same time.
-     *
-     * This method leverages the LIST-EXTENDED and LIST-STATUS extensions on
-     * the IMAP server to improve the efficiency of this operation.
-     *
-     * @deprecated  Use status() instead.
-     *
-     * @param array $mailboxes  The mailboxes to query. Either
-     *                          Horde_Imap_Client_Mailbox objects, strings
-     *                          (UTF-8), or a combination of the two.
-     * @param integer $flags    See status().
-     * @param array $opts       Additional options:
-     *   - sort: (boolean) If true, sort the list of mailboxes?
-     *           DEFAULT: Do not sort the list.
-     *   - sort_delimiter: (string) If 'sort' is true, this is the delimiter
-     *                     used to sort the mailboxes.
-     *                     DEFAULT: '.'
-     *
-     * @return array  An array with the keys as the mailbox names (UTF-8) and
-     *                the values as arrays with the requested keys (from the
-     *                mask given in $flags).
-     */
-    public function statusMultiple($mailboxes,
-                                   $flags = Horde_Imap_Client::STATUS_ALL,
-                                   array $opts = array())
-    {
-        return $this->status($mailboxes, $flags, $opts);
-    }
-
-    /**
-     * Append message(s) to a mailbox.
-     *
-     * @param mixed $mailbox  The mailbox to append the message(s) to. Either
-     *                        a Horde_Imap_Client_Mailbox object or a string
-     *                        (UTF-8).
-     * @param array $data     The message data to append, along with
-     *                        additional options. An array of arrays with
-     *                        each embedded array having the following
-     *                        entries:
-     * <ul>
-     *  <li>
-     *   data: (mixed) The data to append. If a string or a stream resource,
-     *   this will be used as the entire contents of a single message. If an
-     *   array, will catenate all given parts into a single message. This
-     *   array contains one or more arrays with two keys:
-     *   <ul>
-     *    <li>
-     *     t: (string) Either 'url' or 'text'.
-     *    </li>
-     *    <li>
-     *     v: (mixed) If 't' is 'url', this is the IMAP URL to the message
-     *     part to append. If 't' is 'text', this is either a string or
-     *     resource representation of the message part data.
-     *     DEFAULT: NONE (entry is MANDATORY)
-     *    </li>
-     *   </ul>
-     *  </li>
-     *  <li>
-     *   flags: (array) An array of flags/keywords to set on the appended
-     *   message.
-     *   DEFAULT: Only the \Recent flag is set.
-     *  </li>
-     *  <li>
-     *   internaldate: (DateTime) The internaldate to set for the appended
-     *   message.
-     *   DEFAULT: internaldate will be the same date as when the message was
-     *   appended.
-     *  </li>
-     * </ul>
-     * @param array $options  Additonal options:
-     *   - create: (boolean) Try to create $mailbox if it does not exist?
-     *             DEFAULT: No.
-     *
-     * @return Horde_Imap_Client_Ids  The UIDs of the appended messages.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function append($mailbox, $data, array $options = array())
-    {
-        $this->login();
-
-        $mailbox = Horde_Imap_Client_Mailbox::get($mailbox);
-
-        $ret = $this->_append($mailbox, $data, $options);
-
-        if ($ret instanceof Horde_Imap_Client_Ids) {
-            return $ret;
-        }
-
-        $uids = $this->getIdsOb();
-
-        while (list(,$val) = each($data)) {
-            if (is_string($val['data'])) {
-                $text = $val['data'];
-            } elseif (is_resource($val['data'])) {
-                $text = '';
-                rewind($val['data']);
-                while (!feof($val['data'])) {
-                    $text .= fread($val['data'], 512);
-                    if (preg_match("/\n\r{2,}/", $text)) {
-                        break;
-                    }
-                }
-            }
-
-            $headers = Horde_Mime_Headers::parseHeaders($text);
-            $msgid = $headers->getValue('message-id');
-
-            if ($msgid) {
-                $search_query = new Horde_Imap_Client_Search_Query();
-                $search_query->headerText('Message-ID', $msgid);
-                $uidsearch = $this->search($mailbox, $search_query);
-                $uids->add($uidsearch['match']);
-            }
-        }
-
-        return $uids;
-    }
-
-    /**
-     * Append message(s) to a mailbox.
-     *
-     * @param Horde_Imap_Client_Mailbox $mailbox  The mailbox to append the
-     *                                            message(s) to.
-     * @param array $data                         The message data.
-     * @param array $options                      Additional options.
-     *
-     * @return mixed  A Horde_Imap_Client_Ids object containing the UIDs of
-     *                the appended messages (if server supports UIDPLUS
-     *                extension) or true.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    abstract protected function _append(Horde_Imap_Client_Mailbox $mailbox,
-                                        $data, $options);
-
-    /**
-     * Request a checkpoint of the currently selected mailbox (RFC 3501
-     * [6.4.1]).
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function check()
-    {
-        // CHECK only useful if we are already authenticated.
-        if ($this->_isAuthenticated) {
-            $this->_check();
-        }
-    }
-
-    /**
-     * Request a checkpoint of the currently selected mailbox.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    abstract protected function _check();
-
-    /**
-     * Close the connection to the currently selected mailbox, optionally
-     * expunging all deleted messages (RFC 3501 [6.4.2]).
-     *
-     * @param array $options  Additional options:
-     *   - expunge: (boolean) Expunge all messages flagged as deleted?
-     *              DEFAULT: No
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function close(array $options = array())
-    {
-        // This check catches the non-logged in case.
-        if (is_null($this->_selected)) {
-            return;
-        }
-
-        /* If we are caching, search for deleted messages. */
-        if (!empty($options['expunge']) && $this->_initCache(true)) {
-            /* Make sure mailbox is read-write to expunge. */
-            $this->openMailbox($this->_selected, Horde_Imap_Client::OPEN_READWRITE);
-            if ($this->_mode == Horde_Imap_Client::OPEN_READONLY) {
-                throw new Horde_Imap_Client_Exception(
-                    Horde_Imap_Client_Translation::t("Cannot expunge read-only mailbox."),
-                    Horde_Imap_Client_Exception::MAILBOX_READONLY
-                );
-            }
-
-            $search_query = new Horde_Imap_Client_Search_Query();
-            $search_query->flag(Horde_Imap_Client::FLAG_DELETED, true);
-            $search_res = $this->search($this->_selected, $search_query);
-            $mbox = $this->_selected;
-        } else {
-            $search_res = null;
-        }
-
-        $this->_close($options);
-        $this->_selected = null;
-        $this->_mode = 0;
-
-        if (!is_null($search_res)) {
-            $this->_deleteMsgs($mbox, $search_res['match']);
-        }
-    }
-
-    /**
-     * Close the connection to the currently selected mailbox, optionally
-     * expunging all deleted messages (RFC 3501 [6.4.2]).
-     *
-     * @param array $options  Additional options.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    abstract protected function _close($options);
-
-    /**
-     * Expunge deleted messages from the given mailbox.
-     *
-     * @param mixed $mailbox  The mailbox to expunge. Either a
-     *                        Horde_Imap_Client_Mailbox object or a string
-     *                        (UTF-8).
-     * @param array $options  Additional options:
-     *   - delete: (boolean) If true, will flag all messages in 'ids' as
-     *             deleted (since 2.10.0).
-     *             DEFAULT: false
-     *   - ids: (Horde_Imap_Client_Ids) A list of messages to expunge. These
-     *          messages must already be flagged as deleted (unless 'delete'
-     *          is true).
-     *          DEFAULT: All messages marked as deleted will be expunged.
-     *   - list: (boolean) If true, returns the list of expunged messages
-     *           (UIDs only).
-     *           DEFAULT: false
-     *
-     * @return Horde_Imap_Client_Ids  If 'list' option is true, returns the
-     *                                UID list of expunged messages.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function expunge($mailbox, array $options = array())
-    {
-        // Open mailbox call will handle the login.
-        $this->openMailbox($mailbox, Horde_Imap_Client::OPEN_READWRITE);
-
-        /* Don't expunge if the mailbox is readonly. */
-        if ($this->_mode == Horde_Imap_Client::OPEN_READONLY) {
-            throw new Horde_Imap_Client_Exception(
-                Horde_Imap_Client_Translation::t("Cannot expunge read-only mailbox."),
-                Horde_Imap_Client_Exception::MAILBOX_READONLY
-            );
-        }
-
-        if (empty($options['ids'])) {
-            $options['ids'] = $this->getIdsOb(Horde_Imap_Client_Ids::ALL);
-        } elseif ($options['ids']->isEmpty()) {
-            return $this->getIdsOb();
-        }
-
-        return $this->_expunge($options);
-    }
-
-    /**
-     * Expunge all deleted messages from the given mailbox.
-     *
-     * @param array $options  Additional options.
-     *
-     * @return Horde_Imap_Client_Ids  If 'list' option is true, returns the
-     *                                list of expunged messages.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    abstract protected function _expunge($options);
-
-    /**
-     * Search a mailbox.
-     *
-     * @param mixed $mailbox                         The mailbox to search.
-     *                                               Either a
-     *                                               Horde_Imap_Client_Mailbox
-     *                                               object or a string
-     *                                               (UTF-8).
-     * @param Horde_Imap_Client_Search_Query $query  The search query.
-     *                                               Defaults to an ALL
-     *                                               search.
-     * @param array $options                         Additional options:
-     * <ul>
-     *  <li>
-     *   nocache: (boolean) Don't cache the results.
-     *   DEFAULT: false (results cached, if possible)
-     *  </li>
-     *  <li>
-     *   partial: (mixed) The range of results to return (message sequence
-     *   numbers).
-     *   DEFAULT: All messages are returned.
-     *  </li>
-     *  <li>
-     *   results: (array) The data to return. Consists of zero or more of
-     *   the following flags:
-     *   <ul>
-     *    <li>Horde_Imap_Client::SEARCH_RESULTS_COUNT</li>
-     *    <li>Horde_Imap_Client::SEARCH_RESULTS_MATCH (DEFAULT)</li>
-     *    <li>Horde_Imap_Client::SEARCH_RESULTS_MAX</li>
-     *    <li>Horde_Imap_Client::SEARCH_RESULTS_MIN</li>
-     *    <li>Horde_Imap_Client::SEARCH_RESULTS_SAVE</li>
-     *    <li>Horde_Imap_Client::SEARCH_RESULTS_RELEVANCY</li>
-     *   </ul>
-     *  </li>
-     *  <li>
-     *   sequence: (boolean) If true, returns an array of sequence numbers.
-     *   DEFAULT: Returns an array of UIDs
-     *  </li>
-     *  <li>
-     *   sort: (array) Sort the returned list of messages. Multiple sort
-     *   criteria can be specified. Any sort criteria can be sorted in reverse
-     *   order (instead of the default ascending order) by adding a
-     *   Horde_Imap_Client::SORT_REVERSE element to the array directly before
-     *   adding the sort element. The following sort criteria are available:
-     *   <ul>
-     *    <li>Horde_Imap_Client::SORT_ARRIVAL</li>
-     *    <li>Horde_Imap_Client::SORT_CC</li>
-     *    <li>Horde_Imap_Client::SORT_DATE</li>
-     *    <li>Horde_Imap_Client::SORT_DISPLAYFROM
-     *     <ul>
-     *      <li>
-     *       On servers that don't support SORT=DISPLAY, this criteria will
-     *       fallback to doing client-side sorting.
-     *      </li>
-     *     </ul>
-     *    </li>
-     *    <li>Horde_Imap_Client::SORT_DISPLAYFROM_FALLBACK
-     *     <ul>
-     *      <li>
-     *       On servers that don't support SORT=DISPLAY, this criteria will
-     *       fallback to Horde_Imap_Client::SORT_FROM [since 2.4.0].
-     *      </li>
-     *     </ul>
-     *    </li>
-     *    <li>Horde_Imap_Client::SORT_DISPLAYTO
-     *     <ul>
-     *      <li>
-     *       On servers that don't support SORT=DISPLAY, this criteria will
-     *       fallback to doing client-side sorting.
-     *      </li>
-     *     </ul>
-     *    </li>
-     *    <li>Horde_Imap_Client::SORT_DISPLAYTO_FALLBACK
-     *     <ul>
-     *      <li>
-     *       On servers that don't support SORT=DISPLAY, this criteria will
-     *       fallback to Horde_Imap_Client::SORT_TO [since 2.4.0].
-     *      </li>
-     *     </ul>
-     *    </li>
-     *    <li>Horde_Imap_Client::SORT_FROM</li>
-     *    <li>Horde_Imap_Client::SORT_SEQUENCE</li>
-     *    <li>Horde_Imap_Client::SORT_SIZE</li>
-     *    <li>Horde_Imap_Client::SORT_SUBJECT</li>
-     *    <li>Horde_Imap_Client::SORT_TO</li>
-     *    <li>
-     *     [On servers that support SEARCH=FUZZY, this criteria is also
-     *     available:]
-     *     <ul>
-     *      <li>Horde_Imap_Client::SORT_RELEVANCY</li>
-     *     </ul>
-     *    </li>
-     *   </ul>
-     *  </li>
-     * </ul>
-     *
-     * @return array  An array with the following keys:
-     *   - count: (integer) The number of messages that match the search
-     *            criteria. Always returned.
-     *   - match: (Horde_Imap_Client_Ids) The IDs that match $criteria, sorted
-     *            if the 'sort' modifier was set. Returned if
-     *            Horde_Imap_Client::SEARCH_RESULTS_MATCH is set.
-     *   - max: (integer) The UID (default) or message sequence number (if
-     *          'sequence' is true) of the highest message that satisifies
-     *          $criteria. Returns null if no matches found. Returned if
-     *          Horde_Imap_Client::SEARCH_RESULTS_MAX is set.
-     *   - min: (integer) The UID (default) or message sequence number (if
-     *          'sequence' is true) of the lowest message that satisifies
-     *          $criteria. Returns null if no matches found. Returned if
-     *          Horde_Imap_Client::SEARCH_RESULTS_MIN is set.
-     *   - modseq: (integer) The highest mod-sequence for all messages being
-     *            returned. Returned if 'sort' is false, the search query
-     *            includes a MODSEQ command, and the server supports the
-     *            CONDSTORE IMAP extension.
-     *   - relevancy: (array) The list of relevancy scores. Returned if
-     *                Horde_Imap_Client::SEARCH_RESULTS_RELEVANCY is set and
-     *                the server supports FUZZY search matching.
-     *   - save: (boolean) Whether the search results were saved. Returned if
-     *           Horde_Imap_Client::SEARCH_RESULTS_SAVE is set.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function search($mailbox, $query = null, array $options = array())
-    {
-        $this->login();
-
-        if (empty($options['results'])) {
-            $options['results'] = array(
-                Horde_Imap_Client::SEARCH_RESULTS_MATCH,
-                Horde_Imap_Client::SEARCH_RESULTS_COUNT
-            );
-        }
-
-        // Default to an ALL search.
-        if (is_null($query)) {
-            $query = new Horde_Imap_Client_Search_Query();
-        }
-
-        // Check for SEARCHRES support.
-        if ((($pos = array_search(Horde_Imap_Client::SEARCH_RESULTS_SAVE, $options['results'])) !== false) &&
-            !$this->queryCapability('SEARCHRES')) {
-            unset($options['results'][$pos]);
-        }
-
-        // Check for SORT-related options.
-        if (!empty($options['sort'])) {
-            $sort = $this->queryCapability('SORT');
-            foreach ($options['sort'] as $key => $val) {
-                switch ($val) {
-                case Horde_Imap_Client::SORT_DISPLAYFROM_FALLBACK:
-                    $options['sort'][$key] = (!is_array($sort) || !in_array('DISPLAY', $sort))
-                        ? Horde_Imap_Client::SORT_FROM
-                        : Horde_Imap_Client::SORT_DISPLAYFROM;
-                    break;
-
-                case Horde_Imap_Client::SORT_DISPLAYTO_FALLBACK:
-                    $options['sort'][$key] = (!is_array($sort) || !in_array('DISPLAY', $sort))
-                        ? Horde_Imap_Client::SORT_TO
-                        : Horde_Imap_Client::SORT_DISPLAYTO;
-                    break;
-                }
-            }
-        }
-
-        // Check for supported charset.
-        $options['_query'] = $query->build($this->capability());
-        if (!is_null($options['_query']['charset']) &&
-            array_key_exists($options['_query']['charset'], $this->_init['s_charset']) &&
-            !$this->_init['s_charset'][$options['_query']['charset']]) {
-            foreach (array_merge(array_keys(array_filter($this->_init['s_charset'])), array('US-ASCII')) as $val) {
-                try {
-                    $new_query = clone $query;
-                    $new_query->charset($val);
-                    break;
-                } catch (Horde_Imap_Client_Exception_SearchCharset $e) {
-                    unset($new_query);
-                }
-            }
-
-            if (!isset($new_query)) {
-                throw $e;
-            }
-
-            $query = $new_query;
-            $options['_query'] = $query->build($this->capability());
-        }
-
-        /* RFC 6203: MUST NOT request relevancy results if we are not using
-         * FUZZY searching. */
-        if (in_array(Horde_Imap_Client::SEARCH_RESULTS_RELEVANCY, $options['results']) &&
-            !in_array('SEARCH=FUZZY', $options['_query']['exts_used'])) {
-            throw new InvalidArgumentException('Cannot specify RELEVANCY results if not doing a FUZZY search.');
-        }
-
-        /* Optimization - if query is just for a count of either RECENT or
-         * ALL messages, we can send status information instead. Can't
-         * optimize with unseen queries because we may cause an infinite loop
-         * between here and the status() call. */
-        if ((count($options['results']) == 1) &&
-            (reset($options['results']) == Horde_Imap_Client::SEARCH_RESULTS_COUNT)) {
-            switch ($options['_query']['query']) {
-            case 'ALL':
-                $ret = $this->status($this->_selected, Horde_Imap_Client::STATUS_MESSAGES);
-                return array('count' => $ret['messages']);
-
-            case 'RECENT':
-                $ret = $this->status($this->_selected, Horde_Imap_Client::STATUS_RECENT);
-                return array('count' => $ret['recent']);
-            }
-        }
-
-        $this->openMailbox($mailbox, Horde_Imap_Client::OPEN_AUTO);
-
-        /* Take advantage of search result caching.  If CONDSTORE available,
-         * we can cache all queries and invalidate the cache when the MODSEQ
-         * changes. If CONDSTORE not available, we can only store queries
-         * that don't involve flags. We store results by hashing the options
-         * array - the generated query is already added to '_query' key
-         * above. */
-        $cache = null;
-        if (empty($options['nocache']) &&
-            $this->_initCache(true) &&
-            (isset($this->_temp['enabled']['CONDSTORE']) ||
-             !$query->flagSearch())) {
-            $cache = $this->_getSearchCache('search', $options);
-            if (isset($cache['data'])) {
-                if (isset($cache['data']['match'])) {
-                    $cache['data']['match'] = $this->getIdsOb($cache['data']['match']);
-                }
-                return $cache['data'];
-            }
-        }
-
-        /* Optimization: Catch when there are no messages in a mailbox. */
-        $status_res = $this->status($this->_selected, Horde_Imap_Client::STATUS_MESSAGES | Horde_Imap_Client::STATUS_HIGHESTMODSEQ);
-        if ($status_res['messages'] ||
-            in_array(Horde_Imap_Client::SEARCH_RESULTS_SAVE, $options['results'])) {
-            /* RFC 4551 [3.1] - trying to do a MODSEQ SEARCH on a mailbox that
-             * doesn't support it will return BAD. */
-            if (in_array('CONDSTORE', $options['_query']['exts']) &&
-                !$this->_mailboxOb()->getStatus(Horde_Imap_Client::STATUS_HIGHESTMODSEQ)) {
-                throw new Horde_Imap_Client_Exception(
-                    Horde_Imap_Client_Translation::t("Mailbox does not support mod-sequences."),
-                    Horde_Imap_Client_Exception::MBOXNOMODSEQ
-                );
-            }
-
-            $ret = $this->_search($query, $options);
-        } else {
-            $ret = array(
-                'count' => 0
-            );
-
-            foreach ($options['results'] as $val) {
-                switch ($val) {
-                case Horde_Imap_Client::SEARCH_RESULTS_MATCH:
-                    $ret['match'] = $this->getIdsOb();
-                    break;
-
-                case Horde_Imap_Client::SEARCH_RESULTS_MAX:
-                    $ret['max'] = null;
-                    break;
-
-                case Horde_Imap_Client::SEARCH_RESULTS_MIN:
-                    $ret['min'] = null;
-                    break;
-
-                case Horde_Imap_Client::SEARCH_RESULTS_MIN:
-                    if (isset($status_res['highestmodseq'])) {
-                        $ret['modseq'] = $status_res['highestmodseq'];
-                    }
-                    break;
-
-                case Horde_Imap_Client::SEARCH_RESULTS_RELEVANCY:
-                    $ret['relevancy'] = array();
-                    break;
-                }
-            }
-        }
-
-        if ($cache) {
-            $save = $ret;
-            if (isset($save['match'])) {
-                $save['match'] = strval($ret['match']);
-            }
-            $this->_setSearchCache($save, $cache);
-        }
-
-        return $ret;
-    }
-
-    /**
-     * Search a mailbox.
-     *
-     * @param object $query   The search query.
-     * @param array $options  Additional options. The '_query' key contains
-     *                        the value of $query->build().
-     *
-     * @return Horde_Imap_Client_Ids  An array of IDs.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    abstract protected function _search($query, $options);
-
-    /**
-     * Set the comparator to use for searching/sorting (RFC 5255).
-     *
-     * @param string $comparator  The comparator string (see RFC 4790 [3.1] -
-     *                            "collation-id" - for format). The reserved
-     *                            string 'default' can be used to select
-     *                            the default comparator.
-     *
-     * @throws Horde_Imap_Client_Exception
-     * @throws Horde_Imap_Client_Exception_NoSupportExtension
-     */
-    public function setComparator($comparator = null)
-    {
-        $comp = is_null($comparator)
-            ? $this->getParam('comparator')
-            : $comparator;
-        if (is_null($comp)) {
-            return;
-        }
-
-        $this->login();
-
-        $i18n = $this->queryCapability('I18NLEVEL');
-        if (empty($i18n) || (max($i18n) < 2)) {
-            throw new Horde_Imap_Client_Exception_NoSupportExtension(
-                'I18NLEVEL',
-                'The IMAP server does not support changing SEARCH/SORT comparators.'
-            );
-        }
-
-        $this->_setComparator($comp);
-    }
-
-    /**
-     * Set the comparator to use for searching/sorting (RFC 5255).
-     *
-     * @param string $comparator  The comparator string (see RFC 4790 [3.1] -
-     *                            "collation-id" - for format). The reserved
-     *                            string 'default' can be used to select
-     *                            the default comparator.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    abstract protected function _setComparator($comparator);
-
-    /**
-     * Get the comparator used for searching/sorting (RFC 5255).
-     *
-     * @return mixed  Null if the default comparator is being used, or an
-     *                array of comparator information (see RFC 5255 [4.8]).
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function getComparator()
-    {
-        $this->login();
-
-        $i18n = $this->queryCapability('I18NLEVEL');
-        if (empty($i18n) || (max($i18n) < 2)) {
-            return null;
-        }
-
-        return $this->_getComparator();
-    }
-
-    /**
-     * Get the comparator used for searching/sorting (RFC 5255).
-     *
-     * @return mixed  Null if the default comparator is being used, or an
-     *                array of comparator information (see RFC 5255 [4.8]).
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    abstract protected function _getComparator();
-
-    /**
-     * Thread sort a given list of messages (RFC 5256).
-     *
-     * @param mixed $mailbox  The mailbox to query. Either a
-     *                        Horde_Imap_Client_Mailbox object or a string
-     *                        (UTF-8).
-     * @param array $options  Additional options:
-     * <ul>
-     *  <li>
-     *   criteria: (mixed) The following thread criteria are available:
-     *   <ul>
-     *    <li>Horde_Imap_Client::THREAD_ORDEREDSUBJECT</li>
-     *    <li>Horde_Imap_Client::THREAD_REFERENCES</li>
-     *    <li>Horde_Imap_Client::THREAD_REFS</li>
-     *    <li>
-     *     Other algorithms can be explicitly specified by passing the IMAP
-     *     thread algorithm in as a string value.
-     *    </li>
-     *   </ul>
-     *   DEFAULT: Horde_Imap_Client::THREAD_ORDEREDSUBJECT
-     *  </li>
-     *  <li>
-     *   search: (Horde_Imap_Client_Search_Query) The search query.
-     *   DEFAULT: All messages in mailbox included in thread sort.
-     *  </li>
-     *  <li>
-     *   sequence: (boolean) If true, each message is stored and referred to
-     *   by its message sequence number.
-     *   DEFAULT: Stored/referred to by UID.
-     *  </li>
-     * </ul>
-     *
-     * @return Horde_Imap_Client_Data_Thread  A thread data object.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function thread($mailbox, array $options = array())
-    {
-        // Open mailbox call will handle the login.
-        $this->openMailbox($mailbox, Horde_Imap_Client::OPEN_AUTO);
-
-        /* Take advantage of search result caching.  If CONDSTORE available,
-         * we can cache all queries and invalidate the cache when the MODSEQ
-         * changes. If CONDSTORE not available, we can only store queries
-         * that don't involve flags. See search() for similar caching. */
-        $cache = null;
-        if ($this->_initCache(true) &&
-            (isset($this->_temp['enabled']['CONDSTORE']) ||
-             empty($options['search']) ||
-             !$options['search']->flagSearch())) {
-            $cache = $this->_getSearchCache('thread', $options);
-            if (isset($cache['data']) &&
-                ($cache['data'] instanceof Horde_Imap_Client_Data_Thread)) {
-                return $cache['data'];
-            }
-        }
-
-        $status_res = $this->status($this->_selected, Horde_Imap_Client::STATUS_MESSAGES);
-
-        $ob = $status_res['messages']
-            ? $this->_thread($options)
-            : new Horde_Imap_Client_Data_Thread(array(), empty($options['sequence']) ? 'uid' : 'sequence');
-
-        if ($cache) {
-            $this->_setSearchCache($ob, $cache);
-        }
-
-        return $ob;
-    }
-
-    /**
-     * Thread sort a given list of messages (RFC 5256).
-     *
-     * @param array $options  Additional options. See thread().
-     *
-     * @return Horde_Imap_Client_Data_Thread  A thread data object.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    abstract protected function _thread($options);
-
-    /**
-     * Fetch message data (see RFC 3501 [6.4.5]).
-     *
-     * @param mixed $mailbox                        The mailbox to search.
-     *                                              Either a
-     *                                              Horde_Imap_Client_Mailbox
-     *                                              object or a string (UTF-8).
-     * @param Horde_Imap_Client_Fetch_Query $query  Fetch query object.
-     * @param array $options                        Additional options:
-     *   - changedsince: (integer) Only return messages that have a
-     *                   mod-sequence larger than this value. This option
-     *                   requires the CONDSTORE IMAP extension (if not present,
-     *                   this value is ignored). Additionally, the mailbox
-     *                   must support mod-sequences or an exception will be
-     *                   thrown. If valid, this option implicity adds the
-     *                   mod-sequence fetch criteria to the fetch command.
-     *                   DEFAULT: Mod-sequence values are ignored.
-     *   - exists: (boolean) Ensure that all ids returned exist on the server.
-     *             If false, the list of ids returned in the results object
-     *             is not guaranteed to reflect the current state of the
-     *             remote mailbox.
-     *             DEFAULT: false
-     *   - ids: (Horde_Imap_Client_Ids) A list of messages to fetch data from.
-     *          DEFAULT: All messages in $mailbox will be fetched.
-     *   - nocache: (boolean) If true, will not cache the results (previously
-     *              cached data will still be used to generate results) [since
-     *              2.8.0].
-     *              DEFAULT: false
-     *
-     * @return Horde_Imap_Client_Fetch_Results  A results object.
-     *
-     * @throws Horde_Imap_Client_Exception
-     * @throws Horde_Imap_Client_Exception_NoSupportExtension
-     */
-    public function fetch($mailbox, $query, array $options = array())
-    {
-        try {
-            $ret = $this->_fetchWrapper($mailbox, $query, $options);
-            unset($this->_temp['fetch_nocache']);
-            return $ret;
-        } catch (Exception $e) {
-            unset($this->_temp['fetch_nocache']);
-            throw $e;
-        }
-    }
-
-    /**
-     * Wrapper for fetch() to allow internal state to be rest on exception.
-     *
-     * @internal
-     * @see fetch()
-     */
-    private function _fetchWrapper($mailbox, $query, $options)
-    {
-        $this->login();
-
-        $query = clone $query;
-
-        $cache_array = $header_cache = $new_query = array();
-
-        if (empty($options['ids'])) {
-            $options['ids'] = $this->getIdsOb(Horde_Imap_Client_Ids::ALL);
-        } elseif ($options['ids']->isEmpty()) {
-            return new Horde_Imap_Client_Fetch_Results($this->_fetchDataClass);
-        } elseif ($options['ids']->search_res &&
-                  !$this->queryCapability('SEARCHRES')) {
-            /* SEARCHRES requires server support. */
-            throw new Horde_Imap_Client_Exception_NoSupportExtension('SEARCHRES');
-        }
-
-        $this->openMailbox($mailbox, Horde_Imap_Client::OPEN_AUTO);
-        $mbox_ob = $this->_mailboxOb();
-
-        if (!empty($options['nocache'])) {
-            $this->_temp['fetch_nocache'] = true;
-        }
-
-        $cf = $this->_initCache(true)
-            ? $this->_cacheFields()
-            : array();
-
-        if (!empty($cf)) {
-            /* If using cache, we store by UID so we need to return UIDs. */
-            $query->uid();
-        }
-
-        $modseq_check = !empty($options['changedsince']);
-        if ($query->contains(Horde_Imap_Client::FETCH_MODSEQ)) {
-            if (!isset($this->_temp['enabled']['CONDSTORE'])) {
-                unset($query[Horde_Imap_Client::FETCH_MODSEQ]);
-            } elseif (empty($options['changedsince'])) {
-                $modseq_check = true;
-            }
-        }
-
-        if ($modseq_check &&
-            !$mbox_ob->getStatus(Horde_Imap_Client::STATUS_HIGHESTMODSEQ)) {
-            /* RFC 4551 [3.1] - trying to do a MODSEQ FETCH on a mailbox that
-             * doesn't support it will return BAD. */
-            throw new Horde_Imap_Client_Exception(
-                Horde_Imap_Client_Translation::t("Mailbox does not support mod-sequences."),
-                Horde_Imap_Client_Exception::MBOXNOMODSEQ
-            );
-        }
-
-        /* Determine if caching is available and if anything in $query is
-         * cacheable. */
-        foreach ($cf as $k => $v) {
-            if (isset($query[$k])) {
-                switch ($k) {
-                case Horde_Imap_Client::FETCH_ENVELOPE:
-                case Horde_Imap_Client::FETCH_FLAGS:
-                case Horde_Imap_Client::FETCH_IMAPDATE:
-                case Horde_Imap_Client::FETCH_SIZE:
-                case Horde_Imap_Client::FETCH_STRUCTURE:
-                    $cache_array[$k] = $v;
-                    break;
-
-                case Horde_Imap_Client::FETCH_HEADERS:
-                    $this->_temp['headers_caching'] = array();
-
-                    foreach ($query[$k] as $key => $val) {
-                        /* Only cache if directly requested.  Iterate through
-                         * requests to ensure at least one can be cached. */
-                        if (!empty($val['cache']) && !empty($val['peek'])) {
-                            $cache_array[$k] = $v;
-                            ksort($val);
-                            $header_cache[$key] = hash('md5', serialize($val));
-                        }
-                    }
-                    break;
-                }
-            }
-        }
-
-        $ret = new Horde_Imap_Client_Fetch_Results(
-            $this->_fetchDataClass,
-            $options['ids']->sequence ? Horde_Imap_Client_Fetch_Results::SEQUENCE : Horde_Imap_Client_Fetch_Results::UID
-        );
-
-        /* If nothing is cacheable, we can do a straight search. */
-        if (empty($cache_array)) {
-            $options['_query'] = $query;
-            $this->_fetch($ret, array($options));
-            return $ret;
-        }
-
-        $cs_ret = empty($options['changedsince'])
-            ? null
-            : clone $ret;
-
-        /* Convert special searches to UID lists and create mapping. */
-        $ids = $this->resolveIds($this->_selected, $options['ids'], empty($options['exists']) ? 1 : 2);
-
-        /* Add non-user settable cache fields. */
-        $cache_array[Horde_Imap_Client::FETCH_DOWNGRADED] = self::CACHE_DOWNGRADED;
-
-        /* Get the cached values. */
-        $data = $this->_cache->get($this->_selected, $ids->ids, array_values($cache_array), $mbox_ob->getStatus(Horde_Imap_Client::STATUS_UIDVALIDITY));
-
-        /* Build a list of what we still need. */
-        $map = array_flip($mbox_ob->map->map);
-        $sequence = $options['ids']->sequence;
-        foreach ($ids as $uid) {
-            $crit = clone $query;
-
-            if ($sequence) {
-                if (!isset($map[$uid])) {
-                    continue;
-                }
-                $entry_idx = $map[$uid];
-            } else {
-                $entry_idx = $uid;
-                unset($crit[Horde_Imap_Client::FETCH_UID]);
-            }
-
-            $entry = $ret->get($entry_idx);
-
-            if (isset($map[$uid])) {
-                $entry->setSeq($map[$uid]);
-                unset($crit[Horde_Imap_Client::FETCH_SEQ]);
-            }
-
-            $entry->setUid($uid);
-
-            foreach ($cache_array as $key => $cid) {
-                switch ($key) {
-                case Horde_Imap_Client::FETCH_DOWNGRADED:
-                    if (!empty($data[$uid][$cid])) {
-                        $entry->setDowngraded(true);
-                    }
-                    break;
-
-                case Horde_Imap_Client::FETCH_ENVELOPE:
-                    if (isset($data[$uid][$cid]) &&
-                        ($data[$uid][$cid] instanceof Horde_Imap_Client_Data_Envelope)) {
-                        $entry->setEnvelope($data[$uid][$cid]);
-                        unset($crit[$key]);
-                    }
-                    break;
-
-                case Horde_Imap_Client::FETCH_FLAGS:
-                    if (isset($data[$uid][$cid]) &&
-                        is_array($data[$uid][$cid])) {
-                        $entry->setFlags($data[$uid][$cid]);
-                        unset($crit[$key]);
-                    }
-                    break;
-
-                case Horde_Imap_Client::FETCH_HEADERS:
-                    foreach ($header_cache as $hkey => $hval) {
-                        if (isset($data[$uid][$cid][$hval])) {
-                            /* We have found a cached entry with the same
-                             * MD5 sum. */
-                            $entry->setHeaders($hkey, $data[$uid][$cid][$hval]);
-                            $crit->remove($key, $hkey);
-                        } else {
-                            $this->_temp['headers_caching'][$hkey] = $hval;
-                        }
-                    }
-                    break;
-
-                case Horde_Imap_Client::FETCH_IMAPDATE:
-                    if (isset($data[$uid][$cid]) &&
-                        ($data[$uid][$cid] instanceof Horde_Imap_Client_DateTime)) {
-                        $entry->setImapDate($data[$uid][$cid]);
-                        unset($crit[$key]);
-                    }
-                    break;
-
-                case Horde_Imap_Client::FETCH_SIZE:
-                    if (isset($data[$uid][$cid])) {
-                        $entry->setSize($data[$uid][$cid]);
-                        unset($crit[$key]);
-                    }
-                    break;
-
-                case Horde_Imap_Client::FETCH_STRUCTURE:
-                    if (isset($data[$uid][$cid]) &&
-                        ($data[$uid][$cid] instanceof Horde_Mime_Part)) {
-                        $entry->setStructure($data[$uid][$cid]);
-                        unset($crit[$key]);
-                    }
-                    break;
-                }
-            }
-
-            if (count($crit)) {
-                $sig = $crit->hash();
-                if (isset($new_query[$sig])) {
-                    $new_query[$sig]['i'][] = $entry_idx;
-                } else {
-                    $new_query[$sig] = array(
-                        'c' => $crit,
-                        'i' => array($entry_idx)
-                    );
-                }
-            }
-        }
-
-        $to_fetch = array();
-        foreach ($new_query as $val) {
-            $ids_ob = $this->getIdsOb(null, $sequence);
-            $ids_ob->duplicates = true;
-            $ids_ob->add($val['i']);
-            $to_fetch[] = array_merge($options, array(
-                '_query' => $val['c'],
-                'ids' => $ids_ob
-            ));
-        }
-
-        if (!empty($to_fetch)) {
-            $this->_fetch(is_null($cs_ret) ? $ret : $cs_ret, $to_fetch);
-        }
-
-        if (is_null($cs_ret)) {
-            return $ret;
-        }
-
-        /* If doing changedsince query, and all other data is cached, we still
-         * need to hit IMAP server to determine proper results set. */
-        if (empty($new_query)) {
-            $squery = new Horde_Imap_Client_Search_Query();
-            $squery->modseq($options['changedsince'] + 1);
-            $squery->ids($options['ids']);
-
-            $cs = $this->search($this->_selected, $squery, array(
-                'sequence' => $sequence
-            ));
-
-            foreach ($cs['match'] as $val) {
-                $entry = $ret->get($val);
-                if ($sequence) {
-                    $entry->setSeq($val);
-                } else {
-                    $entry->setUid($val);
-                }
-                $cs_ret[$val] = $entry;
-            }
-        } else {
-            foreach ($cs_ret as $key => $val) {
-                $val->merge($ret->get($key));
-            }
-        }
-
-        return $cs_ret;
-    }
-
-    /**
-     * Fetch message data.
-     *
-     * Fetch queries should be grouped in the $queries argument. Each value
-     * is an array of fetch options, with the fetch query stored in the
-     * '_query' parameter. IMPORTANT: All queries must have the same ID
-     * type (either sequence or UID).
-     *
-     * @param Horde_Imap_Client_Fetch_Results $results  Fetch results.
-     * @param array $queries                            The list of queries.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    abstract protected function _fetch(Horde_Imap_Client_Fetch_Results $results,
-                                       $queries);
-
-    /**
-     * Get the list of vanished messages (UIDs that have been expunged since a
-     * given mod-sequence value).
-     *
-     * @param mixed $mailbox   The mailbox to query. Either a
-     *                         Horde_Imap_Client_Mailbox object or a string
-     *                         (UTF-8).
-     * @param integer $modseq  Search for expunged messages after this
-     *                         mod-sequence value.
-     * @param array $opts      Additional options:
-     *   - ids: (Horde_Imap_Client_Ids)  Restrict to these UIDs.
-     *          DEFAULT: Returns full list of UIDs vanished (QRESYNC only).
-     *                   This option is REQUIRED for non-QRESYNC servers or
-     *                   else an empty list will be returned.
-     *
-     * @return Horde_Imap_Client_Ids  List of UIDs that have vanished.
-     *
-     * @throws Horde_Imap_Client_NoSupportExtension
-     */
-    public function vanished($mailbox, $modseq, array $opts = array())
-    {
-        $this->login();
-
-        $qresync = $this->queryCapability('QRESYNC');
-
-        if (empty($opts['ids'])) {
-            if (!$qresync) {
-                return $this->getIdsOb();
-            }
-            $opts['ids'] = $this->getIdsOb(Horde_Imap_Client_Ids::ALL);
-        } elseif ($opts['ids']->isEmpty()) {
-            return $this->getIdsOb();
-        } elseif ($opts['ids']->sequence) {
-            throw new InvalidArgumentException('Vanished requires UIDs.');
-        }
-
-        $this->openMailbox($mailbox, Horde_Imap_Client::OPEN_AUTO);
-
-        if ($qresync) {
-            if (!$this->_mailboxOb()->getStatus(Horde_Imap_Client::STATUS_HIGHESTMODSEQ)) {
-                throw new Horde_Imap_Client_Exception(
-                    Horde_Imap_Client_Translation::t("Mailbox does not support mod-sequences."),
-                    Horde_Imap_Client_Exception::MBOXNOMODSEQ
-                );
-            }
-
-            return $this->_vanished(max(1, $modseq), $opts['ids']);
-        }
-
-        $ids = $this->resolveIds($mailbox, $opts['ids']);
-
-        $squery = new Horde_Imap_Client_Search_Query();
-        $squery->ids($this->getIdsOb($ids->range_string));
-        $search = $this->search($mailbox, $squery, array(
-            'nocache' => true
-        ));
-
-        return $this->getIdsOb(array_diff($ids->ids, $search['match']->ids));
-    }
-
-    /**
-     * Get the list of vanished messages.
-     *
-     * @param integer $modseq             Mod-sequence value.
-     * @param Horde_Imap_Client_Ids $ids  UIDs.
-     *
-     * @return Horde_Imap_Client_Ids  List of UIDs that have vanished.
-     */
-    abstract protected function _vanished($modseq, Horde_Imap_Client_Ids $ids);
-
-    /**
-     * Store message flag data (see RFC 3501 [6.4.6]).
-     *
-     * @param mixed $mailbox  The mailbox containing the messages to modify.
-     *                        Either a Horde_Imap_Client_Mailbox object or a
-     *                        string (UTF-8).
-     * @param array $options  Additional options:
-     *   - add: (array) An array of flags to add.
-     *          DEFAULT: No flags added.
-     *   - ids: (Horde_Imap_Client_Ids) The list of messages to modify.
-     *          DEFAULT: All messages in $mailbox will be modified.
-     *   - remove: (array) An array of flags to remove.
-     *             DEFAULT: No flags removed.
-     *   - replace: (array) Replace the current flags with this set
-     *              of flags. Overrides both the 'add' and 'remove' options.
-     *              DEFAULT: No replace is performed.
-     *   - unchangedsince: (integer) Only changes flags if the mod-sequence ID
-     *                     of the message is equal or less than this value.
-     *                     Requires the CONDSTORE IMAP extension on the server.
-     *                     Also requires the mailbox to support mod-sequences.
-     *                     Will throw an exception if either condition is not
-     *                     met.
-     *                     DEFAULT: mod-sequence is ignored when applying
-     *                              changes
-     *
-     * @return Horde_Imap_Client_Ids  A Horde_Imap_Client_Ids object
-     *                                containing the list of IDs that failed
-     *                                the 'unchangedsince' test.
-     *
-     * @throws Horde_Imap_Client_Exception
-     * @throws Horde_Imap_Client_Exception_NoSupportExtension
-     */
-    public function store($mailbox, array $options = array())
-    {
-        // Open mailbox call will handle the login.
-        $this->openMailbox($mailbox, Horde_Imap_Client::OPEN_READWRITE);
-
-        /* SEARCHRES requires server support. */
-        if (empty($options['ids'])) {
-            $options['ids'] = $this->getIdsOb(Horde_Imap_Client_Ids::ALL);
-        } elseif ($options['ids']->isEmpty()) {
-            return $this->getIdsOb();
-        } elseif ($options['ids']->search_res &&
-                  !$this->queryCapability('SEARCHRES')) {
-            throw new Horde_Imap_Client_Exception_NoSupportExtension('SEARCHRES');
-        }
-
-        if (!empty($options['unchangedsince'])) {
-            if (!isset($this->_temp['enabled']['CONDSTORE'])) {
-                throw new Horde_Imap_Client_Exception_NoSupportExtension('CONDSTORE');
-            }
-
-            /* RFC 4551 [3.1] - trying to do a UNCHANGEDSINCE STORE on a
-             * mailbox that doesn't support it will return BAD. */
-            if (!$this->_mailboxOb()->getStatus(Horde_Imap_Client::STATUS_HIGHESTMODSEQ)) {
-                throw new Horde_Imap_Client_Exception(
-                    Horde_Imap_Client_Translation::t("Mailbox does not support mod-sequences."),
-                    Horde_Imap_Client_Exception::MBOXNOMODSEQ
-                );
-            }
-        }
-
-        return $this->_store($options);
-    }
-
-    /**
-     * Store message flag data.
-     *
-     * @param array $options  Additional options.
-     *
-     * @return Horde_Imap_Client_Ids  A Horde_Imap_Client_Ids object
-     *                                containing the list of IDs that failed
-     *                                the 'unchangedsince' test.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    abstract protected function _store($options);
-
-    /**
-     * Copy messages to another mailbox.
-     *
-     * @param mixed $source   The source mailbox. Either a
-     *                        Horde_Imap_Client_Mailbox object or a string
-     *                        (UTF-8).
-     * @param mixed $dest     The destination mailbox. Either a
-     *                        Horde_Imap_Client_Mailbox object or a string
-     *                        (UTF-8).
-     * @param array $options  Additional options:
-     *   - create: (boolean) Try to create $dest if it does not exist?
-     *             DEFAULT: No.
-     *   - ids: (Horde_Imap_Client_Ids) The list of messages to copy.
-     *          DEFAULT: All messages in $mailbox will be copied.
-     *   - move: (boolean) If true, delete the original messages.
-     *           DEFAULT: Original messages are not deleted.
-     *
-     * @return mixed  An array mapping old UIDs (keys) to new UIDs (values) on
-     *                success (if the IMAP server and/or driver support the
-     *                UIDPLUS extension) or true.
-     *
-     * @throws Horde_Imap_Client_Exception
-     * @throws Horde_Imap_Client_Exception_NoSupportExtension
-     */
-    public function copy($source, $dest, array $options = array())
-    {
-        // Open mailbox call will handle the login.
-        $this->openMailbox($source, empty($options['move']) ? Horde_Imap_Client::OPEN_AUTO : Horde_Imap_Client::OPEN_READWRITE);
-
-        /* SEARCHRES requires server support. */
-        if (empty($options['ids'])) {
-            $options['ids'] = $this->getIdsOb(Horde_Imap_Client_Ids::ALL);
-        } elseif ($options['ids']->isEmpty()) {
-            return array();
-        } elseif ($options['ids']->search_res &&
-                  !$this->queryCapability('SEARCHRES')) {
-            throw new Horde_Imap_Client_Exception_NoSupportExtension('SEARCHRES');
-        }
-
-        return $this->_copy(Horde_Imap_Client_Mailbox::get($dest), $options);
-    }
-
-    /**
-     * Copy messages to another mailbox.
-     *
-     * @param Horde_Imap_Client_Mailbox $dest  The destination mailbox.
-     * @param array $options                   Additional options.
-     *
-     * @return mixed  An array mapping old UIDs (keys) to new UIDs (values) on
-     *                success (if the IMAP server and/or driver support the
-     *                UIDPLUS extension) or true.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    abstract protected function _copy(Horde_Imap_Client_Mailbox $dest,
-                                      $options);
-
-    /**
-     * Set quota limits. The server must support the IMAP QUOTA extension
-     * (RFC 2087).
-     *
-     * @param mixed $root       The quota root. Either a
-     *                          Horde_Imap_Client_Mailbox object or a string
-     *                          (UTF-8).
-     * @param array $resources  The resource values to set. Keys are the
-     *                          resource atom name; value is the resource
-     *                          value.
-     *
-     * @throws Horde_Imap_Client_Exception
-     * @throws Horde_Imap_Client_Exception_NoSupportExtension
-     */
-    public function setQuota($root, array $resources = array())
-    {
-        $this->login();
-
-        if (!$this->queryCapability('QUOTA')) {
-            throw new Horde_Imap_Client_Exception_NoSupportExtension('QUOTA');
-        }
-
-        if (!empty($resources)) {
-            $this->_setQuota(Horde_Imap_Client_Mailbox::get($root), $resources);
-        }
-    }
-
-    /**
-     * Set quota limits.
-     *
-     * @param Horde_Imap_Client_Mailbox $root  The quota root.
-     * @param array $resources                 The resource values to set.
-     *
-     * @return boolean  True on success.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    abstract protected function _setQuota(Horde_Imap_Client_Mailbox $root,
-                                          $resources);
-
-    /**
-     * Get quota limits. The server must support the IMAP QUOTA extension
-     * (RFC 2087).
-     *
-     * @param mixed $root  The quota root. Either a Horde_Imap_Client_Mailbox
-     *                     object or a string (UTF-8).
-     *
-     * @return mixed  An array with resource keys. Each key holds an array
-     *                with 2 values: 'limit' and 'usage'.
-     *
-     * @throws Horde_Imap_Client_Exception
-     * @throws Horde_Imap_Client_Exception_NoSupportExtension
-     */
-    public function getQuota($root)
-    {
-        $this->login();
-
-        if (!$this->queryCapability('QUOTA')) {
-            throw new Horde_Imap_Client_Exception_NoSupportExtension('QUOTA');
-        }
-
-        return $this->_getQuota(Horde_Imap_Client_Mailbox::get($root));
-    }
-
-    /**
-     * Get quota limits.
-     *
-     * @param Horde_Imap_Client_Mailbox $root  The quota root.
-     *
-     * @return mixed  An array with resource keys. Each key holds an array
-     *                with 2 values: 'limit' and 'usage'.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    abstract protected function _getQuota(Horde_Imap_Client_Mailbox $root);
-
-    /**
-     * Get quota limits for a mailbox. The server must support the IMAP QUOTA
-     * extension (RFC 2087).
-     *
-     * @param mixed $mailbox  A mailbox. Either a Horde_Imap_Client_Mailbox
-     *                        object or a string (UTF-8).
-     *
-     * @return mixed  An array with the keys being the quota roots. Each key
-     *                holds an array with resource keys: each of these keys
-     *                holds an array with 2 values: 'limit' and 'usage'.
-     *
-     * @throws Horde_Imap_Client_Exception
-     * @throws Horde_Imap_Client_Exception_NoSupportExtension
-     */
-    public function getQuotaRoot($mailbox)
-    {
-        $this->login();
-
-        if (!$this->queryCapability('QUOTA')) {
-            throw new Horde_Imap_Client_Exception_NoSupportExtension('QUOTA');
-        }
-
-        return $this->_getQuotaRoot(Horde_Imap_Client_Mailbox::get($mailbox));
-    }
-
-    /**
-     * Get quota limits for a mailbox.
-     *
-     * @param Horde_Imap_Client_Mailbox $mailbox  A mailbox.
-     *
-     * @return mixed  An array with the keys being the quota roots. Each key
-     *                holds an array with resource keys: each of these keys
-     *                holds an array with 2 values: 'limit' and 'usage'.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    abstract protected function _getQuotaRoot(Horde_Imap_Client_Mailbox $mailbox);
-
-    /**
-     * Get the ACL rights for a given mailbox. The server must support the
-     * IMAP ACL extension (RFC 2086/4314).
-     *
-     * @param mixed $mailbox  A mailbox. Either a Horde_Imap_Client_Mailbox
-     *                        object or a string (UTF-8).
-     *
-     * @return array  An array with identifiers as the keys and
-     *                Horde_Imap_Client_Data_Acl objects as the values.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function getACL($mailbox)
-    {
-        $this->login();
-        return $this->_getACL(Horde_Imap_Client_Mailbox::get($mailbox));
-    }
-
-    /**
-     * Get ACL rights for a given mailbox.
-     *
-     * @param Horde_Imap_Client_Mailbox $mailbox  A mailbox.
-     *
-     * @return array  An array with identifiers as the keys and
-     *                Horde_Imap_Client_Data_Acl objects as the values.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    abstract protected function _getACL(Horde_Imap_Client_Mailbox $mailbox);
-
-    /**
-     * Set ACL rights for a given mailbox/identifier.
-     *
-     * @param mixed $mailbox      A mailbox. Either a Horde_Imap_Client_Mailbox
-     *                            object or a string (UTF-8).
-     * @param string $identifier  The identifier to alter (UTF-8).
-     * @param array $options      Additional options:
-     *   - rights: (string) The rights to alter or set.
-     *   - action: (string, optional) If 'add' or 'remove', adds or removes the
-     *             specified rights. Sets the rights otherwise.
-     *
-     * @throws Horde_Imap_Client_Exception
-     * @throws Horde_Imap_Client_Exception_NoSupportExtension
-     */
-    public function setACL($mailbox, $identifier, $options)
-    {
-        $this->login();
-
-        if (!$this->queryCapability('ACL')) {
-            throw new Horde_Imap_Client_Exception_NoSupportExtension('ACL');
-        }
-
-        if (empty($options['rights'])) {
-            if (!isset($options['action']) ||
-                ($options['action'] != 'add' && $options['action'] != 'remove')) {
-                $this->_deleteACL(
-                    Horde_Imap_Client_Mailbox::get($mailbox),
-                    Horde_Imap_Client_Utf7imap::Utf8ToUtf7Imap($identifier)
-                );
-            }
-            return;
-        }
-
-        $acl = ($options['rights'] instanceof Horde_Imap_Client_Data_Acl)
-            ? $options['rights']
-            : new Horde_Imap_Client_Data_Acl(strval($options['rights']));
-
-        $options['rights'] = $acl->getString(
-            $this->queryCapability('RIGHTS')
-                ? Horde_Imap_Client_Data_AclCommon::RFC_4314
-                : Horde_Imap_Client_Data_AclCommon::RFC_2086
-        );
-        if (isset($options['action'])) {
-            switch ($options['action']) {
-            case 'add':
-                $options['rights'] = '+' . $options['rights'];
-                break;
-            case 'remove':
-                $options['rights'] = '-' . $options['rights'];
-                break;
-            }
-        }
-
-        $this->_setACL(
-            Horde_Imap_Client_Mailbox::get($mailbox),
-            Horde_Imap_Client_Utf7imap::Utf8ToUtf7Imap($identifier),
-            $options
-        );
-    }
-
-    /**
-     * Set ACL rights for a given mailbox/identifier.
-     *
-     * @param Horde_Imap_Client_Mailbox $mailbox  A mailbox.
-     * @param string $identifier                  The identifier to alter
-     *                                            (UTF7-IMAP).
-     * @param array $options                      Additional options. 'rights'
-     *                                            contains the string of
-     *                                            rights to set on the server.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    abstract protected function _setACL(Horde_Imap_Client_Mailbox $mailbox,
-                                        $identifier, $options);
-
-    /**
-     * Deletes ACL rights for a given mailbox/identifier.
-     *
-     * @param mixed $mailbox      A mailbox. Either a Horde_Imap_Client_Mailbox
-     *                            object or a string (UTF-8).
-     * @param string $identifier  The identifier to delete (UTF-8).
-     *
-     * @throws Horde_Imap_Client_Exception
-     * @throws Horde_Imap_Client_Exception_NoSupportExtension
-     */
-    public function deleteACL($mailbox, $identifier)
-    {
-        $this->login();
-
-        if (!$this->queryCapability('ACL')) {
-            throw new Horde_Imap_Client_Exception_NoSupportExtension('ACL');
-        }
-
-        $this->_deleteACL(
-            Horde_Imap_Client_Mailbox::get($mailbox),
-            Horde_Imap_Client_Utf7imap::Utf8ToUtf7Imap($identifier)
-        );
-    }
-
-    /**
-     * Deletes ACL rights for a given mailbox/identifier.
-     *
-     * @param Horde_Imap_Client_Mailbox $mailbox  A mailbox.
-     * @param string $identifier                  The identifier to delete
-     *                                            (UTF7-IMAP).
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    abstract protected function _deleteACL(Horde_Imap_Client_Mailbox $mailbox,
-                                           $identifier);
-
-    /**
-     * List the ACL rights for a given mailbox/identifier. The server must
-     * support the IMAP ACL extension (RFC 2086/4314).
-     *
-     * @param mixed $mailbox      A mailbox. Either a Horde_Imap_Client_Mailbox
-     *                            object or a string (UTF-8).
-     * @param string $identifier  The identifier to query (UTF-8).
-     *
-     * @return Horde_Imap_Client_Data_AclRights  An ACL data rights object.
-     *
-     * @throws Horde_Imap_Client_Exception
-     * @throws Horde_Imap_Client_Exception_NoSupportExtension
-     */
-    public function listACLRights($mailbox, $identifier)
-    {
-        $this->login();
-
-        if (!$this->queryCapability('ACL')) {
-            throw new Horde_Imap_Client_Exception_NoSupportExtension('ACL');
-        }
-
-        return $this->_listACLRights(
-            Horde_Imap_Client_Mailbox::get($mailbox),
-            Horde_Imap_Client_Utf7imap::Utf8ToUtf7Imap($identifier)
-        );
-    }
-
-    /**
-     * Get ACL rights for a given mailbox/identifier.
-     *
-     * @param Horde_Imap_Client_Mailbox $mailbox  A mailbox.
-     * @param string $identifier                  The identifier to query
-     *                                            (UTF7-IMAP).
-     *
-     * @return Horde_Imap_Client_Data_AclRights  An ACL data rights object.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    abstract protected function _listACLRights(Horde_Imap_Client_Mailbox $mailbox,
-                                               $identifier);
-
-    /**
-     * Get the ACL rights for the current user for a given mailbox. The
-     * server must support the IMAP ACL extension (RFC 2086/4314).
-     *
-     * @param mixed $mailbox  A mailbox. Either a Horde_Imap_Client_Mailbox
-     *                        object or a string (UTF-8).
-     *
-     * @return Horde_Imap_Client_Data_Acl  An ACL data object.
-     *
-     * @throws Horde_Imap_Client_Exception
-     * @throws Horde_Imap_Client_Exception_NoSupportExtension
-     */
-    public function getMyACLRights($mailbox)
-    {
-        $this->login();
-
-        if (!$this->queryCapability('ACL')) {
-            throw new Horde_Imap_Client_Exception_NoSupportExtension('ACL');
-        }
-
-        return $this->_getMyACLRights(Horde_Imap_Client_Mailbox::get($mailbox));
-    }
-
-    /**
-     * Get the ACL rights for the current user for a given mailbox.
-     *
-     * @param Horde_Imap_Client_Mailbox $mailbox  A mailbox.
-     *
-     * @return Horde_Imap_Client_Data_Acl  An ACL data object.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    abstract protected function _getMyACLRights(Horde_Imap_Client_Mailbox $mailbox);
-
-    /**
-     * Return master list of ACL rights available on the server.
-     *
-     * @return array  A list of ACL rights.
-     */
-    public function allAclRights()
-    {
-        $this->login();
-
-        $rights = array(
-            Horde_Imap_Client::ACL_LOOKUP,
-            Horde_Imap_Client::ACL_READ,
-            Horde_Imap_Client::ACL_SEEN,
-            Horde_Imap_Client::ACL_WRITE,
-            Horde_Imap_Client::ACL_INSERT,
-            Horde_Imap_Client::ACL_POST,
-            Horde_Imap_Client::ACL_ADMINISTER
-        );
-
-        if ($capability = $this->queryCapability('RIGHTS')) {
-            // Add rights defined in CAPABILITY string (RFC 4314).
-            return array_merge($rights, str_split(reset($capability)));
-        }
-
-        // Add RFC 2086 rights (deprecated by RFC 4314, but need to keep for
-        // compatibility with old servers).
-        return array_merge($rights, array(
-            Horde_Imap_Client::ACL_CREATE,
-            Horde_Imap_Client::ACL_DELETE
-        ));
-    }
-
-    /**
-     * Get metadata for a given mailbox. The server must support either the
-     * IMAP METADATA extension (RFC 5464) or the ANNOTATEMORE extension
-     * (http://ietfreport.isoc.org/idref/draft-daboo-imap-annotatemore/).
-     *
-     * @param mixed $mailbox  A mailbox. Either a Horde_Imap_Client_Mailbox
-     *                        object or a string (UTF-8).
-     * @param array $entries  The entries to fetch (UTF-8 strings).
-     * @param array $options  Additional options:
-     *   - depth: (string) Either "0", "1" or "infinity". Returns only the
-     *            given value (0), only values one level below the specified
-     *            value (1) or all entries below the specified value
-     *            (infinity).
-     *   - maxsize: (integer) The maximal size the returned values may have.
-     *              DEFAULT: No maximal size.
-     *
-     * @return array  An array with metadata names as the keys and metadata
-     *                values as the values. If 'maxsize' is set, and entries
-     *                exist on the server larger than this size, the size will
-     *                be returned in the key '*longentries'.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function getMetadata($mailbox, $entries, array $options = array())
-    {
-        $this->login();
-
-        if (!is_array($entries)) {
-            $entries = array($entries);
-        }
-
-        return $this->_getMetadata(Horde_Imap_Client_Mailbox::get($mailbox), array_map(array('Horde_Imap_Client_Utf7imap', 'Utf8ToUtf7Imap'), $entries), $options);
-    }
-
-    /**
-     * Get metadata for a given mailbox.
-     *
-     * @param Horde_Imap_Client_Mailbox $mailbox  A mailbox.
-     * @param array $entries                      The entries to fetch
-     *                                            (UTF7-IMAP strings).
-     * @param array $options                      Additional options.
-     *
-     * @return array  An array with metadata names as the keys and metadata
-     *                values as the values.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    abstract protected function _getMetadata(Horde_Imap_Client_Mailbox $mailbox,
-                                             $entries, $options);
-
-    /**
-     * Set metadata for a given mailbox/identifier.
-     *
-     * @param mixed $mailbox  A mailbox. Either a Horde_Imap_Client_Mailbox
-     *                        object or a string (UTF-8). If empty, sets a
-     *                        server annotation.
-     * @param array $data     A set of data values. The metadata values
-     *                        corresponding to the keys of the array will
-     *                        be set to the values in the array.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function setMetadata($mailbox, $data)
-    {
-        $this->login();
-        $this->_setMetadata(Horde_Imap_Client_Mailbox::get($mailbox), $data);
-    }
-
-    /**
-     * Set metadata for a given mailbox/identifier.
-     *
-     * @param Horde_Imap_Client_Mailbox $mailbox  A mailbox.
-     * @param array $data                         A set of data values. See
-     *                                            setMetadata() for format.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    abstract protected function _setMetadata(Horde_Imap_Client_Mailbox $mailbox,
-                                             $data);
-
-    /* Public utility functions. */
-
-    /**
-     * Returns a unique identifier for the current mailbox status.
-     *
-     * @deprecated
-     *
-     * @param mixed $mailbox  A mailbox. Either a Horde_Imap_Client_Mailbox
-     *                        object or a string (UTF-8).
-     * @param array $addl     Additional cache info to add to the cache ID
-     *                        string.
-     *
-     * @return string  The cache ID string, which will change when the
-     *                 composition of the mailbox changes. The uidvalidity
-     *                 will always be the first element, and will be delimited
-     *                 by the '|' character.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function getCacheId($mailbox, array $addl = array())
-    {
-        return Horde_Imap_Client_Base_Deprecated::getCacheId($this, $mailbox, isset($this->_temp['enabled']['CONDSTORE']), $addl);
-    }
-
-    /**
-     * Parses a cacheID created by getCacheId().
-     *
-     * @deprecated
-     *
-     * @param string $id  The cache ID.
-     *
-     * @return array  An array with the following information:
-     *   - highestmodseq: (integer)
-     *   - messages: (integer)
-     *   - uidnext: (integer)
-     *   - uidvalidity: (integer) Always present
-     */
-    public function parseCacheId($id)
-    {
-        return Horde_Imap_Client_Base_Deprecated::parseCacheId($id);
-    }
-
-    /**
-     * Resolves an IDs object into a list of IDs.
-     *
-     * @param Horde_Imap_Client_Mailbox $mailbox  The mailbox.
-     * @param Horde_Imap_Client_Ids $ids          The Ids object.
-     * @param boolean $convert                    Convert to UIDs?
-     *   - 0: No
-     *   - 1: Only if $ids is not already a UIDs object
-     *   - 2: Always
-     *
-     * @return Horde_Imap_Client_Ids  The list of IDs.
-     */
-    public function resolveIds(Horde_Imap_Client_Mailbox $mailbox,
-                               Horde_Imap_Client_Ids $ids, $convert = 0)
-    {
-        $map = $this->_mailboxOb($mailbox)->map;
-
-        if ($ids->special) {
-            /* Optimization for ALL sequence searches. */
-            if (!$convert && $ids->all && $ids->sequence) {
-                $res = $this->status($mailbox, Horde_Imap_Client::STATUS_MESSAGES);
-                return $this->getIdsOb($res['messages'] ? ('1:' . $res['messages']) : array(), true);
-            }
-
-            $convert = 2;
-        } elseif (!$convert || (!$ids->sequence && ($convert == 1))) {
-            return clone $ids;
-        } else {
-            /* Do an all or nothing: either we have all the numbers/UIDs in
-             * memory and can return, or just send the whole ID query to the
-             * server. Any advantage we would get by a partial search are
-             * outweighed by the complexities needed to make the search and
-             * then merge back into the original results. */
-            $lookup = $map->lookup($ids);
-            if (count($lookup) == count($ids)) {
-                return $this->getIdsOb(array_values($lookup));
-            }
-        }
-
-        $query = new Horde_Imap_Client_Search_Query();
-        $query->ids($ids);
-
-        $res = $this->search($mailbox, $query, array(
-            'results' => array(
-                Horde_Imap_Client::SEARCH_RESULTS_MATCH,
-                Horde_Imap_Client::SEARCH_RESULTS_SAVE
-            ),
-            'sequence' => (!$convert && $ids->sequence),
-            'sort' => array(Horde_Imap_Client::SORT_SEQUENCE)
-        ));
-
-        /* Update mapping. */
-        if ($convert) {
-            if ($ids->all) {
-                $ids = $this->getIdsOb('1:' . count($res['match']));
-            } elseif ($ids->special) {
-                return $res['match'];
-            }
-
-            $map->update(array_combine($ids->ids, $res['match']->ids));
-        }
-
-        return $res['match'];
-    }
-
-    /**
-     * Determines if the given charset is valid for search-related queries.
-     * This check pertains just to the basic IMAP SEARCH command.
-     *
-     * @param string $charset  The query charset.
-     *
-     * @return boolean  True if server supports this charset.
-     */
-    public function validSearchCharset($charset)
-    {
-        $charset = strtoupper($charset);
-
-        if ($charset == 'US-ASCII') {
-            return true;
-        }
-
-        if (!isset($this->_init['s_charset'][$charset])) {
-            $s_charset = $this->_init['s_charset'];
-
-            /* Use a dummy search query and search for BADCHARSET response. */
-            $query = new Horde_Imap_Client_Search_Query();
-            $query->charset($charset, false);
-            $query->ids($this->getIdsOb(1, true));
-            $query->text('a');
-            try {
-                $this->search('INBOX', $query, array(
-                    'nocache' => true,
-                    'sequence' => true
-                ));
-                $s_charset[$charset] = true;
-            } catch (Horde_Imap_Client_Exception $e) {
-                $s_charset[$charset] = ($e->getCode() != Horde_Imap_Client_Exception::BADCHARSET);
-            }
-
-            $this->_setInit('s_charset', $s_charset);
-        }
-
-        return $this->_init['s_charset'][$charset];
-    }
-
-    /* Mailbox syncing functions. */
-
-    /**
-     * Returns a unique token for the current mailbox synchronization status.
-     *
-     * @since 2.2.0
-     *
-     * @param mixed $mailbox  A mailbox. Either a Horde_Imap_Client_Mailbox
-     *                        object or a string (UTF-8).
-     *
-     * @return string  The sync token.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function getSyncToken($mailbox)
-    {
-        $out = array();
-
-        foreach ($this->_syncStatus($mailbox) as $key => $val) {
-            $out[] = $key . $val;
-        }
-
-        return base64_encode(implode(',', $out));
-    }
-
-    /**
-     * Synchronize a mailbox from a sync token.
-     *
-     * @since 2.2.0
-     *
-     * @param mixed $mailbox  A mailbox. Either a Horde_Imap_Client_Mailbox
-     *                        object or a string (UTF-8).
-     * @param string $token   A sync token generated by getSyncToken().
-     * @param array $opts     Additional options:
-     *   - criteria: (integer) Mask of Horde_Imap_Client::SYNC_* criteria to
-     *               return. Defaults to SYNC_ALL.
-     *   - ids: (Horde_Imap_Client_Ids) A cached list of UIDs. Unless QRESYNC
-     *          is available on the server, failure to specify this option
-     *          means SYNC_VANISHEDUIDS information cannot be returned.
-     *
-     * @return Horde_Imap_Client_Data_Sync  A sync object.
-     *
-     * @throws Horde_Imap_Client_Exception
-     * @throws Horde_Imap_Client_Exception_Sync
-     */
-    public function sync($mailbox, $token, array $opts = array())
-    {
-        if (($token = base64_decode($token, true)) === false) {
-            throw new Horde_Imap_Client_Exception_Sync('Bad token.', Horde_Imap_Client_Exception_Sync::BAD_TOKEN);
-        }
-
-        $sync = array();
-        foreach (explode(',', $token) as $val) {
-            $sync[substr($val, 0, 1)] = substr($val, 1);
-        }
-
-        return new Horde_Imap_Client_Data_Sync(
-            $this,
-            $mailbox,
-            $sync,
-            $this->_syncStatus($mailbox),
-            (isset($opts['criteria']) ? $opts['criteria'] : Horde_Imap_Client::SYNC_ALL),
-            (isset($opts['ids']) ? $opts['ids'] : null)
-        );
-    }
-
-    /* Private utility functions. */
-
-    /**
-     * Store FETCH data in cache.
-     *
-     * @param Horde_Imap_Client_Fetch_Results $data  The fetch results.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    protected function _updateCache(Horde_Imap_Client_Fetch_Results $data)
-    {
-        if (!empty($this->_temp['fetch_nocache']) ||
-            empty($this->_selected) ||
-            !count($data) ||
-            !$this->_initCache(true)) {
-            return;
-        }
-
-        $c = $this->getParam('cache');
-        if (in_array(strval($this->_selected), $c['fetch_ignore'])) {
-            $this->_debug->info(sprintf("CACHE: Ignoring FETCH data (mailbox: %s)", $this->_selected));
-            return;
-        }
-
-        /* Optimization: we can directly use getStatus() here since we know
-         * these values are initialized. */
-        $mbox_ob = $this->_mailboxOb();
-        $highestmodseq = $mbox_ob->getStatus(Horde_Imap_Client::STATUS_HIGHESTMODSEQ);
-        $uidvalidity = $mbox_ob->getStatus(Horde_Imap_Client::STATUS_UIDVALIDITY);
-
-        $mapping = $modseq = $tocache = array();
-        if (count($data)) {
-            $cf = $this->_cacheFields();
-        }
-
-        foreach ($data as $v) {
-            /* It is possible that we received FETCH information that doesn't
-             * contain UID data. This is uncacheable so don't process. */
-            if (!($uid = $v->getUid())) {
-                return;
-            }
-
-            $tmp = array();
-
-            if ($v->isDowngraded()) {
-                $tmp[self::CACHE_DOWNGRADED] = true;
-            }
-
-            foreach ($cf as $key => $val) {
-                if ($v->exists($key)) {
-                    switch ($key) {
-                    case Horde_Imap_Client::FETCH_ENVELOPE:
-                        $tmp[$val] = $v->getEnvelope();
-                        break;
-
-                    case Horde_Imap_Client::FETCH_FLAGS:
-                        if ($highestmodseq) {
-                            $modseq[$uid] = $v->getModSeq();
-                            $tmp[$val] = $v->getFlags();
-                        }
-                        break;
-
-                    case Horde_Imap_Client::FETCH_HEADERS:
-                        foreach ($this->_temp['headers_caching'] as $label => $hash) {
-                            if ($hdr = $v->getHeaders($label)) {
-                                $tmp[$val][$hash] = $hdr;
-                            }
-                        }
-                        break;
-
-                    case Horde_Imap_Client::FETCH_IMAPDATE:
-                        $tmp[$val] = $v->getImapDate();
-                        break;
-
-                    case Horde_Imap_Client::FETCH_SIZE:
-                        $tmp[$val] = $v->getSize();
-                        break;
-
-                    case Horde_Imap_Client::FETCH_STRUCTURE:
-                        $tmp[$val] = clone $v->getStructure();
-                        break;
-                    }
-                }
-            }
-
-            if (!empty($tmp)) {
-                $tocache[$uid] = $tmp;
-            }
-
-            $mapping[$v->getSeq()] = $uid;
-        }
-
-        if (!empty($mapping)) {
-            if (!empty($tocache)) {
-                $this->_cache->set($this->_selected, $tocache, $uidvalidity);
-            }
-
-            $this->_mailboxOb()->map->update($mapping);
-        }
-
-        if (!empty($modseq)) {
-            $this->_updateModSeq(max(array_merge($modseq, array($highestmodseq))));
-            $mbox_ob->setStatus(Horde_Imap_Client::STATUS_SYNCFLAGUIDS, array_keys($modseq));
-        }
-    }
-
-    /**
-     * Moves cache entries from the current mailbox to another mailbox.
-     *
-     * @param Horde_Imap_Client_Mailbox $to  The destination mailbox.
-     * @param array $map                     Mapping of source UIDs (keys) to
-     *                                       destination UIDs (values).
-     * @param string $uidvalid               UIDVALIDITY of destination
-     *                                       mailbox.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    protected function _moveCache(Horde_Imap_Client_Mailbox $to, $map,
-                                  $uidvalid)
-    {
-        if (!$this->_initCache()) {
-            return;
-        }
-
-        $c = $this->getParam('cache');
-        if (in_array(strval($to), $c['fetch_ignore'])) {
-            $this->_debug->info(sprintf("CACHE: Ignoring moving FETCH data (%s => %s)", $this->_selected, $to));
-            return;
-        }
-
-        $old = $this->_cache->get($this->_selected, array_keys($map), null);
-        $new = array();
-
-        foreach ($map as $key => $val) {
-            if (!empty($old[$key])) {
-                $new[$val] = $old[$key];
-            }
-        }
-
-        if (!empty($new)) {
-            $this->_cache->set($to, $new, $uidvalid);
-        }
-    }
-
-    /**
-     * Delete messages in the cache.
-     *
-     * @param Horde_Imap_Client_Mailbox $mailbox  The mailbox.
-     * @param Horde_Imap_Client_Ids $ids          The list of IDs to delete in
-     *                                            $mailbox.
-     * @param array $opts                         Additional options (not used
-     *                                            in base class).
-     *
-     * @return Horde_Imap_Client_Ids  UIDs that were deleted.
-     * @throws Horde_Imap_Client_Exception
-     */
-    protected function _deleteMsgs(Horde_Imap_Client_Mailbox $mailbox,
-                                   Horde_Imap_Client_Ids $ids,
-                                   array $opts = array())
-    {
-        if (!$this->_initCache()) {
-            return $ids;
-        }
-
-        $mbox_ob = $this->_mailboxOb();
-        $ids_ob = $ids->sequence
-            ? $this->getIdsOb($mbox_ob->map->lookup($ids))
-            : $ids;
-
-        $this->_cache->deleteMsgs($mailbox, $ids_ob->ids);
-        $mbox_ob->setStatus(Horde_Imap_Client::STATUS_SYNCVANISHED, $ids_ob->ids);
-        $mbox_ob->map->remove($ids);
-
-        return $ids_ob;
-    }
-
-    /**
-     * Retrieve data from the search cache.
-     *
-     * @param string $type    The cache type ('search' or 'thread').
-     * @param array $options  The options array of the calling function.
-     *
-     * @return mixed  Returns search cache metadata. If search was retrieved,
-     *                data is in key 'data'.
-     *                Returns null if caching is not available.
-     */
-    protected function _getSearchCache($type, $options)
-    {
-        $status = $this->status($this->_selected, Horde_Imap_Client::STATUS_HIGHESTMODSEQ | Horde_Imap_Client::STATUS_UIDVALIDITY);
-
-        /* Search caching requires MODSEQ, which may not be active for a
-         * mailbox. */
-        if (empty($status['highestmodseq'])) {
-            return null;
-        }
-
-        ksort($options);
-        $cache = hash('md5', $type . serialize($options));
-        $cacheid = $this->getCacheId($this->_selected);
-        $ret = array();
-
-        $md = $this->_cache->getMetaData(
-            $this->_selected,
-            $status['uidvalidity'],
-            array(self::CACHE_SEARCH, self::CACHE_SEARCHID)
-        );
-
-        if (!isset($md[self::CACHE_SEARCHID]) ||
-            ($md[self::CACHE_SEARCHID] != $cacheid)) {
-            $md[self::CACHE_SEARCH] = array();
-            $md[self::CACHE_SEARCHID] = $cacheid;
-            if ($this->_debug->debug &&
-                !isset($this->_temp['searchcacheexpire'][strval($this->_selected)])) {
-                $this->_debug->info(sprintf("SEARCH: Expired from cache (mailbox: %s)", $this->_selected));
-                $this->_temp['searchcacheexpire'][strval($this->_selected)] = true;
-            }
-        } elseif (isset($md[self::CACHE_SEARCH][$cache])) {
-            $this->_debug->info(sprintf("SEARCH: Retrieved %s from cache (mailbox: %s; id: %s)", $type, $this->_selected, $cache));
-            $ret['data'] = $md[self::CACHE_SEARCH][$cache];
-            unset($md[self::CACHE_SEARCHID]);
-        }
-
-        return array_merge($ret, array(
-            'id' => $cache,
-            'metadata' => $md,
-            'type' => $type
-        ));
-    }
-
-    /**
-     * Set data in the search cache.
-     *
-     * @param mixed $data    The cache data to store.
-     * @param string $sdata  The search data returned from _getSearchCache().
-     */
-    protected function _setSearchCache($data, $sdata)
-    {
-        $sdata['metadata'][self::CACHE_SEARCH][$sdata['id']] = $data;
-
-        $this->_cache->setMetaData($this->_selected, null, $sdata['metadata']);
-
-        if ($this->_debug->debug) {
-            $this->_debug->info(sprintf("SEARCH: Saved %s to cache (mailbox: %s; id: %s)", $sdata['type'], $this->_selected, $sdata['id']));
-            unset($this->_temp['searchcacheexpire'][strval($this->_selected)]);
-        }
-    }
-
-    /**
-     * Updates the cached MODSEQ value.
-     *
-     * @param integer $modseq  MODSEQ value to store.
-     *
-     * @return mixed  The MODSEQ of the old value if it was replaced (or false
-     *                if it didn't exist or is the same).
-     */
-    protected function _updateModSeq($modseq)
-    {
-        if (!$this->_initCache(true)) {
-            return false;
-        }
-
-        $mbox_ob = $this->_mailboxOb();
-        $uidvalid = $mbox_ob->getStatus(Horde_Imap_Client::STATUS_UIDVALIDITY);
-        $md = $this->_cache->getMetaData($this->_selected, $uidvalid, array(self::CACHE_MODSEQ));
-
-        if (isset($md[self::CACHE_MODSEQ])) {
-            if ($md[self::CACHE_MODSEQ] < $modseq) {
-                $set = true;
-                $sync = $md[self::CACHE_MODSEQ];
-            } else {
-                $set = false;
-                $sync = 0;
-            }
-            $mbox_ob->setStatus(Horde_Imap_Client::STATUS_SYNCMODSEQ, $md[self::CACHE_MODSEQ]);
-        } else {
-            $set = true;
-            $sync = 0;
-        }
-
-        if ($set) {
-            $this->_cache->setMetaData($this->_selected, $uidvalid, array(
-                self::CACHE_MODSEQ => $modseq
-            ));
-        }
-
-        return $sync;
-    }
-
-    /**
-     * Synchronizes the current mailbox cache with the server (using CONDSTORE
-     * or QRESYNC).
-     */
-    protected function _condstoreSync()
-    {
-        $mbox_ob = $this->_mailboxOb();
-
-        /* Check that modseqs are available in mailbox. */
-        if (!($highestmodseq = $mbox_ob->getStatus(Horde_Imap_Client::STATUS_HIGHESTMODSEQ)) ||
-            !($modseq = $this->_updateModSeq($highestmodseq))) {
-            $mbox_ob->sync = true;
-        }
-
-        if ($mbox_ob->sync) {
-            return;
-        }
-
-        $uids_ob = $this->getIdsOb($this->_cache->get($this->_selected, array(), array(), $mbox_ob->getStatus(Horde_Imap_Client::STATUS_UIDVALIDITY)));
-
-        /* Are we caching flags? */
-        if (array_key_exists(Horde_Imap_Client::FETCH_FLAGS, $this->_cacheFields())) {
-            $fquery = new Horde_Imap_Client_Fetch_Query();
-            $fquery->flags();
-
-            /* Update flags in cache. Cache will be updated in _fetch(). */
-            $this->_fetch(new Horde_Imap_Client_Fetch_Results(), array(
-                array(
-                    '_query' => $fquery,
-                    'changedsince' => $modseq,
-                    'ids' => $uids_ob
-                )
-            ));
-        }
-
-        /* Search for deleted messages, and remove from cache. */
-        $vanished = $this->vanished($this->_selected, $modseq, $uids_ob);
-        $disappear = array_diff($uids_ob->ids, $vanished->ids);
-        if (!empty($disappear)) {
-            $this->_deleteMsgs($this->_selected, $this->getIdsOb($disappear));
-        }
-
-        $mbox_ob->sync = true;
-    }
-
-    /**
-     * Provide the list of available caching fields.
-     *
-     * @return array  The list of available caching fields (fields are in the
-     *                key).
-     */
-    protected function _cacheFields()
-    {
-        $c = $this->getParam('cache');
-        $out = $c['fields'];
-
-        if (!isset($this->_temp['enabled']['CONDSTORE'])) {
-            unset($out[Horde_Imap_Client::FETCH_FLAGS]);
-        }
-
-        return $out;
-    }
-
-    /**
-     * Return the current mailbox synchronization status.
-     *
-     * @param mixed $mailbox  A mailbox. Either a Horde_Imap_Client_Mailbox
-     *                        object or a string (UTF-8).
-     *
-     * @return array  An array with status data. (This data is not guaranteed
-     *                to have any specific format).
-     */
-    protected function _syncStatus($mailbox)
-    {
-        $status = $this->status(
-            $mailbox,
-            Horde_Imap_Client::STATUS_HIGHESTMODSEQ |
-            Horde_Imap_Client::STATUS_MESSAGES |
-            Horde_Imap_Client::STATUS_UIDNEXT_FORCE |
-            Horde_Imap_Client::STATUS_UIDVALIDITY
-        );
-
-        $fields = array('uidnext', 'uidvalidity');
-        if (empty($status['highestmodseq'])) {
-            $fields[] = 'messages';
-        } else {
-            $fields[] = 'highestmodseq';
-        }
-
-        $out = array();
-        $sync_map = array_flip(Horde_Imap_Client_Data_Sync::$map);
-
-        foreach ($fields as $val) {
-            $out[$sync_map[$val]] = $status[$val];
-        }
-
-        return array_filter($out);
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientCacheBackendCachephp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Cache/Backend/Cache.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Cache/Backend/Cache.php  2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Cache/Backend/Cache.php     2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,498 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2005-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2005-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * A Horde_Cache implementation for caching IMAP/POP data.
- * Requires the Horde_Cache package.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2005-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Cache_Backend_Cache extends Horde_Imap_Client_Cache_Backend
-{
-    /** Cache structure version. */
-    const VERSION = 3;
-
-    /**
-     * The cache object.
-     *
-     * @var Horde_Cache
-     */
-    protected $_cache;
-
-    /**
-     * The working data for the current pageload.  All changes take place to
-     * this data.
-     *
-     * @var array
-     */
-    protected $_data = array();
-
-    /**
-     * The list of cache slices loaded.
-     *
-     * @var array
-     */
-    protected $_loaded = array();
-
-    /**
-     * The mapping of UIDs to slices.
-     *
-     * @var array
-     */
-    protected $_slicemap = array();
-
-    /**
-     * The list of items to update:
-     *   - add: (array) List of IDs that were added.
-     *   - slice: (array) List of slices that were modified.
-     *   - slicemap: (boolean) Was slicemap info changed?
-     *
-     * @var array
-     */
-    protected $_update = array();
-
-    /**
-     * Constructor.
-     *
-     * @param array $params  Configuration parameters:
-     * <ul>
-     *  <li>
-     *   REQUIRED Parameters:
-     *   <ul>
-     *    <li>
-     *     cacheob: (Horde_Cache) [REQUIRED] The cache object to use.
-     *    </li>
-     *   </ul>
-     *  </li>
-     *  <li>
-     *   Optional Parameters:
-     *   <ul>
-     *    <li>
-     *     lifetime: (integer) The lifetime of the cache data (in seconds).
-     *               DEFAULT: 1 week (604800 seconds)
-     *    </li>
-     *    <li>
-     *     slicesize: (integer) The slicesize to use.
-     *                DEFAULT: 50
-     *    </li>
-     *   </ul>
-     *  </li>
-     * </ul>
-     */
-    public function __construct(array $params = array())
-    {
-        // Default parameters.
-        $params = array_merge(array(
-            'lifetime' => 604800,
-            'slicesize' => 50
-        ), array_filter($params));
-
-        if (!isset($params['cacheob'])) {
-            throw new InvalidArgumentException('Missing cacheob parameter.');
-        }
-
-        foreach (array('lifetime', 'slicesize') as $val) {
-            $params[$val] = intval($params[$val]);
-        }
-
-        parent::__construct($params);
-    }
-
-    /**
-     * Initialization tasks.
-     */
-    protected function _initOb()
-    {
-        $this->_cache = $this->_params['cacheob'];
-        register_shutdown_function(array($this, 'save'));
-    }
-
-    /**
-     * Updates the cache.
-     */
-    public function save()
-    {
-        $lifetime = $this->_params['lifetime'];
-
-        foreach ($this->_update as $mbox => $val) {
-            $s = &$this->_slicemap[$mbox];
-
-            if (!empty($val['add'])) {
-                if ($s['c'] <= $this->_params['slicesize']) {
-                    $val['slice'][] = $s['i'];
-                    $this->_loadSlice($mbox, $s['i']);
-                }
-                $val['slicemap'] = true;
-
-                foreach (array_keys(array_flip($val['add'])) as $uid) {
-                    if ($s['c']++ > $this->_params['slicesize']) {
-                        $s['c'] = 0;
-                        $val['slice'][] = ++$s['i'];
-                        $this->_loadSlice($mbox, $s['i']);
-                    }
-                    $s['s'][$uid] = $s['i'];
-                }
-            }
-
-            if (!empty($val['slice'])) {
-                $d = &$this->_data[$mbox];
-                $val['slicemap'] = true;
-
-                foreach (array_keys(array_flip($val['slice'])) as $slice) {
-                    $data = array();
-                    foreach (array_keys($s['s'], $slice) as $uid) {
-                        $data[$uid] = is_array($d[$uid])
-                            ? serialize($d[$uid])
-                            : $d[$uid];
-                    }
-                    $this->_cache->set($this->_getCid($mbox, $slice), serialize($data), $lifetime);
-                }
-            }
-
-            if (!empty($val['slicemap'])) {
-                $this->_cache->set($this->_getCid($mbox, 'slicemap'), serialize($s), $lifetime);
-            }
-        }
-
-        $this->_update = array();
-    }
-
-    /**
-     */
-    public function get($mailbox, $uids, $fields, $uidvalid)
-    {
-        $ret = array();
-        $this->_loadUids($mailbox, $uids, $uidvalid);
-
-        if (empty($this->_data[$mailbox])) {
-            return $ret;
-        }
-
-        if (!empty($fields)) {
-            $fields = array_flip($fields);
-        }
-        $ptr = &$this->_data[$mailbox];
-
-        foreach (array_intersect($uids, array_keys($ptr)) as $val) {
-            if (is_string($ptr[$val])) {
-                $ptr[$val] = @unserialize($ptr[$val]);
-            }
-
-            $ret[$val] = (empty($fields) || empty($ptr[$val]))
-                ? $ptr[$val]
-                : array_intersect_key($ptr[$val], $fields);
-        }
-
-        return $ret;
-    }
-
-    /**
-     */
-    public function getCachedUids($mailbox, $uidvalid)
-    {
-        $this->_loadSliceMap($mailbox, $uidvalid);
-        return array_unique(array_merge(
-            array_keys($this->_slicemap[$mailbox]['s']),
-            (isset($this->_update[$mailbox]) ? $this->_update[$mailbox]['add'] : array())
-        ));
-    }
-
-    /**
-     */
-    public function set($mailbox, $data, $uidvalid)
-    {
-        $update = array_keys($data);
-
-        try {
-            $this->_loadUids($mailbox, $update, $uidvalid);
-        } catch (Horde_Imap_Client_Exception $e) {
-            // Ignore invalidity - just start building the new cache
-        }
-
-        $d = &$this->_data[$mailbox];
-        $s = &$this->_slicemap[$mailbox]['s'];
-        $add = $updated = array();
-
-        foreach ($data as $k => $v) {
-            if (isset($d[$k])) {
-                if (is_string($d[$k])) {
-                    $d[$k] = @unserialize($d[$k]);
-                }
-                $d[$k] = is_array($d[$k])
-                    ? array_merge($d[$k], $v)
-                    : $v;
-                if (isset($s[$k])) {
-                    $updated[$s[$k]] = true;
-                }
-            } else {
-                $d[$k] = $v;
-                $add[] = $k;
-            }
-        }
-
-        $this->_toUpdate($mailbox, 'add', $add);
-        $this->_toUpdate($mailbox, 'slice', array_keys($updated));
-    }
-
-    /**
-     */
-    public function getMetaData($mailbox, $uidvalid, $entries)
-    {
-        $this->_loadSliceMap($mailbox, $uidvalid);
-
-        return empty($entries)
-            ? $this->_slicemap[$mailbox]['d']
-            : array_intersect_key($this->_slicemap[$mailbox]['d'], array_flip($entries));
-    }
-
-    /**
-     */
-    public function setMetaData($mailbox, $data)
-    {
-        $this->_loadSliceMap($mailbox, isset($data['uidvalid']) ? $data['uidvalid'] : null);
-        $this->_slicemap[$mailbox]['d'] = array_merge($this->_slicemap[$mailbox]['d'], $data);
-        $this->_toUpdate($mailbox, 'slicemap', true);
-    }
-
-    /**
-     */
-    public function deleteMsgs($mailbox, $uids)
-    {
-        $slicemap = &$this->_slicemap[$mailbox];
-        $deleted = array_intersect_key($slicemap['s'], array_flip($uids));
-
-        if (isset($this->_update[$mailbox])) {
-            $this->_update[$mailbox]['add'] = array_diff(
-                $this->_update[$mailbox]['add'],
-                $uids
-            );
-        }
-
-        if (empty($deleted)) {
-            return;
-        }
-
-        $this->_loadUids($mailbox, array_keys($deleted));
-        $d = &$this->_data[$mailbox];
-
-        foreach (array_keys($deleted) as $id) {
-            unset($d[$id], $slicemap['s'][$id]);
-        }
-
-        foreach (array_unique($deleted) as $slice) {
-            /* Get rid of slice if less than 10% of capacity. */
-            if (($slice != $slicemap['i']) &&
-                ($slice_uids = array_keys($slicemap['s'], $slice)) &&
-                ($this->_params['slicesize'] * 0.1) > count($slice_uids)) {
-                $this->_toUpdate($mailbox, 'add', $slice_uids);
-                $this->_cache->expire($this->_getCid($mailbox, $slice));
-                foreach ($slice_uids as $val) {
-                    unset($slicemap['s'][$val]);
-                }
-            } else {
-                $this->_toUpdate($mailbox, 'slice', array($slice));
-            }
-        }
-    }
-
-    /**
-     */
-    public function deleteMailbox($mailbox)
-    {
-        $this->_loadSliceMap($mailbox);
-        $this->_deleteMailbox($mailbox);
-    }
-
-    /**
-     */
-    public function clear($lifetime)
-    {
-        $this->_cache->clear();
-        $this->_data = $this->_loaded = $this->_slicemap = $this->_update = array();
-    }
-
-    /**
-     * Create the unique ID used to store the data in the cache.
-     *
-     * @param string $mailbox  The mailbox to cache.
-     * @param string $slice    The cache slice.
-     *
-     * @return string  The cache ID.
-     */
-    protected function _getCid($mailbox, $slice)
-    {
-        return implode('|', array(
-            'horde_imap_client',
-            $this->_params['username'],
-            $mailbox,
-            $this->_params['hostspec'],
-            $this->_params['port'],
-            $slice,
-            self::VERSION
-        ));
-    }
-
-    /**
-     * Delete a mailbox from the cache.
-     *
-     * @param string $mbox  The mailbox to delete.
-     */
-    protected function _deleteMailbox($mbox)
-    {
-        foreach (array_merge(array_keys(array_flip($this->_slicemap[$mbox]['s'])), array('slicemap')) as $slice) {
-            $cid = $this->_getCid($mbox, $slice);
-            $this->_cache->expire($cid);
-            unset($this->_loaded[$cid]);
-        }
-
-        unset(
-            $this->_data[$mbox],
-            $this->_slicemap[$mbox],
-            $this->_update[$mbox]
-        );
-    }
-
-    /**
-     * Load UIDs by regenerating from the cache.
-     *
-     * @param string $mailbox    The mailbox to load.
-     * @param array $uids        The UIDs to load.
-     * @param integer $uidvalid  The IMAP uidvalidity value of the mailbox.
-     */
-    protected function _loadUids($mailbox, $uids, $uidvalid = null)
-    {
-        if (!isset($this->_data[$mailbox])) {
-            $this->_data[$mailbox] = array();
-        }
-
-        $this->_loadSliceMap($mailbox, $uidvalid);
-
-        if (!empty($uids)) {
-            foreach (array_unique(array_intersect_key($this->_slicemap[$mailbox]['s'], array_flip($uids))) as $slice) {
-                $this->_loadSlice($mailbox, $slice);
-            }
-        }
-    }
-
-    /**
-     * Load UIDs from a cache slice.
-     *
-     * @param string $mailbox  The mailbox to load.
-     * @param integer $slice   The slice to load.
-     */
-    protected function _loadSlice($mailbox, $slice)
-    {
-        $cache_id = $this->_getCid($mailbox, $slice);
-
-        if (!empty($this->_loaded[$cache_id])) {
-            return;
-        }
-
-        if ((($data = $this->_cache->get($cache_id, 0)) !== false) &&
-            ($data = @unserialize($data)) &&
-            is_array($data)) {
-            $this->_data[$mailbox] += $data;
-            $this->_loaded[$cache_id] = true;
-        } else {
-            $ptr = &$this->_slicemap[$mailbox];
-
-            // Slice data is corrupt; remove from slicemap.
-            foreach (array_keys($ptr['s'], $slice) as $val) {
-                unset($ptr['s'][$val]);
-            }
-
-            if ($slice == $ptr['i']) {
-                $ptr['c'] = 0;
-            }
-        }
-    }
-
-    /**
-     * Load the slicemap for a given mailbox.  The slicemap contains
-     * the uidvalidity information, the UIDs->slice lookup table, and any
-     * metadata that needs to be saved for the mailbox.
-     *
-     * @param string $mailbox    The mailbox.
-     * @param integer $uidvalid  The IMAP uidvalidity value of the mailbox.
-     */
-    protected function _loadSliceMap($mailbox, $uidvalid = null)
-    {
-        if (!isset($this->_slicemap[$mailbox]) &&
-            (($data = $this->_cache->get($this->_getCid($mailbox, 'slicemap'), 0)) !== false) &&
-            ($slice = @unserialize($data)) &&
-            is_array($slice)) {
-            $this->_slicemap[$mailbox] = $slice;
-        }
-
-        if (isset($this->_slicemap[$mailbox])) {
-            $ptr = &$this->_slicemap[$mailbox];
-            if (is_null($ptr['d']['uidvalid'])) {
-                $ptr['d']['uidvalid'] = $uidvalid;
-                return;
-            } elseif (!is_null($uidvalid) &&
-                      ($ptr['d']['uidvalid'] != $uidvalid)) {
-                $this->_deleteMailbox($mailbox);
-            } else {
-                return;
-            }
-        }
-
-        $this->_slicemap[$mailbox] = array(
-            // Tracking count for purposes of determining slices
-            'c' => 0,
-            // Metadata storage
-            // By default includes UIDVALIDITY of mailbox.
-            'd' => array('uidvalid' => $uidvalid),
-            // The ID of the last slice.
-            'i' => 0,
-            // The slice list.
-            's' => array()
-        );
-    }
-
-    /**
-     * Add update entry for a mailbox.
-     *
-     * @param string $mailbox  The mailbox.
-     * @param string $type     'add', 'slice', or 'slicemap'.
-     * @param mixed $data      The data to update.
-     */
-    protected function _toUpdate($mailbox, $type, $data)
-    {
-        if (!isset($this->_update[$mailbox])) {
-            $this->_update[$mailbox] = array(
-                'add' => array(),
-                'slice' => array()
-            );
-        }
-
-        $this->_update[$mailbox][$type] = ($type == 'slicemap')
-            ? $data
-            : array_merge($this->_update[$mailbox][$type], $data);
-    }
-
-    /* Serializable methods. */
-
-    /**
-     */
-    public function serialize()
-    {
-        $this->save();
-        return parent::serialize();
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientCacheBackendDbphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Cache/Backend/Db.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Cache/Backend/Db.php     2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Cache/Backend/Db.php        2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,407 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * A SQL database implementation for caching IMAP/POP data.
- * Requires the Horde_Db package.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Cache_Backend_Db extends Horde_Imap_Client_Cache_Backend
-{
-    /* Table names. */
-    const BASE_TABLE = 'horde_imap_client_data';
-    const MD_TABLE = 'horde_imap_client_metadata';
-    const MSG_TABLE = 'horde_imap_client_message';
-
-    /**
-     * Handle for the database connection.
-     *
-     * @var Horde_Db_Adapter
-     */
-    protected $_db;
-
-    /**
-     * Constructor.
-     *
-     * @param array $params  Configuration parameters:
-     * <ul>
-     *  <li>
-     *   REQUIRED Parameters:
-     *   <ul>
-     *    <li>
-     *     db: (Horde_Db_Adapter) DB object.
-     *    </li>
-     *   </ul>
-     *  </li>
-     * </ul>
-     */
-    public function __construct(array $params = array())
-    {
-        if (!isset($params['db'])) {
-            throw new InvalidArgumentException('Missing db parameter.');
-        }
-
-        parent::__construct($params);
-    }
-
-    /**
-     */
-    protected function _initOb()
-    {
-        $this->_db = $this->_params['db'];
-    }
-
-    /**
-     */
-    public function get($mailbox, $uids, $fields, $uidvalid)
-    {
-        $this->getMetaData($mailbox, $uidvalid, array('uidvalid'));
-
-        $query = $this->_baseSql($mailbox, self::MSG_TABLE);
-        $query[0] = 'SELECT t.data, t.msguid ' . $query[0];
-
-        $uid_query = array();
-        foreach ($uids as $val) {
-            $uid_query[] = 't.msguid = ?';
-            $query[1][] = strval($val);
-        }
-        $query[0] .= ' AND (' . implode(' OR ', $uid_query) . ')';
-
-        $compress = new Horde_Compress_Fast();
-        $out = array();
-
-        try {
-            $columns = $this->_db->columns(self::MSG_TABLE);
-            $res = $this->_db->select($query[0], $query[1]);
-
-            while (($row = $res->fetchObject()) !== false) {
-                $out[$row->msguid] = @unserialize($compress->decompress(
-                    $columns['data']->binaryToString($row->data)
-                ));
-            }
-        } catch (Horde_Db_Exception $e) {}
-
-        return $out;
-    }
-
-    /**
-     */
-    public function getCachedUids($mailbox, $uidvalid)
-    {
-        $this->getMetaData($mailbox, $uidvalid, array('uidvalid'));
-
-        $query = $this->_baseSql($mailbox, self::MSG_TABLE);
-        $query[0] = 'SELECT DISTINCT t.msguid ' . $query[0];
-
-        try {
-            return $this->_db->selectValues($query[0], $query[1]);
-        } catch (Horde_Db_Exception $e) {
-            return array();
-        }
-    }
-
-    /**
-     */
-    public function set($mailbox, $data, $uidvalid)
-    {
-        if ($uid = $this->_getUid($mailbox)) {
-            $res = $this->get($mailbox, array_keys($data), array(), $uidvalid);
-        } else {
-            $res = array();
-            $uid = $this->_createUid($mailbox);
-        }
-
-        $compress = new Horde_Compress_Fast();
-
-        foreach ($data as $key => $val) {
-            if (isset($res[$key])) {
-                try {
-                    /* Update */
-                    $this->_db->update(
-                        sprintf('UPDATE %s SET data = ? WHERE uid = ? AND msguid = ?', self::MSG_TABLE),
-                        array(
-                            new Horde_Db_Value_Binary($compress->compress(serialize(array_merge($res[$key], $val)))),
-                            $uid,
-                            strval($key)
-                        )
-                    );
-                } catch (Horde_Db_Exception $e) {}
-            } else {
-                /* Insert */
-                try {
-                    $this->_db->insert(
-                        sprintf('INSERT INTO %s (data, msguid, uid) VALUES (?, ?, ?)', self::MSG_TABLE),
-                        array(
-                            new Horde_Db_Value_Binary($compress->compress(serialize($val))),
-                            strval($key),
-                            $uid
-                        )
-                    );
-                } catch (Horde_Db_Exception $e) {}
-            }
-        }
-
-        /* Update modified time. */
-        try {
-            $this->_db->update(
-                sprintf(
-                    'UPDATE %s SET modified = ? WHERE uid = ?',
-                    self::BASE_TABLE
-                ),
-                array(time(), $uid)
-            );
-        } catch (Horde_Db_Exception $e) {}
-
-        /* Update uidvalidity. */
-        $this->setMetaData($mailbox, array('uidvalid' => $uidvalid));
-    }
-
-    /**
-     */
-    public function getMetaData($mailbox, $uidvalid, $entries)
-    {
-        $query = $this->_baseSql($mailbox, self::MD_TABLE);
-        $query[0] = 'SELECT t.field, t.data ' . $query[0];
-
-        if (!empty($entries)) {
-            $entries[] = 'uidvalid';
-            $entry_query = array();
-
-            foreach (array_unique($entries) as $val) {
-                $entry_query[] = 't.field = ?';
-                $query[1][] = $val;
-            }
-            $query[0] .= ' AND (' . implode(' OR ', $entry_query) . ')';
-        }
-
-        try {
-            if ($res = $this->_db->selectAssoc($query[0], $query[1])) {
-                $columns = $this->_db->columns(self::MD_TABLE);
-                foreach ($res as $key => $val) {
-                    switch ($key) {
-                    case 'uidvalid':
-                        $res[$key] = $columns['data']->binaryToString($val);
-                        break;
-
-                    default:
-                        $res[$key] = @unserialize(
-                            $columns['data']->binaryToString($val)
-                        );
-                        break;
-                    }
-                }
-
-                if (is_null($uidvalid) ||
-                    !isset($res['uidvalid']) ||
-                    ($res['uidvalid'] == $uidvalid)) {
-                    return $res;
-                }
-
-                $this->deleteMailbox($mailbox);
-            }
-        } catch (Horde_Db_Exception $e) {}
-
-        return array();
-    }
-
-    /**
-     */
-    public function setMetaData($mailbox, $data)
-    {
-        if (!($uid = $this->_getUid($mailbox))) {
-            $uid = $this->_createUid($mailbox);
-        }
-
-        $query = sprintf('SELECT field FROM %s where uid = ?', self::MD_TABLE);
-        $values = array($uid);
-
-        try {
-            $fields = $this->_db->selectValues($query, $values);
-        } catch (Horde_Db_Exception $e) {
-            return;
-        }
-
-        foreach ($data as $key => $val) {
-            $val = new Horde_Db_Value_Binary(($key == 'uidvalid') ? $val : serialize($val));
-
-            if (in_array($key, $fields)) {
-                /* Update */
-                try {
-                    $this->_db->update(
-                        sprintf(
-                            'UPDATE %s SET data = ? WHERE field = ? AND uid = ?',
-                            self::MD_TABLE
-                        ),
-                        array($val, $key, $uid)
-                    );
-                } catch (Horde_Db_Exception $e) {}
-            } else {
-                /* Insert */
-                try {
-                    $this->_db->insert(
-                        sprintf(
-                            'INSERT INTO %s (data, field, uid) VALUES (?, ?, ?)',
-                            self::MD_TABLE
-                        ),
-                        array($val, $key, $uid)
-                    );
-                } catch (Horde_Db_Exception $e) {}
-            }
-        }
-    }
-
-    /**
-     */
-    public function deleteMsgs($mailbox, $uids)
-    {
-        $query = $this->_baseSql($mailbox);
-        $query[0] = sprintf(
-            'DELETE FROM %s WHERE uid IN (SELECT uid ' . $query[0] . ')',
-            self::MSG_TABLE
-        );
-
-        $uid_query = array();
-        foreach ($uids as $val) {
-            $uid_query[] = 'msguid = ?';
-            $query[1][] = strval($val);
-        }
-        $query[0] .= ' AND (' . implode(' OR ', $uid_query) . ')';
-
-        try {
-            $this->_db->delete($query[0], $query[1]);
-        } catch (Horde_Db_Exception $e) {}
-    }
-
-    /**
-     */
-    public function deleteMailbox($mailbox)
-    {
-        if (is_null($uid = $this->_getUid($mailbox))) {
-            return;
-        }
-
-        foreach (array(self::BASE_TABLE, self::MD_TABLE, self::MSG_TABLE) as $val) {
-            try {
-                $this->_db->delete(
-                    sprintf('DELETE FROM %s WHERE uid = ?', $val),
-                    array($uid)
-                );
-            } catch (Horde_Db_Exception $e) {}
-        }
-    }
-
-    /**
-     */
-    public function clear($lifetime)
-    {
-        if (is_null($lifetime)) {
-            try {
-                $this->_db->delete(sprintf('DELETE FROM %s', self::BASE_TABLE));
-                $this->_db->delete(sprintf('DELETE FROM %s', self::MD_TABLE));
-                $this->_db->delete(sprintf('DELETE FROM %s', self::MSG_TABLE));
-            } catch (Horde_Db_Exception $e) {}
-            return;
-        }
-
-        $purge = time() - $lifetime;
-        $sql = 'DELETE FROM %s WHERE uid IN (SELECT uid FROM %s WHERE modified < ?';
-
-        foreach (array(self::MD_TABLE, self::MSG_TABLE) as $val) {
-            try {
-                $this->_db->delete(
-                    sprintf($sql, $val, self::BASE_TABLE),
-                    array($purge)
-                );
-            } catch (Horde_Db_Exception $e) {}
-        }
-
-        try {
-            $this->_db->delete(
-                sprintf('DELETE FROM %s WHERE modified < ?', self::BASE_TABLE),
-                array($purge)
-            );
-        } catch (Horde_Db_Exception $e) {}
-    }
-
-    /**
-     * Prepare the base SQL query.
-     *
-     * @param string $mailbox  The mailbox.
-     * @param string $join     The table to join with the base table.
-     *
-     * @return array  SQL query and bound parameters.
-     */
-    protected function _baseSql($mailbox, $join = null)
-    {
-        $sql = sprintf('FROM %s d', self::BASE_TABLE);
-
-        if (!is_null($join)) {
-            $sql .= sprintf(' INNER JOIN %s t ON d.uid = t.uid', $join);
-        }
-
-        return array(
-            $sql . ' WHERE d.hostspec = ? AND d.port = ? AND d.username = ? AND d.mailbox = ?',
-            array(
-                $this->_params['hostspec'],
-                $this->_params['port'],
-                $this->_params['username'],
-                $mailbox
-            )
-        );
-    }
-
-    /**
-     * @param string $mailbox
-     *
-     * @return string  UID from base table.
-     */
-    protected function _getUid($mailbox)
-    {
-        $query = $this->_baseSql($mailbox);
-        $query[0] = 'SELECT d.uid ' . $query[0];
-
-        try {
-            return $this->_db->selectValue($query[0], $query[1]);
-        } catch (Horde_Db_Exception $e) {
-            return null;
-        }
-    }
-
-    /**
-     * @param string $mailbox
-     *
-     * @return string  UID from base table.
-     */
-    protected function _createUid($mailbox)
-    {
-        return $this->_db->insert(
-            sprintf(
-                'INSERT INTO %s (hostspec, mailbox, port, username) ' .
-                    'VALUES (?, ?, ?, ?)',
-                self::BASE_TABLE
-            ),
-            array(
-                $this->_params['hostspec'],
-                $mailbox,
-                $this->_params['port'],
-                $this->_params['username']
-            )
-        );
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientCacheBackendMongophp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Cache/Backend/Mongo.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Cache/Backend/Mongo.php  2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Cache/Backend/Mongo.php     2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,425 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * A MongoDB database implementation for caching IMAP/POP data.
- * Requires the Horde_Mongo class.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Cache_Backend_Mongo extends Horde_Imap_Client_Cache_Backend implements Horde_Mongo_Collection_Index
-{
-    /* Collection names. */
-    const BASE = 'horde_imap_client_cache_data';
-    const MD = 'horde_imap_client_cache_metadata';
-    const MSG = 'horde_imap_client_cache_message';
-
-    /**
-     * The MongoDB object for the cache data.
-     *
-     * @var MongoDB
-     */
-    protected $_db;
-
-    /**
-     * The list of indices.
-     *
-     * @var array
-     */
-    protected $_indices = array(
-        self::BASE => array(
-            'base_index_1' => array(
-                'hostspec' => 1,
-                'mailbox' => 1,
-                'port' => 1,
-                'username' => 1,
-            )
-        ),
-        self::MSG => array(
-            'msg_index_1' => array(
-                'uid' => 1,
-                'msguid' => 1
-            )
-        )
-    );
-
-    /**
-     * Constructor.
-     *
-     * @param array $params  Configuration parameters:
-     * <ul>
-     *  <li>
-     *   REQUIRED parameters:
-     *   <ul>
-     *    <li>
-     *     mongo_db: (Horde_Mongo_Client) A MongoDB client object.
-     *    </li>
-     *   </ul>
-     *  </li>
-     */
-    public function __construct(array $params = array())
-    {
-        if (!isset($params['mongo_db'])) {
-            throw new InvalidArgumentException('Missing mongo_db parameter.');
-        }
-
-        parent::__construct($params);
-    }
-
-    /**
-     */
-    protected function _initOb()
-    {
-        $this->_db = $this->_params['mongo_db']->selectDB(null);
-    }
-
-    /**
-     */
-    public function get($mailbox, $uids, $fields, $uidvalid)
-    {
-        $this->getMetaData($mailbox, $uidvalid, array('uidvalid'));
-
-        if (!($uid = $this->_getUid($mailbox))) {
-            return array();
-        }
-
-        $out = array();
-        $query = array(
-            'msguid' => array('$in' => array_map('strval', $uids)),
-            'uid' => $uid
-        );
-
-        try {
-            $cursor = $this->_db->selectCollection(self::MSG)->find(
-                $query,
-                array(
-                    'data' => true,
-                    'msguid' => true
-                )
-            );
-            foreach ($cursor as $val) {
-                $out[$val['msguid']] = $this->_value($val['data']);
-            }
-        } catch (MongoException $e) {}
-
-        return $out;
-    }
-
-    /**
-     */
-    public function getCachedUids($mailbox, $uidvalid)
-    {
-        $this->getMetaData($mailbox, $uidvalid, array('uidvalid'));
-
-        if (!($uid = $this->_getUid($mailbox))) {
-            return array();
-        }
-
-        $out = array();
-        $query = array(
-            'uid' => $uid
-        );
-
-        try {
-            $cursor = $this->_db->selectCollection(self::MSG)->find(
-                $query,
-                array(
-                    'msguid' => true
-                )
-            );
-            foreach ($cursor as $val) {
-                $out[] = $val['msguid'];
-            }
-        } catch (MongoException $e) {}
-
-        return $out;
-    }
-
-    /**
-     */
-    public function set($mailbox, $data, $uidvalid)
-    {
-        if ($uid = $this->_getUid($mailbox)) {
-            $res = $this->get($mailbox, array_keys($data), array(), $uidvalid);
-        } else {
-            $res = array();
-            $uid = $this->_createUid($mailbox);
-        }
-
-        $coll = $this->_db->selectCollection(self::MSG);
-
-        foreach ($data as $key => $val) {
-            try {
-                if (isset($res[$key])) {
-                    $coll->update(array(
-                        'msguid' => strval($key),
-                        'uid' => $uid
-                    ), array(
-                        'data' => $this->_value(array_merge($res[$key], $val)),
-                        'msguid' => strval($key),
-                        'uid' => $uid
-                    ));
-                } else {
-                    $coll->insert(array(
-                        'data' => $this->_value($val),
-                        'msguid' => strval($key),
-                        'uid' => $uid
-                    ));
-                }
-            } catch (MongoException $e) {}
-        }
-
-        /* Update modified time. */
-        try {
-            $this->_db->selectCollection(self::BASE)->update(
-                array('uid' => $uid),
-                array('modified' => time())
-            );
-        } catch (MongoException $e) {}
-
-        /* Update uidvalidity. */
-        $this->setMetaData($mailbox, array('uidvalid' => $uidvalid));
-    }
-
-    /**
-     */
-    public function getMetaData($mailbox, $uidvalid, $entries)
-    {
-        if (!($uid = $this->_getUid($mailbox))) {
-            return array();
-        }
-
-        $out = array();
-        $query = array(
-            'uid' => $uid
-        );
-
-        if (!empty($entries)) {
-            $entries[] = 'uidvalid';
-            $query['field'] = array(
-                '$in' => array_unique($entries)
-            );
-        }
-
-        try {
-            $cursor = $this->_db->selectCollection(self::MD)->find(
-                $query,
-                array(
-                    'data' => true,
-                    'field' => true
-                )
-            );
-            foreach ($cursor as $val) {
-                $out[$val['field']] = $this->_value($val['data']);
-            }
-
-            if (is_null($uidvalid) ||
-                !isset($out['uidvalid']) ||
-                ($out['uidvalid'] == $uidvalid)) {
-                return $out;
-            }
-
-            $this->deleteMailbox($mailbox);
-        } catch (Horde_Db_Exception $e) {}
-
-        return array();
-    }
-
-    /**
-     */
-    public function setMetaData($mailbox, $data)
-    {
-        if (!($uid = $this->_getUid($mailbox))) {
-            $uid = $this->_createUid($mailbox);
-        }
-
-        $coll = $this->_db->selectCollection(self::MD);
-
-        foreach ($data as $key => $val) {
-            try {
-                $coll->update(
-                    array(
-                        'field' => $key,
-                        'uid' => $uid
-                    ),
-                    array(
-                        'data' => $this->_value($val),
-                        'field' => $key,
-                        'uid' => $uid
-                    ),
-                    array('upsert' => true)
-                );
-            } catch (MongoException $e) {}
-        }
-    }
-
-    /**
-     */
-    public function deleteMsgs($mailbox, $uids)
-    {
-        if ($uid = $this->_getUid($mailbox)) {
-            try {
-                $this->_db->selectCollection(self::MSG)->remove(array(
-                    'msguid' => array('$in' => array_map('strval', $uids)),
-                    'uid' => $uid
-                ));
-            } catch (MongoException $e) {}
-        }
-    }
-
-    /**
-     */
-    public function deleteMailbox($mailbox)
-    {
-        if (!($uid = $this->_getUid($mailbox))) {
-            return;
-        }
-
-        foreach (array(self::BASE, self::MD, self::MSG) as $val) {
-            try {
-                $this->_db->selectCollection($val)->remove(array(
-                    'uid' => $uid
-                ));
-            } catch (MongoException $e) {}
-        }
-    }
-
-    /**
-     */
-    public function clear($lifetime)
-    {
-        if (is_null($lifetime)) {
-            foreach (array(self::BASE, self::MD, self::MSG) as $val) {
-                $this->_db->selectCollection($val)->drop();
-            }
-            return;
-        }
-
-        $query = array(
-            'modified' => array('$lt' => (time() - $lifetime))
-        );
-        $uids = array();
-
-        try {
-            $cursor = $this->_db->selectCollection(self::BASE)->find($query);
-            foreach ($cursor as $val) {
-                $uids[] = strval($val['_id']);
-            }
-        } catch (MongoException $e) {}
-
-        if (empty($uids)) {
-            return;
-        }
-
-        foreach (array(self::BASE, self::MD, self::MSG) as $val) {
-            try {
-                $this->_db->selectCollection($val)->remove(array(
-                    'uid' => array('$in' => $uids)
-                ));
-            } catch (MongoException $e) {}
-        }
-    }
-
-    /**
-     * Return the UID for a mailbox/user/server combo.
-     *
-     * @param string $mailbox  Mailbox name.
-     *
-     * @return string  UID from base table.
-     */
-    protected function _getUid($mailbox)
-    {
-        $query = array(
-            'hostspec' => $this->_params['hostspec'],
-            'mailbox' => $mailbox,
-            'port' => $this->_params['port'],
-            'username' => $this->_params['username']
-        );
-
-        try {
-            if ($result = $this->_db->selectCollection(self::BASE)->findOne($query)) {
-                return strval($result['_id']);
-            }
-        } catch (MongoException $e) {}
-
-        return null;
-    }
-
-    /**
-     * Create and return the UID for a mailbox/user/server combo.
-     *
-     * @param string $mailbox  Mailbox name.
-     *
-     * @return string  UID from base table.
-     */
-    protected function _createUid($mailbox)
-    {
-        $this->_db->selectCollection(self::BASE)->insert(array(
-            'hostspec' => $this->_params['hostspec'],
-            'mailbox' => $mailbox,
-            'port' => $this->_params['port'],
-            'username' => $this->_params['username']
-        ));
-
-        return $this->_getUid($mailbox);
-    }
-
-    /**
-     * Convert data from/to storage format.
-     *
-     * @param mixed|MongoBinData $data  The data object.
-     *
-     * @return mixed|MongoBinData  The converted data.
-     */
-    protected function _value($data)
-    {
-        static $compress;
-
-        if (!isset($compress)) {
-            $compress = new Horde_Compress_Fast();
-        }
-
-        return ($data instanceof MongoBinData)
-            ? @unserialize($compress->decompress($data->bin))
-            : new MongoBinData($compress->compress(serialize($data)), MongoBinData::BYTE_ARRAY);
-    }
-
-    /* Horde_Mongo_Collection_Index methods. */
-
-    /**
-     */
-    public function checkMongoIndices()
-    {
-        foreach ($this->_indices as $key => $val) {
-            if (!$this->_params['mongo_db']->checkIndices($key, $val)) {
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    /**
-     */
-    public function createMongoIndices()
-    {
-        foreach ($this->_indices as $key => $val) {
-            $this->_params['mongo_db']->createIndices($key, $val);
-        }
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientCacheBackendNullphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Cache/Backend/Null.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Cache/Backend/Null.php   2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Cache/Backend/Null.php      2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,78 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * A null backend class for storing cached IMAP/POP data.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Cache_Backend_Null extends Horde_Imap_Client_Cache_Backend
-{
-    /**
-     */
-    public function get($mailbox, $uids, $fields, $uidvalid)
-    {
-        return array();
-    }
-
-    /**
-     */
-    public function getCachedUids($mailbox, $uidvalid)
-    {
-        return array();
-    }
-
-    /**
-     */
-    public function set($mailbox, $data, $uidvalid)
-    {
-    }
-
-    /**
-     */
-    public function getMetaData($mailbox, $uidvalid, $entries)
-    {
-        return array(
-            'uidvalid' => 0
-        );
-    }
-
-    /**
-     */
-    public function setMetaData($mailbox, $data)
-    {
-    }
-
-    /**
-     */
-    public function deleteMsgs($mailbox, $uids)
-    {
-    }
-
-    /**
-     */
-    public function deleteMailbox($mailbox)
-    {
-    }
-
-    /**
-     */
-    public function clear($lifetime)
-    {
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientCacheBackendphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Cache/Backend.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Cache/Backend.php        2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Cache/Backend.php   2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,165 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * The abstract backend class for storing IMAP cached data.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-abstract class Horde_Imap_Client_Cache_Backend implements Serializable
-{
-    /**
-     * Configuration paramters.
-     * Values set by the base Cache object: hostspec, port, username
-     *
-     * @var array
-     */
-    protected $_params = array();
-
-    /**
-     * Constructor.
-     *
-     * @param array $params  Configuration parameters.
-     */
-    public function __construct(array $params = array())
-    {
-        $this->setParams($params);
-        $this->_initOb();
-    }
-
-    /**
-     * Initialization tasks.
-     */
-    protected function _initOb()
-    {
-    }
-
-    /**
-     * Add configuration parameters.
-     *
-     * @param array $params  Configuration parameters.
-     */
-    public function setParams(array $params = array())
-    {
-        $this->_params = array_merge($this->_params, $params);
-    }
-
-    /**
-     * Get information from the cache for a set of UIDs.
-     *
-     * @param string $mailbox    An IMAP mailbox string.
-     * @param array $uids        The list of message UIDs to retrieve
-     *                           information for.
-     * @param array $fields      An array of fields to retrieve. If empty,
-     *                           returns all cached fields.
-     * @param integer $uidvalid  The IMAP uidvalidity value of the mailbox.
-     *
-     * @return array  An array of arrays with the UID of the message as the
-     *                key (if found) and the fields as values (will be
-     *                undefined if not found).
-     */
-    abstract public function get($mailbox, $uids, $fields, $uidvalid);
-
-    /**
-     * Get the list of cached UIDs.
-     *
-     * @param string $mailbox    An IMAP mailbox string.
-     * @param integer $uidvalid  The IMAP uidvalidity value of the mailbox.
-     *
-     * @return array  The (unsorted) list of cached UIDs.
-     */
-    abstract public function getCachedUids($mailbox, $uidvalid);
-
-    /**
-     * Store data in cache.
-     *
-     * @param string $mailbox    An IMAP mailbox string.
-     * @param array $data        The list of data to save. The keys are the
-     *                           UIDs, the values are an array of information
-     *                           to save.
-     * @param integer $uidvalid  The IMAP uidvalidity value of the mailbox.
-     */
-    abstract public function set($mailbox, $data, $uidvalid);
-
-    /**
-     * Get metadata information for a mailbox.
-     *
-     * @param string $mailbox    An IMAP mailbox string.
-     * @param integer $uidvalid  The IMAP uidvalidity value of the mailbox.
-     * @param array $entries     An array of entries to return. If empty,
-     *                           returns all metadata.
-     *
-     * @return array  The requested metadata. Requested entries that do not
-     *                exist will be undefined. The following entries are
-     *                defaults and always present:
-     *   - uidvalid: (integer) The UIDVALIDITY of the mailbox.
-     */
-    abstract public function getMetaData($mailbox, $uidvalid, $entries);
-
-    /**
-     * Set metadata information for a mailbox.
-     *
-     * @param string $mailbox    An IMAP mailbox string.
-     * @param array $data        The list of data to save. The keys are the
-     *                           metadata IDs, the values are the associated
-     *                           data. (If present, uidvalidity appears as
-     *                           the 'uidvalid' key in $data.)
-     */
-    abstract public function setMetaData($mailbox, $data);
-
-    /**
-     * Delete messages in the cache.
-     *
-     * @param string $mailbox  An IMAP mailbox string.
-     * @param array $uids      The list of message UIDs to delete.
-     */
-    abstract public function deleteMsgs($mailbox, $uids);
-
-    /**
-     * Delete a mailbox from the cache.
-     *
-     * @param string $mailbox  The mailbox to delete.
-     */
-    abstract public function deleteMailbox($mailbox);
-
-    /**
-     * Clear the cache.
-     *
-     * @param integer $lifetime  Only delete entries older than this (in
-     *                           seconds). If null, deletes all entries.
-     */
-    abstract public function clear($lifetime);
-
-
-    /* Serializable methods. */
-
-    /**
-     */
-    public function serialize()
-    {
-        return serialize($this->_params);
-    }
-
-    /**
-     */
-    public function unserialize($data)
-    {
-        $this->_params = unserialize($data);
-        $this->_initOb();
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientCachephp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Cache.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Cache.php        2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Cache.php   2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,253 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2005-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2005-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * An interface to cache data retrieved from the IMAP server.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2005-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Cache
-{
-    /**
-     * Base client object.
-     *
-     * @var Horde_Imap_Client_Base
-     */
-    protected $_baseob;
-
-    /**
-     * Storage backend.
-     *
-     * @var Horde_Imap_Client_Cache_Backend
-     */
-    protected $_backend;
-
-    /**
-     * Debug output.
-     *
-     * @var Horde_Imap_Client_Base_Debug
-     */
-    protected $_debug = false;
-
-    /**
-     * The configuration params.
-     *
-     * @var array
-     */
-    protected $_params = array();
-
-    /**
-     * Constructor.
-     *
-     * @param array $params  Configuration parameters:
-     * <ul>
-     *  <li>
-     *   REQUIRED Parameters:
-     *   <ul>
-     *    <li>
-     *     backend: (Horde_Imap_Client_Cache_Backend) The cache backend.
-     *    </li>
-     *    <li>
-     *     baseob: (Horde_Imap_Client_Base) The base client object.
-     *    </li>
-     *   </ul>
-     *  </li>
-     *  <li>
-     *   Optional Parameters:
-     *   <ul>
-     *    <li>
-     *     debug: (Horde_Imap_Client_Base_Debug) Debug object.
-     *            DEFAULT: No debug output
-     *    </li>
-     *   </ul>
-     *  </li>
-     * </ul>
-     */
-    public function __construct(array $params = array())
-    {
-        $this->_backend = $params['backend'];
-        $this->_baseob = $params['baseob'];
-
-        $this->_backend->setParams(array(
-            'hostspec' => $this->_baseob->getParam('hostspec'),
-            'port' => $this->_baseob->getParam('port'),
-            'username' => $this->_baseob->getParam('username')
-        ));
-
-        if (isset($params['debug']) &&
-            ($params['debug'] instanceof Horde_Imap_Client_Base_Debug)) {
-            $this->_debug = $params['debug'];
-            $this->_debug->info(sprintf("CACHE: Using the %s storage driver.", get_class($this->_backend)));
-        }
-    }
-
-    /**
-     * Get information from the cache.
-     *
-     * @param string $mailbox    An IMAP mailbox string.
-     * @param array $uids        The list of message UIDs to retrieve
-     *                           information for. If empty, returns the list
-     *                           of cached UIDs.
-     * @param array $fields      An array of fields to retrieve. If empty,
-     *                           returns all cached fields.
-     * @param integer $uidvalid  The IMAP uidvalidity value of the mailbox.
-     *
-     * @return array  An array of arrays with the UID of the message as the
-     *                key (if found) and the fields as values (will be
-     *                undefined if not found). If $uids is empty, returns the
-     *                full (unsorted) list of cached UIDs.
-     */
-    public function get($mailbox, array $uids = array(), $fields = array(),
-                        $uidvalid = null)
-    {
-        $mailbox = strval($mailbox);
-
-        if (empty($uids)) {
-            $ret = $this->_backend->getCachedUids($mailbox, $uidvalid);
-        } else {
-            $ret = $this->_backend->get($mailbox, $uids, $fields, $uidvalid);
-
-            if ($this->_debug && !empty($ret)) {
-                $this->_debug->info('CACHE: Retrieved messages (mailbox: ' . $mailbox . '; UIDs: ' . $this->_baseob->getIdsOb(array_keys($ret))->tostring_sort . ")");
-            }
-        }
-
-        return $ret;
-    }
-
-    /**
-     * Store information in cache.
-     *
-     * @param string $mailbox    An IMAP mailbox string.
-     * @param array $data        The list of data to save. The keys are the
-     *                           UIDs, the values are an array of information
-     *                           to save. If empty, do a check to make sure
-     *                           the uidvalidity is still valid.
-     * @param integer $uidvalid  The IMAP uidvalidity value of the mailbox.
-     */
-    public function set($mailbox, $data, $uidvalid)
-    {
-        $mailbox = strval($mailbox);
-
-        if (empty($data)) {
-            $this->_backend->getMetaData($mailbox, $uidvalid, array('uidvalid'));
-        } else {
-            $this->_backend->set($mailbox, $data, $uidvalid);
-
-            if ($this->_debug) {
-                $this->_debug->info('CACHE: Stored messages (mailbox: ' . $mailbox . '; UIDs: ' . $this->_baseob->getIdsOb(array_keys($data))->tostring_sort . ")");
-            }
-        }
-    }
-
-    /**
-     * Get metadata information for a mailbox.
-     *
-     * @param string $mailbox    An IMAP mailbox string.
-     * @param integer $uidvalid  The IMAP uidvalidity value of the mailbox.
-     * @param array $entries     An array of entries to return. If empty,
-     *                           returns all metadata.
-     *
-     * @return array  The requested metadata. Requested entries that do not
-     *                exist will be undefined. The following entries are
-     *                defaults and always present:
-     *   - uidvalid: (integer) The UIDVALIDITY of the mailbox.
-     */
-    public function getMetaData($mailbox, $uidvalid = null,
-                                array $entries = array())
-    {
-        return $this->_backend->getMetaData(strval($mailbox), $uidvalid, $entries);
-    }
-
-    /**
-     * Set metadata information for a mailbox.
-     *
-     * @param string $mailbox    An IMAP mailbox string.
-     * @param integer $uidvalid  The IMAP uidvalidity value of the mailbox.
-     * @param array $data        The list of data to save. The keys are the
-     *                           metadata IDs, the values are the associated
-     *                           data. The following labels are reserved:
-     *                           'uidvalid'.
-     */
-    public function setMetaData($mailbox, $uidvalid, array $data = array())
-    {
-        unset($data['uidvalid']);
-
-        if (!empty($data)) {
-            if (!empty($uidvalid)) {
-                $data['uidvalid'] = $uidvalid;
-            }
-            $mailbox = strval($mailbox);
-
-            $this->_backend->setMetaData($mailbox, $data);
-
-            if ($this->_debug) {
-                $this->_debug->info('CACHE: Stored metadata (mailbox: ' . $mailbox . '; keys: ' . implode(',', array_keys($data)) . ")");
-            }
-        }
-    }
-
-    /**
-     * Delete messages in the cache.
-     *
-     * @param string $mailbox  An IMAP mailbox string.
-     * @param array $uids      The list of message UIDs to delete.
-     */
-    public function deleteMsgs($mailbox, $uids)
-    {
-        if (empty($uids)) {
-            return;
-        }
-
-        $mailbox = strval($mailbox);
-
-        $this->_backend->deleteMsgs($mailbox, $uids);
-
-        if ($this->_debug) {
-            $this->_debug->info('CACHE: Deleted messages (mailbox: ' . $mailbox . '; UIDs: ' . $this->_baseob->getIdsOb($uids)->tostring_sort . ")");
-        }
-    }
-
-    /**
-     * Delete a mailbox from the cache.
-     *
-     * @param string $mbox  The mailbox to delete.
-     */
-    public function deleteMailbox($mbox)
-    {
-        $mbox = strval($mbox);
-        $this->_backend->deleteMailbox($mbox);
-
-        if ($this->_debug) {
-            $this->_debug->info('CACHE: Deleted mailbox (mailbox: ' . $mbox . ")");
-        }
-    }
-
-    /**
-     * Clear the cache.
-     *
-     * @since 2.9.0
-     *
-     * @param integer $lifetime  Only delete entries older than this (in
-     *                           seconds). If null, deletes all entries.
-     */
-    public function clear($lifetime = null)
-    {
-        $this->_backend->clear($lifetime);
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientDataAclphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Acl.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Acl.php     2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Acl.php        2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,156 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2011-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2011-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * ACL rights for a mailbox (see RFC 2086/4314).
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2011-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Data_Acl extends Horde_Imap_Client_Data_AclCommon implements ArrayAccess, IteratorAggregate, Serializable
-{
-    /**
-     * ACL rights.
-     *
-     * @var array
-     */
-    protected $_rights;
-
-    /**
-     * Constructor.
-     *
-     * @param string $rights  The rights (see RFC 4314 [2.1]).
-     */
-    public function __construct($rights = '')
-    {
-        $this->_rights = str_split($rights);
-        $this->_normalize();
-    }
-
-    /**
-     * String representation of the ACL.
-     *
-     * @return string  String representation (RFC 4314 compliant).
-     */
-    public function __toString()
-    {
-        return implode('', $this->_rights);
-    }
-
-    /**
-     * Computes the difference to another rights string.
-     * Virtual rights are ignored.
-     *
-     * @param string $rights  The rights to compute against.
-     *
-     * @return array  Two element array: added and removed.
-     */
-    public function diff($rights)
-    {
-        $rlist = array_diff(str_split($rights), array_keys($this->_virtual));
-
-        return array(
-            'added' => implode('', array_diff($rlist, $this->_rights)),
-            'removed' => implode('', array_diff($this->_rights, $rlist))
-        );
-    }
-
-    /**
-     * Normalize virtual rights (see RFC 4314 [2.1.1]).
-     */
-    protected function _normalize()
-    {
-        /* Clients conforming to RFC 4314 MUST ignore the virtual ACL_CREATE
-         * and ACL_DELETE rights. See RFC 4314 [2.1]. However, we still need
-         * to handle these rights when dealing with RFC 2086 servers since
-         * we are abstracting out use of ACL_CREATE/ACL_DELETE to their
-         * component RFC 4314 rights. */
-        foreach ($this->_virtual as $key => $val) {
-            if ($this[$key]) {
-                unset($this[$key]);
-                if (!$this[reset($val)]) {
-                    $this->_rights = array_unique(array_merge($this->_rights, $val));
-                }
-            }
-        }
-    }
-
-    /* ArrayAccess methods. */
-
-    /**
-     */
-    public function offsetExists($offset)
-    {
-        return $this[$offset];
-    }
-
-    /**
-     */
-    public function offsetGet($offset)
-    {
-        return in_array($offset, $this->_rights);
-    }
-
-    /**
-     */
-    public function offsetSet($offset, $value)
-    {
-        if ($value) {
-            if (!$this[$offset]) {
-                $this->_rights[] = $offset;
-                $this->_normalize();
-            }
-        } elseif ($this[$offset]) {
-            if (isset($this->_virtual[$offset])) {
-                foreach ($this->_virtual[$offset] as $val) {
-                    unset($this[$val]);
-                }
-            }
-            unset($this[$offset]);
-        }
-    }
-
-    /**
-     */
-    public function offsetUnset($offset)
-    {
-        $this->_rights = array_values(array_diff($this->_rights, array($offset)));
-    }
-
-    /* IteratorAggregate method. */
-
-    public function getIterator()
-    {
-        return new ArrayIterator($this->_rights);
-    }
-
-    /* Serializable methods. */
-
-    /**
-     */
-    public function serialize()
-    {
-        return json_encode($this->_rights);
-    }
-
-    /**
-     */
-    public function unserialize($data)
-    {
-        $this->_rights = json_decode($data);
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientDataAclCommonphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/AclCommon.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/AclCommon.php       2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/AclCommon.php  2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,72 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2011-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2011-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Provides common methods shared in all ACL classes (see RFC 2086/4314).
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2011-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Data_AclCommon
-{
-    /* Constants for getString(). */
-    const RFC_2086 = 1;
-    const RFC_4314 = 2;
-
-    /**
-     * List of virtual rights (RFC 4314 [2.1.1]).
-     *
-     * @var array
-     */
-    protected $_virtual = array(
-        Horde_Imap_Client::ACL_CREATE => array(
-            Horde_Imap_Client::ACL_CREATEMBOX,
-            Horde_Imap_Client::ACL_DELETEMBOX
-        ),
-        Horde_Imap_Client::ACL_DELETE => array(
-            Horde_Imap_Client::ACL_DELETEMSGS,
-            // Don't put this first - we do checks on the existence of the
-            // first element in this array to determine the RFC type, and this
-            // is duplicate of right contained in ACL_CREATE.
-            Horde_Imap_Client::ACL_DELETEMBOX,
-            Horde_Imap_Client::ACL_EXPUNGE
-        )
-    );
-
-    /**
-     * Returns the raw string to use in IMAP server calls.
-     *
-     * @param integer $type  The RFC type to use (RFC_* constant).
-     *
-     * @return string  The string representation of the ACL.
-     */
-    public function getString($type = self::RFC_4314)
-    {
-        $acl = strval($this);
-
-        if ($type == self::RFC_2086) {
-            foreach ($this->_virtual as $key => $val) {
-                $acl = str_replace($val, '', $acl, $count);
-                if ($count) {
-                    $acl .= $key;
-                }
-            }
-        }
-
-        return $acl;
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientDataAclNegativephp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/AclNegative.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/AclNegative.php     2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/AclNegative.php        2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,25 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2011-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2011-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * ACL *negative* rights for a mailbox (see RFC 2086/4314 [2]).
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2011-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Data_AclNegative extends Horde_Imap_Client_Data_Acl
-{
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientDataAclRightsphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/AclRights.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/AclRights.php       2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/AclRights.php  2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,208 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2011-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2011-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Available ACL rights for a mailbox/identifier (see RFC 2086/4314).
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2011-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Data_AclRights extends Horde_Imap_Client_Data_AclCommon implements ArrayAccess, Iterator, Serializable
-{
-    /**
-     * ACL optional rights.
-     *
-     * @var array
-     */
-    protected $_optional = array();
-
-    /**
-     * ACL required rights.
-     *
-     * @var array
-     */
-    protected $_required = array();
-
-    /**
-     * Constructor.
-     *
-     * @param array $required  The required rights (see RFC 4314 [2.1]).
-     * @param array $optional  The optional rights (see RFC 4314 [2.1]).
-     */
-    public function __construct(array $required = array(),
-                                array $optional = array())
-    {
-        $this->_required = $required;
-
-        foreach ($optional as $val) {
-            foreach (str_split($val) as $right) {
-                $this->_optional[$right] = $val;
-            }
-        }
-
-        $this->_normalize();
-    }
-
-    /**
-     * String representation of the ACL.
-     *
-     * @return string  String representation (RFC 4314 compliant).
-     *
-     */
-    public function __toString()
-    {
-        return implode('', array_keys(array_flip(array_merge(array_values($this->_required), array_keys($this->_optional)))));
-    }
-
-    /**
-     * Normalize virtual rights (see RFC 4314 [2.1.1]).
-     */
-    protected function _normalize()
-    {
-        /* Clients conforming to RFC 4314 MUST ignore the virtual ACL_CREATE
-         * and ACL_DELETE rights. See RFC 4314 [2.1]. However, we still need
-         * to handle these rights when dealing with RFC 2086 servers since
-         * we are abstracting out use of ACL_CREATE/ACL_DELETE to their
-         * component RFC 4314 rights. */
-        foreach ($this->_virtual as $key => $val) {
-            if (isset($this->_optional[$key])) {
-                unset($this->_optional[$key]);
-                foreach ($val as $val2) {
-                    $this->_optional[$val2] = implode('', $val);
-                }
-            } elseif (($pos = array_search($key, $this->_required)) !== false) {
-                unset($this->_required[$pos]);
-                $this->_required = array_unique(array_merge($this->_required, $val));
-            }
-        }
-    }
-
-    /* ArrayAccess methods. */
-
-    /**
-     */
-    public function offsetExists($offset)
-    {
-        return (bool)$this[$offset];
-    }
-
-    /**
-     */
-    public function offsetGet($offset)
-    {
-        if (isset($this->_optional[$offset])) {
-            return $this->_optional[$offset];
-        }
-
-        $pos = array_search($offset, $this->_required);
-
-        return ($pos === false)
-            ? null
-            : $this->_required[$pos];
-    }
-
-    /**
-     */
-    public function offsetSet($offset, $value)
-    {
-        $this->_optional[$offset] = $value;
-        $this->_normalize();
-    }
-
-    /**
-     */
-    public function offsetUnset($offset)
-    {
-        unset($this->_optional[$offset]);
-        $this->_required = array_values(array_diff($this->_required, array($offset)));
-
-        if (isset($this->_virtual[$offset])) {
-            foreach ($this->_virtual[$offset] as $val) {
-                unset($this[$val]);
-            }
-        }
-    }
-
-    /* Iterator methods. */
-
-    /**
-     */
-    public function current()
-    {
-        $val = current($this->_required);
-        return is_null($val)
-            ? current($this->_optional)
-            : $val;
-    }
-
-    /**
-     */
-    public function key()
-    {
-        $key = key($this->_required);
-        return is_null($key)
-            ? key($this->_optional)
-            : $key;
-    }
-
-    /**
-     */
-    public function next()
-    {
-        if (key($this->_required) === null) {
-            next($this->_optional);
-        } else {
-            next($this->_required);
-        }
-    }
-
-    /**
-     */
-    public function rewind()
-    {
-        reset($this->_required);
-        reset($this->_optional);
-    }
-
-    /**
-     */
-    public function valid()
-    {
-        return ((key($this->_required) !== null) ||
-                (key($this->_optional) !== null));
-
-    }
-
-    /* Serializable methods. */
-
-    /**
-     */
-    public function serialize()
-    {
-        return json_encode(array(
-            $this->_required,
-            $this->_optional
-        ));
-    }
-
-    /**
-     */
-    public function unserialize($data)
-    {
-        list($this->_required, $this->_optional) = json_decode($data);
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientDataBaseSubjectphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/BaseSubject.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/BaseSubject.php     2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/BaseSubject.php        2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,237 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2008-2013 Horde LLC (http://www.horde.org/)
- *
- * getBaseSubject() code adapted from imap-base-subject.c (Dovecot 1.2)
- *   Original code released under the LGPL-2.1
- *   Copyright (c) 2002-2008 Timo Sirainen <tss@iki.fi>
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2002-2008 Timo Sirainen
- * @copyright 2008-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Determines the "base subject" of a string (RFC 5256 [2.1]).
- *
- * @author    Timo Sirainen <tss@iki.fi>
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2002-2008 Timo Sirainen
- * @copyright 2011-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Data_BaseSubject
-{
-    /**
-     * The base subject.
-     *
-     * @var string
-     */
-    protected $_subject;
-
-    /**
-     * Constructor.
-     *
-     * @param string $str  The subject string.
-     * @param array $opts  Additional options:
-     *   - keepblob: (boolean) Don't remove any "blob" information (i.e. text
-     *               leading text between square brackets) from string.
-     *
-     * @return string  The cleaned up subject string.
-     */
-    public function __construct($str, array $opts = array())
-    {
-        // Rule 1a: MIME decode.
-        $str = Horde_Mime::decode($str);
-
-        // Rule 1b: Remove superfluous whitespace.
-        $str = preg_replace("/[\t\r\n ]+/", ' ', $str);
-
-        if (!strlen($str)) {
-            $this->_subject = '';
-        }
-
-        do {
-            /* (2) Remove all trailing text of the subject that matches the
-             * the subj-trailer ABNF, repeat until no more matches are
-             * possible. */
-            $str = preg_replace("/(?:\s*\(fwd\)\s*)+$/i", '', $str);
-
-            do {
-                /* (3) Remove all prefix text of the subject that matches the
-                 * subj-leader ABNF. */
-                $found = $this->_removeSubjLeader($str, !empty($opts['keepblob']));
-
-                /* (4) If there is prefix text of the subject that matches
-                 * the subj-blob ABNF, and removing that prefix leaves a
-                 * non-empty subj-base, then remove the prefix text. */
-                $found = (empty($opts['keepblob']) && $this->_removeBlobWhenNonempty($str)) || $found;
-
-                /* (5) Repeat (3) and (4) until no matches remain. */
-            } while ($found);
-
-            /* (6) If the resulting text begins with the subj-fwd-hdr ABNF and
-             * ends with the subj-fwd-trl ABNF, remove the subj-fwd-hdr and
-             * subj-fwd-trl and repeat from step (2). */
-        } while ($this->_removeSubjFwdHdr($str));
-
-        $this->_subject = $str;
-    }
-
-    /**
-     * Return the "base subject" defined in RFC 5256 [2.1].
-     *
-     * @return string  The base subject.
-     */
-    public function __toString()
-    {
-        return $this->_subject;
-    }
-
-    /**
-     * Remove all prefix text of the subject that matches the subj-leader
-     * ABNF.
-     *
-     * @param string &$str       The subject string.
-     * @param boolean $keepblob  Remove blob information?
-     *
-     * @return boolean  True if string was altered.
-     */
-    protected function _removeSubjLeader(&$str, $keepblob = false)
-    {
-        $ret = false;
-
-        if (!strlen($str)) {
-            return $ret;
-        }
-
-        if ($len = strspn($str, " \t")) {
-            $str = substr($str, $len);
-            $ret = true;
-        }
-
-        $i = 0;
-
-        if (!$keepblob) {
-            while (isset($str[$i]) && ($str[$i] == '[')) {
-                if (($i = $this->_removeBlob($str, $i)) === false) {
-                    return $ret;
-                }
-            }
-        }
-
-        if (stripos($str, 're', $i) === 0) {
-            $i += 2;
-        } elseif (stripos($str, 'fwd', $i) === 0) {
-            $i += 3;
-        } elseif (stripos($str, 'fw', $i) === 0) {
-            $i += 2;
-        } else {
-            return $ret;
-        }
-
-        $i += strspn($str, " \t", $i);
-
-        if (!$keepblob) {
-            while (isset($str[$i]) && ($str[$i] == '[')) {
-                if (($i = $this->_removeBlob($str, $i)) === false) {
-                    return $ret;
-                }
-            }
-        }
-
-        if (!isset($str[$i]) || ($str[$i] != ':')) {
-            return $ret;
-        }
-
-        $str = substr($str, ++$i);
-
-        return true;
-    }
-
-    /**
-     * Remove "[...]" text.
-     *
-     * @param string $str  The subject string.
-     * @param integer $i   Current position.
-     *
-     * @return boolean|integer  False if blob was not found, otherwise the
-     *                          string position of the first non-blob char.
-     */
-    protected function _removeBlob($str, $i)
-    {
-        if ($str[$i] != '[') {
-            return false;
-        }
-
-        ++$i;
-
-        for ($cnt = strlen($str); $i < $cnt; ++$i) {
-            if ($str[$i] == ']') {
-                break;
-            }
-
-            if ($str[$i] == '[') {
-                return false;
-            }
-        }
-
-        if ($i == ($cnt - 1)) {
-            return false;
-        }
-
-        ++$i;
-
-        if ($str[$i] == ' ') {
-            ++$i;
-        }
-
-        return $i;
-    }
-
-    /**
-     * Remove "[...]" text if it doesn't result in the subject becoming
-     * empty.
-     *
-     * @param string &$str  The subject string.
-     *
-     * @return boolean  True if string was altered.
-     */
-    protected function _removeBlobWhenNonempty(&$str)
-    {
-        if ($str &&
-            ($str[0] == '[') &&
-            (($i = $this->_removeBlob($str, 0)) !== false) &&
-            ($i != strlen($str))) {
-            $str = substr($str, $i);
-            return true;
-        }
-
-        return false;
-    }
-
-    /**
-     * Remove a "[fwd: ... ]" string.
-     *
-     * @param string &$str  The subject string.
-     *
-     * @return boolean  True if string was altered.
-     */
-    protected function _removeSubjFwdHdr(&$str)
-    {
-        if ((stripos($str, '[fwd:') !== 0) || (substr($str, -1) != ']')) {
-            return false;
-        }
-
-        $str = substr($str, 5, -1);
-        return true;
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientDataEnvelopephp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Envelope.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Envelope.php        2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Envelope.php   2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,212 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2011-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2011-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Envelope data as returned by the IMAP FETCH command (RFC 3501 [7.4.2]).
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2011-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- *
- * @property Horde_Mail_Rfc822_List $bcc  Bcc address(es).
- * @property Horde_Mail_Rfc822_List $cc  Cc address(es).
- * @property Horde_Imap_Client_DateTime $date  IMAP internal date.
- * @property Horde_Mail_Rfc822_List $from  From address(es).
- * @property string $in_reply_to  Message-ID of the message replied to.
- * @property string $message_id  Message-ID of the message.
- * @property Horde_Mail_Rfc822_List $reply_to  Reply-to address(es).
- * @property Horde_Mail_Rfc822_List $sender  Sender address.
- * @property string $subject  Subject.
- * @property Horde_Mail_Rfc822_List $to  To address(es).
- */
-class Horde_Imap_Client_Data_Envelope implements Serializable
-{
-    /** Serializable version. */
-    const VERSION = 2;
-
-    /**
-     * Data object.
-     *
-     * @var Horde_Mime_Headers
-     */
-    protected $_data;
-
-    /**
-     * Constructor.
-     *
-     * @var array $data  An array of property names (keys) and values to set
-     *                   in this object.
-     */
-    public function __construct(array $data = array())
-    {
-        $this->_data = new Horde_Mime_Headers();
-
-        foreach ($data as $key => $val) {
-            $this->$key = $val;
-        }
-    }
-
-    /**
-     */
-    public function __get($name)
-    {
-        switch ($name) {
-        case 'reply_to':
-            $name = 'reply-to';
-            // Fall-through
-
-        case 'bcc':
-        case 'cc':
-        case 'from':
-        case 'sender':
-        case 'to':
-            if (($ob = $this->_data->getOb($name)) !== null) {
-                return $ob;
-            }
-
-            if (in_array($name, array('sender', 'reply-to'))) {
-                return $this->from;
-            }
-            break;
-
-        case 'date':
-            if (($val = $this->_data->getValue($name)) !== null) {
-                return new Horde_Imap_Client_DateTime($val);
-            }
-            break;
-
-        case 'in_reply_to':
-        case 'message_id':
-        case 'subject':
-            if (($val = $this->_data->getValue($name)) !== null) {
-                return $val;
-            }
-            break;
-        }
-
-        // Default values.
-        switch ($name) {
-        case 'bcc':
-        case 'cc':
-        case 'from':
-        case 'to':
-            return new Horde_Mail_Rfc822_List();
-
-        case 'date':
-            return new Horde_Imap_Client_DateTime();
-
-        case 'in_reply_to':
-        case 'message_id':
-        case 'subject':
-            return '';
-        }
-
-        return null;
-    }
-
-    /**
-     */
-    public function __set($name, $value)
-    {
-        if (!strlen($value)) {
-            return;
-        }
-
-        switch ($name) {
-        case 'bcc':
-        case 'cc':
-        case 'date':
-        case 'from':
-        case 'in_reply_to':
-        case 'message_id':
-        case 'reply_to':
-        case 'sender':
-        case 'subject':
-        case 'to':
-            switch ($name) {
-            case 'from':
-                foreach (array('reply_to', 'sender') as $val) {
-                    if ($this->$val->match($value)) {
-                        $this->_data->removeHeader($val);
-                    }
-                }
-                break;
-
-            case 'reply_to':
-            case 'sender':
-                if ($this->from->match($value)) {
-                    $this->_data->removeHeader($name);
-                    return;
-                }
-
-                /* Convert reply-to name. */
-                if ($name == 'reply_to') {
-                    $name = 'reply-to';
-                }
-                break;
-            }
-
-            $this->_data->addHeader($name, $value, array(
-                'sanity_check' => true
-            ));
-            break;
-        }
-    }
-
-    /**
-     */
-    public function __isset($name)
-    {
-        switch ($name) {
-        case 'reply_to':
-            $name = 'reply-to';
-            // Fall-through
-
-        case 'sender':
-            if ($this->_data->getValue($name) !== null) {
-                return true;
-            }
-            $name = 'from';
-            break;
-        }
-
-        return ($this->_data->getValue($name) !== null);
-    }
-
-    /* Serializable methods. */
-
-    /**
-     */
-    public function serialize()
-    {
-        return serialize(array(
-            'd' => $this->_data,
-            'v' => self::VERSION
-        ));
-    }
-
-    /**
-     */
-    public function unserialize($data)
-    {
-        $data = @unserialize($data);
-        if (empty($data['v']) || ($data['v'] != self::VERSION)) {
-            throw new Exception('Cache version change');
-        }
-
-        $this->_data = $data['d'];
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientDataFetchPop3php"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Fetch/Pop3.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Fetch/Pop3.php      2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Fetch/Pop3.php 2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,36 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2011-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2011-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Object containg POP3 fetch data.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2011-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Data_Fetch_Pop3 extends Horde_Imap_Client_Data_Fetch
-{
-    /**
-     * Set UID.
-     *
-     * @param string $uid  The message UID. Unlike IMAP, this UID does not
-     *                     have to be an integer.
-     */
-    public function setUid($uid)
-    {
-        $this->_data[Horde_Imap_Client::FETCH_UID] = strval($uid);
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientDataFetchphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Fetch.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Fetch.php   2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Fetch.php      2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,567 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2011-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2011-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Object containing data returned by the Horde_Imap_Client_Base#fetch()
- * command.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2011-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Data_Fetch
-{
-    /* Constants. */
-    const HEADER_PARSE = 1;
-    const HEADER_STREAM = 2;
-
-    /**
-     * Internal data array.
-     *
-     * @var array
-     */
-    protected $_data = array();
-
-    /**
-     * Set the full message property.
-     *
-     * @param mixed $msg  The full message text, as either a string or stream
-     *                    resource.
-     */
-    public function setFullMsg($msg)
-    {
-        $this->_data[Horde_Imap_Client::FETCH_FULLMSG] = $msg;
-    }
-
-    /**
-     * Returns the full message.
-     *
-     * @param boolean $stream  Return as a stream?
-     *
-     * @return mixed  The full text of the entire message.
-     */
-    public function getFullMsg($stream = false)
-    {
-        return $this->_msgText($stream, isset($this->_data[Horde_Imap_Client::FETCH_FULLMSG]) ? $this->_data[Horde_Imap_Client::FETCH_FULLMSG] : null);
-    }
-
-    /**
-     * Set the message structure.
-     *
-     * @param Horde_Mime_Part $structure  The base MIME part of the message.
-     */
-    public function setStructure(Horde_Mime_Part $structure)
-    {
-        $this->_data[Horde_Imap_Client::FETCH_STRUCTURE] = $structure;
-    }
-
-    /**
-     * Get the message structure.
-     *
-     * @return Horde_Mime_Part $structure  The base MIME part of the message.
-     */
-    public function getStructure()
-    {
-        return isset($this->_data[Horde_Imap_Client::FETCH_STRUCTURE])
-            ? clone $this->_data[Horde_Imap_Client::FETCH_STRUCTURE]
-            : new Horde_Mime_Part();
-    }
-
-    /**
-     * Set a header entry.
-     *
-     * @param string $label  The search label.
-     * @param mixed $data    Either a Horde_Mime_Headers object or the raw
-     *                       header text.
-     */
-    public function setHeaders($label, $data)
-    {
-        $this->_data[Horde_Imap_Client::FETCH_HEADERS][$label] = $data;
-    }
-
-    /**
-     * Get a header entry.
-     *
-     * @param string $label    The search label.
-     * @param integer $format  The return format. If self::HEADER_PARSE,
-     *                         returns a Horde_Mime_Headers object. If
-     *                         self::HEADER_STREAM, returns a stream.
-     *                         Otherwise, returns header text.
-     *
-     * @return mixed  See $format.
-     */
-    public function getHeaders($label, $format = 0)
-    {
-        return $this->_getHeaders($label, $format, Horde_Imap_Client::FETCH_HEADERS);
-    }
-
-    /**
-     * Set a header text entry.
-     *
-     * @param string $id    The MIME ID.
-     * @param string $text  The header text.
-     */
-    public function setHeaderText($id, $text)
-    {
-        $this->_data[Horde_Imap_Client::FETCH_HEADERTEXT][$id] = $text;
-    }
-
-    /**
-     * Get a header text entry.
-     *
-     * @param string $id       The MIME ID.
-     * @param integer $format  The return format. If self::HEADER_PARSE,
-     *                         returns a Horde_Mime_Headers object. If
-     *                         self::HEADER_STREAM, returns a stream.
-     *                         Otherwise, returns header text.
-     *
-     * @return mixed  See $format.
-     */
-    public function getHeaderText($id = 0, $format = 0)
-    {
-        return $this->_getHeaders($id, $format, Horde_Imap_Client::FETCH_HEADERTEXT);
-    }
-
-    /**
-     * Set a MIME header entry.
-     *
-     * @param string $id    The MIME ID.
-     * @param string $text  The header text.
-     */
-    public function setMimeHeader($id, $text)
-    {
-        $this->_data[Horde_Imap_Client::FETCH_MIMEHEADER][$id] = $text;
-    }
-
-    /**
-     * Get a MIME header entry.
-     *
-     * @param string $id       The MIME ID.
-     * @param integer $format  The return format. If self::HEADER_PARSE,
-     *                         returns a Horde_Mime_Headers object. If
-     *                         self::HEADER_STREAM, returns a stream.
-     *                         Otherwise, returns header text.
-     *
-     * @return mixed  See $format.
-     */
-    public function getMimeHeader($id, $format = 0)
-    {
-        return $this->_getHeaders($id, $format, Horde_Imap_Client::FETCH_MIMEHEADER);
-    }
-
-    /**
-     * Set a body part entry.
-     *
-     * @param string $id      The MIME ID.
-     * @param mixed $text     The body part text, as either a string or stream
-     *                        resource.
-     * @param string $decode  Either '8bit', 'binary', or null.
-     */
-    public function setBodyPart($id, $text, $decode = null)
-    {
-        $this->_data[Horde_Imap_Client::FETCH_BODYPART][$id] = array(
-            'd' => $decode,
-            't' => $text
-        );
-    }
-
-    /**
-     * Set the body part size for a body part.
-     *
-     * @param string $id     The MIME ID.
-     * @param integer $size  The size (in bytes).
-     */
-    public function setBodyPartSize($id, $size)
-    {
-        $this->_data[Horde_Imap_Client::FETCH_BODYPARTSIZE][$id] = intval($size);
-    }
-
-    /**
-     * Get a body part entry.
-     *
-     * @param string $id       The MIME ID.
-     * @param boolean $stream  Return as a stream?
-     *
-     * @return mixed  The full text of the body part.
-     */
-    public function getBodyPart($id, $stream = false)
-    {
-        return $this->_msgText($stream, isset($this->_data[Horde_Imap_Client::FETCH_BODYPART][$id]) ? $this->_data[Horde_Imap_Client::FETCH_BODYPART][$id]['t'] : null);
-    }
-
-    /**
-     * Determines if/how a body part was MIME decoded on the server.
-     *
-     * @param string $id  The MIME ID.
-     *
-     * @return string  Either '8bit', 'binary', or null.
-     */
-    public function getBodyPartDecode($id)
-    {
-        return isset($this->_data[Horde_Imap_Client::FETCH_BODYPART][$id])
-            ? $this->_data[Horde_Imap_Client::FETCH_BODYPART][$id]['d']
-            : null;
-    }
-
-    /**
-     * Returns the body part size, if returned by the server.
-     *
-     * @param string $id  The MIME ID.
-     *
-     * @return integer  The body part size, in bytes.
-     */
-    public function getBodyPartSize($id)
-    {
-        return isset($this->_data[Horde_Imap_Client::FETCH_BODYPARTSIZE][$id])
-            ? $this->_data[Horde_Imap_Client::FETCH_BODYPARTSIZE][$id]
-            : null;
-    }
-
-    /**
-     * Set a body text entry.
-     *
-     * @param string $id   The MIME ID.
-     * @param mixed $text  The body part text, as either a string or stream
-     *                     resource.
-     */
-    public function setBodyText($id, $text)
-    {
-        $this->_data[Horde_Imap_Client::FETCH_BODYTEXT][$id] = $text;
-    }
-
-    /**
-     * Get a body text entry.
-     *
-     * @param string $id       The MIME ID.
-     * @param boolean $stream  Return as a stream?
-     *
-     * @return mixed  The full text of the body text.
-     */
-    public function getBodyText($id = 0, $stream = false)
-    {
-        return $this->_msgText($stream, isset($this->_data[Horde_Imap_Client::FETCH_BODYTEXT][$id]) ? $this->_data[Horde_Imap_Client::FETCH_BODYTEXT][$id] : null);
-    }
-
-    /**
-     * Set envelope data.
-     *
-     * @param array $data  The envelope data to pass to the Envelope object
-     *                     constructor, or an Envelope object.
-     */
-    public function setEnvelope($data)
-    {
-        $this->_data[Horde_Imap_Client::FETCH_ENVELOPE] = is_array($data)
-            ? new Horde_Imap_Client_Data_Envelope($data)
-            : $data;
-    }
-
-    /**
-     * Get envelope data.
-     *
-     * @return Horde_Imap_Client_Data_Envelope  An envelope object.
-     */
-    public function getEnvelope()
-    {
-        return isset($this->_data[Horde_Imap_Client::FETCH_ENVELOPE])
-            ? clone $this->_data[Horde_Imap_Client::FETCH_ENVELOPE]
-            : new Horde_Imap_Client_Data_Envelope();
-    }
-
-    /**
-     * Set IMAP flags.
-     *
-     * @param array $flags  An array of IMAP flags.
-     */
-    public function setFlags(array $flags)
-    {
-        $this->_data[Horde_Imap_Client::FETCH_FLAGS] = array_map('strtolower', $flags);
-    }
-
-    /**
-     * Get IMAP flags.
-     *
-     * @return array  An array of IMAP flags (all flags in lowercase).
-     */
-    public function getFlags()
-    {
-        return isset($this->_data[Horde_Imap_Client::FETCH_FLAGS])
-            ? $this->_data[Horde_Imap_Client::FETCH_FLAGS]
-            : array();
-    }
-
-    /**
-     * Set IMAP internal date.
-     *
-     * @param mixed $date  Either a Horde_Imap_Client_DateTime object or a
-     *                     date string.
-     */
-    public function setImapDate($date)
-    {
-        $this->_data[Horde_Imap_Client::FETCH_IMAPDATE] = is_object($date)
-            ? $date
-            : new Horde_Imap_Client_DateTime($date);
-    }
-
-    /**
-     * Get internal IMAP date.
-     *
-     * @return Horde_Imap_Client_DateTime  A date object.
-     */
-    public function getImapDate()
-    {
-        return isset($this->_data[Horde_Imap_Client::FETCH_IMAPDATE])
-            ? clone $this->_data[Horde_Imap_Client::FETCH_IMAPDATE]
-            : new Horde_Imap_Client_DateTime();
-    }
-
-    /**
-     * Set message size.
-     *
-     * @param integer $size  The size of the message, in bytes.
-     */
-    public function setSize($size)
-    {
-        $this->_data[Horde_Imap_Client::FETCH_SIZE] = intval($size);
-    }
-
-    /**
-     * Get message size.
-     *
-     * @return integer  The size of the message, in bytes.
-     */
-    public function getSize()
-    {
-        return isset($this->_data[Horde_Imap_Client::FETCH_SIZE])
-            ? $this->_data[Horde_Imap_Client::FETCH_SIZE]
-            : 0;
-    }
-
-    /**
-     * Set UID.
-     *
-     * @param integer $uid  The message UID.
-     */
-    public function setUid($uid)
-    {
-        $this->_data[Horde_Imap_Client::FETCH_UID] = intval($uid);
-    }
-
-    /**
-     * Get UID.
-     *
-     * @return integer  The message UID.
-     */
-    public function getUid()
-    {
-        return isset($this->_data[Horde_Imap_Client::FETCH_UID])
-            ? $this->_data[Horde_Imap_Client::FETCH_UID]
-            : null;
-    }
-
-    /**
-     * Set message sequence number.
-     *
-     * @param integer $seq  The message sequence number.
-     */
-    public function setSeq($seq)
-    {
-        $this->_data[Horde_Imap_Client::FETCH_SEQ] = intval($seq);
-    }
-
-    /**
-     * Get message sequence number.
-     *
-     * @return integer  The message sequence number.
-     */
-    public function getSeq()
-    {
-        return isset($this->_data[Horde_Imap_Client::FETCH_SEQ])
-            ? $this->_data[Horde_Imap_Client::FETCH_SEQ]
-            : null;
-    }
-
-    /**
-     * Set the modified sequence value for the message.
-     *
-     * @param integer $modseq  The modseq value.
-     */
-    public function setModSeq($modseq)
-    {
-        $this->_data[Horde_Imap_Client::FETCH_MODSEQ] = intval($modseq);
-    }
-
-    /**
-     * Get the modified sequence value for the message.
-     *
-     * @return integer  The modseq value.
-     */
-    public function getModSeq()
-    {
-        return isset($this->_data[Horde_Imap_Client::FETCH_MODSEQ])
-            ? $this->_data[Horde_Imap_Client::FETCH_MODSEQ]
-            : null;
-    }
-
-    /**
-     * Set the internationalized downgraded status for the message.
-     *
-     * @since 2.11.0
-     *
-     * @param boolean $downgraded  True if at least one message component has
-     *                             been downgraded.
-     */
-    public function setDowngraded($downgraded)
-    {
-        if ($downgraded) {
-            $this->_data[Horde_Imap_Client::FETCH_DOWNGRADED] = true;
-        } else {
-            unset($this->_data[Horde_Imap_Client::FETCH_DOWNGRADED]);
-        }
-    }
-
-    /**
-     * Does the message contain internationalized downgraded data (i.e. it
-     * is a "surrogate" message)?
-     *
-     * @since 2.11.0
-     *
-     * @return boolean  True if at least one message components has been
-     *                  downgraded.
-     */
-    public function isDowngraded()
-    {
-        return !empty($this->_data[Horde_Imap_Client::FETCH_DOWNGRADED]);
-    }
-
-    /**
-     * Return the internal representation of the data.
-     *
-     * @return array  The data array.
-     */
-    public function getRawData()
-    {
-        return $this->_data;
-    }
-
-    /**
-     * Merge a fetch object into this one.
-     *
-     * @param Horde_Imap_Client_Data_Fetch $data  A fetch object.
-     */
-    public function merge(Horde_Imap_Client_Data_Fetch $data)
-    {
-        $this->_data = array_replace_recursive($this->_data, $data->getRawData());
-    }
-
-    /**
-     * Does this object containing cacheable data of the given type?
-     *
-     * @param integer $type  The type to query.
-     *
-     * @return boolean  True if the type is cacheable.
-     */
-    public function exists($type)
-    {
-        return isset($this->_data[$type]);
-    }
-
-    /**
-     * Does this object contain only default values for all fields?
-     *
-     * @return boolean  True if object contains default data.
-     */
-    public function isDefault()
-    {
-        return empty($this->_data);
-    }
-
-    /**
-     * Return text representation of a field.
-     *
-     * @param boolean $stream  Return as a stream?
-     * @param mixed $data      The field data (string or resource) or null if
-     *                         field does not exist.
-     *
-     * @return mixed  Requested text representation.
-     */
-    protected function _msgText($stream, $data)
-    {
-        if ($stream) {
-            if (is_resource($data)) {
-                rewind($data);
-                return $data;
-            }
-
-            $tmp = fopen('php://temp', 'w+');
-
-            if (!is_null($data)) {
-                fwrite($tmp, $data);
-                rewind($tmp);
-            }
-
-            return $tmp;
-        }
-
-        if (is_resource($data)) {
-            rewind($data);
-            return stream_get_contents($data);
-        }
-
-        return strval($data);
-    }
-
-    /**
-     * Return representation of a header field.
-     *
-     * @param string $id       The header id.
-     * @param integer $format  The return format. If self::HEADER_PARSE,
-     *                         returns a Horde_Mime_Headers object. If
-     *                         self::HEADER_STREAM, returns a stream.
-     *                         Otherwise, returns header text.
-     * @param integer $key     The array key where the data is stored in the
-     *                         internal array.
-     *
-     * @return mixed  The data in the format specified by $format.
-     */
-    protected function _getHeaders($id, $format, $key)
-    {
-        switch ($format) {
-        case self::HEADER_STREAM:
-            if (!isset($this->_data[$key][$id])) {
-                return $this->_msgText(true, null);
-            } elseif (is_object($this->_data[$key][$id])) {
-                return $this->_getHeaders($id, 0, $key);
-            }
-            return $this->_msgText(true, $this->_data[$key][$id]);
-
-        case self::HEADER_PARSE:
-            if (!isset($this->_data[$key][$id])) {
-                return new Horde_Mime_Headers();
-            } elseif (is_object($this->_data[$key][$id])) {
-                return clone $this->_data[$key][$id];
-            }
-            return Horde_Mime_Headers::parseHeaders($this->_getHeaders($id, 0, $key));
-        }
-
-        if (!isset($this->_data[$key][$id])) {
-            return '';
-        }
-
-        return is_object($this->_data[$key][$id])
-            ? $this->_data[$key][$id]->toString(array('nowrap' => true))
-            : $this->_msgText(false, $this->_data[$key][$id]);
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientDataFormatAstringphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Astring.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Astring.php  2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Astring.php     2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,32 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Object representation of an IMAP astring (atom or string) (RFC 3501 [4.3]).
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Data_Format_Astring extends Horde_Imap_Client_Data_Format_String
-{
-    /**
-     */
-    public function quoted()
-    {
-        return $this->_filter->quoted || !$this->_data->length();
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientDataFormatAtomphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Atom.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Atom.php     2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Atom.php        2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,53 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Object representation of an IMAP atom (RFC 3501 [4.1]).
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Data_Format_Atom extends Horde_Imap_Client_Data_Format
-{
-    /**
-     */
-    public function escape()
-    {
-        return strlen($this->_data)
-            ? parent::escape()
-            : '""';
-    }
-
-    /**
-     */
-    public function verify()
-    {
-        if (strlen($this->_data) != strlen($this->stripNonAtomCharacters())) {
-            throw new Horde_Imap_Client_Data_Format_Exception('Illegal character in IMAP atom.');
-        }
-    }
-
-    /**
-     * Strip out any characters that are not allowed in an IMAP atom.
-     *
-     * @return string  The atom data disallowed characters removed.
-     */
-    public function stripNonAtomCharacters()
-    {
-        return str_replace(array('(', ')', '{', ' ', '%', '*', '"', '\\', ']'), '', preg_replace('/[\x00-\x1f\x7f]/', '', $this->_data));
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientDataFormatDatephp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Date.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Date.php     2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Date.php        2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,49 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Object representation of an IMAP date string (RFC 3501 [9]).
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Data_Format_Date extends Horde_Imap_Client_Data_Format
-{
-    /**
-     * Constructor.
-     *
-     * @param mixed $data  Either a DateTime object, or a date format that
-     *                     can be converted to a DateTime object.
-     *
-     * @throws Exception
-     */
-    public function __construct($data)
-    {
-        if (!($data instanceof DateTime)) {
-            $data = new Horde_Imap_Client_DateTime($data);
-        }
-
-        parent::__construct($data);
-    }
-
-    /**
-     */
-    public function __toString()
-    {
-        return $this->_data->format('j-M-Y');
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientDataFormatDateTimephp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/DateTime.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/DateTime.php 2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/DateTime.php    2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,39 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Object representation of an IMAP date-time string (RFC 3501 [9]).
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Data_Format_DateTime extends Horde_Imap_Client_Data_Format_Date
-{
-    /**
-     */
-    public function __toString()
-    {
-        return $this->_data->format('j-M-Y H:i:s O');
-    }
-
-    /**
-     */
-    public function escape()
-    {
-        return '"' . strval($this) . '"';
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientDataFormatExceptionphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Exception.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Exception.php        2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Exception.php   2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,25 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Exception object for IMAP data format errors.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Data_Format_Exception extends Horde_Exception_Wrapped
-{
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientDataFormatFilterQuotephp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Filter/Quote.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Filter/Quote.php     2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Filter/Quote.php        2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,43 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Stream filter to output a quoted IMAP string.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Data_Format_Filter_Quote extends php_user_filter
-{
-    /**
-     * @see stream_filter_register()
-     */
-    public function filter($in, $out, &$consumed, $closing)
-    {
-        stream_bucket_append($out, stream_bucket_new($this->stream, '"'));
-
-        while ($bucket = stream_bucket_make_writeable($in)) {
-            $consumed += $bucket->datalen;
-            $bucket->data = addcslashes($bucket->data, '"\\');
-            stream_bucket_append($out, $bucket);
-        }
-
-        stream_bucket_append($out, stream_bucket_new($this->stream, '"'));
-
-        return PSFS_PASS_ON;
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientDataFormatFilterStringphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Filter/String.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Filter/String.php    2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Filter/String.php       2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,112 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Stream filter to analyze an IMAP string to determine how to send to the
- * server.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Data_Format_Filter_String extends php_user_filter
-{
-    /**
-     * @see stream_filter_register()
-     */
-    public function onCreate()
-    {
-        $this->params->binary = false;
-        $this->params->literal = false;
-        // no_quote_list is used below as a config option
-        $this->params->quoted = false;
-
-        return true;
-    }
-
-    /**
-     * @see stream_filter_register()
-     */
-    public function filter($in, $out, &$consumed, $closing)
-    {
-        $p = $this->params;
-        $skip = false;
-
-        while ($bucket = stream_bucket_make_writeable($in)) {
-            if (!$skip) {
-                $len = $bucket->datalen;
-                $str = $bucket->data;
-
-                for ($i = 0; $i < $len; ++$i) {
-                    $chr = ord($str[$i]);
-
-                    switch ($chr) {
-                    case 0: // null
-                        $p->binary = true;
-                        $p->literal = true;
-
-                        // No need to scan input anymore.
-                        $skip = true;
-                        break 2;
-
-                    case 10: // LF
-                    case 13: // CR
-                        $p->literal = true;
-                        break;
-
-                    case 32: // SPACE
-                    case 34: // "
-                    case 40: // (
-                    case 41: // )
-                    case 92: // \
-                    case 123: // {
-                    case 127: // DEL
-                        // These are all invalid ATOM characters.
-                        $p->quoted = true;
-                        break;
-
-                    case 37: // %
-                    case 42: // *
-                        // These are not quoted if being used as wildcards.
-                        if (empty($p->no_quote_list)) {
-                            $p->quoted = true;
-                        }
-                        break;
-
-                    default:
-                        if ($chr < 32) {
-                            // CTL characters must be, at a minimum, quoted.
-                            $p->quoted = true;
-                        } elseif ($chr > 127) {
-                            // 8-bit chars must be in a literal.
-                            $p->literal = true;
-                        }
-                        break;
-                    }
-                }
-            }
-
-            $consumed += $bucket->datalen;
-            stream_bucket_append($out, $bucket);
-        }
-
-        if ($p->literal) {
-            $p->quoted = false;
-        }
-
-        return PSFS_PASS_ON;
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientDataFormatListphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/List.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/List.php     2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/List.php        2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,105 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Object representation of an IMAP parenthesized list (RFC 3501 [4.4]).
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Data_Format_List extends Horde_Imap_Client_Data_Format implements Countable, IteratorAggregate
-{
-    /**
-     * @see add()
-     */
-    public function __construct($data = null)
-    {
-        parent::__construct(array());
-
-        if (!is_null($data)) {
-            $this->add($data);
-        }
-    }
-
-    /**
-     * Add an element to the list.
-     *
-     * @param mixed $data     The data element(s) to add. Either a
-     *                        Horde_Imap_Client_Data_Format object, a string
-     *                        value that will be treated as an IMAP atom, or
-     *                        an array (or iterable object) of objects to add.
-     * @param boolean $merge  Merge the contents of any container objects,
-     *                        instead of adding the objects themselves?
-     *
-     * @return Horde_Imap_Client_Data_Format_List  This object to allow for
-     *                                             chainable calls (since
-     *                                             2.10.0).
-     */
-    public function add($data, $merge = false)
-    {
-        if (is_array($data) || ($merge && ($data instanceof Traversable))) {
-            foreach ($data as $val) {
-                $this->add($val);
-            }
-        } elseif (is_object($data)) {
-            $this->_data[] = $data;
-        } elseif (!is_null($data)) {
-            $this->_data[] = new Horde_Imap_Client_Data_Format_Atom($data);
-        }
-
-        return $this;
-    }
-
-    /**
-     */
-    public function __toString()
-    {
-        $out = '';
-
-        foreach ($this as $val) {
-            if ($val instanceof $this) {
-                $out .= '(' . $val->escape() . ') ';
-            } elseif (($val instanceof Horde_Imap_Client_Data_Format_String) &&
-                      $val->literal()) {
-                throw new Horde_Imap_Client_Data_Format_Exception('Requires literal output.');
-            } else {
-                $out .= $val->escape() . ' ';
-            }
-        }
-
-        return rtrim($out);
-    }
-
-    /* Countable methods. */
-
-    /**
-     */
-    public function count()
-    {
-        return count($this->_data);
-    }
-
-    /* IteratorAggregate method. */
-
-    /**
-     * Iterator loops through the data elements contained in this list.
-     */
-    public function getIterator()
-    {
-        return new ArrayIterator($this->_data);
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientDataFormatListMailboxphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/ListMailbox.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/ListMailbox.php      2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/ListMailbox.php 2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,37 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Object representation of an IMAP mailbox string used in a LIST command.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Data_Format_ListMailbox extends Horde_Imap_Client_Data_Format_Mailbox
-{
-    /**
-     */
-    protected function _filterParams()
-    {
-        $ob = parent::_filterParams();
-
-        /* Don't quote % or * characters. */
-        $ob->no_quote_list = true;
-
-        return $ob;
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientDataFormatMailboxphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Mailbox.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Mailbox.php  2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Mailbox.php     2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,91 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Object representation of an IMAP mailbox string (RFC 3501 [9]).
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Data_Format_Mailbox extends Horde_Imap_Client_Data_Format_Astring
-{
-    /**
-     * Mailbox object.
-     *
-     * @var Horde_Imap_Client_Mailbox
-     */
-    protected $_mailbox;
-
-    /**
-     * @param mixed $data  Either a mailbox object or a UTF-8 mailbox name.
-     */
-    public function __construct($data)
-    {
-        $this->_mailbox = Horde_Imap_Client_Mailbox::get($data);
-
-        parent::__construct($this->_mailbox->utf7imap);
-    }
-
-    /**
-     */
-    public function __toString()
-    {
-        return strval($this->_mailbox);
-    }
-
-    /**
-     */
-    public function getData()
-    {
-        return $this->_mailbox;
-    }
-
-    /**
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function binary()
-    {
-        if (parent::binary()) {
-            // Mailbox data can NEVER be sent as binary.
-            /* @todo: Disable until Horde_Imap_Client 3.0 */
-            // throw new Horde_Imap_Client_Exception(
-            //     'Client error: can not send mailbox to IMAP server as binary data.'
-            // );
-
-            // Temporary fix: send a blank mailbox string.
-            $this->_mailbox = Horde_Imap_Client_Mailbox::get('');
-        }
-
-        return false;
-    }
-
-    /**
-     */
-    public function length()
-    {
-        return strlen($this->_mailbox->utf7imap);
-    }
-
-    /**
-     */
-    public function getStream()
-    {
-        $stream = new Horde_Stream_Temp();
-        $stream->add($this->_mailbox->utf7imap);
-        return $stream;
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientDataFormatNilphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Nil.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Nil.php      2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Nil.php 2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,46 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Object representation of an IMAP NIL (RFC 3501 [4.5]).
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Data_Format_Nil extends Horde_Imap_Client_Data_Format
-{
-    /**
-     */
-    public function __construct($data = null)
-    {
-        // Don't store any data in object.
-    }
-
-    /**
-     */
-    public function __toString()
-    {
-        return '';
-    }
-
-    /**
-     */
-    public function escape()
-    {
-        return 'NIL';
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientDataFormatNstringphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Nstring.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Nstring.php  2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Nstring.php     2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,82 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Object representation of an IMAP nstring (NIL or string) (RFC 3501 [4.5]).
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Data_Format_Nstring extends Horde_Imap_Client_Data_Format_String
-{
-    /**
-     */
-    public function __construct($data = null)
-    {
-        /* Data can be null (NIL) here. */
-        if (is_null($data)) {
-            $this->_data = null;
-        } else {
-            parent::__construct($data);
-        }
-    }
-
-    /**
-     */
-    public function __toString()
-    {
-        return is_null($this->_data)
-            ? ''
-            : parent::__toString();
-    }
-
-    /**
-     */
-    public function escape()
-    {
-        return is_null($this->_data)
-            ? 'NIL'
-            : parent::escape();
-    }
-
-    /**
-     */
-    public function quoted()
-    {
-        return is_null($this->_data)
-            ? false
-            : parent::quoted();
-    }
-
-    /**
-     */
-    public function length()
-    {
-        return is_null($this->_data)
-            ? 0
-            : parent::length();
-    }
-
-    /**
-     */
-    public function getStream()
-    {
-        return is_null($this->_data)
-            ? new Horde_Stream_Temp()
-            : parent::length();
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientDataFormatNumberphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Number.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Number.php   2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Number.php      2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,41 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Object representation of an IMAP number (RFC 3501 [4.2]).
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Data_Format_Number extends Horde_Imap_Client_Data_Format
-{
-    /**
-     */
-    public function __toString()
-    {
-        return strval(intval($this->_data));
-    }
-
-    /**
-     */
-    public function verify()
-    {
-        if (!is_numeric($this->_data)) {
-            throw new Horde_Imap_Client_Data_Format_Exception('Illegal character in IMAP number.');
-        }
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientDataFormatStringphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/String.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/String.php   2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/String.php      2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,209 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Object representation of an IMAP string (RFC 3501 [4.3]).
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Data_Format_String extends Horde_Imap_Client_Data_Format
-{
-    /**
-     * String filter parameters.
-     *
-     * @var string
-     */
-    protected $_filter;
-
-    /**
-     * @param array $opts  Additional options:
-     *   - eol: (boolean) If true, normalize EOLs in input. @since 2.2.0
-     *   - skipscan: (boolean) If true, don't scan input for
-     *               binary/literal/quoted data. @since 2.2.0
-     */
-    public function __construct($data, array $opts = array())
-    {
-        /* String data is stored in a stream. */
-        $this->_data = new Horde_Stream_Temp();
-
-        $this->_filter = $this->_filterParams();
-
-        if (empty($opts['skipscan'])) {
-            stream_filter_register('horde_imap_client_string', 'Horde_Imap_Client_Data_Format_Filter_String');
-            $res = stream_filter_append($this->_data->stream, 'horde_imap_client_string', STREAM_FILTER_WRITE, $this->_filter);
-        } else {
-            $res = null;
-        }
-
-        if (empty($opts['eol'])) {
-            $res2 = null;
-        } else {
-            stream_filter_register('horde_eol', 'Horde_Stream_Filter_Eol');
-            $res2 = stream_filter_append($this->_data->stream, 'horde_eol', STREAM_FILTER_WRITE);
-        }
-
-        $this->_data->add($data);
-
-        if (!is_null($res)) {
-            stream_filter_remove($res);
-        }
-        if (!is_null($res2)) {
-            stream_filter_remove($res2);
-        }
-    }
-
-    /**
-     * Return the base string filter parameters.
-     *
-     * @return object  Filter parameters.
-     */
-    protected function _filterParams()
-    {
-        return new stdClass;
-    }
-
-    /**
-     */
-    public function __toString()
-    {
-        return $this->_data->getString(0);
-    }
-
-    /**
-     */
-    public function escape()
-    {
-        if ($this->literal()) {
-            throw new Horde_Imap_Client_Data_Format_Exception('String requires literal to output.');
-        }
-
-        return $this->quoted()
-            ? stream_get_contents($this->escapeStream())
-            : $this->_data->getString(0);
-    }
-
-    /**
-     * Return the escaped string as a stream.
-     *
-     * @return resource  The IMAP escaped stream.
-     */
-    public function escapeStream()
-    {
-        if ($this->literal()) {
-            throw new Horde_Imap_Client_Data_Format_Exception('String requires literal to output.');
-        }
-
-        rewind($this->_data->stream);
-
-        $stream = new Horde_Stream_Temp();
-        $stream->add($this->_data, true);
-
-        stream_filter_register('horde_imap_client_string_quote', 'Horde_Imap_Client_Data_Format_Filter_Quote');
-        stream_filter_append($stream->stream, 'horde_imap_client_string_quote', STREAM_FILTER_READ);
-
-        return $stream->stream;
-    }
-
-    /**
-     * Does this data item require quoted string output?
-     *
-     * @return boolean  True if quoted output is required.
-     */
-    public function quoted()
-    {
-        /* IMAP strings MUST be quoted if they are not a literal. */
-        return (!isset($this->_filter) || !$this->_filter->literal);
-    }
-
-    /**
-     * Force item to be output quoted.
-     */
-    public function forceQuoted()
-    {
-        $this->_filter = $this->_filterParams();
-        $this->_filter->binary = false;
-        $this->_filter->literal = false;
-        $this->_filter->quoted = true;
-    }
-
-    /**
-     * Does this data item require literal string output?
-     *
-     * @return boolean  True if literal output is required.
-     */
-    public function literal()
-    {
-        return (isset($this->_filter) && $this->_filter->literal);
-    }
-
-    /**
-     * Force item to be output as a literal.
-     */
-    public function forceLiteral()
-    {
-        $this->_filter = $this->_filterParams();
-        // Keep binary status, if set
-        $this->_filter->literal = true;
-        $this->_filter->quoted = false;
-    }
-
-    /**
-     * If literal output, is the data binary?
-     *
-     * @return boolean  True if the literal output is binary.
-     */
-    public function binary()
-    {
-        return (isset($this->_filter) && !empty($this->_filter->binary));
-    }
-
-    /**
-     * Force item to be output as a binary literal.
-     */
-    public function forceBinary()
-    {
-        $this->_filter = $this->_filterParams();
-        $this->_filter->binary = true;
-        $this->_filter->literal = true;
-        $this->_filter->quoted = false;
-    }
-
-    /**
-     * Return the length of the data.
-     *
-     * @since 2.2.0
-     *
-     * @return integer  Data length.
-     */
-    public function length()
-    {
-        return $this->_data->length();
-    }
-
-    /**
-     * Return the contents of the string as a stream object.
-     *
-     * @since 2.3.0
-     *
-     * @return Horde_Stream  The stream object.
-     */
-    public function getStream()
-    {
-        return $this->_data;
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientDataFormatphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format.php  2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format.php     2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,83 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Object representation of an IMAP data format (RFC 3501 [4]).
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Data_Format
-{
-    /**
-     * Data.
-     *
-     * @var mixed
-     */
-    protected $_data;
-
-    /**
-     * Constructor.
-     *
-     * @param mixed $data  Data.
-     */
-    public function __construct($data)
-    {
-        $this->_data = is_resource($data)
-            ? stream_get_contents($data, -1, 0)
-            : $data;
-    }
-
-    /**
-     * Returns the string value of the raw data.
-     *
-     * @return string  String value.
-     */
-    public function __toString()
-    {
-        return strval($this->_data);
-    }
-
-    /**
-     * Returns the raw data.
-     *
-     * @return mixed  Raw data.
-     */
-    public function getData()
-    {
-        return $this->_data;
-    }
-
-    /**
-     * Returns the data formatted for output to the IMAP server.
-     *
-     * @return string  IMAP escaped string.
-     */
-    public function escape()
-    {
-        return strval($this);
-    }
-
-    /**
-     * Verify the data.
-     *
-     * @throws Horde_Imap_Client_Data_Format_Exception
-     */
-    public function verify()
-    {
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientDataSyncphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Sync.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Sync.php    2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Sync.php       2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,268 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Mailbox synchronization results.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- * @since     2.2.0
- *
- * @property-read Horde_Imap_Client_Ids $flagsuids  List of messages with flag
- *                                                  changes.
- * @property-read Horde_Imap_Client_Ids $newmsgsuids  List of new messages.
- * @property-read Horde_Imap_Client_Ids $vanisheduids  List of messages that
- *                                                     have vanished.
- */
-class Horde_Imap_Client_Data_Sync
-{
-    /**
-     * Mappings of status() values to sync keys.
-     *
-     * @since 2.8.0
-     *
-     * @var array
-     */
-    static public $map = array(
-        'H' => 'highestmodseq',
-        'M' => 'messages',
-        'U' => 'uidnext',
-        'V' => 'uidvalidity'
-    );
-
-    /**
-     * Are there messages that have had flag changes?
-     *
-     * @var Horde_Imap_Client_Ids
-     */
-    public $flags = null;
-
-    /**
-     * The previous value of HIGHESTMODSEQ.
-     *
-     * @since 2.8.0
-     *
-     * @var integer
-     */
-    public $highestmodseq = null;
-
-    /**
-     * The synchronized mailbox.
-     *
-     * @var Horde_Imap_Client_Mailbox
-     */
-    public $mailbox;
-
-    /**
-     * The previous number of messages in the mailbox.
-     *
-     * @since 2.8.0
-     *
-     * @var integer
-     */
-    public $messages = null;
-
-    /**
-     * Are there new messages?
-     *
-     * @var boolean
-     */
-    public $newmsgs = null;
-
-    /**
-     * The previous value of UIDNEXT.
-     *
-     * @since 2.8.0
-     *
-     * @var integer
-     */
-    public $uidnext = null;
-
-    /**
-     * The previous value of UIDVALIDITY.
-     *
-     * @since 2.8.0
-     *
-     * @var integer
-     */
-    public $uidvalidity = null;
-
-    /**
-     * The UIDs of messages that are guaranteed to have vanished. This list is
-     * only guaranteed to be available if the server supports QRESYNC or a
-     * list of known UIDs is passed to the sync() method.
-     *
-     * @var Horde_Imap_Client_Ids
-     */
-    public $vanished = null;
-
-    /**
-     * UIDs of messages that have had flag changes.
-     *
-     * @var Horde_Imap_Client_Ids
-     */
-    protected $_flagsuids;
-
-    /**
-     * UIDs of new messages.
-     *
-     * @var Horde_Imap_Client_Ids
-     */
-    protected $_newmsgsuids;
-
-    /**
-     * UIDs of messages that have vanished.
-     *
-     * @var Horde_Imap_Client_Ids
-     */
-    protected $_vanisheduids;
-
-    /**
-     * Constructor.
-     *
-     * @param Horde_Imap_Client_Base $base_ob  Base driver object.
-     * @param mixed $mailbox                   Mailbox to sync.
-     * @param array $sync                      Token sync data.
-     * @param array $curr                      Current sync data.
-     * @param integer $criteria                Mask of criteria to return.
-     * @param Horde_Imap_Client_Ids $ids       List of known UIDs.
-     *
-     * @throws Horde_Imap_Client_Exception
-     * @throws Horde_Imap_Client_Exception_Sync
-     */
-    public function __construct(Horde_Imap_Client_Base $base_ob, $mailbox,
-                                $sync, $curr, $criteria, $ids)
-    {
-        foreach (self::$map as $key => $val) {
-            if (isset($sync[$key])) {
-                $this->$val = $sync[$key];
-            }
-        }
-
-        /* Check uidvalidity. */
-        if (!$this->uidvalidity || ($curr['V'] != $this->uidvalidity)) {
-            throw new Horde_Imap_Client_Exception_Sync('UIDs in cached mailbox have changed.', Horde_Imap_Client_Exception_Sync::UIDVALIDITY_CHANGED);
-        }
-
-        $this->mailbox = $mailbox;
-
-        /* This was a UIDVALIDITY check only. */
-        if (!$criteria) {
-            return;
-        }
-
-        $sync_all = ($criteria & Horde_Imap_Client::SYNC_ALL);
-
-        /* New messages. */
-        if ($sync_all ||
-            ($criteria & Horde_Imap_Client::SYNC_NEWMSGS) ||
-            ($criteria & Horde_Imap_Client::SYNC_NEWMSGSUIDS)) {
-            $this->newmsgs = empty($this->uidnext)
-                ? !empty($curr['U'])
-                : (!empty($curr['U']) && ($curr['U'] > $this->uidnext));
-
-            if ($this->newmsgs &&
-                ($sync_all ||
-                 ($criteria & Horde_Imap_Client::SYNC_NEWMSGSUIDS))) {
-                $new_ids = empty($this->uidnext)
-                    ? Horde_Imap_Client_Ids::ALL
-                    : ($this->uidnext . ':' . $curr['U']);
-
-                $squery = new Horde_Imap_Client_Search_Query();
-                $squery->ids($new_ids);
-                $sres = $base_ob->search($mailbox, $squery);
-
-                $this->_newmsgsuids = $sres['match'];
-            }
-        }
-
-        /* Do single status call to get all necessary data. */
-        if ($this->highestmodseq &&
-            ($sync_all ||
-             ($criteria & Horde_Imap_Client::SYNC_FLAGS) ||
-             ($criteria & Horde_Imap_Client::SYNC_FLAGSUIDS) ||
-             ($criteria & Horde_Imap_Client::SYNC_VANISHED) ||
-             ($criteria & Horde_Imap_Client::SYNC_VANISHEDUIDS))) {
-            $status_sync = $base_ob->status($mailbox, Horde_Imap_Client::STATUS_SYNCMODSEQ | Horde_Imap_Client::STATUS_SYNCFLAGUIDS | Horde_Imap_Client::STATUS_SYNCVANISHED);
-
-            if (!is_null($ids)) {
-                $ids = $base_ob->resolveIds($mailbox, $ids);
-            }
-        }
-
-        /* Flag changes. */
-        if ($sync_all || ($criteria & Horde_Imap_Client::SYNC_FLAGS)) {
-            $this->flags = $this->highestmodseq
-                ? ($this->highestmodseq != $curr['H'])
-                : true;
-        }
-
-        if ($sync_all || ($criteria & Horde_Imap_Client::SYNC_FLAGSUIDS)) {
-            if ($this->highestmodseq) {
-                if ($this->highestmodseq == $status_sync['syncmodseq']) {
-                    $this->_flagsuids = is_null($ids)
-                        ? $status_sync['syncflaguids']
-                        : $base_ob->getIdsOb(array_intersect($ids->ids, $status_sync['syncflaguids']->ids));
-                } else {
-                    $squery = new Horde_Imap_Client_Search_Query();
-                    $squery->modseq($this->highestmodseq + 1);
-                    $sres = $base_ob->search($mailbox, $squery, array(
-                        'ids' => $ids
-                    ));
-                    $this->_flagsuids = $sres['match'];
-                }
-            } else {
-                /* Without MODSEQ, need to mark all FLAGS as changed. */
-                $this->_flagsuids = $base_ob->resolveIds($mailbox, is_null($ids) ? $base_ob->getIdsOb(Horde_Imap_Client_Ids::ALL) : $ids);
-            }
-        }
-
-        /* Vanished messages. */
-        if ($sync_all ||
-            ($criteria & Horde_Imap_Client::SYNC_VANISHED) ||
-            ($criteria & Horde_Imap_Client::SYNC_VANISHEDUIDS)) {
-            if ($this->highestmodseq &&
-                ($this->highestmodseq == $status_sync['syncmodseq'])) {
-                $vanished = is_null($ids)
-                    ? $status_sync['syncvanisheduids']
-                    : $base_ob->getIdsOb(array_intersect($ids->ids, $status_sync['syncvanisheduids']->ids));
-            } else {
-                $vanished = $base_ob->vanished($mailbox, $this->highestmodseq ? $this->highestmodseq : 1, array(
-                    'ids' => $ids
-                ));
-            }
-
-            $this->vanished = (bool)count($vanished);
-            $this->_vanisheduids = $vanished;
-        }
-    }
-
-    /**
-     */
-    public function __get($name)
-    {
-        switch ($name) {
-        case 'flagsuids':
-        case 'newmsgsuids':
-        case 'vanisheduids':
-            $varname = '_' . $name;
-            return empty($this->$varname)
-                ? new Horde_Imap_Client_Ids()
-                : $this->$varname;
-        }
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientDataThreadphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Thread.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Thread.php  2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Thread.php     2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,169 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2008-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2008-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Object representing the threaded sort results from
- * Horde_Imap_Client_Base#thread().
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2008-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Data_Thread implements Countable, Serializable
-{
-    /**
-     * Internal thread data structure. Keys are base values, values are arrays
-     * with keys as the ID and values as the level.
-     *
-     * @var array
-     */
-    protected $_thread = array();
-
-    /**
-     * The index type.
-     *
-     * @var string
-     */
-    protected $_type;
-
-    /**
-     * Constructor.
-     *
-     * @param array $data   See $_thread.
-     * @param string $type  Either 'sequence' or 'uid'.
-     */
-    public function __construct($data, $type)
-    {
-        $this->_thread = $data;
-        $this->_type = $type;
-    }
-
-    /**
-     * Return the ID type.
-     *
-     * @return string  Either 'sequence' or 'uid'.
-     */
-    public function getType()
-    {
-        return $this->_type;
-    }
-
-    /**
-     * Return the sorted list of messages indices.
-     *
-     * @return Horde_Imap_Client_Ids  The sorted list of messages.
-     */
-    public function messageList()
-    {
-        return new Horde_Imap_Client_Ids($this->_getAllIndices(), $this->getType() == 'sequence');
-    }
-
-    /**
-     * Returns the list of messages in a thread.
-     *
-     * @param integer $index  An index contained in the thread.
-     *
-     * @return array  Keys are indices, values are objects with the following
-     *                properties:
-     *   - base: (integer) Base ID of the thread. If null, thread is a single
-     *           message.
-     *   - last: (boolean) If true, this is the last index in the sublevel.
-     *   - level: (integer) The sublevel of the index.
-     */
-    public function getThread($index)
-    {
-        reset($this->_thread);
-        while (list(,$v) = each($this->_thread)) {
-            if (isset($v[$index])) {
-                reset($v);
-
-                $ob = new stdClass;
-                $ob->base = (count($v) > 1) ? key($v) : null;
-                $ob->last = false;
-
-                $levels = $out = array();
-                $last = 0;
-
-                while (list($k2, $v2) = each($v)) {
-                    $ob2 = clone $ob;
-                    $ob2->level = $v2;
-                    $out[$k2] = $ob2;
-
-                    if (($last < $v2) && isset($levels[$v2])) {
-                        $out[$levels[$v2]]->last = true;
-                    }
-                    $levels[$v2] = $k2;
-                    $last = $v2;
-                }
-
-                foreach ($levels as $v) {
-                    $out[$v]->last = true;
-                }
-
-                return $out;
-            }
-        }
-
-        return array();
-    }
-
-    /* Countable methods. */
-
-    /**
-     */
-    public function count()
-    {
-        return count($this->_getAllIndices());
-    }
-
-    /* Serializable methods. */
-
-    /**
-     */
-    public function serialize()
-    {
-        return json_encode(array(
-            $this->_thread,
-            $this->_type
-        ));
-    }
-
-    /**
-     */
-    public function unserialize($data)
-    {
-        list($this->_thread, $this->_type) = json_decode($data, true);
-    }
-
-    /* Protected methods. */
-
-    /**
-     * Return all indices.
-     *
-     * @return array  An array of indices.
-     */
-    protected function _getAllIndices()
-    {
-        $out = array();
-
-        reset($this->_thread);
-        while (list(,$v) = each($this->_thread)) {
-            $out = array_merge($out, array_keys($v));
-        }
-
-        return $out;
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientDateTimephp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/DateTime.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/DateTime.php     2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/DateTime.php        2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,79 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * A wrapper around PHP's native DateTime class that handles improperly
- * formatted dates and adds a few features missing from the base object
- * (string representation; doesn't fail on bad date input).
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_DateTime extends DateTime
-{
-    /**
-     */
-    public function __construct($time = null)
-    {
-        $tz = new DateTimeZone('UTC');
-
-        try {
-            parent::__construct($time, $tz);
-            return;
-        } catch (Exception $e) {}
-
-        /* Bug #5717 - Check for UT vs. UTC. */
-        if (substr(rtrim($time), -3) == ' UT') {
-            try {
-                parent::__construct($time . 'C', $tz);
-                return;
-            } catch (Exception $e) {}
-        }
-
-        /* Bug #9847 - Catch paranthesized timezone information at end of date
-         * string. */
-        $date = preg_replace("/\s*\([^\)]+\)\s*$/", '', $time, -1, $i);
-        if ($i) {
-            try {
-                parent::__construct($date, $tz);
-                return;
-            } catch (Exception $e) {}
-        }
-
-        parent::__construct('@-1', $tz);
-    }
-
-    /**
-     * String representation: UNIX timestamp.
-     */
-    public function __toString()
-    {
-        return $this->error()
-            ? '0'
-            : $this->format('U');
-    }
-
-    /**
-     * Was this an unparseable date?
-     *
-     * @return boolean  True if unparseable.
-     */
-    public function error()
-    {
-        return ($this->format('U') == -1);
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientExceptionNoSupportExtensionphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Exception/NoSupportExtension.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Exception/NoSupportExtension.php 2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Exception/NoSupportExtension.php    2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,50 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Exception thrown for non-supported server extensions.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Exception_NoSupportExtension extends Horde_Imap_Client_Exception
-{
-    /**
-     * The extension not supported on the server.
-     *
-     * @var string
-     */
-    public $extension;
-
-    /**
-     * Constructor.
-     *
-     * @param string $extension  The extension not supported on the server.
-     * @param string $msg        A non-standard error message to use instead
-     *                           of the default.
-     */
-    public function __construct($extension, $msg = null)
-    {
-        $this->extension = $extension;
-
-        if (is_null($msg)) {
-            $msg = sprintf(Horde_Imap_Client_Translation::t("The server does not support the %s extension."), $extension);
-        }
-
-        parent::__construct($msg, self::NOT_SUPPORTED);
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientExceptionNoSupportPop3php"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Exception/NoSupportPop3.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Exception/NoSupportPop3.php      2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Exception/NoSupportPop3.php 2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,38 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Exception thrown for non-supported IMAP features on POP3 servers.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Exception_NoSupportPop3 extends Horde_Imap_Client_Exception
-{
-    /**
-     * Constructor.
-     *
-     * @param string $feature  The feature not supported in POP3.
-     */
-    public function __construct($feature)
-    {
-        parent::__construct(
-            sprintf(Horde_Imap_Client_Translation::t("%s not supported on POP3 servers."), $feature),
-            self::NOT_SUPPORTED
-        );
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientExceptionSearchCharsetphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Exception/SearchCharset.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Exception/SearchCharset.php      2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Exception/SearchCharset.php 2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,49 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Exception thrown if search query text cannot be converted to different
- * charset.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Exception_SearchCharset extends Horde_Imap_Client_Exception
-{
-    /**
-     * Charset that was attempted to be converted to.
-     *
-     * @var string
-     */
-    public $charset;
-
-    /**
-     * Constructor.
-     *
-     * @param string $charset  The charset that was attempted to be converted
-     *                         to.
-     */
-    public function __construct($charset)
-    {
-        $this->charset = $charset;
-
-        parent::__construct(
-            Horde_Imap_Client_Translation::t("Cannot convert search query text to new charset"),
-            self::BADCHARSET
-        );
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientExceptionServerResponsephp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Exception/ServerResponse.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Exception/ServerResponse.php     2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Exception/ServerResponse.php        2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,88 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Exception thrown for server error responses.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- *
- * @property-read string $command  The command that caused the BAD/NO error
- *                                 status.
- * @property-read array $resp_data  The response data array.
- * @property-read integer $status  Server error status.
- */
-class Horde_Imap_Client_Exception_ServerResponse extends Horde_Imap_Client_Exception
-{
-    /**
-     * Pipeline object.
-     *
-     * @var Horde_Imap_Client_Interaction_Pipeline
-     */
-    protected $_pipeline;
-
-    /**
-     * Server response object.
-     *
-     * @var Horde_Imap_Client_Interaction_Server
-     */
-    protected $_server;
-
-    /**
-     * Constructor.
-     *
-     * @param string $msg                                       Error message.
-     * @param integer $code                                     Error code.
-     * @param Horde_Imap_Client_Interaction_Server $server      Server ob.
-     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline ob.
-     */
-    public function __construct(
-        $msg = null,
-        $code = 0,
-        Horde_Imap_Client_Interaction_Server $server,
-        Horde_Imap_Client_Interaction_Pipeline $pipeline
-    )
-    {
-        $this->details = strval($server->token);
-
-        $this->_pipeline = $pipeline;
-        $this->_server = $server;
-
-        parent::__construct($msg, $code);
-    }
-
-    /**
-     */
-    public function __get($name)
-    {
-        switch ($name) {
-        case 'command':
-            return ($this->_server instanceof Horde_Imap_Client_Interaction_Server_Tagged)
-                ? $this->_pipeline->getCmd($this->_server->tag)->getCommand()
-                : null;
-
-        case 'resp_data':
-            return $this->_pipeline->data;
-
-        case 'status':
-            return $this->_server->status;
-
-        default:
-            return parent::__get($name);
-        }
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientExceptionSyncphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Exception/Sync.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Exception/Sync.php       2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Exception/Sync.php  2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,33 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Exception thrown for mailbox synchronization errors.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Exception_Sync extends Horde_Exception_Wrapped
-{
-    /* Error message codes. */
-
-    // Token could not be parsed.
-    const BAD_TOKEN = 1;
-
-    // UIDVALIDITY of the mailbox changed.
-    const UIDVALIDITY_CHANGED = 2;
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientExceptionphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Exception.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Exception.php    2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Exception.php       2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,184 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2008-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2008-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Exception handler for the Horde_Imap_Client package.
- *
- * Additional server debug information MAY be found in the $details
- * property.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2008-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Exception extends Horde_Exception_Wrapped
-{
-    /* Error message codes. */
-
-    // Unspecified error (default)
-    const UNSPECIFIED = 0;
-
-    // There was an unrecoverable error in UTF7IMAP -> UTF8 conversion.
-    const UTF7IMAP_CONVERSION = 3;
-
-    // The server ended the connection.
-    const DISCONNECT = 4;
-
-    // The charset used in the search query is not supported on the server.
-    const BADCHARSET = 5;
-
-    // There were errors parsing the MIME/RFC 2822 header of the part.
-    const PARSEERROR = 6;
-
-    // The server could not decode the MIME part (see RFC 3516)
-    const UNKNOWNCTE = 7;
-
-    // The comparator specified by setComparator() was not recognized by the
-    // IMAP server
-    const BADCOMPARATOR = 9;
-
-    // RFC 4551 [3.1.2] - All mailboxes are not required to support
-    // mod-sequences.
-    const MBOXNOMODSEQ = 10;
-
-    // Thrown if server denies the network connection.
-    const SERVER_CONNECT = 11;
-
-    // Thrown if read error for server response.
-    const SERVER_READERROR = 12;
-
-    // Thrown if write error in server interaction.
-    const SERVER_WRITEERROR = 16;
-
-    // Thrown on CATENATE if the URL is invalid.
-    const CATENATE_BADURL = 13;
-
-    // Thrown on CATENATE if the message was too big.
-    const CATENATE_TOOBIG = 14;
-
-    // Thrown on CREATE if special-use attribute is not supported.
-    const USEATTR = 15;
-
-    // The user did not have permissions to carry out the operation.
-    const NOPERM = 17;
-
-    // The operation was not successful because another user is holding
-    // a necessary resource. The operation may succeed if attempted later.
-    const INUSE = 18;
-
-    // The operation failed because data on the server was corrupt.
-    const CORRUPTION = 19;
-
-    // The operation failed because it exceeded some limit on the server.
-    const LIMIT = 20;
-
-    // The operation failed because the user is over their quota.
-    const OVERQUOTA = 21;
-
-    // The operation failed because the requested creation object already
-    // exists.
-    const ALREADYEXISTS = 22;
-
-    // The operation failed because the requested deletion object did not
-    // exist.
-    const NONEXISTENT = 23;
-
-    // Setting metadata failed because the size of its value is too large.
-    // The maximum octet count the server is willing to accept will be
-    // in the exception message string.
-    const METADATA_MAXSIZE = 24;
-
-    // Setting metadata failed because the maximum number of allowed
-    // annotations has already been reached.
-    const METADATA_TOOMANY = 25;
-
-    // Setting metadata failed because the server does not support private
-    // annotations on one of the specified mailboxes.
-    const METADATA_NOPRIVATE = 26;
-
-    // Invalid metadata entry.
-    const METADATA_INVALID = 27;
-
-
-    // Login failures
-
-    // Could not start mandatory TLS connection.
-    const LOGIN_TLSFAILURE = 100;
-
-    // Could not find an available authentication method.
-    const LOGIN_NOAUTHMETHOD = 101;
-
-    // Generic authentication failure.
-    const LOGIN_AUTHENTICATIONFAILED = 102;
-
-    // Remote server is unavailable.
-    const LOGIN_UNAVAILABLE = 103;
-
-    // Authentication succeeded, but authorization failed.
-    const LOGIN_AUTHORIZATIONFAILED = 104;
-
-    // Authentication is no longer permitted with this passphrase.
-    const LOGIN_EXPIRED = 105;
-
-    // Login requires privacy.
-    const LOGIN_PRIVACYREQUIRED = 106;
-
-
-    // Mailbox access failures
-
-    // Could not open/access mailbox
-    const MAILBOX_NOOPEN = 200;
-
-    // Could not complete the command because the mailbox is read-only
-    const MAILBOX_READONLY = 201;
-
-
-    // POP3 specific error codes
-
-    // Temporary issue. Generally, there is no need to alarm the user for
-    // errors of this type.
-    const POP3_TEMP_ERROR = 300;
-
-    // Permanent error indicated by server.
-    const POP3_PERM_ERROR = 301;
-
-
-    // Unsupported feature error codes
-
-    // Function/feature is not supported on this server.
-    const NOT_SUPPORTED = 400;
-
-
-    /**
-     * Allow the error message to be altered.
-     *
-     * @param string $msg  Error message.
-     */
-    public function setMessage($msg)
-    {
-        $this->message = strval($msg);
-    }
-
-    /**
-     * Allow the error code to be altered.
-     *
-     * @param integer $code  Error code.
-     */
-    public function setCode($code)
-    {
-        $this->code = intval($code);
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientFetchQueryphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Fetch/Query.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Fetch/Query.php  2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Fetch/Query.php     2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,380 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2011-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2011-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Fetch query object for use with Horde_Imap_Client_Base#fetch().
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2011-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Fetch_Query implements ArrayAccess, Countable, Iterator
-{
-    /**
-     * Internal data array.
-     *
-     * @var array
-     */
-    protected $_data = array();
-
-    /**
-     * Get the full text of the message.
-     *
-     * @param array $opts  The following options are available:
-     *   - length: (integer) The length of the substring to return.
-     *             DEFAULT: The entire text is returned.
-     *   - peek: (boolean) If set, does not set the '\Seen' flag on the
-     *            message.
-     *            DEFAULT: The seen flag is set.
-     *   - start: (integer) If a portion of the full text is desired to be
-     *            returned, the starting position is identified here.
-     *            DEFAULT: The entire text is returned.
-     */
-    public function fullText(array $opts = array())
-    {
-        $this->_data[Horde_Imap_Client::FETCH_FULLMSG] = $opts;
-    }
-
-    /**
-     * Return header text.
-     *
-     * Header text is defined only for the base RFC 2822 message or
-     * message/rfc822 parts.
-     *
-     * @param array $opts  The following options are available:
-     *   - id: (string) The MIME ID to obtain the header text for.
-     *         DEFAULT: The header text for the base message will be
-     *         returned.
-     *   - length: (integer) The length of the substring to return.
-     *             DEFAULT: The entire text is returned.
-     *   - peek: (boolean) If set, does not set the '\Seen' flag on the
-     *           message.
-     *           DEFAULT: The seen flag is set.
-     *   - start: (integer) If a portion of the full text is desired to be
-     *            returned, the starting position is identified here.
-     *            DEFAULT: The entire text is returned.
-     */
-    public function headerText(array $opts = array())
-    {
-        $id = isset($opts['id'])
-            ? $opts['id']
-            : 0;
-        $this->_data[Horde_Imap_Client::FETCH_HEADERTEXT][$id] = $opts;
-    }
-
-    /**
-     * Return body text.
-     *
-     * Body text is defined only for the base RFC 2822 message or
-     * message/rfc822 parts.
-     *
-     * @param array $opts  The following options are available:
-     *   - id: (string) The MIME ID to obtain the body text for.
-     *         DEFAULT: The body text for the entire message will be
-     *         returned.
-     *   - length: (integer) The length of the substring to return.
-     *             DEFAULT: The entire text is returned.
-     *   - peek: (boolean) If set, does not set the '\Seen' flag on the
-     *           message.
-     *           DEFAULT: The seen flag is set.
-     *   - start: (integer) If a portion of the full text is desired to be
-     *            returned, the starting position is identified here.
-     *            DEFAULT: The entire text is returned.
-     */
-    public function bodyText(array $opts = array())
-    {
-        $id = isset($opts['id'])
-            ? $opts['id']
-            : 0;
-        $this->_data[Horde_Imap_Client::FETCH_BODYTEXT][$id] = $opts;
-    }
-
-    /**
-     * Return MIME header text.
-     *
-     *  MIME header text is defined only for non-RFC 2822 messages and
-     *  non-message/rfc822 parts.
-     *
-     * @param string $id   The MIME ID to obtain the MIME header text for.
-     * @param array $opts  The following options are available:
-     *   - length: (integer) The length of the substring to return.
-     *             DEFAULT: The entire text is returned.
-     *   - peek: (boolean) If set, does not set the '\Seen' flag on the
-     *           message.
-     *           DEFAULT: The seen flag is set.
-     *   - start: (integer) If a portion of the full text is desired to be
-     *            returned, the starting position is identified here.
-     *            DEFAULT: The entire text is returned.
-     */
-    public function mimeHeader($id, array $opts = array())
-    {
-        $this->_data[Horde_Imap_Client::FETCH_MIMEHEADER][$id] = $opts;
-    }
-
-    /**
-     * Return the body part data for a MIME ID.
-     *
-     * @param string $id   The MIME ID to obtain the body part text for.
-     * @param array $opts  The following options are available:
-     *   - decode: (boolean) Attempt to server-side decode the bodypart data
-     *             if it is MIME transfer encoded.
-     *             DEFAULT: false
-     *   - length: (integer) The length of the substring to return.
-     *             DEFAULT: The entire text is returned.
-     *   - peek: (boolean) If set, does not set the '\Seen' flag on the
-     *           message.
-     *           DEFAULT: The seen flag is set.
-     *   - start: (integer) If a portion of the full text is desired to be
-     *            returned, the starting position is identified here.
-     *            DEFAULT: The entire text is returned.
-     */
-    public function bodyPart($id, array $opts = array())
-    {
-        $this->_data[Horde_Imap_Client::FETCH_BODYPART][$id] = $opts;
-    }
-
-    /**
-     * Returns the decoded body part size for a MIME ID.
-     *
-     * @param string $id  The MIME ID to obtain the decoded body part size
-     *                    for.
-     */
-    public function bodyPartSize($id)
-    {
-        $this->_data[Horde_Imap_Client::FETCH_BODYPARTSIZE][$id] = true;
-    }
-
-    /**
-     * Returns RFC 2822 header text that matches a search string.
-     *
-     * This header search work only with the base RFC 2822 message or
-     * message/rfc822 parts.
-     *
-     * @param string $label  A unique label associated with this particular
-     *                       search. This is how the results are stored.
-     * @param array $search  The search string(s) (case-insensitive).
-     * @param array $opts    The following options are available:
-     *   - cache: (boolean) If true, and 'peek' is also true, will cache
-     *            the result of this call.
-     *            DEFAULT: false
-     *   - id: (string) The MIME ID to search.
-     *         DEFAULT: The base message part
-     *   - length: (integer) The length of the substring to return.
-     *             DEFAULT: The entire text is returned.
-     *   - notsearch: (boolean) Do a 'NOT' search on the headers.
-     *                DEFAULT: false
-     *   - peek: (boolean) If set, does not set the '\Seen' flag on the
-     *           message.
-     *           DEFAULT: The seen flag is set.
-     *   - start: (integer) If a portion of the full text is desired to be
-     *            returned, the starting position is identified here.
-     *            DEFAULT: The entire text is returned.
-     */
-    public function headers($label, $search, array $opts = array())
-    {
-        $this->_data[Horde_Imap_Client::FETCH_HEADERS][$label] = array_merge($opts, array(
-            'headers' => $search
-        ));
-    }
-
-    /**
-     * Return MIME structure information.
-     */
-    public function structure()
-    {
-        $this->_data[Horde_Imap_Client::FETCH_STRUCTURE] = true;
-    }
-
-    /**
-     * Return envelope header data.
-     */
-    public function envelope()
-    {
-        $this->_data[Horde_Imap_Client::FETCH_ENVELOPE] = true;
-    }
-
-    /**
-     * Return flags set for the message.
-     */
-    public function flags()
-    {
-        $this->_data[Horde_Imap_Client::FETCH_FLAGS] = true;
-    }
-
-    /**
-     * Return the internal (IMAP) date of the message.
-     */
-    public function imapDate()
-    {
-        $this->_data[Horde_Imap_Client::FETCH_IMAPDATE] = true;
-    }
-
-    /**
-     * Return the size (in bytes) of the message.
-     */
-    public function size()
-    {
-        $this->_data[Horde_Imap_Client::FETCH_SIZE] = true;
-    }
-
-    /**
-     * Return the unique ID of the message.
-     */
-    public function uid()
-    {
-        $this->_data[Horde_Imap_Client::FETCH_UID] = true;
-    }
-
-    /**
-     * Return the sequence number of the message.
-     */
-    public function seq()
-    {
-        $this->_data[Horde_Imap_Client::FETCH_SEQ] = true;
-    }
-
-    /**
-     * Return the mod-sequence value for the message.
-     *
-     * The server must support the CONDSTORE IMAP extension, and the mailbox
-     * must support mod-sequences.
-     */
-    public function modseq()
-    {
-        $this->_data[Horde_Imap_Client::FETCH_MODSEQ] = true;
-    }
-
-    /**
-     * Does the query contain the given criteria?
-     *
-     * @param integer $criteria  The criteria to remove.
-     *
-     * @return boolean  True if the query contains the given criteria.
-     */
-    public function contains($criteria)
-    {
-        return isset($this->_data[$criteria]);
-    }
-
-    /**
-     * Remove an entry under a given criteria.
-     *
-     * @param integer $criteria  Criteria ID.
-     * @param string $key        The key to remove.
-     */
-    public function remove($criteria, $key)
-    {
-        if (isset($this->_data[$criteria]) &&
-            is_array($this->_data[$criteria])) {
-            unset($this->_data[$criteria][$key]);
-            if (empty($this->_data[$criteria])) {
-                unset($this->_data[$criteria]);
-            }
-        }
-    }
-
-    /**
-     * Returns a MD5 hash of the current query object.
-     *
-     * @return string  MD5 hash.
-     */
-    public function hash()
-    {
-        return hash('md5', serialize($this));
-    }
-
-    /* ArrayAccess methods. */
-
-    /**
-     */
-    public function offsetExists($offset)
-    {
-        return isset($this->_data[$offset]);
-    }
-
-    /**
-     */
-    public function offsetGet($offset)
-    {
-        return isset($this->_data[$offset])
-            ? $this->_data[$offset]
-            : null;
-    }
-
-    /**
-     */
-    public function offsetSet($offset, $value)
-    {
-        $this->_data[$offset] = $value;
-    }
-
-    /**
-     */
-    public function offsetUnset($offset)
-    {
-        unset($this->_data[$offset]);
-    }
-
-    /* Countable methods. */
-
-    /**
-     */
-    public function count()
-    {
-        return count($this->_data);
-    }
-
-    /* Iterator methods. */
-
-    /**
-     */
-    public function current()
-    {
-        $opts = current($this->_data);
-
-        return (!empty($opts) && ($this->key() == Horde_Imap_Client::FETCH_BODYPARTSIZE))
-            ? array_keys($opts)
-            : $opts;
-    }
-
-    /**
-     */
-    public function key()
-    {
-        return key($this->_data);
-    }
-
-    /**
-     */
-    public function next()
-    {
-        next($this->_data);
-    }
-
-    /**
-     */
-    public function rewind()
-    {
-        reset($this->_data);
-    }
-
-    /**
-     */
-    public function valid()
-    {
-        return !is_null($this->key());
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientFetchResultsphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Fetch/Results.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Fetch/Results.php        2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Fetch/Results.php   2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,179 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Fetch results object for use with Horde_Imap_Client_Base#fetch().
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- *
- * @property-read integer $key_type  The key type (sequence or UID).
- */
-class Horde_Imap_Client_Fetch_Results implements ArrayAccess, Countable, IteratorAggregate
-{
-    /* Key type constants. */
-    const SEQUENCE = 1;
-    const UID = 2;
-
-    /**
-     * Internal data array.
-     *
-     * @var array
-     */
-    protected $_data = array();
-
-    /**
-     * Key type.
-     *
-     * @var integer
-     */
-    protected $_keyType;
-
-    /**
-     * Class to use when creating a new fetch object.
-     *
-     * @var string
-     */
-    protected $_obClass;
-
-    /**
-     * Constructor.
-     *
-     * @param string $ob_class   Class to use when creating a new fetch
-     *                           object.
-     * @param integer $key_type  Key type.
-     */
-    public function __construct($ob_class = 'Horde_Imap_Client_Data_Fetch',
-                                $key_type = self::UID)
-    {
-        $this->_obClass = $ob_class;
-        $this->_keyType = $key_type;
-    }
-
-    /**
-     */
-    public function __get($name)
-    {
-        switch ($name) {
-        case 'key_type':
-            return $this->_keyType;
-        }
-    }
-
-    /**
-     * Return a fetch object, creating and storing an empty object in the
-     * results set if it doesn't currently exist.
-     *
-     * @param string $key  The key to retrieve.
-     *
-     * @return Horde_Imap_Client_Data_Fetch  The fetch object.
-     */
-    public function get($key)
-    {
-        if (!isset($this->_data[$key])) {
-            $this->_data[$key] = new $this->_obClass();
-        }
-
-        return $this->_data[$key];
-    }
-
-    /**
-     * Return the list of IDs.
-     *
-     * @return array  ID list.
-     */
-    public function ids()
-    {
-        ksort($this->_data);
-        return array_keys($this->_data);
-    }
-
-    /**
-     * Return the first fetch object in the results, if there is only one
-     * object.
-     *
-     * @return null|Horde_Imap_Client_Data_Fetch  The fetch object if there is
-     *                                            only one object, or null.
-     */
-    public function first()
-    {
-        return (count($this->_data) == 1)
-            ? reset($this->_data)
-            : null;
-    }
-
-    /**
-     * Clears all fetch results.
-     *
-     * @since 2.6.0
-     */
-    public function clear()
-    {
-        $this->_data = array();
-    }
-
-    /* ArrayAccess methods. */
-
-    /**
-     */
-    public function offsetExists($offset)
-    {
-        return isset($this->_data[$offset]);
-    }
-
-    /**
-     */
-    public function offsetGet($offset)
-    {
-        return isset($this->_data[$offset])
-            ? $this->_data[$offset]
-            : null;
-    }
-
-    /**
-     */
-    public function offsetSet($offset, $value)
-    {
-        $this->_data[$offset] = $value;
-    }
-
-    /**
-     */
-    public function offsetUnset($offset)
-    {
-        unset($this->_data[$offset]);
-    }
-
-    /* Countable methods. */
-
-    /**
-     */
-    public function count()
-    {
-        return count($this->_data);
-    }
-
-    /* IteratorAggregate methods. */
-
-    /**
-     */
-    public function getIterator()
-    {
-        ksort($this->_data);
-        return new ArrayIterator($this->_data);
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientIdsMapphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Ids/Map.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Ids/Map.php      2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Ids/Map.php 2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,236 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * An object implementing lookups between UIDs and message sequence numbers.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- * @since     2.1.0
- *
- * @property-read array $map  The raw ID mapping data.
- * @property-read Horde_Imap_Client_Ids $seq  The sorted sequence values.
- * @property-read Horde_Imap_Client_Ids $uids  The sorted UIDs.
- */
-class Horde_Imap_Client_Ids_Map implements Countable, IteratorAggregate, Serializable
-{
-    /**
-     * Sequence -> UID mapping.
-     *
-     * @var array
-     */
-    protected $_ids = array();
-
-    /**
-     * Is the array sorted?
-     *
-     * @var boolean
-     */
-    protected $_sorted = true;
-
-    /**
-     * Constructor.
-     *
-     * @param array $ids  Array of sequence -> UID mapping.
-     */
-    public function __construct(array $ids = array())
-    {
-        $this->update($ids);
-    }
-
-    /**
-     */
-    public function __get($name)
-    {
-        switch ($name) {
-        case 'map':
-            return $this->_ids;
-
-        case 'seq':
-            $this->sort();
-            return new Horde_Imap_Client_Ids(array_keys($this->_ids), true);
-
-        case 'uids':
-            $this->sort();
-            return new Horde_Imap_Client_Ids($this->_ids);
-        }
-    }
-
-    /**
-     * Updates the mapping.
-     *
-     * @param array $ids  Array of sequence -> UID mapping.
-     *
-     * @return boolean  True if the mapping changed.
-     */
-    public function update($ids)
-    {
-        if (empty($ids)) {
-            return false;
-        } elseif (empty($this->_ids)) {
-            $this->_ids = $ids;
-            $change = true;
-        } else {
-            $change = false;
-            foreach ($ids as $k => $v) {
-                if (!isset($this->_ids[$k]) || ($this->_ids[$k] != $v)) {
-                    $this->_ids[$k] = $v;
-                    $change = true;
-                }
-            }
-        }
-
-        if ($change) {
-            $this->_sorted = false;
-        }
-
-        return $change;
-    }
-
-    /**
-     * Create a Sequence <-> UID lookup table.
-     *
-     * @param Horde_Imap_Client_Ids $ids  IDs to lookup.
-     *
-     * @return array  Keys are sequence numbers, values are UIDs.
-     */
-    public function lookup(Horde_Imap_Client_Ids $ids)
-    {
-        if ($ids->all) {
-            return $this->_ids;
-        } elseif ($ids->sequence) {
-            return array_intersect_key($this->_ids, array_flip($ids->ids));
-        }
-
-        return array_intersect($this->_ids, $ids->ids);
-    }
-
-    /**
-     * Removes messages from the ID mapping.
-     *
-     * @param Horde_Imap_Client_Ids $ids  IDs to remove.
-     */
-    public function remove(Horde_Imap_Client_Ids $ids)
-    {
-        /* For sequence numbers, we need to reindex anytime we have an index
-         * that appears equal to or after a previously seen index. If an IMAP
-         * server is smart, it will expunge in reverse order instead. */
-        if ($ids->sequence) {
-            $remove = $ids->ids;
-        } else {
-            $ids->sort();
-            $remove = array_reverse(array_keys($this->lookup($ids)));
-        }
-
-        if (empty($remove)) {
-            return;
-        }
-
-        $this->sort();
-
-        /* Find the minimum sequence number to remove. We know entries before
-         * this are untouched so no need to process them multiple times. */
-        $first = min($remove);
-        $edit = $newids = array();
-        foreach (array_keys($this->_ids) as $i => $seq) {
-            if ($seq >= $first) {
-                $i += (($seq == $first) ? 0 : 1);
-                $newids = array_slice($this->_ids, 0, $i, true);
-                $edit = array_slice($this->_ids, $i + (($seq == $first) ? 0 : 1), null, true);
-                break;
-            }
-        }
-
-        if (!empty($edit)) {
-            foreach ($remove as $val) {
-                $found = false;
-                $tmp = array();
-
-                foreach (array_keys($edit) as $i => $seq) {
-                    if ($found) {
-                        $tmp[$seq - 1] = $edit[$seq];
-                    } elseif ($seq >= $val) {
-                        $tmp = array_slice($edit, 0, ($seq == $val) ? $i : $i + 1, true);
-                        $found = true;
-                    }
-                }
-
-                $edit = $tmp;
-            }
-        }
-
-        $this->_ids = $newids + $edit;
-    }
-
-    /**
-     * Sort the map.
-     */
-    public function sort()
-    {
-        if (!$this->_sorted) {
-            ksort($this->_ids, SORT_NUMERIC);
-            $this->_sorted = true;
-        }
-    }
-
-    /* Countable methods. */
-
-    /**
-     */
-    public function count()
-    {
-        return count($this->_ids);
-    }
-
-    /* IteratorAggregate method. */
-
-    /**
-     */
-    public function getIterator()
-    {
-        return new ArrayIterator($this->_ids);
-    }
-
-    /* Serializable methods. */
-
-    /**
-     */
-    public function serialize()
-    {
-        /* Sort before storing; provides more compressible representation. */
-        $this->sort();
-
-        return json_encode(array(
-            strval(new Horde_Imap_Client_Ids(array_keys($this->_ids))),
-            strval(new Horde_Imap_Client_Ids(array_values($this->_ids)))
-        ));
-    }
-
-    /**
-     */
-    public function unserialize($data)
-    {
-        $data = json_decode($data, true);
-
-        $keys = new Horde_Imap_Client_Ids($data[0]);
-        $vals = new Horde_Imap_Client_Ids($data[1]);
-        $this->_ids = array_combine($keys->ids, $vals->ids);
-
-        /* Guaranteed to be sorted if unserializing. */
-        $this->_sorted = true;
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientIdsPop3php"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Ids/Pop3.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Ids/Pop3.php     2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Ids/Pop3.php        2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,53 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2011-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2011-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Wrapper around Ids object that correctly handles POP3 UID strings.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2011-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Ids_Pop3 extends Horde_Imap_Client_Ids
-{
-    /**
-     * Create a POP3 message sequence string.
-     *
-     * Index Format: UID1[SPACE]UID2...
-     *
-     * @param boolean $sort  Not used in this class.
-     *
-     * @return string  The POP3 message sequence string.
-     */
-    protected function _toSequenceString($sort = true)
-    {
-        /* Use space as delimiter as it is the only printable ASCII character
-         * that is not allowed as part of the UID (RFC 1939 [7]). */
-        return implode(' ', count($this->_ids) > 25000 ? array_unique($this->_ids) : array_keys(array_flip($this->_ids)));
-    }
-
-    /**
-     * Parse a POP3 message sequence string into a list of indices.
-     *
-     * @param string $str  The POP3 message sequence string.
-     *
-     * @return array  An array of UIDs.
-     */
-    protected function _fromSequenceString($str)
-    {
-        return explode(' ', trim($str));
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientIdsphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Ids.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Ids.php  2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Ids.php     2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,449 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2011-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2011-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * An object that provides a way to identify a list of IMAP indices.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2011-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- *
- * @property-read boolean $all  Does this represent an ALL message set?
- * @property-read array $ids  The list of IDs.
- * @property-read boolean $largest  Does this represent the largest ID in use?
- * @property-read string $range_string  Generates a range string consisting of
- *                                      all messages between begin and end of
- *                                      ID list.
- * @property-read boolean $search_res  Does this represent a search result?
- * @property-read boolean $sequence  Are these sequence IDs? If false, these
- *                                   are UIDs.
- * @property-read boolean $special  True if this is a "special" ID
- *                                  representation.
- * @property-read string $tostring  Return the non-sorted string
- *                                  representation.
- * @property-read string $tostring_sort  Return the sorted string
- *                                       representation.
- */
-class Horde_Imap_Client_Ids implements Countable, Iterator, Serializable
-{
-    /* "Special" representation constants. */
-    const ALL = "\01";
-    const SEARCH_RES = "\02";
-    const LARGEST = "\03";
-
-    /**
-     * Allow duplicate IDs?
-     *
-     * @var boolean
-     */
-    public $duplicates = false;
-
-    /**
-     * List of IDs.
-     *
-     * @var mixed
-     */
-    protected $_ids = array();
-
-    /**
-     * Are IDs message sequence numbers?
-     *
-     * @var boolean
-     */
-    protected $_sequence = false;
-
-    /**
-     * Are IDs sorted?
-     *
-     * @var boolean
-     */
-    protected $_sorted = false;
-
-    /**
-     * Constructor.
-     *
-     * @param mixed $ids         See self::add().
-     * @param boolean $sequence  Are $ids message sequence numbers?
-     */
-    public function __construct($ids = null, $sequence = false)
-    {
-        $this->add($ids);
-        $this->_sequence = $sequence;
-    }
-
-    /**
-     */
-    public function __get($name)
-    {
-        switch ($name) {
-        case 'all':
-            return ($this->_ids === self::ALL);
-
-        case 'ids':
-            return is_array($this->_ids)
-                ? $this->_ids
-                : array();
-
-        case 'largest':
-            return ($this->_ids === self::LARGEST);
-
-        case 'range_string':
-            if (!count($this)) {
-                return '';
-            }
-
-            $this->sort();
-            $min = reset($this->_ids);
-            $max = end($this->_ids);
-
-            return ($min == $max)
-                ? $min
-                : $min . ':' . $max;
-
-        case 'search_res':
-            return ($this->_ids === self::SEARCH_RES);
-
-        case 'sequence':
-            return (bool)$this->_sequence;
-
-        case 'special':
-            return is_string($this->_ids);
-
-        case 'tostring':
-        case 'tostring_sort':
-            if ($this->all) {
-                return '1:*';
-            } elseif ($this->largest) {
-                return '*';
-            } elseif ($this->search_res) {
-                return '$';
-            }
-            return strval($this->_toSequenceString($name == 'tostring_sort'));
-        }
-    }
-
-    /**
-     */
-    public function __toString()
-    {
-        return $this->tostring;
-    }
-
-    /**
-     * Add IDs to the current object.
-     *
-     * @param mixed $ids  Either self::ALL, self::SEARCH_RES, self::LARGEST,
-     *                    Horde_Imap_Client_Ids object, array, or sequence
-     *                    string.
-     */
-    public function add($ids)
-    {
-        if (!is_null($ids)) {
-            $add = array();
-
-            if (is_string($ids) &&
-                in_array($ids, array(self::ALL, self::SEARCH_RES, self::LARGEST))) {
-                $this->_ids = $ids;
-                $this->_sorted = false;
-                return;
-            }
-
-            if ($ids instanceof Horde_Imap_Client_Ids) {
-                $add = $ids->ids;
-            } elseif (is_array($ids)) {
-                $add = $ids;
-            } elseif (is_string($ids) || is_integer($ids)) {
-                if (is_numeric($ids)) {
-                    $add = array($ids);
-                } else {
-                    $add = $this->_fromSequenceString($ids);
-                }
-            }
-
-            if (!empty($add)) {
-                $this->_ids = is_array($this->_ids)
-                    ? array_merge($this->_ids, $add)
-                    : $add;
-                if (!$this->duplicates) {
-                    $this->_ids = (count($this->_ids) > 25000)
-                        ? array_unique($this->_ids)
-                        : array_keys(array_flip($this->_ids));
-                }
-                $this->_sorted = false;
-            }
-        }
-    }
-
-    /**
-     * Is this object empty (i.e. does not contain IDs)?
-     *
-     * @return boolean  True if object is empty.
-     */
-    public function isEmpty()
-    {
-        return (is_array($this->_ids) && !count($this->_ids));
-    }
-
-    /**
-     * Reverses the order of the IDs.
-     */
-    public function reverse()
-    {
-        if (is_array($this->_ids)) {
-            $this->_ids = array_reverse($this->_ids);
-        }
-    }
-
-    /**
-     * Sorts the IDs numerically.
-     */
-    public function sort()
-    {
-        if (!$this->_sorted && is_array($this->_ids)) {
-            sort($this->_ids, SORT_NUMERIC);
-            $this->_sorted = true;
-        }
-    }
-
-    /**
-     * Split the sequence string at an approximate length.
-     *
-     * @since 2.7.0
-     *
-     * @param integer $length  Length to split.
-     *
-     * @return array  A list containing individual sequence strings.
-     */
-    public function split($length)
-    {
-        $id = new Horde_Stream_Temp();
-        $id->add($this->tostring_sort, true);
-
-        $out = array();
-
-        do {
-            $out[] = stream_get_contents($id->stream, $length) . $id->getToChar(',');
-        } while (!feof($id->stream));
-
-        return $out;
-    }
-
-    /**
-     * Create an IMAP message sequence string from a list of indices.
-     *
-     * Index Format: range_start:range_end,uid,uid2,...
-     *
-     * @param boolean $sort  Numerically sort the IDs before creating the
-     *                       range?
-     *
-     * @return string  The IMAP message sequence string.
-     */
-    protected function _toSequenceString($sort = true)
-    {
-        if (empty($this->_ids)) {
-            return '';
-        }
-
-        $in = $this->_ids;
-
-        if ($sort) {
-            sort($in, SORT_NUMERIC);
-        }
-
-        $first = $last = array_shift($in);
-        $i = count($in) - 1;
-        $out = array();
-
-        reset($in);
-        while (list($key, $val) = each($in)) {
-            if (($last + 1) == $val) {
-                $last = $val;
-            }
-
-            if (($i == $key) || ($last != $val)) {
-                if ($last == $first) {
-                    $out[] = $first;
-                    if ($i == $key) {
-                        $out[] = $val;
-                    }
-                } else {
-                    $out[] = $first . ':' . $last;
-                    if (($i == $key) && ($last != $val)) {
-                        $out[] = $val;
-                    }
-                }
-                $first = $last = $val;
-            }
-        }
-
-        return empty($out)
-            ? $first
-            : implode(',', $out);
-    }
-
-    /**
-     * Parse an IMAP message sequence string into a list of indices.
-     *
-     * @see _toSequenceString()
-     *
-     * @param string $str  The IMAP message sequence string.
-     *
-     * @return array  An array of indices.
-     */
-    protected function _fromSequenceString($str)
-    {
-        $ids = array();
-        $str = trim($str);
-
-        if (!strlen($str)) {
-            return $ids;
-        }
-
-        $idarray = explode(',', $str);
-
-        reset($idarray);
-        while (list(,$val) = each($idarray)) {
-            $range = explode(':', $val);
-            if (isset($range[1])) {
-                for ($i = min($range), $j = max($range); $i <= $j; ++$i) {
-                    $ids[] = $i;
-                }
-            } else {
-                $ids[] = $val;
-            }
-        }
-
-        return $ids;
-    }
-
-    /* Countable methods. */
-
-    /**
-     */
-    public function count()
-    {
-        return is_array($this->_ids)
-            ? count($this->_ids)
-           : 0;
-    }
-
-    /* Iterator methods. */
-
-    /**
-     */
-    public function current()
-    {
-        return is_array($this->_ids)
-            ? current($this->_ids)
-            : null;
-    }
-
-    /**
-     */
-    public function key()
-    {
-        return is_array($this->_ids)
-            ? key($this->_ids)
-            : null;
-    }
-
-    /**
-     */
-    public function next()
-    {
-        if (is_array($this->_ids)) {
-            next($this->_ids);
-        }
-    }
-
-    /**
-     */
-    public function rewind()
-    {
-        if (is_array($this->_ids)) {
-            reset($this->_ids);
-        }
-    }
-
-    /**
-     */
-    public function valid()
-    {
-        return !is_null($this->key());
-    }
-
-    /* Serializable methods. */
-
-    /**
-     */
-    public function serialize()
-    {
-        $save = array();
-
-        if ($this->duplicates) {
-            $save['d'] = 1;
-        }
-
-        if ($this->_sequence) {
-            $save['s'] = 1;
-        }
-
-        if ($this->_sorted) {
-            $save['is'] = 1;
-        }
-
-        switch ($this->_ids) {
-        case self::ALL:
-            $save['a'] = true;
-            break;
-
-        case self::LARGEST:
-            $save['l'] = true;
-            break;
-
-        case self::SEARCH_RES:
-            $save['sr'] = true;
-            break;
-
-        default:
-            $save['i'] = strval($this);
-            break;
-        }
-
-        return serialize($save);
-    }
-
-    /**
-     */
-    public function unserialize($data)
-    {
-        $save = @unserialize($data);
-
-        $this->duplicates = !empty($save['d']);
-        $this->_sequence = !empty($save['s']);
-        $this->_sorted = !empty($save['is']);
-
-        if (isset($save['a'])) {
-            $this->_ids = self::ALL;
-        } elseif (isset($save['l'])) {
-            $this->_ids = self::LARGEST;
-        } elseif (isset($save['sr'])) {
-            $this->_ids = self::SEARCH_RES;
-        } elseif (isset($save['i'])) {
-            $this->add($save['i']);
-        }
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientInteractionClientphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Client.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Client.php   2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Client.php      2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,61 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * An object representing an IMAP client command interaction (RFC 3501
- * [2.2.1]).
- *
- * @author     Michael Slusarz <slusarz@horde.org>
- * @category   Horde
- * @copyright  2012-2013 Horde LLC
- * @deprecated
- * @license    http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package    Imap_Client
- */
-class Horde_Imap_Client_Interaction_Client extends Horde_Imap_Client_Data_Format_List
-{
-    /**
-     * The command tag.
-     *
-     * @var string
-     */
-    public $tag;
-
-    /**
-     * Constructor.
-     *
-     * @param string $tag  The tag to use. If not set, will be automatically
-     *                     generated.
-     */
-    public function __construct($tag = null)
-    {
-        $this->tag = is_null($tag)
-            ? substr(strval(new Horde_Support_Randomid()), 0, 10)
-            : strval($tag);
-
-        parent::__construct($this->tag);
-    }
-
-    /**
-     * Get the command.
-     *
-     * @return string  The command.
-     */
-    public function getCommand()
-    {
-        return isset($this->_data[1])
-            ? $this->_data[1]
-            : null;
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientInteractionCommandContinuationphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Command/Continuation.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Command/Continuation.php     2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Command/Continuation.php        2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,66 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * An object representing a portion of an IMAP command that requires data
- * sent in a continuation response (RFC 3501 [2.2.1]).
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- * @since     2.10.0
- */
-class Horde_Imap_Client_Interaction_Command_Continuation
-{
-    /**
-     * Closure function to run after continuation response.
-     *
-     * @var Closure
-     */
-    protected $_closure;
-
-    /**
-     * Constructor.
-     *
-     * @param Closure $closure  A function to run after the continuation
-     *                          response is received.  It receives one
-     *                          argument - a Continuation object - and should
-     *                          return a list of arguments to send to the
-     *                          server (via a
-     *                          Horde_Imap_Client_Data_Format_List object).
-     */
-    public function __construct($closure)
-    {
-        $this->_closure = $closure;
-    }
-
-    /**
-     * Calls the closure object.
-     *
-     * @param Horde_Imap_Client_Interaction_Server_Continuation $ob  Continuation
-     *                                                               object.
-     *
-     * @return Horde_Imap_Client_Data_Format_List  Further commands to issue
-     *                                             to the server.
-     */
-    public function getCommands(
-        Horde_Imap_Client_Interaction_Server_Continuation $ob
-    )
-    {
-        $closure = $this->_closure;
-        return $closure($ob);
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientInteractionCommandphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Command.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Command.php  2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Command.php     2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,110 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * An object representing an IMAP command (RFC 3501 [2.2.1]).
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- * @since     2.10.0
- *
- * @property-read boolean $continuation  True if the command requires a server
- *                                       continuation response.
- */
-class Horde_Imap_Client_Interaction_Command extends Horde_Imap_Client_Data_Format_List
-{
-    /**
-     * Debug string to use instead of command text.
-     *
-     * @var string
-     */
-    public $debug = null;
-
-    /**
-     * Use LITERAL+ if available
-     *
-     * @var boolean
-     */
-    public $literalplus = true;
-
-    /**
-     * Are literal8's available?
-     *
-     * @var boolean
-     */
-    public $literal8 = false;
-
-    /**
-     * Server response.
-     *
-     * @var Horde_Imap_Client_Interaction_Server
-     */
-    public $response;
-
-    /**
-     * The command tag.
-     *
-     * @var string
-     */
-    public $tag;
-
-    /**
-     * Constructor.
-     *
-     * @param string $cmd  The IMAP command.
-     * @param string $tag  The tag to use. If not set, will be automatically
-     *                     generated.
-     */
-    public function __construct($cmd, $tag = null)
-    {
-        $this->tag = is_null($tag)
-            ? substr(new Horde_Support_Randomid(), 0, 10)
-            : strval($tag);
-
-        parent::__construct($this->tag);
-
-        $this->add($cmd);
-    }
-
-    /**
-     */
-    public function __get($name)
-    {
-        switch ($name) {
-        case 'continuation':
-            foreach ($this as $val) {
-                if (($val instanceof Horde_Imap_Client_Interaction_Command_Continuation) ||
-                    (($val instanceof Horde_Imap_Client_Data_Format_String) &&
-                     $val->literal())) {
-
-                    return true;
-                }
-            }
-            return false;
-        }
-    }
-
-    /**
-     * Get the command.
-     *
-     * @return string  The command.
-     */
-    public function getCommand()
-    {
-        return $this->_data[1];
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientInteractionPipelinephp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Pipeline.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Pipeline.php 2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Pipeline.php    2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,145 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * An object representing a series of IMAP client commands (RFC 3501 [2.2.1])
- * to be processed at the same time.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- * @since     2.10.0
- *
- * @property-read boolean $finished  True if all commands have finished.
- */
-class Horde_Imap_Client_Interaction_Pipeline implements Countable, IteratorAggregate
-{
-    /**
-     * Data storage from server responses.
-     *
-     * @var array
-     */
-    public $data = array(
-        'modseqs' => array(),
-        'modseqs_nouid' => array()
-    );
-
-    /**
-     * Fetch results.
-     *
-     * @var Horde_Imap_Client_Fetch_Results
-     */
-    public $fetch;
-
-    /**
-     * The list of commands.
-     *
-     * @var array
-     */
-    protected $_commands = array();
-
-    /**
-     * The list of commands to complete.
-     *
-     * @var array
-     */
-    protected $_todo = array();
-
-    /**
-     * Constructor.
-     *
-     * @param Horde_Imap_Client_Fetch_Results $fetch  Fetch results object.
-     */
-    public function __construct(Horde_Imap_Client_Fetch_Results $fetch)
-    {
-        $this->fetch = $fetch;
-    }
-
-    /**
-     */
-    public function __get($name)
-    {
-        switch ($name) {
-        case 'finished':
-            return empty($this->_todo);
-        }
-    }
-
-    /**
-     * Add a command to the pipeline.
-     *
-     * @param Horde_Imap_Client_Interaction_Command $cmd  Command object.
-     * @param boolean $top                                Add command to top
-     *                                                    of queue?
-     */
-    public function add(Horde_Imap_Client_Interaction_Command $cmd,
-                        $top = false)
-    {
-        if ($top) {
-            // This won't re-index keys, which may be numerical.
-            $this->_commands = array($cmd->tag => $cmd) + $this->_commands;
-        } else {
-            $this->_commands[$cmd->tag] = $cmd;
-        }
-        $this->_todo[$cmd->tag] = true;
-    }
-
-    /**
-     * Mark a command as completed.
-     *
-     * @param Horde_Imap_Client_Interaction_Server_Tagged $resp  Tagged server
-     *                                                           response.
-     */
-    public function complete(Horde_Imap_Client_Interaction_Server_Tagged $resp)
-    {
-        $this->_commands[$resp->tag]->response = $resp;
-        unset($this->_todo[$resp->tag]);
-    }
-
-    /**
-     * Return the command for a given tag.
-     *
-     * @param string $tag  The command tag.
-     *
-     * @return Horde_Imap_Client_Interaction_Command  A command object (or
-     *                                                null if the tag does
-     *                                                not exist).
-     */
-    public function getCmd($tag)
-    {
-        return isset($this->_commands[$tag])
-            ? $this->_commands[$tag]
-            : null;
-    }
-
-    /* Countable methods. */
-
-    /**
-     */
-    public function count()
-    {
-        return count($this->_commands);
-    }
-
-    /* IteratorAggregate methods. */
-
-    /**
-     */
-    public function getIterator()
-    {
-        return new ArrayIterator($this->_commands);
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientInteractionServerContinuationphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Server/Continuation.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Server/Continuation.php      2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Server/Continuation.php 2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,25 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * An object representing an IMAP continuation response (RFC 3501 [2.2.2]).
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Interaction_Server_Continuation extends Horde_Imap_Client_Interaction_Server
-{
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientInteractionServerTaggedphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Server/Tagged.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Server/Tagged.php    2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Server/Tagged.php       2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,46 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * An object representing an IMAP tagged response (RFC 3501 [2.2.2]).
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Interaction_Server_Tagged extends Horde_Imap_Client_Interaction_Server
-{
-    /**
-     * Tag.
-     *
-     * @var string
-     */
-    public $tag;
-
-    /**
-     * @param string $tag  Response tag.
-     */
-    public function __construct(Horde_Imap_Client_Tokenize $token, $tag)
-    {
-        $this->tag = $tag;
-
-        parent::__construct($token);
-
-        if (is_null($this->status)) {
-            throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::t("Bad tagged response."));
-        }
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientInteractionServerUntaggedphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Server/Untagged.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Server/Untagged.php  2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Server/Untagged.php     2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,25 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * An object representing an IMAP untagged response (RFC 3501 [2.2.2]).
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Interaction_Server_Untagged extends Horde_Imap_Client_Interaction_Server
-{
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientInteractionServerphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Server.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Server.php   2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Server.php      2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,142 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * An object representing an IMAP server command interaction (RFC 3501
- * [2.2.2]).
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Interaction_Server
-{
-    /* Response codes (RFC 3501 [7.1]). */
-    const BAD = 1;
-    const BYE = 2;
-    const NO = 3;
-    const OK = 4;
-    const PREAUTH = 5;
-
-    /**
-     * Check for status response?
-     *
-     * @var boolean
-     */
-    protected $_checkStatus = true;
-
-    /**
-     * Response code (RFC 3501 [7.1]). Properties:
-     *   - code: (string) Response code.
-     *   - data: (array) Data associated with response.
-     *
-     * @var object
-     */
-    public $responseCode = null;
-
-    /**
-     * Status response from the server.
-     *
-     * @var string
-     */
-    public $status = null;
-
-    /**
-     * IMAP server data.
-     *
-     * @var Horde_Imap_Client_Tokenize
-     */
-    public $token;
-
-    /**
-     * Auto-scan an incoming line to determine the response type.
-     *
-     * @param Horde_Imap_Client_Tokenize $t  Tokenized data returned from the
-     *                                       server.
-     *
-     * @return Horde_Imap_Client_Interaction_Server  A server response object.
-     */
-    static public function create(Horde_Imap_Client_Tokenize $t)
-    {
-        $t->rewind();
-        $tag = $t->next();
-        $t->next();
-
-        switch ($tag) {
-        case '+':
-            return new Horde_Imap_Client_Interaction_Server_Continuation($t);
-
-        case '*':
-            return new Horde_Imap_Client_Interaction_Server_Untagged($t);
-
-        default:
-            return new Horde_Imap_Client_Interaction_Server_Tagged($t, $tag);
-        }
-    }
-
-    /**
-     * Constructor.
-     *
-     * @param Horde_Imap_Client_Tokenize $token  Tokenized data returned from
-     *                                           the server.
-     */
-    public function __construct(Horde_Imap_Client_Tokenize $token)
-    {
-        $this->token = $token;
-
-        /* Check for response status. */
-        $status = $token->current();
-        $valid = array('BAD', 'BYE', 'NO', 'OK', 'PREAUTH');
-
-        if (in_array($status, $valid)) {
-            $this->status = constant(__CLASS__ . '::' . $status);
-            $resp_text = $token->next();
-
-            /* Check for response code. Only occurs if there is a response
-             * status. */
-            if (is_string($resp_text) && ($resp_text[0] == '[')) {
-                $resp = new stdClass;
-                $resp->data = array();
-
-                if ($resp_text[strlen($resp_text) - 1] == ']') {
-                    $resp->code = substr($resp_text, 1, -1);
-                } else {
-                    $resp->code = substr($resp_text, 1);
-
-                    while (($elt = $token->next()) !== false) {
-                        if (is_string($elt) && $elt[strlen($elt) - 1] == ']') {
-                            $resp->data[] = substr($elt, 0, -1);
-                            break;
-                        }
-                        $resp->data[] = is_string($elt)
-                            ? $elt
-                            : $token->flushIterator();
-                    }
-                }
-
-                $token->next();
-                $this->responseCode = $resp;
-            }
-        }
-    }
-
-    /**
-     */
-    public function __toString()
-    {
-        return strval($this->token);
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientMailboxListphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Mailbox/List.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Mailbox/List.php 2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Mailbox/List.php    2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,158 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2004-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2004-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Container of IMAP mailboxes.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2004-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Mailbox_List implements Countable, IteratorAggregate
-{
-    /**
-     * The delimiter character to use.
-     *
-     * @var string
-     */
-    protected $_delimiter;
-
-    /**
-     * Mailbox list.
-     *
-     * @var array
-     */
-    protected $_mboxes = array();
-
-    /**
-     * Should we sort with INBOX at the front of the list?
-     *
-     * @var boolean
-     */
-    protected $_sortinbox;
-
-    /**
-     * Constructor.
-     *
-     * @param mixed $mboxes  A mailbox or list of mailboxes.
-     */
-    public function __construct($mboxes)
-    {
-        $this->_mboxes = is_array($mboxes)
-            ? $mboxes
-            : array($mboxes);
-    }
-
-    /**
-     * Sort the list of mailboxes.
-     *
-     * @param array $opts  Options:
-     *   - delimiter: (string) The delimiter to use.
-     *                DEFAULT: '.'
-     *   - inbox: (boolean) Always put INBOX at the head of the list?
-     *            DEFAULT: Yes
-     *   - noupdate: (boolean) Do not update the object's mailbox list?
-     *               DEFAULT: true
-     *
-     * @return array  List of sorted mailboxes (index association is kept).
-     */
-    public function sort(array $opts = array())
-    {
-        $this->_delimiter = isset($opts['delimiter'])
-            ? $opts['delimiter']
-            : '.';
-        $this->_sortinbox = (!isset($opts['inbox']) || !empty($opts['inbox']));
-
-        if (empty($opts['noupdate'])) {
-            $mboxes = &$this->_mboxes;
-        } else {
-            $mboxes = $this->_mboxes;
-        }
-
-        uasort($mboxes, array($this, '_mboxCompare'));
-
-        return $mboxes;
-    }
-
-    /**
-     * Hierarchical folder sorting function (used with usort()).
-     *
-     * @param string $a  Comparison item 1.
-     * @param string $b  Comparison item 2.
-     *
-     * @return integer  See usort().
-     */
-    protected final function _mboxCompare($a, $b)
-    {
-        /* Always return INBOX as "smaller". */
-        if ($this->_sortinbox) {
-            if (strcasecmp($a, 'INBOX') == 0) {
-                return -1;
-            } elseif (strcasecmp($b, 'INBOX') == 0) {
-                return 1;
-            }
-        }
-
-        $a_parts = explode($this->_delimiter, $a);
-        $b_parts = explode($this->_delimiter, $b);
-
-        $a_count = count($a_parts);
-        $b_count = count($b_parts);
-
-        for ($i = 0, $iMax = min($a_count, $b_count); $i < $iMax; ++$i) {
-            if ($a_parts[$i] != $b_parts[$i]) {
-                /* If only one of the folders is under INBOX, return it as
-                 * "smaller". */
-                if ($this->_sortinbox && ($i == 0)) {
-                    $a_base = (strcasecmp($a_parts[0], 'INBOX') == 0);
-                    $b_base = (strcasecmp($b_parts[0], 'INBOX') == 0);
-                    if ($a_base && !$b_base) {
-                        return -1;
-                    } elseif (!$a_base && $b_base) {
-                        return 1;
-                    }
-                }
-
-                $cmp = strnatcasecmp($a_parts[$i], $b_parts[$i]);
-                return ($cmp == 0)
-                    ? strcmp($a_parts[$i], $b_parts[$i])
-                    : $cmp;
-            } elseif ($a_parts[$i] !== $b_parts[$i]) {
-                return strlen($a_parts[$i]) - strlen($b_parts[$i]);
-            }
-        }
-
-        return ($a_count - $b_count);
-    }
-
-    /* Countable methods. */
-
-    /**
-     */
-    public function count()
-    {
-        return count($this->_mboxes);
-    }
-
-    /* IteratorAggregate methods. */
-
-    /**
-     */
-    public function getIterator()
-    {
-        return new ArrayIterator($this->_mboxes);
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientMailboxphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Mailbox.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Mailbox.php      2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Mailbox.php 2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,142 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2011-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2011-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * An object that provides a way to switch between UTF7-IMAP and
- * human-readable representations of a mailbox name.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2011-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- *
- * @property-read string $list_escape  Escapes mailbox for use in LIST
- *                                     command (UTF-8).
- * @property-read string $utf7imap  Mailbox in UTF7-IMAP.
- * @property-read string $utf8  Mailbox in UTF-8.
- */
-class Horde_Imap_Client_Mailbox implements Serializable
-{
-    /**
-     * UTF7-IMAP representation of mailbox.
-     * If boolean true, it is identical to UTF-8 representation.
-     *
-     * @var mixed
-     */
-    protected $_utf7imap;
-
-    /**
-     * UTF8 representation of mailbox.
-     *
-     * @var string
-     */
-    protected $_utf8;
-
-    /**
-     * Shortcut to obtaining mailbox object.
-     *
-     * @param string $mbox       The mailbox name.
-     * @param boolean $utf7imap  Is mailbox UTF7-IMAP encoded? Otherwise,
-     *                           mailbox is assumed to be UTF-8.
-     *
-     * @return Horde_Imap_Client_Mailbox  A mailbox object.
-     */
-    static public function get($mbox, $utf7imap = false)
-    {
-        return ($mbox instanceof Horde_Imap_Client_Mailbox)
-            ? $mbox
-            : new Horde_Imap_Client_Mailbox($mbox, $utf7imap);
-    }
-
-    /**
-     * Constructor.
-     *
-     * @param string $mbox     The mailbox name.
-     * @param mixed $utf7imap  Is mailbox UTF7-IMAP encoded (true). Otherwise,
-     *                         mailbox is assumed to be UTF-8 encoded.
-     */
-    public function __construct($mbox, $utf7imap = false)
-    {
-        if ($utf7imap) {
-            $this->_utf7imap = $mbox;
-        } else {
-            $this->_utf8 = $mbox;
-        }
-    }
-
-    /**
-     */
-    public function __get($name)
-    {
-        switch ($name) {
-        case 'list_escape':
-            return preg_replace("/\*+/", '%', $this->utf8);
-
-        case 'utf7imap':
-            if (!isset($this->_utf7imap)) {
-                $n = Horde_Imap_Client_Utf7imap::Utf8ToUtf7Imap($this->_utf8);
-                $this->_utf7imap = ($n == $this->_utf8)
-                    ? true
-                    : $n;
-            }
-
-            return ($this->_utf7imap === true)
-                ? $this->_utf8
-                : $this->_utf7imap;
-
-        case 'utf8':
-            if (!isset($this->_utf8)) {
-                $this->_utf8 = Horde_Imap_Client_Utf7imap::Utf7ImapToUtf8($this->_utf7imap);
-                if ($this->_utf8 == $this->_utf7imap) {
-                    $this->_utf7imap = true;
-                }
-            }
-            return $this->_utf8;
-        }
-    }
-
-    /**
-     */
-    public function __toString()
-    {
-        return $this->utf8;
-    }
-
-    /**
-     * Compares this mailbox to another mailbox string.
-     *
-     * @return boolean  True if the items are equal.
-     */
-    public function equals($mbox)
-    {
-        return ($this->utf8 == $mbox);
-    }
-
-    /* Serializable methods. */
-
-    /**
-     */
-    public function serialize()
-    {
-        return json_encode(array($this->_utf7imap, $this->_utf8));
-    }
-
-    /**
-     */
-    public function unserialize($data)
-    {
-        list($this->_utf7imap, $this->_utf8) = json_decode($data, true);
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientSearchQueryphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Search/Query.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Search/Query.php 2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Search/Query.php    2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,745 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2008-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2008-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Abstraction of the IMAP4rev1 search criteria (see RFC 3501 [6.4.4]).
- * Allows translation between abstracted search criteria and a generated IMAP
- * search criteria string suitable for sending to a remote IMAP server.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2008-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Search_Query implements Serializable
-{
-    /* Serialized version. */
-    const VERSION = 3;
-
-    /* Constants for dateSearch() */
-    const DATE_BEFORE = 'BEFORE';
-    const DATE_ON = 'ON';
-    const DATE_SINCE = 'SINCE';
-
-    /* Constants for intervalSearch() */
-    const INTERVAL_OLDER = 'OLDER';
-    const INTERVAL_YOUNGER = 'YOUNGER';
-
-    /**
-     * The charset of the search strings.  All text strings must be in
-     * this charset. By default, this is 'US-ASCII' (see RFC 3501 [6.4.4]).
-     *
-     * @var string
-     */
-    protected $_charset = null;
-
-    /**
-     * The list of search params.
-     *
-     * @var array
-     */
-    protected $_search = array();
-
-    /**
-     * String representation: The IMAP search string.
-     */
-    public function __toString()
-    {
-        $res = $this->build(null);
-        return $res['query']->escape();
-    }
-
-    /**
-     * Sets the charset of the search text.
-     *
-     * @param string $charset   The charset to use for the search.
-     * @param boolean $convert  Convert existing text values?
-     *
-     * @throws Horde_Imap_Client_Exception_SearchCharset
-     */
-    public function charset($charset, $convert = true)
-    {
-        $oldcharset = $this->_charset;
-        $this->_charset = strtoupper($charset);
-
-        if (!$convert || ($oldcharset == $this->_charset)) {
-            return;
-        }
-
-        foreach (array('header', 'text') as $item) {
-            if (isset($this->_search[$item])) {
-                foreach ($this->_search[$item] as $key => $val) {
-                    $new_val = Horde_String::convertCharset($val['text'], $oldcharset, $this->_charset);
-                    if (Horde_String::convertCharset($new_val, $this->_charset, $oldcharset) != $val['text']) {
-                        throw new Horde_Imap_Client_Exception_SearchCharset($this->_charset);
-                    }
-                    $this->_search[$item][$key]['text'] = $new_val;
-                }
-            }
-        }
-    }
-
-    /**
-     * Builds an IMAP4rev1 compliant search string.
-     *
-     * @param array $exts  The list of extensions supported by the server.
-     *                     This determines whether certain criteria can be
-     *                     used, and determines whether workarounds are used
-     *                     for other criteria. In the format returned by
-     *                     Horde_Imap_Client_Base::capability(). If this value
-     *                     is null, all extensions are assumed to be
-     *                     available.
-     *
-     * @return array  An array with these elements:
-     *   - charset: (string) The charset of the search string. If null, no
-     *              text strings appear in query.
-     *   - exts: (array) The list of IMAP extensions used to create the
-     *           string.
-     *   - query: (Horde_Imap_Client_Data_Format_List) The IMAP search
-     *            command.
-     *
-     * @throws Horde_Imap_Client_Exception_NoSupportExtension
-     */
-    public function build($exts = array())
-    {
-        $temp = array(
-            'cmds' => new Horde_Imap_Client_Data_Format_List(),
-            'exts' => $exts,
-            'exts_used' => array()
-        );
-        $cmds = &$temp['cmds'];
-        $charset = null;
-        $exts_used = &$temp['exts_used'];
-        $ptr = &$this->_search;
-
-        if (isset($ptr['new'])) {
-            $this->_addFuzzy(!empty($ptr['newfuzzy']), $temp);
-            if ($ptr['new']) {
-                $cmds->add('NEW');
-                unset($ptr['flag']['UNSEEN']);
-            } else {
-                $cmds->add('OLD');
-            }
-            unset($ptr['flag']['RECENT']);
-        }
-
-        if (!empty($ptr['flag'])) {
-            foreach ($ptr['flag'] as $key => $val) {
-                $this->_addFuzzy(!empty($val['fuzzy']), $temp);
-
-                $tmp = '';
-                if (empty($val['set'])) {
-                    // This is a 'NOT' search.  All system flags but \Recent
-                    // have 'UN' equivalents.
-                    if ($key == 'RECENT') {
-                        $cmds->add('NOT');
-                    } else {
-                        $tmp = 'UN';
-                    }
-                }
-
-                if ($val['type'] == 'keyword') {
-                    $cmds->add(array(
-                        $tmp . 'KEYWORD',
-                        $key
-                    ));
-                } else {
-                    $cmds->add($tmp . $key);
-                }
-            }
-        }
-
-        if (!empty($ptr['header'])) {
-            /* The list of 'system' headers that have a specific search
-             * query. */
-            $systemheaders = array(
-                'BCC', 'CC', 'FROM', 'SUBJECT', 'TO'
-            );
-
-            foreach ($ptr['header'] as $val) {
-                $this->_addFuzzy(!empty($val['fuzzy']), $temp);
-
-                if (!empty($val['not'])) {
-                    $cmds->add('NOT');
-                }
-
-                if (in_array($val['header'], $systemheaders)) {
-                    $cmds->add($val['header']);
-                } else {
-                    $cmds->add(array(
-                        'HEADER',
-                        new Horde_Imap_Client_Data_Format_Astring($val['header'])
-                    ));
-                }
-                $cmds->add(new Horde_Imap_Client_Data_Format_Astring(isset($val['text']) ? $val['text'] : ''));
-                $charset = is_null($this->_charset)
-                    ? 'US-ASCII'
-                    : $this->_charset;
-            }
-        }
-
-        if (!empty($ptr['text'])) {
-            foreach ($ptr['text'] as $val) {
-                $this->_addFuzzy(!empty($val['fuzzy']), $temp);
-
-                if (!empty($val['not'])) {
-                    $cmds->add('NOT');
-                }
-                $cmds->add(array(
-                    $val['type'],
-                    new Horde_Imap_Client_Data_Format_Astring($val['text'])
-                ));
-                if (is_null($charset)) {
-                    $charset = is_null($this->_charset)
-                        ? 'US-ASCII'
-                        : $this->_charset;
-                }
-            }
-        }
-
-        if (!empty($ptr['size'])) {
-            foreach ($ptr['size'] as $key => $val) {
-                $this->_addFuzzy(!empty($val['fuzzy']), $temp);
-                if (!empty($val['not'])) {
-                    $cmds->add('NOT');
-                }
-                $cmds->add(array(
-                    $key,
-                    new Horde_Imap_Client_Data_Format_Number($val['size'])
-                ));
-            }
-        }
-
-        if (isset($ptr['ids']) &&
-            (count($ptr['ids']['ids']) || $ptr['ids']['ids']->special)) {
-            $this->_addFuzzy(!empty($val['fuzzy']), $temp);
-            if (!empty($ptr['ids']['not'])) {
-                $cmds->add('NOT');
-            }
-            if (!$ptr['ids']['ids']->sequence) {
-                $cmds->add('UID');
-            }
-            $cmds->add(strval($ptr['ids']['ids']));
-        }
-
-        if (!empty($ptr['date'])) {
-            foreach ($ptr['date'] as $val) {
-                $this->_addFuzzy(!empty($val['fuzzy']), $temp);
-
-                if (!empty($val['not'])) {
-                    $cmds->add('NOT');
-                }
-
-                if (empty($val['header'])) {
-                    $cmds->add($val['range']);
-                } else {
-                    $cmds->add('SENT' . $val['range']);
-                }
-                $cmds->add($val['date']);
-            }
-        }
-
-        if (!empty($ptr['within'])) {
-            if (is_null($exts) || isset($exts['WITHIN'])) {
-                $exts_used[] = 'WITHIN';
-            }
-
-            foreach ($ptr['within'] as $key => $val) {
-                $this->_addFuzzy(!empty($val['fuzzy']), $temp);
-                if (!empty($val['not'])) {
-                    $cmds->add('NOT');
-                }
-
-                if (is_null($exts) || isset($exts['WITHIN'])) {
-                    $cmds->add(array(
-                        $key,
-                        new Horde_Imap_Client_Data_Format_Number($val['interval'])
-                    ));
-                } else {
-                    // This workaround is only accurate to within 1 day, due
-                    // to limitations with the IMAP4rev1 search commands.
-                    $cmds->add(array(
-                        ($key == self::INTERVAL_OLDER) ? self::DATE_BEFORE : self::DATE_SINCE,
-                        new Horde_Imap_Client_Data_Format_Date('now -' . $val['interval'] . ' seconds')
-                    ));
-                }
-            }
-        }
-
-        if (!empty($ptr['modseq'])) {
-            if (!is_null($exts) && !isset($exts['CONDSTORE'])) {
-                throw new Horde_Imap_Client_Exception_NoSupportExtension('IMAP Server does not support CONDSTORE.');
-            }
-
-            $exts_used[] = 'CONDSTORE';
-
-            $this->_addFuzzy(!empty($ptr['modseq']['fuzzy']), $temp);
-
-            if (!empty($ptr['modseq']['not'])) {
-                $cmds->add('NOT');
-            }
-            $cmds->add('MODSEQ');
-            if (isset($ptr['modseq']['name'])) {
-                $cmds->add(array(
-                    new Horde_Imap_Client_Data_Format_String($ptr['modseq']['name']),
-                    $ptr['modseq']['type']
-                ));
-            }
-            $cmds->add(new Horde_Imap_Client_Data_Format_Number($ptr['modseq']['value']));
-        }
-
-        if (isset($ptr['prevsearch'])) {
-            if (!is_null($exts) && !isset($exts['SEARCHRES'])) {
-                throw new Horde_Imap_Client_Exception_NoSupportExtension('IMAP Server does not support SEARCHRES.');
-            }
-
-            $exts_used[] = 'SEARCHRES';
-
-            $this->_addFuzzy(!empty($ptr['prevsearchfuzzy']), $temp);
-
-            if (!$ptr['prevsearch']) {
-                $cmds->add('NOT');
-            }
-            $cmds->add('$');
-        }
-
-        // Add AND'ed queries
-        if (!empty($ptr['and'])) {
-            foreach ($ptr['and'] as $val) {
-                $ret = $val->build();
-                if ($ret['charset'] != 'US-ASCII') {
-                    $charset = $ret['charset'];
-                }
-                $exts_used = array_merge($exts_used, $ret['exts']);
-                $cmds->add($ret['query'], true);
-            }
-        }
-
-        // Add OR'ed queries
-        if (!empty($ptr['or'])) {
-            foreach ($ptr['or'] as $val) {
-                $ret = $val->build();
-
-                if ($ret['charset'] != 'US-ASCII') {
-                    $charset = $ret['charset'];
-                }
-                $exts_used = array_merge($exts_used, $ret['exts']);
-
-                // First OR'd query
-                if (count($cmds)) {
-                    $new_cmds = new Horde_Imap_Client_Data_Format_List();
-                    $new_cmds->add(array(
-                        'OR',
-                        $ret['query'],
-                        $cmds
-                    ));
-                    $cmds = $new_cmds;
-                } else {
-                    $cmds = $ret['query'];
-                }
-            }
-        }
-
-        // Default search is 'ALL'
-        if (!count($cmds)) {
-            $cmds->add('ALL');
-        }
-
-        return array(
-            'charset' => $charset,
-            'exts' => array_keys(array_flip($exts_used)),
-            'query' => $cmds
-        );
-    }
-
-    /**
-     * Adds fuzzy modifier to search keys.
-     *
-     * @param boolean $add  Add the fuzzy modifier?
-     * @param array $temp   Temporary build data.
-     *
-     * @throws Horde_Imap_Client_Exception_NoSupport_Extension
-     */
-    protected function _addFuzzy($add, &$temp)
-    {
-        if ($add) {
-            if (!isset($temp['exts']['SEARCH']) ||
-                !in_array('FUZZY', $temp['exts']['SEARCH'])) {
-                throw new Horde_Imap_Client_Exception_NoSupportExtension('IMAP Server does not support SEARCH=FUZZY.');
-            }
-            $temp['cmds']->add('FUZZY');
-            $temp['exts_used'][] = 'SEARCH=FUZZY';
-        }
-    }
-
-    /**
-     * Search for a flag/keywords.
-     *
-     * @param string $name  The flag or keyword name.
-     * @param boolean $set  If true, search for messages that have the flag
-     *                      set.  If false, search for messages that do not
-     *                      have the flag set.
-     * @param array $opts   Additional options:
-     *   - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
-     *            MUST support RFC 6203.
-     */
-    public function flag($name, $set = true, array $opts = array())
-    {
-        $name = strtoupper(ltrim($name, '\\'));
-        if (!isset($this->_search['flag'])) {
-            $this->_search['flag'] = array();
-        }
-
-        /* The list of defined system flags (see RFC 3501 [2.3.2]). */
-        $systemflags = array(
-            'ANSWERED', 'DELETED', 'DRAFT', 'FLAGGED', 'RECENT', 'SEEN'
-        );
-
-        $this->_search['flag'][$name] = array_filter(array(
-            'fuzzy' => !empty($opts['fuzzy']),
-            'set' => $set,
-            'type' => in_array($name, $systemflags) ? 'flag' : 'keyword'
-        ));
-    }
-
-    /**
-     * Determines if flags are a part of the search.
-     *
-     * @return boolean  True if search query involves flags.
-     */
-    public function flagSearch()
-    {
-        return !empty($this->_search['flag']);
-    }
-
-    /**
-     * Search for either new messages (messages that have the '\Recent' flag
-     * but not the '\Seen' flag) or old messages (messages that do not have
-     * the '\Recent' flag).  If new messages are searched, this will clear
-     * any '\Recent' or '\Unseen' flag searches.  If old messages are searched,
-     * this will clear any '\Recent' flag search.
-     *
-     * @param boolean $newmsgs  If true, searches for new messages.  Else,
-     *                          search for old messages.
-     * @param array $opts       Additional options:
-     *   - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
-     *            MUST support RFC 6203.
-     */
-    public function newMsgs($newmsgs = true, array $opts = array())
-    {
-        $this->_search['new'] = $newmsgs;
-        if (!empty($opts['fuzzy'])) {
-            $this->_search['newfuzzy'] = true;
-        }
-    }
-
-    /**
-     * Search for text in the header of a message.
-     *
-     * @param string $header  The header field.
-     * @param string $text    The search text.
-     * @param boolean $not    If true, do a 'NOT' search of $text.
-     * @param array $opts     Additional options:
-     *   - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
-     *            MUST support RFC 6203.
-     */
-    public function headerText($header, $text, $not = false,
-                                array $opts = array())
-    {
-        if (!isset($this->_search['header'])) {
-            $this->_search['header'] = array();
-        }
-        $this->_search['header'][] = array_filter(array(
-            'fuzzy' => !empty($opts['fuzzy']),
-            'header' => strtoupper($header),
-            'text' => $text,
-            'not' => $not
-        ));
-    }
-
-    /**
-     * Search for text in either the entire message, or just the body.
-     *
-     * @param string $text      The search text.
-     * @param string $bodyonly  If true, only search in the body of the
-     *                          message. If false, also search in the headers.
-     * @param boolean $not      If true, do a 'NOT' search of $text.
-     * @param array $opts       Additional options:
-     *   - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
-     *            MUST support RFC 6203.
-     */
-    public function text($text, $bodyonly = true, $not = false,
-                         array $opts = array())
-    {
-        if (!isset($this->_search['text'])) {
-            $this->_search['text'] = array();
-        }
-
-        $this->_search['text'][] = array_filter(array(
-            'fuzzy' => !empty($opts['fuzzy']),
-            'not' => $not,
-            'text' => $text,
-            'type' => $bodyonly ? 'BODY' : 'TEXT'
-        ));
-    }
-
-    /**
-     * Search for messages smaller/larger than a certain size.
-     *
-     * @param integer $size    The size (in bytes).
-     * @param boolean $larger  Search for messages larger than $size?
-     * @param boolean $not     If true, do a 'NOT' search of $text.
-     * @param array $opts      Additional options:
-     *   - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
-     *            MUST support RFC 6203.
-     */
-    public function size($size, $larger = false, $not = false,
-                         array $opts = array())
-    {
-        if (!isset($this->_search['size'])) {
-            $this->_search['size'] = array();
-        }
-        $this->_search['size'][$larger ? 'LARGER' : 'SMALLER'] = array_filter(array(
-            'fuzzy' => !empty($opts['fuzzy']),
-            'not' => $not,
-            'size' => (float)$size
-        ));
-    }
-
-    /**
-     * Search for messages within a given ID sequence range. Only one message
-     * range can be specified per query.
-     *
-     * @param Horde_Imap_Client_Ids $ids  The list of IDs to search.
-     * @param boolean $not                If true, do a 'NOT' search of the
-     *                                    IDs.
-     * @param array $opts                 Additional options:
-     *   - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
-     *            MUST support RFC 6203.
-     */
-    public function ids(Horde_Imap_Client_Ids $ids, $not = false,
-                        array $opts = array())
-    {
-        if (!$ids->isEmpty()) {
-            $this->_search['ids'] = array_filter(array(
-                'fuzzy' => !empty($opts['fuzzy']),
-                'ids' => $ids,
-                'not' => $not
-            ));
-        }
-    }
-
-    /**
-     * Search for messages within a date range.
-     *
-     * @param mixed $date    DateTime or Horde_Date object.
-     * @param string $range  Either:
-     *   - Horde_Imap_Client_Search_Query::DATE_BEFORE
-     *   - Horde_Imap_Client_Search_Query::DATE_ON
-     *   - Horde_Imap_Client_Search_Query::DATE_SINCE
-     * @param boolean $header  If true, search using the date in the message
-     *                         headers. If false, search using the internal
-     *                         IMAP date (usually arrival time).
-     * @param boolean $not     If true, do a 'NOT' search of the range.
-     * @param array $opts      Additional options:
-     *   - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
-     *            MUST support RFC 6203.
-     */
-    public function dateSearch($date, $range, $header = true, $not = false,
-                               array $opts = array())
-    {
-        if (!isset($this->_search['date'])) {
-            $this->_search['date'] = array();
-        }
-
-        // We should really be storing the raw DateTime object as data,
-        // but all versions of the query object have converted at this stage.
-        $ob = new Horde_Imap_Client_Data_Format_Date($date);
-
-        $this->_search['date'][] = array_filter(array(
-            'date' => $ob->escape(),
-            'fuzzy' => !empty($opts['fuzzy']),
-            'header' => $header,
-            'range' => $range,
-            'not' => $not
-        ));
-    }
-
-    /**
-     * Search for messages within a given interval. Only one interval of each
-     * type can be specified per search query. If the IMAP server supports
-     * the WITHIN extension (RFC 5032), it will be used.  Otherwise, the
-     * search query will be dynamically created using IMAP4rev1 search
-     * terms.
-     *
-     * @param integer $interval  Seconds from the present.
-     * @param string $range      Either:
-     *   - Horde_Imap_Client_Search_Query::INTERVAL_OLDER
-     *   - Horde_Imap_Client_Search_Query::INTERVAL_YOUNGER
-     * @param boolean $not       If true, do a 'NOT' search.
-     * @param array $opts        Additional options:
-     *   - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
-     *            MUST support RFC 6203.
-     */
-    public function intervalSearch($interval, $range, $not = false,
-                                   array $opts = array())
-    {
-        if (!isset($this->_search['within'])) {
-            $this->_search['within'] = array();
-        }
-        $this->_search['within'][$range] = array(
-            'fuzzy' => !empty($opts['fuzzy']),
-            'interval' => $interval,
-            'not' => $not
-        );
-    }
-
-    /**
-     * AND queries - the contents of this query will be AND'ed (in its
-     * entirety) with the contents of EACH of the queries passed in.  All
-     * AND'd queries must share the same charset as this query.
-     *
-     * @param mixed $queries  A query, or an array of queries, to AND with the
-     *                        current query.
-     */
-    public function andSearch($queries)
-    {
-        if (!isset($this->_search['and'])) {
-            $this->_search['and'] = array();
-        }
-
-        if ($queries instanceof Horde_Imap_Client_Search_Query) {
-            $queries = array($queries);
-        }
-
-        $this->_search['and'] = array_merge($this->_search['and'], $queries);
-    }
-
-    /**
-     * OR a query - the contents of this query will be OR'ed (in its entirety)
-     * with the contents of EACH of the queries passed in.  All OR'd queries
-     * must share the same charset as this query.  All contents of any single
-     * query will be AND'ed together.
-     *
-     * @param mixed $queries  A query, or an array of queries, to OR with the
-     *                        current query.
-     */
-    public function orSearch($queries)
-    {
-        if (!isset($this->_search['or'])) {
-            $this->_search['or'] = array();
-        }
-
-        if ($queries instanceof Horde_Imap_Client_Search_Query) {
-            $queries = array($queries);
-        }
-
-        $this->_search['or'] = array_merge($this->_search['or'], $queries);
-    }
-
-    /**
-     * Search for messages modified since a specific moment. The IMAP server
-     * must support the CONDSTORE extension (RFC 4551) for this query to be
-     * used.
-     *
-     * @param integer $value  The mod-sequence value.
-     * @param string $name    The entry-name string.
-     * @param string $type    Either 'shared', 'priv', or 'all'. Defaults to
-     *                        'all'
-     * @param boolean $not    If true, do a 'NOT' search.
-     * @param array $opts     Additional options:
-     *   - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
-     *            MUST support RFC 6203.
-     */
-    public function modseq($value, $name = null, $type = null, $not = false,
-                           array $opts = array())
-    {
-        if (!is_null($type)) {
-            $type = strtolower($type);
-            if (!in_array($type, array('shared', 'priv', 'all'))) {
-                $type = 'all';
-            }
-        }
-
-        $this->_search['modseq'] = array_filter(array(
-            'fuzzy' => !empty($opts['fuzzy']),
-            'name' => $name,
-            'not' => $not,
-            'type' => (!is_null($name) && is_null($type)) ? 'all' : $type,
-            'value' => $value
-        ));
-    }
-
-    /**
-     * Use the results from the previous SEARCH command. The IMAP server must
-     * support the SEARCHRES extension (RFC 5182) for this query to be used.
-     *
-     * @param boolean $not  If true, don't match the previous query.
-     * @param array $opts   Additional options:
-     *   - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
-     *            MUST support RFC 6203.
-     */
-    public function previousSearch($not = false, array $opts = array())
-    {
-        $this->_search['prevsearch'] = $not;
-        if (!empty($opts['fuzzy'])) {
-            $this->_search['prevsearchfuzzy'] = true;
-        }
-    }
-
-    /* Serializable methods. */
-
-    /**
-     * Serialization.
-     *
-     * @return string  Serialized data.
-     */
-    public function serialize()
-    {
-        $data = array(
-            // Serialized data ID.
-            self::VERSION,
-            $this->_search
-        );
-
-        if (!is_null($this->_charset)) {
-            $data[] = $this->_charset;
-        }
-
-        return serialize($data);
-    }
-
-    /**
-     * Unserialization.
-     *
-     * @param string $data  Serialized data.
-     *
-     * @throws Exception
-     */
-    public function unserialize($data)
-    {
-        $data = @unserialize($data);
-        if (!is_array($data) ||
-            !isset($data[0]) ||
-            ($data[0] != self::VERSION)) {
-            throw new Exception('Cache version change');
-        }
-
-        $this->_search = $data[1];
-        if (isset($data[2])) {
-            $this->_charset = $data[2];
-        }
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientSocketCatenatephp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Socket/Catenate.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Socket/Catenate.php      2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Socket/Catenate.php 2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,167 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Methods for the Socket driver used for a CATENATE command.
- *
- * NOTE: This class is NOT intended to be accessed outside of a Base object.
- * There is NO guarantees that the API of this class will not change across
- * versions.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @internal
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Socket_Catenate
-{
-    /**
-     * Socket object.
-     *
-     * @var Horde_Imap_Client_Socket
-     */
-    protected $_socket;
-
-    /**
-     * Constructor.
-     *
-     * @param Horde_Imap_Client_Socket $socket  Socket object.
-     */
-    public function __construct(Horde_Imap_Client_Socket $socket)
-    {
-        $this->_socket = $socket;
-    }
-
-    /**
-     * Given an IMAP URL, fetches the corresponding part.
-     *
-     * @param Horde_Imap_Client_Url $url  An IMAP URL.
-     *
-     * @return resource  The section contents in a stream. Returns null if
-     *                   the part could not be found.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function fetchFromUrl(Horde_Imap_Client_Url $url)
-    {
-        $ids_ob = $this->_socket->getIdsOb($url->uid);
-
-        // BODY[]
-        if (is_null($url->section)) {
-            $query = new Horde_Imap_Client_Fetch_Query();
-            $query->fullText(array(
-                'peek' => true
-            ));
-
-            $fetch = $this->_socket->fetch($url->mailbox, $query, array(
-                'ids' => $ids_ob
-            ));
-            return $fetch[$url->uid]->getFullMsg(true);
-        }
-
-        $section = trim($url->section);
-
-        // BODY[<#.>HEADER.FIELDS<.NOT>()]
-        if (($pos = stripos($section, 'HEADER.FIELDS')) !== false) {
-            $hdr_pos = strpos($section, '(');
-            $cmd = substr($section, 0, $hdr_pos);
-
-            $query = new Horde_Imap_Client_Fetch_Query();
-            $query->headers(
-                'section',
-                explode(' ', substr($section, $hdr_pos + 1, strrpos($section, ')') - $hdr_pos)),
-                array(
-                    'id' => ($pos ? substr($section, 0, $pos - 1) : 0),
-                    'notsearch' => (stripos($cmd, '.NOT') !== false),
-                    'peek' => true
-                )
-            );
-
-            $fetch = $this->_socket->fetch($url->mailbox, $query, array(
-                'ids' => $ids_ob
-            ));
-            return $fetch[$url->uid]->getHeaders('section', Horde_Imap_Client_Data_Fetch::HEADER_STREAM);
-        }
-
-        // BODY[#]
-        if (is_numeric(substr($section, -1))) {
-            $query = new Horde_Imap_Client_Fetch_Query();
-            $query->bodyPart($section, array(
-                'peek' => true
-            ));
-
-            $fetch = $this->_socket->fetch($url->mailbox, $query, array(
-                'ids' => $ids_ob
-            ));
-            return $fetch[$url->uid]->getBodyPart($section, true);
-        }
-
-        // BODY[<#.>HEADER]
-        if (($pos = stripos($section, 'HEADER')) !== false) {
-            $id = $pos
-                ? substr($section, 0, $pos - 1)
-                : 0;
-
-            $query = new Horde_Imap_Client_Fetch_Query();
-            $query->headerText(array(
-                'id' => $id,
-                'peek' => true
-            ));
-
-            $fetch = $this->_socket->fetch($url->mailbox, $query, array(
-                'ids' => $ids_ob
-            ));
-            return $fetch[$url->uid]->getHeaderText($id, Horde_Imap_Client_Data_Fetch::HEADER_STREAM);
-        }
-
-        // BODY[<#.>TEXT]
-        if (($pos = stripos($section, 'TEXT')) !== false) {
-            $id = $pos
-                ? substr($section, 0, $pos - 1)
-                : 0;
-
-            $query = new Horde_Imap_Client_Fetch_Query();
-            $query->bodyText(array(
-                'id' => $id,
-                'peek' => true
-            ));
-
-            $fetch = $this->_socket->fetch($url->mailbox, $query, array(
-                'ids' => $ids_ob
-            ));
-            return $fetch[$url->uid]->getBodyText($id, true);
-        }
-
-        // BODY[<#.>MIMEHEADER]
-        if (($pos = stripos($section, 'MIME')) !== false) {
-            $id = $pos
-                ? substr($section, 0, $pos - 1)
-                : 0;
-
-            $query = new Horde_Imap_Client_Fetch_Query();
-            $query->mimeHeader($id, array(
-                'peek' => true
-            ));
-
-            $fetch = $this->_socket->fetch($url->mailbox, $query, array(
-                'ids' => $ids_ob
-            ));
-            return $fetch[$url->uid]->getMimeHeader($id, Horde_Imap_Client_Data_Fetch::HEADER_STREAM);
-        }
-
-        return null;
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientSocketClientSortphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Socket/ClientSort.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Socket/ClientSort.php    2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Socket/ClientSort.php       2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,317 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Client sorting methods for the Socket driver.
- *
- * NOTE: This class is NOT intended to be accessed outside of a Base object.
- * There is NO guarantees that the API of this class will not change across
- * versions.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @internal
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Socket_ClientSort
-{
-    /**
-     * Socket object.
-     *
-     * @var Horde_Imap_Client_Socket
-     */
-    protected $_socket;
-
-    /**
-     * Constructor.
-     *
-     * @param Horde_Imap_Client_Socket $socket  Socket object.
-     */
-    public function __construct(Horde_Imap_Client_Socket $socket)
-    {
-        $this->_socket = $socket;
-    }
-
-    /**
-     * Sort search results client side if the server does not support the SORT
-     * IMAP extension (RFC 5256).
-     *
-     * @param Horde_Imap_Client_Ids $res  The search results.
-     * @param array $opts                 The options to _search().
-     *
-     * @return array  The sort results.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function clientSort($res, $opts)
-    {
-        if (!count($res)) {
-            return $res;
-        }
-
-        /* Generate the FETCH command needed. */
-        $query = new Horde_Imap_Client_Fetch_Query();
-
-        foreach ($opts['sort'] as $val) {
-            switch ($val) {
-            case Horde_Imap_Client::SORT_ARRIVAL:
-                $query->imapDate();
-                break;
-
-            case Horde_Imap_Client::SORT_DATE:
-                $query->imapDate();
-                $query->envelope();
-                break;
-
-            case Horde_Imap_Client::SORT_CC:
-            case Horde_Imap_Client::SORT_DISPLAYFROM:
-            case Horde_Imap_Client::SORT_DISPLAYTO:
-            case Horde_Imap_Client::SORT_FROM:
-            case Horde_Imap_Client::SORT_SUBJECT:
-            case Horde_Imap_Client::SORT_TO:
-                $query->envelope();
-                break;
-
-            case Horde_Imap_Client::SORT_SIZE:
-                $query->size();
-                break;
-            }
-        }
-
-        if (!count($query)) {
-            return $res;
-        }
-
-        $mbox = $this->_socket->currentMailbox();
-        $fetch_res = $this->_socket->fetch($mbox['mailbox'], $query, array(
-            'ids' => $res
-        ));
-
-        return $this->_clientSortProcess($res->ids, $fetch_res, $opts['sort']);
-    }
-
-    /**
-     * If server does not support the THREAD IMAP extension (RFC 5256), do
-     * ORDEREDSUBJECT threading on the client side.
-     *
-     * @param Horde_Imap_Client_Fetch_Results $data  Fetch results.
-     * @param boolean $uids                          Are IDs UIDs?
-     *
-     * @return array  The thread sort results.
-     */
-    public function threadOrderedSubject(Horde_Imap_Client_Fetch_Results $data,
-                                         $uids)
-    {
-        $dates = $this->_getSentDates($data, $data->ids());
-        $out = $sorted = $tsort = array();
-
-        foreach ($data as $k => $v) {
-            $subject = strval(new Horde_Imap_Client_Data_BaseSubject($v->getEnvelope()->subject));
-            $sorted[$subject][$k] = $dates[$k];
-        }
-
-        /* Step 1: Sort by base subject (already done).
-         * Step 2: Sort by sent date within each thread. */
-        foreach (array_keys($sorted) as $key) {
-            asort($sorted[$key], SORT_NUMERIC);
-            $tsort[$key] = reset($sorted[$key]);
-        }
-
-        /* Step 3: Sort by the sent date of the first message in the
-         * thread. */
-        asort($tsort, SORT_NUMERIC);
-
-        /* Now, $tsort contains the order of the threads, and each thread
-         * is sorted in $sorted. */
-        foreach (array_keys($tsort) as $key) {
-            $keys = array_keys($sorted[$key]);
-            $out[$keys[0]] = array(
-                $keys[0] => 0
-            ) + array_fill_keys(array_slice($keys, 1) , 1);
-        }
-
-        return new Horde_Imap_Client_Data_Thread($out, $uids ? 'uid' : 'sequence');
-    }
-
-    /**
-     */
-    protected function _clientSortProcess($res, $fetch_res, $sort)
-    {
-        /* The initial sort is on the entire set. */
-        $slices = array(0 => $res);
-        $reverse = false;
-
-        foreach ($sort as $val) {
-            if ($val == Horde_Imap_Client::SORT_REVERSE) {
-                $reverse = true;
-                continue;
-            }
-
-            $slices_list = $slices;
-            $slices = array();
-
-            foreach ($slices_list as $slice_start => $slice) {
-                $sorted = array();
-
-                if ($reverse) {
-                    $slice = array_reverse($slice);
-                }
-
-                switch ($val) {
-                case Horde_Imap_Client::SORT_SEQUENCE:
-                    /* There is no requirement that IDs be returned in
-                     * sequence order (see RFC 4549 [4.3.1]). So we must sort
-                     * ourselves. */
-                    $sorted = array_flip($slice);
-                    ksort($sorted, SORT_NUMERIC);
-                    break;
-
-                case Horde_Imap_Client::SORT_SIZE:
-                    foreach ($slice as $num) {
-                        $sorted[$num] = $fetch_res[$num]->getSize();
-                    }
-                    asort($sorted, SORT_NUMERIC);
-                    break;
-
-                case Horde_Imap_Client::SORT_DISPLAYFROM:
-                case Horde_Imap_Client::SORT_DISPLAYTO:
-                    $field = ($val == Horde_Imap_Client::SORT_DISPLAYFROM)
-                        ? 'from'
-                        : 'to';
-
-                    foreach ($slice as $num) {
-                        $env = $fetch_res[$num]->getEnvelope();
-
-                        if (empty($env->$field)) {
-                            $sorted[$num] = null;
-                        } else {
-                            $addr_ob = reset($env->$field);
-                            if (is_null($sorted[$num] = $addr_ob->personal)) {
-                                $sorted[$num] = $addr_ob->mailbox;
-                            }
-                        }
-                    }
-
-                    asort($sorted, SORT_LOCALE_STRING);
-                    break;
-
-                case Horde_Imap_Client::SORT_CC:
-                case Horde_Imap_Client::SORT_FROM:
-                case Horde_Imap_Client::SORT_TO:
-                    if ($val == Horde_Imap_Client::SORT_CC) {
-                        $field = 'cc';
-                    } elseif ($val == Horde_Imap_Client::SORT_FROM) {
-                        $field = 'from';
-                    } else {
-                        $field = 'to';
-                    }
-
-                    foreach ($slice as $num) {
-                        $tmp = $fetch_res[$num]->getEnvelope()->$field;
-                        $sorted[$num] = count($tmp)
-                            ? $tmp[0]->mailbox
-                            : null;
-                    }
-                    asort($sorted, SORT_LOCALE_STRING);
-                    break;
-
-                case Horde_Imap_Client::SORT_ARRIVAL:
-                    $sorted = $this->_getSentDates($fetch_res, $slice, true);
-                    asort($sorted, SORT_NUMERIC);
-                    break;
-
-                case Horde_Imap_Client::SORT_DATE:
-                    // Date sorting rules in RFC 5256 [2.2]
-                    $sorted = $this->_getSentDates($fetch_res, $slice);
-                    asort($sorted, SORT_NUMERIC);
-                    break;
-
-                case Horde_Imap_Client::SORT_SUBJECT:
-                    // Subject sorting rules in RFC 5256 [2.1]
-                    foreach ($slice as $num) {
-                        $sorted[$num] = strval(new Horde_Imap_Client_Data_BaseSubject($fetch_res[$num]->getEnvelope()->subject));
-                    }
-                    asort($sorted, SORT_LOCALE_STRING);
-                    break;
-                }
-
-                // At this point, keys of $sorted are sequence/UID and values
-                // are the sort strings
-                if (!empty($sorted)) {
-                    if (count($sorted) == count($res)) {
-                        $res = array_keys($sorted);
-                    } else {
-                        array_splice($res, $slice_start, count($slice), array_keys($sorted));
-                    }
-
-                    // Check for ties.
-                    $last = $start = null;
-                    $i = 0;
-                    reset($sorted);
-                    while (list($k, $v) = each($sorted)) {
-                        if (is_null($last) || ($last != $v)) {
-                            if ($i) {
-                                $slices[array_search($start, $res)] = array_slice($sorted, array_search($start, $sorted), $i + 1);
-                                $i = 0;
-                            }
-                            $last = $v;
-                            $start = $k;
-                        } else {
-                            ++$i;
-                        }
-                    }
-                    if ($i) {
-                        $slices[array_search($start, $res)] = array_slice($sorted, array_search($start, $sorted), $i + 1);
-                    }
-                }
-            }
-
-            $reverse = false;
-        }
-
-        return $res;
-    }
-
-    /**
-     * Get the sent dates for purposes of SORT/THREAD sorting under RFC 5256
-     * [2.2].
-     *
-     * @param Horde_Imap_Client_Fetch_Results $data  Data returned from
-     *                                               fetch() that includes
-     *                                               both date and envelope
-     *                                               items.
-     * @param array $ids                             The IDs to process.
-     * @param boolean $internal                      Only use internal date?
-     *
-     * @return array  A mapping of IDs -> UNIX timestamps.
-     */
-    protected function _getSentDates(Horde_Imap_Client_Fetch_Results $data,
-                                     $ids, $internal = false)
-    {
-        $dates = array();
-
-        foreach ($ids as $num) {
-            $dt = ($internal || !isset($data[$num]->getEnvelope()->date))
-                // RFC 5256 [3] & 3501 [6.4.4]: disregard timezone when
-                // using internaldate.
-                ? $data[$num]->getImapDate()
-                : $data[$num]->getEnvelope()->date;
-            $dates[$num] = $dt->format('U');
-        }
-
-        return $dates;
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientSocketConnectionPop3php"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Socket/Connection/Pop3.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Socket/Connection/Pop3.php       2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Socket/Connection/Pop3.php  2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,81 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * PHP stream connection to the POP3 server.
- *
- * NOTE: This class is NOT intended to be accessed outside of the package.
- * There is NO guarantees that the API of this class will not change across
- * versions.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2013 Horde LLC
- * @internal
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Socket_Connection_Pop3
-extends Horde_Imap_Client_Socket_Connection
-{
-    /**
-     * Writes data to the POP3 output stream.
-     *
-     * @param string $data  String data.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function write($data)
-    {
-        if (fwrite($this->_stream, $data . "\r\n") === false) {
-            throw new Horde_Imap_Client_Exception(
-                Horde_Imap_Client_Translation::t("Server write error."),
-                Horde_Imap_Client_Exception::SERVER_WRITEERROR
-            );
-        }
-
-        $this->_debug->client($data);
-    }
-
-    /**
-     * Read data from incoming POP3 stream.
-     *
-     * @return string  Line of data.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function read()
-    {
-        if (feof($this->_stream)) {
-            $this->close();
-            $this->_debug->info("ERROR: Server closed the connection.");
-            throw new Horde_Imap_Client_Exception(
-                Horde_Imap_Client_Translation::t("POP3 Server closed the connection unexpectedly."),
-                Horde_Imap_Client_Exception::DISCONNECT
-            );
-        }
-
-        if (($read = fgets($this->_stream)) === false) {
-            $this->_debug->info("ERROR: IMAP read/timeout error.");
-            throw new Horde_Imap_Client_Exception(
-                Horde_Imap_Client_Translation::t("Error when communicating with the mail server."),
-                Horde_Imap_Client_Exception::SERVER_READERROR
-            );
-        }
-
-        $this->_debug->server(rtrim($read, "\r\n"));
-
-        return $read;
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientSocketConnectionSocketphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Socket/Connection/Socket.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Socket/Connection/Socket.php     2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Socket/Connection/Socket.php        2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,210 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * PHP stream connection to the IMAP server.
- *
- * NOTE: This class is NOT intended to be accessed outside of the package.
- * There is NO guarantees that the API of this class will not change across
- * versions.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2013 Horde LLC
- * @internal
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Socket_Connection_Socket
-extends Horde_Imap_Client_Socket_Connection
-{
-    /**
-     * Sending buffer.
-     *
-     * @var string
-     */
-    protected $_buffer = '';
-
-    /**
-     * Output full data for literals?
-     *
-     * @var boolean
-     */
-    protected $_debugliteral;
-
-    /**
-     * Constructor.
-     *
-     * @param Horde_Imap_Client_Base $base  The base client object.
-     * @param object $debug                 The debug handler.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function __construct(Horde_Imap_Client_Base $base, $debug)
-    {
-        parent::__construct($base, $debug);
-
-        $this->_debugliteral = $base->getParam('debug_literal');
-    }
-
-    /**
-     * Writes data to the IMAP output stream.
-     *
-     * @param string $data  String data.
-     * @param boolean $eol  Append EOL?
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function write($data, $eol = false)
-    {
-        if ($eol) {
-            $buffer = $this->_buffer;
-            $this->_buffer = '';
-
-            if (fwrite($this->_stream, $buffer . $data . ($eol ? "\r\n" : '')) === false) {
-                throw new Horde_Imap_Client_Exception(
-                    Horde_Imap_Client_Translation::t("Server write error."),
-                    Horde_Imap_Client_Exception::SERVER_WRITEERROR
-                );
-            }
-
-            $this->_debug->client($buffer . $data);
-        } else {
-            $this->_buffer .= $data;
-        }
-    }
-
-    /**
-     * Writes literal data to the IMAP output stream.
-     *
-     * @param mixed $data      Either a stream resource, or Horde_Stream
-     *                         object.
-     * @param integer $length  The literal length.
-     * @param boolean $binary  If true, this is binary data.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function writeLiteral($data, $length, $binary = false)
-    {
-        $this->_buffer = '';
-
-        if ($data instanceof Horde_Stream) {
-            $data = $data->stream;
-        }
-
-        rewind($data);
-        while (!feof($data)) {
-            if (fwrite($this->_stream, fread($data, 8192)) === false) {
-                throw new Horde_Imap_Client_Exception(
-                    Horde_Imap_Client_Translation::t("Server write error."),
-                    Horde_Imap_Client_Exception::SERVER_WRITEERROR
-                );
-            }
-        }
-
-        if ($this->_debugliteral) {
-            rewind($data);
-            while (!feof($data)) {
-                $this->_debug->raw(fread($data, 8192));
-            }
-        } else {
-            $this->_debug->client('[' . ($binary ? 'BINARY' : 'LITERAL') . ' DATA: ' . $length . ' bytes]');
-        }
-    }
-
-    /**
-     * Read data from incoming IMAP stream.
-     *
-     * @return Horde_Imap_Client_Tokenize  The tokenized data.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function read()
-    {
-        $got_data = false;
-        $literal_len = null;
-        $token = new Horde_Imap_Client_Tokenize();
-
-        do {
-            if (feof($this->_stream)) {
-                $this->close();
-                $this->_debug->info("ERROR: Server closed the connection.");
-                throw new Horde_Imap_Client_Exception(
-                    Horde_Imap_Client_Translation::t("Mail server closed the connection unexpectedly."),
-                    Horde_Imap_Client_Exception::DISCONNECT
-                );
-            }
-
-            if (is_null($literal_len)) {
-                $buffer = '';
-
-                while (($in = fgets($this->_stream)) !== false) {
-                    $got_data = true;
-
-                    if (substr($in, -1) == "\n") {
-                        $in = rtrim($in);
-                        $this->_debug->server($buffer . $in);
-                        $token->add($in);
-                        break;
-                    }
-
-                    $buffer .= $in;
-                    $token->add($in);
-                }
-
-                /* Check for literal data. */
-                if (is_null($len = $token->getLiteralLength())) {
-                    break;
-                }
-
-                // Skip 0-length literal data.
-                if ($len['length']) {
-                    $binary = $len['binary'];
-                    $literal_len = $len['length'];
-                }
-
-                continue;
-            }
-
-            $old_len = $literal_len;
-
-            while (($literal_len > 0) && !feof($this->_stream)) {
-                $in = fread($this->_stream, min($literal_len, 8192));
-                $token->add($in);
-                if ($this->_debugliteral) {
-                    $this->_debug->raw($in);
-                }
-
-                $got_data = true;
-                $literal_len -= strlen($in);
-            }
-
-            $literal_len = null;
-
-            if (!$this->_debugliteral) {
-                $this->_debug->server('[' . ($binary ? 'BINARY' : 'LITERAL') . ' DATA: ' . $old_len . ' bytes]');
-            }
-        } while (true);
-
-        if (!$got_data) {
-            $this->_debug->info("ERROR: IMAP read/timeout error.");
-            throw new Horde_Imap_Client_Exception(
-                Horde_Imap_Client_Translation::t("Error when communicating with the mail server."),
-                Horde_Imap_Client_Exception::SERVER_READERROR
-            );
-        }
-
-        return $token;
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientSocketConnectionphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Socket/Connection.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Socket/Connection.php    2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Socket/Connection.php       2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,128 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * PHP stream connection to a server.
- *
- * NOTE: This class is NOT intended to be accessed outside of the package.
- * There is NO guarantees that the API of this class will not change across
- * versions.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2013 Horde LLC
- * @internal
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Socket_Connection
-extends Horde_Imap_Client_Base_Connection
-{
-    /**
-     * Sending buffer.
-     *
-     * @var string
-     */
-    protected $_buffer = '';
-
-    /**
-     * The stream connection to the IMAP server.
-     *
-     * @var resource
-     */
-    protected $_stream = null;
-
-    /**
-     * Constructor.
-     *
-     * @param Horde_Imap_Client_Base $base  The base client object.
-     * @param object $debug                 The debug handler.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    public function __construct(Horde_Imap_Client_Base $base, $debug)
-    {
-        parent::__construct($base, $debug);
-
-        switch ($secure = $base->getParam('secure')) {
-        case 'ssl':
-        case 'sslv2':
-        case 'sslv3':
-            $conn = $secure . '://';
-            $this->_secure = true;
-            break;
-
-        case 'tls':
-        default:
-            $conn = 'tcp://';
-            break;
-        }
-
-        $timeout = $base->getParam('timeout');
-
-        $this->_stream = @stream_socket_client(
-            $conn . $base->getParam('hostspec') . ':' . $base->getParam('port'),
-            $error_number,
-            $error_string,
-            $timeout
-        );
-
-        if ($this->_stream === false) {
-            $e = new Horde_Imap_Client_Exception(
-                Horde_Imap_Client_Translation::t("Error connecting to mail server."),
-                Horde_Imap_Client_Exception::SERVER_CONNECT
-            );
-            $e->details = sprintf("[%u] %s", $error_number, $error_string);
-            throw $e;
-        }
-
-        stream_set_timeout($this->_stream, $timeout);
-
-        if (function_exists('stream_set_read_buffer')) {
-            stream_set_read_buffer($this->_stream, 0);
-        }
-        stream_set_write_buffer($this->_stream, 0);
-
-        $this->_connected = true;
-    }
-
-    /**
-     * Start a TLS connection to the server.
-     *
-     * @return boolean  Whether TLS was successfully started.
-     */
-    public function startTls()
-    {
-        if ($this->connected &&
-            !$this->secure &&
-            (@stream_socket_enable_crypto($this->_stream, true, STREAM_CRYPTO_METHOD_TLS_CLIENT) === true)) {
-            $this->_secure = true;
-            return true;
-        }
-
-        return false;
-    }
-
-    /**
-     * Close the connection to the server.
-     */
-    public function close()
-    {
-        if ($this->connected) {
-            @fclose($this->_stream);
-            $this->_connected = $this->_secure = false;
-            $this->_stream = null;
-        }
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientSocketPop3php"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Socket/Pop3.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Socket/Pop3.php  2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Socket/Pop3.php     2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,1264 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2009-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * ---------------------------------------------------------------------------
- *
- * Based on the PEAR Net_POP3 package (version 1.3.6) by:
- *     Richard Heyes <richard@phpguru.org>
- *     Damian Fernandez Sosa <damlists@cnba.uba.ar>
- *
- * Copyright (c) 2002, 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.
- *
- * ---------------------------------------------------------------------------
- *
- * @category  Horde
- * @copyright 2002 Richard Heyes
- * @copyright 2009-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * An interface to a POP3 server using PHP functions.
- *
- * It is an abstraction layer allowing POP3 commands to be used based on
- * IMAP equivalents.
- *
- * This driver implements the following POP3-related RFCs:
- *   - STD 53/RFC 1939: POP3 specification
- *   - RFC 2195: CRAM-MD5 authentication
- *   - RFC 2449: POP3 extension mechanism
- *   - RFC 2595/4616: PLAIN authentication
- *   - RFC 2831: DIGEST-MD5 SASL Authentication (obsoleted by RFC 6331)
- *   - RFC 3206: AUTH/SYS response codes
- *   - RFC 1734/5034: POP3 SASL
- *
- * @author    Richard Heyes <richard@phpguru.org>
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2002 Richard Heyes
- * @copyright 2009-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Socket_Pop3 extends Horde_Imap_Client_Base
-{
-    /**
-     * The list of deleted messages.
-     *
-     * @var array
-     */
-    protected $_deleted = array();
-
-    /**
-     * This object returns POP3 Fetch data objects.
-     *
-     * @var string
-     */
-    protected $_fetchDataClass = 'Horde_Imap_Client_Data_Fetch_Pop3';
-
-    /**
-     */
-    public function __construct(array $params = array())
-    {
-        parent::__construct($params);
-
-        if (empty($params['port'])) {
-            $this->setParam('port', in_array($this->getParam('secure'), array('ssl', 'sslv2', 'sslv3')) ? 995 : 110);
-        }
-    }
-
-    /**
-     */
-    protected function _initCache($current = false)
-    {
-        return parent::_initCache($current) &&
-               $this->queryCapability('UIDL');
-    }
-
-    /**
-     */
-    public function getIdsOb($ids = null, $sequence = false)
-    {
-        return new Horde_Imap_Client_Ids_Pop3($ids, $sequence);
-    }
-
-    /**
-     */
-    protected function _capability()
-    {
-        $this->_connect();
-
-        $capability = array();
-
-        try {
-            $res = $this->_sendLine('CAPA', array(
-                'multiline' => 'array'
-            ));
-
-            foreach ($res['data'] as $val) {
-                $prefix = explode(' ', $val);
-                $capability[strtoupper($prefix[0])] = (count($prefix) > 1)
-                    ? array_slice($prefix, 1)
-                    : true;
-            }
-        } catch (Horde_Imap_Client_Exception $e) {
-            /* Need to probe for capabilities if CAPA command is not
-             * available. */
-            $capability = array('USER', 'SASL');
-
-            try {
-                $this->_sendLine('UIDL', array(
-                    'multiline' => 'none'
-                ));
-                $capability[] = 'UIDL';
-            } catch (Horde_Imap_Client_Exception $e) {}
-
-            try {
-                $this->_sendLine('TOP 1 0', array(
-                    'multiline' => 'none'
-                ));
-                $capability[] = 'TOP';
-            } catch (Horde_Imap_Client_Exception $e) {}
-        }
-
-        $this->_setInit('capability', $capability);
-    }
-
-    /**
-     */
-    protected function _noop()
-    {
-        $this->_sendLine('NOOP');
-    }
-
-    /**
-     * @throws Horde_Imap_Client_Exception_NoSupportPop3
-     */
-    protected function _getNamespaces()
-    {
-        throw new Horde_Imap_Client_Exception_NoSupportPop3('Namespaces');
-    }
-
-    /**
-     */
-    public function alerts()
-    {
-        return array();
-    }
-
-    /**
-     */
-    protected function _login()
-    {
-        $this->_connect();
-
-        // Switch to secure channel if using TLS.
-        if (!$this->isSecureConnection() &&
-            ($this->getParam('secure') == 'tls')) {
-            // Switch over to a TLS connection.
-            if (!$this->queryCapability('STLS')) {
-                throw new Horde_Imap_Client_Exception(
-                    Horde_Imap_Client_Translation::t("Could not open secure connection to the POP3 server.") . ' ' . Horde_Imap_Client_Translation::t("Server does not support secure connections."),
-                    Horde_Imap_Client_Exception::LOGIN_TLSFAILURE
-                );
-            }
-
-            $this->_sendLine('STLS');
-
-            if (!$this->_connection->startTls()) {
-                $this->logout();
-                throw new Horde_Imap_Client_Exception(
-                    Horde_Imap_Client_Translation::t("Could not open secure connection to the POP3 server."),
-                    Horde_Imap_Client_Exception::LOGIN_TLSFAILURE
-                );
-            }
-
-            // Expire cached CAPABILITY information
-            $this->_setInit('capability');
-        }
-
-        if (empty($this->_init['authmethod'])) {
-            $auth_mech = ($sasl = $this->queryCapability('SASL'))
-                ? $sasl
-                : array();
-
-            if (isset($this->_temp['pop3timestamp'])) {
-                $auth_mech[] = 'APOP';
-            }
-
-            $auth_mech[] = 'USER';
-        } else {
-            $auth_mech = array($this->_init['authmethod']);
-        }
-
-        foreach ($auth_mech as $method) {
-            try {
-                $this->_tryLogin($method);
-                $this->_setInit('authmethod', $method);
-                return true;
-            } catch (Horde_Imap_Client_Exception $e) {
-                if (!empty($this->_init['authmethod'])) {
-                    $this->_setInit();
-                    return $this->login();
-                }
-            }
-        }
-
-        throw new Horde_Imap_Client_Exception(
-            Horde_Imap_Client_Translation::t("POP3 server denied authentication."),
-            $e->getCode() ? $e->getCode() : Horde_Imap_Client_Exception::LOGIN_AUTHENTICATIONFAILED
-        );
-    }
-
-    /**
-     * Connects to the server.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    protected function _connect()
-    {
-        if (!is_null($this->_connection)) {
-            return;
-        }
-
-        $this->_connection = new Horde_Imap_Client_Socket_Connection_Pop3($this, $this->_debug);
-
-        $line = $this->_getResponse();
-
-        // Check for string matching APOP timestamp
-        if (preg_match('/<.+@.+>/U', $line['resp'], $matches)) {
-            $this->_temp['pop3timestamp'] = $matches[0];
-        }
-    }
-
-    /**
-     * Authenticate to the POP3 server.
-     *
-     * @param string $method  POP3 login method.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    protected function _tryLogin($method)
-    {
-        $username = $this->getParam('username');
-        $password = $this->getParam('password');
-
-        switch ($method) {
-        case 'CRAM-MD5':
-        case 'CRAM-SHA1':
-        case 'CRAM-SHA256':
-            // RFC 5034: CRAM-MD5
-            // CRAM-SHA1 & CRAM-SHA256 supported by Courier SASL library
-            $challenge = $this->_sendLine('AUTH ' . $method);
-            $response = base64_encode($username . ' ' . hash_hmac(strtolower(substr($method, 5)), base64_decode(substr($challenge['resp'], 2)), $password, true));
-            $this->_sendLine($response, array(
-                'debug' => sprintf('[%s Response - username: %s]', $method, $username)
-            ));
-            break;
-
-        case 'DIGEST-MD5':
-            // RFC 2831; Obsoleted by RFC 6331
-            $challenge = $this->_sendLine('AUTH DIGEST-MD5');
-            $response = base64_encode(new Horde_Imap_Client_Auth_DigestMD5(
-                $username,
-                $password,
-                base64_decode(substr($challenge['resp'], 2)),
-                $this->getParam('hostspec'),
-                'pop3'
-            ));
-            $sresponse = $this->_sendLine($response, array(
-                'debug' => sprintf('[%s Response - username: %s]', $method, $username)
-            ));
-            if (stripos(base64_decode(substr($sresponse['resp'], 2)), 'rspauth=') === false) {
-                throw new Horde_Imap_Client_Exception(
-                    Horde_Imap_Client_Translation::t("Unexpected response from server when authenticating."),
-                    Horde_Imap_Client_Exception::SERVER_CONNECT
-                );
-            }
-
-            /* POP3 doesn't use protocol's third step. */
-            $this->_sendLine('');
-            break;
-
-        case 'LOGIN':
-            // RFC 5034
-            $this->_sendLine('AUTH LOGIN');
-            $this->_sendLine(base64_encode($username), array(
-                'debug' => sprintf('[AUTH LOGIN Command - username: %s]', $username)
-            ));
-            $this->_sendLine(base64_encode($password), array(
-                'debug' => '[AUTH LOGIN Command - password]'
-            ));
-            break;
-
-        case 'PLAIN':
-            // RFC 5034
-            $this->_sendLine('AUTH PLAIN ' . base64_encode(implode("\0", array($username, $this->getParam('password')))), array(
-                'debug' => sprintf('[AUTH PLAIN Command - username: %s]', $username)
-            ));
-            break;
-
-        case 'APOP':
-            // RFC 1939 [7]
-            $this->_sendLine('APOP ' . $username . ' ' . hash('md5', $this->_temp['pop3timestamp'] . $password));
-            break;
-
-        case 'USER':
-            // RFC 1939 [7]
-            $this->_sendLine('USER ' . $username);
-            $this->_sendLine('PASS ' . $password, array(
-                'debug' => '[USER Command - password]'
-            ));
-            break;
-
-        default:
-            throw new Horde_Imap_Client_Exception(
-                sprintf(Horde_Imap_Client_Translation::t("Unknown authentication method: %s"), $method),
-                Horde_Imap_Client_Exception::SERVER_CONNECT
-            );
-        }
-    }
-
-    /**
-     */
-    protected function _logout()
-    {
-        try {
-            $this->_sendLine('QUIT');
-        } catch (Horde_Imap_Client_Exception $e) {}
-        $this->_deleted = array();
-    }
-
-    /**
-     * @throws Horde_Imap_Client_Exception_NoSupportPop3
-     */
-    protected function _sendID($info)
-    {
-        throw new Horde_Imap_Client_Exception_NoSupportPop3('ID command');
-    }
-
-    /**
-     * Return implementation information from the POP3 server (RFC 2449 [6.9]).
-     */
-    protected function _getID()
-    {
-        $id = $this->queryCapability('IMPLEMENTATION');
-        return empty($id)
-            ? array()
-            : array('implementation' => $id);
-    }
-
-    /**
-     * @throws Horde_Imap_Client_Exception_NoSupportPop3
-     */
-    protected function _setLanguage($langs)
-    {
-        throw new Horde_Imap_Client_Exception_NoSupportPop3('LANGUAGE extension');
-    }
-
-    /**
-     * @throws Horde_Imap_Client_Exception_NoSupportPop3
-     */
-    protected function _getLanguage($list)
-    {
-        throw new Horde_Imap_Client_Exception_NoSupportPop3('LANGUAGE extension');
-    }
-
-    /**
-     * @throws Horde_Imap_Client_Exception_NoSupportPop3
-     */
-    protected function _openMailbox(Horde_Imap_Client_Mailbox $mailbox, $mode)
-    {
-        if (strcasecmp($mailbox, 'INBOX') !== 0) {
-            throw new Horde_Imap_Client_Exception_NoSupportPop3('Mailboxes other than INBOX');
-        }
-        $this->_changeSelected($mailbox, $mode);
-    }
-
-    /**
-     * @throws Horde_Imap_Client_Exception_NoSupportPop3
-     */
-    protected function _createMailbox(Horde_Imap_Client_Mailbox $mailbox, $opts)
-    {
-        throw new Horde_Imap_Client_Exception_NoSupportPop3('Creating mailboxes');
-    }
-
-    /**
-     * @throws Horde_Imap_Client_Exception_NoSupportPop3
-     */
-    protected function _deleteMailbox(Horde_Imap_Client_Mailbox $mailbox)
-    {
-        throw new Horde_Imap_Client_Exception_NoSupportPop3('Deleting mailboxes');
-    }
-
-    /**
-     * @throws Horde_Imap_Client_Exception_NoSupportPop3
-     */
-    protected function _renameMailbox(Horde_Imap_Client_Mailbox $old,
-                                      Horde_Imap_Client_Mailbox $new)
-    {
-        throw new Horde_Imap_Client_Exception_NoSupportPop3('Renaming mailboxes');
-    }
-
-    /**
-     * @throws Horde_Imap_Client_Exception_NoSupportPop3
-     */
-    protected function _subscribeMailbox(Horde_Imap_Client_Mailbox $mailbox,
-                                         $subscribe)
-    {
-        throw new Horde_Imap_Client_Exception_NoSupportPop3('Mailboxes other than INBOX');
-    }
-
-    /**
-     */
-    protected function _listMailboxes($pattern, $mode, $options)
-    {
-        $tmp = array(
-            'mailbox' => Horde_Imap_Client_Mailbox::get('INBOX')
-        );
-
-        if (!empty($options['attributes'])) {
-            $tmp['attributes'] = array();
-        }
-        if (!empty($options['delimiter'])) {
-            $tmp['delimiter'] = '';
-        }
-
-        return array('INBOX' => $tmp);
-    }
-
-    /**
-     * @param integer $flags   This driver only supports the options listed
-     *                         under Horde_Imap_Client::STATUS_ALL.
-     * @throws Horde_Imap_Client_Exception_NoSupportPop3
-     */
-    protected function _status($mboxes, $flags)
-    {
-        if ((count($mboxes) > 1) ||
-            (strcasecmp(reset($mboxes), 'INBOX') !== 0)) {
-            throw new Horde_Imap_Client_Exception_NoSupportPop3('Mailboxes other than INBOX');
-        }
-
-        $this->openMailbox('INBOX');
-
-        $ret = array();
-
-        if ($flags & Horde_Imap_Client::STATUS_MESSAGES) {
-            $res = $this->_pop3Cache('stat');
-            $ret['messages'] = $res['msgs'];
-        }
-
-        if ($flags & Horde_Imap_Client::STATUS_RECENT) {
-            $res = $this->_pop3Cache('stat');
-            $ret['recent'] = $res['msgs'];
-        }
-
-        // No need for STATUS_UIDNEXT_FORCE handling since STATUS_UIDNEXT will
-        // always return a value.
-        if ($flags & Horde_Imap_Client::STATUS_UIDNEXT) {
-            $res = $this->_pop3Cache('stat');
-            $ret['uidnext'] = $res['msgs'] + 1;
-        }
-
-        if ($flags & Horde_Imap_Client::STATUS_UIDVALIDITY) {
-            $ret['uidvalidity'] = $this->queryCapability('UIDL')
-                ? 1
-                : microtime(true);
-        }
-
-        if ($flags & Horde_Imap_Client::STATUS_UNSEEN) {
-            $ret['unseen'] = 0;
-        }
-
-        return array('INBOX' => $ret);
-    }
-
-    /**
-     * @throws Horde_Imap_Client_Exception_NoSupportPop3
-     */
-    protected function _append(Horde_Imap_Client_Mailbox $mailbox, $data,
-                               $options)
-    {
-        throw new Horde_Imap_Client_Exception_NoSupportPop3('Appending messages');
-    }
-
-    /**
-     */
-    protected function _check()
-    {
-        $this->noop();
-    }
-
-    /**
-     */
-    protected function _close($options)
-    {
-        if (!empty($options['expunge'])) {
-            $this->logout();
-        }
-    }
-
-    /**
-     * @param array $options  Additional options. 'ids' has no effect in this
-     *                        driver.
-     */
-    protected function _expunge($options)
-    {
-        $msg_list = $this->_deleted;
-        $this->logout();
-        return empty($options['list'])
-            ? null
-            : $msg_list;
-    }
-
-    /**
-     * @throws Horde_Imap_Client_Exception_NoSupportPop3
-     */
-    protected function _search($query, $options)
-    {
-        $sort = empty($options['sort'])
-            ? null
-            : reset($options['sort']);
-
-        // Only support a single query: an ALL search sorted by sequence.
-        if ((strval($options['_query']['query']) != 'ALL') ||
-            ($sort &&
-             ((count($options['sort']) > 1) ||
-              ($sort != Horde_Imap_Client::SORT_SEQUENCE)))) {
-            throw new Horde_Imap_Client_Exception_NoSupportPop3('Server search');
-        }
-
-        $status = $this->status($this->_selected, Horde_Imap_Client::STATUS_MESSAGES);
-        $res = range(1, $status['messages']);
-
-        if (empty($options['sequence'])) {
-            $tmp = array();
-            $uidllist = $this->_pop3Cache('uidl');
-            foreach ($res as $val) {
-                $tmp[] = $uidllist[$val];
-            }
-            $res = $tmp;
-        }
-
-        $ret = array();
-        foreach ($options['results'] as $val) {
-            switch ($val) {
-            case Horde_Imap_Client::SEARCH_RESULTS_COUNT:
-                $ret['count'] = count($res);
-                break;
-
-            case Horde_Imap_Client::SEARCH_RESULTS_MATCH:
-                $ret['match'] = $this->getIdsOb($res);
-                break;
-
-            case Horde_Imap_Client::SEARCH_RESULTS_MAX:
-                $ret['max'] = empty($res) ? null : max($res);
-                break;
-
-            case Horde_Imap_Client::SEARCH_RESULTS_MIN:
-                $ret['min'] = empty($res) ? null : min($res);
-                break;
-            }
-        }
-
-        return $ret;
-    }
-
-    /**
-     * @throws Horde_Imap_Client_Exception_NoSupportPop3
-     */
-    protected function _setComparator($comparator)
-    {
-        throw new Horde_Imap_Client_Exception_NoSupportPop3('Search comparators');
-    }
-
-    /**
-     * @throws Horde_Imap_Client_Exception_NoSupportPop3
-     */
-    protected function _getComparator()
-    {
-        throw new Horde_Imap_Client_Exception_NoSupportPop3('Search comparators');
-    }
-
-    /**
-     * @throws Horde_Imap_Client_Exception_NoSupportPop3
-     */
-    protected function _thread($options)
-    {
-        throw new Horde_Imap_Client_Exception_NoSupportPop3('Server threading');
-    }
-
-    /**
-     */
-    protected function _fetch(Horde_Imap_Client_Fetch_Results $results,
-                              $queries)
-    {
-        foreach ($queries as $options) {
-            $this->_fetchCmd($results, $options);
-        }
-
-        $this->_updateCache($results);
-    }
-
-     /**
-     * Fetch data for a given fetch query.
-     *
-     * @param Horde_Imap_Client_Fetch_Results $results  Fetch results.
-     * @param array $options                            Fetch query options.
-     */
-    protected function _fetchCmd(Horde_Imap_Client_Fetch_Results $results,
-                                 $options)
-    {
-        // Grab sequence IDs - IDs will always be the message number for
-        // POP3 fetch commands.
-        $seq_ids = $this->_getSeqIds($options['ids']);
-        if (empty($seq_ids)) {
-            return;
-        }
-
-        $lookup = $options['ids']->sequence
-            ? array_combine($seq_ids, $seq_ids)
-            : $this->_pop3Cache('uidl');
-
-        foreach ($options['_query'] as $type => $c_val) {
-            switch ($type) {
-            case Horde_Imap_Client::FETCH_FULLMSG:
-                foreach ($seq_ids as $id) {
-                    $tmp = $this->_pop3Cache('msg', $id);
-
-                    if (empty($c_val['start']) && empty($c_val['length'])) {
-                        $tmp2 = fopen('php://temp', 'r+');
-                        stream_copy_to_stream($tmp, $tmp2, empty($c_val['length']) ? -1 : $c_val['length'], empty($c_val['start']) ? 0 : $c_val['start']);
-                        $results->get($lookup[$id])->setFullMsg($tmp2);
-                    } else {
-                        $results->get($lookup[$id])->setFullMsg($tmp);
-                    }
-                }
-                break;
-
-            case Horde_Imap_Client::FETCH_HEADERTEXT:
-                // Ignore 'peek' option
-                foreach ($c_val as $key => $val) {
-                    foreach ($seq_ids as $id) {
-                        /* Message header can be retrieved via TOP, if the
-                         * command is available. */
-                        try {
-                            $tmp = ($key == 0)
-                                ? $this->_pop3Cache('hdr', $id)
-                                : Horde_Mime_Part::getRawPartText(stream_get_contents($this->_pop3Cache('msg', $id)), 'header', $key);
-                            $results->get($lookup[$id])->setHeaderText($key, $this->_processString($tmp, $c_val));
-                        } catch (Horde_Mime_Exception $e) {}
-                    }
-                }
-                break;
-
-            case Horde_Imap_Client::FETCH_BODYTEXT:
-                // Ignore 'peek' option
-                foreach ($c_val as $key => $val) {
-                    foreach ($seq_ids as $id) {
-                        try {
-                            $results->get($lookup[$id])->setBodyText($key, $this->_processString(Horde_Mime_Part::getRawPartText(stream_get_contents($this->_pop3Cache('msg', $id)), 'body', $key), $val));
-                        } catch (Horde_Mime_Exception $e) {}
-                    }
-                }
-                break;
-
-            case Horde_Imap_Client::FETCH_MIMEHEADER:
-                // Ignore 'peek' option
-                foreach ($c_val as $key => $val) {
-                    foreach ($seq_ids as $id) {
-                        try {
-                            $results->get($lookup[$id])->setMimeHeader($key, $this->_processString(Horde_Mime_Part::getRawPartText(stream_get_contents($this->_pop3Cache('msg', $id)), 'header', $key), $val));
-                        } catch (Horde_Mime_Exception $e) {}
-                    }
-                }
-                break;
-
-            case Horde_Imap_Client::FETCH_BODYPART:
-                // Ignore 'decode', 'peek'
-                foreach ($c_val as $key => $val) {
-                    foreach ($seq_ids as $id) {
-                        try {
-                            $results->get($lookup[$id])->setBodyPart($key, $this->_processString(Horde_Mime_Part::getRawPartText(stream_get_contents($this->_pop3Cache('msg', $id)), 'body', $key), $val));
-                        } catch (Horde_Mime_Exception $e) {}
-                    }
-                }
-                break;
-
-            case Horde_Imap_Client::FETCH_HEADERS:
-                // Ignore 'length', 'peek'
-                foreach ($seq_ids as $id) {
-                    $ob = $this->_pop3Cache('hdrob', $id);
-                    foreach ($c_val as $key => $val) {
-                        $tmp = $ob;
-
-                        if (empty($val['notsearch'])) {
-                            $tmp2 = $tmp->toArray(array('nowrap' => true));
-                            foreach (array_keys($tmp2) as $hdr) {
-                                if (!in_array($hdr, $val['headers'])) {
-                                    $tmp->removeHeader($hdr);
-                                }
-                            }
-                        } else {
-                            foreach ($val['headers'] as $hdr) {
-                                $tmp->removeHeader($hdr);
-                            }
-                        }
-
-                        $results->get($lookup[$id])->setHeaders($key, $tmp);
-                    }
-                }
-                break;
-
-            case Horde_Imap_Client::FETCH_STRUCTURE:
-                foreach ($seq_ids as $id) {
-                    if ($ptr = $this->_pop3Cache('msg', $id)) {
-                        try {
-                            $results->get($lookup[$id])->setStructure(Horde_Mime_Part::parseMessage(stream_get_contents($ptr), array('no_body' => true)));
-                        } catch (Horde_Exception $e) {}
-                    }
-                }
-                break;
-
-            case Horde_Imap_Client::FETCH_ENVELOPE:
-                foreach ($seq_ids as $id) {
-                    $tmp = $this->_pop3Cache('hdrob', $id);
-                    $results->get($lookup[$id])->setEnvelope(array(
-                        'date' => $tmp->getValue('date'),
-                        'subject' => $tmp->getValue('subject'),
-                        'from' => $tmp->getOb('from'),
-                        'sender' => $tmp->getOb('sender'),
-                        'reply_to' => $tmp->getOb('reply-to'),
-                        'to' => $tmp->getOb('to'),
-                        'cc' => $tmp->getOb('cc'),
-                        'bcc' => $tmp->getOb('bcc'),
-                        'in_reply_to' => $tmp->getValue('in-reply-to'),
-                        'message_id' => $tmp->getValue('message-id')
-                    ));
-                }
-                break;
-
-            case Horde_Imap_Client::FETCH_IMAPDATE:
-                foreach ($seq_ids as $id) {
-                    $tmp = $this->_pop3Cache('hdrob', $id);
-                    $results->get($lookup[$id])->setImapDate($tmp->getValue('date'));
-                }
-                break;
-
-            case Horde_Imap_Client::FETCH_SIZE:
-                $sizelist = $this->_pop3Cache('size');
-                foreach ($seq_ids as $id) {
-                    $results->get($lookup[$id])->setSize($sizelist[$id]);
-                }
-                break;
-
-            case Horde_Imap_Client::FETCH_SEQ:
-                foreach ($seq_ids as $id) {
-                    $results->get($lookup[$id])->setSeq($id);
-                }
-                break;
-
-            case Horde_Imap_Client::FETCH_UID:
-                $uidllist = $this->_pop3Cache('uidl');
-                foreach ($seq_ids as $id) {
-                    if (isset($uidllist[$id])) {
-                        $results->get($lookup[$id])->setUid($uidllist[$id]);
-                    }
-                }
-                break;
-            }
-        }
-    }
-
-    /**
-     * Retrieve locally cached message data.
-     *
-     * @param string $type    Either 'hdr', 'hdrob', 'msg', 'size', 'stat',
-     *                        or 'uidl'.
-     * @param integer $index  The message index.
-     * @param mixed $data     Additional information needed.
-     *
-     * @return mixed  The cached data. 'msg' returns a stream resource. All
-     *                other types return strings.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    protected function _pop3Cache($type, $index = null, $data = null)
-    {
-        if (isset($this->_temp['pop3cache'][$index][$type])) {
-            if ($type == 'msg') {
-                rewind($this->_temp['pop3cache'][$index][$type]);
-            }
-            return $this->_temp['pop3cache'][$index][$type];
-        }
-
-        switch ($type) {
-        case 'hdr':
-            $data = null;
-            if ($this->queryCapability('TOP')) {
-                try {
-                    $res = $this->_sendLine('TOP ' . $index . ' 0', array(
-                        'multiline' => 'stream'
-                    ));
-                    rewind($res['data']);
-                    $data = stream_get_contents($res['data']);
-                    fclose($res['data']);
-                } catch (Horde_Imap_Client_Exception $e) {}
-            }
-
-            if (is_null($data)) {
-                $data = Horde_Mime_Part::getRawPartText(stream_get_contents($this->_pop3Cache('msg', $index)), 'header', 0);
-            }
-            break;
-
-        case 'hdrob':
-            $data = Horde_Mime_Headers::parseHeaders($this->_pop3Cache('hdr', $index));
-            break;
-
-        case 'msg':
-            $res = $this->_sendLine('RETR ' . $index, array(
-                'multiline' => 'stream'
-            ));
-            $data = $res['data'];
-            rewind($data);
-            break;
-
-        case 'size':
-        case 'uidl':
-            $data = array();
-            try {
-                $res = $this->_sendLine(($type == 'size') ? 'LIST' : 'UIDL', array(
-                    'multiline' => 'array'
-                ));
-                foreach ($res['data'] as $val) {
-                    $resp_data = explode(' ', $val, 2);
-                    $data[$resp_data[0]] = $resp_data[1];
-                }
-            } catch (Horde_Imap_Client_Exception $e) {}
-            break;
-
-        case 'stat':
-            $resp = $this->_sendLine('STAT');
-            $resp_data = explode(' ', $resp['resp'], 2);
-            $data = array('msgs' => $resp_data[0], 'size' => $resp_data[1]);
-            break;
-        }
-
-        $this->_temp['pop3cache'][$index][$type] = $data;
-
-        return $data;
-    }
-
-    /**
-     * Process a string response based on criteria options.
-     *
-     * @param string $str  The original string.
-     * @param array $opts  The criteria options.
-     *
-     * @return string  The requested string.
-     */
-    protected function _processString($str, $opts)
-    {
-        if (!empty($opts['length'])) {
-            return substr($str, empty($opts['start']) ? 0 : $opts['start'], $opts['length']);
-        } elseif (!empty($opts['start'])) {
-            return substr($str, $opts['start']);
-        }
-
-        return $str;
-    }
-
-    /**
-     * @throws Horde_Imap_Client_Exception_NoSupportPop3
-     */
-    protected function _vanished($modseq, Horde_Imap_Client_Ids $ids)
-    {
-        throw new Horde_Imap_Client_Exception_NoSupportPop3('QRESYNC commands');
-    }
-
-    /**
-     * @param array $options  Additional options. This driver does not support
-     *                        'unchangedsince'.
-     */
-    protected function _store($options)
-    {
-        $delete = $reset = false;
-
-        /* Only support deleting/undeleting messages. */
-        if (isset($options['replace'])) {
-            $delete = (bool)(count(array_intersect($options['replace'], array(
-                Horde_Imap_Client::FLAG_DELETED
-            ))));
-            $reset = !$delete;
-        } else {
-            if (!empty($options['add'])) {
-                $delete = (bool)(count(array_intersect($options['add'], array(
-                    Horde_Imap_Client::FLAG_DELETED
-                ))));
-            }
-
-            if (!empty($options['remove'])) {
-                $reset = !(bool)(count(array_intersect($options['remove'], array(
-                    Horde_Imap_Client::FLAG_DELETED
-                ))));
-            }
-        }
-
-        if ($reset) {
-            $this->_sendLine('RSET');
-        } elseif ($delete) {
-            foreach ($this->_getSeqIds($options['ids']) as $id) {
-                try {
-                    $this->_sendLine('DELE ' . $id);
-                    $this->_deleted[] = $id;
-                } catch (Horde_Imap_Client_Exception $e) {}
-            }
-        }
-
-        return $this->getIdsOb();
-    }
-
-    /**
-     * @throws Horde_Imap_Client_Exception_NoSupportPop3
-     */
-    protected function _copy(Horde_Imap_Client_Mailbox $dest, $options)
-    {
-        throw new Horde_Imap_Client_Exception_NoSupportPop3('Copying messages');
-    }
-
-    /**
-     * @throws Horde_Imap_Client_Exception_NoSupportPop3
-     */
-    protected function _setQuota(Horde_Imap_Client_Mailbox $root, $options)
-    {
-        throw new Horde_Imap_Client_Exception_NoSupportPop3('Quotas');
-    }
-
-    /**
-     * @throws Horde_Imap_Client_Exception_NoSupportPop3
-     */
-    protected function _getQuota(Horde_Imap_Client_Mailbox $root)
-    {
-        throw new Horde_Imap_Client_Exception_NoSupportPop3('Quotas');
-    }
-
-    /**
-     * @throws Horde_Imap_Client_Exception_NoSupportPop3
-     */
-    protected function _getQuotaRoot(Horde_Imap_Client_Mailbox $mailbox)
-    {
-        throw new Horde_Imap_Client_Exception_NoSupportPop3('Quotas');
-    }
-
-    /**
-     * @throws Horde_Imap_Client_Exception_NoSupportPop3
-     */
-    protected function _setACL(Horde_Imap_Client_Mailbox $mailbox, $identifier,
-                               $options)
-    {
-        throw new Horde_Imap_Client_Exception_NoSupportPop3('ACLs');
-    }
-
-    /**
-     * @throws Horde_Imap_Client_Exception_NoSupportPop3
-     */
-    protected function _deleteACL(Horde_Imap_Client_Mailbox $mailbox, $identifier)
-    {
-        throw new Horde_Imap_Client_Exception_NoSupportPop3('ACLs');
-    }
-
-    /**
-     * @throws Horde_Imap_Client_Exception_NoSupportPop3
-     */
-    protected function _getACL(Horde_Imap_Client_Mailbox $mailbox)
-    {
-        throw new Horde_Imap_Client_Exception_NoSupportPop3('ACLs');
-    }
-
-    /**
-     * @throws Horde_Imap_Client_Exception_NoSupportPop3
-     */
-    protected function _listACLRights(Horde_Imap_Client_Mailbox $mailbox,
-                                      $identifier)
-    {
-        throw new Horde_Imap_Client_Exception_NoSupportPop3('ACLs');
-    }
-
-    /**
-     * @throws Horde_Imap_Client_Exception_NoSupportPop3
-     */
-    protected function _getMyACLRights(Horde_Imap_Client_Mailbox $mailbox)
-    {
-        throw new Horde_Imap_Client_Exception_NoSupportPop3('ACLs');
-    }
-
-    /**
-     * @throws Horde_Imap_Client_Exception_NoSupportPop3
-     */
-    protected function _getMetadata(Horde_Imap_Client_Mailbox $mailbox,
-                                    $entries, $options)
-    {
-        throw new Horde_Imap_Client_Exception_NoSupportPop3('Metadata');
-    }
-
-    /**
-     * @throws Horde_Imap_Client_Exception_NoSupportPop3
-     */
-    protected function _setMetadata(Horde_Imap_Client_Mailbox $mailbox, $data)
-    {
-        throw new Horde_Imap_Client_Exception_NoSupportPop3('Metadata');
-    }
-
-    /**
-     */
-    protected function _getSearchCache($type, $options)
-    {
-        /* POP3 does not support search caching. */
-        return null;
-    }
-
-    /* Internal functions. */
-
-    /**
-     * Perform a command on the server. A connection to the server must have
-     * already been made.
-     *
-     * @param string $cmd     The command to execute.
-     * @param array $options  Additional options:
-     * <pre>
-     *   - debug: (string) When debugging, send this string instead of the
-     *            actual command/data sent.
-     *            DEFAULT: Raw data output to debug stream.
-     *   - multiline: (mixed) 'array', 'none', or 'stream'.
-     * </pre>
-     *
-     * @return array  See _getResponse().
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    protected function _sendLine($cmd, $options = array())
-    {
-        $old_debug = $this->_debug->debug;
-        if (!empty($options['debug'])) {
-            $this->_debug->raw($options['debug'] . "\n");
-            $this->_debug->debug = false;
-        }
-
-        try {
-            $this->_connection->write($cmd);
-        } catch (Horde_Imap_Client_Exception $e) {
-            $this->_debug->debug = $old_debug;
-            throw $e;
-        }
-
-        $this->_debug->debug = $old_debug;
-
-        return $this->_getResponse(
-            empty($options['multiline']) ? false : $options['multiline']
-        );
-    }
-
-    /**
-     * Gets a line from the stream and parses it.
-     *
-     * @param mixed $multiline  'array', 'none', 'stream', or null.
-     *
-     * @return array  An array with the following keys:
-     *   - data: (mixed) Stream, array, or null.
-     *   - resp: (string) The server response text.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    protected function _getResponse($multiline = false)
-    {
-        $ob = array('resp' => '');
-
-        $read = explode(' ', rtrim($this->_connection->read(), "\r\n"), 2);
-        if (!in_array($read[0], array('+OK', '-ERR'))) {
-            $this->_debug->info("ERROR: IMAP read/timeout error.");
-            throw new Horde_Imap_Client_Exception(
-                Horde_Imap_Client_Translation::t("Error when communicating with the mail server."),
-                Horde_Imap_Client_Exception::SERVER_READERROR
-            );
-        }
-
-        $respcode = null;
-        if (isset($read[1]) &&
-            isset($this->_init['capability']) &&
-            $this->queryCapability('RESP-CODES')) {
-            $respcode = $this->_parseResponseCode($read[1]);
-        }
-
-        switch ($read[0]) {
-        case '+OK':
-            if ($respcode) {
-                $ob['resp'] = $respcode->text;
-            } elseif (isset($read[1])) {
-                $ob['resp'] = $read[1];
-            }
-            break;
-
-        case '-ERR':
-            $errcode = 0;
-            if ($respcode) {
-                $errtext = $respcode->text;
-
-                if (isset($respcode->code)) {
-                    switch ($respcode->code) {
-                    // RFC 2449 [8.1.1]
-                    case 'IN-USE':
-                    // RFC 2449 [8.1.2]
-                    case 'LOGIN-DELAY':
-                        $errcode = Horde_Imap_Client_Exception::LOGIN_UNAVAILABLE;
-                        break;
-
-                    // RFC 3206 [4]
-                    case 'SYS/TEMP':
-                        $errcode = Horde_Imap_Client_Exception::POP3_TEMP_ERROR;
-                        break;
-
-                    // RFC 3206 [4]
-                    case 'SYS/PERM':
-                        $errcode = Horde_Imap_Client_Exception::POP3_PERM_ERROR;
-                        break;
-
-                    // RFC 3206 [5]
-                    case 'AUTH':
-                        $errcode = Horde_Imap_Client_Exception::LOGIN_AUTHENTICATIONFAILED;
-                        break;
-                    }
-                }
-            } elseif (isset($read[1])) {
-                $errtext = $read[1];
-            } else {
-                $errtext = '[No error message provided by server]';
-            }
-
-            $e = new Horde_Imap_Client_Exception(
-                Horde_Imap_Client_Translation::t("POP3 error reported by server."),
-                $errcode
-            );
-            $e->details = $errtext;
-            throw $e;
-        }
-
-        switch ($multiline) {
-        case 'array':
-            $ob['data'] = array();
-            break;
-
-        case 'none':
-            $ob['data'] = null;
-            break;
-
-        case 'stream':
-            $ob['data'] = fopen('php://temp', 'r+');
-            break;
-
-        default:
-            return $ob;
-        }
-
-        do {
-            $orig_read = $this->_connection->read();
-            $read = rtrim($orig_read, "\r\n");
-
-            if ($read == '.') {
-                break;
-            } elseif (substr($read, 0, 2) == '..') {
-                $read = substr($read, 1);
-            }
-
-            if (is_array($ob['data'])) {
-                $ob['data'][] = $read;
-            } elseif (!is_null($ob['data'])) {
-                fwrite($ob['data'], $orig_read);
-            }
-        } while (true);
-
-        return $ob;
-    }
-
-    /**
-     * Returns a list of sequence IDs.
-     *
-     * @param Horde_Imap_Client_Ids $ids  The ID list.
-     *
-     * @return array  A list of sequence IDs.
-     */
-    protected function _getSeqIds(Horde_Imap_Client_Ids $ids)
-    {
-        if (!count($ids)) {
-            $status = $this->status($this->_selected, Horde_Imap_Client::STATUS_MESSAGES);
-            return range(1, $status['messages']);
-        } elseif ($ids->sequence) {
-            return $ids->ids;
-        }
-
-        return array_keys(array_intersect($this->_pop3Cache('uidl'), $ids->ids));
-    }
-
-    /**
-     * Parses response text for response codes (RFC 2449 [8]).
-     *
-     * @param string $text  The response text.
-     *
-     * @return object  An object with the following properties:
-     *   - code: (string) The response code, if it exists.
-     *   - data: (string) The response code data, if it exists.
-     *   - text: (string) The human-readable response text.
-     */
-    protected function _parseResponseCode($text)
-    {
-        $ret = new stdClass;
-
-        $text = trim($text);
-        if ($text[0] == '[') {
-            $pos = strpos($text, ' ', 2);
-            $end_pos = strpos($text, ']', 2);
-            if ($pos > $end_pos) {
-                $ret->code = strtoupper(substr($text, 1, $end_pos - 1));
-            } else {
-                $ret->code = strtoupper(substr($text, 1, $pos - 1));
-                $ret->data = substr($text, $pos + 1, $end_pos - $pos - 1);
-            }
-            $ret->text = trim(substr($text, $end_pos + 1));
-        } else {
-            $ret->text = $text;
-        }
-
-        return $ret;
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientSocketphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Socket.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Socket.php       2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Socket.php  2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,4608 +0,0 @@
</span><del>-<?php
-/**
- * Originally based on code from:
- *   - auth.php (1.49)
- *   - imap_general.php (1.212)
- *   - imap_messages.php (revision 13038)
- *   - strings.php (1.184.2.35)
- * from the Squirrelmail project.
- * Copyright (c) 1999-2007 The SquirrelMail Project Team
- *
- * Copyright 2005-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 1999-2007 The SquirrelMail Project Team
- * @copyright 2005-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * An interface to an IMAP4rev1 server (RFC 3501) using built-in PHP features.
- *
- * Implements the following IMAP-related RFCs (see
- * http://www.iana.org/assignments/imap4-capabilities):
- *   - RFC 2086/4314: ACL
- *   - RFC 2087: QUOTA
- *   - RFC 2088: LITERAL+
- *   - RFC 2195: AUTH=CRAM-MD5
- *   - RFC 2221: LOGIN-REFERRALS
- *   - RFC 2342: NAMESPACE
- *   - RFC 2595/4616: TLS & AUTH=PLAIN
- *   - RFC 2831: DIGEST-MD5 authentication mechanism (obsoleted by RFC 6331)
- *   - RFC 2971: ID
- *   - RFC 3348: CHILDREN
- *   - RFC 3501: IMAP4rev1 specification
- *   - RFC 3502: MULTIAPPEND
- *   - RFC 3516: BINARY
- *   - RFC 3691: UNSELECT
- *   - RFC 4315: UIDPLUS
- *   - RFC 4422: SASL Authentication (for DIGEST-MD5)
- *   - RFC 4466: Collected extensions (updates RFCs 2088, 3501, 3502, 3516)
- *   - RFC 4469/5550: CATENATE
- *   - RFC 4551: CONDSTORE
- *   - RFC 4731: ESEARCH
- *   - RFC 4959: SASL-IR
- *   - RFC 5032: WITHIN
- *   - RFC 5161: ENABLE
- *   - RFC 5162: QRESYNC
- *   - RFC 5182: SEARCHRES
- *   - RFC 5255: LANGUAGE/I18NLEVEL
- *   - RFC 5256: THREAD/SORT
- *   - RFC 5258: LIST-EXTENDED
- *   - RFC 5267: ESORT; PARTIAL search return option
- *   - RFC 5464: METADATA
- *   - RFC 5530: IMAP Response Codes
- *   - RFC 5819: LIST-STATUS
- *   - RFC 5957: SORT=DISPLAY
- *   - RFC 6154: SPECIAL-USE/CREATE-SPECIAL-USE
- *   - RFC 6203: SEARCH=FUZZY
- *   - RFC 6851: MOVE
- *   - RFC 6858: DOWNGRADED response code
- *
- * Implements the following non-RFC extensions:
- * <ul>
- *  <li>draft-ietf-morg-inthread-01: THREAD=REFS</li>
- *  <li>draft-daboo-imap-annotatemore-07: ANNOTATEMORE</li>
- *  <li>draft-daboo-imap-annotatemore-08: ANNOTATEMORE2</li>
- *  <li>XIMAPPROXY
- *   <ul>
- *    <li>Requires imapproxy v1.2.7-rc1 or later</li>
- *    <li>
- *     See https://squirrelmail.svn.sourceforge.net/svnroot/squirrelmail/trunk/imap_proxy/README
- *    </li>
- *   </ul>
- *  </li>
- * </ul>
- *
- * TODO (or not necessary?):
- * <ul>
- *  <li>RFC 2177: IDLE
- *   <ul>
- *    <li>
- *     Probably not necessary due to the limited connection time of each
- *     HTTP/PHP request
- *    </li>
- *   </ul>
- *  <li>RFC 2193: MAILBOX-REFERRALS</li>
- *  <li>
- *   RFC 4467/5092/5524/5550/5593: URLAUTH, URLAUTH=BINARY, URL-PARTIAL
- *  </li>
- *  <li>RFC 4978: COMPRESS=DEFLATE
- *   <ul>
- *    <li>See: http://bugs.php.net/bug.php?id=48725</li>
- *   </ul>
- *  </li>
- *  <li>RFC 5257: ANNOTATE (Experimental)</li>
- *  <li>RFC 5259: CONVERT</li>
- *  <li>RFC 5267: CONTEXT=SEARCH; CONTEXT=SORT</li>
- *  <li>RFC 5465: NOTIFY</li>
- *  <li>RFC 5466: FILTERS</li>
- *  <li>RFC 6237: MULTISEARCH (Experimental)</li>
- *  <li>RFC 6855: UTF8
- * </ul>
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 1999-2007 The SquirrelMail Project Team
- * @copyright 2005-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Socket extends Horde_Imap_Client_Base
-{
-    /* Cache names used exclusively within this class. */
-    const CACHE_FLAGS = 'HICflags';
-
-    /**
-     * Queued commands to send to the server.
-     *
-     * @var array
-     */
-    protected $_cmdQueue = array();
-
-    /**
-     * Mapping of status fields to IMAP names.
-     *
-     * @var array
-     */
-    protected $_statusFields = array(
-        'messages' => Horde_Imap_Client::STATUS_MESSAGES,
-        'recent' => Horde_Imap_Client::STATUS_RECENT,
-        'uidnext' => Horde_Imap_Client::STATUS_UIDNEXT,
-        'uidvalidity' => Horde_Imap_Client::STATUS_UIDVALIDITY,
-        'unseen' => Horde_Imap_Client::STATUS_UNSEEN,
-        'firstunseen' => Horde_Imap_Client::STATUS_FIRSTUNSEEN,
-        'flags' => Horde_Imap_Client::STATUS_FLAGS,
-        'permflags' => Horde_Imap_Client::STATUS_PERMFLAGS,
-        'uidnotsticky' => Horde_Imap_Client::STATUS_UIDNOTSTICKY,
-        'highestmodseq' => Horde_Imap_Client::STATUS_HIGHESTMODSEQ
-    );
-
-    /**
-     * The unique tag to use when making an IMAP query.
-     *
-     * @var integer
-     */
-    protected $_tag = 0;
-
-    /**
-     * @param array $params  A hash containing configuration parameters.
-     *                       Additional parameters to base driver:
-     *   - debug_literal: (boolean) If true, will output the raw text of
-     *                    literal responses to the debug stream. Otherwise,
-     *                    outputs a summary of the literal response.
-     *   - envelope_addrs: (integer) The maximum number of address entries to
-     *                     read for FETCH ENVELOPE address fields.
-     *                     DEFAULT: 1000
-     *   - envelope_string: (integer) The maximum length of string fields
-     *                      returned by the FETCH ENVELOPE command.
-     *                      DEFAULT: 2048
-     */
-    public function __construct(array $params = array())
-    {
-        parent::__construct(array_merge(array(
-            'debug_literal' => false,
-            'envelope_addrs' => 1000,
-            'envelope_string' => 2048
-        ), $params));
-    }
-
-    /**
-     */
-    protected function _capability()
-    {
-        // Need to use connect call here or else we run into loop issues
-        // because _connect() can call capability() internally.
-        $this->_connect();
-
-        // It is possible the server provided capability information on
-        // connect, so check for it now.
-        if (!isset($this->_init['capability'])) {
-            $this->_sendCmd($this->_command('CAPABILITY'));
-        }
-    }
-
-    /**
-     * Parse a CAPABILITY Response (RFC 3501 [7.2.1]).
-     *
-     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
-     *                                                          object.
-     * @param array $data  An array of CAPABILITY strings.
-     */
-    protected function _parseCapability(
-        Horde_Imap_Client_Interaction_Pipeline $pipeline,
-        $data
-    )
-    {
-        if (!empty($this->_temp['no_cap'])) {
-            return;
-        }
-
-        /* Assume capabilities are additive. */
-        $c = empty($this->_init['capability'])
-            ? array()
-            : $this->_init['capability'];
-
-        $pipeline->data['capabilties_set'] = true;
-
-        foreach ($data as $val) {
-            $cap_list = explode('=', $val);
-            $cap_list[0] = strtoupper($cap_list[0]);
-            if (isset($cap_list[1])) {
-                if (!isset($c[$cap_list[0]]) || !is_array($c[$cap_list[0]])) {
-                    $c[$cap_list[0]] = array();
-                }
-                $c[$cap_list[0]][] = $cap_list[1];
-            } elseif (!isset($c[$cap_list[0]])) {
-                $c[$cap_list[0]] = true;
-            }
-        }
-
-        $this->_setInit('capability', $c);
-    }
-
-    /**
-     * Unsets a capability.
-     *
-     * @param string $cap  Capability to unset.
-     */
-    protected function _unsetCapability($cap)
-    {
-        $cap_list = $this->capability();
-        unset($cap_list[$cap]);
-        $this->_setInit('capability', $cap_list);
-    }
-
-    /**
-     */
-    protected function _noop()
-    {
-        // NOOP doesn't return any specific response
-        $this->_sendCmd($this->_command('NOOP'));
-    }
-
-    /**
-     */
-    protected function _getNamespaces()
-    {
-        $data = $this->queryCapability('NAMESPACE')
-            ? $this->_sendCmd($this->_command('NAMESPACE'))->data
-            : array();
-
-        return isset($data['namespace'])
-            ? $data['namespace']
-            : array();
-    }
-
-    /**
-     * Parse a NAMESPACE response (RFC 2342 [5] & RFC 5255 [3.4]).
-     *
-     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
-     *                                                          object.
-     * @param Horde_Imap_Client_Tokenize $data  The NAMESPACE data.
-     */
-    protected function _parseNamespace(
-        Horde_Imap_Client_Interaction_Pipeline $pipeline,
-        Horde_Imap_Client_Tokenize $data
-    )
-    {
-        $namespace_array = array(
-            Horde_Imap_Client::NS_PERSONAL,
-            Horde_Imap_Client::NS_OTHER,
-            Horde_Imap_Client::NS_SHARED
-        );
-
-        $c = array();
-
-        // Per RFC 2342, response from NAMESPACE command is:
-        // (PERSONAL NAMESPACES) (OTHER_USERS NAMESPACE) (SHARED NAMESPACES)
-        foreach ($namespace_array as $val) {
-            $entry = $data->next();
-
-            if (is_null($entry)) {
-                continue;
-            }
-
-            while ($data->next() !== false) {
-                $ob = Horde_Imap_Client_Mailbox::get($data->next(), true);
-
-                $c[strval($ob)] = array(
-                    'delimiter' => $data->next(),
-                    'hidden' => false,
-                    'name' => strval($ob),
-                    'translation' => '',
-                    'type' => $val
-                );
-
-                // RFC 4466: NAMESPACE extensions
-                while (($ext = $data->next()) !== false) {
-                    switch (strtoupper($ext)) {
-                    case 'TRANSLATION':
-                        // RFC 5255 [3.4] - TRANSLATION extension
-                        $data->next();
-                        $c[strval($ob)]['translation'] = $data->next();
-                        $data->next();
-                        break;
-                    }
-                }
-            }
-        }
-
-        $pipeline->data['namespace'] = $c;
-    }
-
-    /**
-     */
-    public function alerts()
-    {
-        $alerts = empty($this->_temp['alerts'])
-            ? array()
-            : $this->_temp['alerts'];
-        $this->_temp['alerts'] = array();
-        return $alerts;
-    }
-
-    /**
-     */
-    protected function _login()
-    {
-        if (!empty($this->_temp['preauth'])) {
-            unset($this->_temp['preauth']);
-            return $this->_loginTasks();
-        }
-
-        $this->_connect();
-
-        $first_login = empty($this->_init['authmethod']);
-
-        // Switch to secure channel if using TLS.
-        if (!$this->isSecureConnection() &&
-            ($this->getParam('secure') == 'tls')) {
-            if ($first_login && !$this->queryCapability('STARTTLS')) {
-                // We should never hit this - STARTTLS is required pursuant
-                // to RFC 3501 [6.2.1].
-                throw new Horde_Imap_Client_Exception(
-                    Horde_Imap_Client_Translation::t("Server does not support TLS connections."),
-                    Horde_Imap_Client_Exception::LOGIN_TLSFAILURE
-                );
-            }
-
-            // Switch over to a TLS connection.
-            // STARTTLS returns no untagged response.
-            $this->_sendCmd($this->_Command('STARTTLS'));
-
-            if (!$this->_connection->startTls()) {
-                $this->logout();
-                throw new Horde_Imap_Client_Exception(
-                    Horde_Imap_Client_Translation::t("Could not open secure TLS connection to the IMAP server."),
-                    Horde_Imap_Client_Exception::LOGIN_TLSFAILURE
-                );
-            }
-
-            if ($first_login) {
-                // Expire cached CAPABILITY information (RFC 3501 [6.2.1])
-                $this->_setInit('capability');
-
-                // Reset language (RFC 5255 [3.1])
-                $this->_setInit('lang');
-            }
-
-            // Set language if using imapproxy
-            if (!empty($this->_init['imapproxy'])) {
-                $this->setLanguage();
-            }
-        }
-
-        if ($first_login) {
-            $imap_auth_mech = array();
-
-            $auth_methods = $this->queryCapability('AUTH');
-            if (!empty($auth_methods)) {
-                // Add SASL methods. Prefer CRAM-MD5 over DIGEST-MD5, as the
-                // latter has been obsoleted (RFC 6331).
-                $imap_auth_mech = array_intersect(array('CRAM-MD5', 'DIGEST-MD5'), $auth_methods);
-
-                // Next, try 'PLAIN' authentication.
-                if (in_array('PLAIN', $auth_methods)) {
-                    $imap_auth_mech[] = 'PLAIN';
-                }
-            }
-
-            // Fall back to 'LOGIN' if available.
-            if (!$this->queryCapability('LOGINDISABLED')) {
-                $imap_auth_mech[] = 'LOGIN';
-            }
-
-            if (empty($imap_auth_mech)) {
-                throw new Horde_Imap_Client_Exception(
-                    Horde_Imap_Client_Translation::t("No supported IMAP authentication method could be found."),
-                    Horde_Imap_Client_Exception::LOGIN_NOAUTHMETHOD
-                );
-            }
-
-            /* Use MD5 authentication first, if available. But no need to use
-             * special authentication if we are already using an encrypted
-             * connection. */
-            if ($this->isSecureConnection()) {
-                $imap_auth_mech = array_reverse($imap_auth_mech);
-            }
-        } else {
-            $imap_auth_mech = array($this->_init['authmethod']);
-        }
-
-        $login_err = null;
-
-        foreach ($imap_auth_mech as $method) {
-            try {
-                $resp = $this->_tryLogin($method);
-                $data = $resp->data;
-                $this->_setInit('authmethod', $method);
-                unset($this->_temp['referralcount']);
-            } catch (Horde_Imap_Client_Exception_ServerResponse $e) {
-                $data = $e->resp_data;
-                if (isset($data['loginerr'])) {
-                    $login_err = $data['loginerr'];
-                }
-                $resp = false;
-            } catch (Horde_Imap_Client_Exception $e) {
-                $resp = false;
-            }
-
-            // Check for login referral (RFC 2221) response - can happen for
-            // an OK, NO, or BYE response.
-            if (isset($data['referral'])) {
-                foreach (array('hostspec', 'port', 'username') as $val) {
-                    if (!is_null($data['referral']->$val)) {
-                        $this->setParam($val, $data['referral']->$val);
-                    }
-                }
-
-                if (!is_null($data['referral']->auth)) {
-                    $this->_setInit('authmethod', $data['referral']->auth);
-                }
-
-                if (!isset($this->_temp['referralcount'])) {
-                    $this->_temp['referralcount'] = 0;
-                }
-
-                // RFC 2221 [3] - Don't follow more than 10 levels of referral
-                // without consulting the user.
-                if (++$this->_temp['referralcount'] < 10) {
-                    $this->logout();
-                    $this->_setInit('capability');
-                    $this->_setInit('namespace', array());
-                    return $this->login();
-                }
-
-                unset($this->_temp['referralcount']);
-            }
-
-            if ($resp) {
-                return $this->_loginTasks($first_login, $resp->data);
-            }
-        }
-
-        /* Try again from scratch if authentication failed in an established,
-         * previously-authenticated object. */
-        if (!empty($this->_init['authmethod'])) {
-            $this->_setInit();
-            try {
-                return $this->login();
-            } catch (Horde_Imap_Client_Exception $e) {}
-        }
-
-        /* Default to AUTHENTICATIONFAILED error (see RFC 5530[3]). */
-        if (is_null($login_err)) {
-            throw new Horde_Imap_Client_Exception(
-                Horde_Imap_Client_Translation::t("Mail server denied authentication."),
-                Horde_Imap_Client_Exception::LOGIN_AUTHENTICATIONFAILED
-            );
-        }
-
-        throw $login_err;
-    }
-
-    /**
-     * Connects to the IMAP server.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    protected function _connect()
-    {
-        if (!is_null($this->_connection)) {
-            return;
-        }
-
-        $this->_connection = new Horde_Imap_Client_Socket_Connection_Socket($this, $this->_debug);
-
-        // If we already have capability information, don't re-set with
-        // (possibly) limited information sent in the initial banner.
-        if (isset($this->_init['capability'])) {
-            $this->_temp['no_cap'] = true;
-        }
-
-        /* Get greeting information.  This is untagged so we need to specially
-         * deal with it here. */
-        try {
-            $this->_getLine($this->_pipeline());
-        } catch (Horde_Imap_Client_Exception_ServerResponse $e) {
-            if ($e->status == Horde_Imap_Client_Interaction_Server::BYE) {
-                /* Server is explicitly rejecting our connection (RFC 3501
-                 * [7.1.5]). */
-                $e->setMessage(Horde_Imap_Client_Translation::t("Server rejected connection."));
-                $e->setCode(Horde_Imap_Client_Exception::SERVER_CONNECT);
-            }
-            throw $e;
-        }
-
-        // Check for IMAP4rev1 support
-        if (!$this->queryCapability('IMAP4REV1')) {
-            throw new Horde_Imap_Client_Exception(
-                Horde_Imap_Client_Translation::t("The mail server does not support IMAP4rev1 (RFC 3501)."),
-                Horde_Imap_Client_Exception::SERVER_CONNECT
-            );
-        }
-
-        // Set language if NOT using imapproxy
-        if (empty($this->_init['imapproxy'])) {
-            if ($this->queryCapability('XIMAPPROXY')) {
-                $this->_setInit('imapproxy', true);
-            } else {
-                $this->setLanguage();
-            }
-        }
-
-        // If pre-authenticated, we need to do all login tasks now.
-        if (!empty($this->_temp['preauth'])) {
-            $this->login();
-        }
-    }
-
-    /**
-     * Authenticate to the IMAP server.
-     *
-     * @param string $method  IMAP login method.
-     *
-     * @return Horde_Imap_Client_Interaction_Pipeline  Pipeline object.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    protected function _tryLogin($method)
-    {
-        $username = $this->getParam('username');
-        $password = $this->getParam('password');
-
-        switch ($method) {
-        case 'CRAM-MD5':
-        case 'CRAM-SHA1':
-        case 'CRAM-SHA256':
-            // RFC 2195: CRAM-MD5
-            // CRAM-SHA1 & CRAM-SHA256 supported by Courier SASL library
-
-            // Need $args because PHP 5.3 doesn't allow access to $this in
-            // anonymous functions.
-            $args = array(
-                $username,
-                strtolower(substr($method, 5)),
-                $password
-            );
-
-            $cmd = $this->_command('AUTHENTICATE')->add(array(
-                $method,
-                new Horde_Imap_Client_Interaction_Command_Continuation(function($ob) use ($args) {
-                    return new Horde_Imap_Client_Data_Format_List(
-                        base64_encode($args[0] . ' ' . hash_hmac($args[1], base64_decode($ob->token->current()), $args[2], false))
-                    );
-                })
-            ));
-            $cmd->debug = sprintf('[AUTHENTICATE %s Command - username: %s]', $method, $username);
-            break;
-
-        case 'DIGEST-MD5':
-            // RFC 2831/4422; obsoleted by RFC 6331
-
-            // Need $args because PHP 5.3 doesn't allow access to $this in
-            // anonymous functions.
-            $args = array(
-                $username,
-                $password,
-                $this->getParam('hostspec')
-            );
-
-            $cmd = $this->_command('AUTHENTICATE')->add(array(
-                $method,
-                new Horde_Imap_Client_Interaction_Command_Continuation(function($ob) use ($args) {
-                    return new Horde_Imap_Client_Data_Format_List(
-                        base64_encode(new Horde_Imap_Client_Auth_DigestMD5(
-                            $args[0],
-                            $args[1],
-                            base64_decode($ob->token->current()),
-                            $args[2],
-                            'imap'
-                        ))
-                    );
-                }),
-                new Horde_Imap_Client_Interaction_Command_Continuation(function($ob) {
-                    if (strpos(base64_decode($ob->token->current()), 'rspauth=') === false) {
-                        throw new Horde_Imap_Client_Exception(
-                            Horde_Imap_Client_Translation::t("Unexpected response from server when authenticating."),
-                            Horde_Imap_Client_Exception::SERVER_CONNECT
-                        );
-                    }
-
-                    return new Horde_Imap_Client_Data_Format_List();
-                })
-            ));
-            $cmd->debug = sprintf('[AUTHENTICATE DIGEST-MD5 Command - username: %s]', $username);
-            break;
-
-        case 'LOGIN':
-            $cmd = $this->_command('LOGIN')->add(array(
-                new Horde_Imap_Client_Data_Format_Astring($username),
-                new Horde_Imap_Client_Data_Format_Astring($password)
-            ));
-            $cmd->debug = sprintf('[LOGIN Command - username: %s]', $username);
-            break;
-
-        case 'PLAIN':
-            // RFC 2595/4616 - PLAIN SASL mechanism
-            $auth = base64_encode(implode("\0", array(
-                $username,
-                $username,
-                $password
-            )));
-            $cmd = $this->_command('AUTHENTICATE')->add('PLAIN');
-
-            if ($this->queryCapability('SASL-IR')) {
-                // IMAP Extension for SASL Initial Client Response (RFC 4959)
-                $cmd->add($auth);
-                $cmd->debug = sprintf('[SASL-IR AUTHENTICATE Command - username: %s]', $username);
-            } else {
-                $cmd->add(new Horde_Imap_Client_Interaction_Command_Continuation(function($ob) use ($auth) {
-                    return new Horde_Imap_Client_Data_Format_List($auth);
-                }));
-                $cmd->debug = sprintf('[AUTHENTICATE Command - username: %s]', $username);
-            }
-            break;
-
-        default:
-            throw new Horde_Imap_Client_Exception(
-                sprintf(Horde_Imap_Client_Translation::t("Unknown authentication method: %s"), $method),
-                Horde_Imap_Client_Exception::SERVER_CONNECT
-            );
-        }
-
-        $pipeline = $this->_pipeline($cmd);
-
-        /* Set a flag indicating whether we have received a CAPABILITY
-         * response after we successfully login. Since capabilities may
-         * be different after login, we need to merge this information into
-         * the current CAPABILITY array (since some servers, e.g. Cyrus,
-         * may not include authentication capabilities that are still
-         * needed in the event this object is eventually serialized). */
-        $pipeline->data['in_login'] = true;
-
-        return $this->_sendCmd($pipeline);
-    }
-
-    /**
-     * Perform login tasks.
-     *
-     * @param boolean $firstlogin  Is this the first login?
-     * @param array $resp          The data response from the login command.
-     *                             May include:
-     *   - logincapset: (boolean) True if CAPABILITY sent after login.
-     *   - proxyreuse: (boolean) True if re-used connection via imapproxy.
-     *
-     * @return boolean  True if global login tasks should be performed.
-     */
-    protected function _loginTasks($firstlogin = true, array $resp = array())
-    {
-        /* If reusing an imapproxy connection, no need to do any of these
-         * login tasks again. */
-        if (!$firstlogin && !empty($resp['proxyreuse'])) {
-            if (isset($this->_init['enabled'])) {
-                $this->_temp['enabled'] = $this->_init['enabled'];
-            }
-
-            // If we have not yet set the language, set it now.
-            if (!isset($this->_init['lang'])) {
-                $this->_temp['lang_queue'] = true;
-                $this->setLanguage();
-                unset($this->_temp['lang_queue']);
-            }
-            return false;
-        }
-
-        /* If we logged in for first time, and server did not return
-         * capability information, we need to mark for retrieval. */
-        if ($firstlogin && empty($resp['capabilities_set'])) {
-            $this->_setInit('capability');
-        }
-
-        $this->_temp['lang_queue'] = true;
-        $this->setLanguage();
-        unset($this->_temp['lang_queue']);
-
-        /* Only active QRESYNC/CONDSTORE if caching is enabled. */
-        if ($this->_initCache()) {
-            if ($this->queryCapability('QRESYNC')) {
-                $this->_enable(array('QRESYNC'));
-            } elseif ($this->queryCapability('CONDSTORE')) {
-                $this->_enable(array('CONDSTORE'));
-            }
-        }
-
-        return true;
-    }
-
-    /**
-     */
-    protected function _logout()
-    {
-        if (empty($this->_temp['logout'])) {
-            /* If using imapproxy, force sending these commands, since they
-             * may not be sent again if they are (likely) initialization
-             * commands. */
-            if (!empty($this->_cmdQueue) &&
-                !empty($this->_init['imapproxy'])) {
-                $this->_sendCmd($this->_pipeline());
-            }
-
-            $this->_temp['logout'] = true;
-            try {
-                $this->_sendCmd($this->_command('LOGOUT'));
-            } catch (Horde_Imap_Client_Exception_ServerResponse $e) {
-                // Ignore server errors
-            }
-            unset($this->_temp['logout']);
-        }
-    }
-
-    /**
-     */
-    protected function _sendID($info)
-    {
-        $cmd = $this->_command('ID');
-
-        if (empty($info)) {
-            $cmd->add(new Horde_Imap_Client_Data_Format_Nil());
-        } else {
-            $tmp = new Horde_Imap_Client_Data_Format_List();
-            foreach ($info as $key => $val) {
-                $tmp->add(array(
-                    new Horde_Imap_Client_Data_Format_String(strtolower($key)),
-                    new Horde_Imap_Client_Data_Format_Nstring($val)
-                ));
-            }
-            $cmd->add($tmp);
-        }
-
-        $this->_temp['id'] = $this->_sendCmd($cmd)->data['id'];
-    }
-
-    /**
-     * Parse an ID response (RFC 2971 [3.2]).
-     *
-     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
-     *                                                          object.
-     * @param Horde_Imap_Client_Tokenize $data  The server response.
-     */
-    protected function _parseID(
-        Horde_Imap_Client_Interaction_Pipeline $pipeline,
-        Horde_Imap_Client_Tokenize $data
-    )
-    {
-        $ids = array();
-
-        if (!is_null($data->next())) {
-            while (($curr = $data->next()) !== false) {
-                if (!is_null($id = $data->next())) {
-                    $ids[$curr] = $id;
-                }
-            }
-        }
-
-        $pipeline->data['id'] = $ids;
-    }
-
-    /**
-     */
-    protected function _getID()
-    {
-        if (!isset($this->_temp['id'])) {
-            $this->sendID();
-        }
-
-        return $this->_temp['id'];
-    }
-
-    /**
-     */
-    protected function _setLanguage($langs)
-    {
-        $cmd = $this->_command('LANGUAGE');
-        foreach ($langs as $lang) {
-            $cmd->add(new Horde_Imap_Client_Data_Format_Astring($lang));
-        }
-
-        if (!empty($this->_temp['lang_queue'])) {
-            $this->_cmdQueue[] = $cmd;
-            return array();
-        }
-
-        try {
-            $this->_sendCmd($cmd);
-        } catch (Horde_Imap_Client_Exception $e) {
-            $this->_setInit('lang', false);
-            return null;
-        }
-
-        return $this->_init['lang'];
-    }
-
-    /**
-     */
-    protected function _getLanguage($list)
-    {
-        if (!$list) {
-            return empty($this->_init['lang'])
-                ? null
-                : $this->_init['lang'];
-        }
-
-        if (!isset($this->_init['langavail'])) {
-            try {
-                $this->_sendCmd($this->_command('LANGUAGE'));
-            } catch (Horde_Imap_Client_Exception $e) {
-                $this->_setInit('langavail', array());
-            }
-        }
-
-        return $this->_init['langavail'];
-    }
-
-    /**
-     * Parse a LANGUAGE response (RFC 5255 [3.3]).
-     *
-     * @param Horde_Imap_Client_Tokenize $data  The server response.
-     */
-    protected function _parseLanguage(Horde_Imap_Client_Tokenize $data)
-    {
-        $lang_list = $data->flushIterator();
-
-        if (count($lang_list) == 1) {
-            // This is the language that was set.
-            $this->_setInit('lang', reset($lang_list));
-        } else {
-            // These are the languages that are available.
-            $this->_setInit('langavail', $lang_list);
-        }
-    }
-
-    /**
-     * Enable an IMAP extension (see RFC 5161).
-     *
-     * @param array $exts  The extensions to enable.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    protected function _enable($exts)
-    {
-        if ($this->queryCapability('ENABLE')) {
-            // Only enable non-enabled extensions.
-            $exts = array_diff($exts, array_keys($this->_temp['enabled']));
-            if (!empty($exts)) {
-                $this->_cmdQueue[] = $this->_command('ENABLE')->add($exts);
-                $this->_enabled($exts, 1);
-            }
-        }
-    }
-
-    /**
-     * Parse an ENABLED response (RFC 5161 [3.2]).
-     *
-     * @param Horde_Imap_Client_Tokenize $data  The server response.
-     */
-    protected function _parseEnabled(Horde_Imap_Client_Tokenize $data)
-    {
-        $this->_enabled($data->flushIterator(), 2);
-    }
-
-    /**
-     */
-    protected function _enabled($exts, $status)
-    {
-        parent::_enabled($exts, $status);
-
-        if (($status == 2) && !empty($this->_init['imapproxy'])) {
-            $this->_setInit('enabled', $this->_temp['enabled']);
-        }
-    }
-
-    /**
-     */
-    protected function _openMailbox(Horde_Imap_Client_Mailbox $mailbox, $mode)
-    {
-        $qresync = isset($this->_temp['enabled']['QRESYNC']);
-
-        $cmd = $this->_command(
-            ($mode == Horde_Imap_Client::OPEN_READONLY) ? 'EXAMINE' : 'SELECT'
-        )->add(
-            new Horde_Imap_Client_Data_Format_Mailbox($mailbox)
-        );
-        $pipeline = $this->_pipeline($cmd);
-
-        /* If QRESYNC is available, synchronize the mailbox. */
-        if ($qresync) {
-            $this->_initCache();
-            $md = $this->_cache->getMetaData($mailbox, null, array(self::CACHE_MODSEQ, 'uidvalid'));
-
-            if (isset($md[self::CACHE_MODSEQ])) {
-                if ($uids = $this->_cache->get($mailbox)) {
-                    $uids = $this->getIdsOb($uids);
-
-                    /* Check for extra long UID string. Assume that any
-                     * server that can handle QRESYNC can also handle long
-                     * input strings (at least 8 KB), so 7 KB is as good as
-                     * any guess as to an upper limit. If this occurs, provide
-                     * a range string (min -> max) instead. */
-                    if (strlen($uid_str = strval($uids)) > 7000) {
-                        $uid_str = $uids->range_string;
-                    }
-                } else {
-                    $uid_str = null;
-                }
-
-                /* Several things can happen with a QRESYNC:
-                 * 1. UIDVALIDITY may have changed.  If so, we need to expire
-                 * the cache immediately (done below).
-                 * 2. NOMODSEQ may have been returned. We can keep current
-                 * message cache data but won't be able to do flag caching.
-                 * 3. VANISHED/FETCH information was returned. These responses
-                 * will have already been handled by those response handlers.
-                 * 4. We are already synced with the local server in which
-                 * case it acts like a normal EXAMINE/SELECT. */
-                $cmd->add(new Horde_Imap_Client_Data_Format_List(array(
-                    'QRESYNC',
-                    new Horde_Imap_Client_Data_Format_List(array_filter(array(
-                        $md['uidvalid'],
-                        $md[self::CACHE_MODSEQ],
-                        $uid_str
-                    )))
-                )));
-            }
-
-            /* Let the 'CLOSED' response code handle mailbox switching if
-             * QRESYNC is active. */
-            if ($this->_selected) {
-                $pipeline->data['qresyncmbox'] = array($mailbox, $mode);
-            } else {
-                $this->_changeSelected($mailbox, $mode);
-            }
-        } else {
-            if (!isset($this->_temp['enabled']['CONDSTORE']) &&
-                $this->_initCache() &&
-                $this->queryCapability('CONDSTORE')) {
-                /* Activate CONDSTORE now if ENABLE is not available. */
-                $cmd->add(new Horde_Imap_Client_Data_Format_List('CONDSTORE'));
-                $this->_enabled(array('CONDSTORE'), 2);
-            }
-
-            $this->_changeSelected($mailbox, $mode);
-        }
-
-        try {
-            $this->_sendCmd($pipeline);
-        } catch (Horde_Imap_Client_Exception_ServerResponse $e) {
-            // An EXAMINE/SELECT failure with a return of 'NO' will cause the
-            // current mailbox to be unselected.
-            if ($e->status == Horde_Imap_Client_Interaction_Server::NO) {
-                $this->_changeSelected(null);
-                $this->_mode = 0;
-                if (!$e->getCode()) {
-                    throw new Horde_Imap_Client_Exception(
-                        sprintf(Horde_Imap_Client_Translation::t("Could not open mailbox \"%s\"."), $mailbox),
-                        Horde_Imap_Client_Exception::MAILBOX_NOOPEN
-                    );
-                }
-            }
-            throw $e;
-        }
-
-        if ($qresync) {
-            /* Mailbox is fully sync'd. */
-            $this->_mailboxOb()->sync = true;
-        }
-    }
-
-    /**
-     */
-    protected function _createMailbox(Horde_Imap_Client_Mailbox $mailbox, $opts)
-    {
-        $cmd = $this->_command('CREATE')->add(
-            new Horde_Imap_Client_Data_Format_Mailbox($mailbox)
-        );
-
-        if (!empty($opts['special_use'])) {
-            $cmd->add(array(
-                'USE',
-                new Horde_Imap_Client_Data_Format_List($opts['special_use'])
-            ));
-        }
-
-        // CREATE returns no untagged information (RFC 3501 [6.3.3])
-        $this->_sendCmd($cmd);
-    }
-
-    /**
-     */
-    protected function _deleteMailbox(Horde_Imap_Client_Mailbox $mailbox)
-    {
-        // Some IMAP servers will not allow a delete of a currently open
-        // mailbox.
-        if ($mailbox->equals($this->_selected)) {
-            $this->close();
-        }
-
-        $cmd = $this->_command('DELETE')->add(
-            new Horde_Imap_Client_Data_Format_Mailbox($mailbox)
-        );
-
-        try {
-            // DELETE returns no untagged information (RFC 3501 [6.3.4])
-            $this->_sendCmd($cmd);
-        } catch (Horde_Imap_Client_Exception $e) {
-            // Some IMAP servers won't allow a mailbox delete unless all
-            // messages in that mailbox are deleted.
-            $this->expunge($mailbox, array(
-                'delete' => true
-            ));
-            $this->_sendCmd($cmd);
-        }
-    }
-
-    /**
-     */
-    protected function _renameMailbox(Horde_Imap_Client_Mailbox $old,
-                                      Horde_Imap_Client_Mailbox $new)
-    {
-        // Some IMAP servers will not allow a rename of a currently open
-        // mailbox.
-        if ($old->equals($this->_selected)) {
-            $this->close();
-        }
-
-        // RENAME returns no untagged information (RFC 3501 [6.3.5])
-        $this->_sendCmd(
-            $this->_command('RENAME')->add(array(
-                new Horde_Imap_Client_Data_Format_Mailbox($old),
-                new Horde_Imap_Client_Data_Format_Mailbox($new)
-            ))
-        );
-    }
-
-    /**
-     */
-    protected function _subscribeMailbox(Horde_Imap_Client_Mailbox $mailbox,
-                                         $subscribe)
-    {
-        // SUBSCRIBE/UNSUBSCRIBE returns no untagged information (RFC 3501
-        // [6.3.6 & 6.3.7])
-        $this->_sendCmd(
-            $this->_command(
-                $subscribe ? 'SUBSCRIBE' : 'UNSUBSCRIBE'
-            )->add(
-                new Horde_Imap_Client_Data_Format_Mailbox($mailbox)
-            )
-        );
-    }
-
-    /**
-     */
-    protected function _listMailboxes($pattern, $mode, $options)
-    {
-        // RFC 5258 [3.1]: Use LSUB for MBOX_SUBSCRIBED if no other server
-        // return options are specified.
-        if (($mode == Horde_Imap_Client::MBOX_SUBSCRIBED) &&
-            empty($options['attributes']) &&
-            empty($options['children']) &&
-            empty($options['recursivematch']) &&
-            empty($options['remote']) &&
-            empty($options['special_use']) &&
-            empty($options['status'])) {
-            return $this->_getMailboxList(
-                $pattern,
-                Horde_Imap_Client::MBOX_SUBSCRIBED,
-                array(
-                    'delimiter' => !empty($options['delimiter']),
-                    'flat' => !empty($options['flat']),
-                    'no_listext' => true
-                )
-            );
-        }
-
-        // Get the list of subscribed/unsubscribed mailboxes. Since LSUB is
-        // not guaranteed to have correct attributes, we must use LIST to
-        // ensure we receive the correct information.
-        if (($mode != Horde_Imap_Client::MBOX_ALL) &&
-            !$this->queryCapability('LIST-EXTENDED')) {
-            $subscribed = $this->_getMailboxList($pattern, Horde_Imap_Client::MBOX_SUBSCRIBED, array('flat' => true));
-
-            // If mode is subscribed, and 'flat' option is true, we can
-            // return now.
-            if (($mode == Horde_Imap_Client::MBOX_SUBSCRIBED) &&
-                !empty($options['flat'])) {
-                return $subscribed;
-            }
-        } else {
-            $subscribed = null;
-        }
-
-        return $this->_getMailboxList($pattern, $mode, $options, $subscribed);
-    }
-
-    /**
-     * Obtain a list of mailboxes.
-     *
-     * @param array $pattern     The mailbox search pattern(s).
-     * @param integer $mode      Which mailboxes to return.
-     * @param array $options     Additional options. 'no_listext' will skip
-     *                           using the LIST-EXTENDED capability.
-     * @param array $subscribed  A list of subscribed mailboxes.
-     *
-     * @return array  See listMailboxes(().
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    protected function _getMailboxList($pattern, $mode, $options,
-                                       $subscribed = null)
-    {
-        $check = (($mode != Horde_Imap_Client::MBOX_ALL) && !is_null($subscribed));
-
-        // Setup entry for use in _parseList().
-        $pipeline = $this->_pipeline();
-        $pipeline->data['mailboxlist'] = array(
-            'check' => $check,
-            'ext' => false,
-            'options' => $options,
-            'subexist' => ($mode == Horde_Imap_Client::MBOX_SUBSCRIBED_EXISTS),
-            'subscribed' => ($check ? array_flip(array_map('strval', $subscribed)) : null)
-        );
-        $pipeline->data['listresponse'] = array();
-
-        $cmds = array();
-        $return_opts = new Horde_Imap_Client_Data_Format_List();
-
-        if ($this->queryCapability('LIST-EXTENDED') &&
-            empty($options['no_listext'])) {
-            $cmd = $this->_command('LIST');
-            $pipeline->data['mailboxlist']['ext'] = true;
-
-            $select_opts = new Horde_Imap_Client_Data_Format_List();
-
-            if (($mode == Horde_Imap_Client::MBOX_SUBSCRIBED) ||
-                ($mode == Horde_Imap_Client::MBOX_SUBSCRIBED_EXISTS)) {
-                $select_opts->add('SUBSCRIBED');
-                $return_opts->add('SUBSCRIBED');
-            }
-
-            if (!empty($options['remote'])) {
-                $select_opts->add('REMOTE');
-            }
-
-            if (!empty($options['recursivematch'])) {
-                $select_opts->add('RECURSIVEMATCH');
-            }
-
-            $cmd->add(array(
-                $select_opts,
-                ''
-            ));
-
-            $tmp = new Horde_Imap_Client_Data_Format_List();
-            foreach ($pattern as $val) {
-                $tmp->add(new Horde_Imap_Client_Data_Format_ListMailbox($val));
-            }
-            $cmd->add($tmp);
-
-            if (!empty($options['children'])) {
-                $return_opts->add('CHILDREN');
-            }
-
-            if (!empty($options['special_use'])) {
-                $return_opts->add('SPECIAL-USE');
-            }
-
-            $cmds[] = $cmd;
-        } else {
-            foreach ($pattern as $val) {
-                $cmds[] = $this->_command(
-                    ($mode == Horde_Imap_Client::MBOX_SUBSCRIBED) ? 'LSUB' : 'LIST'
-                )->add(array(
-                    '',
-                    new Horde_Imap_Client_Data_Format_ListMailbox($val)
-                ));
-            }
-        }
-
-        /* LIST-STATUS does NOT depend on LIST-EXTENDED. */
-        if (!empty($options['status']) &&
-            $this->queryCapability('LIST-STATUS')) {
-            $available_status = array(
-                Horde_Imap_Client::STATUS_MESSAGES,
-                Horde_Imap_Client::STATUS_RECENT,
-                Horde_Imap_Client::STATUS_UIDNEXT,
-                Horde_Imap_Client::STATUS_UIDVALIDITY,
-                Horde_Imap_Client::STATUS_UNSEEN,
-                Horde_Imap_Client::STATUS_HIGHESTMODSEQ
-            );
-
-            $status_opts = array();
-            foreach (array_intersect($this->_statusFields, $available_status) as $key => $val) {
-                if ($options['status'] & $val) {
-                    $status_opts[] = $key;
-                }
-            }
-
-            if (count($status_opts)) {
-                $return_opts->add(array(
-                    'STATUS',
-                    new Horde_Imap_Client_Data_Format_List(
-                        array_map('strtoupper', $status_opts)
-                    )
-                ));
-            }
-        }
-
-        foreach ($cmds as $val) {
-            if (count($return_opts)) {
-                $val->add(array(
-                    'RETURN',
-                    $return_opts
-                ));
-            }
-
-            $pipeline->add($val);
-        }
-
-        try {
-            $lr = $this->_sendCmd($pipeline)->data['listresponse'];
-        } catch (Horde_Imap_Client_Exception_ServerResponse $e) {
-            /* Archiveopteryx 3.1.3 can't process empty list-select-opts list.
-             * Retry using base IMAP4rev1 functionality. */
-            if (($e->status == Horde_Imap_Client_Interaction_Server::BAD) &&
-                $this->queryCapability('LIST-EXTENDED')) {
-                $this->_unsetCapability('LIST-EXTENDED');
-                return $this->_listMailboxes($pattern, $mode, $options);
-            }
-
-            throw $e;
-        }
-
-        if (!empty($options['flat'])) {
-            return array_values($lr);
-        }
-
-        /* Add in STATUS return, if needed. */
-        if (!empty($options['status'])) {
-            foreach ($pattern as $val) {
-                $val_utf8 = Horde_Imap_Client_Utf7imap::Utf7ImapToUtf8($val);
-                if (isset($lr[$val_utf8])) {
-                    $lr[$val_utf8]['status'] = $this->_prepareStatusResponse($status_opts, $val_utf8);
-                }
-            }
-        }
-
-        return $lr;
-    }
-
-    /**
-     * Parse a LIST/LSUB response (RFC 3501 [7.2.2 & 7.2.3]).
-     *
-     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
-     *                                                          object.
-     * @param Horde_Imap_Client_Tokenize $data  The server response (includes
-     *                                          type as first token).
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    protected function _parseList(
-        Horde_Imap_Client_Interaction_Pipeline $pipeline,
-        Horde_Imap_Client_Tokenize $data
-    )
-    {
-        $data->next();
-        $attr = $data->flushIterator();
-        $delimiter = $data->next();
-        $mbox = Horde_Imap_Client_Mailbox::get($data->next(), true);
-        $ml = $pipeline->data['mailboxlist'];
-
-        if ($ml['check'] &&
-            $ml['subexist'] &&
-            // subscribed list is in UTF-8
-            !isset($ml['subscribed'][strval($mbox)])) {
-            return;
-        } elseif ((!$ml['check'] && $ml['subexist']) ||
-                   (empty($ml['options']['flat']) &&
-                    !empty($ml['options']['attributes']))) {
-            $attr = array_flip(array_map('strtolower', $attr));
-            if ($ml['subexist'] &&
-                !$ml['check'] &&
-                isset($attr['\\nonexistent'])) {
-                return;
-            }
-        }
-
-        if (empty($ml['options']['flat'])) {
-            $tmp = array(
-                'mailbox' => $mbox
-            );
-
-            if (!empty($ml['options']['attributes'])) {
-                /* RFC 5258 [3.4]: inferred attributes. */
-                if ($ml['ext']) {
-                    if (isset($attr['\\noinferiors'])) {
-                        $attr['\\hasnochildren'] = 1;
-                    }
-                    if (isset($attr['\\nonexistent'])) {
-                        $attr['\\noselect'] = 1;
-                    }
-                }
-                $tmp['attributes'] = array_keys($attr);
-            }
-            if (!empty($ml['options']['delimiter'])) {
-                $tmp['delimiter'] = $delimiter;
-            }
-            if ($data->next() !== false) {
-                $tmp['extended'] = $data->flushIterator();
-            }
-            $pipeline->data['listresponse'][strval($mbox)] = $tmp;
-        } else {
-            $pipeline->data['listresponse'][] = $mbox;
-        }
-    }
-
-    /**
-     */
-    protected function _status($mboxes, $flags)
-    {
-        $out = $to_process = array();
-        $pipeline = $this->_pipeline();
-        $unseen_flags = array(
-            Horde_Imap_Client::STATUS_FIRSTUNSEEN,
-            Horde_Imap_Client::STATUS_UNSEEN
-        );
-
-        foreach ($mboxes as $mailbox) {
-            /* If FLAGS/PERMFLAGS/UIDNOTSTICKY/FIRSTUNSEEN are needed, we must
-             * do a SELECT/EXAMINE to get this information (data will be
-             * caught in the code below). */
-            if (($flags & Horde_Imap_Client::STATUS_FIRSTUNSEEN) ||
-                ($flags & Horde_Imap_Client::STATUS_FLAGS) ||
-                ($flags & Horde_Imap_Client::STATUS_PERMFLAGS) ||
-                ($flags & Horde_Imap_Client::STATUS_UIDNOTSTICKY)) {
-                $this->openMailbox($mailbox);
-            }
-
-            $mbox_ob = $this->_mailboxOb($mailbox);
-            $data = $query = array();
-
-            foreach ($this->_statusFields as $key => $val) {
-                if (!($val & $flags)) {
-                    continue;
-                }
-
-                if ($val == Horde_Imap_Client::STATUS_HIGHESTMODSEQ) {
-                    /* Don't include modseq returns if server does not support
-                     * it. */
-                    if (!$this->queryCapability('CONDSTORE')) {
-                        continue;
-                    }
-
-                    /* Even though CONDSTORE is available, it may not yet have
-                     * been enabled. */
-                    if (!isset($this->_temp['enabled']['CONDSTORE'])) {
-                        $this->_enabled(array('CONDSTORE'), 2);
-                    }
-                }
-
-                if ($mailbox->equals($this->_selected)) {
-                    if (!is_null($tmp = $mbox_ob->getStatus($val))) {
-                        $data[$key] = $tmp;
-                    } elseif (($val == Horde_Imap_Client::STATUS_UIDNEXT) &&
-                              ($flags & Horde_Imap_Client::STATUS_UIDNEXT_FORCE)) {
-                        /* UIDNEXT is not mandatory. */
-                        if ($mbox_ob->getStatus(Horde_Imap_Client::STATUS_MESSAGES) == 0) {
-                            $data[$key] = 0;
-                        } else {
-                            $fquery = new Horde_Imap_Client_Fetch_Query();
-                            $fquery->uid();
-                            $fetch_res = $this->fetch($this->_selected, $fquery, array(
-                                'ids' => $this->getIdsOb(Horde_Imap_Client_Ids::LARGEST)
-                            ));
-                            $data[$key] = $fetch_res->first()->getUid() + 1;
-                        }
-                    } elseif (in_array($val, $unseen_flags)) {
-                        /* RFC 3501 [6.3.1] - FIRSTUNSEEN information is not
-                         * mandatory. If missing in EXAMINE/SELECT results, we
-                         * need to do a search. An UNSEEN count also requires
-                         * a search. */
-                        $squery = new Horde_Imap_Client_Search_Query();
-                        $squery->flag(Horde_Imap_Client::FLAG_SEEN, false);
-                        $search = $this->search($mailbox, $squery, array(
-                            'results' => array(
-                                Horde_Imap_Client::SEARCH_RESULTS_MIN,
-                                Horde_Imap_Client::SEARCH_RESULTS_COUNT
-                            ),
-                            'sequence' => true
-                        ));
-
-                        $mbox_ob->setStatus(Horde_Imap_Client::STATUS_FIRSTUNSEEN, $search['min']);
-                        $mbox_ob->setStatus(Horde_Imap_Client::STATUS_UNSEEN, $search['count']);
-
-                        $data[$key] = $mbox_ob->getStatus($val);
-                    }
-                } else {
-                    $query[] = $key;
-                }
-            }
-
-            $out[strval($mailbox)] = $data;
-
-            if (count($query)) {
-                $pipeline->add(
-                    $this->_command('STATUS')->add(array(
-                        new Horde_Imap_Client_Data_Format_Mailbox($mailbox),
-                        new Horde_Imap_Client_Data_Format_List(
-                            array_map('strtoupper', $query)
-                        )
-                    ))
-                );
-                $to_process[] = array($query, $mailbox);
-            }
-        }
-
-        if (count($pipeline)) {
-            $this->_sendCmd($pipeline);
-
-            foreach ($to_process as $val) {
-                $out[strval($val[1])] += $this->_prepareStatusResponse($val[0], $val[1]);
-            }
-        }
-
-        return $out;
-    }
-
-    /**
-     * Parse a STATUS response (RFC 3501 [7.2.4], RFC 4551 [3.6])
-     *
-     * @param Horde_Imap_Client_Tokenize $data  Token data
-     */
-    protected function _parseStatus(Horde_Imap_Client_Tokenize $data)
-    {
-        // Mailbox name is in UTF7-IMAP
-        $mbox_ob = $this->_mailboxOb(
-            Horde_Imap_Client_Mailbox::get($data->next(), true)
-        );
-
-        $data->next();
-
-        while (($k = $data->next()) !== false) {
-            $mbox_ob->setStatus(
-                $this->_statusFields[strtolower($k)],
-                $data->next()
-            );
-        }
-    }
-
-    /**
-     * Prepares a status response for a mailbox.
-     *
-     * @param array $request   The status keys to return.
-     * @param string $mailbox  The mailbox to query.
-     */
-    protected function _prepareStatusResponse($request, $mailbox)
-    {
-        $mbox_ob = $this->_mailboxOb($mailbox);
-        $out = array();
-
-        foreach ($request as $val) {
-            $out[$val] = $mbox_ob->getStatus($this->_statusFields[$val]);
-        }
-
-        return $out;
-    }
-
-    /**
-     */
-    protected function _append(Horde_Imap_Client_Mailbox $mailbox, $data,
-                               $options)
-    {
-        // Check for MULTIAPPEND extension (RFC 3502)
-        if ((count($data) > 1) && !$this->queryCapability('MULTIAPPEND')) {
-            $result = $this->getIdsOb();
-            foreach (array_keys($data) as $key) {
-                $res = $this->_append($mailbox, array($data[$key]), $options);
-                if (($res === true) || ($result === true)) {
-                    $result = true;
-                } else {
-                    $result->add($res);
-                }
-            }
-            return $result;
-        }
-
-        // Check for CATENATE extension (RFC 4469)
-        $catenate = $this->queryCapability('CATENATE');
-
-        $asize = 0;
-
-        $cmd = $this->_command('APPEND')->add(
-            new Horde_Imap_Client_Data_Format_Mailbox($mailbox)
-        );
-        $cmd->literal8 = true;
-
-        foreach (array_keys($data) as $key) {
-            if (!empty($data[$key]['flags'])) {
-                $tmp = new Horde_Imap_Client_Data_Format_List();
-                foreach ($data[$key]['flags'] as $val) {
-                    /* Ignore recent flag. RFC 3501 [9]: flag definition */
-                    if (strcasecmp($val, Horde_Imap_Client::FLAG_RECENT) !== 0) {
-                        $tmp->add($val);
-                    }
-                }
-                $cmd->add($tmp);
-            }
-
-            if (!empty($data[$key]['internaldate'])) {
-                $cmd->add(new Horde_Imap_Client_Data_Format_DateTime($data[$key]['internaldate']));
-            }
-
-            if (is_array($data[$key]['data'])) {
-                if ($catenate) {
-                    $cmd->add('CATENATE');
-                    $tmp = new Horde_Imap_Client_Data_Format_List();
-                } else {
-                    $data_stream = new Horde_Stream_Temp();
-                }
-
-                reset($data[$key]['data']);
-                while (list(,$v) = each($data[$key]['data'])) {
-                    switch ($v['t']) {
-                    case 'text':
-                        if ($catenate) {
-                            $tmp->add(array(
-                                'TEXT',
-                                $this->_appendData($v['v'], $asize)
-                            ));
-                        } else {
-                            if (is_resource($v['v'])) {
-                                rewind($v['v']);
-                            }
-                            $data_stream->add($v['v']);
-                        }
-                        break;
-
-                    case 'url':
-                        if ($catenate) {
-                            $tmp->add(array(
-                                'URL',
-                                new Horde_Imap_Client_Data_Format_Astring($v['v'])
-                            ));
-                        } else {
-                            $data_stream->add($this->_convertCatenateUrl($v['v']));
-                        }
-                        break;
-                    }
-                }
-
-                if ($catenate) {
-                    $cmd->add($tmp);
-                } else {
-                    $cmd->add($this->_appendData($data_stream->stream, $asize));
-                }
-            } else {
-                $cmd->add($this->_appendData($data[$key]['data'], $asize));
-            }
-        }
-
-        /* Although it is normally more efficient to use LITERAL+, disable if
-         * payload is over 0.5 MB because it allows the server to throw error
-         * before we potentially push a lot of data to server that would
-         * otherwise be ignored (see RFC 4549 [4.2.2.3]).
-         * Additionally, if using BINARY, since so many IMAP servers have
-         * issues with APPEND + BINARY, don't use LITERAL+ since servers may
-         * send BAD after initial command. */
-        $cmd->literalplus = (($asize < 524288) && !$this->queryCapability('BINARY'));
-
-        // If the mailbox is currently selected read-only, we need to close
-        // because some IMAP implementations won't allow an append. And some
-        // implementations don't support append on ANY open mailbox. Be safe
-        // and always make sure we are in a non-selected state.
-        $this->close();
-
-        try {
-            $resp = $this->_sendCmd($cmd);
-        } catch (Horde_Imap_Client_Exception $e) {
-            switch ($e->getCode()) {
-            case $e::CATENATE_BADURL:
-            case $e::CATENATE_TOOBIG:
-                /* Cyrus 2.4 (at least as of .14) has a broken CATENATE (see
-                 * Bug #11111). Regardless, if CATENATE is broken, we can try
-                 * to fallback to APPEND. */
-                $this->_unsetCapability('CATENATE');
-                return $this->_append($mailbox, $data, $options);
-
-            case $e::DISCONNECT:
-                /* Workaround broken literal8 on Cyrus. */
-                if ($this->queryCapability('BINARY')) {
-                    // Need to re-login first before removing capability.
-                    $this->login();
-                    $this->_unsetCapability('BINARY');
-                    return $this->_append($mailbox, $data, $options);
-                }
-                break;
-            }
-
-            if (!empty($options['create']) &&
-                !empty($e->resp_data['trycreate'])) {
-                $this->createMailbox($mailbox);
-                unset($options['create']);
-                return $this->_append($mailbox, $data, $options);
-            }
-
-            /* RFC 3516/4466 says we should be able to append binary data
-             * using literal8 "~{#} format", but it doesn't seem to work on
-             * all servers tried (UW-IMAP/Cyrus). Do a last-ditch check for
-             * broken BINARY and attempt to fix here. */
-            if ($this->queryCapability('BINARY') &&
-                ($e instanceof Horde_Imap_Client_Exception_ServerResponse)) {
-                switch ($e->status) {
-                case Horde_Imap_Client_Interaction_Server::BAD:
-                case Horde_Imap_Client_Interaction_Server::NO:
-                    $this->_unsetCapability('BINARY');
-                    return $this->_append($mailbox, $data, $options);
-                }
-            }
-
-            throw $e;
-        }
-
-        /* If we reach this point and have data in 'appenduid', UIDPLUS (RFC
-         * 4315) has done the dirty work for us. */
-        return isset($resp->data['appenduid'])
-            ? $resp->data['appenduid']
-            : true;
-    }
-
-    /**
-     * Prepares append message data for insertion into the IMAP command
-     * string.
-     *
-     * @param mixed $data      Either a resource or a string.
-     * @param integer &$asize  Total append size.
-     *
-     * @return Horde_Imap_Client_Data_Format_String  The data object.
-     */
-    protected function _appendData($data, &$asize)
-    {
-        if (is_resource($data)) {
-            rewind($data);
-        }
-
-        $ob = new Horde_Imap_Client_Data_Format_String($data, array(
-            'eol' => true,
-            'skipscan' => true
-        ));
-
-        // APPEND data MUST be sent in a literal (RFC 3501 [6.3.11]).
-        $ob->forceLiteral();
-
-        $asize += $ob->length();
-
-        return $ob;
-    }
-
-    /**
-     * Converts a CATENATE URL to stream data.
-     *
-     * @param string $url  The CATENATE URL.
-     *
-     * @return resource  A stream containing the data.
-     */
-    protected function _convertCatenateUrl($url)
-    {
-        $e = $part = null;
-        $url = new Horde_Imap_Client_Url($url);
-
-        if (!is_null($url->mailbox) && !is_null($url->uid)) {
-            try {
-                $status_res = is_null($url->uidvalidity)
-                    ? null
-                    : $this->status($url->mailbox, Horde_Imap_Client::STATUS_UIDVALIDITY);
-
-                if (is_null($status_res) ||
-                    ($status_res['uidvalidity'] == $url->uidvalidity)) {
-                    if (!isset($this->_temp['catenate_ob'])) {
-                        $this->_temp['catenate_ob'] = new Horde_Imap_Client_Socket_Catenate($this);
-                    }
-                    $part = $this->_temp['catenate_ob']->fetchFromUrl($url);
-                }
-            } catch (Horde_Imap_Client_Exception $e) {}
-        }
-
-        if (is_null($part)) {
-            $message = 'Bad IMAP URL given in CATENATE data: ' . strval($url);
-            if ($e) {
-                $message .= ' ' . $e->getMessage();
-            }
-
-            throw new InvalidArgumentException($message);
-        }
-
-        return $part;
-    }
-
-    /**
-     */
-    protected function _check()
-    {
-        // CHECK returns no untagged information (RFC 3501 [6.4.1])
-        $this->_sendCmd($this->_command('CHECK'));
-    }
-
-    /**
-     */
-    protected function _close($options)
-    {
-        if (empty($options['expunge'])) {
-            if ($this->queryCapability('UNSELECT')) {
-                // RFC 3691 defines 'UNSELECT' for precisely this purpose
-                $this->_sendCmd($this->_command('UNSELECT'));
-            } else {
-                // RFC 3501 [6.4.2]: to close a mailbox without expunge,
-                // select a non-existent mailbox. Selecting a null mailbox
-                // should do the trick.
-                try {
-                    $this->_sendCmd($this->_command('SELECT')->add(''));
-                } catch (Horde_Imap_Client_Exception_ServerResponse $e) {
-                    // Ignore error; it is expected.
-                }
-            }
-        } else {
-            // If caching, we need to know the UIDs being deleted, so call
-            // expunge() before calling close().
-            if ($this->_initCache(true)) {
-                $this->expunge($this->_selected);
-            }
-
-            // CLOSE returns no untagged information (RFC 3501 [6.4.2])
-            $this->_sendCmd($this->_command('CLOSE'));
-        }
-    }
-
-    /**
-     */
-    protected function _expunge($options)
-    {
-        $expunged_ob = $modseq = null;
-        $ids = $options['ids'];
-        $list_msgs = !empty($options['list']);
-        $uidplus = $this->queryCapability('UIDPLUS');
-        $unflag = array();
-        $use_cache = $this->_initCache(true);
-
-        if ($ids->all) {
-            if (!$uidplus && ($list_msgs || $use_cache)) {
-                $ids = $this->resolveIds($this->_selected, $ids, 2);
-            }
-        } elseif ($uidplus) {
-            /* If QRESYNC is not available, and we are returning the list of
-             * expunged messages (or we are caching), we have to make sure we
-             * have a mapping of Sequence -> UIDs. If we have QRESYNC, the
-             * server SHOULD return a VANISHED response with UIDs. However,
-             * even if the server returns EXPUNGEs instead, we can use
-             * vanished() to grab the list. */
-            unset($this->_temp['search_save']);
-            if (isset($this->_temp['enabled']['QRESYNC'])) {
-                $ids = $this->resolveIds($this->_selected, $ids, 1);
-                if ($list_msgs) {
-                    $modseq = $this->_mailboxOb()->getStatus(Horde_Imap_Client::STATUS_HIGHESTMODSEQ);
-                }
-            } else {
-                $ids = $this->resolveIds($this->_selected, $ids, ($list_msgs || $use_cache) ? 2 : 1);
-            }
-            if (!empty($this->_temp['search_save'])) {
-                $ids = $this->getIdsOb(Horde_Imap_Client_Ids::SEARCH_RES);
-            }
-        } else {
-            /* Without UIDPLUS, need to temporarily unflag all messages marked
-             * as deleted but not a part of requested IDs to delete. Use NOT
-             * searches to accomplish this goal. */
-            $squery = new Horde_Imap_Client_Search_Query();
-            $squery->flag(Horde_Imap_Client::FLAG_DELETED, true);
-            $squery->ids($ids, true);
-
-            $s_res = $this->search($this->_selected, $squery, array(
-                'results' => array(
-                    Horde_Imap_Client::SEARCH_RESULTS_MATCH,
-                    Horde_Imap_Client::SEARCH_RESULTS_SAVE
-                )
-            ));
-
-            $this->store($this->_selected, array(
-                'ids' => empty($s_res['save']) ? $s_res['match'] : $this->getIdsOb(Horde_Imap_Client_Ids::SEARCH_RES),
-                'remove' => array(Horde_Imap_Client::FLAG_DELETED)
-            ));
-
-            $unflag = $s_res['match'];
-        }
-
-        if ($list_msgs) {
-            $expunged_ob = $this->getIdsOb();
-            $this->_temp['expunged'] = $expunged_ob;
-        }
-
-        /* Always use UID EXPUNGE if available. */
-        if ($uidplus) {
-            /* We can only pipeline STORE w/ EXPUNGE if using UIDs and UIDPLUS
-             * is available. */
-            if (empty($options['delete'])) {
-                $pipeline = $this->_pipeline();
-            } else {
-                $pipeline = $this->_storeCmd(array(
-                    'add' => array(
-                        Horde_Imap_Client::FLAG_DELETED
-                    ),
-                    'ids' => $ids
-                ));
-            }
-
-            foreach ($ids->split(2000) as $val) {
-                $pipeline->add(
-                    $this->_command('UID EXPUNGE')->add($val)
-                );
-            }
-
-            $resp = $this->_sendCmd($pipeline);
-        } else {
-            if (!empty($options['delete'])) {
-                $this->store($this->_selected, array(
-                    'add' => array(Horde_Imap_Client::FLAG_DELETED),
-                    'ids' => $ids
-                ));
-            }
-
-            if ($use_cache || $list_msgs) {
-                $this->_sendCmd($this->_command('EXPUNGE'));
-            } else {
-                /* This is faster than an EXPUNGE because the server will not
-                 * return untagged EXPUNGE responses. We can only do this if
-                 * we are not updating cache information. */
-                $this->close(array('expunge' => true));
-            }
-        }
-
-        unset($this->_temp['expunged']);
-
-        if (!empty($unflag)) {
-            $this->store($this->_selected, array(
-                'add' => array(Horde_Imap_Client::FLAG_DELETED),
-                'ids' => $unflag
-            ));
-        }
-
-        if (!is_null($modseq) && !empty($resp->data['expunge_seen'])) {
-            /* There's a chance we actually did a full map of sequence -> UID,
-             * but this code should never be reached in the first place so
-             * be ultra-safe and just do a full VANISHED search. */
-            $expunged_ob = $this->vanished($this->_selected, $modseq, array(
-                'ids' => $ids
-            ));
-            $this->_deleteMsgs($this->_selected, $expunged_ob, array(
-                'pipeline' => $resp
-            ));
-        }
-
-        return $expunged_ob;
-    }
-
-    /**
-     * Parse a VANISHED response (RFC 5162 [3.6]).
-     *
-     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
-     *                                                          object.
-     * @param Horde_Imap_Client_Tokenize $data  The response data.
-     */
-    protected function _parseVanished(
-        Horde_Imap_Client_Interaction_Pipeline $pipeline,
-        Horde_Imap_Client_Tokenize $data
-    )
-    {
-        /* There are two forms of VANISHED.  VANISHED (EARLIER) will be sent
-         * in a FETCH (VANISHED) or SELECT/EXAMINE (QRESYNC) call.
-         * If this is the case, we can go ahead and update the cache
-         * immediately (we know we are caching or else QRESYNC would not be
-         * enabled). HIGHESTMODSEQ information will be updated via the tagged
-         * response. */
-        if (($curr = $data->next()) === true) {
-            if (strtoupper($data->next()) == 'EARLIER') {
-                /* Caching is guaranteed to be active if we are using
-                 * QRESYNC. */
-                $data->next();
-                $vanished = $this->getIdsOb($data->next());
-                if (isset($pipeline->data['vanished'])) {
-                    $pipeline->data['vanished']->add($vanished);
-                } else {
-                    $this->_deleteMsgs($this->_selected, $vanished, array(
-                        'pipeline' => $pipeline
-                    ));
-                }
-            }
-        } else {
-            /* The second form is just VANISHED. This is analogous to EXPUNGE
-             * and requires the message count to decrement. */
-            $this->_deleteMsgs($this->_selected, $this->getIdsOb($curr), array(
-                'decrement' => true,
-                'pipeline' => $pipeline
-            ));
-        }
-    }
-
-    /**
-     * Search a mailbox.  This driver supports all IMAP4rev1 search criteria
-     * as defined in RFC 3501.
-     */
-    protected function _search($query, $options)
-    {
-        $sort_criteria = array(
-            Horde_Imap_Client::SORT_ARRIVAL => 'ARRIVAL',
-            Horde_Imap_Client::SORT_CC => 'CC',
-            Horde_Imap_Client::SORT_DATE => 'DATE',
-            Horde_Imap_Client::SORT_DISPLAYFROM => 'DISPLAYFROM',
-            Horde_Imap_Client::SORT_DISPLAYTO => 'DISPLAYTO',
-            Horde_Imap_Client::SORT_FROM => 'FROM',
-            Horde_Imap_Client::SORT_REVERSE => 'REVERSE',
-            Horde_Imap_Client::SORT_RELEVANCY => 'RELEVANCY',
-            // This is a bogus entry to allow the sort options check to
-            // correctly work below.
-            Horde_Imap_Client::SORT_SEQUENCE => 'SEQUENCE',
-            Horde_Imap_Client::SORT_SIZE => 'SIZE',
-            Horde_Imap_Client::SORT_SUBJECT => 'SUBJECT',
-            Horde_Imap_Client::SORT_TO => 'TO'
-        );
-
-        $results_criteria = array(
-            Horde_Imap_Client::SEARCH_RESULTS_COUNT => 'COUNT',
-            Horde_Imap_Client::SEARCH_RESULTS_MATCH => 'ALL',
-            Horde_Imap_Client::SEARCH_RESULTS_MAX => 'MAX',
-            Horde_Imap_Client::SEARCH_RESULTS_MIN => 'MIN',
-            Horde_Imap_Client::SEARCH_RESULTS_RELEVANCY => 'RELEVANCY',
-            Horde_Imap_Client::SEARCH_RESULTS_SAVE => 'SAVE'
-        );
-
-        // Check if the server supports sorting (RFC 5256).
-        $esearch = $return_sort = $server_seq_sort = $server_sort = false;
-        if (!empty($options['sort'])) {
-            /* Make sure sort options are correct. If not, default to no
-             * sort. */
-            if (count(array_intersect($options['sort'], array_keys($sort_criteria))) === 0) {
-                unset($options['sort']);
-            } else {
-                $return_sort = true;
-
-                if ($server_sort = $this->queryCapability('SORT')) {
-                    /* Make sure server supports DISPLAYFROM & DISPLAYTO. */
-                    $server_sort =
-                        !array_intersect($options['sort'], array(Horde_Imap_Client::SORT_DISPLAYFROM, Horde_Imap_Client::SORT_DISPLAYTO)) ||
-                        (is_array($server_sort) &&
-                         in_array('DISPLAY', $server_sort));
-                }
-
-                /* If doing a sequence sort, need to do this on the client
-                 * side. */
-                if ($server_sort &&
-                    in_array(Horde_Imap_Client::SORT_SEQUENCE, $options['sort'])) {
-                    $server_sort = false;
-
-                    /* Optimization: If doing only a sequence sort, just do a
-                     * simple search and sort UIDs/sequences on client side. */
-                    switch (count($options['sort'])) {
-                    case 1:
-                        $server_seq_sort = true;
-                        break;
-
-                    case 2:
-                        $server_seq_sort = (reset($options['sort']) == Horde_Imap_Client::SORT_REVERSE);
-                        break;
-                    }
-                }
-            }
-        }
-
-        $charset = is_null($options['_query']['charset'])
-            ? 'US-ASCII'
-            : $options['_query']['charset'];
-
-        if ($server_sort) {
-            $cmd = $this->_command(
-                empty($options['sequence']) ? 'UID SORT' : 'SORT'
-            );
-            $results = array();
-
-            // Use ESEARCH (RFC 4466) response if server supports.
-            $esearch = false;
-
-            // Check for ESORT capability (RFC 5267)
-            if ($this->queryCapability('ESORT')) {
-                foreach ($options['results'] as $val) {
-                    if (isset($results_criteria[$val]) &&
-                        ($val != Horde_Imap_Client::SEARCH_RESULTS_SAVE)) {
-                        $results[] = $results_criteria[$val];
-                    }
-                }
-                $esearch = true;
-            }
-
-            // Add PARTIAL limiting (RFC 5267 [4.4])
-            if ((!$esearch || !empty($options['partial'])) &&
-                ($cap = $this->queryCapability('CONTEXT')) &&
-                in_array('SORT', $cap)) {
-                /* RFC 5267 indicates RFC 4466 ESEARCH support,
-                 * notwithstanding RFC 4731 support. */
-                $esearch = true;
-
-                if (!empty($options['partial'])) {
-                    /* Can't have both ALL and PARTIAL returns. */
-                    $results = array_diff($results, array('ALL'));
-
-                    $results[] = 'PARTIAL';
-                    $results[] = strval($this->getIdsOb($options['partial']));
-                }
-            }
-
-            if ($esearch && empty($this->_init['noesearch'])) {
-                $cmd->add(array(
-                    'RETURN',
-                    new Horde_Imap_Client_Data_Format_List($results)
-                ));
-            }
-
-            $tmp = new Horde_Imap_Client_Data_Format_List();
-            foreach ($options['sort'] as $val) {
-                if (isset($sort_criteria[$val])) {
-                    $tmp->add($sort_criteria[$val]);
-                }
-            }
-            $cmd->add($tmp);
-
-            // Charset is mandatory for SORT (RFC 5256 [3]).
-            $cmd->add($charset);
-        } else {
-            $cmd = $this->_command(
-                empty($options['sequence']) ? 'UID SEARCH' : 'SEARCH'
-            );
-            $esearch = false;
-            $results = array();
-
-            // Check if the server supports ESEARCH (RFC 4731).
-            if ($this->queryCapability('ESEARCH')) {
-                foreach ($options['results'] as $val) {
-                    if (isset($results_criteria[$val])) {
-                        $results[] = $results_criteria[$val];
-                    }
-                }
-                $esearch = true;
-            }
-
-            // Add PARTIAL limiting (RFC 5267 [4.4]).
-            if ((!$esearch || !empty($options['partial'])) &&
-                ($cap = $this->queryCapability('CONTEXT')) &&
-                in_array('SEARCH', $cap)) {
-                /* RFC 5267 indicates RFC 4466 ESEARCH support,
-                 * notwithstanding RFC 4731 support. */
-                $esearch = true;
-
-                if (!empty($options['partial'])) {
-                    // Can't have both ALL and PARTIAL returns.
-                    $results = array_diff($results, array('ALL'));
-
-                    $results[] = 'PARTIAL';
-                    $results[] = strval($this->getIdsOb($options['partial']));
-                }
-            }
-
-            if ($esearch && empty($this->_init['noesearch'])) {
-                // Always use ESEARCH if available because it returns results
-                // in a more compact sequence-set list
-                $cmd->add(array(
-                    'RETURN',
-                    new Horde_Imap_Client_Data_Format_List($results)
-                ));
-            }
-
-            // Charset is optional for SEARCH (RFC 3501 [6.4.4]).
-            if ($charset != 'US-ASCII') {
-                $cmd->add(array(
-                    'CHARSET',
-                    $options['_query']['charset']
-                ));
-            }
-        }
-
-        $cmd->add($options['_query']['query'], true);
-
-        $pipeline = $this->_pipeline($cmd);
-        $pipeline->data['esearchresp'] = array();
-        $er = &$pipeline->data['esearchresp'];
-        $pipeline->data['searchresp'] = $this->getIdsOb(array(), !empty($options['sequence']));
-        $sr = &$pipeline->data['searchresp'];
-
-        try {
-            $resp = $this->_sendCmd($pipeline);
-        } catch (Horde_Imap_Client_Exception $e) {
-            if (($e instanceof Horde_Imap_Client_Exception_ServerResponse) &&
-                ($e->status == Horde_Imap_Client_Interaction_Server::NO) &&
-                ($charset != 'US-ASCII')) {
-                /* RFC 3501 [6.4.4]: BADCHARSET response code is only a
-                 * SHOULD return. If it doesn't exist, need to check for
-                 * command status of 'NO'. List of supported charsets in
-                 * the BADCHARSET response has already been parsed and stored
-                 * at this point. */
-                $s_charset = $this->_init['s_charset'];
-                $s_charset[$charset] = false;
-                $this->_setInit('s_charset', $s_charset);
-                $e->setCode(Horde_Imap_Client_Exception::BADCHARSET);
-            }
-
-            if (empty($this->_temp['search_retry'])) {
-                $this->_temp['search_retry'] = true;
-
-                /* Bug #9842: Workaround broken Cyrus servers (as of
-                 * 2.4.7). */
-                if ($esearch && ($charset != 'US-ASCII')) {
-                    $this->_unsetCapability('ESEARCH');
-                    $this->_setInit('noesearch', true);
-
-                    try {
-                        return $this->_search($query, $options);
-                    } catch (Horde_Imap_Client_Exception $e) {}
-                }
-
-                /* Try to convert charset. */
-                if (($e->getCode() == Horde_Imap_Client_Exception::BADCHARSET) &&
-                    ($charset != 'US-ASCII')) {
-                    foreach (array_merge(array_keys(array_filter($this->_init['s_charset'])), array('US-ASCII')) as $val) {
-                        $this->_temp['search_retry'] = 1;
-                        $new_query = clone($query);
-                        try {
-                            $new_query->charset($val);
-                            $options['_query'] = $new_query->build($this->capability());
-                            return $this->_search($new_query, $options);
-                        } catch (Horde_Imap_Client_Exception $e) {}
-                    }
-                }
-
-                unset($this->_temp['search_retry']);
-            }
-
-            throw $e;
-        }
-
-        if ($return_sort && !$server_sort) {
-            if ($server_seq_sort) {
-                $sr->sort();
-                if (reset($options['sort']) == Horde_Imap_Client::SORT_REVERSE) {
-                    $sr->reverse();
-                }
-            } else {
-                if (!isset($this->_temp['clientsort'])) {
-                    $this->_temp['clientsort'] = new Horde_Imap_Client_Socket_ClientSort($this);
-                }
-                $sr = $this->getIdsOb($this->_temp['clientsort']->clientSort($sr, $options), !empty($options['sequence']));
-            }
-        }
-
-        $ret = array();
-        foreach ($options['results'] as $val) {
-            switch ($val) {
-            case Horde_Imap_Client::SEARCH_RESULTS_COUNT:
-                $ret['count'] = $esearch ? $er['count'] : count($sr);
-                break;
-
-            case Horde_Imap_Client::SEARCH_RESULTS_MATCH:
-                $ret['match'] = $sr;
-                break;
-
-            case Horde_Imap_Client::SEARCH_RESULTS_MAX:
-                $ret['max'] = $esearch
-                    ? (isset($er['max']) ? $er['max'] : null)
-                    : (count($sr) ? max($sr->ids) : null);
-                break;
-
-            case Horde_Imap_Client::SEARCH_RESULTS_MIN:
-                $ret['min'] = $esearch
-                    ? (isset($er['min']) ? $er['min'] : null)
-                    : (count($sr) ? min($sr->ids) : null);
-                break;
-
-            case Horde_Imap_Client::SEARCH_RESULTS_RELEVANCY:
-                $ret['relevancy'] = ($esearch && isset($er['relevancy'])) ? $er['relevancy'] : array();
-                break;
-
-            case Horde_Imap_Client::SEARCH_RESULTS_SAVE:
-                $this->_temp['search_save'] = $ret['save'] = $esearch ? empty($resp->data['searchnotsaved']) : false;
-                break;
-            }
-        }
-
-        // Add modseq data, if needed.
-        if (!empty($er['modseq'])) {
-            $ret['modseq'] = $er['modseq'];
-        }
-
-        unset($this->_temp['search_retry']);
-
-        /* Check for EXPUNGEISSUED (RFC 2180 [4.3]/RFC 5530 [3]). */
-        if (!empty($resp->data['expungeissued'])) {
-            $this->noop();
-        }
-
-        return $ret;
-    }
-
-    /**
-     * Parse a SEARCH/SORT response (RFC 3501 [7.2.5]; RFC 4466 [3];
-     * RFC 5256 [4]; RFC 5267 [3]).
-     *
-     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
-     *                                                          object.
-     * @param array $data  A list of IDs (message sequence numbers or UIDs).
-     */
-    protected function _parseSearch(
-        Horde_Imap_Client_Interaction_Pipeline $pipeline,
-        $data
-    )
-    {
-        /* More than one search response may be sent. */
-        $pipeline->data['searchresp']->add($data);
-    }
-
-    /**
-     * Parse an ESEARCH response (RFC 4466 [2.6.2])
-     * Format: (TAG "a567") UID COUNT 5 ALL 4:19,21,28
-     *
-     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
-     *                                                          object.
-     * @param Horde_Imap_Client_Tokenize $data  The server response.
-     */
-    protected function _parseEsearch(
-        Horde_Imap_Client_Interaction_Pipeline $pipeline,
-        Horde_Imap_Client_Tokenize $data
-    )
-    {
-        // Ignore search correlator information
-        if ($data->next() === true) {
-            $data->flushIterator(false);
-        }
-
-        // Ignore UID tag
-        $current = $data->next();
-        if (strtoupper($current) == 'UID') {
-            $current = $data->next();
-        }
-
-        do {
-            $val = $data->next();
-            $tag = strtoupper($current);
-
-            switch ($tag) {
-            case 'ALL':
-                $this->_parseSearch($pipeline, $val);
-                break;
-
-            case 'COUNT':
-            case 'MAX':
-            case 'MIN':
-            case 'MODSEQ':
-            case 'RELEVANCY':
-                $pipeline->data['esearchresp'][strtolower($tag)] = $val;
-                break;
-
-            case 'PARTIAL':
-                // RFC 5267 [4.4]
-                $partial = $val->flushIterator();
-                $this->_parseSearch($pipeline, end($partial));
-                break;
-            }
-        } while (($current = $data->next()) !== false);
-    }
-
-    /**
-     */
-    protected function _setComparator($comparator)
-    {
-        $cmd = $this->_command('COMPARATOR');
-        foreach ($comparator as $val) {
-            $cmd->add(new Horde_Imap_Client_Data_Format_Astring($val));
-        }
-        $this->_sendCmd($cmd);
-    }
-
-    /**
-     */
-    protected function _getComparator()
-    {
-        $resp = $this->_sendCmd($this->_command('COMPARATOR'));
-
-        return isset($resp->data['comparator'])
-            ? $resp->data['comparator']
-            : null;
-    }
-
-    /**
-     * Parse a COMPARATOR response (RFC 5255 [4.8])
-     *
-     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
-     *                                                          object.
-     * @param Horde_Imap_Client_Tokenize $data  The server response.
-     */
-    protected function _parseComparator(
-        Horde_Imap_Client_Interaction_Pipeline $pipeline,
-        $data
-    )
-    {
-        $pipeline->data['comparator'] = $data->next();
-        // Ignore optional matching comparator list
-    }
-
-    /**
-     * @throws Horde_Imap_Client_Exception_NoSupportExtension
-     */
-    protected function _thread($options)
-    {
-        $thread_criteria = array(
-            Horde_Imap_Client::THREAD_ORDEREDSUBJECT => 'ORDEREDSUBJECT',
-            Horde_Imap_Client::THREAD_REFERENCES => 'REFERENCES',
-            Horde_Imap_Client::THREAD_REFS => 'REFS'
-        );
-
-        $tsort = (isset($options['criteria']))
-            ? (is_string($options['criteria']) ? strtoupper($options['criteria']) : $thread_criteria[$options['criteria']])
-            : 'ORDEREDSUBJECT';
-
-        $cap = $this->queryCapability('THREAD');
-        if (!$cap || !in_array($tsort, $cap)) {
-            switch ($tsort) {
-            case 'ORDEREDSUBJECT':
-                if (empty($options['search'])) {
-                    $ids = $this->getIdsOb(Horde_Imap_Client_Ids::ALL, !empty($options['sequence']));
-                } else {
-                    $search_res = $this->search($this->_selected, $options['search'], array('sequence' => !empty($options['sequence'])));
-                    $ids = $search_res['match'];
-                }
-
-                /* Do client-side ORDEREDSUBJECT threading. */
-                $query = new Horde_Imap_Client_Fetch_Query();
-                $query->envelope();
-                $query->imapDate();
-
-                $fetch_res = $this->fetch($this->_selected, $query, array(
-                    'ids' => $ids
-                ));
-
-                if (!isset($this->_temp['clientsort'])) {
-                    $this->_temp['clientsort'] = new Horde_Imap_Client_Socket_ClientSort($this);
-                }
-                return $this->_temp['clientsort']->threadOrderedSubject($fetch_res, empty($options['sequence']));
-
-            case 'REFERENCES':
-            case 'REFS':
-                throw new Horde_Imap_Client_Exception_NoSupportExtension(
-                    'THREAD',
-                    sprintf('Server does not support "%s" thread sort.', $tsort)
-                );
-            }
-        }
-
-        $cmd = $this->_command(
-            empty($options['sequence']) ? 'UID THREAD' : 'THREAD'
-        )->add($tsort);
-
-        if (empty($options['search'])) {
-            $cmd->add(array(
-                'US-ASCII',
-                'ALL'
-            ));
-        } else {
-            $search_query = $options['search']->build();
-            $cmd->add(is_null($search_query['charset']) ? 'US-ASCII' : $search_query['charset']);
-            $cmd->add($search_query['query'], true);
-        }
-
-        return new Horde_Imap_Client_Data_Thread(
-            $this->_sendCmd($cmd)->data['threadparse'],
-            empty($options['sequence']) ? 'uid' : 'sequence'
-        );
-    }
-
-    /**
-     * Parse a THREAD response (RFC 5256 [4]).
-     *
-     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
-     *                                                          object.
-     * @param Horde_Imap_Client_Tokenize $data  Thread data.
-     */
-    protected function _parseThread(
-        Horde_Imap_Client_Interaction_Pipeline $pipeline,
-        Horde_Imap_Client_Tokenize $data
-    )
-    {
-        $out = array();
-
-        while ($data->next() !== false) {
-            $thread = array();
-            $this->_parseThreadLevel($thread, $data);
-            $out[] = $thread;
-        }
-
-        $pipeline->data['threadparse'] = $out;
-    }
-
-    /**
-     * Parse a level of a THREAD response (RFC 5256 [4]).
-     *
-     * @param array $thread                     Results.
-     * @param Horde_Imap_Client_Tokenize $data  Thread data.
-     * @param integer $level                    The current tree level.
-     */
-    protected function _parseThreadLevel(&$thread,
-                                         Horde_Imap_Client_Tokenize $data,
-                                         $level = 0)
-    {
-        while (($curr = $data->next()) !== false) {
-            if ($curr === true) {
-                $this->_parseThreadLevel($thread, $data, $level);
-            } elseif (!is_bool($curr)) {
-                $thread[$curr] = $level++;
-            }
-        }
-    }
-
-    /**
-     */
-    protected function _fetch(Horde_Imap_Client_Fetch_Results $results,
-                              $queries)
-    {
-        $pipeline = $this->_pipeline();
-        $pipeline->data['fetch_lookup'] = array();
-
-        foreach ($queries as $options) {
-            $this->_fetchCmd($pipeline, $options);
-            $sequence = $options['ids']->sequence;
-        }
-
-        try {
-            $resp = $this->_sendCmd($pipeline);
-
-            /* Check for EXPUNGEISSUED (RFC 2180 [4.1]/RFC 5530 [3]). */
-            if (!empty($resp->data['expungeissued'])) {
-                $this->noop();
-            }
-        } catch (Horde_Imap_Client_Exception_ServerResponse $e) {
-            // A NO response, when coupled with a sequence FETCH, most
-            // likely means that messages were expunged. RFC 2180 [4.1]
-            if ($sequence &&
-                ($e->status == Horde_Imap_Client_Interaction_Server::NO)) {
-                $this->noop();
-            }
-        } catch (Exception $e) {
-            // For any other error, ignore the Exception - fetch() is nice in
-            // that the return value explicitly handles missing data for any
-            // given message.
-        }
-
-        foreach ($resp->fetch as $k => $v) {
-            $results->get($sequence ? $k : $v->getUid())->merge($v);
-        }
-    }
-
-    /**
-     * Add a FETCH command to the given pipeline.
-     *
-     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
-     *                                                          object.
-     * @param array $options                                    Fetch query
-     *                                                          options
-     */
-    protected function _fetchCmd(
-        Horde_Imap_Client_Interaction_Pipeline $pipeline,
-        $options
-    )
-    {
-        $fetch = new Horde_Imap_Client_Data_Format_List();
-        $sequence = $options['ids']->sequence;
-
-        /* Build an IMAP4rev1 compliant FETCH query. We handle the following
-         * criteria:
-         *   BINARY[.PEEK][<section #>]<<partial>> (RFC 3516)
-         *     see BODY[] response
-         *   BINARY.SIZE[<section #>] (RFC 3516)
-         *   BODY[.PEEK][<section>]<<partial>>
-         *     <section> = HEADER, HEADER.FIELDS, HEADER.FIELDS.NOT, MIME,
-         *                 TEXT, empty
-         *     <<partial>> = 0.# (# of bytes)
-         *   BODYSTRUCTURE
-         *   ENVELOPE
-         *   FLAGS
-         *   INTERNALDATE
-         *   MODSEQ (RFC 4551)
-         *   RFC822.SIZE
-         *   UID
-         *
-         * No need to support these (can be built from other queries):
-         * ===========================================================
-         *   ALL macro => (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE)
-         *   BODY => Use BODYSTRUCTURE instead
-         *   FAST macro => (FLAGS INTERNALDATE RFC822.SIZE)
-         *   FULL macro => (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY)
-         *   RFC822 => BODY[]
-         *   RFC822.HEADER => BODY[HEADER]
-         *   RFC822.TEXT => BODY[TEXT]
-         */
-
-        foreach ($options['_query'] as $type => $c_val) {
-            switch ($type) {
-            case Horde_Imap_Client::FETCH_STRUCTURE:
-                $fetch->add('BODYSTRUCTURE');
-                break;
-
-            case Horde_Imap_Client::FETCH_FULLMSG:
-                if (empty($c_val['peek'])) {
-                    $this->openMailbox($this->_selected, Horde_Imap_Client::OPEN_READWRITE);
-                }
-                $fetch->add(
-                    'BODY' .
-                    (!empty($c_val['peek']) ? '.PEEK' : '') .
-                    '[]' .
-                    $this->_partialAtom($c_val)
-                );
-                break;
-
-            case Horde_Imap_Client::FETCH_HEADERTEXT:
-            case Horde_Imap_Client::FETCH_BODYTEXT:
-            case Horde_Imap_Client::FETCH_MIMEHEADER:
-            case Horde_Imap_Client::FETCH_BODYPART:
-            case Horde_Imap_Client::FETCH_HEADERS:
-                foreach ($c_val as $key => $val) {
-                    $cmd = ($key == 0)
-                        ? ''
-                        : $key . '.';
-                    $main_cmd = 'BODY';
-
-                    switch ($type) {
-                    case Horde_Imap_Client::FETCH_HEADERTEXT:
-                        $cmd .= 'HEADER';
-                        break;
-
-                    case Horde_Imap_Client::FETCH_BODYTEXT:
-                        $cmd .= 'TEXT';
-                        break;
-
-                    case Horde_Imap_Client::FETCH_MIMEHEADER:
-                        $cmd .= 'MIME';
-                        break;
-
-                    case Horde_Imap_Client::FETCH_BODYPART:
-                        // Remove the last dot from the string.
-                        $cmd = substr($cmd, 0, -1);
-
-                        if (!empty($val['decode']) &&
-                            $this->queryCapability('BINARY')) {
-                            $main_cmd = 'BINARY';
-                        }
-                        break;
-
-                    case Horde_Imap_Client::FETCH_HEADERS:
-                        $cmd .= 'HEADER.FIELDS';
-                        if (!empty($val['notsearch'])) {
-                            $cmd .= '.NOT';
-                        }
-                        $cmd .= ' (' . implode(' ', array_map('strtoupper', $val['headers'])) . ')';
-
-                        // Maintain a command -> label lookup so we can put
-                        // the results in the proper location.
-                        $pipeline->data['fetch_lookup'][$cmd] = $key;
-                    }
-
-                    if (empty($val['peek'])) {
-                        $this->openMailbox($this->_selected, Horde_Imap_Client::OPEN_READWRITE);
-                    }
-
-                    $fetch->add(
-                        $main_cmd .
-                        (!empty($val['peek']) ? '.PEEK' : '') .
-                        '[' . $cmd . ']' .
-                        $this->_partialAtom($val)
-                    );
-                }
-                break;
-
-            case Horde_Imap_Client::FETCH_BODYPARTSIZE:
-                if ($this->queryCapability('BINARY')) {
-                    foreach ($c_val as $val) {
-                        $fetch->add('BINARY.SIZE[' . $key . ']');
-                    }
-                }
-                break;
-
-            case Horde_Imap_Client::FETCH_ENVELOPE:
-                $fetch->add('ENVELOPE');
-                break;
-
-            case Horde_Imap_Client::FETCH_FLAGS:
-                $fetch->add('FLAGS');
-                break;
-
-            case Horde_Imap_Client::FETCH_IMAPDATE:
-                $fetch->add('INTERNALDATE');
-                break;
-
-            case Horde_Imap_Client::FETCH_SIZE:
-                $fetch->add('RFC822.SIZE');
-                break;
-
-            case Horde_Imap_Client::FETCH_UID:
-                /* A UID FETCH will always return UID information (RFC 3501
-                 * [6.4.8]). Don't add to query as it just creates a longer
-                 * FETCH command. */
-                if ($sequence || (count($options['_query']) == 1)) {
-                    $fetch->add('UID');
-                }
-                break;
-
-            case Horde_Imap_Client::FETCH_SEQ:
-                // Nothing we need to add to fetch request unless sequence is
-                // the only criteria.
-                if (count($options['_query']) == 1) {
-                    $fetch->add('UID');
-                }
-                break;
-
-            case Horde_Imap_Client::FETCH_MODSEQ:
-                /* The 'changedsince' modifier implicitly adds the MODSEQ
-                 * FETCH item (RFC 4551 [3.3.1]). Don't add to query as it
-                 * just creates a longer FETCH command. */
-                if (empty($options['changedsince'])) {
-                    $fetch->add('MODSEQ');
-                }
-                break;
-            }
-        }
-
-        /* Add changedsince parameters. */
-        if (empty($options['changedsince'])) {
-            $fetch_cmd = $fetch;
-        } else {
-            /* We might just want the list of UIDs changed since a given
-             * modseq. In that case, we don't have any other FETCH attributes,
-             * but RFC 3501 requires at least one specified attribute. */
-            $fetch_cmd = array(
-                count($fetch)
-                    ? $fetch
-                    : new Horde_Imap_Client_Data_Format_List('UID'),
-                new Horde_Imap_Client_Data_Format_List(array(
-                    'CHANGEDSINCE',
-                    new Horde_Imap_Client_Data_Format_Number($options['changedsince'])
-                ))
-            );
-        }
-
-        /* RFC 2683 [3.2.1.5] recommends that lines should be limited to
-         * "approximately 1000 octets". However, servers should allow a
-         * command line of at least "8000 octets". As a compromise, assume
-         * all modern IMAP servers handle ~2000 octets. The FETCH command
-         * should be the only command issued by this library that should ever
-         * approach this limit. For simplification, assume that the UID list
-         * is the limiting factor and split this list at a sequence comma
-         * delimiter if it exceeds 2000 characters. */
-        foreach ($options['ids']->split(2000) as $val) {
-            $cmd = $this->_command(
-                $sequence ? 'FETCH' : 'UID FETCH'
-            )->add(array(
-                $val,
-                $fetch_cmd
-            ));
-            $pipeline->add($cmd);
-        }
-    }
-
-    /**
-     * Add a partial atom to an IMAP command based on the criteria options.
-     *
-     * @param array $opts  Criteria options.
-     *
-     * @return string  The partial atom.
-     */
-    protected function _partialAtom($opts)
-    {
-        if (!empty($opts['length'])) {
-            return '<' . (empty($opts['start']) ? 0 : intval($opts['start'])) . '.' . intval($opts['length']) . '>';
-        }
-
-        return empty($opts['start'])
-            ? ''
-            : ('<' . intval($opts['start']) . '>');
-    }
-
-    /**
-     * Parse a FETCH response (RFC 3501 [7.4.2]). A FETCH response may occur
-     * due to a FETCH command, or due to a change in a message's state (i.e.
-     * the flags change).
-     *
-     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
-     *                                                          object.
-     * @param integer $id                       The message sequence number.
-     * @param Horde_Imap_Client_Tokenize $data  The server response.
-     */
-    protected function _parseFetch(
-        Horde_Imap_Client_Interaction_Pipeline $pipeline,
-        $id,
-        Horde_Imap_Client_Tokenize $data
-    )
-    {
-        if ($data->next() !== true) {
-            return;
-        }
-
-        $ob = $pipeline->fetch->get($id);
-        $ob->setSeq($id);
-
-        $flags = $modseq = $uid = false;
-
-        while (($tag = $data->next()) !== false) {
-            $tag = strtoupper($tag);
-
-            switch ($tag) {
-            case 'BODYSTRUCTURE':
-                $data->next();
-                $structure = $this->_parseBodystructure($data);
-                $structure->buildMimeIds();
-                $ob->setStructure($structure);
-                break;
-
-            case 'ENVELOPE':
-                $data->next();
-                $ob->setEnvelope($this->_parseEnvelope($data));
-                break;
-
-            case 'FLAGS':
-                $data->next();
-                $ob->setFlags($data->flushIterator());
-                $flags = true;
-                break;
-
-            case 'INTERNALDATE':
-                $ob->setImapDate($data->next());
-                break;
-
-            case 'RFC822.SIZE':
-                $ob->setSize($data->next());
-                break;
-
-            case 'UID':
-                $ob->setUid($data->next());
-                $uid = true;
-                break;
-
-            case 'MODSEQ':
-                $data->next();
-                $modseq = $data->next();
-                $data->next();
-
-                /* MODSEQ must be greater than 0, so do sanity checking. */
-                if ($modseq > 0) {
-                    $ob->setModSeq($modseq);
-
-                    /* Store MODSEQ value. It may be used as the highestmodseq
-                     * once a tagged response is received (RFC 5162 [5]). */
-                    $pipeline->data['modseqs'][] = $modseq;
-                }
-                break;
-
-            default:
-                // Catch BODY[*]<#> responses
-                if (strpos($tag, 'BODY[') === 0) {
-                    // Remove the beginning 'BODY['
-                    $tag = substr($tag, 5);
-
-                    // BODY[HEADER.FIELDS] request
-                    if (!empty($pipeline->data['fetch_lookup']) &&
-                        (strpos($tag, 'HEADER.FIELDS') !== false)) {
-                        $data->next();
-                        $sig = $tag . ' (' . implode(' ', array_map('strtoupper', $data->flushIterator())) . ')';
-
-                        // Ignore the trailing bracket
-                        $data->next();
-
-                        $ob->setHeaders($pipeline->data['fetch_lookup'][$sig], $data->next());
-                    } else {
-                        // Remove trailing bracket and octet start info
-                        $tag = substr($tag, 0, strrpos($tag, ']'));
-
-                        if (!strlen($tag)) {
-                            // BODY[] request
-                            if (!is_null($tmp = $data->next())) {
-                                $ob->setFullMsg($tmp);
-                            }
-                        } elseif (is_numeric(substr($tag, -1))) {
-                            // BODY[MIMEID] request
-                            if (!is_null($tmp = $data->next())) {
-                                $ob->setBodyPart($tag, $tmp);
-                            }
-                        } else {
-                            // BODY[HEADER|TEXT|MIME] request
-                            if (($last_dot = strrpos($tag, '.')) === false) {
-                                $mime_id = 0;
-                            } else {
-                                $mime_id = substr($tag, 0, $last_dot);
-                                $tag = substr($tag, $last_dot + 1);
-                            }
-
-                            if (!is_null($tmp = $data->next())) {
-                                switch ($tag) {
-                                case 'HEADER':
-                                    $ob->setHeaderText($mime_id, $tmp);
-                                    break;
-
-                                case 'TEXT':
-                                    $ob->setBodyText($mime_id, $tmp);
-                                    break;
-
-                                case 'MIME':
-                                    $ob->setMimeHeader($mime_id, $tmp);
-                                    break;
-                                }
-                            }
-                        }
-                    }
-                } elseif (strpos($tag, 'BINARY[') === 0) {
-                    // Catch BINARY[*]<#> responses
-                    // Remove the beginning 'BINARY[' and the trailing bracket
-                    // and octet start info
-                    $tag = substr($tag, 7, strrpos($tag, ']') - 7);
-                    $ob->setBodyPart($tag, $data->next(), empty($this->_temp['literal8']) ? '8bit' : 'binary');
-                } elseif (strpos($tag, 'BINARY.SIZE[') === 0) {
-                    // Catch BINARY.SIZE[*] responses
-                    // Remove the beginning 'BINARY.SIZE[' and the trailing
-                    // bracket and octet start info
-                    $tag = substr($tag, 12, strrpos($tag, ']') - 12);
-                    $ob->setBodyPartSize($tag, $data->next());
-                }
-                break;
-            }
-        }
-
-        /* MODSEQ issue: Oh joy. Per RFC 5162 (see Errata #1807), FETCH FLAGS
-         * responses are NOT required to provide UID information, even if
-         * QRESYNC is explicitly enabled. Caveat: the FLAGS information
-         * returned during a SELECT/EXAMINE MUST contain UIDs so we are OK
-         * there.
-         * The good news: all decent IMAP servers (Cyrus, Dovecot) will always
-         * provide UID information, so this is not normally an issue.
-         * The bad news: spec-wise, this behavior cannot be 100% guaranteed.
-         * Compromise: We will watch for a FLAGS response with a MODSEQ and
-         * check if a UID exists also. If not, put the sequence number in a
-         * queue - it is possible the UID information may appear later in an
-         * untagged response. When the command is over, double check to make
-         * sure there are none of these MODSEQ/FLAGS that are still UID-less.
-         * In the (rare) event that there is, don't cache anything and
-         * immediately close the mailbox: flags will be correctly sync'd next
-         * mailbox open so we only lose a bit of caching efficiency.
-         * Otherwise, we could end up with an inconsistent cached state. */
-        if ($flags && $modseq && !$uid) {
-            $pipeline->data['modseqs_nouid'][] = $id;
-        }
-    }
-
-    /**
-     * Recursively parse BODYSTRUCTURE data from a FETCH return (see
-     * RFC 3501 [7.4.2]).
-     *
-     * @param Horde_Imap_Client_Tokenize $data  Data returned from the server.
-     *
-     * @return Horde_Mime_Part  Mime part object.
-     */
-    protected function _parseBodystructure(Horde_Imap_Client_Tokenize $data)
-    {
-        $ob = new Horde_Mime_Part();
-
-        // If index 0 is an array, this is a multipart part.
-        if (($entry = $data->next()) === true) {
-            do {
-                $ob->addPart($this->_parseBodystructure($data));
-            } while (($entry = $data->next()) === true);
-
-            // The subpart type.
-            $ob->setType('multipart/' . $entry);
-
-            // After the subtype is further extension information. This
-            // information MAY appear for BODYSTRUCTURE requests.
-
-            // This is parameter information.
-            if (($tmp = $data->next()) === false) {
-                return $ob;
-            } elseif ($tmp === true) {
-                foreach ($this->_parseStructureParams($data, 'content-type') as $key => $val) {
-                    $ob->setContentTypeParameter($key, $val);
-                }
-            }
-        } else {
-            $ob->setType($entry . '/' . $data->next());
-
-            if ($data->next() === true) {
-                foreach ($this->_parseStructureParams($data, 'content-type') as $key => $val) {
-                    $ob->setContentTypeParameter($key, $val);
-                }
-            }
-
-            if (!is_null($tmp = $data->next())) {
-                $ob->setContentId($tmp);
-            }
-
-            if (!is_null($tmp = $data->next())) {
-                $ob->setDescription(Horde_Mime::decode($tmp));
-            }
-
-            if (!is_null($tmp = $data->next())) {
-                $ob->setTransferEncoding($tmp);
-            }
-
-            $ob->setBytes($data->next());
-
-            // If the type is 'message/rfc822' or 'text/*', several extra
-            // fields are included
-            switch ($ob->getPrimaryType()) {
-            case 'message':
-                if ($ob->getSubType() == 'rfc822') {
-                    if ($data->next() === true) {
-                        // Ignore: envelope
-                        $data->flushIterator(false);
-                    }
-                    if ($data->next() === true) {
-                        $ob->addPart($this->_parseBodystructure($data));
-                    }
-                    $data->next(); // Ignore: lines
-                }
-                break;
-
-            case 'text':
-                $data->next(); // Ignore: lines
-                break;
-            }
-
-            // After the subtype is further extension information. This
-            // information MAY appear for BODYSTRUCTURE requests.
-
-            // Ignore: MD5
-            if ($data->next() === false) {
-                return $ob;
-            }
-        }
-
-        // This is disposition information
-        if (($tmp = $data->next()) === false) {
-            return $ob;
-        } elseif ($tmp === true) {
-            $ob->setDisposition($data->next());
-
-            if ($data->next() === true) {
-                foreach ($this->_parseStructureParams($data, 'content-disposition') as $key => $val) {
-                    $ob->setDispositionParameter($key, $val);
-                }
-            }
-            $data->next();
-        }
-
-        // This is language information. It is either a single value or a list
-        // of values.
-        if (($tmp = $data->next()) === false) {
-            return $ob;
-        } elseif (!is_null($tmp)) {
-            $ob->setLanguage(($tmp === true) ? $data->flushIterator() : $tmp);
-        }
-
-        // Ignore location (RFC 2557) and consume closing paren.
-        $data->flushIterator(false);
-
-        return $ob;
-    }
-
-    /**
-     * Helper function to parse a parameters-like tokenized array.
-     *
-     * @param mixed $data   Message data. Either a Horde_Imap_Client_Tokenize
-     *                      object or null.
-     * @param string $type  The header name.
-     *
-     * @return array  The parameter array.
-     */
-    protected function _parseStructureParams($data, $type)
-    {
-        $params = array();
-
-        if (is_null($data)) {
-            return $params;
-        }
-
-        while (($name = $data->next()) !== false) {
-            $params[strtolower($name)] = $data->next();
-        }
-
-        $ret = Horde_Mime::decodeParam($type, $params);
-
-        return $ret['params'];
-    }
-
-    /**
-     * Parse ENVELOPE data from a FETCH return (see RFC 3501 [7.4.2]).
-     *
-     * @param Horde_Imap_Client_Tokenize $data  Data returned from the server.
-     *
-     * @return Horde_Imap_Client_Data_Envelope  An envelope object.
-     */
-    protected function _parseEnvelope(Horde_Imap_Client_Tokenize $data)
-    {
-        // 'route', the 2nd element, is deprecated by RFC 2822.
-        $addr_structure = array(
-            0 => 'personal',
-            2 => 'mailbox',
-            3 => 'host'
-        );
-        $env_data = array(
-            0 => 'date',
-            1 => 'subject',
-            2 => 'from',
-            3 => 'sender',
-            4 => 'reply_to',
-            5 => 'to',
-            6 => 'cc',
-            7 => 'bcc',
-            8 => 'in_reply_to',
-            9 => 'message_id'
-        );
-
-        $addr_ob = new Horde_Mail_Rfc822_Address();
-        $env_addrs = $this->getParam('envelope_addrs');
-        $env_str = $this->getParam('envelope_string');
-        $key = 0;
-        $ret = new Horde_Imap_Client_Data_Envelope();
-
-        while (($val = $data->next()) !== false) {
-            if (!isset($env_data[$key]) || is_null($val)) {
-                ++$key;
-                continue;
-            }
-
-            if (is_string($val)) {
-                // These entries are text fields.
-                $ret->$env_data[$key] = substr($val, 0, $env_str);
-            } else {
-                // These entries are address structures.
-                $group = null;
-                $key2 = 0;
-                $tmp = new Horde_Mail_Rfc822_List();
-
-                while ($data->next() !== false) {
-                    $a_val = $data->flushIterator();
-
-                    // RFC 3501 [7.4.2]: Group entry when host is NIL.
-                    // Group end when mailbox is NIL; otherwise, this is
-                    // mailbox name.
-                    if (is_null($a_val[3])) {
-                        if (is_null($a_val[2])) {
-                            $group = null;
-                        } else {
-                            $group = new Horde_Mail_Rfc822_Group($a_val[2]);
-                            $tmp->add($group);
-                        }
-                    } else {
-                        $addr = clone $addr_ob;
-
-                        foreach ($addr_structure as $add_key => $add_val) {
-                            if (!is_null($a_val[$add_key])) {
-                                $addr->$add_val = $a_val[$add_key];
-                            }
-                        }
-
-                        if ($group) {
-                            $group->addresses->add($addr);
-                        } else {
-                            $tmp->add($addr);
-                        }
-                    }
-
-                    if (++$key2 >= $env_addrs) {
-                        $data->flushIterator(false);
-                        break;
-                    }
-                }
-
-                $ret->$env_data[$key] = $tmp;
-            }
-
-            ++$key;
-        }
-
-        return $ret;
-    }
-
-    /**
-     */
-    protected function _vanished($modseq, Horde_Imap_Client_Ids $ids)
-    {
-        $pipeline = $this->_pipeline(
-            $this->_command('UID FETCH')->add(array(
-                strval($ids),
-                'UID',
-                new Horde_Imap_Client_Data_Format_List(array(
-                    'VANISHED',
-                    'CHANGEDSINCE',
-                    new Horde_Imap_Client_Data_Format_Number($modseq)
-                ))
-            ))
-        );
-        $pipeline->data['vanished'] = $this->getIdsOb();
-
-        return $this->_sendCmd($pipeline)->data['vanished'];
-    }
-
-    /**
-     */
-    protected function _store($options)
-    {
-        $pipeline = $this->_storeCmd($options);
-        $pipeline->data['modified'] = $this->getIdsOb();
-
-        try {
-            $resp = $this->_sendCmd($pipeline);
-
-            /* Check for EXPUNGEISSUED (RFC 2180 [4.2]/RFC 5530 [3]). */
-            if (!empty($resp->data['expungeissued'])) {
-                $this->noop();
-            }
-
-            return $resp->data['modified'];
-        } catch (Horde_Imap_Client_Exception_ServerResponse $e) {
-            /* A NO response, when coupled with a sequence STORE and
-             * non-SILENT behavior, most likely means that messages were
-             * expunged. RFC 2180 [4.2] */
-            if (empty($pipeline->data['store_silent']) &&
-                !empty($options['sequence']) &&
-                ($e->status == Horde_Imap_Client_Interaction_Server::NO)) {
-                $this->noop();
-            }
-
-            return $pipeline->data['modified'];
-        }
-    }
-
-    /**
-     * Create a store command.
-     *
-     * @param array $options  See Horde_Imap_Client_Base#_store().
-     *
-     * @return Horde_Imap_Client_Interaction_Pipeline  Pipeline object.
-     */
-    protected function _storeCmd($options)
-    {
-        $cmds = array();
-        $silent = empty($options['unchangedsince'])
-             ? !($this->_debug->debug || $this->_initCache(true))
-             : false;
-
-        if (!empty($options['replace'])) {
-            $cmds[] = array(
-                'FLAGS' . ($silent ? '.SILENT' : ''),
-                $options['replace']
-            );
-        } else {
-            foreach (array('add' => '+', 'remove' => '-') as $k => $v) {
-                if (!empty($options[$k])) {
-                    $cmds[] = array(
-                        $v . 'FLAGS' . ($silent ? '.SILENT' : ''),
-                        $options[$k]
-                    );
-                }
-            }
-        }
-
-        $pipeline = $this->_pipeline();
-        $pipeline->data['store_silent'] = $silent;
-
-        foreach ($cmds as $val) {
-            $cmd = $this->_command(
-                empty($options['sequence']) ? 'UID STORE' : 'STORE'
-            )->add(strval($options['ids']));
-            if (!empty($options['unchangedsince'])) {
-                $cmd->add(new Horde_Imap_Client_Data_Format_List(array(
-                    'UNCHANGEDSINCE',
-                    new Horde_Imap_Client_Data_Format_Number(intval($options['unchangedsince']))
-                )));
-            }
-            $cmd->add($val);
-
-            $pipeline->add($cmd);
-        }
-
-        return $pipeline;
-    }
-
-    /**
-     */
-    protected function _copy(Horde_Imap_Client_Mailbox $dest, $options)
-    {
-        /* Check for MOVE command (RFC 6851). */
-        $move_cmd = (!empty($options['move']) &&
-                     $this->queryCapability('MOVE'));
-
-        $cmd = $this->_pipeline(
-            $this->_command(
-                ($options['ids']->sequence ? '' : 'UID ') . ($move_cmd ? 'MOVE' : 'COPY')
-            )->add(array(
-                strval($options['ids']),
-                new Horde_Imap_Client_Data_Format_Mailbox($dest)
-            ))
-        );
-        $cmd->data['copydest'] = $dest;
-
-        // COPY returns no untagged information (RFC 3501 [6.4.7])
-        try {
-            $resp = $this->_sendCmd($cmd);
-        } catch (Horde_Imap_Client_Exception $e) {
-            if (!empty($options['create']) &&
-                !empty($e->resp_data['trycreate'])) {
-                $this->createMailbox($dest);
-                unset($options['create']);
-                return $this->_copy($dest, $options);
-            }
-            throw $e;
-        }
-
-        // If moving, delete the old messages now. Short-circuit if nothing
-        // was moved.
-        if (!$move_cmd &&
-            !empty($options['move']) &&
-            (isset($resp->data['copyuid']) ||
-             !$this->queryCapability('UIDPLUS'))) {
-            $this->expunge($this->_selected, array(
-                'delete' => true,
-                'ids' => $options['ids']
-            ));
-        }
-
-        return isset($resp->data['copyuid'])
-            ? $resp->data['copyuid']
-            : true;
-    }
-
-    /**
-     */
-    protected function _setQuota(Horde_Imap_Client_Mailbox $root, $resources)
-    {
-        $limits = new Horde_Imap_Client_Data_Format_List();
-
-        foreach ($resources as $key => $val) {
-            $limits->add(array(
-                strtoupper($key),
-                new Horde_Imap_Client_Data_Format_Number($val)
-            ));
-        }
-
-        $this->_sendCmd(
-            $this->_command('SETQUOTA')->add(array(
-                new Horde_Imap_Client_Data_Format_Mailbox($root),
-                $limits
-            ))
-        );
-    }
-
-    /**
-     */
-    protected function _getQuota(Horde_Imap_Client_Mailbox $root)
-    {
-        $pipeline = $this->_pipeline(
-            $this->_command('GETQUOTA')->add(
-                new Horde_Imap_Client_Data_Format_Mailbox($root)
-            )
-        );
-        $pipeline->data['quotaresp'] = array();
-
-        return reset($this->_sendCmd($pipeline)->data['quotaresp']);
-    }
-
-    /**
-     * Parse a QUOTA response (RFC 2087 [5.1]).
-     *
-     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
-     *                                                          object.
-     * @param Horde_Imap_Client_Tokenize $data  The server response.
-     */
-    protected function _parseQuota(
-        Horde_Imap_Client_Interaction_Pipeline $pipeline,
-        Horde_Imap_Client_Tokenize $data
-    )
-    {
-        $c = &$pipeline->data['quotaresp'];
-
-        $root = $data->next();
-        $c[$root] = array();
-
-        $data->next();
-
-        while (($curr = $data->next()) !== false) {
-            $c[$root][strtolower($curr)] = array(
-                'usage' => $data->next(),
-                'limit' => $data->next()
-            );
-        }
-    }
-
-    /**
-     */
-    protected function _getQuotaRoot(Horde_Imap_Client_Mailbox $mailbox)
-    {
-        $pipeline = $this->_pipeline(
-            $this->_command('GETQUOTAROOT')->add(
-                new Horde_Imap_Client_Data_Format_Mailbox($mailbox)
-            )
-        );
-        $pipeline->data['quotaresp'] = array();
-
-        return $this->_sendCmd($pipeline)->data['quotaresp'];
-    }
-
-    /**
-     */
-    protected function _setACL(Horde_Imap_Client_Mailbox $mailbox, $identifier,
-                               $options)
-    {
-        // SETACL returns no untagged information (RFC 4314 [3.1]).
-        $this->_sendCmd(
-            $this->_command('SETACL')->add(array(
-                new Horde_Imap_Client_Data_Format_Mailbox($mailbox),
-                new Horde_Imap_Client_Data_Format_Astring($identifier),
-                new Horde_Imap_Client_Data_Format_Astring($options['rights'])
-            ))
-        );
-    }
-
-    /**
-     */
-    protected function _deleteACL(Horde_Imap_Client_Mailbox $mailbox, $identifier)
-    {
-        // DELETEACL returns no untagged information (RFC 4314 [3.2]).
-        $this->_sendCmd(
-            $this->_command('DELETEACL')->add(array(
-                new Horde_Imap_Client_Data_Format_Mailbox($mailbox),
-                new Horde_Imap_Client_Data_Format_Astring($identifier)
-            ))
-        );
-    }
-
-    /**
-     */
-    protected function _getACL(Horde_Imap_Client_Mailbox $mailbox)
-    {
-        return $this->_sendCmd(
-            $this->_command('GETACL')->add(
-                new Horde_Imap_Client_Data_Format_Mailbox($mailbox)
-            )
-        )->data['getacl'];
-    }
-
-    /**
-     * Parse an ACL response (RFC 4314 [3.6]).
-     *
-     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
-     *                                                          object.
-     * @param Horde_Imap_Client_Tokenize $data  The server response.
-     */
-    protected function _parseACL(
-        Horde_Imap_Client_Interaction_Pipeline $pipeline,
-        Horde_Imap_Client_Tokenize $data
-    )
-    {
-        $acl = array();
-
-        // Ignore mailbox argument -> index 1
-        $data->next();
-
-        while (($curr = $data->next()) !== false) {
-            $acl[$curr] = ($curr[0] == '-')
-                ? new Horde_Imap_Client_Data_AclNegative($data->next())
-                : new Horde_Imap_Client_Data_Acl($data->next());
-        }
-
-        $pipeline->data['getacl'] = $acl;
-    }
-
-    /**
-     */
-    protected function _listACLRights(Horde_Imap_Client_Mailbox $mailbox,
-                                      $identifier)
-    {
-        $resp = $this->_sendCmd(
-            $this->_command('LISTRIGHTS')->add(array(
-                new Horde_Imap_Client_Data_Format_Mailbox($mailbox),
-                new Horde_Imap_Client_Data_Format_Astring($identifier)
-            ))
-        );
-
-        return isset($resp->data['listaclrights'])
-            ? $resp->data['listaclrights']
-            : new Horde_Imap_Client_Data_AclRights();
-    }
-
-    /**
-     * Parse a LISTRIGHTS response (RFC 4314 [3.7]).
-     *
-     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
-     *                                                          object.
-     * @param Horde_Imap_Client_Tokenize $data  The server response.
-     */
-    protected function _parseListRights(
-        Horde_Imap_Client_Interaction_Pipeline $pipeline,
-        Horde_Imap_Client_Tokenize $data
-    )
-    {
-        // Ignore mailbox and identifier arguments
-        $data->next();
-        $data->next();
-
-        $pipeline->data['listaclrights'] = new Horde_Imap_Client_Data_AclRights(
-            str_split($data->next()),
-            $data->flushIterator()
-        );
-    }
-
-    /**
-     */
-    protected function _getMyACLRights(Horde_Imap_Client_Mailbox $mailbox)
-    {
-        $resp = $this->_sendCmd(
-            $this->_command('MYRIGHTS')->add(
-                new Horde_Imap_Client_Data_Format_Mailbox($mailbox)
-            )
-        );
-
-        return isset($resp->data['myrights'])
-            ? $resp->data['myrights']
-            : new Horde_Imap_Client_Data_Acl();
-    }
-
-    /**
-     * Parse a MYRIGHTS response (RFC 4314 [3.8]).
-     *
-     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
-     *                                                          object.
-     * @param Horde_Imap_Client_Tokenize $data  The server response.
-     */
-    protected function _parseMyRights(
-        Horde_Imap_Client_Interaction_Pipeline $pipeline,
-        Horde_Imap_Client_Tokenize $data
-    )
-    {
-        // Ignore 1st token (mailbox name)
-        $data->next();
-
-        $pipeline->data['myrights'] = new Horde_Imap_Client_Data_Acl($data->next());
-    }
-
-    /**
-     */
-    protected function _getMetadata(Horde_Imap_Client_Mailbox $mailbox,
-                                    $entries, $options)
-    {
-        $pipeline = $this->_pipeline();
-        $pipeline->data['metadata'] = array();
-
-        if ($this->queryCapability('METADATA') ||
-            ((strlen($mailbox) == 0) &&
-             $this->queryCapability('METADATA-SERVER'))) {
-            $cmd_options = new Horde_Imap_Client_Data_Format_List();
-
-            if (!empty($options['maxsize'])) {
-                $cmd_options->add(array(
-                    'MAXSIZE',
-                    new Horde_Imap_Client_Data_Format_Number($options['maxsize'])
-                ));
-            }
-            if (!empty($options['depth'])) {
-                $cmd_options->add(array(
-                    'DEPTH',
-                    new Horde_Imap_Client_Data_Format_Number($options['depth'])
-                ));
-            }
-
-            $queries = new Horde_Imap_Client_Data_Format_List();
-            foreach ($entries as $md_entry) {
-                $queries->add(new Horde_Imap_Client_Data_Format_Astring($md_entry));
-            }
-
-            $cmd = $this->_command('GETMETADATA')->add(
-                new Horde_Imap_Client_Data_Format_Mailbox($mailbox)
-            );
-            if (count($cmd_options)) {
-                $cmd->add($cmd_options);
-            }
-            $cmd->add($queries);
-
-            $pipeline->add($cmd);
-        } else {
-            if (!$this->queryCapability('ANNOTATEMORE') &&
-                !$this->queryCapability('ANNOTATEMORE2')) {
-                throw new Horde_Imap_Client_Exception_NoSupportExtension('METADATA');
-            }
-
-            $queries = array();
-            foreach ($entries as $md_entry) {
-                list($entry, $type) = $this->_getAnnotateMoreEntry($md_entry);
-
-                if (!isset($queries[$type])) {
-                    $queries[$type] = new Horde_Imap_Client_Data_Format_List();
-                }
-                $queries[$type]->add(new Horde_Imap_Client_Data_Format_String($entry));
-            }
-
-            foreach ($queries as $key => $val) {
-                // TODO: Honor maxsize and depth options.
-                $pipeline->add(
-                    $this->_command('GETANNOTATION')->add(array(
-                        new Horde_Imap_Client_Data_Format_Mailbox($mailbox),
-                        $val,
-                        new Horde_Imap_Client_Data_Format_String($key)
-                    ))
-                );
-            }
-        }
-
-        return $this->_sendCmd($pipeline)->data['metadata'];
-    }
-
-    /**
-     * Split a name for the METADATA extension into the correct syntax for the
-     * older ANNOTATEMORE version.
-     *
-     * @param string $name  A name for a metadata entry.
-     *
-     * @return array  A list of two elements: The entry name and the value
-     *                type.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    protected function _getAnnotateMoreEntry($name)
-    {
-        if (substr($name, 0, 7) == '/shared') {
-            return array(substr($name, 7), 'value.shared');
-        } else if (substr($name, 0, 8) == '/private') {
-            return array(substr($name, 8), 'value.priv');
-        }
-
-        throw new Horde_Imap_Client_Exception(
-            sprintf(Horde_Imap_Client_Translation::t("Invalid METADATA entry: \"%s\"."), $name),
-            Horde_Imap_Client_Exception::METADATA_INVALID
-        );
-    }
-
-    /**
-     */
-    protected function _setMetadata(Horde_Imap_Client_Mailbox $mailbox, $data)
-    {
-        if ($this->queryCapability('METADATA') ||
-            ((strlen($mailbox) == 0) &&
-             $this->queryCapability('METADATA-SERVER'))) {
-            $data_elts = new Horde_Imap_Client_Data_Format_List();
-
-            foreach ($data as $key => $value) {
-                $data_elts->add(array(
-                    new Horde_Imap_Client_Data_Format_Astring($key),
-                    new Horde_Imap_Client_Data_Format_Nstring($value)
-                ));
-            }
-
-            $cmd = $this->_command('SETMETADATA')->add(array(
-                new Horde_Imap_Client_Data_Format_Mailbox($mailbox),
-                $data_elts
-            ));
-        } else {
-            if (!$this->queryCapability('ANNOTATEMORE') &&
-                !$this->queryCapability('ANNOTATEMORE2')) {
-                throw new Horde_Imap_Client_Exception_NoSupportExtension('METADATA');
-            }
-
-            $cmd = $this->_pipeline();
-
-            foreach ($data as $md_entry => $value) {
-                list($entry, $type) = $this->_getAnnotateMoreEntry($md_entry);
-
-                $cmd->add(
-                    $this->_command('SETANNOTATION')->add(array(
-                        new Horde_Imap_Client_Data_Format_Mailbox($mailbox),
-                        new Horde_Imap_Client_Data_Format_String($entry),
-                        new Horde_Imap_Client_Data_Format_List(array(
-                            new Horde_Imap_Client_Data_Format_String($type),
-                            new Horde_Imap_Client_Data_Format_Nstring($value)
-                        ))
-                    ))
-                );
-            }
-        }
-
-        $this->_sendCmd($cmd);
-    }
-
-    /**
-     * Parse a METADATA response (RFC 5464 [4.4]).
-     *
-     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
-     *                                                          object.
-     * @param Horde_Imap_Client_Tokenize $data  The server response.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    protected function _parseMetadata(
-        Horde_Imap_Client_Interaction_Pipeline $pipeline,
-        Horde_Imap_Client_Tokenize $data
-    )
-    {
-        switch ($data->current()) {
-        case 'ANNOTATION':
-            $mbox = $data->next();
-            $entry = $data->next();
-
-            // Ignore unsolicited responses.
-            if ($data->next() !== true) {
-                break;
-            }
-
-            while (($type = $data->next()) !== false) {
-                switch ($type) {
-                case 'value.priv':
-                    $pipeline->data['metadata'][$mbox]['/private' . $entry] = $data->next();
-                    break;
-
-                case 'value.shared':
-                    $pipeline->data['metadata'][$mbox]['/shared' . $entry] = $data->next();
-                    break;
-
-                default:
-                    throw new Horde_Imap_Client_Exception(
-                        sprintf(Horde_Imap_Client_Translation::t("Invalid METADATA value type \"%s\"."), $type),
-                        Horde_Imap_Client_Exception::METADATA_INVALID
-                    );
-                }
-            }
-            break;
-
-        case 'METADATA':
-            $mbox = $data->next();
-
-            // Ignore unsolicited responses.
-            if ($data->next() !== true) {
-                break;
-            }
-
-            while (($entry = $data->next()) !== false) {
-                $pipeline->data['metadata'][$mbox][$entry] = $data->next();
-            }
-            break;
-        }
-    }
-
-    /* Overriden methods. */
-
-    /**
-     * @param array $opts  Options:
-     *   - decrement: (boolean) If true, decrement the message count.
-     *   - pipeline: (Horde_Imap_Client_Interaction_Pipeline) Pipeline object.
-     */
-    protected function _deleteMsgs(Horde_Imap_Client_Mailbox $mailbox,
-                                   Horde_Imap_Client_Ids $ids,
-                                   array $opts = array())
-    {
-        /* If there are pending FETCH cache writes, we need to write them
-         * before the UID -> sequence number mapping changes. */
-        if (isset($opts['pipeline'])) {
-            $this->_updateCache($opts['pipeline']->fetch);
-        }
-
-        $res = parent::_deleteMsgs($mailbox, $ids);
-
-        if (isset($this->_temp['expunged'])) {
-            $this->_temp['expunged']->add($res);
-        }
-
-        if (!empty($opts['decrement'])) {
-            $mbox_ob = $this->_mailboxOb();
-            $mbox_ob->setStatus(
-                Horde_Imap_Client::STATUS_MESSAGES,
-                $mbox_ob->getStatus(Horde_Imap_Client::STATUS_MESSAGES) - count($ids)
-            );
-        }
-    }
-
-    /* Internal functions. */
-
-    /**
-     * Sends command(s) to the IMAP server. A connection to the server must
-     * have already been made.
-     *
-     * @param mixed $cmd  Either a Command object or a Pipeline object.
-     *
-     * @return Horde_Imap_Client_Interaction_Pipeline  A pipeline object.
-     * @throws Horde_Imap_Client_Exception
-     */
-    protected function _sendCmd($cmd)
-    {
-        $pipeline = ($cmd instanceof Horde_Imap_Client_Interaction_Command)
-            ? $this->_pipeline($cmd)
-            : $cmd;
-
-        if (!empty($this->_cmdQueue)) {
-            /* Add commands in reverse order. */
-            foreach (array_reverse($this->_cmdQueue) as $val) {
-                $pipeline->add($val, true);
-            }
-
-            $this->_cmdQueue = array();
-        }
-
-        $cmd_list = array();
-
-        foreach ($pipeline as $val) {
-            if ($val->continuation) {
-                $this->_sendCmdChunk($pipeline, $cmd_list);
-                $this->_sendCmdChunk($pipeline, array($val));
-                $cmd_list = array();
-            } else {
-                $cmd_list[] = $val;
-            }
-        }
-
-        $this->_sendCmdChunk($pipeline, $cmd_list);
-
-        /* If any FLAGS responses contain MODSEQs but not UIDs, don't
-         * cache any data and immediately close the mailbox. */
-        foreach ($pipeline->data['modseqs_nouid'] as $val) {
-            if (!$pipeline->fetch[$val]->getUid()) {
-                $this->_debug->info('Server provided FLAGS MODSEQ without providing UID.');
-                $this->close();
-                return $pipeline;
-            }
-        }
-
-        /* Update HIGHESTMODSEQ value. */
-        if (!empty($pipeline->data['modseqs'])) {
-            $modseq = max($pipeline->data['modseqs']);
-            $this->_mailboxOb()->setStatus(Horde_Imap_Client::STATUS_HIGHESTMODSEQ, $modseq);
-            $this->_updateModSeq($modseq);
-        }
-
-        /* Update cache items. */
-        $this->_updateCache($pipeline->fetch);
-
-        return $pipeline;
-    }
-
-    /**
-     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  The pipeline
-     *                                                          object.
-     * @param array $chunk  List of commands to send.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    protected function _sendCmdChunk($pipeline, $chunk)
-    {
-        if (empty($chunk)) {
-            return;
-        }
-
-        $cmd_count = count($chunk);
-        $exception = null;
-
-        foreach ($chunk as $val) {
-            try {
-                $old_debug = $this->_debug->debug;
-                if (!is_null($val->debug)) {
-                    $this->_debug->raw($val->tag . ' ' . $val->debug . "\n");
-                    $this->_debug->debug = false;
-                }
-                $this->_processCmd($pipeline, $val, $val);
-                $this->_connection->write('', true);
-                $this->_debug->debug = $old_debug;
-            } catch (Horde_Imap_Client_Exception $e) {
-                $this->_debug->debug = $old_debug;
-
-                switch ($e->getCode()) {
-                case Horde_Imap_Client_Exception::SERVER_WRITEERROR:
-                    $this->_temp['logout'] = true;
-                    $this->logout();
-                    break;
-                }
-
-                throw $e;
-            }
-        }
-
-        while ($cmd_count) {
-            try {
-                if ($this->_getLine($pipeline) instanceof Horde_Imap_Client_Interaction_Server_Tagged) {
-                    --$cmd_count;
-                }
-            } catch (Horde_Imap_Client_Exception $e) {
-                switch ($e->getCode()) {
-                case $e::DISCONNECT:
-                    $this->_temp['logout'] = true;
-                    // Fall-through
-
-                case $e::SERVER_READERROR:
-                    $this->logout();
-                    throw $e;
-                }
-
-                // Catch and store exception; don't throw until all input
-                // is read. (For now, only store first exception.)
-                if (is_null($exception)) {
-                    $exception = $e;
-                }
-
-                if (($e instanceof Horde_Imap_Client_Exception_ServerResponse) &&
-                    $e->command) {
-                    --$cmd_count;
-                }
-            }
-        }
-
-        if (!is_null($exception)) {
-            throw $exception;
-        }
-    }
-
-    /**
-     * Process/send a command to the remote server.
-     *
-     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline The pipeline
-     *                                                         object.
-     * @param Horde_Imap_Client_Interaction_Command $cmd  The master command.
-     * @param Horde_Imap_Client_Data_Format_List $data    Commands to send.
-     *
-     * @throws Horde_Imap_Client_Exception
-     * @throws Horde_Imap_Client_Exception_NoSupport
-     */
-    protected function _processCmd($pipeline, $cmd, $data)
-    {
-        foreach ($data as $key => $val) {
-            if ($val instanceof Horde_Imap_Client_Interaction_Command_Continuation) {
-                $this->_connection->write('', true);
-
-                $this->_processCmd(
-                    $pipeline,
-                    $cmd,
-                    $val->getCommands($this->_processCmdContinuation($pipeline))
-                );
-                continue;
-            }
-
-            if ($key) {
-                $this->_connection->write(' ');
-            }
-
-            if ($val instanceof Horde_Imap_Client_Data_Format_List) {
-                $this->_connection->write('(');
-                $this->_processCmd($pipeline, $cmd, $val);
-                $this->_connection->write(')');
-            } elseif (($val instanceof Horde_Imap_Client_Data_Format_String) &&
-                      $val->literal()) {
-                /* RFC 3516/4466: Send literal8 if we have binary data. */
-                if ($cmd->literal8 &&
-                    $val->binary() &&
-                    $this->queryCapability('BINARY')) {
-                    $binary = true;
-                    $this->_connection->write('~');
-                } else {
-                    $binary = false;
-                }
-
-                $literal_len = $val->length();
-                $this->_connection->write('{' . $literal_len);
-
-                /* RFC 2088 - If LITERAL+ is available, saves a roundtrip from
-                 * the server. */
-                if ($cmd->literalplus && $this->queryCapability('LITERAL+')) {
-                    $this->_connection->write('+}', true);
-                } else {
-                    $this->_connection->write('}', true);
-                    $this->_processCmdContinuation($pipeline);
-                }
-
-                $this->_connection->writeLiteral($val->getStream(), $literal_len, $binary);
-            } else {
-                $this->_connection->write($val->escape());
-            }
-        }
-    }
-
-    /**
-     * Process a command continuation response.
-     *
-     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline The pipeline
-     *                                                         object.
-     *
-     * @return Horde_Imap_Client_Interaction_Server_Continuation  Continuation
-     *                                                            object.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    protected function _processCmdContinuation($pipeline)
-    {
-        $ob = $this->_getLine($pipeline);
-        if ($ob instanceof Horde_Imap_Client_Interaction_Server_Continuation) {
-            return $ob;
-        }
-
-        $this->_debug->info("ERROR: Unexpected response from server while waiting for a continuation request.");
-        $e = new Horde_Imap_Client_Exception(
-            Horde_Imap_Client_Translation::t("Error when communicating with the mail server."),
-            Horde_Imap_Client_Exception::SERVER_READERROR
-        );
-        $e->details = strval($ob);
-
-        throw $e;
-    }
-
-    /**
-     * Shortcut to creating a new IMAP client command object.
-     *
-     * @param string $cmd  The IMAP command.
-     *
-     * @return Horde_Imap_Client_Interaction_Command  A command object.
-     */
-    protected function _command($cmd)
-    {
-        return new Horde_Imap_Client_Interaction_Command($cmd, ++$this->_tag);
-    }
-
-    /**
-     * Shortcut to creating a new pipeline object.
-     *
-     * @param Horde_Imap_Client_Interaction_Command $cmd  An IMAP command to
-     *                                                    add.
-     *
-     * @return Horde_Imap_Client_Interaction_Pipeline  A pipeline object.
-     */
-    protected function _pipeline($cmd = null)
-    {
-        if (!isset($this->_temp['fetchob'])) {
-            $this->_temp['fetchob'] = new Horde_Imap_Client_Fetch_Results(
-                $this->_fetchDataClass,
-                Horde_Imap_Client_Fetch_Results::SEQUENCE
-            );
-        }
-
-        $ob = new Horde_Imap_Client_Interaction_Pipeline(
-            clone $this->_temp['fetchob']
-        );
-
-        if (!is_null($cmd)) {
-            $ob->add($cmd);
-        }
-
-        return $ob;
-    }
-
-    /**
-     * Gets data from the IMAP server stream and parses it.
-     *
-     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
-     *                                                          object.
-     *
-     * @return Horde_Imap_Client_Interaction_Server  Server object.
-     *
-     * @throws Horde_Imap_Client_Exception
-     */
-    protected function _getLine(
-        Horde_Imap_Client_Interaction_Pipeline $pipeline
-    )
-    {
-        $server = Horde_Imap_Client_Interaction_Server::create(
-            $this->_connection->read()
-        );
-
-        switch (get_class($server)) {
-        case 'Horde_Imap_Client_Interaction_Server_Continuation':
-            $this->_responseCode($pipeline, $server);
-            break;
-
-        case 'Horde_Imap_Client_Interaction_Server_Tagged':
-            $pipeline->complete($server);
-            $this->_responseCode($pipeline, $server);
-            break;
-
-        case 'Horde_Imap_Client_Interaction_Server_Untagged':
-            if (is_null($server->status)) {
-                $this->_serverResponse($pipeline, $server);
-            } else {
-                $this->_responseCode($pipeline, $server);
-            }
-            break;
-        }
-
-        switch ($server->status) {
-        case $server::BAD:
-            /* A tagged BAD response indicates that the tagged command caused
-             * the error. This information is unknown if untagged (RFC 3501
-             * [7.1.3]). */
-            throw new Horde_Imap_Client_Exception_ServerResponse(
-                Horde_Imap_Client_Translation::t("IMAP error reported by server."),
-                0,
-                $server,
-                $pipeline
-            );
-
-        case $server::BYE:
-            /* A BYE response received as part of a logout command should be
-             * be treated like a regular command: a client MUST process the
-             * entire command until logging out (RFC 3501 [3.4; 7.1.5]). */
-            if (empty($this->_temp['logout'])) {
-                $e = new Horde_Imap_Client_Exception(
-                    Horde_Imap_Client_Translation::t("IMAP Server closed the connection."),
-                    Horde_Imap_Client_Exception::DISCONNECT
-                );
-                $e->details = strval($server);
-                throw $e;
-            }
-            break;
-
-        case $server::NO:
-            /* An untagged NO response indicates a warning; ignore and assume
-             * that it also included response text code that is handled
-             * elsewhere. Throw exception if tagged; command handlers can
-             * catch this if able to workaround this issue (RFC 3501
-             * [7.1.2]). */
-            if ($server instanceof Horde_Imap_Client_Interaction_Server_Tagged) {
-                throw new Horde_Imap_Client_Exception_ServerResponse(
-                    Horde_Imap_Client_Translation::t("IMAP error reported by server."),
-                    0,
-                    $server,
-                    $pipeline
-                );
-            }
-
-        case $server::PREAUTH:
-            /* The user was pre-authenticated. (RFC 3501 [7.1.4]) */
-            $this->_temp['preauth'] = true;
-            break;
-        }
-
-        return $server;
-    }
-
-    /**
-     * Handle untagged server responses (see RFC 3501 [2.2.2]).
-     *
-     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
-     *                                                          object.
-     * @param Horde_Imap_Client_Interaction_Server $ob          Server
-     *                                                          response.
-     */
-    protected function _serverResponse(
-        Horde_Imap_Client_Interaction_Pipeline $pipeline,
-        Horde_Imap_Client_Interaction_Server $ob
-    )
-    {
-        $token = $ob->token;
-
-        /* First, catch untagged responses where the name appears first on the
-         * line. */
-        switch ($first = strtoupper($token->current())) {
-        case 'CAPABILITY':
-            $this->_parseCapability($pipeline, $token->flushIterator());
-            break;
-
-        case 'LIST':
-        case 'LSUB':
-            $this->_parseList($pipeline, $token);
-            break;
-
-        case 'STATUS':
-            // Parse a STATUS response (RFC 3501 [7.2.4]).
-            $this->_parseStatus($token);
-            break;
-
-        case 'SEARCH':
-        case 'SORT':
-            // Parse a SEARCH/SORT response (RFC 3501 [7.2.5] & RFC 5256 [4]).
-            $this->_parseSearch($pipeline, $token->flushIterator());
-            break;
-
-        case 'ESEARCH':
-            // Parse an ESEARCH response (RFC 4466 [2.6.2]).
-            $this->_parseEsearch($pipeline, $token);
-            break;
-
-        case 'FLAGS':
-            $token->next();
-            $this->_mailboxOb()->setStatus(Horde_Imap_Client::STATUS_FLAGS, array_map('strtolower', $token->flushIterator()));
-            break;
-
-        case 'QUOTA':
-            $this->_parseQuota($pipeline, $token);
-            break;
-
-        case 'QUOTAROOT':
-            // Ignore this line - we can get this information from
-            // the untagged QUOTA responses.
-            break;
-
-        case 'NAMESPACE':
-            $this->_parseNamespace($pipeline, $token);
-            break;
-
-        case 'THREAD':
-            $this->_parseThread($pipeline, $token);
-            break;
-
-        case 'ACL':
-            $this->_parseACL($pipeline, $token);
-            break;
-
-        case 'LISTRIGHTS':
-            $this->_parseListRights($pipeline, $token);
-            break;
-
-        case 'MYRIGHTS':
-            $this->_parseMyRights($pipeline, $token);
-            break;
-
-        case 'ID':
-            // ID extension (RFC 2971)
-            $this->_parseID($pipeline, $token);
-            break;
-
-        case 'ENABLED':
-            // ENABLE extension (RFC 5161)
-            $this->_parseEnabled($token);
-            break;
-
-        case 'LANGUAGE':
-            // LANGUAGE extension (RFC 5255 [3.2])
-            $this->_parseLanguage($token);
-            break;
-
-        case 'COMPARATOR':
-            // I18NLEVEL=2 extension (RFC 5255 [4.7])
-            $this->_parseComparator($pipeline, $token);
-            break;
-
-        case 'VANISHED':
-            // QRESYNC extension (RFC 5162 [3.6])
-            $this->_parseVanished($pipeline, $token);
-            break;
-
-        case 'ANNOTATION':
-        case 'METADATA':
-            // Parse a ANNOTATEMORE/METADATA response.
-            $this->_parseMetadata($pipeline, $token);
-            break;
-
-        default:
-            // Next, look for responses where the keywords occur second.
-            switch (strtoupper($token->next())) {
-            case 'EXISTS':
-                // EXISTS response - RFC 3501 [7.3.2]
-                $mbox_ob = $this->_mailboxOb();
-
-                // Increment UIDNEXT if it is set.
-                if ($mbox_ob->open &&
-                    ($uidnext = $mbox_ob->getStatus(Horde_Imap_Client::STATUS_UIDNEXT))) {
-                    $mbox_ob->setStatus(Horde_Imap_Client::STATUS_UIDNEXT, $uidnext + $first - $mbox_ob->getStatus(Horde_Imap_Client::STATUS_MESSAGES));
-                }
-
-                $mbox_ob->setStatus(Horde_Imap_Client::STATUS_MESSAGES, $first);
-                break;
-
-            case 'RECENT':
-                // RECENT response - RFC 3501 [7.3.1]
-                $this->_mailboxOb()->setStatus(Horde_Imap_Client::STATUS_RECENT, $first);
-                break;
-
-            case 'EXPUNGE':
-                // EXPUNGE response - RFC 3501 [7.4.1]
-                $this->_deleteMsgs($this->_selected, $this->getIdsOb($first, true), array(
-                    'decrement' => true,
-                    'pipeline' => $pipeline
-                ));
-                $pipeline->data['expunge_seen'] = true;
-                break;
-
-            case 'FETCH':
-                // FETCH response - RFC 3501 [7.4.2]
-                $this->_parseFetch($pipeline, $first, $token);
-                break;
-            }
-            break;
-        }
-    }
-
-    /**
-     * Handle status responses (see RFC 3501 [7.1]).
-     *
-     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
-     *                                                          object.
-     * @param Horde_Imap_Client_Interaction_Server $ob          Server object.
-     *
-     * @throws Horde_Imap_Client_Exception_ServerResponse
-     */
-    protected function _responseCode(
-        Horde_Imap_Client_Interaction_Pipeline $pipeline,
-        Horde_Imap_Client_Interaction_Server $ob
-    )
-    {
-        if (is_null($ob->responseCode)) {
-            return;
-        }
-
-        $rc = $ob->responseCode;
-
-        switch ($rc->code) {
-        case 'ALERT':
-        // Defined by RFC 5530 [3] - Treat as an alert for now.
-        case 'CONTACTADMIN':
-            if (!isset($this->_temp['alerts'])) {
-                $this->_temp['alerts'] = array();
-            }
-            $this->_temp['alerts'][] = strval($ob->token);
-            break;
-
-        case 'BADCHARSET':
-            /* Store valid search charsets if returned by server. */
-            $s_charset = array();
-            foreach ($rc->data[0] as $val) {
-                $s_charset[$val] = true;
-            }
-
-            if (!empty($s_charset)) {
-                $this->_setInit('s_charset', array_merge(
-                    $this->_init['s_charset'],
-                    $s_charset
-                ));
-            }
-
-            throw new Horde_Imap_Client_Exception_ServerResponse(
-                Horde_Imap_Client_Translation::t("Charset used in search query is not supported on the mail server."),
-                Horde_Imap_Client_Exception::BADCHARSET,
-                $ob,
-                $pipeline
-            );
-
-        case 'CAPABILITY':
-            $this->_parseCapability($pipeline, $rc->data);
-            break;
-
-        case 'PARSE':
-            /* Only throw error on NO/BAD. Message is human readable. */
-            switch ($ob->status) {
-            case Horde_Imap_Client_Interaction_Server::BAD:
-            case Horde_Imap_Client_Interaction_Server::NO:
-                throw new Horde_Imap_Client_Exception_ServerResponse(
-                    sprintf(Horde_Imap_Client_Translation::t("The mail server was unable to parse the contents of the mail message: %s"), strval($ob->token)),
-                    Horde_Imap_Client_Exception::PARSEERROR,
-                    $ob,
-                    $pipeline
-                );
-            }
-            break;
-
-        case 'READ-ONLY':
-            $this->_mode = Horde_Imap_Client::OPEN_READONLY;
-            break;
-
-        case 'READ-WRITE':
-            $this->_mode = Horde_Imap_Client::OPEN_READWRITE;
-            break;
-
-        case 'TRYCREATE':
-            // RFC 3501 [7.1]
-            $pipeline->data['trycreate'] = true;
-            break;
-
-        case 'PERMANENTFLAGS':
-            $this->_mailboxOb()->setStatus(Horde_Imap_Client::STATUS_PERMFLAGS, array_map('strtolower', $rc->data[0]));
-            break;
-
-        case 'UIDNEXT':
-            $this->_mailboxOb()->setStatus(Horde_Imap_Client::STATUS_UIDNEXT, $rc->data[0]);
-            break;
-
-        case 'UIDVALIDITY':
-            $this->_mailboxOb()->setStatus(Horde_Imap_Client::STATUS_UIDVALIDITY, $rc->data[0]);
-            break;
-
-        case 'UNSEEN':
-            /* This is different from the STATUS UNSEEN response - this item,
-             * if defined, returns the first UNSEEN message in the mailbox. */
-            $this->_mailboxOb()->setStatus(Horde_Imap_Client::STATUS_FIRSTUNSEEN, $rc->data[0]);
-            break;
-
-        case 'REFERRAL':
-            // Defined by RFC 2221
-            $pipeline->data['referral'] = new Horde_Imap_Client_Url($rc->data[0]);
-            break;
-
-        case 'UNKNOWN-CTE':
-            // Defined by RFC 3516
-            throw new Horde_Imap_Client_Exception_ServerResponse(
-                Horde_Imap_Client_Translation::t("The mail server was unable to parse the contents of the mail message."),
-                Horde_Imap_Client_Exception::UNKNOWNCTE,
-                $ob,
-                $pipeline
-            );
-
-        case 'APPENDUID':
-            // Defined by RFC 4315
-            // APPENDUID: [0] = UIDVALIDITY, [1] = UID(s)
-            $pipeline->data['appenduid'] = $this->getIdsOb($rc->data[1]);
-            break;
-
-        case 'COPYUID':
-            // Defined by RFC 4315
-            // COPYUID: [0] = UIDVALIDITY, [1] = UIDFROM, [2] = UIDTO
-            $pipeline->data['copyuid'] = array_combine(
-                $this->getIdsOb($rc->data[1])->ids,
-                $this->getIdsOb($rc->data[2])->ids
-            );
-
-            /* Use UIDPLUS information to move cached data to new mailbox (see
-             * RFC 4549 [4.2.2.1]). Need to move now, because a MOVE might
-             * EXPUNGE immediately afterwards. */
-            $this->_moveCache($pipeline->data['copydest'], $pipeline->data['copyuid'], $rc->data[0]);
-            break;
-
-        case 'UIDNOTSTICKY':
-            // Defined by RFC 4315 [3]
-            $this->_mailboxOb()->setStatus(Horde_Imap_Client::STATUS_UIDNOTSTICKY, true);
-            break;
-
-        case 'BADURL':
-            // Defined by RFC 4469 [4.1]
-            throw new Horde_Imap_Client_Exception_ServerResponse(
-                Horde_Imap_Client_Translation::t("Could not save message on server."),
-                Horde_Imap_Client_Exception::CATENATE_BADURL,
-                $ob,
-                $pipeline
-            );
-
-        case 'TOOBIG':
-            // Defined by RFC 4469 [4.2]
-            throw new Horde_Imap_Client_Exception_ServerResponse(
-                Horde_Imap_Client_Translation::t("Could not save message data because it is too large."),
-                Horde_Imap_Client_Exception::CATENATE_TOOBIG,
-                $ob,
-                $pipeline
-            );
-
-        case 'HIGHESTMODSEQ':
-            // Defined by RFC 4551 [3.1.1]
-            $pipeline->data['modseqs'][] = $rc->data[0];
-            break;
-
-        case 'NOMODSEQ':
-            // Defined by RFC 4551 [3.1.2]
-            $pipeline->data['modseqs'][] = 0;
-            break;
-
-        case 'MODIFIED':
-            // Defined by RFC 4551 [3.2]
-            $pipeline->data['modified']->add($rc->data[0]);
-            break;
-
-        case 'CLOSED':
-            // Defined by RFC 5162 [3.7]
-            if (isset($pipeline->data['qresyncmbox'])) {
-                /* If there is any pending FETCH cache entries, flush them
-                 * now before changing mailboxes. */
-                $this->_updateCache($pipeline->fetch);
-                $pipeline->fetch->clear();
-
-                $this->_changeSelected(
-                    $pipeline->data['qresyncmbox'][0],
-                    $pipeline->data['qresyncmbox'][1]
-                );
-                unset($pipeline->data['qresyncmbox']);
-            }
-            break;
-
-        case 'NOTSAVED':
-            // Defined by RFC 5182 [2.5]
-            $pipeline->data['searchnotsaved'] = true;
-            break;
-
-        case 'BADCOMPARATOR':
-            // Defined by RFC 5255 [4.9]
-            throw new Horde_Imap_Client_Exception_ServerResponse(
-                Horde_Imap_Client_Translation::t("The comparison algorithm was not recognized by the server."),
-                Horde_Imap_Client_Exception::BADCOMPARATOR,
-                $ob,
-                $pipeline
-            );
-
-        case 'METADATA':
-            $md = $rc->data[0];
-
-            switch ($md[0]) {
-            case 'LONGENTRIES':
-                // Defined by RFC 5464 [4.2.1]
-                $pipeline->data['metadata']['*longentries'] = intval($md[1]);
-                break;
-
-            case 'MAXSIZE':
-                // Defined by RFC 5464 [4.3]
-                throw new Horde_Imap_Client_Exception_ServerResponse(
-                    Horde_Imap_Client_Translation::t("The metadata item could not be saved because it is too large."),
-                    Horde_Imap_Client_Exception::METADATA_MAXSIZE,
-                    $ob,
-                    $pipeline
-                );
-
-            case 'NOPRIVATE':
-                // Defined by RFC 5464 [4.3]
-                throw new Horde_Imap_Client_Exception_ServerResponse(
-                    Horde_Imap_Client_Translation::t("The metadata item could not be saved because the server does not support private annotations."),
-                    Horde_Imap_Client_Exception::METADATA_NOPRIVATE,
-                    $ob,
-                    $pipeline
-                );
-
-            case 'TOOMANY':
-                // Defined by RFC 5464 [4.3]
-                throw new Horde_Imap_Client_Exception_ServerResponse(
-                    Horde_Imap_Client_Translation::t("The metadata item could not be saved because the maximum number of annotations has been exceeded."),
-                    Horde_Imap_Client_Exception::METADATA_TOOMANY,
-                    $ob,
-                    $pipeline
-                );
-            }
-            break;
-
-        case 'UNAVAILABLE':
-            // Defined by RFC 5530 [3]
-            $pipeline->data['loginerr'] = new Horde_Imap_Client_Exception(
-                Horde_Imap_Client_Translation::t("Remote server is temporarily unavailable."),
-                Horde_Imap_Client_Exception::LOGIN_UNAVAILABLE
-            );
-            break;
-
-        case 'AUTHENTICATIONFAILED':
-            // Defined by RFC 5530 [3]
-            $pipeline->data['loginerr'] = new Horde_Imap_Client_Exception(
-                Horde_Imap_Client_Translation::t("Authentication failed."),
-                Horde_Imap_Client_Exception::LOGIN_AUTHENTICATIONFAILED
-            );
-            break;
-
-        case 'AUTHORIZATIONFAILED':
-            // Defined by RFC 5530 [3]
-            $pipeline->data['loginerr'] = new Horde_Imap_Client_Exception(
-                Horde_Imap_Client_Translation::t("Authentication was successful, but authorization failed."),
-                Horde_Imap_Client_Exception::LOGIN_AUTHORIZATIONFAILED
-            );
-            break;
-
-        case 'EXPIRED':
-            // Defined by RFC 5530 [3]
-            $pipeline->data['loginerr'] = new Horde_Imap_Client_Exception(
-                Horde_Imap_Client_Translation::t("Authentication credentials have expired."),
-                Horde_Imap_Client_Exception::LOGIN_EXPIRED
-            );
-            break;
-
-        case 'PRIVACYREQUIRED':
-            // Defined by RFC 5530 [3]
-            $pipeline->data['loginerr'] = new Horde_Imap_Client_Exception(
-                Horde_Imap_Client_Translation::t("Operation failed due to a lack of a secure connection."),
-                Horde_Imap_Client_Exception::LOGIN_PRIVACYREQUIRED
-            );
-            break;
-
-        case 'NOPERM':
-            // Defined by RFC 5530 [3]
-            throw new Horde_Imap_Client_Exception_ServerResponse(
-                Horde_Imap_Client_Translation::t("You do not have adequate permissions to carry out this operation."),
-                Horde_Imap_Client_Exception::NOPERM,
-                $ob,
-                $pipeline
-            );
-
-        case 'INUSE':
-            // Defined by RFC 5530 [3]
-            throw new Horde_Imap_Client_Exception_ServerResponse(
-                Horde_Imap_Client_Translation::t("There was a temporary issue when attempting this operation. Please try again later."),
-                Horde_Imap_Client_Exception::INUSE,
-                $ob,
-                $pipeline
-            );
-
-        case 'EXPUNGEISSUED':
-            // Defined by RFC 5530 [3]
-            $pipeline->data['expungeissued'] = true;
-            break;
-
-        case 'CORRUPTION':
-            // Defined by RFC 5530 [3]
-            throw new Horde_Imap_Client_Exception_ServerResponse(
-                Horde_Imap_Client_Translation::t("The mail server is reporting corrupt data in your mailbox."),
-                Horde_Imap_Client_Exception::CORRUPTION,
-                $ob,
-                $pipeline
-            );
-
-        case 'SERVERBUG':
-        case 'CLIENTBUG':
-        case 'CANNOT':
-            // Defined by RFC 5530 [3]
-            $this->_debug->info("ERROR: mail server explicitly reporting an error.");
-            break;
-
-        case 'LIMIT':
-            // Defined by RFC 5530 [3]
-            throw new Horde_Imap_Client_Exception_ServerResponse(
-                Horde_Imap_Client_Translation::t("The mail server has denied the request."),
-                Horde_Imap_Client_Exception::LIMIT,
-                $ob,
-                $pipeline
-            );
-
-        case 'OVERQUOTA':
-            // Defined by RFC 5530 [3]
-            throw new Horde_Imap_Client_Exception_ServerResponse(
-                Horde_Imap_Client_Translation::t("The operation failed because the quota has been exceeded on the mail server."),
-                Horde_Imap_Client_Exception::OVERQUOTA,
-                $ob,
-                $pipeline
-            );
-
-        case 'ALREADYEXISTS':
-            // Defined by RFC 5530 [3]
-            throw new Horde_Imap_Client_Exception_ServerResponse(
-                Horde_Imap_Client_Translation::t("The object could not be created because it already exists."),
-                Horde_Imap_Client_Exception::ALREADYEXISTS,
-                $ob,
-                $pipeline
-            );
-
-        case 'NONEXISTENT':
-            // Defined by RFC 5530 [3]
-            throw new Horde_Imap_Client_Exception_ServerResponse(
-                Horde_Imap_Client_Translation::t("The object could not be deleted because it does not exist."),
-                Horde_Imap_Client_Exception::NONEXISTENT,
-                $ob,
-                $pipeline
-            );
-
-        case 'USEATTR':
-            // Defined by RFC 6154 [3]
-            throw new Horde_Imap_Client_Exception_ServerResponse(
-                Horde_Imap_Client_Translation::t("The special-use attribute requested for the mailbox is not supported."),
-                Horde_Imap_Client_Exception::USEATTR,
-                $ob,
-                $pipeline
-            );
-
-        case 'DOWNGRADED':
-            // Defined by RFC 6858 [3]
-            $downgraded = $this->getIdsOb($rc->data[0]);
-            foreach ($pipeline->fetch as $val) {
-                if (in_array($val->getUid(), $downgraded)) {
-                    $val->setDowngraded(true);
-                }
-            }
-            break;
-
-        case 'XPROXYREUSE':
-            // The proxy connection was reused, so no need to do login tasks.
-            $pipeline->data['proxyreuse'] = true;
-            break;
-
-        default:
-            // Unknown response codes SHOULD be ignored - RFC 3501 [7.1]
-            break;
-        }
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientTokenizephp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Tokenize.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Tokenize.php     2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Tokenize.php        2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,310 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Tokenization of an IMAP data stream.
- *
- * NOTE: This class is NOT intended to be accessed outside of this package.
- * There is NO guarantees that the API of this class will not change across
- * versions.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @internal
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- *
- * @property-read boolean $eos  Has the end of the stream been reached?
- */
-class Horde_Imap_Client_Tokenize implements Iterator
-{
-    /**
-     * Current data.
-     *
-     * @var mixed
-     */
-    protected $_current = false;
-
-    /**
-     * Current key.
-     *
-     * @var integer
-     */
-    protected $_key = false;
-
-    /**
-     * Sublevel.
-     *
-     * @var integer
-     */
-    protected $_level = false;
-
-    /**
-     * Data stream.
-     *
-     * @var Horde_Stream
-     */
-    protected $_stream;
-
-    /**
-     * Constructor.
-     *
-     * @param mixed $data  Data to add (string, resource, or Horde_Stream
-     *                     object).
-     */
-    public function __construct($data = null)
-    {
-        $this->_stream = new Horde_Stream_Temp();
-
-        if (!is_null($data)) {
-            $this->add($data);
-        }
-    }
-
-    /**
-     */
-    public function __get($name)
-    {
-        switch ($name) {
-        case 'eos':
-            return feof($this->_stream->stream);
-        }
-    }
-
-    /**
-     */
-    public function __sleep()
-    {
-        throw new LogicException('Object can not be serialized.');
-    }
-
-    /**
-     */
-    public function __toString()
-    {
-        $pos = ftell($this->_stream->stream);
-        $out = $this->_current . ' ' . $this->_stream->getString();
-        fseek($this->_stream->stream, $pos);
-        return $out;
-    }
-
-    /**
-     * Add data to buffer.
-     *
-     * @param mixed $data  Data to add (string, resource, or Horde_Stream
-     *                     object).
-     */
-    public function add($data)
-    {
-        $this->_stream->add($data);
-    }
-
-    /**
-     * Flush the remaining entries left in the iterator.
-     *
-     * @param boolean $return    If true, return entries. Only returns entries
-     *                           on the current level.
-     * @param boolean $sublevel  Only flush items in current sublevel?
-     *
-     * @return array  The entries if $return is true.
-     */
-    public function flushIterator($return = true, $sublevel = true)
-    {
-        $out = array();
-
-        if ($return) {
-            $level = $sublevel ? $this->_level : 0;
-            do {
-                $curr = $this->next();
-                if ($this->_level < $level) {
-                    break;
-                }
-
-                if (!is_bool($curr) && ($level == $this->_level)) {
-                    $out[] = $curr;
-                }
-            } while (($curr !== false) || $this->_level || !$this->eos);
-        } elseif ($sublevel && $this->_level) {
-            $level = $this->_level;
-            while ($level <= $this->_level) {
-                $this->next();
-            }
-        } else {
-            fseek($this->_stream->stream, 0, SEEK_END);
-            fgetc($this->_stream->stream);
-            $this->_current = $this->_key = $this->_level = false;
-        }
-
-        return $out;
-    }
-
-    /**
-     * Return literal length data located at the end of the stream.
-     *
-     * @return mixed  Null if no literal data found, or an array with these
-     *                keys:
-     *   - binary: (boolean) True if this is a literal8.
-     *   - length: (integer) Length of the literal.
-     */
-    public function getLiteralLength()
-    {
-        fseek($this->_stream->stream, -1, SEEK_END);
-        if ($this->_stream->peek() == '}') {
-            $literal_data = $this->_stream->getString($this->_stream->search('{', true) - 1);
-            $literal_len = substr($literal_data, 2, -1);
-
-            if (is_numeric($literal_len)) {
-                return array(
-                    'binary' => ($literal_data[0] == '~'),
-                    'length' => intval($literal_len)
-                );
-            }
-        }
-
-        return null;
-    }
-
-    /* Iterator methods. */
-
-    /**
-     */
-    public function current()
-    {
-        return $this->_current;
-    }
-
-    /**
-     */
-    public function key()
-    {
-        return $this->_key;
-    }
-
-    /**
-     * @return mixed  Either a string, boolean (true for open paren, false for
-     *                close paren/EOS), or null.
-     */
-    public function next()
-    {
-        if ((($this->_current = $this->_parseStream()) === false) &&
-            $this->eos) {
-            $this->_key = $this->_level = false;
-        } else {
-            ++$this->_key;
-        }
-
-        return $this->_current;
-    }
-
-    /**
-     */
-    public function rewind()
-    {
-        fseek($this->_stream->stream, 0);
-        $this->_current = false;
-        $this->_key = -1;
-        $this->_level = 0;
-    }
-
-    /**
-     */
-    public function valid()
-    {
-        return ($this->_level !== false);
-    }
-
-    /**
-     * Returns the next token and increments the internal stream pointer.
-     *
-     * @see next()
-     */
-    protected function _parseStream()
-    {
-        $in_quote = false;
-        $stream = $this->_stream->stream;
-        $text = '';
-
-        while (($c = fgetc($stream)) !== false) {
-            switch ($c) {
-            case '\\':
-                $text .= $in_quote
-                    ? fgetc($stream)
-                    : $c;
-                break;
-
-            case '"':
-                if ($in_quote) {
-                    return $text;
-                } else {
-                    $in_quote = true;
-                }
-                break;
-
-            default:
-                if ($in_quote) {
-                    $text .= $c;
-                    break;
-                }
-
-                switch ($c) {
-                case '(':
-                    ++$this->_level;
-                    return true;
-
-                case ')':
-                    if (strlen($text)) {
-                        fseek($stream, -1, SEEK_CUR);
-                        break 3;
-                    }
-                    --$this->_level;
-                    return false;
-
-                case '~':
-                    // Ignore binary string identifier. PHP strings are
-                    // binary-safe.
-                    break;
-
-                case '{':
-                    return stream_get_contents($stream, $this->_stream->getToChar('}'));
-
-                case ' ':
-                    if (strlen($text)) {
-                        break 3;
-                    }
-                    break;
-
-                default:
-                    $text .= $c;
-                    break;
-                }
-                break;
-            }
-        }
-
-        switch (strlen($text)) {
-        case 0:
-            return false;
-
-        case 3:
-            if (strcasecmp($text, 'NIL') === 0) {
-                return null;
-            }
-            // Fall-through
-
-        default:
-            return $text;
-        }
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientUrlphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Url.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Url.php  2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Url.php     2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,285 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2008-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2008-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Object representation of a a POP3 (RFC 2384) or IMAP (RFC 5092/5593) URL.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2008-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- *
- * @property-read boolean $relative  Is this a relative URL?
- */
-class Horde_Imap_Client_Url implements Serializable
-{
-    /**
-     * The authentication method to use.
-     *
-     * @var string
-     */
-    public $auth = null;
-
-    /**
-     * The remote server (not present for relative URLs).
-     *
-     * @var string
-     */
-    public $hostspec = null;
-
-    /**
-     * The IMAP mailbox
-     *
-     * @var string
-     */
-    public $mailbox = null;
-
-    /**
-     * A byte range for use with IMAP FETCH.
-     *
-     * @var string
-     */
-    public $partial = null;
-
-    /**
-     * The remote port (not present for relative URLs).
-     *
-     * @var integer
-     */
-    public $port = null;
-
-    /**
-     * The protocol type. Either 'imap' or 'pop' (not present for relative
-     * URLs).
-     *
-     * @var string
-     */
-    public $protocol = null;
-
-    /**
-     * A search query to be run with IMAP SEARCH.
-     *
-     * @var string
-     */
-    public $search = null;
-
-    /**
-     * A MIME part ID.
-     *
-     * @var string
-     */
-    public $section = null;
-
-    /**
-     * The username to use on the remote server.
-     *
-     * @var string
-     */
-    public $username = null;
-
-    /**
-     * The IMAP UID.
-     *
-     * @var string
-     */
-    public $uid = null;
-
-    /**
-     * The IMAP UIDVALIDITY for the given mailbox.
-     *
-     * @var integer
-     */
-    public $uidvalidity = null;
-
-    /**
-     * URLAUTH info (not parsed).
-     *
-     * @var string
-     */
-    public $urlauth = null;
-
-    /**
-     * Constructor.
-     *
-     * Absolute IMAP URLs takes one of the following forms:
-     *   - imap://<iserver>[/]
-     *   - imap://<iserver>/<enc-mailbox>[<uidvalidity>][?<enc-search>]
-     *   - imap://<iserver>/<enc-mailbox>[<uidvalidity>]<iuid>[<isection>][<ipartial>][<iurlauth>]
-     *
-     * POP URLs take one of the following forms:
-     *   - pop://<user>;auth=<auth>@<host>:<port>
-     *
-     * @param string $url  A URL string.
-     */
-    public function __construct($url = null)
-    {
-        if (!is_null($url)) {
-            $this->_parse($url);
-        }
-    }
-
-    /**
-     * Create a POP3 (RFC 2384) or IMAP (RFC 5092/5593) URL.
-     *
-     * @return string  A URL string.
-     */
-    public function __toString()
-    {
-        $url = '';
-
-        if (!is_null($this->protocol)) {
-            $url = $this->protocol . '://';
-
-            if (!is_null($this->username)) {
-                $url .= $this->username;
-            }
-
-            if (!is_null($this->auth)) {
-                $url .= ';AUTH=' . $this->auth . '@';
-            } elseif (!is_null($this->username)) {
-                $url .= '@';
-            }
-
-            $url .= $this->hostspec;
-
-            if (!is_null($this->port) && ($this->port != 143)) {
-                $url .= ':' . $this->port;
-            }
-        }
-
-        $url .= '/';
-
-        if (is_null($this->protocol) || ($this->protocol == 'imap')) {
-            $url .= urlencode($this->mailbox);
-
-            if (!empty($this->uidvalidity)) {
-                $url .= ';UIDVALIDITY=' . $this->uidvalidity;
-            }
-
-            if (!is_null($this->search)) {
-                $url .= '?' . urlencode($this->search);
-            } else {
-                if (!is_null($this->uid)) {
-                    $url .= '/;UID=' . $this->uid;
-                }
-
-                if (!is_null($this->section)) {
-                    $url .= '/;SECTION=' . $this->section;
-                }
-
-                if (!is_null($this->partial)) {
-                    $url .= '/;PARTIAL=' . $this->partial;
-                }
-
-                if (!is_null($this->urlauth)) {
-                    $url .= '/;URLAUTH=' . $this->urlauth;
-                }
-            }
-        }
-
-        return $url;
-    }
-
-    /**
-     */
-    public function __get($name)
-    {
-        switch ($name) {
-        case 'relative':
-            return (is_null($this->hostspec) &&
-                is_null($this->port) &&
-                is_null($this->protocol));
-        }
-    }
-
-    /**
-     */
-    protected function _parse($url)
-    {
-        $data = parse_url(trim($url));
-
-        if (isset($data['scheme'])) {
-            $protocol = strtolower($data['scheme']);
-            if (!in_array($protocol, array('imap', 'pop'))) {
-                return;
-            }
-
-            if (isset($data['host'])) {
-                $this->hostspec = $data['host'];
-            }
-            $this->port = isset($data['port'])
-                ? $data['port']
-                : 143;
-            $this->protocol = $protocol;
-        }
-
-        /* Check for username/auth information. */
-        if (isset($data['user'])) {
-            if (($pos = stripos($data['user'], ';AUTH=')) !== false) {
-                $auth = substr($data['user'], $pos + 6);
-                if ($auth != '*') {
-                    $this->auth = $auth;
-                }
-                $data['user'] = substr($data['user'], 0, $pos);
-            }
-
-            if (strlen($data['user'])) {
-                $this->username = $data['user'];
-            }
-        }
-
-        /* IMAP-only information. */
-        if (is_null($this->protocol) || ($this->protocol == 'imap')) {
-            if (isset($data['path'])) {
-                $data['path'] = ltrim($data['path'], '/');
-                $parts = explode('/;', $data['path']);
-
-                $mbox = array_shift($parts);
-                if (($pos = stripos($mbox, ';UIDVALIDITY=')) !== false) {
-                    $this->uidvalidity = intval(substr($mbox, $pos + 13));
-                    $mbox = substr($mbox, 0, $pos);
-                }
-                $this->mailbox = urldecode($mbox);
-
-            }
-
-            if (count($parts)) {
-                foreach ($parts as $val) {
-                    list($k, $v) = explode('=', $val);
-                    $property = strtolower($k);
-                    $this->$property = $v;
-                }
-            } elseif (isset($data['query'])) {
-                $this->search = urldecode($data['query']);
-            }
-        }
-    }
-
-    /* Serializable methods. */
-
-    /**
-     */
-    public function serialize()
-    {
-        return strval($this);
-    }
-
-    /**
-     */
-    public function unserialize($data)
-    {
-        $this->_parse($data);
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientUtf7imapphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client/Utf7imap.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client/Utf7imap.php     2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client/Utf7imap.php        2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,302 +0,0 @@
</span><del>-<?php
-/**
- * Originally based on code:
- *
- *  Copyright (C) 2000 Edmund Grimley Evans <edmundo@rano.org>
- *  Released under the GPL (version 2)
- *
- *  Translated from C to PHP by Thomas Bruederli <roundcube@gmail.com>
- *  Code extracted from the RoundCube Webmail (http://roundcube.net) project,
- *    SVN revision 1757
- *  The RoundCube project is released under the GPL (version 2)
- *
- * Copyright 2008-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2000 Edmund Grimley Evans <edmundo@rano.org>
- * @copyright 2008-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Allows conversions between UTF-8 and UTF7-IMAP (RFC 3501 [5.1.3]).
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2000 Edmund Grimley Evans <edmundo@rano.org>
- * @copyright 2008-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client_Utf7imap
-{
-    /**
-     * Lookup table for conversion.
-     *
-     * @var array
-     */
-    private static $_index64 = array(
-        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, 63, -1, -1, -1,
-        52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
-        -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
-        15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
-        -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
-        41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1
-    );
-
-    /**
-     * Lookup table for conversion.
-     *
-     * @var array
-     */
-    private static $_base64 = 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', '+', ','
-    );
-
-    /**
-     * Is mbstring extension available?
-     *
-     * @var array
-     */
-    private static $_mbstring = null;
-
-    /**
-     * Convert a string from UTF7-IMAP to UTF-8.
-     *
-     * @param string $str  The UTF7-IMAP string.
-     *
-     * @return string  The converted UTF-8 string.
-     * @throws Horde_Imap_Client_Exception
-     */
-    public static function Utf7ImapToUtf8($str)
-    {
-        if ($str instanceof Horde_Imap_Client_Mailbox) {
-            return $str->utf8;
-        }
-
-        $str = strval($str);
-
-        /* Try mbstring, if available, which should be faster. Don't use the
-         * IMAP utf7_* functions because they are known to be buggy. */
-        if (is_null(self::$_mbstring)) {
-            self::$_mbstring = extension_loaded('mbstring');
-        }
-        if (self::$_mbstring) {
-            return @mb_convert_encoding($str, 'UTF-8', 'UTF7-IMAP');
-        }
-
-        $p = '';
-        $ptr = &self::$_index64;
-
-        for ($i = 0, $u7len = strlen($str); $u7len > 0; ++$i, --$u7len) {
-            $u7 = $str[$i];
-            if ($u7 == '&') {
-                $u7 = $str[++$i];
-                if (--$u7len && ($u7 == '-')) {
-                    $p .= '&';
-                    continue;
-                }
-
-                $ch = 0;
-                $k = 10;
-                for (; $u7len > 0; ++$i, --$u7len) {
-                    $u7 = $str[$i];
-
-                    if ((ord($u7) & 0x80) || ($b = $ptr[ord($u7)]) == -1) {
-                        break;
-                    }
-
-                    if ($k > 0) {
-                        $ch |= $b << $k;
-                        $k -= 6;
-                    } else {
-                        $ch |= $b >> (-$k);
-                        if ($ch < 0x80) {
-                            /* Printable US-ASCII */
-                            if ((0x20 <= $ch) && ($ch < 0x7f)) {
-                                throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::t("Error converting UTF7-IMAP string."), Horde_Imap_Client_Exception::UTF7IMAP_CONVERSION);
-                            }
-                            $p .= chr($ch);
-                        } else if ($ch < 0x800) {
-                            $p .= chr(0xc0 | ($ch >> 6)) .
-                                  chr(0x80 | ($ch & 0x3f));
-                        } else {
-                            $p .= chr(0xe0 | ($ch >> 12)) .
-                                  chr(0x80 | (($ch >> 6) & 0x3f)) .
-                                  chr(0x80 | ($ch & 0x3f));
-                        }
-
-                        $ch = ($b << (16 + $k)) & 0xffff;
-                        $k += 10;
-                    }
-                }
-
-                /* Non-zero or too many extra bits -OR-
-                 * Base64 not properly terminated -OR-
-                 * Adjacent Base64 sections. */
-                if (($ch || ($k < 6)) ||
-                    (!$u7len || $u7 != '-') ||
-                    (($u7len > 2) &&
-                     ($str[$i + 1] == '&') &&
-                     ($str[$i + 2] != '-'))) {
-                    throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::t("Error converting UTF7-IMAP string."), Horde_Imap_Client_Exception::UTF7IMAP_CONVERSION);
-                }
-            } elseif ((ord($u7) < 0x20) || (ord($u7) >= 0x7f)) {
-                /* Not printable US-ASCII */
-                throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::t("Error converting UTF7-IMAP string."), Horde_Imap_Client_Exception::UTF7IMAP_CONVERSION);
-            } else {
-                $p .= $u7;
-            }
-        }
-
-        return $p;
-    }
-
-    /**
-     * Convert a string from UTF-8 to UTF7-IMAP.
-     *
-     * @param string $str     The UTF-8 string.
-     * @param boolean $force  Assume $str is UTF-8 (no-autodetection)? If
-     *                        false, attempts to auto-detect if string is
-     *                        already in UTF7-IMAP.
-     *
-     * @return string  The converted UTF7-IMAP string.
-     * @throws Horde_Imap_Client_Exception
-     */
-    public static function Utf8ToUtf7Imap($str, $force = true)
-    {
-        if ($str instanceof Horde_Imap_Client_Mailbox) {
-            return $str->utf7imap;
-        }
-
-        $str = strval($str);
-
-        /* No need to do conversion if all chars are in US-ASCII range or if
-         * no ampersand is present. But will assume that an already encoded
-         * ampersand means string is in UTF7-IMAP already. */
-        if (!$force &&
-            !preg_match('/[\x80-\xff]|&$|&(?![,+A-Za-z0-9]*-)/', $str)) {
-            return $str;
-        }
-
-        /* Try mbstring, if available, which should be faster. Don't use the
-         * IMAP utf7_* functions because they are known to be buggy. */
-        if (is_null(self::$_mbstring)) {
-            self::$_mbstring = extension_loaded('mbstring');
-        }
-        if (self::$_mbstring) {
-            return @mb_convert_encoding($str, 'UTF7-IMAP', 'UTF-8');
-        }
-
-        $u8len = strlen($str);
-        $i = 0;
-        $base64 = false;
-        $p = '';
-        $ptr = &self::$_base64;
-
-        while ($u8len) {
-            $u8 = $str[$i];
-            $c = ord($u8);
-
-            if ($c < 0x80) {
-                $ch = $c;
-                $n = 0;
-            } elseif ($c < 0xc2) {
-                throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::t("Error converting UTF7-IMAP string."), Horde_Imap_Client_Exception::UTF7IMAP_CONVERSION);
-            } elseif ($c < 0xe0) {
-                $ch = $c & 0x1f;
-                $n = 1;
-            } elseif ($c < 0xf0) {
-                $ch = $c & 0x0f;
-                $n = 2;
-            } elseif ($c < 0xf8) {
-                $ch = $c & 0x07;
-                $n = 3;
-            } elseif ($c < 0xfc) {
-                $ch = $c & 0x03;
-                $n = 4;
-            } elseif ($c < 0xfe) {
-                $ch = $c & 0x01;
-                $n = 5;
-            } else {
-                throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::t("Error converting UTF7-IMAP string."), Horde_Imap_Client_Exception::UTF7IMAP_CONVERSION);
-            }
-
-            if ($n > --$u8len) {
-                throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::t("Error converting UTF7-IMAP string."), Horde_Imap_Client_Exception::UTF7IMAP_CONVERSION);
-            }
-
-            ++$i;
-
-            for ($j = 0; $j < $n; ++$j) {
-                $o = ord($str[$i + $j]);
-                if (($o & 0xc0) != 0x80) {
-                    throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::t("Error converting UTF7-IMAP string."), Horde_Imap_Client_Exception::UTF7IMAP_CONVERSION);
-                }
-                $ch = ($ch << 6) | ($o & 0x3f);
-            }
-
-            if (($n > 1) && !($ch >> ($n * 5 + 1))) {
-                throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::t("Error converting UTF7-IMAP string."), Horde_Imap_Client_Exception::UTF7IMAP_CONVERSION);
-            }
-
-            $i += $n;
-            $u8len -= $n;
-
-            if (($ch < 0x20) || ($ch >= 0x7f)) {
-                if (!$base64) {
-                    $p .= '&';
-                    $base64 = true;
-                    $b = 0;
-                    $k = 10;
-                }
-
-                if ($ch & ~0xffff) {
-                    $ch = 0xfffe;
-                }
-
-                $p .= $ptr[($b | $ch >> $k)];
-                $k -= 6;
-                for (; $k >= 0; $k -= 6) {
-                    $p .= $ptr[(($ch >> $k) & 0x3f)];
-                }
-
-                $b = ($ch << (-$k)) & 0x3f;
-                $k += 16;
-            } else {
-                if ($base64) {
-                    if ($k > 10) {
-                        $p .= $ptr[$b];
-                    }
-                    $p .= '-';
-                    $base64 = false;
-                }
-
-                $p .= chr($ch);
-                if (chr($ch) == '&') {
-                    $p .= '-';
-                }
-            }
-        }
-
-        if ($base64) {
-            if ($k > 10) {
-                $p .= $ptr[$b];
-            }
-            $p .= '-';
-        }
-
-        return $p;
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeClientphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Client.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Client.php      2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Client.php 2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,197 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2008-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2008-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-
-/**
- * Base class for Horde_Imap_Client package. Defines common constants for use
- * in the package.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2008-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Imap_Client
- */
-class Horde_Imap_Client
-{
-    /* Constants for openMailbox() */
-    const OPEN_READONLY = 1;
-    const OPEN_READWRITE = 2;
-    const OPEN_AUTO = 3;
-
-    /* Constants for listMailboxes() */
-    const MBOX_SUBSCRIBED = 1;
-    const MBOX_SUBSCRIBED_EXISTS = 2;
-    const MBOX_UNSUBSCRIBED = 3;
-    const MBOX_ALL = 4;
-
-    /* Constants for status() */
-    const STATUS_MESSAGES = 1;
-    const STATUS_RECENT = 2;
-    const STATUS_UIDNEXT = 4;
-    const STATUS_UIDVALIDITY = 8;
-    const STATUS_UNSEEN = 16;
-    const STATUS_ALL = 32;
-    const STATUS_FIRSTUNSEEN = 64;
-    const STATUS_FLAGS = 128;
-    const STATUS_PERMFLAGS = 256;
-    const STATUS_HIGHESTMODSEQ = 512;
-    const STATUS_SYNCMODSEQ = 1024;
-    const STATUS_SYNCFLAGUIDS = 2048;
-    const STATUS_UIDNOTSTICKY = 4096;
-    const STATUS_UIDNEXT_FORCE = 8192;
-    const STATUS_SYNCVANISHED = 16384;
-    /* @since 2.12.0 */
-    const STATUS_RECENT_TOTAL = 32768;
-
-    /* Constants for search() */
-    const SORT_ARRIVAL = 1;
-    const SORT_CC = 2;
-    const SORT_DATE = 3;
-    const SORT_FROM = 4;
-    const SORT_REVERSE = 5;
-    const SORT_SIZE = 6;
-    const SORT_SUBJECT = 7;
-    const SORT_TO = 8;
-    /* SORT_THREAD provided for completeness - it is not a valid sort criteria
-     * for search() (use thread() instead). */
-    const SORT_THREAD = 9;
-    /* Sort criteria defined in RFC 5957 */
-    const SORT_DISPLAYFROM = 10;
-    const SORT_DISPLAYTO = 11;
-    /* SORT_SEQUENCE does a simple numerical sort on the returned
-     * UIDs/sequence numbers. */
-    const SORT_SEQUENCE = 12;
-    /* Fuzzy sort criteria defined in RFC 6203 */
-    const SORT_RELEVANCY = 13;
-    /* @since 2.4.0 */
-    const SORT_DISPLAYFROM_FALLBACK = 14;
-    /* @since 2.4.0 */
-    const SORT_DISPLAYTO_FALLBACK = 15;
-
-    /* Search results constants */
-    const SEARCH_RESULTS_COUNT = 1;
-    const SEARCH_RESULTS_MATCH = 2;
-    const SEARCH_RESULTS_MAX = 3;
-    const SEARCH_RESULTS_MIN = 4;
-    const SEARCH_RESULTS_SAVE = 5;
-    /* Fuzzy sort criteria defined in RFC 6203 */
-    const SEARCH_RESULTS_RELEVANCY = 6;
-
-    /* Constants for thread() */
-    const THREAD_ORDEREDSUBJECT = 1;
-    const THREAD_REFERENCES = 2;
-    const THREAD_REFS = 3;
-
-    /* Fetch criteria constants. */
-    const FETCH_STRUCTURE = 1;
-    const FETCH_FULLMSG = 2;
-    const FETCH_HEADERTEXT = 3;
-    const FETCH_BODYTEXT = 4;
-    const FETCH_MIMEHEADER = 5;
-    const FETCH_BODYPART = 6;
-    const FETCH_BODYPARTSIZE = 7;
-    const FETCH_HEADERS = 8;
-    const FETCH_ENVELOPE = 9;
-    const FETCH_FLAGS = 10;
-    const FETCH_IMAPDATE = 11;
-    const FETCH_SIZE = 12;
-    const FETCH_UID = 13;
-    const FETCH_SEQ = 14;
-    const FETCH_MODSEQ = 15;
-    /* @since 2.11.0 */
-    const FETCH_DOWNGRADED = 16;
-
-    /* Namespace constants. */
-    const NS_PERSONAL = 1;
-    const NS_OTHER = 2;
-    const NS_SHARED = 3;
-
-    /* ACL constants (RFC 4314 [2.1]). */
-    const ACL_LOOKUP = 'l';
-    const ACL_READ = 'r';
-    const ACL_SEEN = 's';
-    const ACL_WRITE = 'w';
-    const ACL_INSERT = 'i';
-    const ACL_POST = 'p';
-    const ACL_CREATEMBOX = 'k';
-    const ACL_DELETEMBOX = 'x';
-    const ACL_DELETEMSGS = 't';
-    const ACL_EXPUNGE = 'e';
-    const ACL_ADMINISTER = 'a';
-    // Old constants (RFC 2086 [3]; RFC 4314 [2.1.1])
-    const ACL_CREATE = 'c';
-    const ACL_DELETE = 'd';
-
-    /* System flags. */
-    // RFC 3501 [2.3.2]
-    const FLAG_ANSWERED = '\\answered';
-    const FLAG_DELETED = '\\deleted';
-    const FLAG_DRAFT = '\\draft';
-    const FLAG_FLAGGED = '\\flagged';
-    const FLAG_RECENT = '\\recent';
-    const FLAG_SEEN = '\\seen';
-    // RFC 3503 [3.3]
-    const FLAG_MDNSENT = '$mdnsent';
-    // RFC 5550 [2.8]
-    const FLAG_FORWARDED = '$forwarded';
-    // RFC 5788 registered keywords:
-    // http://www.ietf.org/mail-archive/web/morg/current/msg00441.html
-    const FLAG_JUNK = '$junk';
-    const FLAG_NOTJUNK = '$notjunk';
-
-    /* Special-use mailbox attributes (RFC 6154 [2]). */
-    const SPECIALUSE_ALL = '\\All';
-    const SPECIALUSE_ARCHIVE = '\\Archive';
-    const SPECIALUSE_DRAFTS = '\\Drafts';
-    const SPECIALUSE_FLAGGED = '\\Flagged';
-    const SPECIALUSE_JUNK = '\\Junk';
-    const SPECIALUSE_SENT = '\\Sent';
-    const SPECIALUSE_TRASH = '\\Trash';
-
-    /* Constants for sync(). */
-    const SYNC_UIDVALIDITY = 0;
-    const SYNC_FLAGS = 1;
-    const SYNC_FLAGSUIDS = 2;
-    const SYNC_NEWMSGS = 4;
-    const SYNC_NEWMSGSUIDS = 8;
-    const SYNC_VANISHED = 16;
-    const SYNC_VANISHEDUIDS = 32;
-    const SYNC_ALL = 64;
-
-    /**
-     * Capability dependencies.
-     *
-     * @var array
-     */
-    static public $capability_deps = array(
-        // RFC 5162 [1]
-        'QRESYNC' => array(
-            // QRESYNC requires CONDSTORE, but the latter is implied and is
-            // not required to be listed.
-            'ENABLE'
-        ),
-        // RFC 5182 [2.1]
-        'SEARCHRES' => array(
-            'ESEARCH'
-        ),
-        // RFC 5255 [3.1]
-        'LANGUAGE' => array(
-            'NAMESPACE'
-        ),
-        // RFC 5957 [1]
-        'SORT=DISPLAY' => array(
-            'SORT'
-        )
-    );
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeExceptionWrappedphpfromrev22362013codebykatpostbyemailtrunkincludeHordeExceptionWrappedphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Exception/Wrapped.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Exception-Wrapped.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Exception/Wrapped.php                           (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Exception/Wrapped.php      2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,53 @@
</span><ins>+<?php
+/**
+ * Horde exception class that can wrap and set its details from PEAR_Error,
+ * Exception, and other objects with similar interfaces.
+ *
+ * Copyright 2008-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category Horde
+ * @package  Exception
+ */
+class Horde_Exception_Wrapped extends Horde_Exception
+{
+    /**
+     * Exception constructor.
+     *
+     * @param mixed $message The exception message, a PEAR_Error
+     *                       object, or an Exception object.
+     * @param int   $code    A numeric error code.
+     */
+    public function __construct($message = null, $code = 0)
+    {
+        $previous = null;
+        if (is_object($message) &&
+            method_exists($message, 'getMessage')) {
+            if (empty($code) &&
+                method_exists($message, 'getCode')) {
+                $code = (int)$message->getCode();
+            }
+            if ($message instanceof Exception) {
+                $previous = $message;
+            }
+            if (method_exists($message, 'getUserinfo') &&
+                $details = $message->getUserinfo()) {
+                $this->details = $details;
+            } elseif (!empty($message->details)) {
+                $this->details = $message->details;
+            }
+            $message = (string)$message->getMessage();
+        }
+
+        /* Modified for WordPress inclusion:
+            The "previous" argument was added in PHP 5.3. */
+        if( version_compare( PHP_VERSION, '5.3' ) < 0 ) {
+            parent::__construct($message, $code);
+        }
+        else {
+            parent::__construct($message, $code, $previous);
+        }
+    }
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeExceptionWrappedphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Exception-Wrapped.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Exception-Wrapped.php   2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Exception-Wrapped.php      2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,53 +0,0 @@
</span><del>-<?php
-/**
- * Horde exception class that can wrap and set its details from PEAR_Error,
- * Exception, and other objects with similar interfaces.
- *
- * Copyright 2008-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category Horde
- * @package  Exception
- */
-class Horde_Exception_Wrapped extends Horde_Exception
-{
-    /**
-     * Exception constructor.
-     *
-     * @param mixed $message The exception message, a PEAR_Error
-     *                       object, or an Exception object.
-     * @param int   $code    A numeric error code.
-     */
-    public function __construct($message = null, $code = 0)
-    {
-        $previous = null;
-        if (is_object($message) &&
-            method_exists($message, 'getMessage')) {
-            if (empty($code) &&
-                method_exists($message, 'getCode')) {
-                $code = (int)$message->getCode();
-            }
-            if ($message instanceof Exception) {
-                $previous = $message;
-            }
-            if (method_exists($message, 'getUserinfo') &&
-                $details = $message->getUserinfo()) {
-                $this->details = $details;
-            } elseif (!empty($message->details)) {
-                $this->details = $message->details;
-            }
-            $message = (string)$message->getMessage();
-        }
-
-        /* Modified for WordPress inclusion:
-            The "previous" argument was added in PHP 5.3. */
-        if( version_compare( PHP_VERSION, '5.3' ) < 0 ) {
-            parent::__construct($message, $code);
-        }
-        else {
-            parent::__construct($message, $code, $previous);
-        }
-    }
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeHordeUtilphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Horde-Util.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Horde-Util.php  2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Horde-Util.php     2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,564 +0,0 @@
</span><del>-<?php
-/**
- * The Horde_Util:: class provides generally useful methods.
- *
- * Copyright 1999-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @author   Chuck Hagenbuch <chuck@horde.org>
- * @author   Jon Parise <jon@horde.org>
- * @category Horde
- * @license  http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package  Util
- */
-class Horde_Util
-{
-    /**
-     * A list of random patterns to use for overwriting purposes.
-     * See http://www.cs.auckland.ac.nz/~pgut001/pubs/secure_del.html.
-     * We save the random overwrites for efficiency reasons.
-     *
-     * @var array
-     */
-    static public $patterns = array(
-        "\x55", "\xaa", "\x92\x49\x24", "\x49\x24\x92", "\x24\x92\x49",
-        "\x00", "\x11", "\x22", "\x33", "\x44", "\x55", "\x66", "\x77",
-        "\x88", "\x99", "\xaa", "\xbb", "\xcc", "\xdd", "\xee", "\xff",
-        "\x92\x49\x24", "\x49\x24\x92", "\x24\x92\x49", "\x6d\xb6\xdb",
-        "\xb6\xdb\x6d", "\xdb\x6d\xb6"
-    );
-
-    /**
-     * Are magic quotes in use?
-     *
-     * @var boolean
-     */
-    static protected $_magicquotes = null;
-
-    /**
-     * TODO
-     *
-     * @var array
-     */
-    static protected $_shutdowndata = array(
-        'dirs' => array(),
-        'files' => array(),
-        'secure' => array()
-    );
-
-    /**
-     * Has the shutdown method been registered?
-     *
-     * @var boolean
-     */
-    static protected $_shutdownreg = false;
-
-    /**
-     * Cache for extensionExists().
-     *
-     * @var array
-     */
-    static protected $_cache = array();
-
-    /**
-     * Checks to see if a value has been set by the script and not by GET,
-     * POST, or cookie input. The value being checked MUST be in the global
-     * scope.
-     *
-     * @param string $varname  The variable name to check.
-     * @param mixed $default   Default value if the variable isn't present
-     *                         or was specified by the user. Defaults to null.
-     *
-     * @return mixed  $default if the var is in user input or not present,
-     *                the variable value otherwise.
-     */
-    static public function nonInputVar($varname, $default = null)
-    {
-        return (isset($_GET[$varname]) || isset($_POST[$varname]) || isset($_COOKIE[$varname]))
-            ? $default
-            : (isset($GLOBALS[$varname]) ? $GLOBALS[$varname] : $default);
-    }
-
-    /**
-     * Returns a hidden form input containing the session name and id.
-     *
-     * @param boolean $append_session  0 = only if needed, 1 = always.
-     *
-     * @return string  The hidden form input, if needed/requested.
-     */
-    static public function formInput($append_session = 0)
-    {
-        return (($append_session == 1) || !isset($_COOKIE[session_name()]))
-            ? '<input type="hidden" name="' . htmlspecialchars(session_name()) . '" value="' . htmlspecialchars(session_id()) . "\" />\n"
-            : '';
-    }
-
-    /**
-     * Prints a hidden form input containing the session name and id.
-     *
-     * @param boolean $append_session  0 = only if needed, 1 = always.
-     */
-    static public function pformInput($append_session = 0)
-    {
-        echo self::formInput($append_session);
-    }
-
-    /**
-     * If magic_quotes_gpc is in use, run stripslashes() on $var.
-     *
-     * @param mixed $var  The string, or an array of strings, to un-quote.
-     *
-     * @return mixed  $var, minus any magic quotes.
-     */
-    static public function dispelMagicQuotes($var)
-    {
-        if (is_null(self::$_magicquotes)) {
-            self::$_magicquotes = get_magic_quotes_gpc();
-        }
-
-        if (self::$_magicquotes) {
-            $var = is_array($var)
-                ? array_map(array(__CLASS__, 'dispelMagicQuotes'), $var)
-                : stripslashes($var);
-        }
-
-        return $var;
-    }
-
-    /**
-     * Gets a form variable from GET or POST data, stripped of magic quotes if
-     * necessary. If the variable is somehow set in both the GET data and the
-     * POST data, the value from the POST data will be returned and the GET
-     * value will be ignored.
-     *
-     * @param string $var      The name of the form variable to look for.
-     * @param string $default  The value to return if the variable is not
-     *                         there.
-     *
-     * @return string  The cleaned form variable, or $default.
-     */
-    static public function getFormData($var, $default = null)
-    {
-        return (($val = self::getPost($var)) !== null)
-            ? $val
-            : self::getGet($var, $default);
-    }
-
-    /**
-     * Gets a form variable from GET data, stripped of magic quotes if
-     * necessary. This function will NOT return a POST variable.
-     *
-     * @param string $var      The name of the form variable to look for.
-     * @param string $default  The value to return if the variable is not
-     *                         there.
-     *
-     * @return string  The cleaned form variable, or $default.
-     */
-    static public function getGet($var, $default = null)
-    {
-        return (isset($_GET[$var]))
-            ? self::dispelMagicQuotes($_GET[$var])
-            : $default;
-    }
-
-    /**
-     * Gets a form variable from POST data, stripped of magic quotes if
-     * necessary. This function will NOT return a GET variable.
-     *
-     * @param string $var      The name of the form variable to look for.
-     * @param string $default  The value to return if the variable is not
-     *                         there.
-     *
-     * @return string  The cleaned form variable, or $default.
-     */
-    static public function getPost($var, $default = null)
-    {
-        return (isset($_POST[$var]))
-            ? self::dispelMagicQuotes($_POST[$var])
-            : $default;
-    }
-
-    /**
-     * Creates a temporary filename for the lifetime of the script, and
-     * (optionally) registers it to be deleted at request shutdown.
-     *
-     * @param string $prefix   Prefix to make the temporary name more
-     *                         recognizable.
-     * @param boolean $delete  Delete the file at the end of the request?
-     * @param string $dir      Directory to create the temporary file in.
-     * @param boolean $secure  If deleting the file, should we securely delete
-     *                         the file by overwriting it with random data?
-     *
-     * @return string   Returns the full path-name to the temporary file.
-     *                  Returns false if a temp file could not be created.
-     */
-    static public function getTempFile($prefix = '', $delete = true, $dir = '',
-                                       $secure = false)
-    {
-        $tempDir = (empty($dir) || !is_dir($dir))
-            ? sys_get_temp_dir()
-            : $dir;
-
-        $tempFile = tempnam($tempDir, $prefix);
-
-        // If the file was created, then register it for deletion and return.
-        if (empty($tempFile)) {
-            return false;
-        }
-
-        if ($delete) {
-            self::deleteAtShutdown($tempFile, true, $secure);
-        }
-
-        return $tempFile;
-    }
-
-    /**
-     * Creates a temporary filename with a specific extension for the lifetime
-     * of the script, and (optionally) registers it to be deleted at request
-     * shutdown.
-     *
-     * @param string $extension  The file extension to use.
-     * @param string $prefix     Prefix to make the temporary name more
-     *                           recognizable.
-     * @param boolean $delete    Delete the file at the end of the request?
-     * @param string $dir        Directory to create the temporary file in.
-     * @param boolean $secure    If deleting file, should we securely delete
-     *                           the file by overwriting it with random data?
-     *
-     * @return string   Returns the full path-name to the temporary file.
-     *                  Returns false if a temporary file could not be created.
-     */
-    static public function getTempFileWithExtension($extension = '.tmp',
-                                                    $prefix = '',
-                                                    $delete = true, $dir = '',
-                                                    $secure = false)
-    {
-        $tempDir = (empty($dir) || !is_dir($dir))
-            ? sys_get_temp_dir()
-            : $dir;
-
-        if (empty($tempDir)) {
-            return false;
-        }
-
-        $windows = substr(PHP_OS, 0, 3) == 'WIN';
-        $tries = 1;
-        do {
-            // Get a known, unique temporary file name.
-            $sysFileName = tempnam($tempDir, $prefix);
-            if ($sysFileName === false) {
-                return false;
-            }
-
-            // tack on the extension
-            $tmpFileName = $sysFileName . $extension;
-            if ($sysFileName == $tmpFileName) {
-                return $sysFileName;
-            }
-
-            // Move or point the created temporary file to the full filename
-            // with extension. These calls fail if the new name already
-            // exists.
-            $fileCreated = ($windows ? @rename($sysFileName, $tmpFileName) : @link($sysFileName, $tmpFileName));
-            if ($fileCreated) {
-                if (!$windows) {
-                    unlink($sysFileName);
-                }
-
-                if ($delete) {
-                    self::deleteAtShutdown($tmpFileName, true, $secure);
-                }
-
-                return $tmpFileName;
-            }
-
-            unlink($sysFileName);
-        } while (++$tries <= 5);
-
-        return false;
-    }
-
-    /**
-     * Creates a temporary directory in the system's temporary directory.
-     *
-     * @param boolean $delete   Delete the temporary directory at the end of
-     *                          the request?
-     * @param string $temp_dir  Use this temporary directory as the directory
-     *                          where the temporary directory will be created.
-     *
-     * @return string  The pathname to the new temporary directory.
-     *                 Returns false if directory not created.
-     */
-    static public function createTempDir($delete = true, $temp_dir = null)
-    {
-        if (is_null($temp_dir)) {
-            $temp_dir = sys_get_temp_dir();
-        }
-
-        if (empty($temp_dir)) {
-            return false;
-        }
-
-        /* Get the first 8 characters of a random string to use as a temporary
-           directory name. */
-        do {
-            $new_dir = $temp_dir . '/' . substr(base_convert(uniqid(mt_rand()), 10, 36), 0, 8);
-        } while (file_exists($new_dir));
-
-        $old_umask = umask(0000);
-        if (!mkdir($new_dir, 0700)) {
-            $new_dir = false;
-        } elseif ($delete) {
-            self::deleteAtShutdown($new_dir);
-        }
-        umask($old_umask);
-
-        return $new_dir;
-    }
-
-    /**
-     * Returns the canonical path of the string.  Like PHP's built-in
-     * realpath() except the directory need not exist on the local server.
-     *
-     * Algorithim loosely based on code from the Perl File::Spec::Unix module
-     * (version 1.5).
-     *
-     * @param string $path  A file path.
-     *
-     * @return string  The canonicalized file path.
-     */
-    static public function realPath($path)
-    {
-        /* Standardize on UNIX directory separators. */
-        if (!strncasecmp(PHP_OS, 'WIN', 3)) {
-            $path = str_replace('\\', '/', $path);
-        }
-
-        /* xx////xx -> xx/xx
-         * xx/././xx -> xx/xx */
-        $path = preg_replace(array("|/+|", "@(/\.)+(/|\Z(?!\n))@"), array('/', '/'), $path);
-
-        /* ./xx -> xx */
-        if ($path != './') {
-            $path = preg_replace("|^(\./)+|", '', $path);
-        }
-
-        /* /../../xx -> xx */
-        $path = preg_replace("|^/(\.\./?)+|", '/', $path);
-
-        /* xx/ -> xx */
-        if ($path != '/') {
-            $path = preg_replace("|/\Z(?!\n)|", '', $path);
-        }
-
-        /* /xx/.. -> / */
-        while (strpos($path, '/..') !== false) {
-            $path = preg_replace("|/[^/]+/\.\.|", '', $path);
-        }
-
-        return empty($path) ? '/' : $path;
-    }
-
-    /**
-     * Removes given elements at request shutdown.
-     *
-     * If called with a filename will delete that file at request shutdown; if
-     * called with a directory will remove that directory and all files in that
-     * directory at request shutdown.
-     *
-     * If called with no arguments, return all elements to be deleted (this
-     * should only be done by Horde_Util::_deleteAtShutdown()).
-     *
-     * The first time it is called, it initializes the array and registers
-     * Horde_Util::_deleteAtShutdown() as a shutdown function - no need to do
-     * so manually.
-     *
-     * The second parameter allows the unregistering of previously registered
-     * elements.
-     *
-     * @param string $filename   The filename to be deleted at the end of the
-     *                           request.
-     * @param boolean $register  If true, then register the element for
-     *                           deletion, otherwise, unregister it.
-     * @param boolean $secure    If deleting file, should we securely delete
-     *                           the file?
-     */
-    static public function deleteAtShutdown($filename, $register = true,
-                                            $secure = false)
-    {
-        /* Initialization of variables and shutdown functions. */
-        if (!self::$_shutdownreg) {
-            register_shutdown_function(array(__CLASS__, 'shutdown'));
-            self::$_shutdownreg = true;
-        }
-
-        $ptr = &self::$_shutdowndata;
-        if ($register) {
-            if (@is_dir($filename)) {
-                $ptr['dirs'][$filename] = true;
-            } else {
-                $ptr['files'][$filename] = true;
-            }
-
-            if ($secure) {
-                $ptr['secure'][$filename] = true;
-            }
-        } else {
-            unset($ptr['dirs'][$filename], $ptr['files'][$filename], $ptr['secure'][$filename]);
-        }
-    }
-
-    /**
-     * Deletes registered files at request shutdown.
-     *
-     * This function should never be called manually; it is registered as a
-     * shutdown function by Horde_Util::deleteAtShutdown() and called
-     * automatically at the end of the request.
-     *
-     * Contains code from gpg_functions.php.
-     * Copyright 2002-2003 Braverock Ventures
-     */
-    static public function shutdown()
-    {
-        $ptr = &self::$_shutdowndata;
-
-        foreach ($ptr['files'] as $file => $val) {
-            /* Delete files */
-            if ($val && file_exists($file)) {
-                /* Should we securely delete the file by overwriting the data
-                   with a random string? */
-                if (isset($ptr['secure'][$file])) {
-                    $filesize = filesize($file);
-                    $fp = fopen($file, 'r+');
-                    foreach (self::$patterns as $pattern) {
-                        $pattern = substr(str_repeat($pattern, floor($filesize / strlen($pattern)) + 1), 0, $filesize);
-                        fwrite($fp, $pattern);
-                        fseek($fp, 0);
-                    }
-                    fclose($fp);
-                }
-                @unlink($file);
-            }
-        }
-
-        foreach ($ptr['dirs'] as $dir => $val) {
-            /* Delete directories */
-            if ($val && file_exists($dir)) {
-                /* Make sure directory is empty. */
-                $dir_class = dir($dir);
-                while (false !== ($entry = $dir_class->read())) {
-                    if ($entry != '.' && $entry != '..') {
-                        @unlink($dir . '/' . $entry);
-                    }
-                }
-                $dir_class->close();
-                @rmdir($dir);
-            }
-        }
-    }
-
-    /**
-     * Caches the result of extension_loaded() calls.
-     *
-     * @param string $ext  The extension name.
-     *
-     * @return boolean  Is the extension loaded?
-     */
-    static public function extensionExists($ext)
-    {
-        if (!isset(self::$_cache[$ext])) {
-            self::$_cache[$ext] = extension_loaded($ext);
-        }
-
-        return self::$_cache[$ext];
-    }
-
-    /**
-     * Tries to load a PHP extension, behaving correctly for all operating
-     * systems.
-     *
-     * @param string $ext  The extension to load.
-     *
-     * @return boolean  True if the extension is now loaded, false if not.
-     *                  True can mean that the extension was already loaded,
-     *                  OR was loaded dynamically.
-     */
-    static public function loadExtension($ext)
-    {
-        /* If $ext is already loaded, our work is done. */
-        if (self::extensionExists($ext)) {
-            return true;
-        }
-
-        /* See if we can call dl() at all, by the current ini settings.
-         * dl() has been removed in some PHP 5.3 SAPIs. */
-        if ((ini_get('enable_dl') != 1) ||
-            (ini_get('safe_mode') == 1) ||
-            !function_exists('dl')) {
-            return false;
-        }
-
-        if (!strncasecmp(PHP_OS, 'WIN', 3)) {
-            $suffix = 'dll';
-        } else {
-            switch (PHP_OS) {
-            case 'HP-UX':
-                $suffix = 'sl';
-                break;
-
-            case 'AIX':
-                $suffix = 'a';
-                break;
-
-            case 'OSX':
-                $suffix = 'bundle';
-                break;
-
-            default:
-                $suffix = 'so';
-            }
-        }
-
-        return dl($ext . '.' . $suffix) || dl('php_' . $ext . '.' . $suffix);
-    }
-
-    /**
-     * Utility function to obtain PATH_INFO information.
-     *
-     * @return string  The PATH_INFO string.
-     */
-    static public function getPathInfo()
-    {
-        if (isset($_SERVER['PATH_INFO']) &&
-            (strpos($_SERVER['SERVER_SOFTWARE'], 'lighttpd') === false)) {
-            return $_SERVER['PATH_INFO'];
-        } elseif (isset($_SERVER['REQUEST_URI']) &&
-                  isset($_SERVER['SCRIPT_NAME'])) {
-            $search = Horde_String::common($_SERVER['SCRIPT_NAME'], $_SERVER['REQUEST_URI']);
-            if (substr($search, -1) == '/') {
-                $search = substr($search, 0, -1);
-            }
-            $search = array($search);
-            if (!empty($_SERVER['QUERY_STRING'])) {
-                // We can't use QUERY_STRING directly because URL rewriting
-                // might add more parameters to the query string than those
-                // from the request URI.
-                $url = parse_url($_SERVER['REQUEST_URI']);
-                if (!empty($url['query'])) {
-                    $search[] = '?' . $url['query'];
-                }
-            }
-            $path = str_replace($search, '', $_SERVER['REQUEST_URI']);
-            if ($path == '/') {
-                $path = '';
-            }
-            return $path;
-        }
-
-        return '';
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientAuthDigestMD5phpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientAuthDigestMD5php"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Auth/DigestMD5.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Auth/DigestMD5.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Auth/DigestMD5.php                          (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Auth/DigestMD5.php     2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,186 @@
</span><ins>+<?php
+/**
+ * Copyright (c) 2002-2003 Richard Heyes
+ * Copyright 2011-2013 Horde LLC (http://www.horde.org/)
+ *
+ * This code is based on the original code contained in the PEAR Auth_SASL
+ * package (v0.5.1):
+ *   $Id: DigestMD5.php 294702 2010-02-07 16:03:55Z cweiske $
+ *
+ * That code is covered by the BSD 3-Clause license, as set forth below:
+ * +-----------------------------------------------------------------------+
+ * | 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.  |
+ * +-----------------------------------------------------------------------+
+ *
+ * @category  Horde
+ * @copyright 2002-2003 Richard Heyes
+ * @copyright 2011-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Provides the code needed to authenticate via the DIGEST-MD5 SASL mechanism
+ * (defined in RFC 2831). This method has been obsoleted by RFC 6331, but
+ * still is in use on legacy servers.
+ *
+ * @author    Richard Heyes <richard@php.net>
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @copyright 2002-2003 Richard Heyes
+ * @copyright 2011-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Auth_DigestMD5
+{
+    /**
+     * Digest response components.
+     *
+     * @var string
+     */
+    protected $_response;
+
+    /**
+     * Generate the Digest-MD5 response.
+     *
+     * @param string $id         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 connecting to.
+     * @param string $service    The service name (e.g. 'imap', 'pop3').
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function __construct($id, $pass, $challenge, $hostname, $service)
+    {
+        $challenge = $this->_parseChallenge($challenge);
+        $cnonce = $this->_getCnonce();
+        $digest_uri = sprintf('%s/%s', $service, $hostname);
+
+        /* Get response value. */
+        $A1 = sprintf('%s:%s:%s', pack('H32', hash('md5', sprintf('%s:%s:%s', $id, $challenge['realm'], $pass))), $challenge['nonce'], $cnonce);
+        $A2 = 'AUTHENTICATE:' . $digest_uri;
+        $response_value = hash('md5', sprintf('%s:%s:00000001:%s:auth:%s', hash('md5', $A1), $challenge['nonce'], $cnonce, hash('md5', $A2)));
+
+        $this->_response = array(
+            'cnonce' => '"' . $cnonce . '"',
+            'digest-uri' => '"' . $digest_uri . '"',
+            'maxbuf' => $challenge['maxbuf'],
+            'nc' => '00000001',
+            'nonce' => '"' . $challenge['nonce'] . '"',
+            'qop' => 'auth',
+            'response' => $response_value,
+            'username' => '"' . $id . '"'
+        );
+
+        if (strlen($challenge['realm'])) {
+            $this->_response['realm'] = '"' . $challenge['realm'] . '"';
+        }
+    }
+
+    /**
+     * Cooerce to string.
+     *
+     * @return string  The digest response (not base64 encoded).
+     */
+    public function __toString()
+    {
+        $out = array();
+        foreach ($this->_response as $key => $val) {
+            $out[] = $key . '=' . $val;
+        }
+        return implode(',', $out);
+    }
+
+    /**
+     * Return specific digest response directive.
+     *
+     * @return mixed  Requested directive, or null if it does not exist.
+     */
+    public function __get($name)
+    {
+        return isset($this->_response[$name])
+            ? $this->_response[$name]
+            : null;
+    }
+
+    /**
+    * Parses and verifies the digest challenge.
+    *
+    * @param string $challenge  The digest challenge
+    *
+    * @return array  The parsed challenge as an array with directives as keys.
+    *
+    * @throws Horde_Imap_Client_Exception
+    */
+    protected function _parseChallenge($challenge)
+    {
+        $tokens = array(
+            'maxbuf' => 65536,
+            'realm' => ''
+        );
+
+        preg_match_all('/([a-z-]+)=("[^"]+(?<!\\\)"|[^,]+)/i', $challenge, $matches, PREG_SET_ORDER);
+
+        foreach ($matches as $val) {
+            $tokens[$val[1]] = trim($val[2], '"');
+        }
+
+        // Required directives.
+        if (!isset($tokens['nonce']) || !isset($tokens['algorithm'])) {
+            throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::t("Authentication failure."), 'SERVER_CONNECT');
+        }
+
+        return $tokens;
+    }
+
+    /**
+     * Creates the client nonce for the response
+     *
+     * @return string  The cnonce value.
+     */
+    protected function _getCnonce()
+    {
+        if ((@is_readable('/dev/urandom') &&
+             ($fd = @fopen('/dev/urandom', 'r'))) ||
+            (@is_readable('/dev/random') &&
+             ($fd = @fopen('/dev/random', 'r')))) {
+            $str = fread($fd, 32);
+            fclose($fd);
+        } else {
+            $str = '';
+            for ($i = 0; $i < 32; ++$i) {
+                $str .= chr(mt_rand(0, 255));
+            }
+        }
+
+        return base64_encode($str);
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientBaseConnectionphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientBaseConnectionphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Base/Connection.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Base/Connection.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Base/Connection.php                         (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Base/Connection.php    2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,112 @@
</span><ins>+<?php
+/**
+ * Copyright 2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Abstract interface for the object used to connect to remote mail server.
+ *
+ * NOTE: This class is NOT intended to be accessed outside of the package.
+ * There is NO guarantees that the API of this class will not change across
+ * versions.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2013 Horde LLC
+ * @internal
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ *
+ * @property-read boolean $connected  Is there a connection to the server?
+ * @property-read boolean $secure  Is the connection secure?
+ */
+abstract class Horde_Imap_Client_Base_Connection
+{
+    /**
+     * Is there a connection to the server?
+     *
+     * @var boolean
+     */
+    protected $_connected = false;
+
+    /**
+     * Debug object.
+     *
+     * @var object
+     */
+    protected $_debug;
+
+    /**
+     * Is the connection secure?
+     *
+     * @var boolean
+     */
+    protected $_secure = false;
+
+    /**
+     * Constructor.
+     *
+     * @param Horde_Imap_Client_Base $base  The base client object.
+     * @param object $debug                 The debug handler.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function __construct(Horde_Imap_Client_Base $base, $debug)
+    {
+        if ($base->getParam('secure') && !extension_loaded('openssl')) {
+            throw new InvalidArgumentException('Secure connections require the PHP openssl extension.');
+        }
+
+        $this->_debug = $debug;
+    }
+
+    /**
+     */
+    public function __get($name)
+    {
+        switch ($name) {
+        case 'connected':
+            return $this->_connected;
+
+        case 'secure':
+            return $this->_secure;
+        }
+    }
+
+    /**
+     * This object can not be cloned.
+     */
+    public function __clone()
+    {
+        throw new LogicException('Object cannot be cloned.');
+    }
+
+    /**
+     * This object can not be serialized.
+     */
+    public function __sleep()
+    {
+        throw new LogicException('Object can not be serialized.');
+    }
+
+    /**
+     * Start a TLS connection to the server.
+     *
+     * @return boolean  Whether TLS was successfully started.
+     */
+    abstract public function startTls();
+
+    /**
+     * Close the connection to the server.
+     */
+    abstract public function close();
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientBaseDebugphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientBaseDebugphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Base/Debug.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Base/Debug.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Base/Debug.php                              (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Base/Debug.php 2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,152 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * An object allowing management of debugging output within a
+ * Horde_Imap_Client_Base object.
+ *
+ * NOTE: This class is NOT intended to be accessed outside of a Base object.
+ * There is NO guarantees that the API of this class will not change across
+ * versions.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @internal
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Base_Debug
+{
+    /* Time, in seconds, to be labeled a slow command. */
+    const SLOW_CMD = 5;
+
+    /**
+     * Is debugging active?
+     *
+     * @var boolean
+     */
+    public $debug = true;
+
+    /**
+     * The debug stream.
+     *
+     * @var resource
+     */
+    protected $_stream;
+
+    /**
+     * Timestamp of last command.
+     *
+     * @var integer
+     */
+    protected $_time = null;
+
+    /**
+     * Constructor.
+     *
+     * @param mixed $debug  The debug target.
+     */
+    public function __construct($debug)
+    {
+        $this->_stream = is_resource($debug)
+            ? $debug
+            : @fopen($debug, 'a');
+        register_shutdown_function(array($this, 'shutdown'));
+    }
+
+    /**
+     * Shutdown function.
+     */
+    public function shutdown()
+    {
+        if (is_resource($this->_stream)) {
+            fflush($this->_stream);
+            fclose($this->_stream);
+            $this->_stream = null;
+        }
+    }
+
+    /**
+     * Write client output to debug log.
+     *
+     * @param string $msg  Debug message.
+     */
+    public function client($msg)
+    {
+        $this->_write($msg . "\n", 'C: ');
+    }
+
+    /**
+     * Write informational message to debug log.
+     *
+     * @param string $msg  Debug message.
+     */
+    public function info($msg)
+    {
+        $this->_write($msg . "\n", '>> ');
+    }
+
+    /**
+     * Write server output to debug log.
+     *
+     * @param string $msg  Debug message.
+     */
+    public function raw($msg)
+    {
+        $this->_write($msg);
+    }
+
+    /**
+     * Write server output to debug log.
+     *
+     * @param string $msg  Debug message.
+     */
+    public function server($msg)
+    {
+        $this->_write($msg . "\n", 'S: ');
+    }
+
+    /**
+     * Write debug information to the output stream.
+     *
+     * @param string $msg  Debug data.
+     */
+    protected function _write($msg, $pre = null)
+    {
+        if (!$this->debug || !$this->_stream) {
+            return;
+        }
+
+        if (!is_null($pre)) {
+            $new_time = microtime(true);
+
+            if (is_null($this->_time)) {
+                fwrite(
+                    $this->_stream,
+                    str_repeat('-', 30) . "\n" . '>> ' . date('r') . "\n"
+                );
+            } elseif (($diff = ($new_time - $this->_time)) > self::SLOW_CMD) {
+                fwrite(
+                    $this->_stream,
+                    '>> Slow Command: ' . round($diff, 3) . " seconds\n"
+                );
+            }
+
+            $this->_time = $new_time;
+        }
+
+        fwrite($this->_stream, $pre . $msg);
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientBaseDeprecatedphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientBaseDeprecatedphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Base/Deprecated.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Base/Deprecated.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Base/Deprecated.php                         (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Base/Deprecated.php    2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,109 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Class containing deprecated Horde_Imap_Client_Base methods that will be
+ * removed in version 3.0.
+ *
+ * NOTE: This class is NOT intended to be accessed outside of a Base object.
+ * There is NO guarantees that the API of this class will not change across
+ * versions.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @internal
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Base_Deprecated
+{
+    /**
+     * Returns a unique identifier for the current mailbox status.
+     *
+     * @param Horde_Imap_Client_Base $base_ob  The base driver object.
+     * @param mixed $mailbox                   A mailbox. Either a
+     *                                         Horde_Imap_Client_Mailbox
+     *                                         object or a string (UTF-8).
+     * @param boolean $condstore               Is CONDSTORE enabled?
+     * @param array $addl                      Additional cache info to add to
+     *                                         the cache ID string.
+     *
+     * @return string  The cache ID string, which will change when the
+     *                 composition of the mailbox changes. The uidvalidity
+     *                 will always be the first element, and will be delimited
+     *                 by the '|' character.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    static public function getCacheId($base_ob, $mailbox, $condstore,
+                                      array $addl = array())
+    {
+        $query = Horde_Imap_Client::STATUS_UIDVALIDITY | Horde_Imap_Client::STATUS_MESSAGES | Horde_Imap_Client::STATUS_UIDNEXT;
+
+        /* Use MODSEQ as cache ID if CONDSTORE extension is available. */
+        if ($condstore) {
+            $query |= Horde_Imap_Client::STATUS_HIGHESTMODSEQ;
+        } else {
+            $query |= Horde_Imap_Client::STATUS_UIDNEXT_FORCE;
+        }
+
+        $status = $base_ob->status($mailbox, $query);
+
+        if (empty($status['highestmodseq'])) {
+            $parts = array(
+                'V' . $status['uidvalidity'],
+                'U' . $status['uidnext'],
+                'M' . $status['messages']
+            );
+        } else {
+            $parts = array(
+                'V' . $status['uidvalidity'],
+                'H' . $status['highestmodseq']
+            );
+        }
+
+        return implode('|', array_merge($parts, $addl));
+    }
+
+    /**
+     * Parses a cacheID created by getCacheId().
+     *
+     * @param string $id  The cache ID.
+     *
+     * @return array  An array with the following information:
+     *   - highestmodseq: (integer)
+     *   - messages: (integer)
+     *   - uidnext: (integer)
+     *   - uidvalidity: (integer) Always present
+     */
+    static public function parseCacheId($id)
+    {
+        $data = array(
+            'H' => 'highestmodseq',
+            'M' => 'messages',
+            'U' => 'uidnext',
+            'V' => 'uidvalidity'
+        );
+        $info = array();
+
+        foreach (explode('|', $id) as $part) {
+            if (isset($data[$part[0]])) {
+                $info[$data[$part[0]]] = intval(substr($part, 1));
+            }
+        }
+
+        return $info;
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientBaseMailboxphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientBaseMailboxphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Base/Mailbox.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Base/Mailbox.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Base/Mailbox.php                            (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Base/Mailbox.php       2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,176 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * An object allowing management of mailbox state within a
+ * Horde_Imap_Client_Base object.
+ *
+ * NOTE: This class is NOT intended to be accessed outside of a Base object.
+ * There is NO guarantees that the API of this class will not change across
+ * versions.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @internal
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Base_Mailbox
+{
+    /**
+     * Mapping object.
+     *
+     * @var Horde_Imap_Client_Ids_Map
+     */
+    public $map;
+
+    /**
+     * Is mailbox opened?
+     *
+     * @var boolean
+     */
+    public $open;
+
+    /**
+     * Is mailbox sync'd with remote server (via CONDSTORE/QRESYNC)?
+     *
+     * @var boolean
+     */
+    public $sync;
+
+    /**
+     * Status information.
+     *
+     * @var array
+     */
+    protected $_status = array();
+
+    /**
+     * Constructor.
+     */
+    public function __construct()
+    {
+        $this->reset();
+    }
+
+    /**
+     * Get status information for the mailbox.
+     *
+     * @param integer $entry  STATUS_* constant.
+     *
+     * @return mixed  Status information.
+     */
+    public function getStatus($entry)
+    {
+        if (isset($this->_status[$entry])) {
+            return $this->_status[$entry];
+        }
+
+        switch ($entry) {
+        case Horde_Imap_Client::STATUS_FIRSTUNSEEN:
+            /* If we know there are no messages in the current mailbox, we
+             * know there are no unseen messages. */
+            return empty($this->_status[Horde_Imap_Client::STATUS_MESSAGES])
+                ? false
+                : null;
+
+        case Horde_Imap_Client::STATUS_RECENT_TOTAL:
+        case Horde_Imap_Client::STATUS_SYNCMODSEQ:
+            return 0;
+
+        case Horde_Imap_Client::STATUS_SYNCFLAGUIDS:
+        case Horde_Imap_Client::STATUS_SYNCVANISHED:
+            return array();
+
+        case Horde_Imap_Client::STATUS_PERMFLAGS:
+            /* If PERMFLAGS is not returned by server, must assume that all
+             * flags can be change permanently (RFC 3501 [6.3.1]). */
+            $flags = isset($this->_status[Horde_Imap_Client::STATUS_FLAGS])
+                ? $this->_status[Horde_Imap_Client::STATUS_FLAGS]
+                : array();
+            $flags[] = "\\*";
+            return $flags;
+
+        case Horde_Imap_Client::STATUS_UIDNOTSTICKY:
+            /* In the absence of explicit uidnotsticky identification, assume
+             * that UIDs are sticky. */
+            return false;
+
+        case Horde_Imap_Client::STATUS_UNSEEN:
+            /* If we know there are no messages in the current mailbox, we
+             * know there are no unseen messages . */
+            return empty($this->_status[Horde_Imap_Client::STATUS_MESSAGES])
+                ? 0
+                : null;
+
+        default:
+            return null;
+        }
+    }
+
+    /**
+     * Set status information for the mailbox.
+     *
+     * @param integer $entry  STATUS_* constant.
+     * @param mixed $value    Status information.
+     */
+    public function setStatus($entry, $value)
+    {
+        switch ($entry) {
+        case Horde_Imap_Client::STATUS_RECENT:
+            /* Keep track of RECENT_TOTAL information. */
+            $this->_status[Horde_Imap_Client::STATUS_RECENT_TOTAL] = isset($this->_status[Horde_Imap_Client::STATUS_RECENT_TOTAL])
+                ? ($this->_status[Horde_Imap_Client::STATUS_RECENT_TOTAL] + $value)
+                : $value;
+            break;
+
+        case Horde_Imap_Client::STATUS_SYNCMODSEQ:
+            /* This is only set once per access. */
+            if (isset($this->_status[$entry])) {
+                return;
+            }
+            break;
+
+        case Horde_Imap_Client::STATUS_SYNCFLAGUIDS:
+        case Horde_Imap_Client::STATUS_SYNCVANISHED:
+            if (!isset($this->_status[$entry])) {
+                $this->_status[$entry] = array();
+            }
+            $this->_status[$entry] = array_merge($this->_status[$entry], $value);
+            return;
+        }
+
+        $this->_status[$entry] = $value;
+    }
+
+    /**
+     * Reset the mailbox information.
+     */
+    public function reset()
+    {
+        $keep = array(
+            Horde_Imap_Client::STATUS_SYNCFLAGUIDS,
+            Horde_Imap_Client::STATUS_SYNCMODSEQ,
+            Horde_Imap_Client::STATUS_SYNCVANISHED
+        );
+
+        foreach (array_diff(array_keys($this->_status), $keep) as $val) {
+            unset($this->_status[$val]);
+        }
+
+        $this->map = new Horde_Imap_Client_Ids_Map();
+        $this->open = $this->sync = false;
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientBasephpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientBasephp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Base.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Base.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Base.php                            (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Base.php       2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,4103 @@
</span><ins>+<?php
+/**
+ * Copyright 2008-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2008-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * An abstracted API interface to IMAP backends supporting the IMAP4rev1
+ * protocol (RFC 3501).
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2008-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+abstract class Horde_Imap_Client_Base implements Serializable
+{
+    /* Serialized version. */
+    const VERSION = 2;
+
+    /* Cache names for miscellaneous data. */
+    const CACHE_MODSEQ = '_m';
+    const CACHE_SEARCH = '_s';
+    /* @since 2.9.0 */
+    const CACHE_SEARCHID = '_i';
+
+    /* Cache names used exclusively within this class. @since 2.11.0 */
+    const CACHE_DOWNGRADED = 'HICdg';
+
+    /**
+     * The list of fetch fields that can be cached, and their cache names.
+     *
+     * @var array
+     */
+    public $cacheFields = array(
+        Horde_Imap_Client::FETCH_ENVELOPE => 'HICenv',
+        Horde_Imap_Client::FETCH_FLAGS => 'HICflags',
+        Horde_Imap_Client::FETCH_HEADERS => 'HIChdrs',
+        Horde_Imap_Client::FETCH_IMAPDATE => 'HICdate',
+        Horde_Imap_Client::FETCH_SIZE => 'HICsize',
+        Horde_Imap_Client::FETCH_STRUCTURE => 'HICstruct'
+    );
+
+    /**
+     * Has the internal configuration changed?
+     *
+     * @var boolean
+     */
+    public $changed = false;
+
+    /**
+     * The Horde_Imap_Client_Cache object.
+     *
+     * @var Horde_Imap_Client_Cache
+     */
+    protected $_cache = null;
+
+    /**
+     * Connection to the IMAP server.
+     *
+     * @var Horde_Imap_Client_Base_Connection
+     */
+    protected $_connection = null;
+
+    /**
+     * The debug object.
+     *
+     * @var Horde_Imap_Client_Base_Debug
+     */
+    protected $_debug = null;
+
+    /**
+     * The fetch data object type to return.
+     *
+     * @var string
+     */
+    protected $_fetchDataClass = 'Horde_Imap_Client_Data_Fetch';
+
+    /**
+     * Cached server data.
+     *
+     * @var array
+     */
+    protected $_init;
+
+    /**
+     * Is there an active authenticated connection to the IMAP Server?
+     *
+     * @var boolean
+     */
+    protected $_isAuthenticated = false;
+
+    /**
+     * The current mailbox selection mode.
+     *
+     * @var integer
+     */
+    protected $_mode = 0;
+
+    /**
+     * Hash containing connection parameters.
+     * This hash never changes.
+     *
+     * @var array
+     */
+    protected $_params = array();
+
+    /**
+     * The currently selected mailbox.
+     *
+     * @var Horde_Imap_Client_Mailbox
+     */
+    protected $_selected = null;
+
+    /**
+     * Temp array (destroyed at end of process).
+     *
+     * @var array
+     */
+    protected $_temp = array(
+        'enabled' => array()
+    );
+
+    /**
+     * Constructor.
+     *
+     * @param array $params   Configuration parameters:
+     * <ul>
+     *  <li>REQUIRED Parameters
+     *   <ul>
+     *    <li>password: (string) The IMAP user password.</li>
+     *    <li>username: (string) The IMAP username.</li>
+     *   </ul>
+     *  </li>
+     *  <li>Optional Parameters
+     *   <ul>
+     *    <li>
+     *     cache: (array) If set, caches data from fetch(), search(), and
+     *            thread() calls. Requires the horde/Cache package to be
+     *            installed. The array can contain the following keys (see
+     *            Horde_Imap_Client_Cache for default values):
+     *     <ul>
+     *      <li>
+     *       backend: [REQUIRED (or cacheob)] (Horde_Imap_Client_Cache_Backend)
+     *                Backend cache driver (as of 2.9.0).
+     *      </li>
+     *      <li>
+     *       cacheob: [REQUIRED (or backend)] (Horde_Cache) The cache object to
+     *                use (DEPRECATED).
+     *      </li>
+     *      <li>
+     *       fetch_ignore: (array) A list of mailboxes to ignore when storing
+     *                     fetch data.
+     *      </li>
+     *      <li>
+     *       fields: (array) The fetch criteria to cache. If not defined, all
+     *               cacheable data is cached. The following is a list of
+     *               criteria that can be cached:
+     *       <ul>
+     *        <li>Horde_Imap_Client::FETCH_ENVELOPE</li>
+     *        <li>Horde_Imap_Client::FETCH_FLAGS
+     *         <ul>
+     *          <li>
+     *           Only if server supports CONDSTORE extension
+     *          </li>
+     *         </ul>
+     *        </li>
+     *        <li>Horde_Imap_Client::FETCH_HEADERS
+     *         <ul>
+     *          <li>
+     *           Only for queries that specifically request caching
+     *          </li>
+     *         </ul>
+     *        </li>
+     *        <li>Horde_Imap_Client::FETCH_IMAPDATE</li>
+     *        <li>Horde_Imap_Client::FETCH_SIZE</li>
+     *        <li>Horde_Imap_Client::FETCH_STRUCTURE</li>
+     *       </ul>
+     *      </li>
+     *     </ul>
+     *    </li>
+     *    <li>
+     *     capability_ignore: (array) A list of IMAP capabilites to ignore,
+     *                        even if they are supported on the server.
+     *                        DEFAULT: No supported capabilities are ignored.
+     *    </li>
+     *    <li>
+     *     comparator: (string) The search comparator to use instead of the
+     *                 default IMAP server comparator. See
+     *                 Horde_Imap_Client_Base#setComparator() for format.
+     *                 DEFAULT: Use the server default
+     *    </li>
+     *    <li>
+     *     debug: (string) If set, will output debug information to the stream
+     *            provided. The value can be any PHP supported wrapper that
+     *            can be opened via fopen().
+     *            DEFAULT: No debug output
+     *    </li>
+     *    <li>
+     *     encryptKey: (array) A callback to a function that returns the key
+     *                 used to encrypt the password. This function MUST be
+     *                 static.
+     *                 DEFAULT: No encryption
+     *    </li>
+     *    <li>
+     *     hostspec: (string) The hostname or IP address of the server.
+     *               DEFAULT: 'localhost'
+     *    </li>
+     *    <li>
+     *     id: (array) Send ID information to the IMAP server (only if server
+     *         supports the ID extension). An array with the keys as the
+     *         fields to send and the values being the associated values. See
+     *         RFC 2971 [3.3] for a list of defined standard field values.
+     *         DEFAULT: No info sent to server
+     *    </li>
+     *    <li>
+     *     lang: (array) A list of languages (in priority order) to be used to
+     *           display human readable messages.
+     *           DEFAULT: Messages output in IMAP server default language
+     *    </li>
+     *    <li>
+     *     port: (integer) The server port to which we will connect.
+     *           DEFAULT: 143 (imap or imap w/TLS) or 993 (imaps)
+     *    </li>
+     *    <li>
+     *     secure: (string) Use SSL or TLS to connect.
+     *             VALUES:
+     *     <ul>
+     *      <li>false</li>
+     *      <li>'ssl' (Auto-detect SSL version)</li>
+     *      <li>'sslv2' (Force SSL version 3)</li>
+     *      <li>'sslv3' (Force SSL version 2)</li>
+     *      <li>'tls'</li>
+     *     </ul>
+     *             DEFAULT: No encryption</li>
+     *    </li>
+     *    <li>
+     *     timeout: (integer)  Connection timeout, in seconds.
+     *              DEFAULT: 30 seconds
+     *    </li>
+     *   </ul>
+     *  </li>
+     * </ul>
+     */
+    public function __construct(array $params = array())
+    {
+        if (!isset($params['username']) || !isset($params['password'])) {
+            throw new InvalidArgumentException('Horde_Imap_Client requires a username and password.');
+        }
+
+        $this->_setInit();
+
+        // Default values.
+        $params = array_merge(array(
+            'encryptKey' => null,
+            'hostspec' => 'localhost',
+            'secure' => false,
+            'timeout' => 30
+        ), array_filter($params));
+
+        if ($params['secure'] === true) {
+            $params['secure'] = 'tls';
+        }
+
+        if (!isset($params['port'])) {
+            $params['port'] = (isset($params['secure']) && in_array($params['secure'], array('ssl', 'sslv2', 'sslv3')))
+                ? 993
+                : 143;
+        }
+
+        if (empty($params['cache'])) {
+            $params['cache'] = array('fields' => array());
+        } elseif (empty($params['cache']['fields'])) {
+            $params['cache']['fields'] = $this->cacheFields;
+        } else {
+            $params['cache']['fields'] = array_flip($params['cache']['fields']);
+        }
+
+        if (empty($params['cache']['fetch_ignore'])) {
+            $params['cache']['fetch_ignore'] = array();
+        }
+
+        $this->_params = $params;
+        $this->setParam('password', $params['password']);
+
+        $this->changed = true;
+        $this->_initOb();
+    }
+
+    /**
+     * Get encryption key.
+     *
+     * @return string  The encryption key.
+     */
+    protected function _getEncryptKey()
+    {
+        if (is_callable($ekey = $this->getParam('encryptKey'))) {
+            return call_user_func($ekey);
+        }
+
+        throw new InvalidArgumentException('encryptKey parameter is not a valid callback.');
+    }
+
+    /**
+     * Do initialization tasks.
+     */
+    protected function _initOb()
+    {
+        register_shutdown_function(array($this, 'shutdown'));
+        $this->_debug = ($debug = $this->getParam('debug'))
+            ? new Horde_Imap_Client_Base_Debug($debug)
+            : new Horde_Support_Stub();
+    }
+
+    /**
+     * Shutdown actions.
+     */
+    public function shutdown()
+    {
+        $this->logout();
+    }
+
+    /**
+     * This object can not be cloned.
+     */
+    public function __clone()
+    {
+        throw new LogicException('Object cannot be cloned.');
+    }
+
+    /**
+     */
+    public function serialize()
+    {
+        return serialize(array(
+            'i' => $this->_init,
+            'p' => $this->_params,
+            'v' => self::VERSION
+        ));
+    }
+
+    /**
+     */
+    public function unserialize($data)
+    {
+        $data = @unserialize($data);
+        if (!is_array($data) ||
+            !isset($data['v']) ||
+            ($data['v'] != self::VERSION)) {
+            throw new Exception('Cache version change');
+        }
+
+        $this->_init = $data['i'];
+        $this->_params = $data['p'];
+
+        $this->_initOb();
+    }
+
+    /**
+     * Set an initialization value.
+     *
+     * @param string $key  The initialization key. If null, resets all keys.
+     * @param mixed $val   The cached value. If null, removes the key.
+     */
+    public function _setInit($key = null, $val = null)
+    {
+        if (is_null($key)) {
+            $this->_init = array(
+                'namespace' => array(),
+                's_charset' => array()
+            );
+        } elseif (is_null($val)) {
+            unset($this->_init[$key]);
+        } else {
+            switch ($key) {
+            case 'capability':
+                if ($ci = $this->getParam('capability_ignore')) {
+                    if ($this->_debug->debug &&
+                        ($ignored = array_intersect_key($val, array_flip($ci)))) {
+                        $this->_debug->info(sprintf("CONFIG: IGNORING these IMAP capabilities: %s", implode(', ', array_keys($ignored))));
+                    }
+
+                    $val = array_diff_key($val, array_flip($ci));
+                }
+
+                /* RFC 5162 [1] - QRESYNC implies CONDSTORE and ENABLE, even
+                 * if not listed as a capability. */
+                if (!empty($val['QRESYNC'])) {
+                    $val['CONDSTORE'] = true;
+                    $val['ENABLE'] = true;
+                }
+                break;
+            }
+
+            /* Nothing has changed. */
+            if (isset($this->_init[$key]) && ($this->_init[$key] == $val)) {
+                return;
+            }
+
+            $this->_init[$key] = $val;
+        }
+
+        $this->changed = true;
+    }
+
+    /**
+     * Set the list of enabled extensions.
+     *
+     * @param array $exts      The list of extensions.
+     * @param integer $status  1 means to report as ENABLED, although it has
+     *                         not been formally enabled on server yet. 2 is
+     *                         verified enabled on the server.
+     */
+    protected function _enabled($exts, $status)
+    {
+        /* RFC 5162 [1] - Enabling QRESYNC also implies enabling of
+         * CONDSTORE. */
+        if (in_array('QRESYNC', $exts)) {
+            $exts[] = 'CONDSTORE';
+        }
+
+        switch ($status) {
+        case 2:
+            $enabled_list = array_intersect(array(2), $this->_temp['enabled']);
+            break;
+
+        case 1:
+        default:
+            $enabled_list = $this->_temp['enabled'];
+            $status = 1;
+            break;
+        }
+
+        $this->_temp['enabled'] = array_merge(
+            $enabled_list,
+            array_fill_keys($exts, $status)
+        );
+    }
+
+    /**
+     * Initialize the Horde_Imap_Client_Cache object, if necessary.
+     *
+     * @param boolean $current  If true, we are going to update the currently
+     *                          selected mailbox. Add an additional check to
+     *                          see if caching is available in current
+     *                          mailbox.
+     *
+     * @return boolean  Returns true if caching is enabled.
+     */
+    protected function _initCache($current = false)
+    {
+        $c = $this->getParam('cache');
+
+        if (empty($c['fields'])) {
+            return false;
+        }
+
+        if (is_null($this->_cache)) {
+            if (isset($c['backend']) &&
+                ($c['backend'] instanceof Horde_Imap_Client_Cache_Backend)) {
+                $backend = $c['backend'];
+            } elseif (isset($c['cacheob'])) {
+                /* Deprecated */
+                $backend = new Horde_Imap_Client_Cache_Backend_Cache($c);
+            } else {
+                return false;
+            }
+
+            $this->_cache = new Horde_Imap_Client_Cache(array(
+                'backend' => $backend,
+                'baseob' => $this,
+                'debug' => $this->_debug
+            ));
+        }
+
+        return $current
+            /* If UIDs are labeled as not sticky, don't cache since UIDs will
+             * change on every access. */
+            ? !($this->_mailboxOb()->getStatus(Horde_Imap_Client::STATUS_UIDNOTSTICKY))
+            : true;
+    }
+
+    /**
+     * Returns a value from the internal params array.
+     *
+     * @param string $key  The param key.
+     *
+     * @return mixed  The param value, or null if not found.
+     */
+    public function getParam($key)
+    {
+        /* Passwords may be stored encrypted. */
+        if (($key == 'password') && !empty($this->_params['_passencrypt'])) {
+            try {
+                $secret = new Horde_Secret();
+                return $secret->read($this->_getEncryptKey(), $this->_params['password']);
+            } catch (Exception $e) {
+                return null;
+            }
+        }
+
+        return isset($this->_params[$key])
+            ? $this->_params[$key]
+            : null;
+    }
+
+    /**
+     * Sets a configuration parameter value.
+     *
+     * @param string $key  The param key.
+     * @param mixed $val   The param value.
+     */
+    public function setParam($key, $val)
+    {
+        switch ($key) {
+        case 'password':
+            // Encrypt password.
+            try {
+                $encrypt_key = $this->_getEncryptKey();
+                if (strlen($encrypt_key)) {
+                    $secret = new Horde_Secret();
+                    $val = $secret->write($encrypt_key, $val);
+                    $this->_params['_passencrypt'] = true;
+                }
+            } catch (Exception $e) {
+                $this->_params['_passencrypt'] = false;
+            }
+            break;
+        }
+
+        $this->_params[$key] = $val;
+        $this->changed = true;
+    }
+
+    /**
+     * Returns the Horde_Imap_Client_Cache object used, if available.
+     *
+     * @return mixed  Either the cache object or null.
+     */
+    public function getCache()
+    {
+        $this->_initCache();
+        return $this->_cache;
+    }
+
+    /**
+     * Returns the correct IDs object for use with this driver.
+     *
+     * @param mixed $ids         See add().
+     * @param boolean $sequence  Are $ids message sequence numbers?
+     *
+     * @return Horde_Imap_Client_Ids  The IDs object.
+     */
+    public function getIdsOb($ids = null, $sequence = false)
+    {
+        return new Horde_Imap_Client_Ids($ids, $sequence);
+    }
+
+    /**
+     * Returns whether the IMAP server supports the given capability
+     * (See RFC 3501 [6.1.1]).
+     *
+     * @param string $capability  The capability string to query.
+     *
+     * @return mixed  True if the server supports the queried capability,
+     *                false if it doesn't, or an array if the capability can
+     *                contain multiple values.
+     */
+    public function queryCapability($capability)
+    {
+        try {
+            $this->capability();
+        } catch (Horde_Imap_Client_Exception $e) {
+            return false;
+        }
+
+        $capability = strtoupper($capability);
+
+        if (!isset($this->_init['capability'][$capability])) {
+            return false;
+        }
+
+        /* Check for capability requirements. */
+        if (isset(Horde_Imap_Client::$capability_deps[$capability])) {
+            foreach (Horde_Imap_Client::$capability_deps[$capability] as $val) {
+                if (!$this->queryCapability($val)) {
+                    return false;
+                }
+            }
+        }
+
+        return $this->_init['capability'][$capability];
+    }
+
+    /**
+     * Get CAPABILITY information from the IMAP server.
+     *
+     * @return array  The capability array.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function capability()
+    {
+        if (!isset($this->_init['capability'])) {
+            $this->_capability();
+        }
+
+        return $this->_init['capability'];
+    }
+
+    /**
+     * Retrieve CAPABILITY information from the IMAP server.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    abstract protected function _capability();
+
+    /**
+     * Send a NOOP command (RFC 3501 [6.1.2]).
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function noop()
+    {
+        // NOOP only useful if we are already authenticated.
+        if ($this->_isAuthenticated) {
+            $this->_noop();
+        }
+    }
+
+    /**
+     * Send a NOOP command.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    abstract protected function _noop();
+
+    /**
+     * Get the NAMESPACE information from the IMAP server (RFC 2342).
+     *
+     * @param array $additional  If the server supports namespaces, any
+     *                           additional namespaces to add to the
+     *                           namespace list that are not broadcast by
+     *                           the server. The namespaces must be UTF-8
+     *                           strings.
+     *
+     * @return array  An array of namespace information with the name as the
+     *                key (UTF-8) and the following values:
+     * <ul>
+     *  <li>delimiter: (string) The namespace delimiter.</li>
+     *  <li>hidden: (boolean) Is this a hidden namespace?</li>
+     *  <li>name: (string) The namespace name (UTF-8).</li>
+     *  <li>
+     *   translation: (string) Returns the translated name of the namespace
+     *   (UTF-8). Requires RFC 5255 and a previous call to setLanguage().
+     *  </li>
+     *  <li>
+     *   type: (integer) The namespace type. Either:
+     *   <ul>
+     *    <li>Horde_Imap_Client::NS_PERSONAL</li>
+     *    <li>Horde_Imap_Client::NS_OTHER</li>
+     *    <li>Horde_Imap_Client::NS_SHARED</li>
+     *   </ul>
+     *  </li>
+     * </ul>
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function getNamespaces(array $additional = array())
+    {
+        $this->login();
+
+        $additional = array_map('strval', $additional);
+        $sig = hash('md5', serialize($additional));
+
+        if (isset($this->_init['namespace'][$sig])) {
+            return $this->_init['namespace'][$sig];
+        }
+
+        $ns = $this->_getNamespaces();
+
+        /* Skip namespaces if we have already auto-detected them. Also, hidden
+         * namespaces cannot be empty. */
+        $to_process = array_diff(array_filter($additional, 'strlen'), array_keys($ns));;
+        if (!empty($to_process)) {
+            foreach ($this->listMailboxes($to_process, Horde_Imap_Client::MBOX_ALL, array('delimiter' => true)) as $val) {
+                $ns[$val] = array(
+                    'delimiter' => $first['delimiter'],
+                    'hidden' => true,
+                    'name' => $val,
+                    'translation' => '',
+                    'type' => Horde_Imap_Client::NS_SHARED
+                );
+            }
+        }
+
+        if (empty($ns)) {
+            /* This accurately determines the namespace information of the
+             * base namespace if the NAMESPACE command is not supported.
+             * See: RFC 3501 [6.3.8] */
+            $mbox = $this->listMailboxes('', Horde_Imap_Client::MBOX_ALL, array('delimiter' => true));
+            $first = reset($mbox);
+            $ns[''] = array(
+                'delimiter' => $first['delimiter'],
+                'hidden' => false,
+                'name' => '',
+                'translation' => '',
+                'type' => Horde_Imap_Client::NS_PERSONAL
+            );
+        }
+
+        $this->_setInit('namespace', array_merge($this->_init['namespace'], array($sig => $ns)));
+
+        return $ns;
+    }
+
+    /**
+     * Get the NAMESPACE information from the IMAP server.
+     *
+     * @return array  An array of namespace information. See getNamespaces()
+     *                for format.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    abstract protected function _getNamespaces();
+
+    /**
+     * Display if connection to the server has been secured via TLS or SSL.
+     *
+     * @return boolean  True if the IMAP connection is secured.
+     */
+    public function isSecureConnection()
+    {
+        return ($this->_connection && $this->_connection->secure);
+    }
+
+    /**
+     * Connect to the remote server.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    abstract protected function _connect();
+
+    /**
+     * Return a list of alerts that MUST be presented to the user (RFC 3501
+     * [7.1]).
+     *
+     * @return array  An array of alert messages.
+     */
+    abstract public function alerts();
+
+    /**
+     * Login to the IMAP server.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function login()
+    {
+        if (!$this->_isAuthenticated && $this->_login()) {
+            if ($this->getParam('id')) {
+                try {
+                    $this->sendID();
+                } catch (Horde_Imap_Client_Exception_NoSupportExtension $e) {
+                    // Ignore if server doesn't support ID extension.
+                }
+            }
+
+            if ($this->getParam('comparator')) {
+                try {
+                    $this->setComparator();
+                } catch (Horde_Imap_Client_Exception_NoSupportExtension $e) {
+                    // Ignore if server doesn't support I18NLEVEL=2
+                }
+            }
+        }
+
+        $this->_isAuthenticated = true;
+    }
+
+    /**
+     * Login to the IMAP server.
+     *
+     * @return boolean  Return true if global login tasks should be run.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    abstract protected function _login();
+
+    /**
+     * Logout from the IMAP server (see RFC 3501 [6.1.3]).
+     */
+    public function logout()
+    {
+        if ($this->_isAuthenticated && $this->_connection->connected) {
+            $this->_logout();
+            $this->_connection->close();
+        }
+
+        $this->_connection = $this->_selected = null;
+        $this->_isAuthenticated = false;
+        $this->_mode = 0;
+    }
+
+    /**
+     * Logout from the IMAP server (see RFC 3501 [6.1.3]).
+     */
+    abstract protected function _logout();
+
+    /**
+     * Send ID information to the IMAP server (RFC 2971).
+     *
+     * @param array $info  Overrides the value of the 'id' param and sends
+     *                     this information instead.
+     *
+     * @throws Horde_Imap_Client_Exception
+     * @throws Horde_Imap_Client_Exception_NoSupportExtension
+     */
+    public function sendID($info = null)
+    {
+        if (!$this->queryCapability('ID')) {
+            throw new Horde_Imap_Client_Exception_NoSupportExtension('ID');
+        }
+
+        /* Modified for WordPress inclusion: The shorthand ternary operator ?: was introduced in PHP 5.3. */
+        $this->_sendID(is_null($info) ? ($this->getParam('id') ? $this->getParam('id') : array()) : $info);
+    }
+
+    /**
+     * Send ID information to the IMAP server (RFC 2971).
+     *
+     * @param array $info  The information to send to the server.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    abstract protected function _sendID($info);
+
+    /**
+     * Return ID information from the IMAP server (RFC 2971).
+     *
+     * @return array  An array of information returned, with the keys as the
+     *                'field' and the values as the 'value'.
+     *
+     * @throws Horde_Imap_Client_Exception
+     * @throws Horde_Imap_Client_Exception_NoSupportExtension
+     */
+    public function getID()
+    {
+        if (!$this->queryCapability('ID')) {
+            throw new Horde_Imap_Client_Exception_NoSupportExtension('ID');
+        }
+
+        return $this->_getID();
+    }
+
+    /**
+     * Return ID information from the IMAP server (RFC 2971).
+     *
+     * @return array  An array of information returned, with the keys as the
+     *                'field' and the values as the 'value'.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    abstract protected function _getID();
+
+    /**
+     * Sets the preferred language for server response messages (RFC 5255).
+     *
+     * @param array $langs  Overrides the value of the 'lang' param and sends
+     *                      this list of preferred languages instead. The
+     *                      special string 'i-default' can be used to restore
+     *                      the language to the server default.
+     *
+     * @return string  The language accepted by the server, or null if the
+     *                 default language is used.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function setLanguage($langs = null)
+    {
+        $lang = null;
+
+        if ($this->queryCapability('LANGUAGE')) {
+            $lang = is_null($langs)
+                ? $this->getParam('lang')
+                : $langs;
+        }
+
+        return is_null($lang)
+            ? null
+            : $this->_setLanguage($lang);
+    }
+
+    /**
+     * Sets the preferred language for server response messages (RFC 5255).
+     *
+     * @param array $langs  The preferred list of languages.
+     *
+     * @return string  The language accepted by the server, or null if the
+     *                 default language is used.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    abstract protected function _setLanguage($langs);
+
+    /**
+     * Gets the preferred language for server response messages (RFC 5255).
+     *
+     * @param array $list  If true, return the list of available languages.
+     *
+     * @return mixed  If $list is true, the list of languages available on the
+     *                server (may be empty). If false, the language used by
+     *                the server, or null if the default language is used.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function getLanguage($list = false)
+    {
+        if (!$this->queryCapability('LANGUAGE')) {
+            return $list ? array() : null;
+        }
+
+        return $this->_getLanguage($list);
+    }
+
+    /**
+     * Gets the preferred language for server response messages (RFC 5255).
+     *
+     * @param array $list  If true, return the list of available languages.
+     *
+     * @return mixed  If $list is true, the list of languages available on the
+     *                server (may be empty). If false, the language used by
+     *                the server, or null if the default language is used.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    abstract protected function _getLanguage($list);
+
+    /**
+     * Open a mailbox.
+     *
+     * @param mixed $mailbox  The mailbox to open. Either a
+     *                        Horde_Imap_Client_Mailbox object or a string
+     *                        (UTF-8).
+     * @param integer $mode   The access mode. Either
+     *   - Horde_Imap_Client::OPEN_READONLY
+     *   - Horde_Imap_Client::OPEN_READWRITE
+     *   - Horde_Imap_Client::OPEN_AUTO
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function openMailbox($mailbox, $mode = Horde_Imap_Client::OPEN_AUTO)
+    {
+        $this->login();
+
+        $change = false;
+        $mailbox = Horde_Imap_Client_Mailbox::get($mailbox);
+
+        if ($mode == Horde_Imap_Client::OPEN_AUTO) {
+            if (is_null($this->_selected) ||
+                !$mailbox->equals($this->_selected)) {
+                $mode = Horde_Imap_Client::OPEN_READONLY;
+                $change = true;
+            }
+        } else {
+            $change = (is_null($this->_selected) ||
+                       !$mailbox->equals($this->_selected) ||
+                       ($mode != $this->_mode));
+        }
+
+        if ($change) {
+            $this->_openMailbox($mailbox, $mode);
+            $this->_mailboxOb()->open = true;
+            if ($this->_initCache(true)) {
+                $this->_condstoreSync();
+            }
+        }
+    }
+
+    /**
+     * Open a mailbox.
+     *
+     * @param Horde_Imap_Client_Mailbox $mailbox  The mailbox to open.
+     * @param integer $mode                       The access mode.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    abstract protected function _openMailbox(Horde_Imap_Client_Mailbox $mailbox,
+                                             $mode);
+
+    /**
+     * Called when the selected mailbox is changed.
+     *
+     * @param mixed $mailbox  The selected mailbox or null.
+     * @param integer $mode   The access mode.
+     */
+    protected function _changeSelected($mailbox = null, $mode = null)
+    {
+        $this->_mode = $mode;
+        if (is_null($mailbox)) {
+            $this->_selected = null;
+        } else {
+            $this->_selected = clone $mailbox;
+            $this->_mailboxOb()->reset();
+        }
+    }
+
+    /**
+     * Return the Horde_Imap_Client_Base_Mailbox object.
+     *
+     * @param string $mailbox  The mailbox name. Defaults to currently
+     *                         selected mailbox.
+     *
+     * @return Horde_Imap_Client_Base_Mailbox  Mailbox object.
+     */
+    protected function _mailboxOb($mailbox = null)
+    {
+        $name = is_null($mailbox)
+            ? strval($this->_selected)
+            : strval($mailbox);
+
+        if (!isset($this->_temp['mailbox_ob'][$name])) {
+            $this->_temp['mailbox_ob'][$name] = new Horde_Imap_Client_Base_Mailbox();
+        }
+
+        return $this->_temp['mailbox_ob'][$name];
+    }
+
+    /**
+     * Return the currently opened mailbox and access mode.
+     *
+     * @return mixed  Null if no mailbox selected, or an array with two
+     *                elements:
+     *   - mailbox: (Horde_Imap_Client_Mailbox) The mailbox object.
+     *   - mode: (integer) Current mode.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function currentMailbox()
+    {
+        return is_null($this->_selected)
+            ? null
+            : array(
+                'mailbox' => clone $this->_selected,
+                'mode' => $this->_mode
+            );
+    }
+
+    /**
+     * Create a mailbox.
+     *
+     * @param mixed $mailbox  The mailbox to create. Either a
+     *                        Horde_Imap_Client_Mailbox object or a string
+     *                        (UTF-8).
+     * @param array $opts     Additional options:
+     *   - special_use: (array) An array of special-use flags to mark the
+     *                  mailbox with. The server MUST support RFC 6154.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function createMailbox($mailbox, array $opts = array())
+    {
+        $this->login();
+
+        if (!$this->queryCapability('CREATE-SPECIAL-USE')) {
+            unset($opts['special_use']);
+        }
+
+        $this->_createMailbox(Horde_Imap_Client_Mailbox::get($mailbox), $opts);
+    }
+
+    /**
+     * Create a mailbox.
+     *
+     * @param Horde_Imap_Client_Mailbox $mailbox  The mailbox to create.
+     * @param array $opts                         Additional options. See
+     *                                            createMailbox().
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    abstract protected function _createMailbox(Horde_Imap_Client_Mailbox $mailbox,
+                                               $opts);
+
+    /**
+     * Delete a mailbox.
+     *
+     * @param mixed $mailbox  The mailbox to delete. Either a
+     *                        Horde_Imap_Client_Mailbox object or a string
+     *                        (UTF-8).
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function deleteMailbox($mailbox)
+    {
+        $this->login();
+
+        $mailbox = Horde_Imap_Client_Mailbox::get($mailbox);
+
+        $this->_deleteMailbox($mailbox);
+        $this->_deleteMailboxPost($mailbox);
+    }
+
+    /**
+     * Delete a mailbox.
+     *
+     * @param Horde_Imap_Client_Mailbox $mailbox  The mailbox to delete.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    abstract protected function _deleteMailbox(Horde_Imap_Client_Mailbox $mailbox);
+
+    /**
+     * Actions to perform after a mailbox delete.
+     *
+     * @param Horde_Imap_Client_Mailbox $mailbox  The deleted mailbox.
+     */
+    protected function _deleteMailboxPost(Horde_Imap_Client_Mailbox $mailbox)
+    {
+        /* Delete mailbox caches. */
+        if ($this->_initCache()) {
+            $this->_cache->deleteMailbox($mailbox);
+        }
+        unset($this->_temp['mailbox_ob'][strval($mailbox)]);
+
+        /* Unsubscribe from mailbox. */
+        try {
+            $this->subscribeMailbox($mailbox, false);
+        } catch (Horde_Imap_Client_Exception $e) {
+            // Ignore failed unsubscribe request
+        }
+    }
+
+    /**
+     * Rename a mailbox.
+     *
+     * @param mixed $old  The old mailbox name. Either a
+     *                    Horde_Imap_Client_Mailbox object or a string (UTF-8).
+     * @param mixed $new  The new mailbox name. Either a
+     *                    Horde_Imap_Client_Mailbox object or a string (UTF-8).
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function renameMailbox($old, $new)
+    {
+        // Login will be handled by first listMailboxes() call.
+
+        $old = Horde_Imap_Client_Mailbox::get($old);
+        $new = Horde_Imap_Client_Mailbox::get($new);
+
+        /* Check if old mailbox(es) were subscribed to. */
+        $base = $this->listMailboxes($old, Horde_Imap_Client::MBOX_SUBSCRIBED, array('delimiter' => true));
+        if (empty($base)) {
+            $base = $this->listMailboxes($old, Horde_Imap_Client::MBOX_ALL, array('delimiter' => true));
+            $base = reset($base);
+            $subscribed = array();
+        } else {
+            $base = reset($base);
+            $subscribed = array($base['mailbox']);
+        }
+
+        $all_mboxes = array($base['mailbox']);
+        if (strlen($base['delimiter'])) {
+            $search = $old->list_escape . $base['delimiter'] . '*';
+            $all_mboxes = array_merge($all_mboxes, $this->listMailboxes($search, Horde_Imap_Client::MBOX_ALL, array('flat' => true)));
+            $subscribed = array_merge($subscribed, $this->listMailboxes($search, Horde_Imap_Client::MBOX_SUBSCRIBED, array('flat' => true)));
+        }
+
+        $this->_renameMailbox($old, $new);
+
+        /* Delete mailbox actions. */
+        foreach ($all_mboxes as $val) {
+            $this->_deleteMailboxPost($val);
+        }
+
+        foreach ($subscribed as $val) {
+            try {
+                $this->subscribeMailbox(new Horde_Imap_Client_Mailbox(substr_replace($val, $new, 0, strlen($old))));
+            } catch (Horde_Imap_Client_Exception $e) {
+                // Ignore failed subscription requests
+            }
+        }
+    }
+
+    /**
+     * Rename a mailbox.
+     *
+     * @param Horde_Imap_Client_Mailbox $old  The old mailbox name.
+     * @param Horde_Imap_Client_Mailbox $new  The new mailbox name.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    abstract protected function _renameMailbox(Horde_Imap_Client_Mailbox $old,
+                                               Horde_Imap_Client_Mailbox $new);
+
+    /**
+     * Manage subscription status for a mailbox.
+     *
+     * @param mixed $mailbox      The mailbox to [un]subscribe to. Either a
+     *                            Horde_Imap_Client_Mailbox object or a string
+     *                            (UTF-8).
+     * @param boolean $subscribe  True to subscribe, false to unsubscribe.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function subscribeMailbox($mailbox, $subscribe = true)
+    {
+        $this->login();
+        $this->_subscribeMailbox(Horde_Imap_Client_Mailbox::get($mailbox), (bool)$subscribe);
+    }
+
+    /**
+     * Manage subscription status for a mailbox.
+     *
+     * @param Horde_Imap_Client_Mailbox $mailbox  The mailbox to [un]subscribe
+     *                                            to.
+     * @param boolean $subscribe                  True to subscribe, false to
+     *                                            unsubscribe.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    abstract protected function _subscribeMailbox(Horde_Imap_Client_Mailbox $mailbox,
+                                                  $subscribe);
+
+    /**
+     * Obtain a list of mailboxes matching a pattern.
+     *
+     * @param mixed $pattern   The mailbox search pattern(s) (see RFC 3501
+     *                         [6.3.8] for the format). A UTF-8 string or an
+     *                         array of strings. If a Horde_Imap_Client_Mailbox
+     *                         object is given, it is escaped (i.e. wildcard
+     *                         patterns are converted to return the miminal
+     *                         number of matches possible).
+     * @param integer $mode    Which mailboxes to return.  Either:
+     *   - Horde_Imap_Client::MBOX_SUBSCRIBED
+     *   - Horde_Imap_Client::MBOX_SUBSCRIBED_EXISTS
+     *   - Horde_Imap_Client::MBOX_UNSUBSCRIBED
+     *   - Horde_Imap_Client::MBOX_ALL
+     * @param array $options   Additional options:
+     * <ul>
+     *  <li>
+     *   attributes: (boolean) If true, return attribute information under
+     *   the 'attributes' key.
+     *   DEFAULT: Do not return this information.
+     *  </li>
+     *  <li>
+     *   children: (boolean) Tell server to return children attribute
+     *   information (\HasChildren, \HasNoChildren). Requires the
+     *   LIST-EXTENDED extension to guarantee this information is returned.
+     *   Server MAY return this attribute without this option, or if the
+     *   CHILDREN extension is available, but it is not guaranteed.
+     *   DEFAULT: false
+     *  </li>
+     *  <li>
+     *   delimiter: (boolean) If true, return delimiter information under the
+     *   'delimiter' key.
+     *   DEFAULT: Do not return this information.
+     *  </li>
+     *  <li>
+     *   flat: (boolean) If true, return a flat list of mailbox names only.
+     *   Overrides both the 'attributes' and 'delimiter' options.
+     *   DEFAULT: Do not return flat list.
+     *  </li>
+     *  <li>
+     *   recursivematch: (boolean) Force the server to return information
+     *   about parent mailboxes that don't match other selection options, but
+     *   have some submailboxes that do. Information about children is
+     *   returned in the CHILDINFO extended data item ('extended'). Requires
+     *   the LIST-EXTENDED extension.
+     *   DEFAULT: false
+     *  </li>
+     *  <li>
+     *   remote: (boolean) Tell server to return mailboxes that reside on
+     *   another server. Requires the LIST-EXTENDED extension.
+     *   DEFAULT: false
+     *  </li>
+     *  <li>
+     *   special_use: (boolean) Tell server to return special-use attribute
+     *   information (\Drafts, \Flagged, \Junk, \Sent, \Trash, \All,
+     *   \Archive). Server must support the SPECIAL-USE return option for this
+     *   setting to have any effect. Server MAY return this attribute without
+     *   this option.
+     *   DEFAULT: false
+     *  <li>
+     *   status: (integer) Tell server to return status information. The
+     *   value is a bitmask that may contain any of:
+     *   <ul>
+     *    <li>Horde_Imap_Client::STATUS_MESSAGES</li>
+     *    <li>Horde_Imap_Client::STATUS_RECENT</li>
+     *    <li>Horde_Imap_Client::STATUS_UIDNEXT</li>
+     *    <li>Horde_Imap_Client::STATUS_UIDVALIDITY</li>
+     *    <li>Horde_Imap_Client::STATUS_UNSEEN</li>
+     *    <li>Horde_Imap_Client::STATUS_HIGHESTMODSEQ</li>
+     *   </ul>
+     *   DEFAULT: 0
+     *  </li>
+     *  <li>
+     *   sort: (boolean) If true, return a sorted list of mailboxes?
+     *   DEFAULT: Do not sort the list.
+     *  </li>
+     *  <li>
+     *   sort_delimiter: (string) If 'sort' is true, this is the delimiter
+     *   used to sort the mailboxes.
+     *   DEFAULT: '.'
+     *  </li>
+     * </ul>
+     *
+     * @return array  If 'flat' option is true, the array values are a list
+     *                of Horde_Imap_Client_Mailbox objects. Otherwise, the
+     *                keys are UTF-8 mailbox names and the values are arrays
+     *                with these keys:
+     *   - attributes: (array) List of lower-cased attributes [only if
+     *                 'attributes' option is true].
+     *   - delimiter: (string) The delimiter for the mailbox [only if
+     *                'delimiter' option is true].
+     *   - extended: (TODO) TODO [only if 'recursivematch' option is true and
+     *               LIST-EXTENDED extension is supported on the server].
+     *   - mailbox: (Horde_Imap_Client_Mailbox) The mailbox object.
+     *   - status: (array) See status() [only if 'status' option is true].
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function listMailboxes($pattern,
+                                  $mode = Horde_Imap_Client::MBOX_ALL,
+                                  array $options = array())
+    {
+        $this->login();
+
+        $pattern = is_array($pattern)
+            ? array_unique($pattern)
+            : array($pattern);
+
+        /* Prepare patterns. */
+        $plist = array();
+        foreach ($pattern as $val) {
+            if ($val instanceof Horde_Imap_Client_Mailbox) {
+                $val = $val->list_escape;
+            }
+            $plist[] = Horde_Imap_Client_Mailbox::get(preg_replace(
+                array("/\*{2,}/", "/\%{2,}/"),
+                array('*', '%'),
+                Horde_Imap_Client_Utf7imap::Utf8ToUtf7Imap($val)
+            ), true);
+        }
+
+        if (isset($options['special_use']) &&
+            !$this->queryCapability('SPECIAL-USE')) {
+            unset($options['special_use']);
+        }
+
+        $ret = $this->_listMailboxes($plist, $mode, $options);
+
+        if (!empty($options['status']) &&
+            !$this->queryCapability('LIST-STATUS')) {
+            $status = $this->statusMultiple($this->_selected, $options['status']);
+            foreach ($status as $key => $val) {
+                $ret[$key]['status'] = $val;
+            }
+        }
+
+        if (empty($options['sort'])) {
+            return $ret;
+        }
+
+        $list_ob = new Horde_Imap_Client_Mailbox_List(empty($options['flat']) ? array_keys($ret) : $ret);
+        $sorted = $list_ob->sort(array(
+            'delimiter' => empty($options['sort_delimiter']) ? '.' : $options['sort_delimiter']
+        ));
+
+        if (!empty($options['flat'])) {
+            return $sorted;
+        }
+
+        $out = array();
+        foreach ($sorted as $val) {
+            $out[$val] = $ret[$val];
+        }
+
+        return $out;
+    }
+
+    /**
+     * Obtain a list of mailboxes matching a pattern.
+     *
+     * @param array $pattern  The mailbox search patterns
+     *                        (Horde_Imap_Client_Mailbox objects).
+     * @param integer $mode   Which mailboxes to return.
+     * @param array $options  Additional options.
+     *
+     * @return array  See listMailboxes().
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    abstract protected function _listMailboxes($pattern, $mode, $options);
+
+    /**
+     * Obtain status information for a mailbox.
+     *
+     * @param mixed $mailbox  The mailbox(es) to query. Either a
+     *                        Horde_Imap_Client_Mailbox object, a string
+     *                        (UTF-8), or an array of objects/strings (since
+     *                        2.10.0).
+     * @param integer $flags  A bitmask of information requested from the
+     *                        server. Allowed flags:
+     * <ul>
+     *  <li>
+     *   Horde_Imap_Client::STATUS_MESSAGES
+     *   <ul>
+     *    <li>
+     *     Return key: messages
+     *    </li>
+     *    <li>
+     *     Return format: (integer) The number of messages in the mailbox.
+     *    </li>
+     *   </ul>
+     *  </li>
+     *  <li>
+     *   Horde_Imap_Client::STATUS_RECENT
+     *   <ul>
+     *    <li>
+     *     Return key: recent
+     *    </li>
+     *    <li>
+     *     Return format: (integer) The number of messages with the \Recent
+     *     flag set as currently reported in the mailbox
+     *    </li>
+     *   </ul>
+     *  </li>
+     *  <li>
+     *   Horde_Imap_Client::STATUS_RECENT_TOTAL
+     *   <ul>
+     *    <li>
+     *     Return key: recent_total
+     *    </li>
+     *    <li>
+     *     Return format: (integer) The number of messages with the \Recent
+     *     flag set. This returns the total number of messages that have
+     *     been marked as recent in this mailbox since the PHP process began.
+     *     (since 2.12.0)
+     *    </li>
+     *   </ul>
+     *  </li>
+     *  <li>
+     *   Horde_Imap_Client::STATUS_UIDNEXT
+     *   <ul>
+     *    <li>
+     *     Return key: uidnext
+     *    </li>
+     *    <li>
+     *     Return format: (integer) The next UID to be assigned in the
+     *     mailbox. Only returned if the server automatically provides the
+     *     data.
+     *    </li>
+     *   </ul>
+     *  </li>
+     *  <li>
+     *   Horde_Imap_Client::STATUS_UIDNEXT_FORCE
+     *   <ul>
+     *    <li>
+     *     Return key: uidnext
+     *    </li>
+     *    <li>
+     *     Return format: (integer) The next UID to be assigned in the
+     *     mailbox. This option will always determine this value, even if the
+     *     server does not automatically provide this data.
+     *    </li>
+     *   </ul>
+     *  </li>
+     *  <li>
+     *   Horde_Imap_Client::STATUS_UIDVALIDITY
+     *   <ul>
+     *    <li>
+     *     Return key: uidvalidity
+     *    </li>
+     *    <li>
+     *     Return format: (integer) The unique identifier validity of the
+     *     mailbox.
+     *    </li>
+     *   </ul>
+     *  </li>
+     *  <li>
+     *   Horde_Imap_Client::STATUS_UNSEEN
+     *   <ul>
+     *    <li>
+     *     Return key: unseen
+     *    </li>
+     *    <li>
+     *     Return format: (integer) The number of messages which do not have
+     *     the \Seen flag set.
+     *    </li>
+     *   </ul>
+     *  </li>
+     *  <li>
+     *   Horde_Imap_Client::STATUS_FIRSTUNSEEN
+     *   <ul>
+     *    <li>
+     *     Return key: firstunseen
+     *    </li>
+     *    <li>
+     *     Return format: (integer) The sequence number of the first unseen
+     *     message in the mailbox.
+     *    </li>
+     *   </ul>
+     *  </li>
+     *  <li>
+     *   Horde_Imap_Client::STATUS_FLAGS
+     *   <ul>
+     *    <li>
+     *     Return key: flags
+     *    </li>
+     *    <li>
+     *     Return format: (array) The list of defined flags in the mailbox
+     *     (all flags are in lowercase).
+     *    </li>
+     *   </ul>
+     *  </li>
+     *  <li>
+     *   Horde_Imap_Client::STATUS_PERMFLAGS
+     *   <ul>
+     *    <li>
+     *     Return key: permflags
+     *    </li>
+     *    <li>
+     *     Return format: (array) The list of flags that a client can change
+     *     permanently (all flags are in lowercase).
+     *    </li>
+     *   </ul>
+     *  </li>
+     *  <li>
+     *   Horde_Imap_Client::STATUS_HIGHESTMODSEQ
+     *   <ul>
+     *    <li>
+     *     Return key: highestmodseq
+     *    </li>
+     *    <li>
+     *     Return format: (integer) If the server supports the CONDSTORE
+     *     IMAP extension, this will be the highest mod-sequence value of all
+     *     messages in the mailbox. Else 0 if CONDSTORE not available or the
+     *     mailbox does not support mod-sequences.
+     *    </li>
+     *   </ul>
+     *  </li>
+     *  <li>
+     *   Horde_Imap_Client::STATUS_SYNCMODSEQ
+     *   <ul>
+     *    <li>
+     *     Return key: syncmodseq
+     *    </li>
+     *    <li>
+     *     Return format: (integer) If caching, and the server supports the
+     *     CONDSTORE IMAP extension, this is the cached mod-sequence value of
+     *     the mailbox when it was opened for the first time in this access.
+     *     Will be null if not caching, CONDSTORE not available, or the
+     *     mailbox does not support mod-sequences.
+     *    </li>
+     *   </ul>
+     *  </li>
+     *  <li>
+     *   Horde_Imap_Client::STATUS_SYNCFLAGUIDS
+     *   <ul>
+     *    <li>
+     *     Return key: syncflaguids
+     *    </li>
+     *    <li>
+     *     Return format: (Horde_Imap_Client_Ids) If caching, the server
+     *     supports the CONDSTORE IMAP extension, and the mailbox contained
+     *     cached data when opened for the first time in this access, this is
+     *     the list of UIDs in which flags have changed since
+     *     STATUS_SYNCMODSEQ.
+     *    </li>
+     *   </ul>
+     *  </li>
+     *  <li>
+     *   Horde_Imap_Client::STATUS_SYNCVANISHED
+     *   <ul>
+     *    <li>
+     *     Return key: syncvanished
+     *    </li>
+     *    <li>
+     *     Return format: (Horde_Imap_Client_Ids) If caching, the server
+     *     supports the CONDSTORE IMAP extension, and the mailbox contained
+     *     cached data when opened for the first time in this access, this is
+     *     the list of UIDs which have been deleted since STATUS_SYNCMODSEQ.
+     *    </li>
+     *   </ul>
+     *  </li>
+     *  <li>
+     *   Horde_Imap_Client::STATUS_UIDNOTSTICKY
+     *   <ul>
+     *    <li>
+     *     Return key: uidnotsticky
+     *    </li>
+     *    <li>
+     *     Return format: (boolean) If the server supports the UIDPLUS IMAP
+     *     extension, and the queried mailbox does not support persistent
+     *     UIDs, this value will be true. In all other cases, this value will
+     *     be false.
+     *    </li>
+     *   </ul>
+     *  </li>
+     *  <li>
+     *   Horde_Imap_Client::STATUS_ALL (DEFAULT)
+     *   <ul>
+     *    <li>
+     *     Shortcut to return 'messages', 'recent', 'uidnext', 'uidvalidity',
+     *     and 'unseen' values.
+     *    </li>
+     *   </ul>
+     *  </li>
+     * </ul>
+     * @param array $opts     Additional options:
+     *   - sort: (boolean) If true, sort the list of mailboxes? (since 2.10.0)
+     *           DEFAULT: Do not sort the list.
+     *   - sort_delimiter: (string) If 'sort' is true, this is the delimiter
+     *                     used to sort the mailboxes. (since 2.10.0)
+     *                     DEFAULT: '.'
+     *
+     * @return array  If $mailbox contains multiple mailboxes, an array with
+     *                keys being the UTF-8 mailbox name and values as arrays
+     *                containing the requested keys (see above).
+     *                Otherwise, an array with keys as the requested keys (see
+     *                above) and values as the key data.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function status($mailbox, $flags = Horde_Imap_Client::STATUS_ALL,
+                           array $opts = array())
+    {
+        $opts = array_merge(array(
+            'sort' => false,
+            'sort_delimiter' => '.'
+        ), $opts);
+
+        $this->login();
+
+        if (is_array($mailbox)) {
+            if (empty($mailbox)) {
+                return array();
+            }
+            $ret_array = true;
+        } else {
+            $mailbox = array($mailbox);
+            $ret_array = false;
+        }
+
+        $mlist = array_map(array('Horde_Imap_Client_Mailbox', 'get'), $mailbox);
+
+        $unselected_flags = array(
+            'messages' => Horde_Imap_Client::STATUS_MESSAGES,
+            'recent' => Horde_Imap_Client::STATUS_RECENT,
+            'uidnext' => Horde_Imap_Client::STATUS_UIDNEXT,
+            'uidvalidity' => Horde_Imap_Client::STATUS_UIDVALIDITY,
+            'unseen' => Horde_Imap_Client::STATUS_UNSEEN
+        );
+
+        if ($flags & Horde_Imap_Client::STATUS_ALL) {
+            foreach ($unselected_flags as $val) {
+                $flags |= $val;
+            }
+        }
+
+        $master = $ret = array();
+
+        /* Catch flags that are not supported. */
+        if (($flags & Horde_Imap_Client::STATUS_HIGHESTMODSEQ) &&
+            !isset($this->_temp['enabled']['CONDSTORE'])) {
+            $master['highestmodseq'] = 0;
+            $flags &= ~Horde_Imap_Client::STATUS_HIGHESTMODSEQ;
+        }
+
+        if (($flags & Horde_Imap_Client::STATUS_UIDNOTSTICKY) &&
+            !$this->queryCapability('UIDPLUS')) {
+            $master['uidnotsticky'] = false;
+            $flags &= ~Horde_Imap_Client::STATUS_UIDNOTSTICKY;
+        }
+
+        /* UIDNEXT return options. */
+        if ($flags & Horde_Imap_Client::STATUS_UIDNEXT_FORCE) {
+            $flags |= Horde_Imap_Client::STATUS_UIDNEXT;
+        }
+
+        foreach ($mlist as $val) {
+            $name = strval($val);
+            $tmp_flags = $flags;
+
+            /* A list of STATUS options (other than those handled directly
+             * below) that require the mailbox to be explicitly opened. */
+            $opened = ($flags & Horde_Imap_Client::STATUS_FIRSTUNSEEN) ||
+                ($flags & Horde_Imap_Client::STATUS_FLAGS) ||
+                ($flags & Horde_Imap_Client::STATUS_PERMFLAGS) ||
+                ($flags & Horde_Imap_Client::STATUS_UIDNOTSTICKY) ||
+                /* Check if already in mailbox. */
+                $val->equals($this->_selected) ||
+                /* Force mailboxes containing wildcards to be accessed via
+                 * STATUS so that wildcards do not return a bunch of
+                 * mailboxes in the LIST-STATUS response. */
+                (strpbrk($name, '*%') !== false);
+
+            $ret[$name] = $master;
+            $ptr = &$ret[$name];
+
+            /* STATUS_PERMFLAGS requires a read/write mailbox. */
+            if ($flags & Horde_Imap_Client::STATUS_PERMFLAGS) {
+                $this->openMailbox($val, Horde_Imap_Client::OPEN_READWRITE);
+                $opened = true;
+            }
+
+            /* Handle SYNC related return options. These require the mailbox
+             * to be opened at least once. */
+            if ($flags & Horde_Imap_Client::STATUS_SYNCMODSEQ) {
+                $this->openMailbox($val);
+                $ptr['syncmodseq'] = $this->_mailboxOb($val)->getStatus(Horde_Imap_Client::STATUS_SYNCMODSEQ);
+                $tmp_flags &= ~Horde_Imap_Client::STATUS_SYNCMODSEQ;
+                $opened = true;
+            }
+
+            if ($flags & Horde_Imap_Client::STATUS_SYNCFLAGUIDS) {
+                $this->openMailbox($val);
+                $ptr['syncflaguids'] = $this->getIdsOb($this->_mailboxOb($val)->getStatus(Horde_Imap_Client::STATUS_SYNCFLAGUIDS));
+                $tmp_flags &= ~Horde_Imap_Client::STATUS_SYNCFLAGUIDS;
+                $opened = true;
+            }
+
+            if ($flags & Horde_Imap_Client::STATUS_SYNCVANISHED) {
+                $this->openMailbox($val);
+                $ptr['syncvanished'] = $this->getIdsOb($this->_mailboxOb($val)->getStatus(Horde_Imap_Client::STATUS_SYNCVANISHED));
+                $tmp_flags &= ~Horde_Imap_Client::STATUS_SYNCVANISHED;
+                $opened = true;
+            }
+
+            /* Handle RECENT_TOTAL option. */
+            if ($flags & Horde_Imap_Client::STATUS_RECENT_TOTAL) {
+                $this->openMailbox($val);
+                $ptr['recent_total'] = $this->_mailboxOb($val)->getStatus(Horde_Imap_Client::STATUS_RECENT_TOTAL);
+                $tmp_flags &= ~Horde_Imap_Client::STATUS_RECENT_TOTAL;
+                $opened = true;
+            }
+
+            if ($opened) {
+                if ($tmp_flags) {
+                    $tmp = $this->_status(array($val), $tmp_flags);
+                    $ptr += reset($tmp);
+                }
+            } else {
+                $to_process[] = $val;
+            }
+        }
+
+        if ($flags && !empty($to_process)) {
+            if ((count($to_process) > 1) &&
+                $this->queryCapability('LIST-STATUS')) {
+                foreach ($this->listMailboxes($to_process, Horde_Imap_Client::MBOX_ALL, array('status' => $flags)) as $key => $val) {
+                    if (isset($val['status'])) {
+                        $ret[$key] += $val['status'];
+                    }
+                }
+            } else {
+                foreach ($this->_status($to_process, $flags) as $key => $val) {
+                    $ret[$key] += $val;
+                }
+            }
+        }
+
+        if (!$opts['sort'] || (count($ret) == 1)) {
+            return $ret_array
+                ? $ret
+                : reset($ret);
+        }
+
+        $list_ob = new Horde_Imap_Client_Mailbox_List(array_keys($ret));
+        $sorted = $list_ob->sort(array(
+            'delimiter' => $opts['sort_delimiter']
+        ));
+
+        $out = array();
+        foreach ($sorted as $val) {
+            $out[$val] = $ret[$val];
+        }
+
+        return $out;
+    }
+
+    /**
+     * Obtain status information for mailboxes.
+     *
+     * @param array $mboxes   The list of mailbox objects to query.
+     * @param integer $flags  A bitmask of information requested from the
+     *                        server.
+     *
+     * @return array  See array return for status().
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    abstract protected function _status($mboxes, $flags);
+
+    /**
+     * Perform a STATUS call on multiple mailboxes at the same time.
+     *
+     * This method leverages the LIST-EXTENDED and LIST-STATUS extensions on
+     * the IMAP server to improve the efficiency of this operation.
+     *
+     * @deprecated  Use status() instead.
+     *
+     * @param array $mailboxes  The mailboxes to query. Either
+     *                          Horde_Imap_Client_Mailbox objects, strings
+     *                          (UTF-8), or a combination of the two.
+     * @param integer $flags    See status().
+     * @param array $opts       Additional options:
+     *   - sort: (boolean) If true, sort the list of mailboxes?
+     *           DEFAULT: Do not sort the list.
+     *   - sort_delimiter: (string) If 'sort' is true, this is the delimiter
+     *                     used to sort the mailboxes.
+     *                     DEFAULT: '.'
+     *
+     * @return array  An array with the keys as the mailbox names (UTF-8) and
+     *                the values as arrays with the requested keys (from the
+     *                mask given in $flags).
+     */
+    public function statusMultiple($mailboxes,
+                                   $flags = Horde_Imap_Client::STATUS_ALL,
+                                   array $opts = array())
+    {
+        return $this->status($mailboxes, $flags, $opts);
+    }
+
+    /**
+     * Append message(s) to a mailbox.
+     *
+     * @param mixed $mailbox  The mailbox to append the message(s) to. Either
+     *                        a Horde_Imap_Client_Mailbox object or a string
+     *                        (UTF-8).
+     * @param array $data     The message data to append, along with
+     *                        additional options. An array of arrays with
+     *                        each embedded array having the following
+     *                        entries:
+     * <ul>
+     *  <li>
+     *   data: (mixed) The data to append. If a string or a stream resource,
+     *   this will be used as the entire contents of a single message. If an
+     *   array, will catenate all given parts into a single message. This
+     *   array contains one or more arrays with two keys:
+     *   <ul>
+     *    <li>
+     *     t: (string) Either 'url' or 'text'.
+     *    </li>
+     *    <li>
+     *     v: (mixed) If 't' is 'url', this is the IMAP URL to the message
+     *     part to append. If 't' is 'text', this is either a string or
+     *     resource representation of the message part data.
+     *     DEFAULT: NONE (entry is MANDATORY)
+     *    </li>
+     *   </ul>
+     *  </li>
+     *  <li>
+     *   flags: (array) An array of flags/keywords to set on the appended
+     *   message.
+     *   DEFAULT: Only the \Recent flag is set.
+     *  </li>
+     *  <li>
+     *   internaldate: (DateTime) The internaldate to set for the appended
+     *   message.
+     *   DEFAULT: internaldate will be the same date as when the message was
+     *   appended.
+     *  </li>
+     * </ul>
+     * @param array $options  Additonal options:
+     *   - create: (boolean) Try to create $mailbox if it does not exist?
+     *             DEFAULT: No.
+     *
+     * @return Horde_Imap_Client_Ids  The UIDs of the appended messages.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function append($mailbox, $data, array $options = array())
+    {
+        $this->login();
+
+        $mailbox = Horde_Imap_Client_Mailbox::get($mailbox);
+
+        $ret = $this->_append($mailbox, $data, $options);
+
+        if ($ret instanceof Horde_Imap_Client_Ids) {
+            return $ret;
+        }
+
+        $uids = $this->getIdsOb();
+
+        while (list(,$val) = each($data)) {
+            if (is_string($val['data'])) {
+                $text = $val['data'];
+            } elseif (is_resource($val['data'])) {
+                $text = '';
+                rewind($val['data']);
+                while (!feof($val['data'])) {
+                    $text .= fread($val['data'], 512);
+                    if (preg_match("/\n\r{2,}/", $text)) {
+                        break;
+                    }
+                }
+            }
+
+            $headers = Horde_Mime_Headers::parseHeaders($text);
+            $msgid = $headers->getValue('message-id');
+
+            if ($msgid) {
+                $search_query = new Horde_Imap_Client_Search_Query();
+                $search_query->headerText('Message-ID', $msgid);
+                $uidsearch = $this->search($mailbox, $search_query);
+                $uids->add($uidsearch['match']);
+            }
+        }
+
+        return $uids;
+    }
+
+    /**
+     * Append message(s) to a mailbox.
+     *
+     * @param Horde_Imap_Client_Mailbox $mailbox  The mailbox to append the
+     *                                            message(s) to.
+     * @param array $data                         The message data.
+     * @param array $options                      Additional options.
+     *
+     * @return mixed  A Horde_Imap_Client_Ids object containing the UIDs of
+     *                the appended messages (if server supports UIDPLUS
+     *                extension) or true.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    abstract protected function _append(Horde_Imap_Client_Mailbox $mailbox,
+                                        $data, $options);
+
+    /**
+     * Request a checkpoint of the currently selected mailbox (RFC 3501
+     * [6.4.1]).
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function check()
+    {
+        // CHECK only useful if we are already authenticated.
+        if ($this->_isAuthenticated) {
+            $this->_check();
+        }
+    }
+
+    /**
+     * Request a checkpoint of the currently selected mailbox.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    abstract protected function _check();
+
+    /**
+     * Close the connection to the currently selected mailbox, optionally
+     * expunging all deleted messages (RFC 3501 [6.4.2]).
+     *
+     * @param array $options  Additional options:
+     *   - expunge: (boolean) Expunge all messages flagged as deleted?
+     *              DEFAULT: No
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function close(array $options = array())
+    {
+        // This check catches the non-logged in case.
+        if (is_null($this->_selected)) {
+            return;
+        }
+
+        /* If we are caching, search for deleted messages. */
+        if (!empty($options['expunge']) && $this->_initCache(true)) {
+            /* Make sure mailbox is read-write to expunge. */
+            $this->openMailbox($this->_selected, Horde_Imap_Client::OPEN_READWRITE);
+            if ($this->_mode == Horde_Imap_Client::OPEN_READONLY) {
+                throw new Horde_Imap_Client_Exception(
+                    Horde_Imap_Client_Translation::t("Cannot expunge read-only mailbox."),
+                    Horde_Imap_Client_Exception::MAILBOX_READONLY
+                );
+            }
+
+            $search_query = new Horde_Imap_Client_Search_Query();
+            $search_query->flag(Horde_Imap_Client::FLAG_DELETED, true);
+            $search_res = $this->search($this->_selected, $search_query);
+            $mbox = $this->_selected;
+        } else {
+            $search_res = null;
+        }
+
+        $this->_close($options);
+        $this->_selected = null;
+        $this->_mode = 0;
+
+        if (!is_null($search_res)) {
+            $this->_deleteMsgs($mbox, $search_res['match']);
+        }
+    }
+
+    /**
+     * Close the connection to the currently selected mailbox, optionally
+     * expunging all deleted messages (RFC 3501 [6.4.2]).
+     *
+     * @param array $options  Additional options.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    abstract protected function _close($options);
+
+    /**
+     * Expunge deleted messages from the given mailbox.
+     *
+     * @param mixed $mailbox  The mailbox to expunge. Either a
+     *                        Horde_Imap_Client_Mailbox object or a string
+     *                        (UTF-8).
+     * @param array $options  Additional options:
+     *   - delete: (boolean) If true, will flag all messages in 'ids' as
+     *             deleted (since 2.10.0).
+     *             DEFAULT: false
+     *   - ids: (Horde_Imap_Client_Ids) A list of messages to expunge. These
+     *          messages must already be flagged as deleted (unless 'delete'
+     *          is true).
+     *          DEFAULT: All messages marked as deleted will be expunged.
+     *   - list: (boolean) If true, returns the list of expunged messages
+     *           (UIDs only).
+     *           DEFAULT: false
+     *
+     * @return Horde_Imap_Client_Ids  If 'list' option is true, returns the
+     *                                UID list of expunged messages.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function expunge($mailbox, array $options = array())
+    {
+        // Open mailbox call will handle the login.
+        $this->openMailbox($mailbox, Horde_Imap_Client::OPEN_READWRITE);
+
+        /* Don't expunge if the mailbox is readonly. */
+        if ($this->_mode == Horde_Imap_Client::OPEN_READONLY) {
+            throw new Horde_Imap_Client_Exception(
+                Horde_Imap_Client_Translation::t("Cannot expunge read-only mailbox."),
+                Horde_Imap_Client_Exception::MAILBOX_READONLY
+            );
+        }
+
+        if (empty($options['ids'])) {
+            $options['ids'] = $this->getIdsOb(Horde_Imap_Client_Ids::ALL);
+        } elseif ($options['ids']->isEmpty()) {
+            return $this->getIdsOb();
+        }
+
+        return $this->_expunge($options);
+    }
+
+    /**
+     * Expunge all deleted messages from the given mailbox.
+     *
+     * @param array $options  Additional options.
+     *
+     * @return Horde_Imap_Client_Ids  If 'list' option is true, returns the
+     *                                list of expunged messages.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    abstract protected function _expunge($options);
+
+    /**
+     * Search a mailbox.
+     *
+     * @param mixed $mailbox                         The mailbox to search.
+     *                                               Either a
+     *                                               Horde_Imap_Client_Mailbox
+     *                                               object or a string
+     *                                               (UTF-8).
+     * @param Horde_Imap_Client_Search_Query $query  The search query.
+     *                                               Defaults to an ALL
+     *                                               search.
+     * @param array $options                         Additional options:
+     * <ul>
+     *  <li>
+     *   nocache: (boolean) Don't cache the results.
+     *   DEFAULT: false (results cached, if possible)
+     *  </li>
+     *  <li>
+     *   partial: (mixed) The range of results to return (message sequence
+     *   numbers).
+     *   DEFAULT: All messages are returned.
+     *  </li>
+     *  <li>
+     *   results: (array) The data to return. Consists of zero or more of
+     *   the following flags:
+     *   <ul>
+     *    <li>Horde_Imap_Client::SEARCH_RESULTS_COUNT</li>
+     *    <li>Horde_Imap_Client::SEARCH_RESULTS_MATCH (DEFAULT)</li>
+     *    <li>Horde_Imap_Client::SEARCH_RESULTS_MAX</li>
+     *    <li>Horde_Imap_Client::SEARCH_RESULTS_MIN</li>
+     *    <li>Horde_Imap_Client::SEARCH_RESULTS_SAVE</li>
+     *    <li>Horde_Imap_Client::SEARCH_RESULTS_RELEVANCY</li>
+     *   </ul>
+     *  </li>
+     *  <li>
+     *   sequence: (boolean) If true, returns an array of sequence numbers.
+     *   DEFAULT: Returns an array of UIDs
+     *  </li>
+     *  <li>
+     *   sort: (array) Sort the returned list of messages. Multiple sort
+     *   criteria can be specified. Any sort criteria can be sorted in reverse
+     *   order (instead of the default ascending order) by adding a
+     *   Horde_Imap_Client::SORT_REVERSE element to the array directly before
+     *   adding the sort element. The following sort criteria are available:
+     *   <ul>
+     *    <li>Horde_Imap_Client::SORT_ARRIVAL</li>
+     *    <li>Horde_Imap_Client::SORT_CC</li>
+     *    <li>Horde_Imap_Client::SORT_DATE</li>
+     *    <li>Horde_Imap_Client::SORT_DISPLAYFROM
+     *     <ul>
+     *      <li>
+     *       On servers that don't support SORT=DISPLAY, this criteria will
+     *       fallback to doing client-side sorting.
+     *      </li>
+     *     </ul>
+     *    </li>
+     *    <li>Horde_Imap_Client::SORT_DISPLAYFROM_FALLBACK
+     *     <ul>
+     *      <li>
+     *       On servers that don't support SORT=DISPLAY, this criteria will
+     *       fallback to Horde_Imap_Client::SORT_FROM [since 2.4.0].
+     *      </li>
+     *     </ul>
+     *    </li>
+     *    <li>Horde_Imap_Client::SORT_DISPLAYTO
+     *     <ul>
+     *      <li>
+     *       On servers that don't support SORT=DISPLAY, this criteria will
+     *       fallback to doing client-side sorting.
+     *      </li>
+     *     </ul>
+     *    </li>
+     *    <li>Horde_Imap_Client::SORT_DISPLAYTO_FALLBACK
+     *     <ul>
+     *      <li>
+     *       On servers that don't support SORT=DISPLAY, this criteria will
+     *       fallback to Horde_Imap_Client::SORT_TO [since 2.4.0].
+     *      </li>
+     *     </ul>
+     *    </li>
+     *    <li>Horde_Imap_Client::SORT_FROM</li>
+     *    <li>Horde_Imap_Client::SORT_SEQUENCE</li>
+     *    <li>Horde_Imap_Client::SORT_SIZE</li>
+     *    <li>Horde_Imap_Client::SORT_SUBJECT</li>
+     *    <li>Horde_Imap_Client::SORT_TO</li>
+     *    <li>
+     *     [On servers that support SEARCH=FUZZY, this criteria is also
+     *     available:]
+     *     <ul>
+     *      <li>Horde_Imap_Client::SORT_RELEVANCY</li>
+     *     </ul>
+     *    </li>
+     *   </ul>
+     *  </li>
+     * </ul>
+     *
+     * @return array  An array with the following keys:
+     *   - count: (integer) The number of messages that match the search
+     *            criteria. Always returned.
+     *   - match: (Horde_Imap_Client_Ids) The IDs that match $criteria, sorted
+     *            if the 'sort' modifier was set. Returned if
+     *            Horde_Imap_Client::SEARCH_RESULTS_MATCH is set.
+     *   - max: (integer) The UID (default) or message sequence number (if
+     *          'sequence' is true) of the highest message that satisifies
+     *          $criteria. Returns null if no matches found. Returned if
+     *          Horde_Imap_Client::SEARCH_RESULTS_MAX is set.
+     *   - min: (integer) The UID (default) or message sequence number (if
+     *          'sequence' is true) of the lowest message that satisifies
+     *          $criteria. Returns null if no matches found. Returned if
+     *          Horde_Imap_Client::SEARCH_RESULTS_MIN is set.
+     *   - modseq: (integer) The highest mod-sequence for all messages being
+     *            returned. Returned if 'sort' is false, the search query
+     *            includes a MODSEQ command, and the server supports the
+     *            CONDSTORE IMAP extension.
+     *   - relevancy: (array) The list of relevancy scores. Returned if
+     *                Horde_Imap_Client::SEARCH_RESULTS_RELEVANCY is set and
+     *                the server supports FUZZY search matching.
+     *   - save: (boolean) Whether the search results were saved. Returned if
+     *           Horde_Imap_Client::SEARCH_RESULTS_SAVE is set.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function search($mailbox, $query = null, array $options = array())
+    {
+        $this->login();
+
+        if (empty($options['results'])) {
+            $options['results'] = array(
+                Horde_Imap_Client::SEARCH_RESULTS_MATCH,
+                Horde_Imap_Client::SEARCH_RESULTS_COUNT
+            );
+        }
+
+        // Default to an ALL search.
+        if (is_null($query)) {
+            $query = new Horde_Imap_Client_Search_Query();
+        }
+
+        // Check for SEARCHRES support.
+        if ((($pos = array_search(Horde_Imap_Client::SEARCH_RESULTS_SAVE, $options['results'])) !== false) &&
+            !$this->queryCapability('SEARCHRES')) {
+            unset($options['results'][$pos]);
+        }
+
+        // Check for SORT-related options.
+        if (!empty($options['sort'])) {
+            $sort = $this->queryCapability('SORT');
+            foreach ($options['sort'] as $key => $val) {
+                switch ($val) {
+                case Horde_Imap_Client::SORT_DISPLAYFROM_FALLBACK:
+                    $options['sort'][$key] = (!is_array($sort) || !in_array('DISPLAY', $sort))
+                        ? Horde_Imap_Client::SORT_FROM
+                        : Horde_Imap_Client::SORT_DISPLAYFROM;
+                    break;
+
+                case Horde_Imap_Client::SORT_DISPLAYTO_FALLBACK:
+                    $options['sort'][$key] = (!is_array($sort) || !in_array('DISPLAY', $sort))
+                        ? Horde_Imap_Client::SORT_TO
+                        : Horde_Imap_Client::SORT_DISPLAYTO;
+                    break;
+                }
+            }
+        }
+
+        // Check for supported charset.
+        $options['_query'] = $query->build($this->capability());
+        if (!is_null($options['_query']['charset']) &&
+            array_key_exists($options['_query']['charset'], $this->_init['s_charset']) &&
+            !$this->_init['s_charset'][$options['_query']['charset']]) {
+            foreach (array_merge(array_keys(array_filter($this->_init['s_charset'])), array('US-ASCII')) as $val) {
+                try {
+                    $new_query = clone $query;
+                    $new_query->charset($val);
+                    break;
+                } catch (Horde_Imap_Client_Exception_SearchCharset $e) {
+                    unset($new_query);
+                }
+            }
+
+            if (!isset($new_query)) {
+                throw $e;
+            }
+
+            $query = $new_query;
+            $options['_query'] = $query->build($this->capability());
+        }
+
+        /* RFC 6203: MUST NOT request relevancy results if we are not using
+         * FUZZY searching. */
+        if (in_array(Horde_Imap_Client::SEARCH_RESULTS_RELEVANCY, $options['results']) &&
+            !in_array('SEARCH=FUZZY', $options['_query']['exts_used'])) {
+            throw new InvalidArgumentException('Cannot specify RELEVANCY results if not doing a FUZZY search.');
+        }
+
+        /* Optimization - if query is just for a count of either RECENT or
+         * ALL messages, we can send status information instead. Can't
+         * optimize with unseen queries because we may cause an infinite loop
+         * between here and the status() call. */
+        if ((count($options['results']) == 1) &&
+            (reset($options['results']) == Horde_Imap_Client::SEARCH_RESULTS_COUNT)) {
+            switch ($options['_query']['query']) {
+            case 'ALL':
+                $ret = $this->status($this->_selected, Horde_Imap_Client::STATUS_MESSAGES);
+                return array('count' => $ret['messages']);
+
+            case 'RECENT':
+                $ret = $this->status($this->_selected, Horde_Imap_Client::STATUS_RECENT);
+                return array('count' => $ret['recent']);
+            }
+        }
+
+        $this->openMailbox($mailbox, Horde_Imap_Client::OPEN_AUTO);
+
+        /* Take advantage of search result caching.  If CONDSTORE available,
+         * we can cache all queries and invalidate the cache when the MODSEQ
+         * changes. If CONDSTORE not available, we can only store queries
+         * that don't involve flags. We store results by hashing the options
+         * array - the generated query is already added to '_query' key
+         * above. */
+        $cache = null;
+        if (empty($options['nocache']) &&
+            $this->_initCache(true) &&
+            (isset($this->_temp['enabled']['CONDSTORE']) ||
+             !$query->flagSearch())) {
+            $cache = $this->_getSearchCache('search', $options);
+            if (isset($cache['data'])) {
+                if (isset($cache['data']['match'])) {
+                    $cache['data']['match'] = $this->getIdsOb($cache['data']['match']);
+                }
+                return $cache['data'];
+            }
+        }
+
+        /* Optimization: Catch when there are no messages in a mailbox. */
+        $status_res = $this->status($this->_selected, Horde_Imap_Client::STATUS_MESSAGES | Horde_Imap_Client::STATUS_HIGHESTMODSEQ);
+        if ($status_res['messages'] ||
+            in_array(Horde_Imap_Client::SEARCH_RESULTS_SAVE, $options['results'])) {
+            /* RFC 4551 [3.1] - trying to do a MODSEQ SEARCH on a mailbox that
+             * doesn't support it will return BAD. */
+            if (in_array('CONDSTORE', $options['_query']['exts']) &&
+                !$this->_mailboxOb()->getStatus(Horde_Imap_Client::STATUS_HIGHESTMODSEQ)) {
+                throw new Horde_Imap_Client_Exception(
+                    Horde_Imap_Client_Translation::t("Mailbox does not support mod-sequences."),
+                    Horde_Imap_Client_Exception::MBOXNOMODSEQ
+                );
+            }
+
+            $ret = $this->_search($query, $options);
+        } else {
+            $ret = array(
+                'count' => 0
+            );
+
+            foreach ($options['results'] as $val) {
+                switch ($val) {
+                case Horde_Imap_Client::SEARCH_RESULTS_MATCH:
+                    $ret['match'] = $this->getIdsOb();
+                    break;
+
+                case Horde_Imap_Client::SEARCH_RESULTS_MAX:
+                    $ret['max'] = null;
+                    break;
+
+                case Horde_Imap_Client::SEARCH_RESULTS_MIN:
+                    $ret['min'] = null;
+                    break;
+
+                case Horde_Imap_Client::SEARCH_RESULTS_MIN:
+                    if (isset($status_res['highestmodseq'])) {
+                        $ret['modseq'] = $status_res['highestmodseq'];
+                    }
+                    break;
+
+                case Horde_Imap_Client::SEARCH_RESULTS_RELEVANCY:
+                    $ret['relevancy'] = array();
+                    break;
+                }
+            }
+        }
+
+        if ($cache) {
+            $save = $ret;
+            if (isset($save['match'])) {
+                $save['match'] = strval($ret['match']);
+            }
+            $this->_setSearchCache($save, $cache);
+        }
+
+        return $ret;
+    }
+
+    /**
+     * Search a mailbox.
+     *
+     * @param object $query   The search query.
+     * @param array $options  Additional options. The '_query' key contains
+     *                        the value of $query->build().
+     *
+     * @return Horde_Imap_Client_Ids  An array of IDs.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    abstract protected function _search($query, $options);
+
+    /**
+     * Set the comparator to use for searching/sorting (RFC 5255).
+     *
+     * @param string $comparator  The comparator string (see RFC 4790 [3.1] -
+     *                            "collation-id" - for format). The reserved
+     *                            string 'default' can be used to select
+     *                            the default comparator.
+     *
+     * @throws Horde_Imap_Client_Exception
+     * @throws Horde_Imap_Client_Exception_NoSupportExtension
+     */
+    public function setComparator($comparator = null)
+    {
+        $comp = is_null($comparator)
+            ? $this->getParam('comparator')
+            : $comparator;
+        if (is_null($comp)) {
+            return;
+        }
+
+        $this->login();
+
+        $i18n = $this->queryCapability('I18NLEVEL');
+        if (empty($i18n) || (max($i18n) < 2)) {
+            throw new Horde_Imap_Client_Exception_NoSupportExtension(
+                'I18NLEVEL',
+                'The IMAP server does not support changing SEARCH/SORT comparators.'
+            );
+        }
+
+        $this->_setComparator($comp);
+    }
+
+    /**
+     * Set the comparator to use for searching/sorting (RFC 5255).
+     *
+     * @param string $comparator  The comparator string (see RFC 4790 [3.1] -
+     *                            "collation-id" - for format). The reserved
+     *                            string 'default' can be used to select
+     *                            the default comparator.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    abstract protected function _setComparator($comparator);
+
+    /**
+     * Get the comparator used for searching/sorting (RFC 5255).
+     *
+     * @return mixed  Null if the default comparator is being used, or an
+     *                array of comparator information (see RFC 5255 [4.8]).
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function getComparator()
+    {
+        $this->login();
+
+        $i18n = $this->queryCapability('I18NLEVEL');
+        if (empty($i18n) || (max($i18n) < 2)) {
+            return null;
+        }
+
+        return $this->_getComparator();
+    }
+
+    /**
+     * Get the comparator used for searching/sorting (RFC 5255).
+     *
+     * @return mixed  Null if the default comparator is being used, or an
+     *                array of comparator information (see RFC 5255 [4.8]).
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    abstract protected function _getComparator();
+
+    /**
+     * Thread sort a given list of messages (RFC 5256).
+     *
+     * @param mixed $mailbox  The mailbox to query. Either a
+     *                        Horde_Imap_Client_Mailbox object or a string
+     *                        (UTF-8).
+     * @param array $options  Additional options:
+     * <ul>
+     *  <li>
+     *   criteria: (mixed) The following thread criteria are available:
+     *   <ul>
+     *    <li>Horde_Imap_Client::THREAD_ORDEREDSUBJECT</li>
+     *    <li>Horde_Imap_Client::THREAD_REFERENCES</li>
+     *    <li>Horde_Imap_Client::THREAD_REFS</li>
+     *    <li>
+     *     Other algorithms can be explicitly specified by passing the IMAP
+     *     thread algorithm in as a string value.
+     *    </li>
+     *   </ul>
+     *   DEFAULT: Horde_Imap_Client::THREAD_ORDEREDSUBJECT
+     *  </li>
+     *  <li>
+     *   search: (Horde_Imap_Client_Search_Query) The search query.
+     *   DEFAULT: All messages in mailbox included in thread sort.
+     *  </li>
+     *  <li>
+     *   sequence: (boolean) If true, each message is stored and referred to
+     *   by its message sequence number.
+     *   DEFAULT: Stored/referred to by UID.
+     *  </li>
+     * </ul>
+     *
+     * @return Horde_Imap_Client_Data_Thread  A thread data object.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function thread($mailbox, array $options = array())
+    {
+        // Open mailbox call will handle the login.
+        $this->openMailbox($mailbox, Horde_Imap_Client::OPEN_AUTO);
+
+        /* Take advantage of search result caching.  If CONDSTORE available,
+         * we can cache all queries and invalidate the cache when the MODSEQ
+         * changes. If CONDSTORE not available, we can only store queries
+         * that don't involve flags. See search() for similar caching. */
+        $cache = null;
+        if ($this->_initCache(true) &&
+            (isset($this->_temp['enabled']['CONDSTORE']) ||
+             empty($options['search']) ||
+             !$options['search']->flagSearch())) {
+            $cache = $this->_getSearchCache('thread', $options);
+            if (isset($cache['data']) &&
+                ($cache['data'] instanceof Horde_Imap_Client_Data_Thread)) {
+                return $cache['data'];
+            }
+        }
+
+        $status_res = $this->status($this->_selected, Horde_Imap_Client::STATUS_MESSAGES);
+
+        $ob = $status_res['messages']
+            ? $this->_thread($options)
+            : new Horde_Imap_Client_Data_Thread(array(), empty($options['sequence']) ? 'uid' : 'sequence');
+
+        if ($cache) {
+            $this->_setSearchCache($ob, $cache);
+        }
+
+        return $ob;
+    }
+
+    /**
+     * Thread sort a given list of messages (RFC 5256).
+     *
+     * @param array $options  Additional options. See thread().
+     *
+     * @return Horde_Imap_Client_Data_Thread  A thread data object.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    abstract protected function _thread($options);
+
+    /**
+     * Fetch message data (see RFC 3501 [6.4.5]).
+     *
+     * @param mixed $mailbox                        The mailbox to search.
+     *                                              Either a
+     *                                              Horde_Imap_Client_Mailbox
+     *                                              object or a string (UTF-8).
+     * @param Horde_Imap_Client_Fetch_Query $query  Fetch query object.
+     * @param array $options                        Additional options:
+     *   - changedsince: (integer) Only return messages that have a
+     *                   mod-sequence larger than this value. This option
+     *                   requires the CONDSTORE IMAP extension (if not present,
+     *                   this value is ignored). Additionally, the mailbox
+     *                   must support mod-sequences or an exception will be
+     *                   thrown. If valid, this option implicity adds the
+     *                   mod-sequence fetch criteria to the fetch command.
+     *                   DEFAULT: Mod-sequence values are ignored.
+     *   - exists: (boolean) Ensure that all ids returned exist on the server.
+     *             If false, the list of ids returned in the results object
+     *             is not guaranteed to reflect the current state of the
+     *             remote mailbox.
+     *             DEFAULT: false
+     *   - ids: (Horde_Imap_Client_Ids) A list of messages to fetch data from.
+     *          DEFAULT: All messages in $mailbox will be fetched.
+     *   - nocache: (boolean) If true, will not cache the results (previously
+     *              cached data will still be used to generate results) [since
+     *              2.8.0].
+     *              DEFAULT: false
+     *
+     * @return Horde_Imap_Client_Fetch_Results  A results object.
+     *
+     * @throws Horde_Imap_Client_Exception
+     * @throws Horde_Imap_Client_Exception_NoSupportExtension
+     */
+    public function fetch($mailbox, $query, array $options = array())
+    {
+        try {
+            $ret = $this->_fetchWrapper($mailbox, $query, $options);
+            unset($this->_temp['fetch_nocache']);
+            return $ret;
+        } catch (Exception $e) {
+            unset($this->_temp['fetch_nocache']);
+            throw $e;
+        }
+    }
+
+    /**
+     * Wrapper for fetch() to allow internal state to be rest on exception.
+     *
+     * @internal
+     * @see fetch()
+     */
+    private function _fetchWrapper($mailbox, $query, $options)
+    {
+        $this->login();
+
+        $query = clone $query;
+
+        $cache_array = $header_cache = $new_query = array();
+
+        if (empty($options['ids'])) {
+            $options['ids'] = $this->getIdsOb(Horde_Imap_Client_Ids::ALL);
+        } elseif ($options['ids']->isEmpty()) {
+            return new Horde_Imap_Client_Fetch_Results($this->_fetchDataClass);
+        } elseif ($options['ids']->search_res &&
+                  !$this->queryCapability('SEARCHRES')) {
+            /* SEARCHRES requires server support. */
+            throw new Horde_Imap_Client_Exception_NoSupportExtension('SEARCHRES');
+        }
+
+        $this->openMailbox($mailbox, Horde_Imap_Client::OPEN_AUTO);
+        $mbox_ob = $this->_mailboxOb();
+
+        if (!empty($options['nocache'])) {
+            $this->_temp['fetch_nocache'] = true;
+        }
+
+        $cf = $this->_initCache(true)
+            ? $this->_cacheFields()
+            : array();
+
+        if (!empty($cf)) {
+            /* If using cache, we store by UID so we need to return UIDs. */
+            $query->uid();
+        }
+
+        $modseq_check = !empty($options['changedsince']);
+        if ($query->contains(Horde_Imap_Client::FETCH_MODSEQ)) {
+            if (!isset($this->_temp['enabled']['CONDSTORE'])) {
+                unset($query[Horde_Imap_Client::FETCH_MODSEQ]);
+            } elseif (empty($options['changedsince'])) {
+                $modseq_check = true;
+            }
+        }
+
+        if ($modseq_check &&
+            !$mbox_ob->getStatus(Horde_Imap_Client::STATUS_HIGHESTMODSEQ)) {
+            /* RFC 4551 [3.1] - trying to do a MODSEQ FETCH on a mailbox that
+             * doesn't support it will return BAD. */
+            throw new Horde_Imap_Client_Exception(
+                Horde_Imap_Client_Translation::t("Mailbox does not support mod-sequences."),
+                Horde_Imap_Client_Exception::MBOXNOMODSEQ
+            );
+        }
+
+        /* Determine if caching is available and if anything in $query is
+         * cacheable. */
+        foreach ($cf as $k => $v) {
+            if (isset($query[$k])) {
+                switch ($k) {
+                case Horde_Imap_Client::FETCH_ENVELOPE:
+                case Horde_Imap_Client::FETCH_FLAGS:
+                case Horde_Imap_Client::FETCH_IMAPDATE:
+                case Horde_Imap_Client::FETCH_SIZE:
+                case Horde_Imap_Client::FETCH_STRUCTURE:
+                    $cache_array[$k] = $v;
+                    break;
+
+                case Horde_Imap_Client::FETCH_HEADERS:
+                    $this->_temp['headers_caching'] = array();
+
+                    foreach ($query[$k] as $key => $val) {
+                        /* Only cache if directly requested.  Iterate through
+                         * requests to ensure at least one can be cached. */
+                        if (!empty($val['cache']) && !empty($val['peek'])) {
+                            $cache_array[$k] = $v;
+                            ksort($val);
+                            $header_cache[$key] = hash('md5', serialize($val));
+                        }
+                    }
+                    break;
+                }
+            }
+        }
+
+        $ret = new Horde_Imap_Client_Fetch_Results(
+            $this->_fetchDataClass,
+            $options['ids']->sequence ? Horde_Imap_Client_Fetch_Results::SEQUENCE : Horde_Imap_Client_Fetch_Results::UID
+        );
+
+        /* If nothing is cacheable, we can do a straight search. */
+        if (empty($cache_array)) {
+            $options['_query'] = $query;
+            $this->_fetch($ret, array($options));
+            return $ret;
+        }
+
+        $cs_ret = empty($options['changedsince'])
+            ? null
+            : clone $ret;
+
+        /* Convert special searches to UID lists and create mapping. */
+        $ids = $this->resolveIds($this->_selected, $options['ids'], empty($options['exists']) ? 1 : 2);
+
+        /* Add non-user settable cache fields. */
+        $cache_array[Horde_Imap_Client::FETCH_DOWNGRADED] = self::CACHE_DOWNGRADED;
+
+        /* Get the cached values. */
+        $data = $this->_cache->get($this->_selected, $ids->ids, array_values($cache_array), $mbox_ob->getStatus(Horde_Imap_Client::STATUS_UIDVALIDITY));
+
+        /* Build a list of what we still need. */
+        $map = array_flip($mbox_ob->map->map);
+        $sequence = $options['ids']->sequence;
+        foreach ($ids as $uid) {
+            $crit = clone $query;
+
+            if ($sequence) {
+                if (!isset($map[$uid])) {
+                    continue;
+                }
+                $entry_idx = $map[$uid];
+            } else {
+                $entry_idx = $uid;
+                unset($crit[Horde_Imap_Client::FETCH_UID]);
+            }
+
+            $entry = $ret->get($entry_idx);
+
+            if (isset($map[$uid])) {
+                $entry->setSeq($map[$uid]);
+                unset($crit[Horde_Imap_Client::FETCH_SEQ]);
+            }
+
+            $entry->setUid($uid);
+
+            foreach ($cache_array as $key => $cid) {
+                switch ($key) {
+                case Horde_Imap_Client::FETCH_DOWNGRADED:
+                    if (!empty($data[$uid][$cid])) {
+                        $entry->setDowngraded(true);
+                    }
+                    break;
+
+                case Horde_Imap_Client::FETCH_ENVELOPE:
+                    if (isset($data[$uid][$cid]) &&
+                        ($data[$uid][$cid] instanceof Horde_Imap_Client_Data_Envelope)) {
+                        $entry->setEnvelope($data[$uid][$cid]);
+                        unset($crit[$key]);
+                    }
+                    break;
+
+                case Horde_Imap_Client::FETCH_FLAGS:
+                    if (isset($data[$uid][$cid]) &&
+                        is_array($data[$uid][$cid])) {
+                        $entry->setFlags($data[$uid][$cid]);
+                        unset($crit[$key]);
+                    }
+                    break;
+
+                case Horde_Imap_Client::FETCH_HEADERS:
+                    foreach ($header_cache as $hkey => $hval) {
+                        if (isset($data[$uid][$cid][$hval])) {
+                            /* We have found a cached entry with the same
+                             * MD5 sum. */
+                            $entry->setHeaders($hkey, $data[$uid][$cid][$hval]);
+                            $crit->remove($key, $hkey);
+                        } else {
+                            $this->_temp['headers_caching'][$hkey] = $hval;
+                        }
+                    }
+                    break;
+
+                case Horde_Imap_Client::FETCH_IMAPDATE:
+                    if (isset($data[$uid][$cid]) &&
+                        ($data[$uid][$cid] instanceof Horde_Imap_Client_DateTime)) {
+                        $entry->setImapDate($data[$uid][$cid]);
+                        unset($crit[$key]);
+                    }
+                    break;
+
+                case Horde_Imap_Client::FETCH_SIZE:
+                    if (isset($data[$uid][$cid])) {
+                        $entry->setSize($data[$uid][$cid]);
+                        unset($crit[$key]);
+                    }
+                    break;
+
+                case Horde_Imap_Client::FETCH_STRUCTURE:
+                    if (isset($data[$uid][$cid]) &&
+                        ($data[$uid][$cid] instanceof Horde_Mime_Part)) {
+                        $entry->setStructure($data[$uid][$cid]);
+                        unset($crit[$key]);
+                    }
+                    break;
+                }
+            }
+
+            if (count($crit)) {
+                $sig = $crit->hash();
+                if (isset($new_query[$sig])) {
+                    $new_query[$sig]['i'][] = $entry_idx;
+                } else {
+                    $new_query[$sig] = array(
+                        'c' => $crit,
+                        'i' => array($entry_idx)
+                    );
+                }
+            }
+        }
+
+        $to_fetch = array();
+        foreach ($new_query as $val) {
+            $ids_ob = $this->getIdsOb(null, $sequence);
+            $ids_ob->duplicates = true;
+            $ids_ob->add($val['i']);
+            $to_fetch[] = array_merge($options, array(
+                '_query' => $val['c'],
+                'ids' => $ids_ob
+            ));
+        }
+
+        if (!empty($to_fetch)) {
+            $this->_fetch(is_null($cs_ret) ? $ret : $cs_ret, $to_fetch);
+        }
+
+        if (is_null($cs_ret)) {
+            return $ret;
+        }
+
+        /* If doing changedsince query, and all other data is cached, we still
+         * need to hit IMAP server to determine proper results set. */
+        if (empty($new_query)) {
+            $squery = new Horde_Imap_Client_Search_Query();
+            $squery->modseq($options['changedsince'] + 1);
+            $squery->ids($options['ids']);
+
+            $cs = $this->search($this->_selected, $squery, array(
+                'sequence' => $sequence
+            ));
+
+            foreach ($cs['match'] as $val) {
+                $entry = $ret->get($val);
+                if ($sequence) {
+                    $entry->setSeq($val);
+                } else {
+                    $entry->setUid($val);
+                }
+                $cs_ret[$val] = $entry;
+            }
+        } else {
+            foreach ($cs_ret as $key => $val) {
+                $val->merge($ret->get($key));
+            }
+        }
+
+        return $cs_ret;
+    }
+
+    /**
+     * Fetch message data.
+     *
+     * Fetch queries should be grouped in the $queries argument. Each value
+     * is an array of fetch options, with the fetch query stored in the
+     * '_query' parameter. IMPORTANT: All queries must have the same ID
+     * type (either sequence or UID).
+     *
+     * @param Horde_Imap_Client_Fetch_Results $results  Fetch results.
+     * @param array $queries                            The list of queries.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    abstract protected function _fetch(Horde_Imap_Client_Fetch_Results $results,
+                                       $queries);
+
+    /**
+     * Get the list of vanished messages (UIDs that have been expunged since a
+     * given mod-sequence value).
+     *
+     * @param mixed $mailbox   The mailbox to query. Either a
+     *                         Horde_Imap_Client_Mailbox object or a string
+     *                         (UTF-8).
+     * @param integer $modseq  Search for expunged messages after this
+     *                         mod-sequence value.
+     * @param array $opts      Additional options:
+     *   - ids: (Horde_Imap_Client_Ids)  Restrict to these UIDs.
+     *          DEFAULT: Returns full list of UIDs vanished (QRESYNC only).
+     *                   This option is REQUIRED for non-QRESYNC servers or
+     *                   else an empty list will be returned.
+     *
+     * @return Horde_Imap_Client_Ids  List of UIDs that have vanished.
+     *
+     * @throws Horde_Imap_Client_NoSupportExtension
+     */
+    public function vanished($mailbox, $modseq, array $opts = array())
+    {
+        $this->login();
+
+        $qresync = $this->queryCapability('QRESYNC');
+
+        if (empty($opts['ids'])) {
+            if (!$qresync) {
+                return $this->getIdsOb();
+            }
+            $opts['ids'] = $this->getIdsOb(Horde_Imap_Client_Ids::ALL);
+        } elseif ($opts['ids']->isEmpty()) {
+            return $this->getIdsOb();
+        } elseif ($opts['ids']->sequence) {
+            throw new InvalidArgumentException('Vanished requires UIDs.');
+        }
+
+        $this->openMailbox($mailbox, Horde_Imap_Client::OPEN_AUTO);
+
+        if ($qresync) {
+            if (!$this->_mailboxOb()->getStatus(Horde_Imap_Client::STATUS_HIGHESTMODSEQ)) {
+                throw new Horde_Imap_Client_Exception(
+                    Horde_Imap_Client_Translation::t("Mailbox does not support mod-sequences."),
+                    Horde_Imap_Client_Exception::MBOXNOMODSEQ
+                );
+            }
+
+            return $this->_vanished(max(1, $modseq), $opts['ids']);
+        }
+
+        $ids = $this->resolveIds($mailbox, $opts['ids']);
+
+        $squery = new Horde_Imap_Client_Search_Query();
+        $squery->ids($this->getIdsOb($ids->range_string));
+        $search = $this->search($mailbox, $squery, array(
+            'nocache' => true
+        ));
+
+        return $this->getIdsOb(array_diff($ids->ids, $search['match']->ids));
+    }
+
+    /**
+     * Get the list of vanished messages.
+     *
+     * @param integer $modseq             Mod-sequence value.
+     * @param Horde_Imap_Client_Ids $ids  UIDs.
+     *
+     * @return Horde_Imap_Client_Ids  List of UIDs that have vanished.
+     */
+    abstract protected function _vanished($modseq, Horde_Imap_Client_Ids $ids);
+
+    /**
+     * Store message flag data (see RFC 3501 [6.4.6]).
+     *
+     * @param mixed $mailbox  The mailbox containing the messages to modify.
+     *                        Either a Horde_Imap_Client_Mailbox object or a
+     *                        string (UTF-8).
+     * @param array $options  Additional options:
+     *   - add: (array) An array of flags to add.
+     *          DEFAULT: No flags added.
+     *   - ids: (Horde_Imap_Client_Ids) The list of messages to modify.
+     *          DEFAULT: All messages in $mailbox will be modified.
+     *   - remove: (array) An array of flags to remove.
+     *             DEFAULT: No flags removed.
+     *   - replace: (array) Replace the current flags with this set
+     *              of flags. Overrides both the 'add' and 'remove' options.
+     *              DEFAULT: No replace is performed.
+     *   - unchangedsince: (integer) Only changes flags if the mod-sequence ID
+     *                     of the message is equal or less than this value.
+     *                     Requires the CONDSTORE IMAP extension on the server.
+     *                     Also requires the mailbox to support mod-sequences.
+     *                     Will throw an exception if either condition is not
+     *                     met.
+     *                     DEFAULT: mod-sequence is ignored when applying
+     *                              changes
+     *
+     * @return Horde_Imap_Client_Ids  A Horde_Imap_Client_Ids object
+     *                                containing the list of IDs that failed
+     *                                the 'unchangedsince' test.
+     *
+     * @throws Horde_Imap_Client_Exception
+     * @throws Horde_Imap_Client_Exception_NoSupportExtension
+     */
+    public function store($mailbox, array $options = array())
+    {
+        // Open mailbox call will handle the login.
+        $this->openMailbox($mailbox, Horde_Imap_Client::OPEN_READWRITE);
+
+        /* SEARCHRES requires server support. */
+        if (empty($options['ids'])) {
+            $options['ids'] = $this->getIdsOb(Horde_Imap_Client_Ids::ALL);
+        } elseif ($options['ids']->isEmpty()) {
+            return $this->getIdsOb();
+        } elseif ($options['ids']->search_res &&
+                  !$this->queryCapability('SEARCHRES')) {
+            throw new Horde_Imap_Client_Exception_NoSupportExtension('SEARCHRES');
+        }
+
+        if (!empty($options['unchangedsince'])) {
+            if (!isset($this->_temp['enabled']['CONDSTORE'])) {
+                throw new Horde_Imap_Client_Exception_NoSupportExtension('CONDSTORE');
+            }
+
+            /* RFC 4551 [3.1] - trying to do a UNCHANGEDSINCE STORE on a
+             * mailbox that doesn't support it will return BAD. */
+            if (!$this->_mailboxOb()->getStatus(Horde_Imap_Client::STATUS_HIGHESTMODSEQ)) {
+                throw new Horde_Imap_Client_Exception(
+                    Horde_Imap_Client_Translation::t("Mailbox does not support mod-sequences."),
+                    Horde_Imap_Client_Exception::MBOXNOMODSEQ
+                );
+            }
+        }
+
+        return $this->_store($options);
+    }
+
+    /**
+     * Store message flag data.
+     *
+     * @param array $options  Additional options.
+     *
+     * @return Horde_Imap_Client_Ids  A Horde_Imap_Client_Ids object
+     *                                containing the list of IDs that failed
+     *                                the 'unchangedsince' test.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    abstract protected function _store($options);
+
+    /**
+     * Copy messages to another mailbox.
+     *
+     * @param mixed $source   The source mailbox. Either a
+     *                        Horde_Imap_Client_Mailbox object or a string
+     *                        (UTF-8).
+     * @param mixed $dest     The destination mailbox. Either a
+     *                        Horde_Imap_Client_Mailbox object or a string
+     *                        (UTF-8).
+     * @param array $options  Additional options:
+     *   - create: (boolean) Try to create $dest if it does not exist?
+     *             DEFAULT: No.
+     *   - ids: (Horde_Imap_Client_Ids) The list of messages to copy.
+     *          DEFAULT: All messages in $mailbox will be copied.
+     *   - move: (boolean) If true, delete the original messages.
+     *           DEFAULT: Original messages are not deleted.
+     *
+     * @return mixed  An array mapping old UIDs (keys) to new UIDs (values) on
+     *                success (if the IMAP server and/or driver support the
+     *                UIDPLUS extension) or true.
+     *
+     * @throws Horde_Imap_Client_Exception
+     * @throws Horde_Imap_Client_Exception_NoSupportExtension
+     */
+    public function copy($source, $dest, array $options = array())
+    {
+        // Open mailbox call will handle the login.
+        $this->openMailbox($source, empty($options['move']) ? Horde_Imap_Client::OPEN_AUTO : Horde_Imap_Client::OPEN_READWRITE);
+
+        /* SEARCHRES requires server support. */
+        if (empty($options['ids'])) {
+            $options['ids'] = $this->getIdsOb(Horde_Imap_Client_Ids::ALL);
+        } elseif ($options['ids']->isEmpty()) {
+            return array();
+        } elseif ($options['ids']->search_res &&
+                  !$this->queryCapability('SEARCHRES')) {
+            throw new Horde_Imap_Client_Exception_NoSupportExtension('SEARCHRES');
+        }
+
+        return $this->_copy(Horde_Imap_Client_Mailbox::get($dest), $options);
+    }
+
+    /**
+     * Copy messages to another mailbox.
+     *
+     * @param Horde_Imap_Client_Mailbox $dest  The destination mailbox.
+     * @param array $options                   Additional options.
+     *
+     * @return mixed  An array mapping old UIDs (keys) to new UIDs (values) on
+     *                success (if the IMAP server and/or driver support the
+     *                UIDPLUS extension) or true.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    abstract protected function _copy(Horde_Imap_Client_Mailbox $dest,
+                                      $options);
+
+    /**
+     * Set quota limits. The server must support the IMAP QUOTA extension
+     * (RFC 2087).
+     *
+     * @param mixed $root       The quota root. Either a
+     *                          Horde_Imap_Client_Mailbox object or a string
+     *                          (UTF-8).
+     * @param array $resources  The resource values to set. Keys are the
+     *                          resource atom name; value is the resource
+     *                          value.
+     *
+     * @throws Horde_Imap_Client_Exception
+     * @throws Horde_Imap_Client_Exception_NoSupportExtension
+     */
+    public function setQuota($root, array $resources = array())
+    {
+        $this->login();
+
+        if (!$this->queryCapability('QUOTA')) {
+            throw new Horde_Imap_Client_Exception_NoSupportExtension('QUOTA');
+        }
+
+        if (!empty($resources)) {
+            $this->_setQuota(Horde_Imap_Client_Mailbox::get($root), $resources);
+        }
+    }
+
+    /**
+     * Set quota limits.
+     *
+     * @param Horde_Imap_Client_Mailbox $root  The quota root.
+     * @param array $resources                 The resource values to set.
+     *
+     * @return boolean  True on success.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    abstract protected function _setQuota(Horde_Imap_Client_Mailbox $root,
+                                          $resources);
+
+    /**
+     * Get quota limits. The server must support the IMAP QUOTA extension
+     * (RFC 2087).
+     *
+     * @param mixed $root  The quota root. Either a Horde_Imap_Client_Mailbox
+     *                     object or a string (UTF-8).
+     *
+     * @return mixed  An array with resource keys. Each key holds an array
+     *                with 2 values: 'limit' and 'usage'.
+     *
+     * @throws Horde_Imap_Client_Exception
+     * @throws Horde_Imap_Client_Exception_NoSupportExtension
+     */
+    public function getQuota($root)
+    {
+        $this->login();
+
+        if (!$this->queryCapability('QUOTA')) {
+            throw new Horde_Imap_Client_Exception_NoSupportExtension('QUOTA');
+        }
+
+        return $this->_getQuota(Horde_Imap_Client_Mailbox::get($root));
+    }
+
+    /**
+     * Get quota limits.
+     *
+     * @param Horde_Imap_Client_Mailbox $root  The quota root.
+     *
+     * @return mixed  An array with resource keys. Each key holds an array
+     *                with 2 values: 'limit' and 'usage'.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    abstract protected function _getQuota(Horde_Imap_Client_Mailbox $root);
+
+    /**
+     * Get quota limits for a mailbox. The server must support the IMAP QUOTA
+     * extension (RFC 2087).
+     *
+     * @param mixed $mailbox  A mailbox. Either a Horde_Imap_Client_Mailbox
+     *                        object or a string (UTF-8).
+     *
+     * @return mixed  An array with the keys being the quota roots. Each key
+     *                holds an array with resource keys: each of these keys
+     *                holds an array with 2 values: 'limit' and 'usage'.
+     *
+     * @throws Horde_Imap_Client_Exception
+     * @throws Horde_Imap_Client_Exception_NoSupportExtension
+     */
+    public function getQuotaRoot($mailbox)
+    {
+        $this->login();
+
+        if (!$this->queryCapability('QUOTA')) {
+            throw new Horde_Imap_Client_Exception_NoSupportExtension('QUOTA');
+        }
+
+        return $this->_getQuotaRoot(Horde_Imap_Client_Mailbox::get($mailbox));
+    }
+
+    /**
+     * Get quota limits for a mailbox.
+     *
+     * @param Horde_Imap_Client_Mailbox $mailbox  A mailbox.
+     *
+     * @return mixed  An array with the keys being the quota roots. Each key
+     *                holds an array with resource keys: each of these keys
+     *                holds an array with 2 values: 'limit' and 'usage'.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    abstract protected function _getQuotaRoot(Horde_Imap_Client_Mailbox $mailbox);
+
+    /**
+     * Get the ACL rights for a given mailbox. The server must support the
+     * IMAP ACL extension (RFC 2086/4314).
+     *
+     * @param mixed $mailbox  A mailbox. Either a Horde_Imap_Client_Mailbox
+     *                        object or a string (UTF-8).
+     *
+     * @return array  An array with identifiers as the keys and
+     *                Horde_Imap_Client_Data_Acl objects as the values.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function getACL($mailbox)
+    {
+        $this->login();
+        return $this->_getACL(Horde_Imap_Client_Mailbox::get($mailbox));
+    }
+
+    /**
+     * Get ACL rights for a given mailbox.
+     *
+     * @param Horde_Imap_Client_Mailbox $mailbox  A mailbox.
+     *
+     * @return array  An array with identifiers as the keys and
+     *                Horde_Imap_Client_Data_Acl objects as the values.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    abstract protected function _getACL(Horde_Imap_Client_Mailbox $mailbox);
+
+    /**
+     * Set ACL rights for a given mailbox/identifier.
+     *
+     * @param mixed $mailbox      A mailbox. Either a Horde_Imap_Client_Mailbox
+     *                            object or a string (UTF-8).
+     * @param string $identifier  The identifier to alter (UTF-8).
+     * @param array $options      Additional options:
+     *   - rights: (string) The rights to alter or set.
+     *   - action: (string, optional) If 'add' or 'remove', adds or removes the
+     *             specified rights. Sets the rights otherwise.
+     *
+     * @throws Horde_Imap_Client_Exception
+     * @throws Horde_Imap_Client_Exception_NoSupportExtension
+     */
+    public function setACL($mailbox, $identifier, $options)
+    {
+        $this->login();
+
+        if (!$this->queryCapability('ACL')) {
+            throw new Horde_Imap_Client_Exception_NoSupportExtension('ACL');
+        }
+
+        if (empty($options['rights'])) {
+            if (!isset($options['action']) ||
+                ($options['action'] != 'add' && $options['action'] != 'remove')) {
+                $this->_deleteACL(
+                    Horde_Imap_Client_Mailbox::get($mailbox),
+                    Horde_Imap_Client_Utf7imap::Utf8ToUtf7Imap($identifier)
+                );
+            }
+            return;
+        }
+
+        $acl = ($options['rights'] instanceof Horde_Imap_Client_Data_Acl)
+            ? $options['rights']
+            : new Horde_Imap_Client_Data_Acl(strval($options['rights']));
+
+        $options['rights'] = $acl->getString(
+            $this->queryCapability('RIGHTS')
+                ? Horde_Imap_Client_Data_AclCommon::RFC_4314
+                : Horde_Imap_Client_Data_AclCommon::RFC_2086
+        );
+        if (isset($options['action'])) {
+            switch ($options['action']) {
+            case 'add':
+                $options['rights'] = '+' . $options['rights'];
+                break;
+            case 'remove':
+                $options['rights'] = '-' . $options['rights'];
+                break;
+            }
+        }
+
+        $this->_setACL(
+            Horde_Imap_Client_Mailbox::get($mailbox),
+            Horde_Imap_Client_Utf7imap::Utf8ToUtf7Imap($identifier),
+            $options
+        );
+    }
+
+    /**
+     * Set ACL rights for a given mailbox/identifier.
+     *
+     * @param Horde_Imap_Client_Mailbox $mailbox  A mailbox.
+     * @param string $identifier                  The identifier to alter
+     *                                            (UTF7-IMAP).
+     * @param array $options                      Additional options. 'rights'
+     *                                            contains the string of
+     *                                            rights to set on the server.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    abstract protected function _setACL(Horde_Imap_Client_Mailbox $mailbox,
+                                        $identifier, $options);
+
+    /**
+     * Deletes ACL rights for a given mailbox/identifier.
+     *
+     * @param mixed $mailbox      A mailbox. Either a Horde_Imap_Client_Mailbox
+     *                            object or a string (UTF-8).
+     * @param string $identifier  The identifier to delete (UTF-8).
+     *
+     * @throws Horde_Imap_Client_Exception
+     * @throws Horde_Imap_Client_Exception_NoSupportExtension
+     */
+    public function deleteACL($mailbox, $identifier)
+    {
+        $this->login();
+
+        if (!$this->queryCapability('ACL')) {
+            throw new Horde_Imap_Client_Exception_NoSupportExtension('ACL');
+        }
+
+        $this->_deleteACL(
+            Horde_Imap_Client_Mailbox::get($mailbox),
+            Horde_Imap_Client_Utf7imap::Utf8ToUtf7Imap($identifier)
+        );
+    }
+
+    /**
+     * Deletes ACL rights for a given mailbox/identifier.
+     *
+     * @param Horde_Imap_Client_Mailbox $mailbox  A mailbox.
+     * @param string $identifier                  The identifier to delete
+     *                                            (UTF7-IMAP).
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    abstract protected function _deleteACL(Horde_Imap_Client_Mailbox $mailbox,
+                                           $identifier);
+
+    /**
+     * List the ACL rights for a given mailbox/identifier. The server must
+     * support the IMAP ACL extension (RFC 2086/4314).
+     *
+     * @param mixed $mailbox      A mailbox. Either a Horde_Imap_Client_Mailbox
+     *                            object or a string (UTF-8).
+     * @param string $identifier  The identifier to query (UTF-8).
+     *
+     * @return Horde_Imap_Client_Data_AclRights  An ACL data rights object.
+     *
+     * @throws Horde_Imap_Client_Exception
+     * @throws Horde_Imap_Client_Exception_NoSupportExtension
+     */
+    public function listACLRights($mailbox, $identifier)
+    {
+        $this->login();
+
+        if (!$this->queryCapability('ACL')) {
+            throw new Horde_Imap_Client_Exception_NoSupportExtension('ACL');
+        }
+
+        return $this->_listACLRights(
+            Horde_Imap_Client_Mailbox::get($mailbox),
+            Horde_Imap_Client_Utf7imap::Utf8ToUtf7Imap($identifier)
+        );
+    }
+
+    /**
+     * Get ACL rights for a given mailbox/identifier.
+     *
+     * @param Horde_Imap_Client_Mailbox $mailbox  A mailbox.
+     * @param string $identifier                  The identifier to query
+     *                                            (UTF7-IMAP).
+     *
+     * @return Horde_Imap_Client_Data_AclRights  An ACL data rights object.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    abstract protected function _listACLRights(Horde_Imap_Client_Mailbox $mailbox,
+                                               $identifier);
+
+    /**
+     * Get the ACL rights for the current user for a given mailbox. The
+     * server must support the IMAP ACL extension (RFC 2086/4314).
+     *
+     * @param mixed $mailbox  A mailbox. Either a Horde_Imap_Client_Mailbox
+     *                        object or a string (UTF-8).
+     *
+     * @return Horde_Imap_Client_Data_Acl  An ACL data object.
+     *
+     * @throws Horde_Imap_Client_Exception
+     * @throws Horde_Imap_Client_Exception_NoSupportExtension
+     */
+    public function getMyACLRights($mailbox)
+    {
+        $this->login();
+
+        if (!$this->queryCapability('ACL')) {
+            throw new Horde_Imap_Client_Exception_NoSupportExtension('ACL');
+        }
+
+        return $this->_getMyACLRights(Horde_Imap_Client_Mailbox::get($mailbox));
+    }
+
+    /**
+     * Get the ACL rights for the current user for a given mailbox.
+     *
+     * @param Horde_Imap_Client_Mailbox $mailbox  A mailbox.
+     *
+     * @return Horde_Imap_Client_Data_Acl  An ACL data object.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    abstract protected function _getMyACLRights(Horde_Imap_Client_Mailbox $mailbox);
+
+    /**
+     * Return master list of ACL rights available on the server.
+     *
+     * @return array  A list of ACL rights.
+     */
+    public function allAclRights()
+    {
+        $this->login();
+
+        $rights = array(
+            Horde_Imap_Client::ACL_LOOKUP,
+            Horde_Imap_Client::ACL_READ,
+            Horde_Imap_Client::ACL_SEEN,
+            Horde_Imap_Client::ACL_WRITE,
+            Horde_Imap_Client::ACL_INSERT,
+            Horde_Imap_Client::ACL_POST,
+            Horde_Imap_Client::ACL_ADMINISTER
+        );
+
+        if ($capability = $this->queryCapability('RIGHTS')) {
+            // Add rights defined in CAPABILITY string (RFC 4314).
+            return array_merge($rights, str_split(reset($capability)));
+        }
+
+        // Add RFC 2086 rights (deprecated by RFC 4314, but need to keep for
+        // compatibility with old servers).
+        return array_merge($rights, array(
+            Horde_Imap_Client::ACL_CREATE,
+            Horde_Imap_Client::ACL_DELETE
+        ));
+    }
+
+    /**
+     * Get metadata for a given mailbox. The server must support either the
+     * IMAP METADATA extension (RFC 5464) or the ANNOTATEMORE extension
+     * (http://ietfreport.isoc.org/idref/draft-daboo-imap-annotatemore/).
+     *
+     * @param mixed $mailbox  A mailbox. Either a Horde_Imap_Client_Mailbox
+     *                        object or a string (UTF-8).
+     * @param array $entries  The entries to fetch (UTF-8 strings).
+     * @param array $options  Additional options:
+     *   - depth: (string) Either "0", "1" or "infinity". Returns only the
+     *            given value (0), only values one level below the specified
+     *            value (1) or all entries below the specified value
+     *            (infinity).
+     *   - maxsize: (integer) The maximal size the returned values may have.
+     *              DEFAULT: No maximal size.
+     *
+     * @return array  An array with metadata names as the keys and metadata
+     *                values as the values. If 'maxsize' is set, and entries
+     *                exist on the server larger than this size, the size will
+     *                be returned in the key '*longentries'.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function getMetadata($mailbox, $entries, array $options = array())
+    {
+        $this->login();
+
+        if (!is_array($entries)) {
+            $entries = array($entries);
+        }
+
+        return $this->_getMetadata(Horde_Imap_Client_Mailbox::get($mailbox), array_map(array('Horde_Imap_Client_Utf7imap', 'Utf8ToUtf7Imap'), $entries), $options);
+    }
+
+    /**
+     * Get metadata for a given mailbox.
+     *
+     * @param Horde_Imap_Client_Mailbox $mailbox  A mailbox.
+     * @param array $entries                      The entries to fetch
+     *                                            (UTF7-IMAP strings).
+     * @param array $options                      Additional options.
+     *
+     * @return array  An array with metadata names as the keys and metadata
+     *                values as the values.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    abstract protected function _getMetadata(Horde_Imap_Client_Mailbox $mailbox,
+                                             $entries, $options);
+
+    /**
+     * Set metadata for a given mailbox/identifier.
+     *
+     * @param mixed $mailbox  A mailbox. Either a Horde_Imap_Client_Mailbox
+     *                        object or a string (UTF-8). If empty, sets a
+     *                        server annotation.
+     * @param array $data     A set of data values. The metadata values
+     *                        corresponding to the keys of the array will
+     *                        be set to the values in the array.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function setMetadata($mailbox, $data)
+    {
+        $this->login();
+        $this->_setMetadata(Horde_Imap_Client_Mailbox::get($mailbox), $data);
+    }
+
+    /**
+     * Set metadata for a given mailbox/identifier.
+     *
+     * @param Horde_Imap_Client_Mailbox $mailbox  A mailbox.
+     * @param array $data                         A set of data values. See
+     *                                            setMetadata() for format.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    abstract protected function _setMetadata(Horde_Imap_Client_Mailbox $mailbox,
+                                             $data);
+
+    /* Public utility functions. */
+
+    /**
+     * Returns a unique identifier for the current mailbox status.
+     *
+     * @deprecated
+     *
+     * @param mixed $mailbox  A mailbox. Either a Horde_Imap_Client_Mailbox
+     *                        object or a string (UTF-8).
+     * @param array $addl     Additional cache info to add to the cache ID
+     *                        string.
+     *
+     * @return string  The cache ID string, which will change when the
+     *                 composition of the mailbox changes. The uidvalidity
+     *                 will always be the first element, and will be delimited
+     *                 by the '|' character.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function getCacheId($mailbox, array $addl = array())
+    {
+        return Horde_Imap_Client_Base_Deprecated::getCacheId($this, $mailbox, isset($this->_temp['enabled']['CONDSTORE']), $addl);
+    }
+
+    /**
+     * Parses a cacheID created by getCacheId().
+     *
+     * @deprecated
+     *
+     * @param string $id  The cache ID.
+     *
+     * @return array  An array with the following information:
+     *   - highestmodseq: (integer)
+     *   - messages: (integer)
+     *   - uidnext: (integer)
+     *   - uidvalidity: (integer) Always present
+     */
+    public function parseCacheId($id)
+    {
+        return Horde_Imap_Client_Base_Deprecated::parseCacheId($id);
+    }
+
+    /**
+     * Resolves an IDs object into a list of IDs.
+     *
+     * @param Horde_Imap_Client_Mailbox $mailbox  The mailbox.
+     * @param Horde_Imap_Client_Ids $ids          The Ids object.
+     * @param boolean $convert                    Convert to UIDs?
+     *   - 0: No
+     *   - 1: Only if $ids is not already a UIDs object
+     *   - 2: Always
+     *
+     * @return Horde_Imap_Client_Ids  The list of IDs.
+     */
+    public function resolveIds(Horde_Imap_Client_Mailbox $mailbox,
+                               Horde_Imap_Client_Ids $ids, $convert = 0)
+    {
+        $map = $this->_mailboxOb($mailbox)->map;
+
+        if ($ids->special) {
+            /* Optimization for ALL sequence searches. */
+            if (!$convert && $ids->all && $ids->sequence) {
+                $res = $this->status($mailbox, Horde_Imap_Client::STATUS_MESSAGES);
+                return $this->getIdsOb($res['messages'] ? ('1:' . $res['messages']) : array(), true);
+            }
+
+            $convert = 2;
+        } elseif (!$convert || (!$ids->sequence && ($convert == 1))) {
+            return clone $ids;
+        } else {
+            /* Do an all or nothing: either we have all the numbers/UIDs in
+             * memory and can return, or just send the whole ID query to the
+             * server. Any advantage we would get by a partial search are
+             * outweighed by the complexities needed to make the search and
+             * then merge back into the original results. */
+            $lookup = $map->lookup($ids);
+            if (count($lookup) == count($ids)) {
+                return $this->getIdsOb(array_values($lookup));
+            }
+        }
+
+        $query = new Horde_Imap_Client_Search_Query();
+        $query->ids($ids);
+
+        $res = $this->search($mailbox, $query, array(
+            'results' => array(
+                Horde_Imap_Client::SEARCH_RESULTS_MATCH,
+                Horde_Imap_Client::SEARCH_RESULTS_SAVE
+            ),
+            'sequence' => (!$convert && $ids->sequence),
+            'sort' => array(Horde_Imap_Client::SORT_SEQUENCE)
+        ));
+
+        /* Update mapping. */
+        if ($convert) {
+            if ($ids->all) {
+                $ids = $this->getIdsOb('1:' . count($res['match']));
+            } elseif ($ids->special) {
+                return $res['match'];
+            }
+
+            $map->update(array_combine($ids->ids, $res['match']->ids));
+        }
+
+        return $res['match'];
+    }
+
+    /**
+     * Determines if the given charset is valid for search-related queries.
+     * This check pertains just to the basic IMAP SEARCH command.
+     *
+     * @param string $charset  The query charset.
+     *
+     * @return boolean  True if server supports this charset.
+     */
+    public function validSearchCharset($charset)
+    {
+        $charset = strtoupper($charset);
+
+        if ($charset == 'US-ASCII') {
+            return true;
+        }
+
+        if (!isset($this->_init['s_charset'][$charset])) {
+            $s_charset = $this->_init['s_charset'];
+
+            /* Use a dummy search query and search for BADCHARSET response. */
+            $query = new Horde_Imap_Client_Search_Query();
+            $query->charset($charset, false);
+            $query->ids($this->getIdsOb(1, true));
+            $query->text('a');
+            try {
+                $this->search('INBOX', $query, array(
+                    'nocache' => true,
+                    'sequence' => true
+                ));
+                $s_charset[$charset] = true;
+            } catch (Horde_Imap_Client_Exception $e) {
+                $s_charset[$charset] = ($e->getCode() != Horde_Imap_Client_Exception::BADCHARSET);
+            }
+
+            $this->_setInit('s_charset', $s_charset);
+        }
+
+        return $this->_init['s_charset'][$charset];
+    }
+
+    /* Mailbox syncing functions. */
+
+    /**
+     * Returns a unique token for the current mailbox synchronization status.
+     *
+     * @since 2.2.0
+     *
+     * @param mixed $mailbox  A mailbox. Either a Horde_Imap_Client_Mailbox
+     *                        object or a string (UTF-8).
+     *
+     * @return string  The sync token.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function getSyncToken($mailbox)
+    {
+        $out = array();
+
+        foreach ($this->_syncStatus($mailbox) as $key => $val) {
+            $out[] = $key . $val;
+        }
+
+        return base64_encode(implode(',', $out));
+    }
+
+    /**
+     * Synchronize a mailbox from a sync token.
+     *
+     * @since 2.2.0
+     *
+     * @param mixed $mailbox  A mailbox. Either a Horde_Imap_Client_Mailbox
+     *                        object or a string (UTF-8).
+     * @param string $token   A sync token generated by getSyncToken().
+     * @param array $opts     Additional options:
+     *   - criteria: (integer) Mask of Horde_Imap_Client::SYNC_* criteria to
+     *               return. Defaults to SYNC_ALL.
+     *   - ids: (Horde_Imap_Client_Ids) A cached list of UIDs. Unless QRESYNC
+     *          is available on the server, failure to specify this option
+     *          means SYNC_VANISHEDUIDS information cannot be returned.
+     *
+     * @return Horde_Imap_Client_Data_Sync  A sync object.
+     *
+     * @throws Horde_Imap_Client_Exception
+     * @throws Horde_Imap_Client_Exception_Sync
+     */
+    public function sync($mailbox, $token, array $opts = array())
+    {
+        if (($token = base64_decode($token, true)) === false) {
+            throw new Horde_Imap_Client_Exception_Sync('Bad token.', Horde_Imap_Client_Exception_Sync::BAD_TOKEN);
+        }
+
+        $sync = array();
+        foreach (explode(',', $token) as $val) {
+            $sync[substr($val, 0, 1)] = substr($val, 1);
+        }
+
+        return new Horde_Imap_Client_Data_Sync(
+            $this,
+            $mailbox,
+            $sync,
+            $this->_syncStatus($mailbox),
+            (isset($opts['criteria']) ? $opts['criteria'] : Horde_Imap_Client::SYNC_ALL),
+            (isset($opts['ids']) ? $opts['ids'] : null)
+        );
+    }
+
+    /* Private utility functions. */
+
+    /**
+     * Store FETCH data in cache.
+     *
+     * @param Horde_Imap_Client_Fetch_Results $data  The fetch results.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    protected function _updateCache(Horde_Imap_Client_Fetch_Results $data)
+    {
+        if (!empty($this->_temp['fetch_nocache']) ||
+            empty($this->_selected) ||
+            !count($data) ||
+            !$this->_initCache(true)) {
+            return;
+        }
+
+        $c = $this->getParam('cache');
+        if (in_array(strval($this->_selected), $c['fetch_ignore'])) {
+            $this->_debug->info(sprintf("CACHE: Ignoring FETCH data (mailbox: %s)", $this->_selected));
+            return;
+        }
+
+        /* Optimization: we can directly use getStatus() here since we know
+         * these values are initialized. */
+        $mbox_ob = $this->_mailboxOb();
+        $highestmodseq = $mbox_ob->getStatus(Horde_Imap_Client::STATUS_HIGHESTMODSEQ);
+        $uidvalidity = $mbox_ob->getStatus(Horde_Imap_Client::STATUS_UIDVALIDITY);
+
+        $mapping = $modseq = $tocache = array();
+        if (count($data)) {
+            $cf = $this->_cacheFields();
+        }
+
+        foreach ($data as $v) {
+            /* It is possible that we received FETCH information that doesn't
+             * contain UID data. This is uncacheable so don't process. */
+            if (!($uid = $v->getUid())) {
+                return;
+            }
+
+            $tmp = array();
+
+            if ($v->isDowngraded()) {
+                $tmp[self::CACHE_DOWNGRADED] = true;
+            }
+
+            foreach ($cf as $key => $val) {
+                if ($v->exists($key)) {
+                    switch ($key) {
+                    case Horde_Imap_Client::FETCH_ENVELOPE:
+                        $tmp[$val] = $v->getEnvelope();
+                        break;
+
+                    case Horde_Imap_Client::FETCH_FLAGS:
+                        if ($highestmodseq) {
+                            $modseq[$uid] = $v->getModSeq();
+                            $tmp[$val] = $v->getFlags();
+                        }
+                        break;
+
+                    case Horde_Imap_Client::FETCH_HEADERS:
+                        foreach ($this->_temp['headers_caching'] as $label => $hash) {
+                            if ($hdr = $v->getHeaders($label)) {
+                                $tmp[$val][$hash] = $hdr;
+                            }
+                        }
+                        break;
+
+                    case Horde_Imap_Client::FETCH_IMAPDATE:
+                        $tmp[$val] = $v->getImapDate();
+                        break;
+
+                    case Horde_Imap_Client::FETCH_SIZE:
+                        $tmp[$val] = $v->getSize();
+                        break;
+
+                    case Horde_Imap_Client::FETCH_STRUCTURE:
+                        $tmp[$val] = clone $v->getStructure();
+                        break;
+                    }
+                }
+            }
+
+            if (!empty($tmp)) {
+                $tocache[$uid] = $tmp;
+            }
+
+            $mapping[$v->getSeq()] = $uid;
+        }
+
+        if (!empty($mapping)) {
+            if (!empty($tocache)) {
+                $this->_cache->set($this->_selected, $tocache, $uidvalidity);
+            }
+
+            $this->_mailboxOb()->map->update($mapping);
+        }
+
+        if (!empty($modseq)) {
+            $this->_updateModSeq(max(array_merge($modseq, array($highestmodseq))));
+            $mbox_ob->setStatus(Horde_Imap_Client::STATUS_SYNCFLAGUIDS, array_keys($modseq));
+        }
+    }
+
+    /**
+     * Moves cache entries from the current mailbox to another mailbox.
+     *
+     * @param Horde_Imap_Client_Mailbox $to  The destination mailbox.
+     * @param array $map                     Mapping of source UIDs (keys) to
+     *                                       destination UIDs (values).
+     * @param string $uidvalid               UIDVALIDITY of destination
+     *                                       mailbox.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    protected function _moveCache(Horde_Imap_Client_Mailbox $to, $map,
+                                  $uidvalid)
+    {
+        if (!$this->_initCache()) {
+            return;
+        }
+
+        $c = $this->getParam('cache');
+        if (in_array(strval($to), $c['fetch_ignore'])) {
+            $this->_debug->info(sprintf("CACHE: Ignoring moving FETCH data (%s => %s)", $this->_selected, $to));
+            return;
+        }
+
+        $old = $this->_cache->get($this->_selected, array_keys($map), null);
+        $new = array();
+
+        foreach ($map as $key => $val) {
+            if (!empty($old[$key])) {
+                $new[$val] = $old[$key];
+            }
+        }
+
+        if (!empty($new)) {
+            $this->_cache->set($to, $new, $uidvalid);
+        }
+    }
+
+    /**
+     * Delete messages in the cache.
+     *
+     * @param Horde_Imap_Client_Mailbox $mailbox  The mailbox.
+     * @param Horde_Imap_Client_Ids $ids          The list of IDs to delete in
+     *                                            $mailbox.
+     * @param array $opts                         Additional options (not used
+     *                                            in base class).
+     *
+     * @return Horde_Imap_Client_Ids  UIDs that were deleted.
+     * @throws Horde_Imap_Client_Exception
+     */
+    protected function _deleteMsgs(Horde_Imap_Client_Mailbox $mailbox,
+                                   Horde_Imap_Client_Ids $ids,
+                                   array $opts = array())
+    {
+        if (!$this->_initCache()) {
+            return $ids;
+        }
+
+        $mbox_ob = $this->_mailboxOb();
+        $ids_ob = $ids->sequence
+            ? $this->getIdsOb($mbox_ob->map->lookup($ids))
+            : $ids;
+
+        $this->_cache->deleteMsgs($mailbox, $ids_ob->ids);
+        $mbox_ob->setStatus(Horde_Imap_Client::STATUS_SYNCVANISHED, $ids_ob->ids);
+        $mbox_ob->map->remove($ids);
+
+        return $ids_ob;
+    }
+
+    /**
+     * Retrieve data from the search cache.
+     *
+     * @param string $type    The cache type ('search' or 'thread').
+     * @param array $options  The options array of the calling function.
+     *
+     * @return mixed  Returns search cache metadata. If search was retrieved,
+     *                data is in key 'data'.
+     *                Returns null if caching is not available.
+     */
+    protected function _getSearchCache($type, $options)
+    {
+        $status = $this->status($this->_selected, Horde_Imap_Client::STATUS_HIGHESTMODSEQ | Horde_Imap_Client::STATUS_UIDVALIDITY);
+
+        /* Search caching requires MODSEQ, which may not be active for a
+         * mailbox. */
+        if (empty($status['highestmodseq'])) {
+            return null;
+        }
+
+        ksort($options);
+        $cache = hash('md5', $type . serialize($options));
+        $cacheid = $this->getCacheId($this->_selected);
+        $ret = array();
+
+        $md = $this->_cache->getMetaData(
+            $this->_selected,
+            $status['uidvalidity'],
+            array(self::CACHE_SEARCH, self::CACHE_SEARCHID)
+        );
+
+        if (!isset($md[self::CACHE_SEARCHID]) ||
+            ($md[self::CACHE_SEARCHID] != $cacheid)) {
+            $md[self::CACHE_SEARCH] = array();
+            $md[self::CACHE_SEARCHID] = $cacheid;
+            if ($this->_debug->debug &&
+                !isset($this->_temp['searchcacheexpire'][strval($this->_selected)])) {
+                $this->_debug->info(sprintf("SEARCH: Expired from cache (mailbox: %s)", $this->_selected));
+                $this->_temp['searchcacheexpire'][strval($this->_selected)] = true;
+            }
+        } elseif (isset($md[self::CACHE_SEARCH][$cache])) {
+            $this->_debug->info(sprintf("SEARCH: Retrieved %s from cache (mailbox: %s; id: %s)", $type, $this->_selected, $cache));
+            $ret['data'] = $md[self::CACHE_SEARCH][$cache];
+            unset($md[self::CACHE_SEARCHID]);
+        }
+
+        return array_merge($ret, array(
+            'id' => $cache,
+            'metadata' => $md,
+            'type' => $type
+        ));
+    }
+
+    /**
+     * Set data in the search cache.
+     *
+     * @param mixed $data    The cache data to store.
+     * @param string $sdata  The search data returned from _getSearchCache().
+     */
+    protected function _setSearchCache($data, $sdata)
+    {
+        $sdata['metadata'][self::CACHE_SEARCH][$sdata['id']] = $data;
+
+        $this->_cache->setMetaData($this->_selected, null, $sdata['metadata']);
+
+        if ($this->_debug->debug) {
+            $this->_debug->info(sprintf("SEARCH: Saved %s to cache (mailbox: %s; id: %s)", $sdata['type'], $this->_selected, $sdata['id']));
+            unset($this->_temp['searchcacheexpire'][strval($this->_selected)]);
+        }
+    }
+
+    /**
+     * Updates the cached MODSEQ value.
+     *
+     * @param integer $modseq  MODSEQ value to store.
+     *
+     * @return mixed  The MODSEQ of the old value if it was replaced (or false
+     *                if it didn't exist or is the same).
+     */
+    protected function _updateModSeq($modseq)
+    {
+        if (!$this->_initCache(true)) {
+            return false;
+        }
+
+        $mbox_ob = $this->_mailboxOb();
+        $uidvalid = $mbox_ob->getStatus(Horde_Imap_Client::STATUS_UIDVALIDITY);
+        $md = $this->_cache->getMetaData($this->_selected, $uidvalid, array(self::CACHE_MODSEQ));
+
+        if (isset($md[self::CACHE_MODSEQ])) {
+            if ($md[self::CACHE_MODSEQ] < $modseq) {
+                $set = true;
+                $sync = $md[self::CACHE_MODSEQ];
+            } else {
+                $set = false;
+                $sync = 0;
+            }
+            $mbox_ob->setStatus(Horde_Imap_Client::STATUS_SYNCMODSEQ, $md[self::CACHE_MODSEQ]);
+        } else {
+            $set = true;
+            $sync = 0;
+        }
+
+        if ($set) {
+            $this->_cache->setMetaData($this->_selected, $uidvalid, array(
+                self::CACHE_MODSEQ => $modseq
+            ));
+        }
+
+        return $sync;
+    }
+
+    /**
+     * Synchronizes the current mailbox cache with the server (using CONDSTORE
+     * or QRESYNC).
+     */
+    protected function _condstoreSync()
+    {
+        $mbox_ob = $this->_mailboxOb();
+
+        /* Check that modseqs are available in mailbox. */
+        if (!($highestmodseq = $mbox_ob->getStatus(Horde_Imap_Client::STATUS_HIGHESTMODSEQ)) ||
+            !($modseq = $this->_updateModSeq($highestmodseq))) {
+            $mbox_ob->sync = true;
+        }
+
+        if ($mbox_ob->sync) {
+            return;
+        }
+
+        $uids_ob = $this->getIdsOb($this->_cache->get($this->_selected, array(), array(), $mbox_ob->getStatus(Horde_Imap_Client::STATUS_UIDVALIDITY)));
+
+        /* Are we caching flags? */
+        if (array_key_exists(Horde_Imap_Client::FETCH_FLAGS, $this->_cacheFields())) {
+            $fquery = new Horde_Imap_Client_Fetch_Query();
+            $fquery->flags();
+
+            /* Update flags in cache. Cache will be updated in _fetch(). */
+            $this->_fetch(new Horde_Imap_Client_Fetch_Results(), array(
+                array(
+                    '_query' => $fquery,
+                    'changedsince' => $modseq,
+                    'ids' => $uids_ob
+                )
+            ));
+        }
+
+        /* Search for deleted messages, and remove from cache. */
+        $vanished = $this->vanished($this->_selected, $modseq, $uids_ob);
+        $disappear = array_diff($uids_ob->ids, $vanished->ids);
+        if (!empty($disappear)) {
+            $this->_deleteMsgs($this->_selected, $this->getIdsOb($disappear));
+        }
+
+        $mbox_ob->sync = true;
+    }
+
+    /**
+     * Provide the list of available caching fields.
+     *
+     * @return array  The list of available caching fields (fields are in the
+     *                key).
+     */
+    protected function _cacheFields()
+    {
+        $c = $this->getParam('cache');
+        $out = $c['fields'];
+
+        if (!isset($this->_temp['enabled']['CONDSTORE'])) {
+            unset($out[Horde_Imap_Client::FETCH_FLAGS]);
+        }
+
+        return $out;
+    }
+
+    /**
+     * Return the current mailbox synchronization status.
+     *
+     * @param mixed $mailbox  A mailbox. Either a Horde_Imap_Client_Mailbox
+     *                        object or a string (UTF-8).
+     *
+     * @return array  An array with status data. (This data is not guaranteed
+     *                to have any specific format).
+     */
+    protected function _syncStatus($mailbox)
+    {
+        $status = $this->status(
+            $mailbox,
+            Horde_Imap_Client::STATUS_HIGHESTMODSEQ |
+            Horde_Imap_Client::STATUS_MESSAGES |
+            Horde_Imap_Client::STATUS_UIDNEXT_FORCE |
+            Horde_Imap_Client::STATUS_UIDVALIDITY
+        );
+
+        $fields = array('uidnext', 'uidvalidity');
+        if (empty($status['highestmodseq'])) {
+            $fields[] = 'messages';
+        } else {
+            $fields[] = 'highestmodseq';
+        }
+
+        $out = array();
+        $sync_map = array_flip(Horde_Imap_Client_Data_Sync::$map);
+
+        foreach ($fields as $val) {
+            $out[$sync_map[$val]] = $status[$val];
+        }
+
+        return array_filter($out);
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientCacheBackendCachephpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientCacheBackendCachephp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Cache/Backend/Cache.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Cache/Backend/Cache.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Cache/Backend/Cache.php                             (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Cache/Backend/Cache.php        2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,498 @@
</span><ins>+<?php
+/**
+ * Copyright 2005-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2005-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * A Horde_Cache implementation for caching IMAP/POP data.
+ * Requires the Horde_Cache package.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2005-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Cache_Backend_Cache extends Horde_Imap_Client_Cache_Backend
+{
+    /** Cache structure version. */
+    const VERSION = 3;
+
+    /**
+     * The cache object.
+     *
+     * @var Horde_Cache
+     */
+    protected $_cache;
+
+    /**
+     * The working data for the current pageload.  All changes take place to
+     * this data.
+     *
+     * @var array
+     */
+    protected $_data = array();
+
+    /**
+     * The list of cache slices loaded.
+     *
+     * @var array
+     */
+    protected $_loaded = array();
+
+    /**
+     * The mapping of UIDs to slices.
+     *
+     * @var array
+     */
+    protected $_slicemap = array();
+
+    /**
+     * The list of items to update:
+     *   - add: (array) List of IDs that were added.
+     *   - slice: (array) List of slices that were modified.
+     *   - slicemap: (boolean) Was slicemap info changed?
+     *
+     * @var array
+     */
+    protected $_update = array();
+
+    /**
+     * Constructor.
+     *
+     * @param array $params  Configuration parameters:
+     * <ul>
+     *  <li>
+     *   REQUIRED Parameters:
+     *   <ul>
+     *    <li>
+     *     cacheob: (Horde_Cache) [REQUIRED] The cache object to use.
+     *    </li>
+     *   </ul>
+     *  </li>
+     *  <li>
+     *   Optional Parameters:
+     *   <ul>
+     *    <li>
+     *     lifetime: (integer) The lifetime of the cache data (in seconds).
+     *               DEFAULT: 1 week (604800 seconds)
+     *    </li>
+     *    <li>
+     *     slicesize: (integer) The slicesize to use.
+     *                DEFAULT: 50
+     *    </li>
+     *   </ul>
+     *  </li>
+     * </ul>
+     */
+    public function __construct(array $params = array())
+    {
+        // Default parameters.
+        $params = array_merge(array(
+            'lifetime' => 604800,
+            'slicesize' => 50
+        ), array_filter($params));
+
+        if (!isset($params['cacheob'])) {
+            throw new InvalidArgumentException('Missing cacheob parameter.');
+        }
+
+        foreach (array('lifetime', 'slicesize') as $val) {
+            $params[$val] = intval($params[$val]);
+        }
+
+        parent::__construct($params);
+    }
+
+    /**
+     * Initialization tasks.
+     */
+    protected function _initOb()
+    {
+        $this->_cache = $this->_params['cacheob'];
+        register_shutdown_function(array($this, 'save'));
+    }
+
+    /**
+     * Updates the cache.
+     */
+    public function save()
+    {
+        $lifetime = $this->_params['lifetime'];
+
+        foreach ($this->_update as $mbox => $val) {
+            $s = &$this->_slicemap[$mbox];
+
+            if (!empty($val['add'])) {
+                if ($s['c'] <= $this->_params['slicesize']) {
+                    $val['slice'][] = $s['i'];
+                    $this->_loadSlice($mbox, $s['i']);
+                }
+                $val['slicemap'] = true;
+
+                foreach (array_keys(array_flip($val['add'])) as $uid) {
+                    if ($s['c']++ > $this->_params['slicesize']) {
+                        $s['c'] = 0;
+                        $val['slice'][] = ++$s['i'];
+                        $this->_loadSlice($mbox, $s['i']);
+                    }
+                    $s['s'][$uid] = $s['i'];
+                }
+            }
+
+            if (!empty($val['slice'])) {
+                $d = &$this->_data[$mbox];
+                $val['slicemap'] = true;
+
+                foreach (array_keys(array_flip($val['slice'])) as $slice) {
+                    $data = array();
+                    foreach (array_keys($s['s'], $slice) as $uid) {
+                        $data[$uid] = is_array($d[$uid])
+                            ? serialize($d[$uid])
+                            : $d[$uid];
+                    }
+                    $this->_cache->set($this->_getCid($mbox, $slice), serialize($data), $lifetime);
+                }
+            }
+
+            if (!empty($val['slicemap'])) {
+                $this->_cache->set($this->_getCid($mbox, 'slicemap'), serialize($s), $lifetime);
+            }
+        }
+
+        $this->_update = array();
+    }
+
+    /**
+     */
+    public function get($mailbox, $uids, $fields, $uidvalid)
+    {
+        $ret = array();
+        $this->_loadUids($mailbox, $uids, $uidvalid);
+
+        if (empty($this->_data[$mailbox])) {
+            return $ret;
+        }
+
+        if (!empty($fields)) {
+            $fields = array_flip($fields);
+        }
+        $ptr = &$this->_data[$mailbox];
+
+        foreach (array_intersect($uids, array_keys($ptr)) as $val) {
+            if (is_string($ptr[$val])) {
+                $ptr[$val] = @unserialize($ptr[$val]);
+            }
+
+            $ret[$val] = (empty($fields) || empty($ptr[$val]))
+                ? $ptr[$val]
+                : array_intersect_key($ptr[$val], $fields);
+        }
+
+        return $ret;
+    }
+
+    /**
+     */
+    public function getCachedUids($mailbox, $uidvalid)
+    {
+        $this->_loadSliceMap($mailbox, $uidvalid);
+        return array_unique(array_merge(
+            array_keys($this->_slicemap[$mailbox]['s']),
+            (isset($this->_update[$mailbox]) ? $this->_update[$mailbox]['add'] : array())
+        ));
+    }
+
+    /**
+     */
+    public function set($mailbox, $data, $uidvalid)
+    {
+        $update = array_keys($data);
+
+        try {
+            $this->_loadUids($mailbox, $update, $uidvalid);
+        } catch (Horde_Imap_Client_Exception $e) {
+            // Ignore invalidity - just start building the new cache
+        }
+
+        $d = &$this->_data[$mailbox];
+        $s = &$this->_slicemap[$mailbox]['s'];
+        $add = $updated = array();
+
+        foreach ($data as $k => $v) {
+            if (isset($d[$k])) {
+                if (is_string($d[$k])) {
+                    $d[$k] = @unserialize($d[$k]);
+                }
+                $d[$k] = is_array($d[$k])
+                    ? array_merge($d[$k], $v)
+                    : $v;
+                if (isset($s[$k])) {
+                    $updated[$s[$k]] = true;
+                }
+            } else {
+                $d[$k] = $v;
+                $add[] = $k;
+            }
+        }
+
+        $this->_toUpdate($mailbox, 'add', $add);
+        $this->_toUpdate($mailbox, 'slice', array_keys($updated));
+    }
+
+    /**
+     */
+    public function getMetaData($mailbox, $uidvalid, $entries)
+    {
+        $this->_loadSliceMap($mailbox, $uidvalid);
+
+        return empty($entries)
+            ? $this->_slicemap[$mailbox]['d']
+            : array_intersect_key($this->_slicemap[$mailbox]['d'], array_flip($entries));
+    }
+
+    /**
+     */
+    public function setMetaData($mailbox, $data)
+    {
+        $this->_loadSliceMap($mailbox, isset($data['uidvalid']) ? $data['uidvalid'] : null);
+        $this->_slicemap[$mailbox]['d'] = array_merge($this->_slicemap[$mailbox]['d'], $data);
+        $this->_toUpdate($mailbox, 'slicemap', true);
+    }
+
+    /**
+     */
+    public function deleteMsgs($mailbox, $uids)
+    {
+        $slicemap = &$this->_slicemap[$mailbox];
+        $deleted = array_intersect_key($slicemap['s'], array_flip($uids));
+
+        if (isset($this->_update[$mailbox])) {
+            $this->_update[$mailbox]['add'] = array_diff(
+                $this->_update[$mailbox]['add'],
+                $uids
+            );
+        }
+
+        if (empty($deleted)) {
+            return;
+        }
+
+        $this->_loadUids($mailbox, array_keys($deleted));
+        $d = &$this->_data[$mailbox];
+
+        foreach (array_keys($deleted) as $id) {
+            unset($d[$id], $slicemap['s'][$id]);
+        }
+
+        foreach (array_unique($deleted) as $slice) {
+            /* Get rid of slice if less than 10% of capacity. */
+            if (($slice != $slicemap['i']) &&
+                ($slice_uids = array_keys($slicemap['s'], $slice)) &&
+                ($this->_params['slicesize'] * 0.1) > count($slice_uids)) {
+                $this->_toUpdate($mailbox, 'add', $slice_uids);
+                $this->_cache->expire($this->_getCid($mailbox, $slice));
+                foreach ($slice_uids as $val) {
+                    unset($slicemap['s'][$val]);
+                }
+            } else {
+                $this->_toUpdate($mailbox, 'slice', array($slice));
+            }
+        }
+    }
+
+    /**
+     */
+    public function deleteMailbox($mailbox)
+    {
+        $this->_loadSliceMap($mailbox);
+        $this->_deleteMailbox($mailbox);
+    }
+
+    /**
+     */
+    public function clear($lifetime)
+    {
+        $this->_cache->clear();
+        $this->_data = $this->_loaded = $this->_slicemap = $this->_update = array();
+    }
+
+    /**
+     * Create the unique ID used to store the data in the cache.
+     *
+     * @param string $mailbox  The mailbox to cache.
+     * @param string $slice    The cache slice.
+     *
+     * @return string  The cache ID.
+     */
+    protected function _getCid($mailbox, $slice)
+    {
+        return implode('|', array(
+            'horde_imap_client',
+            $this->_params['username'],
+            $mailbox,
+            $this->_params['hostspec'],
+            $this->_params['port'],
+            $slice,
+            self::VERSION
+        ));
+    }
+
+    /**
+     * Delete a mailbox from the cache.
+     *
+     * @param string $mbox  The mailbox to delete.
+     */
+    protected function _deleteMailbox($mbox)
+    {
+        foreach (array_merge(array_keys(array_flip($this->_slicemap[$mbox]['s'])), array('slicemap')) as $slice) {
+            $cid = $this->_getCid($mbox, $slice);
+            $this->_cache->expire($cid);
+            unset($this->_loaded[$cid]);
+        }
+
+        unset(
+            $this->_data[$mbox],
+            $this->_slicemap[$mbox],
+            $this->_update[$mbox]
+        );
+    }
+
+    /**
+     * Load UIDs by regenerating from the cache.
+     *
+     * @param string $mailbox    The mailbox to load.
+     * @param array $uids        The UIDs to load.
+     * @param integer $uidvalid  The IMAP uidvalidity value of the mailbox.
+     */
+    protected function _loadUids($mailbox, $uids, $uidvalid = null)
+    {
+        if (!isset($this->_data[$mailbox])) {
+            $this->_data[$mailbox] = array();
+        }
+
+        $this->_loadSliceMap($mailbox, $uidvalid);
+
+        if (!empty($uids)) {
+            foreach (array_unique(array_intersect_key($this->_slicemap[$mailbox]['s'], array_flip($uids))) as $slice) {
+                $this->_loadSlice($mailbox, $slice);
+            }
+        }
+    }
+
+    /**
+     * Load UIDs from a cache slice.
+     *
+     * @param string $mailbox  The mailbox to load.
+     * @param integer $slice   The slice to load.
+     */
+    protected function _loadSlice($mailbox, $slice)
+    {
+        $cache_id = $this->_getCid($mailbox, $slice);
+
+        if (!empty($this->_loaded[$cache_id])) {
+            return;
+        }
+
+        if ((($data = $this->_cache->get($cache_id, 0)) !== false) &&
+            ($data = @unserialize($data)) &&
+            is_array($data)) {
+            $this->_data[$mailbox] += $data;
+            $this->_loaded[$cache_id] = true;
+        } else {
+            $ptr = &$this->_slicemap[$mailbox];
+
+            // Slice data is corrupt; remove from slicemap.
+            foreach (array_keys($ptr['s'], $slice) as $val) {
+                unset($ptr['s'][$val]);
+            }
+
+            if ($slice == $ptr['i']) {
+                $ptr['c'] = 0;
+            }
+        }
+    }
+
+    /**
+     * Load the slicemap for a given mailbox.  The slicemap contains
+     * the uidvalidity information, the UIDs->slice lookup table, and any
+     * metadata that needs to be saved for the mailbox.
+     *
+     * @param string $mailbox    The mailbox.
+     * @param integer $uidvalid  The IMAP uidvalidity value of the mailbox.
+     */
+    protected function _loadSliceMap($mailbox, $uidvalid = null)
+    {
+        if (!isset($this->_slicemap[$mailbox]) &&
+            (($data = $this->_cache->get($this->_getCid($mailbox, 'slicemap'), 0)) !== false) &&
+            ($slice = @unserialize($data)) &&
+            is_array($slice)) {
+            $this->_slicemap[$mailbox] = $slice;
+        }
+
+        if (isset($this->_slicemap[$mailbox])) {
+            $ptr = &$this->_slicemap[$mailbox];
+            if (is_null($ptr['d']['uidvalid'])) {
+                $ptr['d']['uidvalid'] = $uidvalid;
+                return;
+            } elseif (!is_null($uidvalid) &&
+                      ($ptr['d']['uidvalid'] != $uidvalid)) {
+                $this->_deleteMailbox($mailbox);
+            } else {
+                return;
+            }
+        }
+
+        $this->_slicemap[$mailbox] = array(
+            // Tracking count for purposes of determining slices
+            'c' => 0,
+            // Metadata storage
+            // By default includes UIDVALIDITY of mailbox.
+            'd' => array('uidvalid' => $uidvalid),
+            // The ID of the last slice.
+            'i' => 0,
+            // The slice list.
+            's' => array()
+        );
+    }
+
+    /**
+     * Add update entry for a mailbox.
+     *
+     * @param string $mailbox  The mailbox.
+     * @param string $type     'add', 'slice', or 'slicemap'.
+     * @param mixed $data      The data to update.
+     */
+    protected function _toUpdate($mailbox, $type, $data)
+    {
+        if (!isset($this->_update[$mailbox])) {
+            $this->_update[$mailbox] = array(
+                'add' => array(),
+                'slice' => array()
+            );
+        }
+
+        $this->_update[$mailbox][$type] = ($type == 'slicemap')
+            ? $data
+            : array_merge($this->_update[$mailbox][$type], $data);
+    }
+
+    /* Serializable methods. */
+
+    /**
+     */
+    public function serialize()
+    {
+        $this->save();
+        return parent::serialize();
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientCacheBackendDbphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientCacheBackendDbphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Cache/Backend/Db.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Cache/Backend/Db.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Cache/Backend/Db.php                                (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Cache/Backend/Db.php   2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,407 @@
</span><ins>+<?php
+/**
+ * Copyright 2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * A SQL database implementation for caching IMAP/POP data.
+ * Requires the Horde_Db package.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Cache_Backend_Db extends Horde_Imap_Client_Cache_Backend
+{
+    /* Table names. */
+    const BASE_TABLE = 'horde_imap_client_data';
+    const MD_TABLE = 'horde_imap_client_metadata';
+    const MSG_TABLE = 'horde_imap_client_message';
+
+    /**
+     * Handle for the database connection.
+     *
+     * @var Horde_Db_Adapter
+     */
+    protected $_db;
+
+    /**
+     * Constructor.
+     *
+     * @param array $params  Configuration parameters:
+     * <ul>
+     *  <li>
+     *   REQUIRED Parameters:
+     *   <ul>
+     *    <li>
+     *     db: (Horde_Db_Adapter) DB object.
+     *    </li>
+     *   </ul>
+     *  </li>
+     * </ul>
+     */
+    public function __construct(array $params = array())
+    {
+        if (!isset($params['db'])) {
+            throw new InvalidArgumentException('Missing db parameter.');
+        }
+
+        parent::__construct($params);
+    }
+
+    /**
+     */
+    protected function _initOb()
+    {
+        $this->_db = $this->_params['db'];
+    }
+
+    /**
+     */
+    public function get($mailbox, $uids, $fields, $uidvalid)
+    {
+        $this->getMetaData($mailbox, $uidvalid, array('uidvalid'));
+
+        $query = $this->_baseSql($mailbox, self::MSG_TABLE);
+        $query[0] = 'SELECT t.data, t.msguid ' . $query[0];
+
+        $uid_query = array();
+        foreach ($uids as $val) {
+            $uid_query[] = 't.msguid = ?';
+            $query[1][] = strval($val);
+        }
+        $query[0] .= ' AND (' . implode(' OR ', $uid_query) . ')';
+
+        $compress = new Horde_Compress_Fast();
+        $out = array();
+
+        try {
+            $columns = $this->_db->columns(self::MSG_TABLE);
+            $res = $this->_db->select($query[0], $query[1]);
+
+            while (($row = $res->fetchObject()) !== false) {
+                $out[$row->msguid] = @unserialize($compress->decompress(
+                    $columns['data']->binaryToString($row->data)
+                ));
+            }
+        } catch (Horde_Db_Exception $e) {}
+
+        return $out;
+    }
+
+    /**
+     */
+    public function getCachedUids($mailbox, $uidvalid)
+    {
+        $this->getMetaData($mailbox, $uidvalid, array('uidvalid'));
+
+        $query = $this->_baseSql($mailbox, self::MSG_TABLE);
+        $query[0] = 'SELECT DISTINCT t.msguid ' . $query[0];
+
+        try {
+            return $this->_db->selectValues($query[0], $query[1]);
+        } catch (Horde_Db_Exception $e) {
+            return array();
+        }
+    }
+
+    /**
+     */
+    public function set($mailbox, $data, $uidvalid)
+    {
+        if ($uid = $this->_getUid($mailbox)) {
+            $res = $this->get($mailbox, array_keys($data), array(), $uidvalid);
+        } else {
+            $res = array();
+            $uid = $this->_createUid($mailbox);
+        }
+
+        $compress = new Horde_Compress_Fast();
+
+        foreach ($data as $key => $val) {
+            if (isset($res[$key])) {
+                try {
+                    /* Update */
+                    $this->_db->update(
+                        sprintf('UPDATE %s SET data = ? WHERE uid = ? AND msguid = ?', self::MSG_TABLE),
+                        array(
+                            new Horde_Db_Value_Binary($compress->compress(serialize(array_merge($res[$key], $val)))),
+                            $uid,
+                            strval($key)
+                        )
+                    );
+                } catch (Horde_Db_Exception $e) {}
+            } else {
+                /* Insert */
+                try {
+                    $this->_db->insert(
+                        sprintf('INSERT INTO %s (data, msguid, uid) VALUES (?, ?, ?)', self::MSG_TABLE),
+                        array(
+                            new Horde_Db_Value_Binary($compress->compress(serialize($val))),
+                            strval($key),
+                            $uid
+                        )
+                    );
+                } catch (Horde_Db_Exception $e) {}
+            }
+        }
+
+        /* Update modified time. */
+        try {
+            $this->_db->update(
+                sprintf(
+                    'UPDATE %s SET modified = ? WHERE uid = ?',
+                    self::BASE_TABLE
+                ),
+                array(time(), $uid)
+            );
+        } catch (Horde_Db_Exception $e) {}
+
+        /* Update uidvalidity. */
+        $this->setMetaData($mailbox, array('uidvalid' => $uidvalid));
+    }
+
+    /**
+     */
+    public function getMetaData($mailbox, $uidvalid, $entries)
+    {
+        $query = $this->_baseSql($mailbox, self::MD_TABLE);
+        $query[0] = 'SELECT t.field, t.data ' . $query[0];
+
+        if (!empty($entries)) {
+            $entries[] = 'uidvalid';
+            $entry_query = array();
+
+            foreach (array_unique($entries) as $val) {
+                $entry_query[] = 't.field = ?';
+                $query[1][] = $val;
+            }
+            $query[0] .= ' AND (' . implode(' OR ', $entry_query) . ')';
+        }
+
+        try {
+            if ($res = $this->_db->selectAssoc($query[0], $query[1])) {
+                $columns = $this->_db->columns(self::MD_TABLE);
+                foreach ($res as $key => $val) {
+                    switch ($key) {
+                    case 'uidvalid':
+                        $res[$key] = $columns['data']->binaryToString($val);
+                        break;
+
+                    default:
+                        $res[$key] = @unserialize(
+                            $columns['data']->binaryToString($val)
+                        );
+                        break;
+                    }
+                }
+
+                if (is_null($uidvalid) ||
+                    !isset($res['uidvalid']) ||
+                    ($res['uidvalid'] == $uidvalid)) {
+                    return $res;
+                }
+
+                $this->deleteMailbox($mailbox);
+            }
+        } catch (Horde_Db_Exception $e) {}
+
+        return array();
+    }
+
+    /**
+     */
+    public function setMetaData($mailbox, $data)
+    {
+        if (!($uid = $this->_getUid($mailbox))) {
+            $uid = $this->_createUid($mailbox);
+        }
+
+        $query = sprintf('SELECT field FROM %s where uid = ?', self::MD_TABLE);
+        $values = array($uid);
+
+        try {
+            $fields = $this->_db->selectValues($query, $values);
+        } catch (Horde_Db_Exception $e) {
+            return;
+        }
+
+        foreach ($data as $key => $val) {
+            $val = new Horde_Db_Value_Binary(($key == 'uidvalid') ? $val : serialize($val));
+
+            if (in_array($key, $fields)) {
+                /* Update */
+                try {
+                    $this->_db->update(
+                        sprintf(
+                            'UPDATE %s SET data = ? WHERE field = ? AND uid = ?',
+                            self::MD_TABLE
+                        ),
+                        array($val, $key, $uid)
+                    );
+                } catch (Horde_Db_Exception $e) {}
+            } else {
+                /* Insert */
+                try {
+                    $this->_db->insert(
+                        sprintf(
+                            'INSERT INTO %s (data, field, uid) VALUES (?, ?, ?)',
+                            self::MD_TABLE
+                        ),
+                        array($val, $key, $uid)
+                    );
+                } catch (Horde_Db_Exception $e) {}
+            }
+        }
+    }
+
+    /**
+     */
+    public function deleteMsgs($mailbox, $uids)
+    {
+        $query = $this->_baseSql($mailbox);
+        $query[0] = sprintf(
+            'DELETE FROM %s WHERE uid IN (SELECT uid ' . $query[0] . ')',
+            self::MSG_TABLE
+        );
+
+        $uid_query = array();
+        foreach ($uids as $val) {
+            $uid_query[] = 'msguid = ?';
+            $query[1][] = strval($val);
+        }
+        $query[0] .= ' AND (' . implode(' OR ', $uid_query) . ')';
+
+        try {
+            $this->_db->delete($query[0], $query[1]);
+        } catch (Horde_Db_Exception $e) {}
+    }
+
+    /**
+     */
+    public function deleteMailbox($mailbox)
+    {
+        if (is_null($uid = $this->_getUid($mailbox))) {
+            return;
+        }
+
+        foreach (array(self::BASE_TABLE, self::MD_TABLE, self::MSG_TABLE) as $val) {
+            try {
+                $this->_db->delete(
+                    sprintf('DELETE FROM %s WHERE uid = ?', $val),
+                    array($uid)
+                );
+            } catch (Horde_Db_Exception $e) {}
+        }
+    }
+
+    /**
+     */
+    public function clear($lifetime)
+    {
+        if (is_null($lifetime)) {
+            try {
+                $this->_db->delete(sprintf('DELETE FROM %s', self::BASE_TABLE));
+                $this->_db->delete(sprintf('DELETE FROM %s', self::MD_TABLE));
+                $this->_db->delete(sprintf('DELETE FROM %s', self::MSG_TABLE));
+            } catch (Horde_Db_Exception $e) {}
+            return;
+        }
+
+        $purge = time() - $lifetime;
+        $sql = 'DELETE FROM %s WHERE uid IN (SELECT uid FROM %s WHERE modified < ?';
+
+        foreach (array(self::MD_TABLE, self::MSG_TABLE) as $val) {
+            try {
+                $this->_db->delete(
+                    sprintf($sql, $val, self::BASE_TABLE),
+                    array($purge)
+                );
+            } catch (Horde_Db_Exception $e) {}
+        }
+
+        try {
+            $this->_db->delete(
+                sprintf('DELETE FROM %s WHERE modified < ?', self::BASE_TABLE),
+                array($purge)
+            );
+        } catch (Horde_Db_Exception $e) {}
+    }
+
+    /**
+     * Prepare the base SQL query.
+     *
+     * @param string $mailbox  The mailbox.
+     * @param string $join     The table to join with the base table.
+     *
+     * @return array  SQL query and bound parameters.
+     */
+    protected function _baseSql($mailbox, $join = null)
+    {
+        $sql = sprintf('FROM %s d', self::BASE_TABLE);
+
+        if (!is_null($join)) {
+            $sql .= sprintf(' INNER JOIN %s t ON d.uid = t.uid', $join);
+        }
+
+        return array(
+            $sql . ' WHERE d.hostspec = ? AND d.port = ? AND d.username = ? AND d.mailbox = ?',
+            array(
+                $this->_params['hostspec'],
+                $this->_params['port'],
+                $this->_params['username'],
+                $mailbox
+            )
+        );
+    }
+
+    /**
+     * @param string $mailbox
+     *
+     * @return string  UID from base table.
+     */
+    protected function _getUid($mailbox)
+    {
+        $query = $this->_baseSql($mailbox);
+        $query[0] = 'SELECT d.uid ' . $query[0];
+
+        try {
+            return $this->_db->selectValue($query[0], $query[1]);
+        } catch (Horde_Db_Exception $e) {
+            return null;
+        }
+    }
+
+    /**
+     * @param string $mailbox
+     *
+     * @return string  UID from base table.
+     */
+    protected function _createUid($mailbox)
+    {
+        return $this->_db->insert(
+            sprintf(
+                'INSERT INTO %s (hostspec, mailbox, port, username) ' .
+                    'VALUES (?, ?, ?, ?)',
+                self::BASE_TABLE
+            ),
+            array(
+                $this->_params['hostspec'],
+                $mailbox,
+                $this->_params['port'],
+                $this->_params['username']
+            )
+        );
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientCacheBackendMongophpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientCacheBackendMongophp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Cache/Backend/Mongo.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Cache/Backend/Mongo.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Cache/Backend/Mongo.php                             (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Cache/Backend/Mongo.php        2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,425 @@
</span><ins>+<?php
+/**
+ * Copyright 2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * A MongoDB database implementation for caching IMAP/POP data.
+ * Requires the Horde_Mongo class.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Cache_Backend_Mongo extends Horde_Imap_Client_Cache_Backend implements Horde_Mongo_Collection_Index
+{
+    /* Collection names. */
+    const BASE = 'horde_imap_client_cache_data';
+    const MD = 'horde_imap_client_cache_metadata';
+    const MSG = 'horde_imap_client_cache_message';
+
+    /**
+     * The MongoDB object for the cache data.
+     *
+     * @var MongoDB
+     */
+    protected $_db;
+
+    /**
+     * The list of indices.
+     *
+     * @var array
+     */
+    protected $_indices = array(
+        self::BASE => array(
+            'base_index_1' => array(
+                'hostspec' => 1,
+                'mailbox' => 1,
+                'port' => 1,
+                'username' => 1,
+            )
+        ),
+        self::MSG => array(
+            'msg_index_1' => array(
+                'uid' => 1,
+                'msguid' => 1
+            )
+        )
+    );
+
+    /**
+     * Constructor.
+     *
+     * @param array $params  Configuration parameters:
+     * <ul>
+     *  <li>
+     *   REQUIRED parameters:
+     *   <ul>
+     *    <li>
+     *     mongo_db: (Horde_Mongo_Client) A MongoDB client object.
+     *    </li>
+     *   </ul>
+     *  </li>
+     */
+    public function __construct(array $params = array())
+    {
+        if (!isset($params['mongo_db'])) {
+            throw new InvalidArgumentException('Missing mongo_db parameter.');
+        }
+
+        parent::__construct($params);
+    }
+
+    /**
+     */
+    protected function _initOb()
+    {
+        $this->_db = $this->_params['mongo_db']->selectDB(null);
+    }
+
+    /**
+     */
+    public function get($mailbox, $uids, $fields, $uidvalid)
+    {
+        $this->getMetaData($mailbox, $uidvalid, array('uidvalid'));
+
+        if (!($uid = $this->_getUid($mailbox))) {
+            return array();
+        }
+
+        $out = array();
+        $query = array(
+            'msguid' => array('$in' => array_map('strval', $uids)),
+            'uid' => $uid
+        );
+
+        try {
+            $cursor = $this->_db->selectCollection(self::MSG)->find(
+                $query,
+                array(
+                    'data' => true,
+                    'msguid' => true
+                )
+            );
+            foreach ($cursor as $val) {
+                $out[$val['msguid']] = $this->_value($val['data']);
+            }
+        } catch (MongoException $e) {}
+
+        return $out;
+    }
+
+    /**
+     */
+    public function getCachedUids($mailbox, $uidvalid)
+    {
+        $this->getMetaData($mailbox, $uidvalid, array('uidvalid'));
+
+        if (!($uid = $this->_getUid($mailbox))) {
+            return array();
+        }
+
+        $out = array();
+        $query = array(
+            'uid' => $uid
+        );
+
+        try {
+            $cursor = $this->_db->selectCollection(self::MSG)->find(
+                $query,
+                array(
+                    'msguid' => true
+                )
+            );
+            foreach ($cursor as $val) {
+                $out[] = $val['msguid'];
+            }
+        } catch (MongoException $e) {}
+
+        return $out;
+    }
+
+    /**
+     */
+    public function set($mailbox, $data, $uidvalid)
+    {
+        if ($uid = $this->_getUid($mailbox)) {
+            $res = $this->get($mailbox, array_keys($data), array(), $uidvalid);
+        } else {
+            $res = array();
+            $uid = $this->_createUid($mailbox);
+        }
+
+        $coll = $this->_db->selectCollection(self::MSG);
+
+        foreach ($data as $key => $val) {
+            try {
+                if (isset($res[$key])) {
+                    $coll->update(array(
+                        'msguid' => strval($key),
+                        'uid' => $uid
+                    ), array(
+                        'data' => $this->_value(array_merge($res[$key], $val)),
+                        'msguid' => strval($key),
+                        'uid' => $uid
+                    ));
+                } else {
+                    $coll->insert(array(
+                        'data' => $this->_value($val),
+                        'msguid' => strval($key),
+                        'uid' => $uid
+                    ));
+                }
+            } catch (MongoException $e) {}
+        }
+
+        /* Update modified time. */
+        try {
+            $this->_db->selectCollection(self::BASE)->update(
+                array('uid' => $uid),
+                array('modified' => time())
+            );
+        } catch (MongoException $e) {}
+
+        /* Update uidvalidity. */
+        $this->setMetaData($mailbox, array('uidvalid' => $uidvalid));
+    }
+
+    /**
+     */
+    public function getMetaData($mailbox, $uidvalid, $entries)
+    {
+        if (!($uid = $this->_getUid($mailbox))) {
+            return array();
+        }
+
+        $out = array();
+        $query = array(
+            'uid' => $uid
+        );
+
+        if (!empty($entries)) {
+            $entries[] = 'uidvalid';
+            $query['field'] = array(
+                '$in' => array_unique($entries)
+            );
+        }
+
+        try {
+            $cursor = $this->_db->selectCollection(self::MD)->find(
+                $query,
+                array(
+                    'data' => true,
+                    'field' => true
+                )
+            );
+            foreach ($cursor as $val) {
+                $out[$val['field']] = $this->_value($val['data']);
+            }
+
+            if (is_null($uidvalid) ||
+                !isset($out['uidvalid']) ||
+                ($out['uidvalid'] == $uidvalid)) {
+                return $out;
+            }
+
+            $this->deleteMailbox($mailbox);
+        } catch (Horde_Db_Exception $e) {}
+
+        return array();
+    }
+
+    /**
+     */
+    public function setMetaData($mailbox, $data)
+    {
+        if (!($uid = $this->_getUid($mailbox))) {
+            $uid = $this->_createUid($mailbox);
+        }
+
+        $coll = $this->_db->selectCollection(self::MD);
+
+        foreach ($data as $key => $val) {
+            try {
+                $coll->update(
+                    array(
+                        'field' => $key,
+                        'uid' => $uid
+                    ),
+                    array(
+                        'data' => $this->_value($val),
+                        'field' => $key,
+                        'uid' => $uid
+                    ),
+                    array('upsert' => true)
+                );
+            } catch (MongoException $e) {}
+        }
+    }
+
+    /**
+     */
+    public function deleteMsgs($mailbox, $uids)
+    {
+        if ($uid = $this->_getUid($mailbox)) {
+            try {
+                $this->_db->selectCollection(self::MSG)->remove(array(
+                    'msguid' => array('$in' => array_map('strval', $uids)),
+                    'uid' => $uid
+                ));
+            } catch (MongoException $e) {}
+        }
+    }
+
+    /**
+     */
+    public function deleteMailbox($mailbox)
+    {
+        if (!($uid = $this->_getUid($mailbox))) {
+            return;
+        }
+
+        foreach (array(self::BASE, self::MD, self::MSG) as $val) {
+            try {
+                $this->_db->selectCollection($val)->remove(array(
+                    'uid' => $uid
+                ));
+            } catch (MongoException $e) {}
+        }
+    }
+
+    /**
+     */
+    public function clear($lifetime)
+    {
+        if (is_null($lifetime)) {
+            foreach (array(self::BASE, self::MD, self::MSG) as $val) {
+                $this->_db->selectCollection($val)->drop();
+            }
+            return;
+        }
+
+        $query = array(
+            'modified' => array('$lt' => (time() - $lifetime))
+        );
+        $uids = array();
+
+        try {
+            $cursor = $this->_db->selectCollection(self::BASE)->find($query);
+            foreach ($cursor as $val) {
+                $uids[] = strval($val['_id']);
+            }
+        } catch (MongoException $e) {}
+
+        if (empty($uids)) {
+            return;
+        }
+
+        foreach (array(self::BASE, self::MD, self::MSG) as $val) {
+            try {
+                $this->_db->selectCollection($val)->remove(array(
+                    'uid' => array('$in' => $uids)
+                ));
+            } catch (MongoException $e) {}
+        }
+    }
+
+    /**
+     * Return the UID for a mailbox/user/server combo.
+     *
+     * @param string $mailbox  Mailbox name.
+     *
+     * @return string  UID from base table.
+     */
+    protected function _getUid($mailbox)
+    {
+        $query = array(
+            'hostspec' => $this->_params['hostspec'],
+            'mailbox' => $mailbox,
+            'port' => $this->_params['port'],
+            'username' => $this->_params['username']
+        );
+
+        try {
+            if ($result = $this->_db->selectCollection(self::BASE)->findOne($query)) {
+                return strval($result['_id']);
+            }
+        } catch (MongoException $e) {}
+
+        return null;
+    }
+
+    /**
+     * Create and return the UID for a mailbox/user/server combo.
+     *
+     * @param string $mailbox  Mailbox name.
+     *
+     * @return string  UID from base table.
+     */
+    protected function _createUid($mailbox)
+    {
+        $this->_db->selectCollection(self::BASE)->insert(array(
+            'hostspec' => $this->_params['hostspec'],
+            'mailbox' => $mailbox,
+            'port' => $this->_params['port'],
+            'username' => $this->_params['username']
+        ));
+
+        return $this->_getUid($mailbox);
+    }
+
+    /**
+     * Convert data from/to storage format.
+     *
+     * @param mixed|MongoBinData $data  The data object.
+     *
+     * @return mixed|MongoBinData  The converted data.
+     */
+    protected function _value($data)
+    {
+        static $compress;
+
+        if (!isset($compress)) {
+            $compress = new Horde_Compress_Fast();
+        }
+
+        return ($data instanceof MongoBinData)
+            ? @unserialize($compress->decompress($data->bin))
+            : new MongoBinData($compress->compress(serialize($data)), MongoBinData::BYTE_ARRAY);
+    }
+
+    /* Horde_Mongo_Collection_Index methods. */
+
+    /**
+     */
+    public function checkMongoIndices()
+    {
+        foreach ($this->_indices as $key => $val) {
+            if (!$this->_params['mongo_db']->checkIndices($key, $val)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     */
+    public function createMongoIndices()
+    {
+        foreach ($this->_indices as $key => $val) {
+            $this->_params['mongo_db']->createIndices($key, $val);
+        }
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientCacheBackendNullphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientCacheBackendNullphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Cache/Backend/Null.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Cache/Backend/Null.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Cache/Backend/Null.php                              (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Cache/Backend/Null.php 2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,78 @@
</span><ins>+<?php
+/**
+ * Copyright 2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * A null backend class for storing cached IMAP/POP data.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Cache_Backend_Null extends Horde_Imap_Client_Cache_Backend
+{
+    /**
+     */
+    public function get($mailbox, $uids, $fields, $uidvalid)
+    {
+        return array();
+    }
+
+    /**
+     */
+    public function getCachedUids($mailbox, $uidvalid)
+    {
+        return array();
+    }
+
+    /**
+     */
+    public function set($mailbox, $data, $uidvalid)
+    {
+    }
+
+    /**
+     */
+    public function getMetaData($mailbox, $uidvalid, $entries)
+    {
+        return array(
+            'uidvalid' => 0
+        );
+    }
+
+    /**
+     */
+    public function setMetaData($mailbox, $data)
+    {
+    }
+
+    /**
+     */
+    public function deleteMsgs($mailbox, $uids)
+    {
+    }
+
+    /**
+     */
+    public function deleteMailbox($mailbox)
+    {
+    }
+
+    /**
+     */
+    public function clear($lifetime)
+    {
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientCacheBackendphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientCacheBackendphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Cache/Backend.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Cache/Backend.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Cache/Backend.php                           (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Cache/Backend.php      2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,165 @@
</span><ins>+<?php
+/**
+ * Copyright 2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * The abstract backend class for storing IMAP cached data.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+abstract class Horde_Imap_Client_Cache_Backend implements Serializable
+{
+    /**
+     * Configuration paramters.
+     * Values set by the base Cache object: hostspec, port, username
+     *
+     * @var array
+     */
+    protected $_params = array();
+
+    /**
+     * Constructor.
+     *
+     * @param array $params  Configuration parameters.
+     */
+    public function __construct(array $params = array())
+    {
+        $this->setParams($params);
+        $this->_initOb();
+    }
+
+    /**
+     * Initialization tasks.
+     */
+    protected function _initOb()
+    {
+    }
+
+    /**
+     * Add configuration parameters.
+     *
+     * @param array $params  Configuration parameters.
+     */
+    public function setParams(array $params = array())
+    {
+        $this->_params = array_merge($this->_params, $params);
+    }
+
+    /**
+     * Get information from the cache for a set of UIDs.
+     *
+     * @param string $mailbox    An IMAP mailbox string.
+     * @param array $uids        The list of message UIDs to retrieve
+     *                           information for.
+     * @param array $fields      An array of fields to retrieve. If empty,
+     *                           returns all cached fields.
+     * @param integer $uidvalid  The IMAP uidvalidity value of the mailbox.
+     *
+     * @return array  An array of arrays with the UID of the message as the
+     *                key (if found) and the fields as values (will be
+     *                undefined if not found).
+     */
+    abstract public function get($mailbox, $uids, $fields, $uidvalid);
+
+    /**
+     * Get the list of cached UIDs.
+     *
+     * @param string $mailbox    An IMAP mailbox string.
+     * @param integer $uidvalid  The IMAP uidvalidity value of the mailbox.
+     *
+     * @return array  The (unsorted) list of cached UIDs.
+     */
+    abstract public function getCachedUids($mailbox, $uidvalid);
+
+    /**
+     * Store data in cache.
+     *
+     * @param string $mailbox    An IMAP mailbox string.
+     * @param array $data        The list of data to save. The keys are the
+     *                           UIDs, the values are an array of information
+     *                           to save.
+     * @param integer $uidvalid  The IMAP uidvalidity value of the mailbox.
+     */
+    abstract public function set($mailbox, $data, $uidvalid);
+
+    /**
+     * Get metadata information for a mailbox.
+     *
+     * @param string $mailbox    An IMAP mailbox string.
+     * @param integer $uidvalid  The IMAP uidvalidity value of the mailbox.
+     * @param array $entries     An array of entries to return. If empty,
+     *                           returns all metadata.
+     *
+     * @return array  The requested metadata. Requested entries that do not
+     *                exist will be undefined. The following entries are
+     *                defaults and always present:
+     *   - uidvalid: (integer) The UIDVALIDITY of the mailbox.
+     */
+    abstract public function getMetaData($mailbox, $uidvalid, $entries);
+
+    /**
+     * Set metadata information for a mailbox.
+     *
+     * @param string $mailbox    An IMAP mailbox string.
+     * @param array $data        The list of data to save. The keys are the
+     *                           metadata IDs, the values are the associated
+     *                           data. (If present, uidvalidity appears as
+     *                           the 'uidvalid' key in $data.)
+     */
+    abstract public function setMetaData($mailbox, $data);
+
+    /**
+     * Delete messages in the cache.
+     *
+     * @param string $mailbox  An IMAP mailbox string.
+     * @param array $uids      The list of message UIDs to delete.
+     */
+    abstract public function deleteMsgs($mailbox, $uids);
+
+    /**
+     * Delete a mailbox from the cache.
+     *
+     * @param string $mailbox  The mailbox to delete.
+     */
+    abstract public function deleteMailbox($mailbox);
+
+    /**
+     * Clear the cache.
+     *
+     * @param integer $lifetime  Only delete entries older than this (in
+     *                           seconds). If null, deletes all entries.
+     */
+    abstract public function clear($lifetime);
+
+
+    /* Serializable methods. */
+
+    /**
+     */
+    public function serialize()
+    {
+        return serialize($this->_params);
+    }
+
+    /**
+     */
+    public function unserialize($data)
+    {
+        $this->_params = unserialize($data);
+        $this->_initOb();
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientCachephpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientCachephp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Cache.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Cache.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Cache.php                           (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Cache.php      2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,253 @@
</span><ins>+<?php
+/**
+ * Copyright 2005-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2005-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * An interface to cache data retrieved from the IMAP server.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2005-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Cache
+{
+    /**
+     * Base client object.
+     *
+     * @var Horde_Imap_Client_Base
+     */
+    protected $_baseob;
+
+    /**
+     * Storage backend.
+     *
+     * @var Horde_Imap_Client_Cache_Backend
+     */
+    protected $_backend;
+
+    /**
+     * Debug output.
+     *
+     * @var Horde_Imap_Client_Base_Debug
+     */
+    protected $_debug = false;
+
+    /**
+     * The configuration params.
+     *
+     * @var array
+     */
+    protected $_params = array();
+
+    /**
+     * Constructor.
+     *
+     * @param array $params  Configuration parameters:
+     * <ul>
+     *  <li>
+     *   REQUIRED Parameters:
+     *   <ul>
+     *    <li>
+     *     backend: (Horde_Imap_Client_Cache_Backend) The cache backend.
+     *    </li>
+     *    <li>
+     *     baseob: (Horde_Imap_Client_Base) The base client object.
+     *    </li>
+     *   </ul>
+     *  </li>
+     *  <li>
+     *   Optional Parameters:
+     *   <ul>
+     *    <li>
+     *     debug: (Horde_Imap_Client_Base_Debug) Debug object.
+     *            DEFAULT: No debug output
+     *    </li>
+     *   </ul>
+     *  </li>
+     * </ul>
+     */
+    public function __construct(array $params = array())
+    {
+        $this->_backend = $params['backend'];
+        $this->_baseob = $params['baseob'];
+
+        $this->_backend->setParams(array(
+            'hostspec' => $this->_baseob->getParam('hostspec'),
+            'port' => $this->_baseob->getParam('port'),
+            'username' => $this->_baseob->getParam('username')
+        ));
+
+        if (isset($params['debug']) &&
+            ($params['debug'] instanceof Horde_Imap_Client_Base_Debug)) {
+            $this->_debug = $params['debug'];
+            $this->_debug->info(sprintf("CACHE: Using the %s storage driver.", get_class($this->_backend)));
+        }
+    }
+
+    /**
+     * Get information from the cache.
+     *
+     * @param string $mailbox    An IMAP mailbox string.
+     * @param array $uids        The list of message UIDs to retrieve
+     *                           information for. If empty, returns the list
+     *                           of cached UIDs.
+     * @param array $fields      An array of fields to retrieve. If empty,
+     *                           returns all cached fields.
+     * @param integer $uidvalid  The IMAP uidvalidity value of the mailbox.
+     *
+     * @return array  An array of arrays with the UID of the message as the
+     *                key (if found) and the fields as values (will be
+     *                undefined if not found). If $uids is empty, returns the
+     *                full (unsorted) list of cached UIDs.
+     */
+    public function get($mailbox, array $uids = array(), $fields = array(),
+                        $uidvalid = null)
+    {
+        $mailbox = strval($mailbox);
+
+        if (empty($uids)) {
+            $ret = $this->_backend->getCachedUids($mailbox, $uidvalid);
+        } else {
+            $ret = $this->_backend->get($mailbox, $uids, $fields, $uidvalid);
+
+            if ($this->_debug && !empty($ret)) {
+                $this->_debug->info('CACHE: Retrieved messages (mailbox: ' . $mailbox . '; UIDs: ' . $this->_baseob->getIdsOb(array_keys($ret))->tostring_sort . ")");
+            }
+        }
+
+        return $ret;
+    }
+
+    /**
+     * Store information in cache.
+     *
+     * @param string $mailbox    An IMAP mailbox string.
+     * @param array $data        The list of data to save. The keys are the
+     *                           UIDs, the values are an array of information
+     *                           to save. If empty, do a check to make sure
+     *                           the uidvalidity is still valid.
+     * @param integer $uidvalid  The IMAP uidvalidity value of the mailbox.
+     */
+    public function set($mailbox, $data, $uidvalid)
+    {
+        $mailbox = strval($mailbox);
+
+        if (empty($data)) {
+            $this->_backend->getMetaData($mailbox, $uidvalid, array('uidvalid'));
+        } else {
+            $this->_backend->set($mailbox, $data, $uidvalid);
+
+            if ($this->_debug) {
+                $this->_debug->info('CACHE: Stored messages (mailbox: ' . $mailbox . '; UIDs: ' . $this->_baseob->getIdsOb(array_keys($data))->tostring_sort . ")");
+            }
+        }
+    }
+
+    /**
+     * Get metadata information for a mailbox.
+     *
+     * @param string $mailbox    An IMAP mailbox string.
+     * @param integer $uidvalid  The IMAP uidvalidity value of the mailbox.
+     * @param array $entries     An array of entries to return. If empty,
+     *                           returns all metadata.
+     *
+     * @return array  The requested metadata. Requested entries that do not
+     *                exist will be undefined. The following entries are
+     *                defaults and always present:
+     *   - uidvalid: (integer) The UIDVALIDITY of the mailbox.
+     */
+    public function getMetaData($mailbox, $uidvalid = null,
+                                array $entries = array())
+    {
+        return $this->_backend->getMetaData(strval($mailbox), $uidvalid, $entries);
+    }
+
+    /**
+     * Set metadata information for a mailbox.
+     *
+     * @param string $mailbox    An IMAP mailbox string.
+     * @param integer $uidvalid  The IMAP uidvalidity value of the mailbox.
+     * @param array $data        The list of data to save. The keys are the
+     *                           metadata IDs, the values are the associated
+     *                           data. The following labels are reserved:
+     *                           'uidvalid'.
+     */
+    public function setMetaData($mailbox, $uidvalid, array $data = array())
+    {
+        unset($data['uidvalid']);
+
+        if (!empty($data)) {
+            if (!empty($uidvalid)) {
+                $data['uidvalid'] = $uidvalid;
+            }
+            $mailbox = strval($mailbox);
+
+            $this->_backend->setMetaData($mailbox, $data);
+
+            if ($this->_debug) {
+                $this->_debug->info('CACHE: Stored metadata (mailbox: ' . $mailbox . '; keys: ' . implode(',', array_keys($data)) . ")");
+            }
+        }
+    }
+
+    /**
+     * Delete messages in the cache.
+     *
+     * @param string $mailbox  An IMAP mailbox string.
+     * @param array $uids      The list of message UIDs to delete.
+     */
+    public function deleteMsgs($mailbox, $uids)
+    {
+        if (empty($uids)) {
+            return;
+        }
+
+        $mailbox = strval($mailbox);
+
+        $this->_backend->deleteMsgs($mailbox, $uids);
+
+        if ($this->_debug) {
+            $this->_debug->info('CACHE: Deleted messages (mailbox: ' . $mailbox . '; UIDs: ' . $this->_baseob->getIdsOb($uids)->tostring_sort . ")");
+        }
+    }
+
+    /**
+     * Delete a mailbox from the cache.
+     *
+     * @param string $mbox  The mailbox to delete.
+     */
+    public function deleteMailbox($mbox)
+    {
+        $mbox = strval($mbox);
+        $this->_backend->deleteMailbox($mbox);
+
+        if ($this->_debug) {
+            $this->_debug->info('CACHE: Deleted mailbox (mailbox: ' . $mbox . ")");
+        }
+    }
+
+    /**
+     * Clear the cache.
+     *
+     * @since 2.9.0
+     *
+     * @param integer $lifetime  Only delete entries older than this (in
+     *                           seconds). If null, deletes all entries.
+     */
+    public function clear($lifetime = null)
+    {
+        $this->_backend->clear($lifetime);
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientDataAclphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientDataAclphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Acl.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Acl.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Acl.php                                (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Acl.php   2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,156 @@
</span><ins>+<?php
+/**
+ * Copyright 2011-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2011-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * ACL rights for a mailbox (see RFC 2086/4314).
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2011-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Data_Acl extends Horde_Imap_Client_Data_AclCommon implements ArrayAccess, IteratorAggregate, Serializable
+{
+    /**
+     * ACL rights.
+     *
+     * @var array
+     */
+    protected $_rights;
+
+    /**
+     * Constructor.
+     *
+     * @param string $rights  The rights (see RFC 4314 [2.1]).
+     */
+    public function __construct($rights = '')
+    {
+        $this->_rights = str_split($rights);
+        $this->_normalize();
+    }
+
+    /**
+     * String representation of the ACL.
+     *
+     * @return string  String representation (RFC 4314 compliant).
+     */
+    public function __toString()
+    {
+        return implode('', $this->_rights);
+    }
+
+    /**
+     * Computes the difference to another rights string.
+     * Virtual rights are ignored.
+     *
+     * @param string $rights  The rights to compute against.
+     *
+     * @return array  Two element array: added and removed.
+     */
+    public function diff($rights)
+    {
+        $rlist = array_diff(str_split($rights), array_keys($this->_virtual));
+
+        return array(
+            'added' => implode('', array_diff($rlist, $this->_rights)),
+            'removed' => implode('', array_diff($this->_rights, $rlist))
+        );
+    }
+
+    /**
+     * Normalize virtual rights (see RFC 4314 [2.1.1]).
+     */
+    protected function _normalize()
+    {
+        /* Clients conforming to RFC 4314 MUST ignore the virtual ACL_CREATE
+         * and ACL_DELETE rights. See RFC 4314 [2.1]. However, we still need
+         * to handle these rights when dealing with RFC 2086 servers since
+         * we are abstracting out use of ACL_CREATE/ACL_DELETE to their
+         * component RFC 4314 rights. */
+        foreach ($this->_virtual as $key => $val) {
+            if ($this[$key]) {
+                unset($this[$key]);
+                if (!$this[reset($val)]) {
+                    $this->_rights = array_unique(array_merge($this->_rights, $val));
+                }
+            }
+        }
+    }
+
+    /* ArrayAccess methods. */
+
+    /**
+     */
+    public function offsetExists($offset)
+    {
+        return $this[$offset];
+    }
+
+    /**
+     */
+    public function offsetGet($offset)
+    {
+        return in_array($offset, $this->_rights);
+    }
+
+    /**
+     */
+    public function offsetSet($offset, $value)
+    {
+        if ($value) {
+            if (!$this[$offset]) {
+                $this->_rights[] = $offset;
+                $this->_normalize();
+            }
+        } elseif ($this[$offset]) {
+            if (isset($this->_virtual[$offset])) {
+                foreach ($this->_virtual[$offset] as $val) {
+                    unset($this[$val]);
+                }
+            }
+            unset($this[$offset]);
+        }
+    }
+
+    /**
+     */
+    public function offsetUnset($offset)
+    {
+        $this->_rights = array_values(array_diff($this->_rights, array($offset)));
+    }
+
+    /* IteratorAggregate method. */
+
+    public function getIterator()
+    {
+        return new ArrayIterator($this->_rights);
+    }
+
+    /* Serializable methods. */
+
+    /**
+     */
+    public function serialize()
+    {
+        return json_encode($this->_rights);
+    }
+
+    /**
+     */
+    public function unserialize($data)
+    {
+        $this->_rights = json_decode($data);
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientDataAclCommonphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientDataAclCommonphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/AclCommon.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/AclCommon.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/AclCommon.php                          (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/AclCommon.php     2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,72 @@
</span><ins>+<?php
+/**
+ * Copyright 2011-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2011-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Provides common methods shared in all ACL classes (see RFC 2086/4314).
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2011-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Data_AclCommon
+{
+    /* Constants for getString(). */
+    const RFC_2086 = 1;
+    const RFC_4314 = 2;
+
+    /**
+     * List of virtual rights (RFC 4314 [2.1.1]).
+     *
+     * @var array
+     */
+    protected $_virtual = array(
+        Horde_Imap_Client::ACL_CREATE => array(
+            Horde_Imap_Client::ACL_CREATEMBOX,
+            Horde_Imap_Client::ACL_DELETEMBOX
+        ),
+        Horde_Imap_Client::ACL_DELETE => array(
+            Horde_Imap_Client::ACL_DELETEMSGS,
+            // Don't put this first - we do checks on the existence of the
+            // first element in this array to determine the RFC type, and this
+            // is duplicate of right contained in ACL_CREATE.
+            Horde_Imap_Client::ACL_DELETEMBOX,
+            Horde_Imap_Client::ACL_EXPUNGE
+        )
+    );
+
+    /**
+     * Returns the raw string to use in IMAP server calls.
+     *
+     * @param integer $type  The RFC type to use (RFC_* constant).
+     *
+     * @return string  The string representation of the ACL.
+     */
+    public function getString($type = self::RFC_4314)
+    {
+        $acl = strval($this);
+
+        if ($type == self::RFC_2086) {
+            foreach ($this->_virtual as $key => $val) {
+                $acl = str_replace($val, '', $acl, $count);
+                if ($count) {
+                    $acl .= $key;
+                }
+            }
+        }
+
+        return $acl;
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientDataAclNegativephpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientDataAclNegativephp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/AclNegative.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/AclNegative.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/AclNegative.php                                (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/AclNegative.php   2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,25 @@
</span><ins>+<?php
+/**
+ * Copyright 2011-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2011-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * ACL *negative* rights for a mailbox (see RFC 2086/4314 [2]).
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2011-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Data_AclNegative extends Horde_Imap_Client_Data_Acl
+{
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientDataAclRightsphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientDataAclRightsphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/AclRights.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/AclRights.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/AclRights.php                          (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/AclRights.php     2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,208 @@
</span><ins>+<?php
+/**
+ * Copyright 2011-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2011-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Available ACL rights for a mailbox/identifier (see RFC 2086/4314).
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2011-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Data_AclRights extends Horde_Imap_Client_Data_AclCommon implements ArrayAccess, Iterator, Serializable
+{
+    /**
+     * ACL optional rights.
+     *
+     * @var array
+     */
+    protected $_optional = array();
+
+    /**
+     * ACL required rights.
+     *
+     * @var array
+     */
+    protected $_required = array();
+
+    /**
+     * Constructor.
+     *
+     * @param array $required  The required rights (see RFC 4314 [2.1]).
+     * @param array $optional  The optional rights (see RFC 4314 [2.1]).
+     */
+    public function __construct(array $required = array(),
+                                array $optional = array())
+    {
+        $this->_required = $required;
+
+        foreach ($optional as $val) {
+            foreach (str_split($val) as $right) {
+                $this->_optional[$right] = $val;
+            }
+        }
+
+        $this->_normalize();
+    }
+
+    /**
+     * String representation of the ACL.
+     *
+     * @return string  String representation (RFC 4314 compliant).
+     *
+     */
+    public function __toString()
+    {
+        return implode('', array_keys(array_flip(array_merge(array_values($this->_required), array_keys($this->_optional)))));
+    }
+
+    /**
+     * Normalize virtual rights (see RFC 4314 [2.1.1]).
+     */
+    protected function _normalize()
+    {
+        /* Clients conforming to RFC 4314 MUST ignore the virtual ACL_CREATE
+         * and ACL_DELETE rights. See RFC 4314 [2.1]. However, we still need
+         * to handle these rights when dealing with RFC 2086 servers since
+         * we are abstracting out use of ACL_CREATE/ACL_DELETE to their
+         * component RFC 4314 rights. */
+        foreach ($this->_virtual as $key => $val) {
+            if (isset($this->_optional[$key])) {
+                unset($this->_optional[$key]);
+                foreach ($val as $val2) {
+                    $this->_optional[$val2] = implode('', $val);
+                }
+            } elseif (($pos = array_search($key, $this->_required)) !== false) {
+                unset($this->_required[$pos]);
+                $this->_required = array_unique(array_merge($this->_required, $val));
+            }
+        }
+    }
+
+    /* ArrayAccess methods. */
+
+    /**
+     */
+    public function offsetExists($offset)
+    {
+        return (bool)$this[$offset];
+    }
+
+    /**
+     */
+    public function offsetGet($offset)
+    {
+        if (isset($this->_optional[$offset])) {
+            return $this->_optional[$offset];
+        }
+
+        $pos = array_search($offset, $this->_required);
+
+        return ($pos === false)
+            ? null
+            : $this->_required[$pos];
+    }
+
+    /**
+     */
+    public function offsetSet($offset, $value)
+    {
+        $this->_optional[$offset] = $value;
+        $this->_normalize();
+    }
+
+    /**
+     */
+    public function offsetUnset($offset)
+    {
+        unset($this->_optional[$offset]);
+        $this->_required = array_values(array_diff($this->_required, array($offset)));
+
+        if (isset($this->_virtual[$offset])) {
+            foreach ($this->_virtual[$offset] as $val) {
+                unset($this[$val]);
+            }
+        }
+    }
+
+    /* Iterator methods. */
+
+    /**
+     */
+    public function current()
+    {
+        $val = current($this->_required);
+        return is_null($val)
+            ? current($this->_optional)
+            : $val;
+    }
+
+    /**
+     */
+    public function key()
+    {
+        $key = key($this->_required);
+        return is_null($key)
+            ? key($this->_optional)
+            : $key;
+    }
+
+    /**
+     */
+    public function next()
+    {
+        if (key($this->_required) === null) {
+            next($this->_optional);
+        } else {
+            next($this->_required);
+        }
+    }
+
+    /**
+     */
+    public function rewind()
+    {
+        reset($this->_required);
+        reset($this->_optional);
+    }
+
+    /**
+     */
+    public function valid()
+    {
+        return ((key($this->_required) !== null) ||
+                (key($this->_optional) !== null));
+
+    }
+
+    /* Serializable methods. */
+
+    /**
+     */
+    public function serialize()
+    {
+        return json_encode(array(
+            $this->_required,
+            $this->_optional
+        ));
+    }
+
+    /**
+     */
+    public function unserialize($data)
+    {
+        list($this->_required, $this->_optional) = json_decode($data);
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientDataBaseSubjectphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientDataBaseSubjectphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/BaseSubject.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/BaseSubject.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/BaseSubject.php                                (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/BaseSubject.php   2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,237 @@
</span><ins>+<?php
+/**
+ * Copyright 2008-2013 Horde LLC (http://www.horde.org/)
+ *
+ * getBaseSubject() code adapted from imap-base-subject.c (Dovecot 1.2)
+ *   Original code released under the LGPL-2.1
+ *   Copyright (c) 2002-2008 Timo Sirainen <tss@iki.fi>
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2002-2008 Timo Sirainen
+ * @copyright 2008-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Determines the "base subject" of a string (RFC 5256 [2.1]).
+ *
+ * @author    Timo Sirainen <tss@iki.fi>
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2002-2008 Timo Sirainen
+ * @copyright 2011-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Data_BaseSubject
+{
+    /**
+     * The base subject.
+     *
+     * @var string
+     */
+    protected $_subject;
+
+    /**
+     * Constructor.
+     *
+     * @param string $str  The subject string.
+     * @param array $opts  Additional options:
+     *   - keepblob: (boolean) Don't remove any "blob" information (i.e. text
+     *               leading text between square brackets) from string.
+     *
+     * @return string  The cleaned up subject string.
+     */
+    public function __construct($str, array $opts = array())
+    {
+        // Rule 1a: MIME decode.
+        $str = Horde_Mime::decode($str);
+
+        // Rule 1b: Remove superfluous whitespace.
+        $str = preg_replace("/[\t\r\n ]+/", ' ', $str);
+
+        if (!strlen($str)) {
+            $this->_subject = '';
+        }
+
+        do {
+            /* (2) Remove all trailing text of the subject that matches the
+             * the subj-trailer ABNF, repeat until no more matches are
+             * possible. */
+            $str = preg_replace("/(?:\s*\(fwd\)\s*)+$/i", '', $str);
+
+            do {
+                /* (3) Remove all prefix text of the subject that matches the
+                 * subj-leader ABNF. */
+                $found = $this->_removeSubjLeader($str, !empty($opts['keepblob']));
+
+                /* (4) If there is prefix text of the subject that matches
+                 * the subj-blob ABNF, and removing that prefix leaves a
+                 * non-empty subj-base, then remove the prefix text. */
+                $found = (empty($opts['keepblob']) && $this->_removeBlobWhenNonempty($str)) || $found;
+
+                /* (5) Repeat (3) and (4) until no matches remain. */
+            } while ($found);
+
+            /* (6) If the resulting text begins with the subj-fwd-hdr ABNF and
+             * ends with the subj-fwd-trl ABNF, remove the subj-fwd-hdr and
+             * subj-fwd-trl and repeat from step (2). */
+        } while ($this->_removeSubjFwdHdr($str));
+
+        $this->_subject = $str;
+    }
+
+    /**
+     * Return the "base subject" defined in RFC 5256 [2.1].
+     *
+     * @return string  The base subject.
+     */
+    public function __toString()
+    {
+        return $this->_subject;
+    }
+
+    /**
+     * Remove all prefix text of the subject that matches the subj-leader
+     * ABNF.
+     *
+     * @param string &$str       The subject string.
+     * @param boolean $keepblob  Remove blob information?
+     *
+     * @return boolean  True if string was altered.
+     */
+    protected function _removeSubjLeader(&$str, $keepblob = false)
+    {
+        $ret = false;
+
+        if (!strlen($str)) {
+            return $ret;
+        }
+
+        if ($len = strspn($str, " \t")) {
+            $str = substr($str, $len);
+            $ret = true;
+        }
+
+        $i = 0;
+
+        if (!$keepblob) {
+            while (isset($str[$i]) && ($str[$i] == '[')) {
+                if (($i = $this->_removeBlob($str, $i)) === false) {
+                    return $ret;
+                }
+            }
+        }
+
+        if (stripos($str, 're', $i) === 0) {
+            $i += 2;
+        } elseif (stripos($str, 'fwd', $i) === 0) {
+            $i += 3;
+        } elseif (stripos($str, 'fw', $i) === 0) {
+            $i += 2;
+        } else {
+            return $ret;
+        }
+
+        $i += strspn($str, " \t", $i);
+
+        if (!$keepblob) {
+            while (isset($str[$i]) && ($str[$i] == '[')) {
+                if (($i = $this->_removeBlob($str, $i)) === false) {
+                    return $ret;
+                }
+            }
+        }
+
+        if (!isset($str[$i]) || ($str[$i] != ':')) {
+            return $ret;
+        }
+
+        $str = substr($str, ++$i);
+
+        return true;
+    }
+
+    /**
+     * Remove "[...]" text.
+     *
+     * @param string $str  The subject string.
+     * @param integer $i   Current position.
+     *
+     * @return boolean|integer  False if blob was not found, otherwise the
+     *                          string position of the first non-blob char.
+     */
+    protected function _removeBlob($str, $i)
+    {
+        if ($str[$i] != '[') {
+            return false;
+        }
+
+        ++$i;
+
+        for ($cnt = strlen($str); $i < $cnt; ++$i) {
+            if ($str[$i] == ']') {
+                break;
+            }
+
+            if ($str[$i] == '[') {
+                return false;
+            }
+        }
+
+        if ($i == ($cnt - 1)) {
+            return false;
+        }
+
+        ++$i;
+
+        if ($str[$i] == ' ') {
+            ++$i;
+        }
+
+        return $i;
+    }
+
+    /**
+     * Remove "[...]" text if it doesn't result in the subject becoming
+     * empty.
+     *
+     * @param string &$str  The subject string.
+     *
+     * @return boolean  True if string was altered.
+     */
+    protected function _removeBlobWhenNonempty(&$str)
+    {
+        if ($str &&
+            ($str[0] == '[') &&
+            (($i = $this->_removeBlob($str, 0)) !== false) &&
+            ($i != strlen($str))) {
+            $str = substr($str, $i);
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Remove a "[fwd: ... ]" string.
+     *
+     * @param string &$str  The subject string.
+     *
+     * @return boolean  True if string was altered.
+     */
+    protected function _removeSubjFwdHdr(&$str)
+    {
+        if ((stripos($str, '[fwd:') !== 0) || (substr($str, -1) != ']')) {
+            return false;
+        }
+
+        $str = substr($str, 5, -1);
+        return true;
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientDataEnvelopephpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientDataEnvelopephp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Envelope.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Envelope.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Envelope.php                           (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Envelope.php      2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,212 @@
</span><ins>+<?php
+/**
+ * Copyright 2011-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2011-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Envelope data as returned by the IMAP FETCH command (RFC 3501 [7.4.2]).
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2011-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ *
+ * @property Horde_Mail_Rfc822_List $bcc  Bcc address(es).
+ * @property Horde_Mail_Rfc822_List $cc  Cc address(es).
+ * @property Horde_Imap_Client_DateTime $date  IMAP internal date.
+ * @property Horde_Mail_Rfc822_List $from  From address(es).
+ * @property string $in_reply_to  Message-ID of the message replied to.
+ * @property string $message_id  Message-ID of the message.
+ * @property Horde_Mail_Rfc822_List $reply_to  Reply-to address(es).
+ * @property Horde_Mail_Rfc822_List $sender  Sender address.
+ * @property string $subject  Subject.
+ * @property Horde_Mail_Rfc822_List $to  To address(es).
+ */
+class Horde_Imap_Client_Data_Envelope implements Serializable
+{
+    /** Serializable version. */
+    const VERSION = 2;
+
+    /**
+     * Data object.
+     *
+     * @var Horde_Mime_Headers
+     */
+    protected $_data;
+
+    /**
+     * Constructor.
+     *
+     * @var array $data  An array of property names (keys) and values to set
+     *                   in this object.
+     */
+    public function __construct(array $data = array())
+    {
+        $this->_data = new Horde_Mime_Headers();
+
+        foreach ($data as $key => $val) {
+            $this->$key = $val;
+        }
+    }
+
+    /**
+     */
+    public function __get($name)
+    {
+        switch ($name) {
+        case 'reply_to':
+            $name = 'reply-to';
+            // Fall-through
+
+        case 'bcc':
+        case 'cc':
+        case 'from':
+        case 'sender':
+        case 'to':
+            if (($ob = $this->_data->getOb($name)) !== null) {
+                return $ob;
+            }
+
+            if (in_array($name, array('sender', 'reply-to'))) {
+                return $this->from;
+            }
+            break;
+
+        case 'date':
+            if (($val = $this->_data->getValue($name)) !== null) {
+                return new Horde_Imap_Client_DateTime($val);
+            }
+            break;
+
+        case 'in_reply_to':
+        case 'message_id':
+        case 'subject':
+            if (($val = $this->_data->getValue($name)) !== null) {
+                return $val;
+            }
+            break;
+        }
+
+        // Default values.
+        switch ($name) {
+        case 'bcc':
+        case 'cc':
+        case 'from':
+        case 'to':
+            return new Horde_Mail_Rfc822_List();
+
+        case 'date':
+            return new Horde_Imap_Client_DateTime();
+
+        case 'in_reply_to':
+        case 'message_id':
+        case 'subject':
+            return '';
+        }
+
+        return null;
+    }
+
+    /**
+     */
+    public function __set($name, $value)
+    {
+        if (!strlen($value)) {
+            return;
+        }
+
+        switch ($name) {
+        case 'bcc':
+        case 'cc':
+        case 'date':
+        case 'from':
+        case 'in_reply_to':
+        case 'message_id':
+        case 'reply_to':
+        case 'sender':
+        case 'subject':
+        case 'to':
+            switch ($name) {
+            case 'from':
+                foreach (array('reply_to', 'sender') as $val) {
+                    if ($this->$val->match($value)) {
+                        $this->_data->removeHeader($val);
+                    }
+                }
+                break;
+
+            case 'reply_to':
+            case 'sender':
+                if ($this->from->match($value)) {
+                    $this->_data->removeHeader($name);
+                    return;
+                }
+
+                /* Convert reply-to name. */
+                if ($name == 'reply_to') {
+                    $name = 'reply-to';
+                }
+                break;
+            }
+
+            $this->_data->addHeader($name, $value, array(
+                'sanity_check' => true
+            ));
+            break;
+        }
+    }
+
+    /**
+     */
+    public function __isset($name)
+    {
+        switch ($name) {
+        case 'reply_to':
+            $name = 'reply-to';
+            // Fall-through
+
+        case 'sender':
+            if ($this->_data->getValue($name) !== null) {
+                return true;
+            }
+            $name = 'from';
+            break;
+        }
+
+        return ($this->_data->getValue($name) !== null);
+    }
+
+    /* Serializable methods. */
+
+    /**
+     */
+    public function serialize()
+    {
+        return serialize(array(
+            'd' => $this->_data,
+            'v' => self::VERSION
+        ));
+    }
+
+    /**
+     */
+    public function unserialize($data)
+    {
+        $data = @unserialize($data);
+        if (empty($data['v']) || ($data['v'] != self::VERSION)) {
+            throw new Exception('Cache version change');
+        }
+
+        $this->_data = $data['d'];
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientDataFetchPop3phpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientDataFetchPop3php"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Fetch/Pop3.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Fetch/Pop3.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Fetch/Pop3.php                         (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Fetch/Pop3.php    2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,36 @@
</span><ins>+<?php
+/**
+ * Copyright 2011-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2011-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Object containg POP3 fetch data.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2011-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Data_Fetch_Pop3 extends Horde_Imap_Client_Data_Fetch
+{
+    /**
+     * Set UID.
+     *
+     * @param string $uid  The message UID. Unlike IMAP, this UID does not
+     *                     have to be an integer.
+     */
+    public function setUid($uid)
+    {
+        $this->_data[Horde_Imap_Client::FETCH_UID] = strval($uid);
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientDataFetchphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientDataFetchphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Fetch.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Fetch.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Fetch.php                              (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Fetch.php 2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,567 @@
</span><ins>+<?php
+/**
+ * Copyright 2011-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2011-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Object containing data returned by the Horde_Imap_Client_Base#fetch()
+ * command.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2011-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Data_Fetch
+{
+    /* Constants. */
+    const HEADER_PARSE = 1;
+    const HEADER_STREAM = 2;
+
+    /**
+     * Internal data array.
+     *
+     * @var array
+     */
+    protected $_data = array();
+
+    /**
+     * Set the full message property.
+     *
+     * @param mixed $msg  The full message text, as either a string or stream
+     *                    resource.
+     */
+    public function setFullMsg($msg)
+    {
+        $this->_data[Horde_Imap_Client::FETCH_FULLMSG] = $msg;
+    }
+
+    /**
+     * Returns the full message.
+     *
+     * @param boolean $stream  Return as a stream?
+     *
+     * @return mixed  The full text of the entire message.
+     */
+    public function getFullMsg($stream = false)
+    {
+        return $this->_msgText($stream, isset($this->_data[Horde_Imap_Client::FETCH_FULLMSG]) ? $this->_data[Horde_Imap_Client::FETCH_FULLMSG] : null);
+    }
+
+    /**
+     * Set the message structure.
+     *
+     * @param Horde_Mime_Part $structure  The base MIME part of the message.
+     */
+    public function setStructure(Horde_Mime_Part $structure)
+    {
+        $this->_data[Horde_Imap_Client::FETCH_STRUCTURE] = $structure;
+    }
+
+    /**
+     * Get the message structure.
+     *
+     * @return Horde_Mime_Part $structure  The base MIME part of the message.
+     */
+    public function getStructure()
+    {
+        return isset($this->_data[Horde_Imap_Client::FETCH_STRUCTURE])
+            ? clone $this->_data[Horde_Imap_Client::FETCH_STRUCTURE]
+            : new Horde_Mime_Part();
+    }
+
+    /**
+     * Set a header entry.
+     *
+     * @param string $label  The search label.
+     * @param mixed $data    Either a Horde_Mime_Headers object or the raw
+     *                       header text.
+     */
+    public function setHeaders($label, $data)
+    {
+        $this->_data[Horde_Imap_Client::FETCH_HEADERS][$label] = $data;
+    }
+
+    /**
+     * Get a header entry.
+     *
+     * @param string $label    The search label.
+     * @param integer $format  The return format. If self::HEADER_PARSE,
+     *                         returns a Horde_Mime_Headers object. If
+     *                         self::HEADER_STREAM, returns a stream.
+     *                         Otherwise, returns header text.
+     *
+     * @return mixed  See $format.
+     */
+    public function getHeaders($label, $format = 0)
+    {
+        return $this->_getHeaders($label, $format, Horde_Imap_Client::FETCH_HEADERS);
+    }
+
+    /**
+     * Set a header text entry.
+     *
+     * @param string $id    The MIME ID.
+     * @param string $text  The header text.
+     */
+    public function setHeaderText($id, $text)
+    {
+        $this->_data[Horde_Imap_Client::FETCH_HEADERTEXT][$id] = $text;
+    }
+
+    /**
+     * Get a header text entry.
+     *
+     * @param string $id       The MIME ID.
+     * @param integer $format  The return format. If self::HEADER_PARSE,
+     *                         returns a Horde_Mime_Headers object. If
+     *                         self::HEADER_STREAM, returns a stream.
+     *                         Otherwise, returns header text.
+     *
+     * @return mixed  See $format.
+     */
+    public function getHeaderText($id = 0, $format = 0)
+    {
+        return $this->_getHeaders($id, $format, Horde_Imap_Client::FETCH_HEADERTEXT);
+    }
+
+    /**
+     * Set a MIME header entry.
+     *
+     * @param string $id    The MIME ID.
+     * @param string $text  The header text.
+     */
+    public function setMimeHeader($id, $text)
+    {
+        $this->_data[Horde_Imap_Client::FETCH_MIMEHEADER][$id] = $text;
+    }
+
+    /**
+     * Get a MIME header entry.
+     *
+     * @param string $id       The MIME ID.
+     * @param integer $format  The return format. If self::HEADER_PARSE,
+     *                         returns a Horde_Mime_Headers object. If
+     *                         self::HEADER_STREAM, returns a stream.
+     *                         Otherwise, returns header text.
+     *
+     * @return mixed  See $format.
+     */
+    public function getMimeHeader($id, $format = 0)
+    {
+        return $this->_getHeaders($id, $format, Horde_Imap_Client::FETCH_MIMEHEADER);
+    }
+
+    /**
+     * Set a body part entry.
+     *
+     * @param string $id      The MIME ID.
+     * @param mixed $text     The body part text, as either a string or stream
+     *                        resource.
+     * @param string $decode  Either '8bit', 'binary', or null.
+     */
+    public function setBodyPart($id, $text, $decode = null)
+    {
+        $this->_data[Horde_Imap_Client::FETCH_BODYPART][$id] = array(
+            'd' => $decode,
+            't' => $text
+        );
+    }
+
+    /**
+     * Set the body part size for a body part.
+     *
+     * @param string $id     The MIME ID.
+     * @param integer $size  The size (in bytes).
+     */
+    public function setBodyPartSize($id, $size)
+    {
+        $this->_data[Horde_Imap_Client::FETCH_BODYPARTSIZE][$id] = intval($size);
+    }
+
+    /**
+     * Get a body part entry.
+     *
+     * @param string $id       The MIME ID.
+     * @param boolean $stream  Return as a stream?
+     *
+     * @return mixed  The full text of the body part.
+     */
+    public function getBodyPart($id, $stream = false)
+    {
+        return $this->_msgText($stream, isset($this->_data[Horde_Imap_Client::FETCH_BODYPART][$id]) ? $this->_data[Horde_Imap_Client::FETCH_BODYPART][$id]['t'] : null);
+    }
+
+    /**
+     * Determines if/how a body part was MIME decoded on the server.
+     *
+     * @param string $id  The MIME ID.
+     *
+     * @return string  Either '8bit', 'binary', or null.
+     */
+    public function getBodyPartDecode($id)
+    {
+        return isset($this->_data[Horde_Imap_Client::FETCH_BODYPART][$id])
+            ? $this->_data[Horde_Imap_Client::FETCH_BODYPART][$id]['d']
+            : null;
+    }
+
+    /**
+     * Returns the body part size, if returned by the server.
+     *
+     * @param string $id  The MIME ID.
+     *
+     * @return integer  The body part size, in bytes.
+     */
+    public function getBodyPartSize($id)
+    {
+        return isset($this->_data[Horde_Imap_Client::FETCH_BODYPARTSIZE][$id])
+            ? $this->_data[Horde_Imap_Client::FETCH_BODYPARTSIZE][$id]
+            : null;
+    }
+
+    /**
+     * Set a body text entry.
+     *
+     * @param string $id   The MIME ID.
+     * @param mixed $text  The body part text, as either a string or stream
+     *                     resource.
+     */
+    public function setBodyText($id, $text)
+    {
+        $this->_data[Horde_Imap_Client::FETCH_BODYTEXT][$id] = $text;
+    }
+
+    /**
+     * Get a body text entry.
+     *
+     * @param string $id       The MIME ID.
+     * @param boolean $stream  Return as a stream?
+     *
+     * @return mixed  The full text of the body text.
+     */
+    public function getBodyText($id = 0, $stream = false)
+    {
+        return $this->_msgText($stream, isset($this->_data[Horde_Imap_Client::FETCH_BODYTEXT][$id]) ? $this->_data[Horde_Imap_Client::FETCH_BODYTEXT][$id] : null);
+    }
+
+    /**
+     * Set envelope data.
+     *
+     * @param array $data  The envelope data to pass to the Envelope object
+     *                     constructor, or an Envelope object.
+     */
+    public function setEnvelope($data)
+    {
+        $this->_data[Horde_Imap_Client::FETCH_ENVELOPE] = is_array($data)
+            ? new Horde_Imap_Client_Data_Envelope($data)
+            : $data;
+    }
+
+    /**
+     * Get envelope data.
+     *
+     * @return Horde_Imap_Client_Data_Envelope  An envelope object.
+     */
+    public function getEnvelope()
+    {
+        return isset($this->_data[Horde_Imap_Client::FETCH_ENVELOPE])
+            ? clone $this->_data[Horde_Imap_Client::FETCH_ENVELOPE]
+            : new Horde_Imap_Client_Data_Envelope();
+    }
+
+    /**
+     * Set IMAP flags.
+     *
+     * @param array $flags  An array of IMAP flags.
+     */
+    public function setFlags(array $flags)
+    {
+        $this->_data[Horde_Imap_Client::FETCH_FLAGS] = array_map('strtolower', $flags);
+    }
+
+    /**
+     * Get IMAP flags.
+     *
+     * @return array  An array of IMAP flags (all flags in lowercase).
+     */
+    public function getFlags()
+    {
+        return isset($this->_data[Horde_Imap_Client::FETCH_FLAGS])
+            ? $this->_data[Horde_Imap_Client::FETCH_FLAGS]
+            : array();
+    }
+
+    /**
+     * Set IMAP internal date.
+     *
+     * @param mixed $date  Either a Horde_Imap_Client_DateTime object or a
+     *                     date string.
+     */
+    public function setImapDate($date)
+    {
+        $this->_data[Horde_Imap_Client::FETCH_IMAPDATE] = is_object($date)
+            ? $date
+            : new Horde_Imap_Client_DateTime($date);
+    }
+
+    /**
+     * Get internal IMAP date.
+     *
+     * @return Horde_Imap_Client_DateTime  A date object.
+     */
+    public function getImapDate()
+    {
+        return isset($this->_data[Horde_Imap_Client::FETCH_IMAPDATE])
+            ? clone $this->_data[Horde_Imap_Client::FETCH_IMAPDATE]
+            : new Horde_Imap_Client_DateTime();
+    }
+
+    /**
+     * Set message size.
+     *
+     * @param integer $size  The size of the message, in bytes.
+     */
+    public function setSize($size)
+    {
+        $this->_data[Horde_Imap_Client::FETCH_SIZE] = intval($size);
+    }
+
+    /**
+     * Get message size.
+     *
+     * @return integer  The size of the message, in bytes.
+     */
+    public function getSize()
+    {
+        return isset($this->_data[Horde_Imap_Client::FETCH_SIZE])
+            ? $this->_data[Horde_Imap_Client::FETCH_SIZE]
+            : 0;
+    }
+
+    /**
+     * Set UID.
+     *
+     * @param integer $uid  The message UID.
+     */
+    public function setUid($uid)
+    {
+        $this->_data[Horde_Imap_Client::FETCH_UID] = intval($uid);
+    }
+
+    /**
+     * Get UID.
+     *
+     * @return integer  The message UID.
+     */
+    public function getUid()
+    {
+        return isset($this->_data[Horde_Imap_Client::FETCH_UID])
+            ? $this->_data[Horde_Imap_Client::FETCH_UID]
+            : null;
+    }
+
+    /**
+     * Set message sequence number.
+     *
+     * @param integer $seq  The message sequence number.
+     */
+    public function setSeq($seq)
+    {
+        $this->_data[Horde_Imap_Client::FETCH_SEQ] = intval($seq);
+    }
+
+    /**
+     * Get message sequence number.
+     *
+     * @return integer  The message sequence number.
+     */
+    public function getSeq()
+    {
+        return isset($this->_data[Horde_Imap_Client::FETCH_SEQ])
+            ? $this->_data[Horde_Imap_Client::FETCH_SEQ]
+            : null;
+    }
+
+    /**
+     * Set the modified sequence value for the message.
+     *
+     * @param integer $modseq  The modseq value.
+     */
+    public function setModSeq($modseq)
+    {
+        $this->_data[Horde_Imap_Client::FETCH_MODSEQ] = intval($modseq);
+    }
+
+    /**
+     * Get the modified sequence value for the message.
+     *
+     * @return integer  The modseq value.
+     */
+    public function getModSeq()
+    {
+        return isset($this->_data[Horde_Imap_Client::FETCH_MODSEQ])
+            ? $this->_data[Horde_Imap_Client::FETCH_MODSEQ]
+            : null;
+    }
+
+    /**
+     * Set the internationalized downgraded status for the message.
+     *
+     * @since 2.11.0
+     *
+     * @param boolean $downgraded  True if at least one message component has
+     *                             been downgraded.
+     */
+    public function setDowngraded($downgraded)
+    {
+        if ($downgraded) {
+            $this->_data[Horde_Imap_Client::FETCH_DOWNGRADED] = true;
+        } else {
+            unset($this->_data[Horde_Imap_Client::FETCH_DOWNGRADED]);
+        }
+    }
+
+    /**
+     * Does the message contain internationalized downgraded data (i.e. it
+     * is a "surrogate" message)?
+     *
+     * @since 2.11.0
+     *
+     * @return boolean  True if at least one message components has been
+     *                  downgraded.
+     */
+    public function isDowngraded()
+    {
+        return !empty($this->_data[Horde_Imap_Client::FETCH_DOWNGRADED]);
+    }
+
+    /**
+     * Return the internal representation of the data.
+     *
+     * @return array  The data array.
+     */
+    public function getRawData()
+    {
+        return $this->_data;
+    }
+
+    /**
+     * Merge a fetch object into this one.
+     *
+     * @param Horde_Imap_Client_Data_Fetch $data  A fetch object.
+     */
+    public function merge(Horde_Imap_Client_Data_Fetch $data)
+    {
+        $this->_data = array_replace_recursive($this->_data, $data->getRawData());
+    }
+
+    /**
+     * Does this object containing cacheable data of the given type?
+     *
+     * @param integer $type  The type to query.
+     *
+     * @return boolean  True if the type is cacheable.
+     */
+    public function exists($type)
+    {
+        return isset($this->_data[$type]);
+    }
+
+    /**
+     * Does this object contain only default values for all fields?
+     *
+     * @return boolean  True if object contains default data.
+     */
+    public function isDefault()
+    {
+        return empty($this->_data);
+    }
+
+    /**
+     * Return text representation of a field.
+     *
+     * @param boolean $stream  Return as a stream?
+     * @param mixed $data      The field data (string or resource) or null if
+     *                         field does not exist.
+     *
+     * @return mixed  Requested text representation.
+     */
+    protected function _msgText($stream, $data)
+    {
+        if ($stream) {
+            if (is_resource($data)) {
+                rewind($data);
+                return $data;
+            }
+
+            $tmp = fopen('php://temp', 'w+');
+
+            if (!is_null($data)) {
+                fwrite($tmp, $data);
+                rewind($tmp);
+            }
+
+            return $tmp;
+        }
+
+        if (is_resource($data)) {
+            rewind($data);
+            return stream_get_contents($data);
+        }
+
+        return strval($data);
+    }
+
+    /**
+     * Return representation of a header field.
+     *
+     * @param string $id       The header id.
+     * @param integer $format  The return format. If self::HEADER_PARSE,
+     *                         returns a Horde_Mime_Headers object. If
+     *                         self::HEADER_STREAM, returns a stream.
+     *                         Otherwise, returns header text.
+     * @param integer $key     The array key where the data is stored in the
+     *                         internal array.
+     *
+     * @return mixed  The data in the format specified by $format.
+     */
+    protected function _getHeaders($id, $format, $key)
+    {
+        switch ($format) {
+        case self::HEADER_STREAM:
+            if (!isset($this->_data[$key][$id])) {
+                return $this->_msgText(true, null);
+            } elseif (is_object($this->_data[$key][$id])) {
+                return $this->_getHeaders($id, 0, $key);
+            }
+            return $this->_msgText(true, $this->_data[$key][$id]);
+
+        case self::HEADER_PARSE:
+            if (!isset($this->_data[$key][$id])) {
+                return new Horde_Mime_Headers();
+            } elseif (is_object($this->_data[$key][$id])) {
+                return clone $this->_data[$key][$id];
+            }
+            return Horde_Mime_Headers::parseHeaders($this->_getHeaders($id, 0, $key));
+        }
+
+        if (!isset($this->_data[$key][$id])) {
+            return '';
+        }
+
+        return is_object($this->_data[$key][$id])
+            ? $this->_data[$key][$id]->toString(array('nowrap' => true))
+            : $this->_msgText(false, $this->_data[$key][$id]);
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientDataFormatAstringphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientDataFormatAstringphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Astring.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Astring.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Astring.php                             (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Astring.php        2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,32 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Object representation of an IMAP astring (atom or string) (RFC 3501 [4.3]).
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Data_Format_Astring extends Horde_Imap_Client_Data_Format_String
+{
+    /**
+     */
+    public function quoted()
+    {
+        return $this->_filter->quoted || !$this->_data->length();
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientDataFormatAtomphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientDataFormatAtomphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Atom.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Atom.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Atom.php                                (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Atom.php   2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,53 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Object representation of an IMAP atom (RFC 3501 [4.1]).
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Data_Format_Atom extends Horde_Imap_Client_Data_Format
+{
+    /**
+     */
+    public function escape()
+    {
+        return strlen($this->_data)
+            ? parent::escape()
+            : '""';
+    }
+
+    /**
+     */
+    public function verify()
+    {
+        if (strlen($this->_data) != strlen($this->stripNonAtomCharacters())) {
+            throw new Horde_Imap_Client_Data_Format_Exception('Illegal character in IMAP atom.');
+        }
+    }
+
+    /**
+     * Strip out any characters that are not allowed in an IMAP atom.
+     *
+     * @return string  The atom data disallowed characters removed.
+     */
+    public function stripNonAtomCharacters()
+    {
+        return str_replace(array('(', ')', '{', ' ', '%', '*', '"', '\\', ']'), '', preg_replace('/[\x00-\x1f\x7f]/', '', $this->_data));
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientDataFormatDatephpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientDataFormatDatephp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Date.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Date.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Date.php                                (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Date.php   2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,49 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Object representation of an IMAP date string (RFC 3501 [9]).
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Data_Format_Date extends Horde_Imap_Client_Data_Format
+{
+    /**
+     * Constructor.
+     *
+     * @param mixed $data  Either a DateTime object, or a date format that
+     *                     can be converted to a DateTime object.
+     *
+     * @throws Exception
+     */
+    public function __construct($data)
+    {
+        if (!($data instanceof DateTime)) {
+            $data = new Horde_Imap_Client_DateTime($data);
+        }
+
+        parent::__construct($data);
+    }
+
+    /**
+     */
+    public function __toString()
+    {
+        return $this->_data->format('j-M-Y');
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientDataFormatDateTimephpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientDataFormatDateTimephp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/DateTime.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/DateTime.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/DateTime.php                            (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/DateTime.php       2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,39 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Object representation of an IMAP date-time string (RFC 3501 [9]).
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Data_Format_DateTime extends Horde_Imap_Client_Data_Format_Date
+{
+    /**
+     */
+    public function __toString()
+    {
+        return $this->_data->format('j-M-Y H:i:s O');
+    }
+
+    /**
+     */
+    public function escape()
+    {
+        return '"' . strval($this) . '"';
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientDataFormatExceptionphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientDataFormatExceptionphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Exception.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Exception.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Exception.php                           (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Exception.php      2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,25 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Exception object for IMAP data format errors.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Data_Format_Exception extends Horde_Exception_Wrapped
+{
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientDataFormatFilterQuotephpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientDataFormatFilterQuotephp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Filter/Quote.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Filter/Quote.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Filter/Quote.php                                (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Filter/Quote.php   2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,43 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Stream filter to output a quoted IMAP string.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Data_Format_Filter_Quote extends php_user_filter
+{
+    /**
+     * @see stream_filter_register()
+     */
+    public function filter($in, $out, &$consumed, $closing)
+    {
+        stream_bucket_append($out, stream_bucket_new($this->stream, '"'));
+
+        while ($bucket = stream_bucket_make_writeable($in)) {
+            $consumed += $bucket->datalen;
+            $bucket->data = addcslashes($bucket->data, '"\\');
+            stream_bucket_append($out, $bucket);
+        }
+
+        stream_bucket_append($out, stream_bucket_new($this->stream, '"'));
+
+        return PSFS_PASS_ON;
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientDataFormatFilterStringphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientDataFormatFilterStringphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Filter/String.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Filter/String.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Filter/String.php                               (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Filter/String.php  2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,112 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Stream filter to analyze an IMAP string to determine how to send to the
+ * server.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Data_Format_Filter_String extends php_user_filter
+{
+    /**
+     * @see stream_filter_register()
+     */
+    public function onCreate()
+    {
+        $this->params->binary = false;
+        $this->params->literal = false;
+        // no_quote_list is used below as a config option
+        $this->params->quoted = false;
+
+        return true;
+    }
+
+    /**
+     * @see stream_filter_register()
+     */
+    public function filter($in, $out, &$consumed, $closing)
+    {
+        $p = $this->params;
+        $skip = false;
+
+        while ($bucket = stream_bucket_make_writeable($in)) {
+            if (!$skip) {
+                $len = $bucket->datalen;
+                $str = $bucket->data;
+
+                for ($i = 0; $i < $len; ++$i) {
+                    $chr = ord($str[$i]);
+
+                    switch ($chr) {
+                    case 0: // null
+                        $p->binary = true;
+                        $p->literal = true;
+
+                        // No need to scan input anymore.
+                        $skip = true;
+                        break 2;
+
+                    case 10: // LF
+                    case 13: // CR
+                        $p->literal = true;
+                        break;
+
+                    case 32: // SPACE
+                    case 34: // "
+                    case 40: // (
+                    case 41: // )
+                    case 92: // \
+                    case 123: // {
+                    case 127: // DEL
+                        // These are all invalid ATOM characters.
+                        $p->quoted = true;
+                        break;
+
+                    case 37: // %
+                    case 42: // *
+                        // These are not quoted if being used as wildcards.
+                        if (empty($p->no_quote_list)) {
+                            $p->quoted = true;
+                        }
+                        break;
+
+                    default:
+                        if ($chr < 32) {
+                            // CTL characters must be, at a minimum, quoted.
+                            $p->quoted = true;
+                        } elseif ($chr > 127) {
+                            // 8-bit chars must be in a literal.
+                            $p->literal = true;
+                        }
+                        break;
+                    }
+                }
+            }
+
+            $consumed += $bucket->datalen;
+            stream_bucket_append($out, $bucket);
+        }
+
+        if ($p->literal) {
+            $p->quoted = false;
+        }
+
+        return PSFS_PASS_ON;
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientDataFormatListphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientDataFormatListphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/List.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/List.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/List.php                                (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/List.php   2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,105 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Object representation of an IMAP parenthesized list (RFC 3501 [4.4]).
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Data_Format_List extends Horde_Imap_Client_Data_Format implements Countable, IteratorAggregate
+{
+    /**
+     * @see add()
+     */
+    public function __construct($data = null)
+    {
+        parent::__construct(array());
+
+        if (!is_null($data)) {
+            $this->add($data);
+        }
+    }
+
+    /**
+     * Add an element to the list.
+     *
+     * @param mixed $data     The data element(s) to add. Either a
+     *                        Horde_Imap_Client_Data_Format object, a string
+     *                        value that will be treated as an IMAP atom, or
+     *                        an array (or iterable object) of objects to add.
+     * @param boolean $merge  Merge the contents of any container objects,
+     *                        instead of adding the objects themselves?
+     *
+     * @return Horde_Imap_Client_Data_Format_List  This object to allow for
+     *                                             chainable calls (since
+     *                                             2.10.0).
+     */
+    public function add($data, $merge = false)
+    {
+        if (is_array($data) || ($merge && ($data instanceof Traversable))) {
+            foreach ($data as $val) {
+                $this->add($val);
+            }
+        } elseif (is_object($data)) {
+            $this->_data[] = $data;
+        } elseif (!is_null($data)) {
+            $this->_data[] = new Horde_Imap_Client_Data_Format_Atom($data);
+        }
+
+        return $this;
+    }
+
+    /**
+     */
+    public function __toString()
+    {
+        $out = '';
+
+        foreach ($this as $val) {
+            if ($val instanceof $this) {
+                $out .= '(' . $val->escape() . ') ';
+            } elseif (($val instanceof Horde_Imap_Client_Data_Format_String) &&
+                      $val->literal()) {
+                throw new Horde_Imap_Client_Data_Format_Exception('Requires literal output.');
+            } else {
+                $out .= $val->escape() . ' ';
+            }
+        }
+
+        return rtrim($out);
+    }
+
+    /* Countable methods. */
+
+    /**
+     */
+    public function count()
+    {
+        return count($this->_data);
+    }
+
+    /* IteratorAggregate method. */
+
+    /**
+     * Iterator loops through the data elements contained in this list.
+     */
+    public function getIterator()
+    {
+        return new ArrayIterator($this->_data);
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientDataFormatListMailboxphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientDataFormatListMailboxphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/ListMailbox.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/ListMailbox.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/ListMailbox.php                         (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/ListMailbox.php    2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,37 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Object representation of an IMAP mailbox string used in a LIST command.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Data_Format_ListMailbox extends Horde_Imap_Client_Data_Format_Mailbox
+{
+    /**
+     */
+    protected function _filterParams()
+    {
+        $ob = parent::_filterParams();
+
+        /* Don't quote % or * characters. */
+        $ob->no_quote_list = true;
+
+        return $ob;
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientDataFormatMailboxphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientDataFormatMailboxphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Mailbox.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Mailbox.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Mailbox.php                             (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Mailbox.php        2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,91 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Object representation of an IMAP mailbox string (RFC 3501 [9]).
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Data_Format_Mailbox extends Horde_Imap_Client_Data_Format_Astring
+{
+    /**
+     * Mailbox object.
+     *
+     * @var Horde_Imap_Client_Mailbox
+     */
+    protected $_mailbox;
+
+    /**
+     * @param mixed $data  Either a mailbox object or a UTF-8 mailbox name.
+     */
+    public function __construct($data)
+    {
+        $this->_mailbox = Horde_Imap_Client_Mailbox::get($data);
+
+        parent::__construct($this->_mailbox->utf7imap);
+    }
+
+    /**
+     */
+    public function __toString()
+    {
+        return strval($this->_mailbox);
+    }
+
+    /**
+     */
+    public function getData()
+    {
+        return $this->_mailbox;
+    }
+
+    /**
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function binary()
+    {
+        if (parent::binary()) {
+            // Mailbox data can NEVER be sent as binary.
+            /* @todo: Disable until Horde_Imap_Client 3.0 */
+            // throw new Horde_Imap_Client_Exception(
+            //     'Client error: can not send mailbox to IMAP server as binary data.'
+            // );
+
+            // Temporary fix: send a blank mailbox string.
+            $this->_mailbox = Horde_Imap_Client_Mailbox::get('');
+        }
+
+        return false;
+    }
+
+    /**
+     */
+    public function length()
+    {
+        return strlen($this->_mailbox->utf7imap);
+    }
+
+    /**
+     */
+    public function getStream()
+    {
+        $stream = new Horde_Stream_Temp();
+        $stream->add($this->_mailbox->utf7imap);
+        return $stream;
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientDataFormatNilphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientDataFormatNilphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Nil.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Nil.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Nil.php                         (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Nil.php    2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,46 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Object representation of an IMAP NIL (RFC 3501 [4.5]).
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Data_Format_Nil extends Horde_Imap_Client_Data_Format
+{
+    /**
+     */
+    public function __construct($data = null)
+    {
+        // Don't store any data in object.
+    }
+
+    /**
+     */
+    public function __toString()
+    {
+        return '';
+    }
+
+    /**
+     */
+    public function escape()
+    {
+        return 'NIL';
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientDataFormatNstringphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientDataFormatNstringphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Nstring.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Nstring.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Nstring.php                             (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Nstring.php        2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,82 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Object representation of an IMAP nstring (NIL or string) (RFC 3501 [4.5]).
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Data_Format_Nstring extends Horde_Imap_Client_Data_Format_String
+{
+    /**
+     */
+    public function __construct($data = null)
+    {
+        /* Data can be null (NIL) here. */
+        if (is_null($data)) {
+            $this->_data = null;
+        } else {
+            parent::__construct($data);
+        }
+    }
+
+    /**
+     */
+    public function __toString()
+    {
+        return is_null($this->_data)
+            ? ''
+            : parent::__toString();
+    }
+
+    /**
+     */
+    public function escape()
+    {
+        return is_null($this->_data)
+            ? 'NIL'
+            : parent::escape();
+    }
+
+    /**
+     */
+    public function quoted()
+    {
+        return is_null($this->_data)
+            ? false
+            : parent::quoted();
+    }
+
+    /**
+     */
+    public function length()
+    {
+        return is_null($this->_data)
+            ? 0
+            : parent::length();
+    }
+
+    /**
+     */
+    public function getStream()
+    {
+        return is_null($this->_data)
+            ? new Horde_Stream_Temp()
+            : parent::length();
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientDataFormatNumberphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientDataFormatNumberphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Number.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/Number.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Number.php                              (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/Number.php 2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,41 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Object representation of an IMAP number (RFC 3501 [4.2]).
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Data_Format_Number extends Horde_Imap_Client_Data_Format
+{
+    /**
+     */
+    public function __toString()
+    {
+        return strval(intval($this->_data));
+    }
+
+    /**
+     */
+    public function verify()
+    {
+        if (!is_numeric($this->_data)) {
+            throw new Horde_Imap_Client_Data_Format_Exception('Illegal character in IMAP number.');
+        }
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientDataFormatStringphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientDataFormatStringphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/String.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format/String.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/String.php                              (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format/String.php 2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,209 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Object representation of an IMAP string (RFC 3501 [4.3]).
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Data_Format_String extends Horde_Imap_Client_Data_Format
+{
+    /**
+     * String filter parameters.
+     *
+     * @var string
+     */
+    protected $_filter;
+
+    /**
+     * @param array $opts  Additional options:
+     *   - eol: (boolean) If true, normalize EOLs in input. @since 2.2.0
+     *   - skipscan: (boolean) If true, don't scan input for
+     *               binary/literal/quoted data. @since 2.2.0
+     */
+    public function __construct($data, array $opts = array())
+    {
+        /* String data is stored in a stream. */
+        $this->_data = new Horde_Stream_Temp();
+
+        $this->_filter = $this->_filterParams();
+
+        if (empty($opts['skipscan'])) {
+            stream_filter_register('horde_imap_client_string', 'Horde_Imap_Client_Data_Format_Filter_String');
+            $res = stream_filter_append($this->_data->stream, 'horde_imap_client_string', STREAM_FILTER_WRITE, $this->_filter);
+        } else {
+            $res = null;
+        }
+
+        if (empty($opts['eol'])) {
+            $res2 = null;
+        } else {
+            stream_filter_register('horde_eol', 'Horde_Stream_Filter_Eol');
+            $res2 = stream_filter_append($this->_data->stream, 'horde_eol', STREAM_FILTER_WRITE);
+        }
+
+        $this->_data->add($data);
+
+        if (!is_null($res)) {
+            stream_filter_remove($res);
+        }
+        if (!is_null($res2)) {
+            stream_filter_remove($res2);
+        }
+    }
+
+    /**
+     * Return the base string filter parameters.
+     *
+     * @return object  Filter parameters.
+     */
+    protected function _filterParams()
+    {
+        return new stdClass;
+    }
+
+    /**
+     */
+    public function __toString()
+    {
+        return $this->_data->getString(0);
+    }
+
+    /**
+     */
+    public function escape()
+    {
+        if ($this->literal()) {
+            throw new Horde_Imap_Client_Data_Format_Exception('String requires literal to output.');
+        }
+
+        return $this->quoted()
+            ? stream_get_contents($this->escapeStream())
+            : $this->_data->getString(0);
+    }
+
+    /**
+     * Return the escaped string as a stream.
+     *
+     * @return resource  The IMAP escaped stream.
+     */
+    public function escapeStream()
+    {
+        if ($this->literal()) {
+            throw new Horde_Imap_Client_Data_Format_Exception('String requires literal to output.');
+        }
+
+        rewind($this->_data->stream);
+
+        $stream = new Horde_Stream_Temp();
+        $stream->add($this->_data, true);
+
+        stream_filter_register('horde_imap_client_string_quote', 'Horde_Imap_Client_Data_Format_Filter_Quote');
+        stream_filter_append($stream->stream, 'horde_imap_client_string_quote', STREAM_FILTER_READ);
+
+        return $stream->stream;
+    }
+
+    /**
+     * Does this data item require quoted string output?
+     *
+     * @return boolean  True if quoted output is required.
+     */
+    public function quoted()
+    {
+        /* IMAP strings MUST be quoted if they are not a literal. */
+        return (!isset($this->_filter) || !$this->_filter->literal);
+    }
+
+    /**
+     * Force item to be output quoted.
+     */
+    public function forceQuoted()
+    {
+        $this->_filter = $this->_filterParams();
+        $this->_filter->binary = false;
+        $this->_filter->literal = false;
+        $this->_filter->quoted = true;
+    }
+
+    /**
+     * Does this data item require literal string output?
+     *
+     * @return boolean  True if literal output is required.
+     */
+    public function literal()
+    {
+        return (isset($this->_filter) && $this->_filter->literal);
+    }
+
+    /**
+     * Force item to be output as a literal.
+     */
+    public function forceLiteral()
+    {
+        $this->_filter = $this->_filterParams();
+        // Keep binary status, if set
+        $this->_filter->literal = true;
+        $this->_filter->quoted = false;
+    }
+
+    /**
+     * If literal output, is the data binary?
+     *
+     * @return boolean  True if the literal output is binary.
+     */
+    public function binary()
+    {
+        return (isset($this->_filter) && !empty($this->_filter->binary));
+    }
+
+    /**
+     * Force item to be output as a binary literal.
+     */
+    public function forceBinary()
+    {
+        $this->_filter = $this->_filterParams();
+        $this->_filter->binary = true;
+        $this->_filter->literal = true;
+        $this->_filter->quoted = false;
+    }
+
+    /**
+     * Return the length of the data.
+     *
+     * @since 2.2.0
+     *
+     * @return integer  Data length.
+     */
+    public function length()
+    {
+        return $this->_data->length();
+    }
+
+    /**
+     * Return the contents of the string as a stream object.
+     *
+     * @since 2.3.0
+     *
+     * @return Horde_Stream  The stream object.
+     */
+    public function getStream()
+    {
+        return $this->_data;
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientDataFormatphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientDataFormatphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Format.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format.php                             (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Format.php        2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,83 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Object representation of an IMAP data format (RFC 3501 [4]).
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Data_Format
+{
+    /**
+     * Data.
+     *
+     * @var mixed
+     */
+    protected $_data;
+
+    /**
+     * Constructor.
+     *
+     * @param mixed $data  Data.
+     */
+    public function __construct($data)
+    {
+        $this->_data = is_resource($data)
+            ? stream_get_contents($data, -1, 0)
+            : $data;
+    }
+
+    /**
+     * Returns the string value of the raw data.
+     *
+     * @return string  String value.
+     */
+    public function __toString()
+    {
+        return strval($this->_data);
+    }
+
+    /**
+     * Returns the raw data.
+     *
+     * @return mixed  Raw data.
+     */
+    public function getData()
+    {
+        return $this->_data;
+    }
+
+    /**
+     * Returns the data formatted for output to the IMAP server.
+     *
+     * @return string  IMAP escaped string.
+     */
+    public function escape()
+    {
+        return strval($this);
+    }
+
+    /**
+     * Verify the data.
+     *
+     * @throws Horde_Imap_Client_Data_Format_Exception
+     */
+    public function verify()
+    {
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientDataSyncphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientDataSyncphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Sync.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Sync.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Sync.php                               (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Sync.php  2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,268 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Mailbox synchronization results.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ * @since     2.2.0
+ *
+ * @property-read Horde_Imap_Client_Ids $flagsuids  List of messages with flag
+ *                                                  changes.
+ * @property-read Horde_Imap_Client_Ids $newmsgsuids  List of new messages.
+ * @property-read Horde_Imap_Client_Ids $vanisheduids  List of messages that
+ *                                                     have vanished.
+ */
+class Horde_Imap_Client_Data_Sync
+{
+    /**
+     * Mappings of status() values to sync keys.
+     *
+     * @since 2.8.0
+     *
+     * @var array
+     */
+    static public $map = array(
+        'H' => 'highestmodseq',
+        'M' => 'messages',
+        'U' => 'uidnext',
+        'V' => 'uidvalidity'
+    );
+
+    /**
+     * Are there messages that have had flag changes?
+     *
+     * @var Horde_Imap_Client_Ids
+     */
+    public $flags = null;
+
+    /**
+     * The previous value of HIGHESTMODSEQ.
+     *
+     * @since 2.8.0
+     *
+     * @var integer
+     */
+    public $highestmodseq = null;
+
+    /**
+     * The synchronized mailbox.
+     *
+     * @var Horde_Imap_Client_Mailbox
+     */
+    public $mailbox;
+
+    /**
+     * The previous number of messages in the mailbox.
+     *
+     * @since 2.8.0
+     *
+     * @var integer
+     */
+    public $messages = null;
+
+    /**
+     * Are there new messages?
+     *
+     * @var boolean
+     */
+    public $newmsgs = null;
+
+    /**
+     * The previous value of UIDNEXT.
+     *
+     * @since 2.8.0
+     *
+     * @var integer
+     */
+    public $uidnext = null;
+
+    /**
+     * The previous value of UIDVALIDITY.
+     *
+     * @since 2.8.0
+     *
+     * @var integer
+     */
+    public $uidvalidity = null;
+
+    /**
+     * The UIDs of messages that are guaranteed to have vanished. This list is
+     * only guaranteed to be available if the server supports QRESYNC or a
+     * list of known UIDs is passed to the sync() method.
+     *
+     * @var Horde_Imap_Client_Ids
+     */
+    public $vanished = null;
+
+    /**
+     * UIDs of messages that have had flag changes.
+     *
+     * @var Horde_Imap_Client_Ids
+     */
+    protected $_flagsuids;
+
+    /**
+     * UIDs of new messages.
+     *
+     * @var Horde_Imap_Client_Ids
+     */
+    protected $_newmsgsuids;
+
+    /**
+     * UIDs of messages that have vanished.
+     *
+     * @var Horde_Imap_Client_Ids
+     */
+    protected $_vanisheduids;
+
+    /**
+     * Constructor.
+     *
+     * @param Horde_Imap_Client_Base $base_ob  Base driver object.
+     * @param mixed $mailbox                   Mailbox to sync.
+     * @param array $sync                      Token sync data.
+     * @param array $curr                      Current sync data.
+     * @param integer $criteria                Mask of criteria to return.
+     * @param Horde_Imap_Client_Ids $ids       List of known UIDs.
+     *
+     * @throws Horde_Imap_Client_Exception
+     * @throws Horde_Imap_Client_Exception_Sync
+     */
+    public function __construct(Horde_Imap_Client_Base $base_ob, $mailbox,
+                                $sync, $curr, $criteria, $ids)
+    {
+        foreach (self::$map as $key => $val) {
+            if (isset($sync[$key])) {
+                $this->$val = $sync[$key];
+            }
+        }
+
+        /* Check uidvalidity. */
+        if (!$this->uidvalidity || ($curr['V'] != $this->uidvalidity)) {
+            throw new Horde_Imap_Client_Exception_Sync('UIDs in cached mailbox have changed.', Horde_Imap_Client_Exception_Sync::UIDVALIDITY_CHANGED);
+        }
+
+        $this->mailbox = $mailbox;
+
+        /* This was a UIDVALIDITY check only. */
+        if (!$criteria) {
+            return;
+        }
+
+        $sync_all = ($criteria & Horde_Imap_Client::SYNC_ALL);
+
+        /* New messages. */
+        if ($sync_all ||
+            ($criteria & Horde_Imap_Client::SYNC_NEWMSGS) ||
+            ($criteria & Horde_Imap_Client::SYNC_NEWMSGSUIDS)) {
+            $this->newmsgs = empty($this->uidnext)
+                ? !empty($curr['U'])
+                : (!empty($curr['U']) && ($curr['U'] > $this->uidnext));
+
+            if ($this->newmsgs &&
+                ($sync_all ||
+                 ($criteria & Horde_Imap_Client::SYNC_NEWMSGSUIDS))) {
+                $new_ids = empty($this->uidnext)
+                    ? Horde_Imap_Client_Ids::ALL
+                    : ($this->uidnext . ':' . $curr['U']);
+
+                $squery = new Horde_Imap_Client_Search_Query();
+                $squery->ids($new_ids);
+                $sres = $base_ob->search($mailbox, $squery);
+
+                $this->_newmsgsuids = $sres['match'];
+            }
+        }
+
+        /* Do single status call to get all necessary data. */
+        if ($this->highestmodseq &&
+            ($sync_all ||
+             ($criteria & Horde_Imap_Client::SYNC_FLAGS) ||
+             ($criteria & Horde_Imap_Client::SYNC_FLAGSUIDS) ||
+             ($criteria & Horde_Imap_Client::SYNC_VANISHED) ||
+             ($criteria & Horde_Imap_Client::SYNC_VANISHEDUIDS))) {
+            $status_sync = $base_ob->status($mailbox, Horde_Imap_Client::STATUS_SYNCMODSEQ | Horde_Imap_Client::STATUS_SYNCFLAGUIDS | Horde_Imap_Client::STATUS_SYNCVANISHED);
+
+            if (!is_null($ids)) {
+                $ids = $base_ob->resolveIds($mailbox, $ids);
+            }
+        }
+
+        /* Flag changes. */
+        if ($sync_all || ($criteria & Horde_Imap_Client::SYNC_FLAGS)) {
+            $this->flags = $this->highestmodseq
+                ? ($this->highestmodseq != $curr['H'])
+                : true;
+        }
+
+        if ($sync_all || ($criteria & Horde_Imap_Client::SYNC_FLAGSUIDS)) {
+            if ($this->highestmodseq) {
+                if ($this->highestmodseq == $status_sync['syncmodseq']) {
+                    $this->_flagsuids = is_null($ids)
+                        ? $status_sync['syncflaguids']
+                        : $base_ob->getIdsOb(array_intersect($ids->ids, $status_sync['syncflaguids']->ids));
+                } else {
+                    $squery = new Horde_Imap_Client_Search_Query();
+                    $squery->modseq($this->highestmodseq + 1);
+                    $sres = $base_ob->search($mailbox, $squery, array(
+                        'ids' => $ids
+                    ));
+                    $this->_flagsuids = $sres['match'];
+                }
+            } else {
+                /* Without MODSEQ, need to mark all FLAGS as changed. */
+                $this->_flagsuids = $base_ob->resolveIds($mailbox, is_null($ids) ? $base_ob->getIdsOb(Horde_Imap_Client_Ids::ALL) : $ids);
+            }
+        }
+
+        /* Vanished messages. */
+        if ($sync_all ||
+            ($criteria & Horde_Imap_Client::SYNC_VANISHED) ||
+            ($criteria & Horde_Imap_Client::SYNC_VANISHEDUIDS)) {
+            if ($this->highestmodseq &&
+                ($this->highestmodseq == $status_sync['syncmodseq'])) {
+                $vanished = is_null($ids)
+                    ? $status_sync['syncvanisheduids']
+                    : $base_ob->getIdsOb(array_intersect($ids->ids, $status_sync['syncvanisheduids']->ids));
+            } else {
+                $vanished = $base_ob->vanished($mailbox, $this->highestmodseq ? $this->highestmodseq : 1, array(
+                    'ids' => $ids
+                ));
+            }
+
+            $this->vanished = (bool)count($vanished);
+            $this->_vanisheduids = $vanished;
+        }
+    }
+
+    /**
+     */
+    public function __get($name)
+    {
+        switch ($name) {
+        case 'flagsuids':
+        case 'newmsgsuids':
+        case 'vanisheduids':
+            $varname = '_' . $name;
+            return empty($this->$varname)
+                ? new Horde_Imap_Client_Ids()
+                : $this->$varname;
+        }
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientDataThreadphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientDataThreadphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Thread.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Data/Thread.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Thread.php                             (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Data/Thread.php        2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,169 @@
</span><ins>+<?php
+/**
+ * Copyright 2008-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2008-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Object representing the threaded sort results from
+ * Horde_Imap_Client_Base#thread().
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2008-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Data_Thread implements Countable, Serializable
+{
+    /**
+     * Internal thread data structure. Keys are base values, values are arrays
+     * with keys as the ID and values as the level.
+     *
+     * @var array
+     */
+    protected $_thread = array();
+
+    /**
+     * The index type.
+     *
+     * @var string
+     */
+    protected $_type;
+
+    /**
+     * Constructor.
+     *
+     * @param array $data   See $_thread.
+     * @param string $type  Either 'sequence' or 'uid'.
+     */
+    public function __construct($data, $type)
+    {
+        $this->_thread = $data;
+        $this->_type = $type;
+    }
+
+    /**
+     * Return the ID type.
+     *
+     * @return string  Either 'sequence' or 'uid'.
+     */
+    public function getType()
+    {
+        return $this->_type;
+    }
+
+    /**
+     * Return the sorted list of messages indices.
+     *
+     * @return Horde_Imap_Client_Ids  The sorted list of messages.
+     */
+    public function messageList()
+    {
+        return new Horde_Imap_Client_Ids($this->_getAllIndices(), $this->getType() == 'sequence');
+    }
+
+    /**
+     * Returns the list of messages in a thread.
+     *
+     * @param integer $index  An index contained in the thread.
+     *
+     * @return array  Keys are indices, values are objects with the following
+     *                properties:
+     *   - base: (integer) Base ID of the thread. If null, thread is a single
+     *           message.
+     *   - last: (boolean) If true, this is the last index in the sublevel.
+     *   - level: (integer) The sublevel of the index.
+     */
+    public function getThread($index)
+    {
+        reset($this->_thread);
+        while (list(,$v) = each($this->_thread)) {
+            if (isset($v[$index])) {
+                reset($v);
+
+                $ob = new stdClass;
+                $ob->base = (count($v) > 1) ? key($v) : null;
+                $ob->last = false;
+
+                $levels = $out = array();
+                $last = 0;
+
+                while (list($k2, $v2) = each($v)) {
+                    $ob2 = clone $ob;
+                    $ob2->level = $v2;
+                    $out[$k2] = $ob2;
+
+                    if (($last < $v2) && isset($levels[$v2])) {
+                        $out[$levels[$v2]]->last = true;
+                    }
+                    $levels[$v2] = $k2;
+                    $last = $v2;
+                }
+
+                foreach ($levels as $v) {
+                    $out[$v]->last = true;
+                }
+
+                return $out;
+            }
+        }
+
+        return array();
+    }
+
+    /* Countable methods. */
+
+    /**
+     */
+    public function count()
+    {
+        return count($this->_getAllIndices());
+    }
+
+    /* Serializable methods. */
+
+    /**
+     */
+    public function serialize()
+    {
+        return json_encode(array(
+            $this->_thread,
+            $this->_type
+        ));
+    }
+
+    /**
+     */
+    public function unserialize($data)
+    {
+        list($this->_thread, $this->_type) = json_decode($data, true);
+    }
+
+    /* Protected methods. */
+
+    /**
+     * Return all indices.
+     *
+     * @return array  An array of indices.
+     */
+    protected function _getAllIndices()
+    {
+        $out = array();
+
+        reset($this->_thread);
+        while (list(,$v) = each($this->_thread)) {
+            $out = array_merge($out, array_keys($v));
+        }
+
+        return $out;
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientDateTimephpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientDateTimephp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/DateTime.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/DateTime.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/DateTime.php                                (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/DateTime.php   2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,79 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * A wrapper around PHP's native DateTime class that handles improperly
+ * formatted dates and adds a few features missing from the base object
+ * (string representation; doesn't fail on bad date input).
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_DateTime extends DateTime
+{
+    /**
+     */
+    public function __construct($time = null)
+    {
+        $tz = new DateTimeZone('UTC');
+
+        try {
+            parent::__construct($time, $tz);
+            return;
+        } catch (Exception $e) {}
+
+        /* Bug #5717 - Check for UT vs. UTC. */
+        if (substr(rtrim($time), -3) == ' UT') {
+            try {
+                parent::__construct($time . 'C', $tz);
+                return;
+            } catch (Exception $e) {}
+        }
+
+        /* Bug #9847 - Catch paranthesized timezone information at end of date
+         * string. */
+        $date = preg_replace("/\s*\([^\)]+\)\s*$/", '', $time, -1, $i);
+        if ($i) {
+            try {
+                parent::__construct($date, $tz);
+                return;
+            } catch (Exception $e) {}
+        }
+
+        parent::__construct('@-1', $tz);
+    }
+
+    /**
+     * String representation: UNIX timestamp.
+     */
+    public function __toString()
+    {
+        return $this->error()
+            ? '0'
+            : $this->format('U');
+    }
+
+    /**
+     * Was this an unparseable date?
+     *
+     * @return boolean  True if unparseable.
+     */
+    public function error()
+    {
+        return ($this->format('U') == -1);
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientExceptionNoSupportExtensionphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientExceptionNoSupportExtensionphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Exception/NoSupportExtension.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Exception/NoSupportExtension.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Exception/NoSupportExtension.php                            (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Exception/NoSupportExtension.php       2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,50 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Exception thrown for non-supported server extensions.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Exception_NoSupportExtension extends Horde_Imap_Client_Exception
+{
+    /**
+     * The extension not supported on the server.
+     *
+     * @var string
+     */
+    public $extension;
+
+    /**
+     * Constructor.
+     *
+     * @param string $extension  The extension not supported on the server.
+     * @param string $msg        A non-standard error message to use instead
+     *                           of the default.
+     */
+    public function __construct($extension, $msg = null)
+    {
+        $this->extension = $extension;
+
+        if (is_null($msg)) {
+            $msg = sprintf(Horde_Imap_Client_Translation::t("The server does not support the %s extension."), $extension);
+        }
+
+        parent::__construct($msg, self::NOT_SUPPORTED);
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientExceptionNoSupportPop3phpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientExceptionNoSupportPop3php"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Exception/NoSupportPop3.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Exception/NoSupportPop3.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Exception/NoSupportPop3.php                         (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Exception/NoSupportPop3.php    2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,38 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Exception thrown for non-supported IMAP features on POP3 servers.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Exception_NoSupportPop3 extends Horde_Imap_Client_Exception
+{
+    /**
+     * Constructor.
+     *
+     * @param string $feature  The feature not supported in POP3.
+     */
+    public function __construct($feature)
+    {
+        parent::__construct(
+            sprintf(Horde_Imap_Client_Translation::t("%s not supported on POP3 servers."), $feature),
+            self::NOT_SUPPORTED
+        );
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientExceptionSearchCharsetphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientExceptionSearchCharsetphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Exception/SearchCharset.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Exception/SearchCharset.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Exception/SearchCharset.php                         (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Exception/SearchCharset.php    2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,49 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Exception thrown if search query text cannot be converted to different
+ * charset.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Exception_SearchCharset extends Horde_Imap_Client_Exception
+{
+    /**
+     * Charset that was attempted to be converted to.
+     *
+     * @var string
+     */
+    public $charset;
+
+    /**
+     * Constructor.
+     *
+     * @param string $charset  The charset that was attempted to be converted
+     *                         to.
+     */
+    public function __construct($charset)
+    {
+        $this->charset = $charset;
+
+        parent::__construct(
+            Horde_Imap_Client_Translation::t("Cannot convert search query text to new charset"),
+            self::BADCHARSET
+        );
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientExceptionServerResponsephpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientExceptionServerResponsephp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Exception/ServerResponse.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Exception/ServerResponse.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Exception/ServerResponse.php                                (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Exception/ServerResponse.php   2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,88 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Exception thrown for server error responses.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ *
+ * @property-read string $command  The command that caused the BAD/NO error
+ *                                 status.
+ * @property-read array $resp_data  The response data array.
+ * @property-read integer $status  Server error status.
+ */
+class Horde_Imap_Client_Exception_ServerResponse extends Horde_Imap_Client_Exception
+{
+    /**
+     * Pipeline object.
+     *
+     * @var Horde_Imap_Client_Interaction_Pipeline
+     */
+    protected $_pipeline;
+
+    /**
+     * Server response object.
+     *
+     * @var Horde_Imap_Client_Interaction_Server
+     */
+    protected $_server;
+
+    /**
+     * Constructor.
+     *
+     * @param string $msg                                       Error message.
+     * @param integer $code                                     Error code.
+     * @param Horde_Imap_Client_Interaction_Server $server      Server ob.
+     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline ob.
+     */
+    public function __construct(
+        $msg = null,
+        $code = 0,
+        Horde_Imap_Client_Interaction_Server $server,
+        Horde_Imap_Client_Interaction_Pipeline $pipeline
+    )
+    {
+        $this->details = strval($server->token);
+
+        $this->_pipeline = $pipeline;
+        $this->_server = $server;
+
+        parent::__construct($msg, $code);
+    }
+
+    /**
+     */
+    public function __get($name)
+    {
+        switch ($name) {
+        case 'command':
+            return ($this->_server instanceof Horde_Imap_Client_Interaction_Server_Tagged)
+                ? $this->_pipeline->getCmd($this->_server->tag)->getCommand()
+                : null;
+
+        case 'resp_data':
+            return $this->_pipeline->data;
+
+        case 'status':
+            return $this->_server->status;
+
+        default:
+            return parent::__get($name);
+        }
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientExceptionSyncphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientExceptionSyncphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Exception/Sync.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Exception/Sync.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Exception/Sync.php                          (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Exception/Sync.php     2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,33 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Exception thrown for mailbox synchronization errors.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Exception_Sync extends Horde_Exception_Wrapped
+{
+    /* Error message codes. */
+
+    // Token could not be parsed.
+    const BAD_TOKEN = 1;
+
+    // UIDVALIDITY of the mailbox changed.
+    const UIDVALIDITY_CHANGED = 2;
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientExceptionphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientExceptionphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Exception.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Exception.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Exception.php                               (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Exception.php  2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,184 @@
</span><ins>+<?php
+/**
+ * Copyright 2008-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2008-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Exception handler for the Horde_Imap_Client package.
+ *
+ * Additional server debug information MAY be found in the $details
+ * property.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2008-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Exception extends Horde_Exception_Wrapped
+{
+    /* Error message codes. */
+
+    // Unspecified error (default)
+    const UNSPECIFIED = 0;
+
+    // There was an unrecoverable error in UTF7IMAP -> UTF8 conversion.
+    const UTF7IMAP_CONVERSION = 3;
+
+    // The server ended the connection.
+    const DISCONNECT = 4;
+
+    // The charset used in the search query is not supported on the server.
+    const BADCHARSET = 5;
+
+    // There were errors parsing the MIME/RFC 2822 header of the part.
+    const PARSEERROR = 6;
+
+    // The server could not decode the MIME part (see RFC 3516)
+    const UNKNOWNCTE = 7;
+
+    // The comparator specified by setComparator() was not recognized by the
+    // IMAP server
+    const BADCOMPARATOR = 9;
+
+    // RFC 4551 [3.1.2] - All mailboxes are not required to support
+    // mod-sequences.
+    const MBOXNOMODSEQ = 10;
+
+    // Thrown if server denies the network connection.
+    const SERVER_CONNECT = 11;
+
+    // Thrown if read error for server response.
+    const SERVER_READERROR = 12;
+
+    // Thrown if write error in server interaction.
+    const SERVER_WRITEERROR = 16;
+
+    // Thrown on CATENATE if the URL is invalid.
+    const CATENATE_BADURL = 13;
+
+    // Thrown on CATENATE if the message was too big.
+    const CATENATE_TOOBIG = 14;
+
+    // Thrown on CREATE if special-use attribute is not supported.
+    const USEATTR = 15;
+
+    // The user did not have permissions to carry out the operation.
+    const NOPERM = 17;
+
+    // The operation was not successful because another user is holding
+    // a necessary resource. The operation may succeed if attempted later.
+    const INUSE = 18;
+
+    // The operation failed because data on the server was corrupt.
+    const CORRUPTION = 19;
+
+    // The operation failed because it exceeded some limit on the server.
+    const LIMIT = 20;
+
+    // The operation failed because the user is over their quota.
+    const OVERQUOTA = 21;
+
+    // The operation failed because the requested creation object already
+    // exists.
+    const ALREADYEXISTS = 22;
+
+    // The operation failed because the requested deletion object did not
+    // exist.
+    const NONEXISTENT = 23;
+
+    // Setting metadata failed because the size of its value is too large.
+    // The maximum octet count the server is willing to accept will be
+    // in the exception message string.
+    const METADATA_MAXSIZE = 24;
+
+    // Setting metadata failed because the maximum number of allowed
+    // annotations has already been reached.
+    const METADATA_TOOMANY = 25;
+
+    // Setting metadata failed because the server does not support private
+    // annotations on one of the specified mailboxes.
+    const METADATA_NOPRIVATE = 26;
+
+    // Invalid metadata entry.
+    const METADATA_INVALID = 27;
+
+
+    // Login failures
+
+    // Could not start mandatory TLS connection.
+    const LOGIN_TLSFAILURE = 100;
+
+    // Could not find an available authentication method.
+    const LOGIN_NOAUTHMETHOD = 101;
+
+    // Generic authentication failure.
+    const LOGIN_AUTHENTICATIONFAILED = 102;
+
+    // Remote server is unavailable.
+    const LOGIN_UNAVAILABLE = 103;
+
+    // Authentication succeeded, but authorization failed.
+    const LOGIN_AUTHORIZATIONFAILED = 104;
+
+    // Authentication is no longer permitted with this passphrase.
+    const LOGIN_EXPIRED = 105;
+
+    // Login requires privacy.
+    const LOGIN_PRIVACYREQUIRED = 106;
+
+
+    // Mailbox access failures
+
+    // Could not open/access mailbox
+    const MAILBOX_NOOPEN = 200;
+
+    // Could not complete the command because the mailbox is read-only
+    const MAILBOX_READONLY = 201;
+
+
+    // POP3 specific error codes
+
+    // Temporary issue. Generally, there is no need to alarm the user for
+    // errors of this type.
+    const POP3_TEMP_ERROR = 300;
+
+    // Permanent error indicated by server.
+    const POP3_PERM_ERROR = 301;
+
+
+    // Unsupported feature error codes
+
+    // Function/feature is not supported on this server.
+    const NOT_SUPPORTED = 400;
+
+
+    /**
+     * Allow the error message to be altered.
+     *
+     * @param string $msg  Error message.
+     */
+    public function setMessage($msg)
+    {
+        $this->message = strval($msg);
+    }
+
+    /**
+     * Allow the error code to be altered.
+     *
+     * @param integer $code  Error code.
+     */
+    public function setCode($code)
+    {
+        $this->code = intval($code);
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientFetchQueryphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientFetchQueryphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Fetch/Query.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Fetch/Query.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Fetch/Query.php                             (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Fetch/Query.php        2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,380 @@
</span><ins>+<?php
+/**
+ * Copyright 2011-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2011-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Fetch query object for use with Horde_Imap_Client_Base#fetch().
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2011-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Fetch_Query implements ArrayAccess, Countable, Iterator
+{
+    /**
+     * Internal data array.
+     *
+     * @var array
+     */
+    protected $_data = array();
+
+    /**
+     * Get the full text of the message.
+     *
+     * @param array $opts  The following options are available:
+     *   - length: (integer) The length of the substring to return.
+     *             DEFAULT: The entire text is returned.
+     *   - peek: (boolean) If set, does not set the '\Seen' flag on the
+     *            message.
+     *            DEFAULT: The seen flag is set.
+     *   - start: (integer) If a portion of the full text is desired to be
+     *            returned, the starting position is identified here.
+     *            DEFAULT: The entire text is returned.
+     */
+    public function fullText(array $opts = array())
+    {
+        $this->_data[Horde_Imap_Client::FETCH_FULLMSG] = $opts;
+    }
+
+    /**
+     * Return header text.
+     *
+     * Header text is defined only for the base RFC 2822 message or
+     * message/rfc822 parts.
+     *
+     * @param array $opts  The following options are available:
+     *   - id: (string) The MIME ID to obtain the header text for.
+     *         DEFAULT: The header text for the base message will be
+     *         returned.
+     *   - length: (integer) The length of the substring to return.
+     *             DEFAULT: The entire text is returned.
+     *   - peek: (boolean) If set, does not set the '\Seen' flag on the
+     *           message.
+     *           DEFAULT: The seen flag is set.
+     *   - start: (integer) If a portion of the full text is desired to be
+     *            returned, the starting position is identified here.
+     *            DEFAULT: The entire text is returned.
+     */
+    public function headerText(array $opts = array())
+    {
+        $id = isset($opts['id'])
+            ? $opts['id']
+            : 0;
+        $this->_data[Horde_Imap_Client::FETCH_HEADERTEXT][$id] = $opts;
+    }
+
+    /**
+     * Return body text.
+     *
+     * Body text is defined only for the base RFC 2822 message or
+     * message/rfc822 parts.
+     *
+     * @param array $opts  The following options are available:
+     *   - id: (string) The MIME ID to obtain the body text for.
+     *         DEFAULT: The body text for the entire message will be
+     *         returned.
+     *   - length: (integer) The length of the substring to return.
+     *             DEFAULT: The entire text is returned.
+     *   - peek: (boolean) If set, does not set the '\Seen' flag on the
+     *           message.
+     *           DEFAULT: The seen flag is set.
+     *   - start: (integer) If a portion of the full text is desired to be
+     *            returned, the starting position is identified here.
+     *            DEFAULT: The entire text is returned.
+     */
+    public function bodyText(array $opts = array())
+    {
+        $id = isset($opts['id'])
+            ? $opts['id']
+            : 0;
+        $this->_data[Horde_Imap_Client::FETCH_BODYTEXT][$id] = $opts;
+    }
+
+    /**
+     * Return MIME header text.
+     *
+     *  MIME header text is defined only for non-RFC 2822 messages and
+     *  non-message/rfc822 parts.
+     *
+     * @param string $id   The MIME ID to obtain the MIME header text for.
+     * @param array $opts  The following options are available:
+     *   - length: (integer) The length of the substring to return.
+     *             DEFAULT: The entire text is returned.
+     *   - peek: (boolean) If set, does not set the '\Seen' flag on the
+     *           message.
+     *           DEFAULT: The seen flag is set.
+     *   - start: (integer) If a portion of the full text is desired to be
+     *            returned, the starting position is identified here.
+     *            DEFAULT: The entire text is returned.
+     */
+    public function mimeHeader($id, array $opts = array())
+    {
+        $this->_data[Horde_Imap_Client::FETCH_MIMEHEADER][$id] = $opts;
+    }
+
+    /**
+     * Return the body part data for a MIME ID.
+     *
+     * @param string $id   The MIME ID to obtain the body part text for.
+     * @param array $opts  The following options are available:
+     *   - decode: (boolean) Attempt to server-side decode the bodypart data
+     *             if it is MIME transfer encoded.
+     *             DEFAULT: false
+     *   - length: (integer) The length of the substring to return.
+     *             DEFAULT: The entire text is returned.
+     *   - peek: (boolean) If set, does not set the '\Seen' flag on the
+     *           message.
+     *           DEFAULT: The seen flag is set.
+     *   - start: (integer) If a portion of the full text is desired to be
+     *            returned, the starting position is identified here.
+     *            DEFAULT: The entire text is returned.
+     */
+    public function bodyPart($id, array $opts = array())
+    {
+        $this->_data[Horde_Imap_Client::FETCH_BODYPART][$id] = $opts;
+    }
+
+    /**
+     * Returns the decoded body part size for a MIME ID.
+     *
+     * @param string $id  The MIME ID to obtain the decoded body part size
+     *                    for.
+     */
+    public function bodyPartSize($id)
+    {
+        $this->_data[Horde_Imap_Client::FETCH_BODYPARTSIZE][$id] = true;
+    }
+
+    /**
+     * Returns RFC 2822 header text that matches a search string.
+     *
+     * This header search work only with the base RFC 2822 message or
+     * message/rfc822 parts.
+     *
+     * @param string $label  A unique label associated with this particular
+     *                       search. This is how the results are stored.
+     * @param array $search  The search string(s) (case-insensitive).
+     * @param array $opts    The following options are available:
+     *   - cache: (boolean) If true, and 'peek' is also true, will cache
+     *            the result of this call.
+     *            DEFAULT: false
+     *   - id: (string) The MIME ID to search.
+     *         DEFAULT: The base message part
+     *   - length: (integer) The length of the substring to return.
+     *             DEFAULT: The entire text is returned.
+     *   - notsearch: (boolean) Do a 'NOT' search on the headers.
+     *                DEFAULT: false
+     *   - peek: (boolean) If set, does not set the '\Seen' flag on the
+     *           message.
+     *           DEFAULT: The seen flag is set.
+     *   - start: (integer) If a portion of the full text is desired to be
+     *            returned, the starting position is identified here.
+     *            DEFAULT: The entire text is returned.
+     */
+    public function headers($label, $search, array $opts = array())
+    {
+        $this->_data[Horde_Imap_Client::FETCH_HEADERS][$label] = array_merge($opts, array(
+            'headers' => $search
+        ));
+    }
+
+    /**
+     * Return MIME structure information.
+     */
+    public function structure()
+    {
+        $this->_data[Horde_Imap_Client::FETCH_STRUCTURE] = true;
+    }
+
+    /**
+     * Return envelope header data.
+     */
+    public function envelope()
+    {
+        $this->_data[Horde_Imap_Client::FETCH_ENVELOPE] = true;
+    }
+
+    /**
+     * Return flags set for the message.
+     */
+    public function flags()
+    {
+        $this->_data[Horde_Imap_Client::FETCH_FLAGS] = true;
+    }
+
+    /**
+     * Return the internal (IMAP) date of the message.
+     */
+    public function imapDate()
+    {
+        $this->_data[Horde_Imap_Client::FETCH_IMAPDATE] = true;
+    }
+
+    /**
+     * Return the size (in bytes) of the message.
+     */
+    public function size()
+    {
+        $this->_data[Horde_Imap_Client::FETCH_SIZE] = true;
+    }
+
+    /**
+     * Return the unique ID of the message.
+     */
+    public function uid()
+    {
+        $this->_data[Horde_Imap_Client::FETCH_UID] = true;
+    }
+
+    /**
+     * Return the sequence number of the message.
+     */
+    public function seq()
+    {
+        $this->_data[Horde_Imap_Client::FETCH_SEQ] = true;
+    }
+
+    /**
+     * Return the mod-sequence value for the message.
+     *
+     * The server must support the CONDSTORE IMAP extension, and the mailbox
+     * must support mod-sequences.
+     */
+    public function modseq()
+    {
+        $this->_data[Horde_Imap_Client::FETCH_MODSEQ] = true;
+    }
+
+    /**
+     * Does the query contain the given criteria?
+     *
+     * @param integer $criteria  The criteria to remove.
+     *
+     * @return boolean  True if the query contains the given criteria.
+     */
+    public function contains($criteria)
+    {
+        return isset($this->_data[$criteria]);
+    }
+
+    /**
+     * Remove an entry under a given criteria.
+     *
+     * @param integer $criteria  Criteria ID.
+     * @param string $key        The key to remove.
+     */
+    public function remove($criteria, $key)
+    {
+        if (isset($this->_data[$criteria]) &&
+            is_array($this->_data[$criteria])) {
+            unset($this->_data[$criteria][$key]);
+            if (empty($this->_data[$criteria])) {
+                unset($this->_data[$criteria]);
+            }
+        }
+    }
+
+    /**
+     * Returns a MD5 hash of the current query object.
+     *
+     * @return string  MD5 hash.
+     */
+    public function hash()
+    {
+        return hash('md5', serialize($this));
+    }
+
+    /* ArrayAccess methods. */
+
+    /**
+     */
+    public function offsetExists($offset)
+    {
+        return isset($this->_data[$offset]);
+    }
+
+    /**
+     */
+    public function offsetGet($offset)
+    {
+        return isset($this->_data[$offset])
+            ? $this->_data[$offset]
+            : null;
+    }
+
+    /**
+     */
+    public function offsetSet($offset, $value)
+    {
+        $this->_data[$offset] = $value;
+    }
+
+    /**
+     */
+    public function offsetUnset($offset)
+    {
+        unset($this->_data[$offset]);
+    }
+
+    /* Countable methods. */
+
+    /**
+     */
+    public function count()
+    {
+        return count($this->_data);
+    }
+
+    /* Iterator methods. */
+
+    /**
+     */
+    public function current()
+    {
+        $opts = current($this->_data);
+
+        return (!empty($opts) && ($this->key() == Horde_Imap_Client::FETCH_BODYPARTSIZE))
+            ? array_keys($opts)
+            : $opts;
+    }
+
+    /**
+     */
+    public function key()
+    {
+        return key($this->_data);
+    }
+
+    /**
+     */
+    public function next()
+    {
+        next($this->_data);
+    }
+
+    /**
+     */
+    public function rewind()
+    {
+        reset($this->_data);
+    }
+
+    /**
+     */
+    public function valid()
+    {
+        return !is_null($this->key());
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientFetchResultsphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientFetchResultsphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Fetch/Results.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Fetch/Results.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Fetch/Results.php                           (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Fetch/Results.php      2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,179 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Fetch results object for use with Horde_Imap_Client_Base#fetch().
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ *
+ * @property-read integer $key_type  The key type (sequence or UID).
+ */
+class Horde_Imap_Client_Fetch_Results implements ArrayAccess, Countable, IteratorAggregate
+{
+    /* Key type constants. */
+    const SEQUENCE = 1;
+    const UID = 2;
+
+    /**
+     * Internal data array.
+     *
+     * @var array
+     */
+    protected $_data = array();
+
+    /**
+     * Key type.
+     *
+     * @var integer
+     */
+    protected $_keyType;
+
+    /**
+     * Class to use when creating a new fetch object.
+     *
+     * @var string
+     */
+    protected $_obClass;
+
+    /**
+     * Constructor.
+     *
+     * @param string $ob_class   Class to use when creating a new fetch
+     *                           object.
+     * @param integer $key_type  Key type.
+     */
+    public function __construct($ob_class = 'Horde_Imap_Client_Data_Fetch',
+                                $key_type = self::UID)
+    {
+        $this->_obClass = $ob_class;
+        $this->_keyType = $key_type;
+    }
+
+    /**
+     */
+    public function __get($name)
+    {
+        switch ($name) {
+        case 'key_type':
+            return $this->_keyType;
+        }
+    }
+
+    /**
+     * Return a fetch object, creating and storing an empty object in the
+     * results set if it doesn't currently exist.
+     *
+     * @param string $key  The key to retrieve.
+     *
+     * @return Horde_Imap_Client_Data_Fetch  The fetch object.
+     */
+    public function get($key)
+    {
+        if (!isset($this->_data[$key])) {
+            $this->_data[$key] = new $this->_obClass();
+        }
+
+        return $this->_data[$key];
+    }
+
+    /**
+     * Return the list of IDs.
+     *
+     * @return array  ID list.
+     */
+    public function ids()
+    {
+        ksort($this->_data);
+        return array_keys($this->_data);
+    }
+
+    /**
+     * Return the first fetch object in the results, if there is only one
+     * object.
+     *
+     * @return null|Horde_Imap_Client_Data_Fetch  The fetch object if there is
+     *                                            only one object, or null.
+     */
+    public function first()
+    {
+        return (count($this->_data) == 1)
+            ? reset($this->_data)
+            : null;
+    }
+
+    /**
+     * Clears all fetch results.
+     *
+     * @since 2.6.0
+     */
+    public function clear()
+    {
+        $this->_data = array();
+    }
+
+    /* ArrayAccess methods. */
+
+    /**
+     */
+    public function offsetExists($offset)
+    {
+        return isset($this->_data[$offset]);
+    }
+
+    /**
+     */
+    public function offsetGet($offset)
+    {
+        return isset($this->_data[$offset])
+            ? $this->_data[$offset]
+            : null;
+    }
+
+    /**
+     */
+    public function offsetSet($offset, $value)
+    {
+        $this->_data[$offset] = $value;
+    }
+
+    /**
+     */
+    public function offsetUnset($offset)
+    {
+        unset($this->_data[$offset]);
+    }
+
+    /* Countable methods. */
+
+    /**
+     */
+    public function count()
+    {
+        return count($this->_data);
+    }
+
+    /* IteratorAggregate methods. */
+
+    /**
+     */
+    public function getIterator()
+    {
+        ksort($this->_data);
+        return new ArrayIterator($this->_data);
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientIdsMapphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientIdsMapphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Ids/Map.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Ids/Map.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Ids/Map.php                         (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Ids/Map.php    2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,236 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * An object implementing lookups between UIDs and message sequence numbers.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ * @since     2.1.0
+ *
+ * @property-read array $map  The raw ID mapping data.
+ * @property-read Horde_Imap_Client_Ids $seq  The sorted sequence values.
+ * @property-read Horde_Imap_Client_Ids $uids  The sorted UIDs.
+ */
+class Horde_Imap_Client_Ids_Map implements Countable, IteratorAggregate, Serializable
+{
+    /**
+     * Sequence -> UID mapping.
+     *
+     * @var array
+     */
+    protected $_ids = array();
+
+    /**
+     * Is the array sorted?
+     *
+     * @var boolean
+     */
+    protected $_sorted = true;
+
+    /**
+     * Constructor.
+     *
+     * @param array $ids  Array of sequence -> UID mapping.
+     */
+    public function __construct(array $ids = array())
+    {
+        $this->update($ids);
+    }
+
+    /**
+     */
+    public function __get($name)
+    {
+        switch ($name) {
+        case 'map':
+            return $this->_ids;
+
+        case 'seq':
+            $this->sort();
+            return new Horde_Imap_Client_Ids(array_keys($this->_ids), true);
+
+        case 'uids':
+            $this->sort();
+            return new Horde_Imap_Client_Ids($this->_ids);
+        }
+    }
+
+    /**
+     * Updates the mapping.
+     *
+     * @param array $ids  Array of sequence -> UID mapping.
+     *
+     * @return boolean  True if the mapping changed.
+     */
+    public function update($ids)
+    {
+        if (empty($ids)) {
+            return false;
+        } elseif (empty($this->_ids)) {
+            $this->_ids = $ids;
+            $change = true;
+        } else {
+            $change = false;
+            foreach ($ids as $k => $v) {
+                if (!isset($this->_ids[$k]) || ($this->_ids[$k] != $v)) {
+                    $this->_ids[$k] = $v;
+                    $change = true;
+                }
+            }
+        }
+
+        if ($change) {
+            $this->_sorted = false;
+        }
+
+        return $change;
+    }
+
+    /**
+     * Create a Sequence <-> UID lookup table.
+     *
+     * @param Horde_Imap_Client_Ids $ids  IDs to lookup.
+     *
+     * @return array  Keys are sequence numbers, values are UIDs.
+     */
+    public function lookup(Horde_Imap_Client_Ids $ids)
+    {
+        if ($ids->all) {
+            return $this->_ids;
+        } elseif ($ids->sequence) {
+            return array_intersect_key($this->_ids, array_flip($ids->ids));
+        }
+
+        return array_intersect($this->_ids, $ids->ids);
+    }
+
+    /**
+     * Removes messages from the ID mapping.
+     *
+     * @param Horde_Imap_Client_Ids $ids  IDs to remove.
+     */
+    public function remove(Horde_Imap_Client_Ids $ids)
+    {
+        /* For sequence numbers, we need to reindex anytime we have an index
+         * that appears equal to or after a previously seen index. If an IMAP
+         * server is smart, it will expunge in reverse order instead. */
+        if ($ids->sequence) {
+            $remove = $ids->ids;
+        } else {
+            $ids->sort();
+            $remove = array_reverse(array_keys($this->lookup($ids)));
+        }
+
+        if (empty($remove)) {
+            return;
+        }
+
+        $this->sort();
+
+        /* Find the minimum sequence number to remove. We know entries before
+         * this are untouched so no need to process them multiple times. */
+        $first = min($remove);
+        $edit = $newids = array();
+        foreach (array_keys($this->_ids) as $i => $seq) {
+            if ($seq >= $first) {
+                $i += (($seq == $first) ? 0 : 1);
+                $newids = array_slice($this->_ids, 0, $i, true);
+                $edit = array_slice($this->_ids, $i + (($seq == $first) ? 0 : 1), null, true);
+                break;
+            }
+        }
+
+        if (!empty($edit)) {
+            foreach ($remove as $val) {
+                $found = false;
+                $tmp = array();
+
+                foreach (array_keys($edit) as $i => $seq) {
+                    if ($found) {
+                        $tmp[$seq - 1] = $edit[$seq];
+                    } elseif ($seq >= $val) {
+                        $tmp = array_slice($edit, 0, ($seq == $val) ? $i : $i + 1, true);
+                        $found = true;
+                    }
+                }
+
+                $edit = $tmp;
+            }
+        }
+
+        $this->_ids = $newids + $edit;
+    }
+
+    /**
+     * Sort the map.
+     */
+    public function sort()
+    {
+        if (!$this->_sorted) {
+            ksort($this->_ids, SORT_NUMERIC);
+            $this->_sorted = true;
+        }
+    }
+
+    /* Countable methods. */
+
+    /**
+     */
+    public function count()
+    {
+        return count($this->_ids);
+    }
+
+    /* IteratorAggregate method. */
+
+    /**
+     */
+    public function getIterator()
+    {
+        return new ArrayIterator($this->_ids);
+    }
+
+    /* Serializable methods. */
+
+    /**
+     */
+    public function serialize()
+    {
+        /* Sort before storing; provides more compressible representation. */
+        $this->sort();
+
+        return json_encode(array(
+            strval(new Horde_Imap_Client_Ids(array_keys($this->_ids))),
+            strval(new Horde_Imap_Client_Ids(array_values($this->_ids)))
+        ));
+    }
+
+    /**
+     */
+    public function unserialize($data)
+    {
+        $data = json_decode($data, true);
+
+        $keys = new Horde_Imap_Client_Ids($data[0]);
+        $vals = new Horde_Imap_Client_Ids($data[1]);
+        $this->_ids = array_combine($keys->ids, $vals->ids);
+
+        /* Guaranteed to be sorted if unserializing. */
+        $this->_sorted = true;
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientIdsPop3phpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientIdsPop3php"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Ids/Pop3.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Ids/Pop3.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Ids/Pop3.php                                (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Ids/Pop3.php   2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,53 @@
</span><ins>+<?php
+/**
+ * Copyright 2011-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2011-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Wrapper around Ids object that correctly handles POP3 UID strings.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2011-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Ids_Pop3 extends Horde_Imap_Client_Ids
+{
+    /**
+     * Create a POP3 message sequence string.
+     *
+     * Index Format: UID1[SPACE]UID2...
+     *
+     * @param boolean $sort  Not used in this class.
+     *
+     * @return string  The POP3 message sequence string.
+     */
+    protected function _toSequenceString($sort = true)
+    {
+        /* Use space as delimiter as it is the only printable ASCII character
+         * that is not allowed as part of the UID (RFC 1939 [7]). */
+        return implode(' ', count($this->_ids) > 25000 ? array_unique($this->_ids) : array_keys(array_flip($this->_ids)));
+    }
+
+    /**
+     * Parse a POP3 message sequence string into a list of indices.
+     *
+     * @param string $str  The POP3 message sequence string.
+     *
+     * @return array  An array of UIDs.
+     */
+    protected function _fromSequenceString($str)
+    {
+        return explode(' ', trim($str));
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientIdsphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientIdsphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Ids.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Ids.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Ids.php                             (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Ids.php        2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,449 @@
</span><ins>+<?php
+/**
+ * Copyright 2011-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2011-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * An object that provides a way to identify a list of IMAP indices.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2011-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ *
+ * @property-read boolean $all  Does this represent an ALL message set?
+ * @property-read array $ids  The list of IDs.
+ * @property-read boolean $largest  Does this represent the largest ID in use?
+ * @property-read string $range_string  Generates a range string consisting of
+ *                                      all messages between begin and end of
+ *                                      ID list.
+ * @property-read boolean $search_res  Does this represent a search result?
+ * @property-read boolean $sequence  Are these sequence IDs? If false, these
+ *                                   are UIDs.
+ * @property-read boolean $special  True if this is a "special" ID
+ *                                  representation.
+ * @property-read string $tostring  Return the non-sorted string
+ *                                  representation.
+ * @property-read string $tostring_sort  Return the sorted string
+ *                                       representation.
+ */
+class Horde_Imap_Client_Ids implements Countable, Iterator, Serializable
+{
+    /* "Special" representation constants. */
+    const ALL = "\01";
+    const SEARCH_RES = "\02";
+    const LARGEST = "\03";
+
+    /**
+     * Allow duplicate IDs?
+     *
+     * @var boolean
+     */
+    public $duplicates = false;
+
+    /**
+     * List of IDs.
+     *
+     * @var mixed
+     */
+    protected $_ids = array();
+
+    /**
+     * Are IDs message sequence numbers?
+     *
+     * @var boolean
+     */
+    protected $_sequence = false;
+
+    /**
+     * Are IDs sorted?
+     *
+     * @var boolean
+     */
+    protected $_sorted = false;
+
+    /**
+     * Constructor.
+     *
+     * @param mixed $ids         See self::add().
+     * @param boolean $sequence  Are $ids message sequence numbers?
+     */
+    public function __construct($ids = null, $sequence = false)
+    {
+        $this->add($ids);
+        $this->_sequence = $sequence;
+    }
+
+    /**
+     */
+    public function __get($name)
+    {
+        switch ($name) {
+        case 'all':
+            return ($this->_ids === self::ALL);
+
+        case 'ids':
+            return is_array($this->_ids)
+                ? $this->_ids
+                : array();
+
+        case 'largest':
+            return ($this->_ids === self::LARGEST);
+
+        case 'range_string':
+            if (!count($this)) {
+                return '';
+            }
+
+            $this->sort();
+            $min = reset($this->_ids);
+            $max = end($this->_ids);
+
+            return ($min == $max)
+                ? $min
+                : $min . ':' . $max;
+
+        case 'search_res':
+            return ($this->_ids === self::SEARCH_RES);
+
+        case 'sequence':
+            return (bool)$this->_sequence;
+
+        case 'special':
+            return is_string($this->_ids);
+
+        case 'tostring':
+        case 'tostring_sort':
+            if ($this->all) {
+                return '1:*';
+            } elseif ($this->largest) {
+                return '*';
+            } elseif ($this->search_res) {
+                return '$';
+            }
+            return strval($this->_toSequenceString($name == 'tostring_sort'));
+        }
+    }
+
+    /**
+     */
+    public function __toString()
+    {
+        return $this->tostring;
+    }
+
+    /**
+     * Add IDs to the current object.
+     *
+     * @param mixed $ids  Either self::ALL, self::SEARCH_RES, self::LARGEST,
+     *                    Horde_Imap_Client_Ids object, array, or sequence
+     *                    string.
+     */
+    public function add($ids)
+    {
+        if (!is_null($ids)) {
+            $add = array();
+
+            if (is_string($ids) &&
+                in_array($ids, array(self::ALL, self::SEARCH_RES, self::LARGEST))) {
+                $this->_ids = $ids;
+                $this->_sorted = false;
+                return;
+            }
+
+            if ($ids instanceof Horde_Imap_Client_Ids) {
+                $add = $ids->ids;
+            } elseif (is_array($ids)) {
+                $add = $ids;
+            } elseif (is_string($ids) || is_integer($ids)) {
+                if (is_numeric($ids)) {
+                    $add = array($ids);
+                } else {
+                    $add = $this->_fromSequenceString($ids);
+                }
+            }
+
+            if (!empty($add)) {
+                $this->_ids = is_array($this->_ids)
+                    ? array_merge($this->_ids, $add)
+                    : $add;
+                if (!$this->duplicates) {
+                    $this->_ids = (count($this->_ids) > 25000)
+                        ? array_unique($this->_ids)
+                        : array_keys(array_flip($this->_ids));
+                }
+                $this->_sorted = false;
+            }
+        }
+    }
+
+    /**
+     * Is this object empty (i.e. does not contain IDs)?
+     *
+     * @return boolean  True if object is empty.
+     */
+    public function isEmpty()
+    {
+        return (is_array($this->_ids) && !count($this->_ids));
+    }
+
+    /**
+     * Reverses the order of the IDs.
+     */
+    public function reverse()
+    {
+        if (is_array($this->_ids)) {
+            $this->_ids = array_reverse($this->_ids);
+        }
+    }
+
+    /**
+     * Sorts the IDs numerically.
+     */
+    public function sort()
+    {
+        if (!$this->_sorted && is_array($this->_ids)) {
+            sort($this->_ids, SORT_NUMERIC);
+            $this->_sorted = true;
+        }
+    }
+
+    /**
+     * Split the sequence string at an approximate length.
+     *
+     * @since 2.7.0
+     *
+     * @param integer $length  Length to split.
+     *
+     * @return array  A list containing individual sequence strings.
+     */
+    public function split($length)
+    {
+        $id = new Horde_Stream_Temp();
+        $id->add($this->tostring_sort, true);
+
+        $out = array();
+
+        do {
+            $out[] = stream_get_contents($id->stream, $length) . $id->getToChar(',');
+        } while (!feof($id->stream));
+
+        return $out;
+    }
+
+    /**
+     * Create an IMAP message sequence string from a list of indices.
+     *
+     * Index Format: range_start:range_end,uid,uid2,...
+     *
+     * @param boolean $sort  Numerically sort the IDs before creating the
+     *                       range?
+     *
+     * @return string  The IMAP message sequence string.
+     */
+    protected function _toSequenceString($sort = true)
+    {
+        if (empty($this->_ids)) {
+            return '';
+        }
+
+        $in = $this->_ids;
+
+        if ($sort) {
+            sort($in, SORT_NUMERIC);
+        }
+
+        $first = $last = array_shift($in);
+        $i = count($in) - 1;
+        $out = array();
+
+        reset($in);
+        while (list($key, $val) = each($in)) {
+            if (($last + 1) == $val) {
+                $last = $val;
+            }
+
+            if (($i == $key) || ($last != $val)) {
+                if ($last == $first) {
+                    $out[] = $first;
+                    if ($i == $key) {
+                        $out[] = $val;
+                    }
+                } else {
+                    $out[] = $first . ':' . $last;
+                    if (($i == $key) && ($last != $val)) {
+                        $out[] = $val;
+                    }
+                }
+                $first = $last = $val;
+            }
+        }
+
+        return empty($out)
+            ? $first
+            : implode(',', $out);
+    }
+
+    /**
+     * Parse an IMAP message sequence string into a list of indices.
+     *
+     * @see _toSequenceString()
+     *
+     * @param string $str  The IMAP message sequence string.
+     *
+     * @return array  An array of indices.
+     */
+    protected function _fromSequenceString($str)
+    {
+        $ids = array();
+        $str = trim($str);
+
+        if (!strlen($str)) {
+            return $ids;
+        }
+
+        $idarray = explode(',', $str);
+
+        reset($idarray);
+        while (list(,$val) = each($idarray)) {
+            $range = explode(':', $val);
+            if (isset($range[1])) {
+                for ($i = min($range), $j = max($range); $i <= $j; ++$i) {
+                    $ids[] = $i;
+                }
+            } else {
+                $ids[] = $val;
+            }
+        }
+
+        return $ids;
+    }
+
+    /* Countable methods. */
+
+    /**
+     */
+    public function count()
+    {
+        return is_array($this->_ids)
+            ? count($this->_ids)
+           : 0;
+    }
+
+    /* Iterator methods. */
+
+    /**
+     */
+    public function current()
+    {
+        return is_array($this->_ids)
+            ? current($this->_ids)
+            : null;
+    }
+
+    /**
+     */
+    public function key()
+    {
+        return is_array($this->_ids)
+            ? key($this->_ids)
+            : null;
+    }
+
+    /**
+     */
+    public function next()
+    {
+        if (is_array($this->_ids)) {
+            next($this->_ids);
+        }
+    }
+
+    /**
+     */
+    public function rewind()
+    {
+        if (is_array($this->_ids)) {
+            reset($this->_ids);
+        }
+    }
+
+    /**
+     */
+    public function valid()
+    {
+        return !is_null($this->key());
+    }
+
+    /* Serializable methods. */
+
+    /**
+     */
+    public function serialize()
+    {
+        $save = array();
+
+        if ($this->duplicates) {
+            $save['d'] = 1;
+        }
+
+        if ($this->_sequence) {
+            $save['s'] = 1;
+        }
+
+        if ($this->_sorted) {
+            $save['is'] = 1;
+        }
+
+        switch ($this->_ids) {
+        case self::ALL:
+            $save['a'] = true;
+            break;
+
+        case self::LARGEST:
+            $save['l'] = true;
+            break;
+
+        case self::SEARCH_RES:
+            $save['sr'] = true;
+            break;
+
+        default:
+            $save['i'] = strval($this);
+            break;
+        }
+
+        return serialize($save);
+    }
+
+    /**
+     */
+    public function unserialize($data)
+    {
+        $save = @unserialize($data);
+
+        $this->duplicates = !empty($save['d']);
+        $this->_sequence = !empty($save['s']);
+        $this->_sorted = !empty($save['is']);
+
+        if (isset($save['a'])) {
+            $this->_ids = self::ALL;
+        } elseif (isset($save['l'])) {
+            $this->_ids = self::LARGEST;
+        } elseif (isset($save['sr'])) {
+            $this->_ids = self::SEARCH_RES;
+        } elseif (isset($save['i'])) {
+            $this->add($save['i']);
+        }
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientInteractionClientphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientInteractionClientphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Interaction/Client.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Client.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Interaction/Client.php                              (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Interaction/Client.php 2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,61 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * An object representing an IMAP client command interaction (RFC 3501
+ * [2.2.1]).
+ *
+ * @author     Michael Slusarz <slusarz@horde.org>
+ * @category   Horde
+ * @copyright  2012-2013 Horde LLC
+ * @deprecated
+ * @license    http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package    Imap_Client
+ */
+class Horde_Imap_Client_Interaction_Client extends Horde_Imap_Client_Data_Format_List
+{
+    /**
+     * The command tag.
+     *
+     * @var string
+     */
+    public $tag;
+
+    /**
+     * Constructor.
+     *
+     * @param string $tag  The tag to use. If not set, will be automatically
+     *                     generated.
+     */
+    public function __construct($tag = null)
+    {
+        $this->tag = is_null($tag)
+            ? substr(strval(new Horde_Support_Randomid()), 0, 10)
+            : strval($tag);
+
+        parent::__construct($this->tag);
+    }
+
+    /**
+     * Get the command.
+     *
+     * @return string  The command.
+     */
+    public function getCommand()
+    {
+        return isset($this->_data[1])
+            ? $this->_data[1]
+            : null;
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientInteractionCommandContinuationphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientInteractionCommandContinuationphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Interaction/Command/Continuation.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Command/Continuation.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Interaction/Command/Continuation.php                                (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Interaction/Command/Continuation.php   2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,66 @@
</span><ins>+<?php
+/**
+ * Copyright 2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * An object representing a portion of an IMAP command that requires data
+ * sent in a continuation response (RFC 3501 [2.2.1]).
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ * @since     2.10.0
+ */
+class Horde_Imap_Client_Interaction_Command_Continuation
+{
+    /**
+     * Closure function to run after continuation response.
+     *
+     * @var Closure
+     */
+    protected $_closure;
+
+    /**
+     * Constructor.
+     *
+     * @param Closure $closure  A function to run after the continuation
+     *                          response is received.  It receives one
+     *                          argument - a Continuation object - and should
+     *                          return a list of arguments to send to the
+     *                          server (via a
+     *                          Horde_Imap_Client_Data_Format_List object).
+     */
+    public function __construct($closure)
+    {
+        $this->_closure = $closure;
+    }
+
+    /**
+     * Calls the closure object.
+     *
+     * @param Horde_Imap_Client_Interaction_Server_Continuation $ob  Continuation
+     *                                                               object.
+     *
+     * @return Horde_Imap_Client_Data_Format_List  Further commands to issue
+     *                                             to the server.
+     */
+    public function getCommands(
+        Horde_Imap_Client_Interaction_Server_Continuation $ob
+    )
+    {
+        $closure = $this->_closure;
+        return $closure($ob);
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientInteractionCommandphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientInteractionCommandphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Interaction/Command.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Command.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Interaction/Command.php                             (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Interaction/Command.php        2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,110 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * An object representing an IMAP command (RFC 3501 [2.2.1]).
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ * @since     2.10.0
+ *
+ * @property-read boolean $continuation  True if the command requires a server
+ *                                       continuation response.
+ */
+class Horde_Imap_Client_Interaction_Command extends Horde_Imap_Client_Data_Format_List
+{
+    /**
+     * Debug string to use instead of command text.
+     *
+     * @var string
+     */
+    public $debug = null;
+
+    /**
+     * Use LITERAL+ if available
+     *
+     * @var boolean
+     */
+    public $literalplus = true;
+
+    /**
+     * Are literal8's available?
+     *
+     * @var boolean
+     */
+    public $literal8 = false;
+
+    /**
+     * Server response.
+     *
+     * @var Horde_Imap_Client_Interaction_Server
+     */
+    public $response;
+
+    /**
+     * The command tag.
+     *
+     * @var string
+     */
+    public $tag;
+
+    /**
+     * Constructor.
+     *
+     * @param string $cmd  The IMAP command.
+     * @param string $tag  The tag to use. If not set, will be automatically
+     *                     generated.
+     */
+    public function __construct($cmd, $tag = null)
+    {
+        $this->tag = is_null($tag)
+            ? substr(new Horde_Support_Randomid(), 0, 10)
+            : strval($tag);
+
+        parent::__construct($this->tag);
+
+        $this->add($cmd);
+    }
+
+    /**
+     */
+    public function __get($name)
+    {
+        switch ($name) {
+        case 'continuation':
+            foreach ($this as $val) {
+                if (($val instanceof Horde_Imap_Client_Interaction_Command_Continuation) ||
+                    (($val instanceof Horde_Imap_Client_Data_Format_String) &&
+                     $val->literal())) {
+
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Get the command.
+     *
+     * @return string  The command.
+     */
+    public function getCommand()
+    {
+        return $this->_data[1];
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientInteractionPipelinephpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientInteractionPipelinephp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Interaction/Pipeline.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Pipeline.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Interaction/Pipeline.php                            (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Interaction/Pipeline.php       2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,145 @@
</span><ins>+<?php
+/**
+ * Copyright 2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * An object representing a series of IMAP client commands (RFC 3501 [2.2.1])
+ * to be processed at the same time.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ * @since     2.10.0
+ *
+ * @property-read boolean $finished  True if all commands have finished.
+ */
+class Horde_Imap_Client_Interaction_Pipeline implements Countable, IteratorAggregate
+{
+    /**
+     * Data storage from server responses.
+     *
+     * @var array
+     */
+    public $data = array(
+        'modseqs' => array(),
+        'modseqs_nouid' => array()
+    );
+
+    /**
+     * Fetch results.
+     *
+     * @var Horde_Imap_Client_Fetch_Results
+     */
+    public $fetch;
+
+    /**
+     * The list of commands.
+     *
+     * @var array
+     */
+    protected $_commands = array();
+
+    /**
+     * The list of commands to complete.
+     *
+     * @var array
+     */
+    protected $_todo = array();
+
+    /**
+     * Constructor.
+     *
+     * @param Horde_Imap_Client_Fetch_Results $fetch  Fetch results object.
+     */
+    public function __construct(Horde_Imap_Client_Fetch_Results $fetch)
+    {
+        $this->fetch = $fetch;
+    }
+
+    /**
+     */
+    public function __get($name)
+    {
+        switch ($name) {
+        case 'finished':
+            return empty($this->_todo);
+        }
+    }
+
+    /**
+     * Add a command to the pipeline.
+     *
+     * @param Horde_Imap_Client_Interaction_Command $cmd  Command object.
+     * @param boolean $top                                Add command to top
+     *                                                    of queue?
+     */
+    public function add(Horde_Imap_Client_Interaction_Command $cmd,
+                        $top = false)
+    {
+        if ($top) {
+            // This won't re-index keys, which may be numerical.
+            $this->_commands = array($cmd->tag => $cmd) + $this->_commands;
+        } else {
+            $this->_commands[$cmd->tag] = $cmd;
+        }
+        $this->_todo[$cmd->tag] = true;
+    }
+
+    /**
+     * Mark a command as completed.
+     *
+     * @param Horde_Imap_Client_Interaction_Server_Tagged $resp  Tagged server
+     *                                                           response.
+     */
+    public function complete(Horde_Imap_Client_Interaction_Server_Tagged $resp)
+    {
+        $this->_commands[$resp->tag]->response = $resp;
+        unset($this->_todo[$resp->tag]);
+    }
+
+    /**
+     * Return the command for a given tag.
+     *
+     * @param string $tag  The command tag.
+     *
+     * @return Horde_Imap_Client_Interaction_Command  A command object (or
+     *                                                null if the tag does
+     *                                                not exist).
+     */
+    public function getCmd($tag)
+    {
+        return isset($this->_commands[$tag])
+            ? $this->_commands[$tag]
+            : null;
+    }
+
+    /* Countable methods. */
+
+    /**
+     */
+    public function count()
+    {
+        return count($this->_commands);
+    }
+
+    /* IteratorAggregate methods. */
+
+    /**
+     */
+    public function getIterator()
+    {
+        return new ArrayIterator($this->_commands);
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientInteractionServerContinuationphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientInteractionServerContinuationphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Interaction/Server/Continuation.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Server/Continuation.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Interaction/Server/Continuation.php                         (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Interaction/Server/Continuation.php    2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,25 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * An object representing an IMAP continuation response (RFC 3501 [2.2.2]).
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Interaction_Server_Continuation extends Horde_Imap_Client_Interaction_Server
+{
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientInteractionServerTaggedphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientInteractionServerTaggedphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Interaction/Server/Tagged.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Server/Tagged.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Interaction/Server/Tagged.php                               (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Interaction/Server/Tagged.php  2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,46 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * An object representing an IMAP tagged response (RFC 3501 [2.2.2]).
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Interaction_Server_Tagged extends Horde_Imap_Client_Interaction_Server
+{
+    /**
+     * Tag.
+     *
+     * @var string
+     */
+    public $tag;
+
+    /**
+     * @param string $tag  Response tag.
+     */
+    public function __construct(Horde_Imap_Client_Tokenize $token, $tag)
+    {
+        $this->tag = $tag;
+
+        parent::__construct($token);
+
+        if (is_null($this->status)) {
+            throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::t("Bad tagged response."));
+        }
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientInteractionServerUntaggedphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientInteractionServerUntaggedphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Interaction/Server/Untagged.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Server/Untagged.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Interaction/Server/Untagged.php                             (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Interaction/Server/Untagged.php        2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,25 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * An object representing an IMAP untagged response (RFC 3501 [2.2.2]).
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Interaction_Server_Untagged extends Horde_Imap_Client_Interaction_Server
+{
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientInteractionServerphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientInteractionServerphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Interaction/Server.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Interaction/Server.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Interaction/Server.php                              (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Interaction/Server.php 2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,142 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * An object representing an IMAP server command interaction (RFC 3501
+ * [2.2.2]).
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Interaction_Server
+{
+    /* Response codes (RFC 3501 [7.1]). */
+    const BAD = 1;
+    const BYE = 2;
+    const NO = 3;
+    const OK = 4;
+    const PREAUTH = 5;
+
+    /**
+     * Check for status response?
+     *
+     * @var boolean
+     */
+    protected $_checkStatus = true;
+
+    /**
+     * Response code (RFC 3501 [7.1]). Properties:
+     *   - code: (string) Response code.
+     *   - data: (array) Data associated with response.
+     *
+     * @var object
+     */
+    public $responseCode = null;
+
+    /**
+     * Status response from the server.
+     *
+     * @var string
+     */
+    public $status = null;
+
+    /**
+     * IMAP server data.
+     *
+     * @var Horde_Imap_Client_Tokenize
+     */
+    public $token;
+
+    /**
+     * Auto-scan an incoming line to determine the response type.
+     *
+     * @param Horde_Imap_Client_Tokenize $t  Tokenized data returned from the
+     *                                       server.
+     *
+     * @return Horde_Imap_Client_Interaction_Server  A server response object.
+     */
+    static public function create(Horde_Imap_Client_Tokenize $t)
+    {
+        $t->rewind();
+        $tag = $t->next();
+        $t->next();
+
+        switch ($tag) {
+        case '+':
+            return new Horde_Imap_Client_Interaction_Server_Continuation($t);
+
+        case '*':
+            return new Horde_Imap_Client_Interaction_Server_Untagged($t);
+
+        default:
+            return new Horde_Imap_Client_Interaction_Server_Tagged($t, $tag);
+        }
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param Horde_Imap_Client_Tokenize $token  Tokenized data returned from
+     *                                           the server.
+     */
+    public function __construct(Horde_Imap_Client_Tokenize $token)
+    {
+        $this->token = $token;
+
+        /* Check for response status. */
+        $status = $token->current();
+        $valid = array('BAD', 'BYE', 'NO', 'OK', 'PREAUTH');
+
+        if (in_array($status, $valid)) {
+            $this->status = constant(__CLASS__ . '::' . $status);
+            $resp_text = $token->next();
+
+            /* Check for response code. Only occurs if there is a response
+             * status. */
+            if (is_string($resp_text) && ($resp_text[0] == '[')) {
+                $resp = new stdClass;
+                $resp->data = array();
+
+                if ($resp_text[strlen($resp_text) - 1] == ']') {
+                    $resp->code = substr($resp_text, 1, -1);
+                } else {
+                    $resp->code = substr($resp_text, 1);
+
+                    while (($elt = $token->next()) !== false) {
+                        if (is_string($elt) && $elt[strlen($elt) - 1] == ']') {
+                            $resp->data[] = substr($elt, 0, -1);
+                            break;
+                        }
+                        $resp->data[] = is_string($elt)
+                            ? $elt
+                            : $token->flushIterator();
+                    }
+                }
+
+                $token->next();
+                $this->responseCode = $resp;
+            }
+        }
+    }
+
+    /**
+     */
+    public function __toString()
+    {
+        return strval($this->token);
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientMailboxListphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientMailboxListphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Mailbox/List.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Mailbox/List.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Mailbox/List.php                            (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Mailbox/List.php       2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,158 @@
</span><ins>+<?php
+/**
+ * Copyright 2004-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2004-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Container of IMAP mailboxes.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2004-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Mailbox_List implements Countable, IteratorAggregate
+{
+    /**
+     * The delimiter character to use.
+     *
+     * @var string
+     */
+    protected $_delimiter;
+
+    /**
+     * Mailbox list.
+     *
+     * @var array
+     */
+    protected $_mboxes = array();
+
+    /**
+     * Should we sort with INBOX at the front of the list?
+     *
+     * @var boolean
+     */
+    protected $_sortinbox;
+
+    /**
+     * Constructor.
+     *
+     * @param mixed $mboxes  A mailbox or list of mailboxes.
+     */
+    public function __construct($mboxes)
+    {
+        $this->_mboxes = is_array($mboxes)
+            ? $mboxes
+            : array($mboxes);
+    }
+
+    /**
+     * Sort the list of mailboxes.
+     *
+     * @param array $opts  Options:
+     *   - delimiter: (string) The delimiter to use.
+     *                DEFAULT: '.'
+     *   - inbox: (boolean) Always put INBOX at the head of the list?
+     *            DEFAULT: Yes
+     *   - noupdate: (boolean) Do not update the object's mailbox list?
+     *               DEFAULT: true
+     *
+     * @return array  List of sorted mailboxes (index association is kept).
+     */
+    public function sort(array $opts = array())
+    {
+        $this->_delimiter = isset($opts['delimiter'])
+            ? $opts['delimiter']
+            : '.';
+        $this->_sortinbox = (!isset($opts['inbox']) || !empty($opts['inbox']));
+
+        if (empty($opts['noupdate'])) {
+            $mboxes = &$this->_mboxes;
+        } else {
+            $mboxes = $this->_mboxes;
+        }
+
+        uasort($mboxes, array($this, '_mboxCompare'));
+
+        return $mboxes;
+    }
+
+    /**
+     * Hierarchical folder sorting function (used with usort()).
+     *
+     * @param string $a  Comparison item 1.
+     * @param string $b  Comparison item 2.
+     *
+     * @return integer  See usort().
+     */
+    protected final function _mboxCompare($a, $b)
+    {
+        /* Always return INBOX as "smaller". */
+        if ($this->_sortinbox) {
+            if (strcasecmp($a, 'INBOX') == 0) {
+                return -1;
+            } elseif (strcasecmp($b, 'INBOX') == 0) {
+                return 1;
+            }
+        }
+
+        $a_parts = explode($this->_delimiter, $a);
+        $b_parts = explode($this->_delimiter, $b);
+
+        $a_count = count($a_parts);
+        $b_count = count($b_parts);
+
+        for ($i = 0, $iMax = min($a_count, $b_count); $i < $iMax; ++$i) {
+            if ($a_parts[$i] != $b_parts[$i]) {
+                /* If only one of the folders is under INBOX, return it as
+                 * "smaller". */
+                if ($this->_sortinbox && ($i == 0)) {
+                    $a_base = (strcasecmp($a_parts[0], 'INBOX') == 0);
+                    $b_base = (strcasecmp($b_parts[0], 'INBOX') == 0);
+                    if ($a_base && !$b_base) {
+                        return -1;
+                    } elseif (!$a_base && $b_base) {
+                        return 1;
+                    }
+                }
+
+                $cmp = strnatcasecmp($a_parts[$i], $b_parts[$i]);
+                return ($cmp == 0)
+                    ? strcmp($a_parts[$i], $b_parts[$i])
+                    : $cmp;
+            } elseif ($a_parts[$i] !== $b_parts[$i]) {
+                return strlen($a_parts[$i]) - strlen($b_parts[$i]);
+            }
+        }
+
+        return ($a_count - $b_count);
+    }
+
+    /* Countable methods. */
+
+    /**
+     */
+    public function count()
+    {
+        return count($this->_mboxes);
+    }
+
+    /* IteratorAggregate methods. */
+
+    /**
+     */
+    public function getIterator()
+    {
+        return new ArrayIterator($this->_mboxes);
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientMailboxphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientMailboxphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Mailbox.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Mailbox.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Mailbox.php                         (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Mailbox.php    2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,142 @@
</span><ins>+<?php
+/**
+ * Copyright 2011-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2011-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * An object that provides a way to switch between UTF7-IMAP and
+ * human-readable representations of a mailbox name.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2011-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ *
+ * @property-read string $list_escape  Escapes mailbox for use in LIST
+ *                                     command (UTF-8).
+ * @property-read string $utf7imap  Mailbox in UTF7-IMAP.
+ * @property-read string $utf8  Mailbox in UTF-8.
+ */
+class Horde_Imap_Client_Mailbox implements Serializable
+{
+    /**
+     * UTF7-IMAP representation of mailbox.
+     * If boolean true, it is identical to UTF-8 representation.
+     *
+     * @var mixed
+     */
+    protected $_utf7imap;
+
+    /**
+     * UTF8 representation of mailbox.
+     *
+     * @var string
+     */
+    protected $_utf8;
+
+    /**
+     * Shortcut to obtaining mailbox object.
+     *
+     * @param string $mbox       The mailbox name.
+     * @param boolean $utf7imap  Is mailbox UTF7-IMAP encoded? Otherwise,
+     *                           mailbox is assumed to be UTF-8.
+     *
+     * @return Horde_Imap_Client_Mailbox  A mailbox object.
+     */
+    static public function get($mbox, $utf7imap = false)
+    {
+        return ($mbox instanceof Horde_Imap_Client_Mailbox)
+            ? $mbox
+            : new Horde_Imap_Client_Mailbox($mbox, $utf7imap);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param string $mbox     The mailbox name.
+     * @param mixed $utf7imap  Is mailbox UTF7-IMAP encoded (true). Otherwise,
+     *                         mailbox is assumed to be UTF-8 encoded.
+     */
+    public function __construct($mbox, $utf7imap = false)
+    {
+        if ($utf7imap) {
+            $this->_utf7imap = $mbox;
+        } else {
+            $this->_utf8 = $mbox;
+        }
+    }
+
+    /**
+     */
+    public function __get($name)
+    {
+        switch ($name) {
+        case 'list_escape':
+            return preg_replace("/\*+/", '%', $this->utf8);
+
+        case 'utf7imap':
+            if (!isset($this->_utf7imap)) {
+                $n = Horde_Imap_Client_Utf7imap::Utf8ToUtf7Imap($this->_utf8);
+                $this->_utf7imap = ($n == $this->_utf8)
+                    ? true
+                    : $n;
+            }
+
+            return ($this->_utf7imap === true)
+                ? $this->_utf8
+                : $this->_utf7imap;
+
+        case 'utf8':
+            if (!isset($this->_utf8)) {
+                $this->_utf8 = Horde_Imap_Client_Utf7imap::Utf7ImapToUtf8($this->_utf7imap);
+                if ($this->_utf8 == $this->_utf7imap) {
+                    $this->_utf7imap = true;
+                }
+            }
+            return $this->_utf8;
+        }
+    }
+
+    /**
+     */
+    public function __toString()
+    {
+        return $this->utf8;
+    }
+
+    /**
+     * Compares this mailbox to another mailbox string.
+     *
+     * @return boolean  True if the items are equal.
+     */
+    public function equals($mbox)
+    {
+        return ($this->utf8 == $mbox);
+    }
+
+    /* Serializable methods. */
+
+    /**
+     */
+    public function serialize()
+    {
+        return json_encode(array($this->_utf7imap, $this->_utf8));
+    }
+
+    /**
+     */
+    public function unserialize($data)
+    {
+        list($this->_utf7imap, $this->_utf8) = json_decode($data, true);
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientSearchQueryphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientSearchQueryphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Search/Query.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Search/Query.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Search/Query.php                            (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Search/Query.php       2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,745 @@
</span><ins>+<?php
+/**
+ * Copyright 2008-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2008-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Abstraction of the IMAP4rev1 search criteria (see RFC 3501 [6.4.4]).
+ * Allows translation between abstracted search criteria and a generated IMAP
+ * search criteria string suitable for sending to a remote IMAP server.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2008-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Search_Query implements Serializable
+{
+    /* Serialized version. */
+    const VERSION = 3;
+
+    /* Constants for dateSearch() */
+    const DATE_BEFORE = 'BEFORE';
+    const DATE_ON = 'ON';
+    const DATE_SINCE = 'SINCE';
+
+    /* Constants for intervalSearch() */
+    const INTERVAL_OLDER = 'OLDER';
+    const INTERVAL_YOUNGER = 'YOUNGER';
+
+    /**
+     * The charset of the search strings.  All text strings must be in
+     * this charset. By default, this is 'US-ASCII' (see RFC 3501 [6.4.4]).
+     *
+     * @var string
+     */
+    protected $_charset = null;
+
+    /**
+     * The list of search params.
+     *
+     * @var array
+     */
+    protected $_search = array();
+
+    /**
+     * String representation: The IMAP search string.
+     */
+    public function __toString()
+    {
+        $res = $this->build(null);
+        return $res['query']->escape();
+    }
+
+    /**
+     * Sets the charset of the search text.
+     *
+     * @param string $charset   The charset to use for the search.
+     * @param boolean $convert  Convert existing text values?
+     *
+     * @throws Horde_Imap_Client_Exception_SearchCharset
+     */
+    public function charset($charset, $convert = true)
+    {
+        $oldcharset = $this->_charset;
+        $this->_charset = strtoupper($charset);
+
+        if (!$convert || ($oldcharset == $this->_charset)) {
+            return;
+        }
+
+        foreach (array('header', 'text') as $item) {
+            if (isset($this->_search[$item])) {
+                foreach ($this->_search[$item] as $key => $val) {
+                    $new_val = Horde_String::convertCharset($val['text'], $oldcharset, $this->_charset);
+                    if (Horde_String::convertCharset($new_val, $this->_charset, $oldcharset) != $val['text']) {
+                        throw new Horde_Imap_Client_Exception_SearchCharset($this->_charset);
+                    }
+                    $this->_search[$item][$key]['text'] = $new_val;
+                }
+            }
+        }
+    }
+
+    /**
+     * Builds an IMAP4rev1 compliant search string.
+     *
+     * @param array $exts  The list of extensions supported by the server.
+     *                     This determines whether certain criteria can be
+     *                     used, and determines whether workarounds are used
+     *                     for other criteria. In the format returned by
+     *                     Horde_Imap_Client_Base::capability(). If this value
+     *                     is null, all extensions are assumed to be
+     *                     available.
+     *
+     * @return array  An array with these elements:
+     *   - charset: (string) The charset of the search string. If null, no
+     *              text strings appear in query.
+     *   - exts: (array) The list of IMAP extensions used to create the
+     *           string.
+     *   - query: (Horde_Imap_Client_Data_Format_List) The IMAP search
+     *            command.
+     *
+     * @throws Horde_Imap_Client_Exception_NoSupportExtension
+     */
+    public function build($exts = array())
+    {
+        $temp = array(
+            'cmds' => new Horde_Imap_Client_Data_Format_List(),
+            'exts' => $exts,
+            'exts_used' => array()
+        );
+        $cmds = &$temp['cmds'];
+        $charset = null;
+        $exts_used = &$temp['exts_used'];
+        $ptr = &$this->_search;
+
+        if (isset($ptr['new'])) {
+            $this->_addFuzzy(!empty($ptr['newfuzzy']), $temp);
+            if ($ptr['new']) {
+                $cmds->add('NEW');
+                unset($ptr['flag']['UNSEEN']);
+            } else {
+                $cmds->add('OLD');
+            }
+            unset($ptr['flag']['RECENT']);
+        }
+
+        if (!empty($ptr['flag'])) {
+            foreach ($ptr['flag'] as $key => $val) {
+                $this->_addFuzzy(!empty($val['fuzzy']), $temp);
+
+                $tmp = '';
+                if (empty($val['set'])) {
+                    // This is a 'NOT' search.  All system flags but \Recent
+                    // have 'UN' equivalents.
+                    if ($key == 'RECENT') {
+                        $cmds->add('NOT');
+                    } else {
+                        $tmp = 'UN';
+                    }
+                }
+
+                if ($val['type'] == 'keyword') {
+                    $cmds->add(array(
+                        $tmp . 'KEYWORD',
+                        $key
+                    ));
+                } else {
+                    $cmds->add($tmp . $key);
+                }
+            }
+        }
+
+        if (!empty($ptr['header'])) {
+            /* The list of 'system' headers that have a specific search
+             * query. */
+            $systemheaders = array(
+                'BCC', 'CC', 'FROM', 'SUBJECT', 'TO'
+            );
+
+            foreach ($ptr['header'] as $val) {
+                $this->_addFuzzy(!empty($val['fuzzy']), $temp);
+
+                if (!empty($val['not'])) {
+                    $cmds->add('NOT');
+                }
+
+                if (in_array($val['header'], $systemheaders)) {
+                    $cmds->add($val['header']);
+                } else {
+                    $cmds->add(array(
+                        'HEADER',
+                        new Horde_Imap_Client_Data_Format_Astring($val['header'])
+                    ));
+                }
+                $cmds->add(new Horde_Imap_Client_Data_Format_Astring(isset($val['text']) ? $val['text'] : ''));
+                $charset = is_null($this->_charset)
+                    ? 'US-ASCII'
+                    : $this->_charset;
+            }
+        }
+
+        if (!empty($ptr['text'])) {
+            foreach ($ptr['text'] as $val) {
+                $this->_addFuzzy(!empty($val['fuzzy']), $temp);
+
+                if (!empty($val['not'])) {
+                    $cmds->add('NOT');
+                }
+                $cmds->add(array(
+                    $val['type'],
+                    new Horde_Imap_Client_Data_Format_Astring($val['text'])
+                ));
+                if (is_null($charset)) {
+                    $charset = is_null($this->_charset)
+                        ? 'US-ASCII'
+                        : $this->_charset;
+                }
+            }
+        }
+
+        if (!empty($ptr['size'])) {
+            foreach ($ptr['size'] as $key => $val) {
+                $this->_addFuzzy(!empty($val['fuzzy']), $temp);
+                if (!empty($val['not'])) {
+                    $cmds->add('NOT');
+                }
+                $cmds->add(array(
+                    $key,
+                    new Horde_Imap_Client_Data_Format_Number($val['size'])
+                ));
+            }
+        }
+
+        if (isset($ptr['ids']) &&
+            (count($ptr['ids']['ids']) || $ptr['ids']['ids']->special)) {
+            $this->_addFuzzy(!empty($val['fuzzy']), $temp);
+            if (!empty($ptr['ids']['not'])) {
+                $cmds->add('NOT');
+            }
+            if (!$ptr['ids']['ids']->sequence) {
+                $cmds->add('UID');
+            }
+            $cmds->add(strval($ptr['ids']['ids']));
+        }
+
+        if (!empty($ptr['date'])) {
+            foreach ($ptr['date'] as $val) {
+                $this->_addFuzzy(!empty($val['fuzzy']), $temp);
+
+                if (!empty($val['not'])) {
+                    $cmds->add('NOT');
+                }
+
+                if (empty($val['header'])) {
+                    $cmds->add($val['range']);
+                } else {
+                    $cmds->add('SENT' . $val['range']);
+                }
+                $cmds->add($val['date']);
+            }
+        }
+
+        if (!empty($ptr['within'])) {
+            if (is_null($exts) || isset($exts['WITHIN'])) {
+                $exts_used[] = 'WITHIN';
+            }
+
+            foreach ($ptr['within'] as $key => $val) {
+                $this->_addFuzzy(!empty($val['fuzzy']), $temp);
+                if (!empty($val['not'])) {
+                    $cmds->add('NOT');
+                }
+
+                if (is_null($exts) || isset($exts['WITHIN'])) {
+                    $cmds->add(array(
+                        $key,
+                        new Horde_Imap_Client_Data_Format_Number($val['interval'])
+                    ));
+                } else {
+                    // This workaround is only accurate to within 1 day, due
+                    // to limitations with the IMAP4rev1 search commands.
+                    $cmds->add(array(
+                        ($key == self::INTERVAL_OLDER) ? self::DATE_BEFORE : self::DATE_SINCE,
+                        new Horde_Imap_Client_Data_Format_Date('now -' . $val['interval'] . ' seconds')
+                    ));
+                }
+            }
+        }
+
+        if (!empty($ptr['modseq'])) {
+            if (!is_null($exts) && !isset($exts['CONDSTORE'])) {
+                throw new Horde_Imap_Client_Exception_NoSupportExtension('IMAP Server does not support CONDSTORE.');
+            }
+
+            $exts_used[] = 'CONDSTORE';
+
+            $this->_addFuzzy(!empty($ptr['modseq']['fuzzy']), $temp);
+
+            if (!empty($ptr['modseq']['not'])) {
+                $cmds->add('NOT');
+            }
+            $cmds->add('MODSEQ');
+            if (isset($ptr['modseq']['name'])) {
+                $cmds->add(array(
+                    new Horde_Imap_Client_Data_Format_String($ptr['modseq']['name']),
+                    $ptr['modseq']['type']
+                ));
+            }
+            $cmds->add(new Horde_Imap_Client_Data_Format_Number($ptr['modseq']['value']));
+        }
+
+        if (isset($ptr['prevsearch'])) {
+            if (!is_null($exts) && !isset($exts['SEARCHRES'])) {
+                throw new Horde_Imap_Client_Exception_NoSupportExtension('IMAP Server does not support SEARCHRES.');
+            }
+
+            $exts_used[] = 'SEARCHRES';
+
+            $this->_addFuzzy(!empty($ptr['prevsearchfuzzy']), $temp);
+
+            if (!$ptr['prevsearch']) {
+                $cmds->add('NOT');
+            }
+            $cmds->add('$');
+        }
+
+        // Add AND'ed queries
+        if (!empty($ptr['and'])) {
+            foreach ($ptr['and'] as $val) {
+                $ret = $val->build();
+                if ($ret['charset'] != 'US-ASCII') {
+                    $charset = $ret['charset'];
+                }
+                $exts_used = array_merge($exts_used, $ret['exts']);
+                $cmds->add($ret['query'], true);
+            }
+        }
+
+        // Add OR'ed queries
+        if (!empty($ptr['or'])) {
+            foreach ($ptr['or'] as $val) {
+                $ret = $val->build();
+
+                if ($ret['charset'] != 'US-ASCII') {
+                    $charset = $ret['charset'];
+                }
+                $exts_used = array_merge($exts_used, $ret['exts']);
+
+                // First OR'd query
+                if (count($cmds)) {
+                    $new_cmds = new Horde_Imap_Client_Data_Format_List();
+                    $new_cmds->add(array(
+                        'OR',
+                        $ret['query'],
+                        $cmds
+                    ));
+                    $cmds = $new_cmds;
+                } else {
+                    $cmds = $ret['query'];
+                }
+            }
+        }
+
+        // Default search is 'ALL'
+        if (!count($cmds)) {
+            $cmds->add('ALL');
+        }
+
+        return array(
+            'charset' => $charset,
+            'exts' => array_keys(array_flip($exts_used)),
+            'query' => $cmds
+        );
+    }
+
+    /**
+     * Adds fuzzy modifier to search keys.
+     *
+     * @param boolean $add  Add the fuzzy modifier?
+     * @param array $temp   Temporary build data.
+     *
+     * @throws Horde_Imap_Client_Exception_NoSupport_Extension
+     */
+    protected function _addFuzzy($add, &$temp)
+    {
+        if ($add) {
+            if (!isset($temp['exts']['SEARCH']) ||
+                !in_array('FUZZY', $temp['exts']['SEARCH'])) {
+                throw new Horde_Imap_Client_Exception_NoSupportExtension('IMAP Server does not support SEARCH=FUZZY.');
+            }
+            $temp['cmds']->add('FUZZY');
+            $temp['exts_used'][] = 'SEARCH=FUZZY';
+        }
+    }
+
+    /**
+     * Search for a flag/keywords.
+     *
+     * @param string $name  The flag or keyword name.
+     * @param boolean $set  If true, search for messages that have the flag
+     *                      set.  If false, search for messages that do not
+     *                      have the flag set.
+     * @param array $opts   Additional options:
+     *   - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
+     *            MUST support RFC 6203.
+     */
+    public function flag($name, $set = true, array $opts = array())
+    {
+        $name = strtoupper(ltrim($name, '\\'));
+        if (!isset($this->_search['flag'])) {
+            $this->_search['flag'] = array();
+        }
+
+        /* The list of defined system flags (see RFC 3501 [2.3.2]). */
+        $systemflags = array(
+            'ANSWERED', 'DELETED', 'DRAFT', 'FLAGGED', 'RECENT', 'SEEN'
+        );
+
+        $this->_search['flag'][$name] = array_filter(array(
+            'fuzzy' => !empty($opts['fuzzy']),
+            'set' => $set,
+            'type' => in_array($name, $systemflags) ? 'flag' : 'keyword'
+        ));
+    }
+
+    /**
+     * Determines if flags are a part of the search.
+     *
+     * @return boolean  True if search query involves flags.
+     */
+    public function flagSearch()
+    {
+        return !empty($this->_search['flag']);
+    }
+
+    /**
+     * Search for either new messages (messages that have the '\Recent' flag
+     * but not the '\Seen' flag) or old messages (messages that do not have
+     * the '\Recent' flag).  If new messages are searched, this will clear
+     * any '\Recent' or '\Unseen' flag searches.  If old messages are searched,
+     * this will clear any '\Recent' flag search.
+     *
+     * @param boolean $newmsgs  If true, searches for new messages.  Else,
+     *                          search for old messages.
+     * @param array $opts       Additional options:
+     *   - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
+     *            MUST support RFC 6203.
+     */
+    public function newMsgs($newmsgs = true, array $opts = array())
+    {
+        $this->_search['new'] = $newmsgs;
+        if (!empty($opts['fuzzy'])) {
+            $this->_search['newfuzzy'] = true;
+        }
+    }
+
+    /**
+     * Search for text in the header of a message.
+     *
+     * @param string $header  The header field.
+     * @param string $text    The search text.
+     * @param boolean $not    If true, do a 'NOT' search of $text.
+     * @param array $opts     Additional options:
+     *   - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
+     *            MUST support RFC 6203.
+     */
+    public function headerText($header, $text, $not = false,
+                                array $opts = array())
+    {
+        if (!isset($this->_search['header'])) {
+            $this->_search['header'] = array();
+        }
+        $this->_search['header'][] = array_filter(array(
+            'fuzzy' => !empty($opts['fuzzy']),
+            'header' => strtoupper($header),
+            'text' => $text,
+            'not' => $not
+        ));
+    }
+
+    /**
+     * Search for text in either the entire message, or just the body.
+     *
+     * @param string $text      The search text.
+     * @param string $bodyonly  If true, only search in the body of the
+     *                          message. If false, also search in the headers.
+     * @param boolean $not      If true, do a 'NOT' search of $text.
+     * @param array $opts       Additional options:
+     *   - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
+     *            MUST support RFC 6203.
+     */
+    public function text($text, $bodyonly = true, $not = false,
+                         array $opts = array())
+    {
+        if (!isset($this->_search['text'])) {
+            $this->_search['text'] = array();
+        }
+
+        $this->_search['text'][] = array_filter(array(
+            'fuzzy' => !empty($opts['fuzzy']),
+            'not' => $not,
+            'text' => $text,
+            'type' => $bodyonly ? 'BODY' : 'TEXT'
+        ));
+    }
+
+    /**
+     * Search for messages smaller/larger than a certain size.
+     *
+     * @param integer $size    The size (in bytes).
+     * @param boolean $larger  Search for messages larger than $size?
+     * @param boolean $not     If true, do a 'NOT' search of $text.
+     * @param array $opts      Additional options:
+     *   - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
+     *            MUST support RFC 6203.
+     */
+    public function size($size, $larger = false, $not = false,
+                         array $opts = array())
+    {
+        if (!isset($this->_search['size'])) {
+            $this->_search['size'] = array();
+        }
+        $this->_search['size'][$larger ? 'LARGER' : 'SMALLER'] = array_filter(array(
+            'fuzzy' => !empty($opts['fuzzy']),
+            'not' => $not,
+            'size' => (float)$size
+        ));
+    }
+
+    /**
+     * Search for messages within a given ID sequence range. Only one message
+     * range can be specified per query.
+     *
+     * @param Horde_Imap_Client_Ids $ids  The list of IDs to search.
+     * @param boolean $not                If true, do a 'NOT' search of the
+     *                                    IDs.
+     * @param array $opts                 Additional options:
+     *   - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
+     *            MUST support RFC 6203.
+     */
+    public function ids(Horde_Imap_Client_Ids $ids, $not = false,
+                        array $opts = array())
+    {
+        if (!$ids->isEmpty()) {
+            $this->_search['ids'] = array_filter(array(
+                'fuzzy' => !empty($opts['fuzzy']),
+                'ids' => $ids,
+                'not' => $not
+            ));
+        }
+    }
+
+    /**
+     * Search for messages within a date range.
+     *
+     * @param mixed $date    DateTime or Horde_Date object.
+     * @param string $range  Either:
+     *   - Horde_Imap_Client_Search_Query::DATE_BEFORE
+     *   - Horde_Imap_Client_Search_Query::DATE_ON
+     *   - Horde_Imap_Client_Search_Query::DATE_SINCE
+     * @param boolean $header  If true, search using the date in the message
+     *                         headers. If false, search using the internal
+     *                         IMAP date (usually arrival time).
+     * @param boolean $not     If true, do a 'NOT' search of the range.
+     * @param array $opts      Additional options:
+     *   - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
+     *            MUST support RFC 6203.
+     */
+    public function dateSearch($date, $range, $header = true, $not = false,
+                               array $opts = array())
+    {
+        if (!isset($this->_search['date'])) {
+            $this->_search['date'] = array();
+        }
+
+        // We should really be storing the raw DateTime object as data,
+        // but all versions of the query object have converted at this stage.
+        $ob = new Horde_Imap_Client_Data_Format_Date($date);
+
+        $this->_search['date'][] = array_filter(array(
+            'date' => $ob->escape(),
+            'fuzzy' => !empty($opts['fuzzy']),
+            'header' => $header,
+            'range' => $range,
+            'not' => $not
+        ));
+    }
+
+    /**
+     * Search for messages within a given interval. Only one interval of each
+     * type can be specified per search query. If the IMAP server supports
+     * the WITHIN extension (RFC 5032), it will be used.  Otherwise, the
+     * search query will be dynamically created using IMAP4rev1 search
+     * terms.
+     *
+     * @param integer $interval  Seconds from the present.
+     * @param string $range      Either:
+     *   - Horde_Imap_Client_Search_Query::INTERVAL_OLDER
+     *   - Horde_Imap_Client_Search_Query::INTERVAL_YOUNGER
+     * @param boolean $not       If true, do a 'NOT' search.
+     * @param array $opts        Additional options:
+     *   - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
+     *            MUST support RFC 6203.
+     */
+    public function intervalSearch($interval, $range, $not = false,
+                                   array $opts = array())
+    {
+        if (!isset($this->_search['within'])) {
+            $this->_search['within'] = array();
+        }
+        $this->_search['within'][$range] = array(
+            'fuzzy' => !empty($opts['fuzzy']),
+            'interval' => $interval,
+            'not' => $not
+        );
+    }
+
+    /**
+     * AND queries - the contents of this query will be AND'ed (in its
+     * entirety) with the contents of EACH of the queries passed in.  All
+     * AND'd queries must share the same charset as this query.
+     *
+     * @param mixed $queries  A query, or an array of queries, to AND with the
+     *                        current query.
+     */
+    public function andSearch($queries)
+    {
+        if (!isset($this->_search['and'])) {
+            $this->_search['and'] = array();
+        }
+
+        if ($queries instanceof Horde_Imap_Client_Search_Query) {
+            $queries = array($queries);
+        }
+
+        $this->_search['and'] = array_merge($this->_search['and'], $queries);
+    }
+
+    /**
+     * OR a query - the contents of this query will be OR'ed (in its entirety)
+     * with the contents of EACH of the queries passed in.  All OR'd queries
+     * must share the same charset as this query.  All contents of any single
+     * query will be AND'ed together.
+     *
+     * @param mixed $queries  A query, or an array of queries, to OR with the
+     *                        current query.
+     */
+    public function orSearch($queries)
+    {
+        if (!isset($this->_search['or'])) {
+            $this->_search['or'] = array();
+        }
+
+        if ($queries instanceof Horde_Imap_Client_Search_Query) {
+            $queries = array($queries);
+        }
+
+        $this->_search['or'] = array_merge($this->_search['or'], $queries);
+    }
+
+    /**
+     * Search for messages modified since a specific moment. The IMAP server
+     * must support the CONDSTORE extension (RFC 4551) for this query to be
+     * used.
+     *
+     * @param integer $value  The mod-sequence value.
+     * @param string $name    The entry-name string.
+     * @param string $type    Either 'shared', 'priv', or 'all'. Defaults to
+     *                        'all'
+     * @param boolean $not    If true, do a 'NOT' search.
+     * @param array $opts     Additional options:
+     *   - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
+     *            MUST support RFC 6203.
+     */
+    public function modseq($value, $name = null, $type = null, $not = false,
+                           array $opts = array())
+    {
+        if (!is_null($type)) {
+            $type = strtolower($type);
+            if (!in_array($type, array('shared', 'priv', 'all'))) {
+                $type = 'all';
+            }
+        }
+
+        $this->_search['modseq'] = array_filter(array(
+            'fuzzy' => !empty($opts['fuzzy']),
+            'name' => $name,
+            'not' => $not,
+            'type' => (!is_null($name) && is_null($type)) ? 'all' : $type,
+            'value' => $value
+        ));
+    }
+
+    /**
+     * Use the results from the previous SEARCH command. The IMAP server must
+     * support the SEARCHRES extension (RFC 5182) for this query to be used.
+     *
+     * @param boolean $not  If true, don't match the previous query.
+     * @param array $opts   Additional options:
+     *   - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
+     *            MUST support RFC 6203.
+     */
+    public function previousSearch($not = false, array $opts = array())
+    {
+        $this->_search['prevsearch'] = $not;
+        if (!empty($opts['fuzzy'])) {
+            $this->_search['prevsearchfuzzy'] = true;
+        }
+    }
+
+    /* Serializable methods. */
+
+    /**
+     * Serialization.
+     *
+     * @return string  Serialized data.
+     */
+    public function serialize()
+    {
+        $data = array(
+            // Serialized data ID.
+            self::VERSION,
+            $this->_search
+        );
+
+        if (!is_null($this->_charset)) {
+            $data[] = $this->_charset;
+        }
+
+        return serialize($data);
+    }
+
+    /**
+     * Unserialization.
+     *
+     * @param string $data  Serialized data.
+     *
+     * @throws Exception
+     */
+    public function unserialize($data)
+    {
+        $data = @unserialize($data);
+        if (!is_array($data) ||
+            !isset($data[0]) ||
+            ($data[0] != self::VERSION)) {
+            throw new Exception('Cache version change');
+        }
+
+        $this->_search = $data[1];
+        if (isset($data[2])) {
+            $this->_charset = $data[2];
+        }
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientSocketCatenatephpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientSocketCatenatephp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Socket/Catenate.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Socket/Catenate.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Socket/Catenate.php                         (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Socket/Catenate.php    2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,167 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Methods for the Socket driver used for a CATENATE command.
+ *
+ * NOTE: This class is NOT intended to be accessed outside of a Base object.
+ * There is NO guarantees that the API of this class will not change across
+ * versions.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @internal
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Socket_Catenate
+{
+    /**
+     * Socket object.
+     *
+     * @var Horde_Imap_Client_Socket
+     */
+    protected $_socket;
+
+    /**
+     * Constructor.
+     *
+     * @param Horde_Imap_Client_Socket $socket  Socket object.
+     */
+    public function __construct(Horde_Imap_Client_Socket $socket)
+    {
+        $this->_socket = $socket;
+    }
+
+    /**
+     * Given an IMAP URL, fetches the corresponding part.
+     *
+     * @param Horde_Imap_Client_Url $url  An IMAP URL.
+     *
+     * @return resource  The section contents in a stream. Returns null if
+     *                   the part could not be found.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function fetchFromUrl(Horde_Imap_Client_Url $url)
+    {
+        $ids_ob = $this->_socket->getIdsOb($url->uid);
+
+        // BODY[]
+        if (is_null($url->section)) {
+            $query = new Horde_Imap_Client_Fetch_Query();
+            $query->fullText(array(
+                'peek' => true
+            ));
+
+            $fetch = $this->_socket->fetch($url->mailbox, $query, array(
+                'ids' => $ids_ob
+            ));
+            return $fetch[$url->uid]->getFullMsg(true);
+        }
+
+        $section = trim($url->section);
+
+        // BODY[<#.>HEADER.FIELDS<.NOT>()]
+        if (($pos = stripos($section, 'HEADER.FIELDS')) !== false) {
+            $hdr_pos = strpos($section, '(');
+            $cmd = substr($section, 0, $hdr_pos);
+
+            $query = new Horde_Imap_Client_Fetch_Query();
+            $query->headers(
+                'section',
+                explode(' ', substr($section, $hdr_pos + 1, strrpos($section, ')') - $hdr_pos)),
+                array(
+                    'id' => ($pos ? substr($section, 0, $pos - 1) : 0),
+                    'notsearch' => (stripos($cmd, '.NOT') !== false),
+                    'peek' => true
+                )
+            );
+
+            $fetch = $this->_socket->fetch($url->mailbox, $query, array(
+                'ids' => $ids_ob
+            ));
+            return $fetch[$url->uid]->getHeaders('section', Horde_Imap_Client_Data_Fetch::HEADER_STREAM);
+        }
+
+        // BODY[#]
+        if (is_numeric(substr($section, -1))) {
+            $query = new Horde_Imap_Client_Fetch_Query();
+            $query->bodyPart($section, array(
+                'peek' => true
+            ));
+
+            $fetch = $this->_socket->fetch($url->mailbox, $query, array(
+                'ids' => $ids_ob
+            ));
+            return $fetch[$url->uid]->getBodyPart($section, true);
+        }
+
+        // BODY[<#.>HEADER]
+        if (($pos = stripos($section, 'HEADER')) !== false) {
+            $id = $pos
+                ? substr($section, 0, $pos - 1)
+                : 0;
+
+            $query = new Horde_Imap_Client_Fetch_Query();
+            $query->headerText(array(
+                'id' => $id,
+                'peek' => true
+            ));
+
+            $fetch = $this->_socket->fetch($url->mailbox, $query, array(
+                'ids' => $ids_ob
+            ));
+            return $fetch[$url->uid]->getHeaderText($id, Horde_Imap_Client_Data_Fetch::HEADER_STREAM);
+        }
+
+        // BODY[<#.>TEXT]
+        if (($pos = stripos($section, 'TEXT')) !== false) {
+            $id = $pos
+                ? substr($section, 0, $pos - 1)
+                : 0;
+
+            $query = new Horde_Imap_Client_Fetch_Query();
+            $query->bodyText(array(
+                'id' => $id,
+                'peek' => true
+            ));
+
+            $fetch = $this->_socket->fetch($url->mailbox, $query, array(
+                'ids' => $ids_ob
+            ));
+            return $fetch[$url->uid]->getBodyText($id, true);
+        }
+
+        // BODY[<#.>MIMEHEADER]
+        if (($pos = stripos($section, 'MIME')) !== false) {
+            $id = $pos
+                ? substr($section, 0, $pos - 1)
+                : 0;
+
+            $query = new Horde_Imap_Client_Fetch_Query();
+            $query->mimeHeader($id, array(
+                'peek' => true
+            ));
+
+            $fetch = $this->_socket->fetch($url->mailbox, $query, array(
+                'ids' => $ids_ob
+            ));
+            return $fetch[$url->uid]->getMimeHeader($id, Horde_Imap_Client_Data_Fetch::HEADER_STREAM);
+        }
+
+        return null;
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientSocketClientSortphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientSocketClientSortphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Socket/ClientSort.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Socket/ClientSort.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Socket/ClientSort.php                               (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Socket/ClientSort.php  2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,317 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Client sorting methods for the Socket driver.
+ *
+ * NOTE: This class is NOT intended to be accessed outside of a Base object.
+ * There is NO guarantees that the API of this class will not change across
+ * versions.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @internal
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Socket_ClientSort
+{
+    /**
+     * Socket object.
+     *
+     * @var Horde_Imap_Client_Socket
+     */
+    protected $_socket;
+
+    /**
+     * Constructor.
+     *
+     * @param Horde_Imap_Client_Socket $socket  Socket object.
+     */
+    public function __construct(Horde_Imap_Client_Socket $socket)
+    {
+        $this->_socket = $socket;
+    }
+
+    /**
+     * Sort search results client side if the server does not support the SORT
+     * IMAP extension (RFC 5256).
+     *
+     * @param Horde_Imap_Client_Ids $res  The search results.
+     * @param array $opts                 The options to _search().
+     *
+     * @return array  The sort results.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function clientSort($res, $opts)
+    {
+        if (!count($res)) {
+            return $res;
+        }
+
+        /* Generate the FETCH command needed. */
+        $query = new Horde_Imap_Client_Fetch_Query();
+
+        foreach ($opts['sort'] as $val) {
+            switch ($val) {
+            case Horde_Imap_Client::SORT_ARRIVAL:
+                $query->imapDate();
+                break;
+
+            case Horde_Imap_Client::SORT_DATE:
+                $query->imapDate();
+                $query->envelope();
+                break;
+
+            case Horde_Imap_Client::SORT_CC:
+            case Horde_Imap_Client::SORT_DISPLAYFROM:
+            case Horde_Imap_Client::SORT_DISPLAYTO:
+            case Horde_Imap_Client::SORT_FROM:
+            case Horde_Imap_Client::SORT_SUBJECT:
+            case Horde_Imap_Client::SORT_TO:
+                $query->envelope();
+                break;
+
+            case Horde_Imap_Client::SORT_SIZE:
+                $query->size();
+                break;
+            }
+        }
+
+        if (!count($query)) {
+            return $res;
+        }
+
+        $mbox = $this->_socket->currentMailbox();
+        $fetch_res = $this->_socket->fetch($mbox['mailbox'], $query, array(
+            'ids' => $res
+        ));
+
+        return $this->_clientSortProcess($res->ids, $fetch_res, $opts['sort']);
+    }
+
+    /**
+     * If server does not support the THREAD IMAP extension (RFC 5256), do
+     * ORDEREDSUBJECT threading on the client side.
+     *
+     * @param Horde_Imap_Client_Fetch_Results $data  Fetch results.
+     * @param boolean $uids                          Are IDs UIDs?
+     *
+     * @return array  The thread sort results.
+     */
+    public function threadOrderedSubject(Horde_Imap_Client_Fetch_Results $data,
+                                         $uids)
+    {
+        $dates = $this->_getSentDates($data, $data->ids());
+        $out = $sorted = $tsort = array();
+
+        foreach ($data as $k => $v) {
+            $subject = strval(new Horde_Imap_Client_Data_BaseSubject($v->getEnvelope()->subject));
+            $sorted[$subject][$k] = $dates[$k];
+        }
+
+        /* Step 1: Sort by base subject (already done).
+         * Step 2: Sort by sent date within each thread. */
+        foreach (array_keys($sorted) as $key) {
+            asort($sorted[$key], SORT_NUMERIC);
+            $tsort[$key] = reset($sorted[$key]);
+        }
+
+        /* Step 3: Sort by the sent date of the first message in the
+         * thread. */
+        asort($tsort, SORT_NUMERIC);
+
+        /* Now, $tsort contains the order of the threads, and each thread
+         * is sorted in $sorted. */
+        foreach (array_keys($tsort) as $key) {
+            $keys = array_keys($sorted[$key]);
+            $out[$keys[0]] = array(
+                $keys[0] => 0
+            ) + array_fill_keys(array_slice($keys, 1) , 1);
+        }
+
+        return new Horde_Imap_Client_Data_Thread($out, $uids ? 'uid' : 'sequence');
+    }
+
+    /**
+     */
+    protected function _clientSortProcess($res, $fetch_res, $sort)
+    {
+        /* The initial sort is on the entire set. */
+        $slices = array(0 => $res);
+        $reverse = false;
+
+        foreach ($sort as $val) {
+            if ($val == Horde_Imap_Client::SORT_REVERSE) {
+                $reverse = true;
+                continue;
+            }
+
+            $slices_list = $slices;
+            $slices = array();
+
+            foreach ($slices_list as $slice_start => $slice) {
+                $sorted = array();
+
+                if ($reverse) {
+                    $slice = array_reverse($slice);
+                }
+
+                switch ($val) {
+                case Horde_Imap_Client::SORT_SEQUENCE:
+                    /* There is no requirement that IDs be returned in
+                     * sequence order (see RFC 4549 [4.3.1]). So we must sort
+                     * ourselves. */
+                    $sorted = array_flip($slice);
+                    ksort($sorted, SORT_NUMERIC);
+                    break;
+
+                case Horde_Imap_Client::SORT_SIZE:
+                    foreach ($slice as $num) {
+                        $sorted[$num] = $fetch_res[$num]->getSize();
+                    }
+                    asort($sorted, SORT_NUMERIC);
+                    break;
+
+                case Horde_Imap_Client::SORT_DISPLAYFROM:
+                case Horde_Imap_Client::SORT_DISPLAYTO:
+                    $field = ($val == Horde_Imap_Client::SORT_DISPLAYFROM)
+                        ? 'from'
+                        : 'to';
+
+                    foreach ($slice as $num) {
+                        $env = $fetch_res[$num]->getEnvelope();
+
+                        if (empty($env->$field)) {
+                            $sorted[$num] = null;
+                        } else {
+                            $addr_ob = reset($env->$field);
+                            if (is_null($sorted[$num] = $addr_ob->personal)) {
+                                $sorted[$num] = $addr_ob->mailbox;
+                            }
+                        }
+                    }
+
+                    asort($sorted, SORT_LOCALE_STRING);
+                    break;
+
+                case Horde_Imap_Client::SORT_CC:
+                case Horde_Imap_Client::SORT_FROM:
+                case Horde_Imap_Client::SORT_TO:
+                    if ($val == Horde_Imap_Client::SORT_CC) {
+                        $field = 'cc';
+                    } elseif ($val == Horde_Imap_Client::SORT_FROM) {
+                        $field = 'from';
+                    } else {
+                        $field = 'to';
+                    }
+
+                    foreach ($slice as $num) {
+                        $tmp = $fetch_res[$num]->getEnvelope()->$field;
+                        $sorted[$num] = count($tmp)
+                            ? $tmp[0]->mailbox
+                            : null;
+                    }
+                    asort($sorted, SORT_LOCALE_STRING);
+                    break;
+
+                case Horde_Imap_Client::SORT_ARRIVAL:
+                    $sorted = $this->_getSentDates($fetch_res, $slice, true);
+                    asort($sorted, SORT_NUMERIC);
+                    break;
+
+                case Horde_Imap_Client::SORT_DATE:
+                    // Date sorting rules in RFC 5256 [2.2]
+                    $sorted = $this->_getSentDates($fetch_res, $slice);
+                    asort($sorted, SORT_NUMERIC);
+                    break;
+
+                case Horde_Imap_Client::SORT_SUBJECT:
+                    // Subject sorting rules in RFC 5256 [2.1]
+                    foreach ($slice as $num) {
+                        $sorted[$num] = strval(new Horde_Imap_Client_Data_BaseSubject($fetch_res[$num]->getEnvelope()->subject));
+                    }
+                    asort($sorted, SORT_LOCALE_STRING);
+                    break;
+                }
+
+                // At this point, keys of $sorted are sequence/UID and values
+                // are the sort strings
+                if (!empty($sorted)) {
+                    if (count($sorted) == count($res)) {
+                        $res = array_keys($sorted);
+                    } else {
+                        array_splice($res, $slice_start, count($slice), array_keys($sorted));
+                    }
+
+                    // Check for ties.
+                    $last = $start = null;
+                    $i = 0;
+                    reset($sorted);
+                    while (list($k, $v) = each($sorted)) {
+                        if (is_null($last) || ($last != $v)) {
+                            if ($i) {
+                                $slices[array_search($start, $res)] = array_slice($sorted, array_search($start, $sorted), $i + 1);
+                                $i = 0;
+                            }
+                            $last = $v;
+                            $start = $k;
+                        } else {
+                            ++$i;
+                        }
+                    }
+                    if ($i) {
+                        $slices[array_search($start, $res)] = array_slice($sorted, array_search($start, $sorted), $i + 1);
+                    }
+                }
+            }
+
+            $reverse = false;
+        }
+
+        return $res;
+    }
+
+    /**
+     * Get the sent dates for purposes of SORT/THREAD sorting under RFC 5256
+     * [2.2].
+     *
+     * @param Horde_Imap_Client_Fetch_Results $data  Data returned from
+     *                                               fetch() that includes
+     *                                               both date and envelope
+     *                                               items.
+     * @param array $ids                             The IDs to process.
+     * @param boolean $internal                      Only use internal date?
+     *
+     * @return array  A mapping of IDs -> UNIX timestamps.
+     */
+    protected function _getSentDates(Horde_Imap_Client_Fetch_Results $data,
+                                     $ids, $internal = false)
+    {
+        $dates = array();
+
+        foreach ($ids as $num) {
+            $dt = ($internal || !isset($data[$num]->getEnvelope()->date))
+                // RFC 5256 [3] & 3501 [6.4.4]: disregard timezone when
+                // using internaldate.
+                ? $data[$num]->getImapDate()
+                : $data[$num]->getEnvelope()->date;
+            $dates[$num] = $dt->format('U');
+        }
+
+        return $dates;
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientSocketConnectionPop3phpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientSocketConnectionPop3php"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Socket/Connection/Pop3.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Socket/Connection/Pop3.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Socket/Connection/Pop3.php                          (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Socket/Connection/Pop3.php     2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,81 @@
</span><ins>+<?php
+/**
+ * Copyright 2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * PHP stream connection to the POP3 server.
+ *
+ * NOTE: This class is NOT intended to be accessed outside of the package.
+ * There is NO guarantees that the API of this class will not change across
+ * versions.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2013 Horde LLC
+ * @internal
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Socket_Connection_Pop3
+extends Horde_Imap_Client_Socket_Connection
+{
+    /**
+     * Writes data to the POP3 output stream.
+     *
+     * @param string $data  String data.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function write($data)
+    {
+        if (fwrite($this->_stream, $data . "\r\n") === false) {
+            throw new Horde_Imap_Client_Exception(
+                Horde_Imap_Client_Translation::t("Server write error."),
+                Horde_Imap_Client_Exception::SERVER_WRITEERROR
+            );
+        }
+
+        $this->_debug->client($data);
+    }
+
+    /**
+     * Read data from incoming POP3 stream.
+     *
+     * @return string  Line of data.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function read()
+    {
+        if (feof($this->_stream)) {
+            $this->close();
+            $this->_debug->info("ERROR: Server closed the connection.");
+            throw new Horde_Imap_Client_Exception(
+                Horde_Imap_Client_Translation::t("POP3 Server closed the connection unexpectedly."),
+                Horde_Imap_Client_Exception::DISCONNECT
+            );
+        }
+
+        if (($read = fgets($this->_stream)) === false) {
+            $this->_debug->info("ERROR: IMAP read/timeout error.");
+            throw new Horde_Imap_Client_Exception(
+                Horde_Imap_Client_Translation::t("Error when communicating with the mail server."),
+                Horde_Imap_Client_Exception::SERVER_READERROR
+            );
+        }
+
+        $this->_debug->server(rtrim($read, "\r\n"));
+
+        return $read;
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientSocketConnectionSocketphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientSocketConnectionSocketphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Socket/Connection/Socket.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Socket/Connection/Socket.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Socket/Connection/Socket.php                                (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Socket/Connection/Socket.php   2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,210 @@
</span><ins>+<?php
+/**
+ * Copyright 2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * PHP stream connection to the IMAP server.
+ *
+ * NOTE: This class is NOT intended to be accessed outside of the package.
+ * There is NO guarantees that the API of this class will not change across
+ * versions.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2013 Horde LLC
+ * @internal
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Socket_Connection_Socket
+extends Horde_Imap_Client_Socket_Connection
+{
+    /**
+     * Sending buffer.
+     *
+     * @var string
+     */
+    protected $_buffer = '';
+
+    /**
+     * Output full data for literals?
+     *
+     * @var boolean
+     */
+    protected $_debugliteral;
+
+    /**
+     * Constructor.
+     *
+     * @param Horde_Imap_Client_Base $base  The base client object.
+     * @param object $debug                 The debug handler.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function __construct(Horde_Imap_Client_Base $base, $debug)
+    {
+        parent::__construct($base, $debug);
+
+        $this->_debugliteral = $base->getParam('debug_literal');
+    }
+
+    /**
+     * Writes data to the IMAP output stream.
+     *
+     * @param string $data  String data.
+     * @param boolean $eol  Append EOL?
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function write($data, $eol = false)
+    {
+        if ($eol) {
+            $buffer = $this->_buffer;
+            $this->_buffer = '';
+
+            if (fwrite($this->_stream, $buffer . $data . ($eol ? "\r\n" : '')) === false) {
+                throw new Horde_Imap_Client_Exception(
+                    Horde_Imap_Client_Translation::t("Server write error."),
+                    Horde_Imap_Client_Exception::SERVER_WRITEERROR
+                );
+            }
+
+            $this->_debug->client($buffer . $data);
+        } else {
+            $this->_buffer .= $data;
+        }
+    }
+
+    /**
+     * Writes literal data to the IMAP output stream.
+     *
+     * @param mixed $data      Either a stream resource, or Horde_Stream
+     *                         object.
+     * @param integer $length  The literal length.
+     * @param boolean $binary  If true, this is binary data.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function writeLiteral($data, $length, $binary = false)
+    {
+        $this->_buffer = '';
+
+        if ($data instanceof Horde_Stream) {
+            $data = $data->stream;
+        }
+
+        rewind($data);
+        while (!feof($data)) {
+            if (fwrite($this->_stream, fread($data, 8192)) === false) {
+                throw new Horde_Imap_Client_Exception(
+                    Horde_Imap_Client_Translation::t("Server write error."),
+                    Horde_Imap_Client_Exception::SERVER_WRITEERROR
+                );
+            }
+        }
+
+        if ($this->_debugliteral) {
+            rewind($data);
+            while (!feof($data)) {
+                $this->_debug->raw(fread($data, 8192));
+            }
+        } else {
+            $this->_debug->client('[' . ($binary ? 'BINARY' : 'LITERAL') . ' DATA: ' . $length . ' bytes]');
+        }
+    }
+
+    /**
+     * Read data from incoming IMAP stream.
+     *
+     * @return Horde_Imap_Client_Tokenize  The tokenized data.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function read()
+    {
+        $got_data = false;
+        $literal_len = null;
+        $token = new Horde_Imap_Client_Tokenize();
+
+        do {
+            if (feof($this->_stream)) {
+                $this->close();
+                $this->_debug->info("ERROR: Server closed the connection.");
+                throw new Horde_Imap_Client_Exception(
+                    Horde_Imap_Client_Translation::t("Mail server closed the connection unexpectedly."),
+                    Horde_Imap_Client_Exception::DISCONNECT
+                );
+            }
+
+            if (is_null($literal_len)) {
+                $buffer = '';
+
+                while (($in = fgets($this->_stream)) !== false) {
+                    $got_data = true;
+
+                    if (substr($in, -1) == "\n") {
+                        $in = rtrim($in);
+                        $this->_debug->server($buffer . $in);
+                        $token->add($in);
+                        break;
+                    }
+
+                    $buffer .= $in;
+                    $token->add($in);
+                }
+
+                /* Check for literal data. */
+                if (is_null($len = $token->getLiteralLength())) {
+                    break;
+                }
+
+                // Skip 0-length literal data.
+                if ($len['length']) {
+                    $binary = $len['binary'];
+                    $literal_len = $len['length'];
+                }
+
+                continue;
+            }
+
+            $old_len = $literal_len;
+
+            while (($literal_len > 0) && !feof($this->_stream)) {
+                $in = fread($this->_stream, min($literal_len, 8192));
+                $token->add($in);
+                if ($this->_debugliteral) {
+                    $this->_debug->raw($in);
+                }
+
+                $got_data = true;
+                $literal_len -= strlen($in);
+            }
+
+            $literal_len = null;
+
+            if (!$this->_debugliteral) {
+                $this->_debug->server('[' . ($binary ? 'BINARY' : 'LITERAL') . ' DATA: ' . $old_len . ' bytes]');
+            }
+        } while (true);
+
+        if (!$got_data) {
+            $this->_debug->info("ERROR: IMAP read/timeout error.");
+            throw new Horde_Imap_Client_Exception(
+                Horde_Imap_Client_Translation::t("Error when communicating with the mail server."),
+                Horde_Imap_Client_Exception::SERVER_READERROR
+            );
+        }
+
+        return $token;
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientSocketConnectionphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientSocketConnectionphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Socket/Connection.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Socket/Connection.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Socket/Connection.php                               (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Socket/Connection.php  2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,128 @@
</span><ins>+<?php
+/**
+ * Copyright 2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * PHP stream connection to a server.
+ *
+ * NOTE: This class is NOT intended to be accessed outside of the package.
+ * There is NO guarantees that the API of this class will not change across
+ * versions.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2013 Horde LLC
+ * @internal
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Socket_Connection
+extends Horde_Imap_Client_Base_Connection
+{
+    /**
+     * Sending buffer.
+     *
+     * @var string
+     */
+    protected $_buffer = '';
+
+    /**
+     * The stream connection to the IMAP server.
+     *
+     * @var resource
+     */
+    protected $_stream = null;
+
+    /**
+     * Constructor.
+     *
+     * @param Horde_Imap_Client_Base $base  The base client object.
+     * @param object $debug                 The debug handler.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    public function __construct(Horde_Imap_Client_Base $base, $debug)
+    {
+        parent::__construct($base, $debug);
+
+        switch ($secure = $base->getParam('secure')) {
+        case 'ssl':
+        case 'sslv2':
+        case 'sslv3':
+            $conn = $secure . '://';
+            $this->_secure = true;
+            break;
+
+        case 'tls':
+        default:
+            $conn = 'tcp://';
+            break;
+        }
+
+        $timeout = $base->getParam('timeout');
+
+        $this->_stream = @stream_socket_client(
+            $conn . $base->getParam('hostspec') . ':' . $base->getParam('port'),
+            $error_number,
+            $error_string,
+            $timeout
+        );
+
+        if ($this->_stream === false) {
+            $e = new Horde_Imap_Client_Exception(
+                Horde_Imap_Client_Translation::t("Error connecting to mail server."),
+                Horde_Imap_Client_Exception::SERVER_CONNECT
+            );
+            $e->details = sprintf("[%u] %s", $error_number, $error_string);
+            throw $e;
+        }
+
+        stream_set_timeout($this->_stream, $timeout);
+
+        if (function_exists('stream_set_read_buffer')) {
+            stream_set_read_buffer($this->_stream, 0);
+        }
+        stream_set_write_buffer($this->_stream, 0);
+
+        $this->_connected = true;
+    }
+
+    /**
+     * Start a TLS connection to the server.
+     *
+     * @return boolean  Whether TLS was successfully started.
+     */
+    public function startTls()
+    {
+        if ($this->connected &&
+            !$this->secure &&
+            (@stream_socket_enable_crypto($this->_stream, true, STREAM_CRYPTO_METHOD_TLS_CLIENT) === true)) {
+            $this->_secure = true;
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Close the connection to the server.
+     */
+    public function close()
+    {
+        if ($this->connected) {
+            @fclose($this->_stream);
+            $this->_connected = $this->_secure = false;
+            $this->_stream = null;
+        }
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientSocketPop3phpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientSocketPop3php"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Socket/Pop3.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Socket/Pop3.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Socket/Pop3.php                             (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Socket/Pop3.php        2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,1264 @@
</span><ins>+<?php
+/**
+ * Copyright 2009-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * ---------------------------------------------------------------------------
+ *
+ * Based on the PEAR Net_POP3 package (version 1.3.6) by:
+ *     Richard Heyes <richard@phpguru.org>
+ *     Damian Fernandez Sosa <damlists@cnba.uba.ar>
+ *
+ * Copyright (c) 2002, 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.
+ *
+ * ---------------------------------------------------------------------------
+ *
+ * @category  Horde
+ * @copyright 2002 Richard Heyes
+ * @copyright 2009-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * An interface to a POP3 server using PHP functions.
+ *
+ * It is an abstraction layer allowing POP3 commands to be used based on
+ * IMAP equivalents.
+ *
+ * This driver implements the following POP3-related RFCs:
+ *   - STD 53/RFC 1939: POP3 specification
+ *   - RFC 2195: CRAM-MD5 authentication
+ *   - RFC 2449: POP3 extension mechanism
+ *   - RFC 2595/4616: PLAIN authentication
+ *   - RFC 2831: DIGEST-MD5 SASL Authentication (obsoleted by RFC 6331)
+ *   - RFC 3206: AUTH/SYS response codes
+ *   - RFC 1734/5034: POP3 SASL
+ *
+ * @author    Richard Heyes <richard@phpguru.org>
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2002 Richard Heyes
+ * @copyright 2009-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Socket_Pop3 extends Horde_Imap_Client_Base
+{
+    /**
+     * The list of deleted messages.
+     *
+     * @var array
+     */
+    protected $_deleted = array();
+
+    /**
+     * This object returns POP3 Fetch data objects.
+     *
+     * @var string
+     */
+    protected $_fetchDataClass = 'Horde_Imap_Client_Data_Fetch_Pop3';
+
+    /**
+     */
+    public function __construct(array $params = array())
+    {
+        parent::__construct($params);
+
+        if (empty($params['port'])) {
+            $this->setParam('port', in_array($this->getParam('secure'), array('ssl', 'sslv2', 'sslv3')) ? 995 : 110);
+        }
+    }
+
+    /**
+     */
+    protected function _initCache($current = false)
+    {
+        return parent::_initCache($current) &&
+               $this->queryCapability('UIDL');
+    }
+
+    /**
+     */
+    public function getIdsOb($ids = null, $sequence = false)
+    {
+        return new Horde_Imap_Client_Ids_Pop3($ids, $sequence);
+    }
+
+    /**
+     */
+    protected function _capability()
+    {
+        $this->_connect();
+
+        $capability = array();
+
+        try {
+            $res = $this->_sendLine('CAPA', array(
+                'multiline' => 'array'
+            ));
+
+            foreach ($res['data'] as $val) {
+                $prefix = explode(' ', $val);
+                $capability[strtoupper($prefix[0])] = (count($prefix) > 1)
+                    ? array_slice($prefix, 1)
+                    : true;
+            }
+        } catch (Horde_Imap_Client_Exception $e) {
+            /* Need to probe for capabilities if CAPA command is not
+             * available. */
+            $capability = array('USER', 'SASL');
+
+            try {
+                $this->_sendLine('UIDL', array(
+                    'multiline' => 'none'
+                ));
+                $capability[] = 'UIDL';
+            } catch (Horde_Imap_Client_Exception $e) {}
+
+            try {
+                $this->_sendLine('TOP 1 0', array(
+                    'multiline' => 'none'
+                ));
+                $capability[] = 'TOP';
+            } catch (Horde_Imap_Client_Exception $e) {}
+        }
+
+        $this->_setInit('capability', $capability);
+    }
+
+    /**
+     */
+    protected function _noop()
+    {
+        $this->_sendLine('NOOP');
+    }
+
+    /**
+     * @throws Horde_Imap_Client_Exception_NoSupportPop3
+     */
+    protected function _getNamespaces()
+    {
+        throw new Horde_Imap_Client_Exception_NoSupportPop3('Namespaces');
+    }
+
+    /**
+     */
+    public function alerts()
+    {
+        return array();
+    }
+
+    /**
+     */
+    protected function _login()
+    {
+        $this->_connect();
+
+        // Switch to secure channel if using TLS.
+        if (!$this->isSecureConnection() &&
+            ($this->getParam('secure') == 'tls')) {
+            // Switch over to a TLS connection.
+            if (!$this->queryCapability('STLS')) {
+                throw new Horde_Imap_Client_Exception(
+                    Horde_Imap_Client_Translation::t("Could not open secure connection to the POP3 server.") . ' ' . Horde_Imap_Client_Translation::t("Server does not support secure connections."),
+                    Horde_Imap_Client_Exception::LOGIN_TLSFAILURE
+                );
+            }
+
+            $this->_sendLine('STLS');
+
+            if (!$this->_connection->startTls()) {
+                $this->logout();
+                throw new Horde_Imap_Client_Exception(
+                    Horde_Imap_Client_Translation::t("Could not open secure connection to the POP3 server."),
+                    Horde_Imap_Client_Exception::LOGIN_TLSFAILURE
+                );
+            }
+
+            // Expire cached CAPABILITY information
+            $this->_setInit('capability');
+        }
+
+        if (empty($this->_init['authmethod'])) {
+            $auth_mech = ($sasl = $this->queryCapability('SASL'))
+                ? $sasl
+                : array();
+
+            if (isset($this->_temp['pop3timestamp'])) {
+                $auth_mech[] = 'APOP';
+            }
+
+            $auth_mech[] = 'USER';
+        } else {
+            $auth_mech = array($this->_init['authmethod']);
+        }
+
+        foreach ($auth_mech as $method) {
+            try {
+                $this->_tryLogin($method);
+                $this->_setInit('authmethod', $method);
+                return true;
+            } catch (Horde_Imap_Client_Exception $e) {
+                if (!empty($this->_init['authmethod'])) {
+                    $this->_setInit();
+                    return $this->login();
+                }
+            }
+        }
+
+        throw new Horde_Imap_Client_Exception(
+            Horde_Imap_Client_Translation::t("POP3 server denied authentication."),
+            $e->getCode() ? $e->getCode() : Horde_Imap_Client_Exception::LOGIN_AUTHENTICATIONFAILED
+        );
+    }
+
+    /**
+     * Connects to the server.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    protected function _connect()
+    {
+        if (!is_null($this->_connection)) {
+            return;
+        }
+
+        $this->_connection = new Horde_Imap_Client_Socket_Connection_Pop3($this, $this->_debug);
+
+        $line = $this->_getResponse();
+
+        // Check for string matching APOP timestamp
+        if (preg_match('/<.+@.+>/U', $line['resp'], $matches)) {
+            $this->_temp['pop3timestamp'] = $matches[0];
+        }
+    }
+
+    /**
+     * Authenticate to the POP3 server.
+     *
+     * @param string $method  POP3 login method.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    protected function _tryLogin($method)
+    {
+        $username = $this->getParam('username');
+        $password = $this->getParam('password');
+
+        switch ($method) {
+        case 'CRAM-MD5':
+        case 'CRAM-SHA1':
+        case 'CRAM-SHA256':
+            // RFC 5034: CRAM-MD5
+            // CRAM-SHA1 & CRAM-SHA256 supported by Courier SASL library
+            $challenge = $this->_sendLine('AUTH ' . $method);
+            $response = base64_encode($username . ' ' . hash_hmac(strtolower(substr($method, 5)), base64_decode(substr($challenge['resp'], 2)), $password, true));
+            $this->_sendLine($response, array(
+                'debug' => sprintf('[%s Response - username: %s]', $method, $username)
+            ));
+            break;
+
+        case 'DIGEST-MD5':
+            // RFC 2831; Obsoleted by RFC 6331
+            $challenge = $this->_sendLine('AUTH DIGEST-MD5');
+            $response = base64_encode(new Horde_Imap_Client_Auth_DigestMD5(
+                $username,
+                $password,
+                base64_decode(substr($challenge['resp'], 2)),
+                $this->getParam('hostspec'),
+                'pop3'
+            ));
+            $sresponse = $this->_sendLine($response, array(
+                'debug' => sprintf('[%s Response - username: %s]', $method, $username)
+            ));
+            if (stripos(base64_decode(substr($sresponse['resp'], 2)), 'rspauth=') === false) {
+                throw new Horde_Imap_Client_Exception(
+                    Horde_Imap_Client_Translation::t("Unexpected response from server when authenticating."),
+                    Horde_Imap_Client_Exception::SERVER_CONNECT
+                );
+            }
+
+            /* POP3 doesn't use protocol's third step. */
+            $this->_sendLine('');
+            break;
+
+        case 'LOGIN':
+            // RFC 5034
+            $this->_sendLine('AUTH LOGIN');
+            $this->_sendLine(base64_encode($username), array(
+                'debug' => sprintf('[AUTH LOGIN Command - username: %s]', $username)
+            ));
+            $this->_sendLine(base64_encode($password), array(
+                'debug' => '[AUTH LOGIN Command - password]'
+            ));
+            break;
+
+        case 'PLAIN':
+            // RFC 5034
+            $this->_sendLine('AUTH PLAIN ' . base64_encode(implode("\0", array($username, $this->getParam('password')))), array(
+                'debug' => sprintf('[AUTH PLAIN Command - username: %s]', $username)
+            ));
+            break;
+
+        case 'APOP':
+            // RFC 1939 [7]
+            $this->_sendLine('APOP ' . $username . ' ' . hash('md5', $this->_temp['pop3timestamp'] . $password));
+            break;
+
+        case 'USER':
+            // RFC 1939 [7]
+            $this->_sendLine('USER ' . $username);
+            $this->_sendLine('PASS ' . $password, array(
+                'debug' => '[USER Command - password]'
+            ));
+            break;
+
+        default:
+            throw new Horde_Imap_Client_Exception(
+                sprintf(Horde_Imap_Client_Translation::t("Unknown authentication method: %s"), $method),
+                Horde_Imap_Client_Exception::SERVER_CONNECT
+            );
+        }
+    }
+
+    /**
+     */
+    protected function _logout()
+    {
+        try {
+            $this->_sendLine('QUIT');
+        } catch (Horde_Imap_Client_Exception $e) {}
+        $this->_deleted = array();
+    }
+
+    /**
+     * @throws Horde_Imap_Client_Exception_NoSupportPop3
+     */
+    protected function _sendID($info)
+    {
+        throw new Horde_Imap_Client_Exception_NoSupportPop3('ID command');
+    }
+
+    /**
+     * Return implementation information from the POP3 server (RFC 2449 [6.9]).
+     */
+    protected function _getID()
+    {
+        $id = $this->queryCapability('IMPLEMENTATION');
+        return empty($id)
+            ? array()
+            : array('implementation' => $id);
+    }
+
+    /**
+     * @throws Horde_Imap_Client_Exception_NoSupportPop3
+     */
+    protected function _setLanguage($langs)
+    {
+        throw new Horde_Imap_Client_Exception_NoSupportPop3('LANGUAGE extension');
+    }
+
+    /**
+     * @throws Horde_Imap_Client_Exception_NoSupportPop3
+     */
+    protected function _getLanguage($list)
+    {
+        throw new Horde_Imap_Client_Exception_NoSupportPop3('LANGUAGE extension');
+    }
+
+    /**
+     * @throws Horde_Imap_Client_Exception_NoSupportPop3
+     */
+    protected function _openMailbox(Horde_Imap_Client_Mailbox $mailbox, $mode)
+    {
+        if (strcasecmp($mailbox, 'INBOX') !== 0) {
+            throw new Horde_Imap_Client_Exception_NoSupportPop3('Mailboxes other than INBOX');
+        }
+        $this->_changeSelected($mailbox, $mode);
+    }
+
+    /**
+     * @throws Horde_Imap_Client_Exception_NoSupportPop3
+     */
+    protected function _createMailbox(Horde_Imap_Client_Mailbox $mailbox, $opts)
+    {
+        throw new Horde_Imap_Client_Exception_NoSupportPop3('Creating mailboxes');
+    }
+
+    /**
+     * @throws Horde_Imap_Client_Exception_NoSupportPop3
+     */
+    protected function _deleteMailbox(Horde_Imap_Client_Mailbox $mailbox)
+    {
+        throw new Horde_Imap_Client_Exception_NoSupportPop3('Deleting mailboxes');
+    }
+
+    /**
+     * @throws Horde_Imap_Client_Exception_NoSupportPop3
+     */
+    protected function _renameMailbox(Horde_Imap_Client_Mailbox $old,
+                                      Horde_Imap_Client_Mailbox $new)
+    {
+        throw new Horde_Imap_Client_Exception_NoSupportPop3('Renaming mailboxes');
+    }
+
+    /**
+     * @throws Horde_Imap_Client_Exception_NoSupportPop3
+     */
+    protected function _subscribeMailbox(Horde_Imap_Client_Mailbox $mailbox,
+                                         $subscribe)
+    {
+        throw new Horde_Imap_Client_Exception_NoSupportPop3('Mailboxes other than INBOX');
+    }
+
+    /**
+     */
+    protected function _listMailboxes($pattern, $mode, $options)
+    {
+        $tmp = array(
+            'mailbox' => Horde_Imap_Client_Mailbox::get('INBOX')
+        );
+
+        if (!empty($options['attributes'])) {
+            $tmp['attributes'] = array();
+        }
+        if (!empty($options['delimiter'])) {
+            $tmp['delimiter'] = '';
+        }
+
+        return array('INBOX' => $tmp);
+    }
+
+    /**
+     * @param integer $flags   This driver only supports the options listed
+     *                         under Horde_Imap_Client::STATUS_ALL.
+     * @throws Horde_Imap_Client_Exception_NoSupportPop3
+     */
+    protected function _status($mboxes, $flags)
+    {
+        if ((count($mboxes) > 1) ||
+            (strcasecmp(reset($mboxes), 'INBOX') !== 0)) {
+            throw new Horde_Imap_Client_Exception_NoSupportPop3('Mailboxes other than INBOX');
+        }
+
+        $this->openMailbox('INBOX');
+
+        $ret = array();
+
+        if ($flags & Horde_Imap_Client::STATUS_MESSAGES) {
+            $res = $this->_pop3Cache('stat');
+            $ret['messages'] = $res['msgs'];
+        }
+
+        if ($flags & Horde_Imap_Client::STATUS_RECENT) {
+            $res = $this->_pop3Cache('stat');
+            $ret['recent'] = $res['msgs'];
+        }
+
+        // No need for STATUS_UIDNEXT_FORCE handling since STATUS_UIDNEXT will
+        // always return a value.
+        if ($flags & Horde_Imap_Client::STATUS_UIDNEXT) {
+            $res = $this->_pop3Cache('stat');
+            $ret['uidnext'] = $res['msgs'] + 1;
+        }
+
+        if ($flags & Horde_Imap_Client::STATUS_UIDVALIDITY) {
+            $ret['uidvalidity'] = $this->queryCapability('UIDL')
+                ? 1
+                : microtime(true);
+        }
+
+        if ($flags & Horde_Imap_Client::STATUS_UNSEEN) {
+            $ret['unseen'] = 0;
+        }
+
+        return array('INBOX' => $ret);
+    }
+
+    /**
+     * @throws Horde_Imap_Client_Exception_NoSupportPop3
+     */
+    protected function _append(Horde_Imap_Client_Mailbox $mailbox, $data,
+                               $options)
+    {
+        throw new Horde_Imap_Client_Exception_NoSupportPop3('Appending messages');
+    }
+
+    /**
+     */
+    protected function _check()
+    {
+        $this->noop();
+    }
+
+    /**
+     */
+    protected function _close($options)
+    {
+        if (!empty($options['expunge'])) {
+            $this->logout();
+        }
+    }
+
+    /**
+     * @param array $options  Additional options. 'ids' has no effect in this
+     *                        driver.
+     */
+    protected function _expunge($options)
+    {
+        $msg_list = $this->_deleted;
+        $this->logout();
+        return empty($options['list'])
+            ? null
+            : $msg_list;
+    }
+
+    /**
+     * @throws Horde_Imap_Client_Exception_NoSupportPop3
+     */
+    protected function _search($query, $options)
+    {
+        $sort = empty($options['sort'])
+            ? null
+            : reset($options['sort']);
+
+        // Only support a single query: an ALL search sorted by sequence.
+        if ((strval($options['_query']['query']) != 'ALL') ||
+            ($sort &&
+             ((count($options['sort']) > 1) ||
+              ($sort != Horde_Imap_Client::SORT_SEQUENCE)))) {
+            throw new Horde_Imap_Client_Exception_NoSupportPop3('Server search');
+        }
+
+        $status = $this->status($this->_selected, Horde_Imap_Client::STATUS_MESSAGES);
+        $res = range(1, $status['messages']);
+
+        if (empty($options['sequence'])) {
+            $tmp = array();
+            $uidllist = $this->_pop3Cache('uidl');
+            foreach ($res as $val) {
+                $tmp[] = $uidllist[$val];
+            }
+            $res = $tmp;
+        }
+
+        $ret = array();
+        foreach ($options['results'] as $val) {
+            switch ($val) {
+            case Horde_Imap_Client::SEARCH_RESULTS_COUNT:
+                $ret['count'] = count($res);
+                break;
+
+            case Horde_Imap_Client::SEARCH_RESULTS_MATCH:
+                $ret['match'] = $this->getIdsOb($res);
+                break;
+
+            case Horde_Imap_Client::SEARCH_RESULTS_MAX:
+                $ret['max'] = empty($res) ? null : max($res);
+                break;
+
+            case Horde_Imap_Client::SEARCH_RESULTS_MIN:
+                $ret['min'] = empty($res) ? null : min($res);
+                break;
+            }
+        }
+
+        return $ret;
+    }
+
+    /**
+     * @throws Horde_Imap_Client_Exception_NoSupportPop3
+     */
+    protected function _setComparator($comparator)
+    {
+        throw new Horde_Imap_Client_Exception_NoSupportPop3('Search comparators');
+    }
+
+    /**
+     * @throws Horde_Imap_Client_Exception_NoSupportPop3
+     */
+    protected function _getComparator()
+    {
+        throw new Horde_Imap_Client_Exception_NoSupportPop3('Search comparators');
+    }
+
+    /**
+     * @throws Horde_Imap_Client_Exception_NoSupportPop3
+     */
+    protected function _thread($options)
+    {
+        throw new Horde_Imap_Client_Exception_NoSupportPop3('Server threading');
+    }
+
+    /**
+     */
+    protected function _fetch(Horde_Imap_Client_Fetch_Results $results,
+                              $queries)
+    {
+        foreach ($queries as $options) {
+            $this->_fetchCmd($results, $options);
+        }
+
+        $this->_updateCache($results);
+    }
+
+     /**
+     * Fetch data for a given fetch query.
+     *
+     * @param Horde_Imap_Client_Fetch_Results $results  Fetch results.
+     * @param array $options                            Fetch query options.
+     */
+    protected function _fetchCmd(Horde_Imap_Client_Fetch_Results $results,
+                                 $options)
+    {
+        // Grab sequence IDs - IDs will always be the message number for
+        // POP3 fetch commands.
+        $seq_ids = $this->_getSeqIds($options['ids']);
+        if (empty($seq_ids)) {
+            return;
+        }
+
+        $lookup = $options['ids']->sequence
+            ? array_combine($seq_ids, $seq_ids)
+            : $this->_pop3Cache('uidl');
+
+        foreach ($options['_query'] as $type => $c_val) {
+            switch ($type) {
+            case Horde_Imap_Client::FETCH_FULLMSG:
+                foreach ($seq_ids as $id) {
+                    $tmp = $this->_pop3Cache('msg', $id);
+
+                    if (empty($c_val['start']) && empty($c_val['length'])) {
+                        $tmp2 = fopen('php://temp', 'r+');
+                        stream_copy_to_stream($tmp, $tmp2, empty($c_val['length']) ? -1 : $c_val['length'], empty($c_val['start']) ? 0 : $c_val['start']);
+                        $results->get($lookup[$id])->setFullMsg($tmp2);
+                    } else {
+                        $results->get($lookup[$id])->setFullMsg($tmp);
+                    }
+                }
+                break;
+
+            case Horde_Imap_Client::FETCH_HEADERTEXT:
+                // Ignore 'peek' option
+                foreach ($c_val as $key => $val) {
+                    foreach ($seq_ids as $id) {
+                        /* Message header can be retrieved via TOP, if the
+                         * command is available. */
+                        try {
+                            $tmp = ($key == 0)
+                                ? $this->_pop3Cache('hdr', $id)
+                                : Horde_Mime_Part::getRawPartText(stream_get_contents($this->_pop3Cache('msg', $id)), 'header', $key);
+                            $results->get($lookup[$id])->setHeaderText($key, $this->_processString($tmp, $c_val));
+                        } catch (Horde_Mime_Exception $e) {}
+                    }
+                }
+                break;
+
+            case Horde_Imap_Client::FETCH_BODYTEXT:
+                // Ignore 'peek' option
+                foreach ($c_val as $key => $val) {
+                    foreach ($seq_ids as $id) {
+                        try {
+                            $results->get($lookup[$id])->setBodyText($key, $this->_processString(Horde_Mime_Part::getRawPartText(stream_get_contents($this->_pop3Cache('msg', $id)), 'body', $key), $val));
+                        } catch (Horde_Mime_Exception $e) {}
+                    }
+                }
+                break;
+
+            case Horde_Imap_Client::FETCH_MIMEHEADER:
+                // Ignore 'peek' option
+                foreach ($c_val as $key => $val) {
+                    foreach ($seq_ids as $id) {
+                        try {
+                            $results->get($lookup[$id])->setMimeHeader($key, $this->_processString(Horde_Mime_Part::getRawPartText(stream_get_contents($this->_pop3Cache('msg', $id)), 'header', $key), $val));
+                        } catch (Horde_Mime_Exception $e) {}
+                    }
+                }
+                break;
+
+            case Horde_Imap_Client::FETCH_BODYPART:
+                // Ignore 'decode', 'peek'
+                foreach ($c_val as $key => $val) {
+                    foreach ($seq_ids as $id) {
+                        try {
+                            $results->get($lookup[$id])->setBodyPart($key, $this->_processString(Horde_Mime_Part::getRawPartText(stream_get_contents($this->_pop3Cache('msg', $id)), 'body', $key), $val));
+                        } catch (Horde_Mime_Exception $e) {}
+                    }
+                }
+                break;
+
+            case Horde_Imap_Client::FETCH_HEADERS:
+                // Ignore 'length', 'peek'
+                foreach ($seq_ids as $id) {
+                    $ob = $this->_pop3Cache('hdrob', $id);
+                    foreach ($c_val as $key => $val) {
+                        $tmp = $ob;
+
+                        if (empty($val['notsearch'])) {
+                            $tmp2 = $tmp->toArray(array('nowrap' => true));
+                            foreach (array_keys($tmp2) as $hdr) {
+                                if (!in_array($hdr, $val['headers'])) {
+                                    $tmp->removeHeader($hdr);
+                                }
+                            }
+                        } else {
+                            foreach ($val['headers'] as $hdr) {
+                                $tmp->removeHeader($hdr);
+                            }
+                        }
+
+                        $results->get($lookup[$id])->setHeaders($key, $tmp);
+                    }
+                }
+                break;
+
+            case Horde_Imap_Client::FETCH_STRUCTURE:
+                foreach ($seq_ids as $id) {
+                    if ($ptr = $this->_pop3Cache('msg', $id)) {
+                        try {
+                            $results->get($lookup[$id])->setStructure(Horde_Mime_Part::parseMessage(stream_get_contents($ptr), array('no_body' => true)));
+                        } catch (Horde_Exception $e) {}
+                    }
+                }
+                break;
+
+            case Horde_Imap_Client::FETCH_ENVELOPE:
+                foreach ($seq_ids as $id) {
+                    $tmp = $this->_pop3Cache('hdrob', $id);
+                    $results->get($lookup[$id])->setEnvelope(array(
+                        'date' => $tmp->getValue('date'),
+                        'subject' => $tmp->getValue('subject'),
+                        'from' => $tmp->getOb('from'),
+                        'sender' => $tmp->getOb('sender'),
+                        'reply_to' => $tmp->getOb('reply-to'),
+                        'to' => $tmp->getOb('to'),
+                        'cc' => $tmp->getOb('cc'),
+                        'bcc' => $tmp->getOb('bcc'),
+                        'in_reply_to' => $tmp->getValue('in-reply-to'),
+                        'message_id' => $tmp->getValue('message-id')
+                    ));
+                }
+                break;
+
+            case Horde_Imap_Client::FETCH_IMAPDATE:
+                foreach ($seq_ids as $id) {
+                    $tmp = $this->_pop3Cache('hdrob', $id);
+                    $results->get($lookup[$id])->setImapDate($tmp->getValue('date'));
+                }
+                break;
+
+            case Horde_Imap_Client::FETCH_SIZE:
+                $sizelist = $this->_pop3Cache('size');
+                foreach ($seq_ids as $id) {
+                    $results->get($lookup[$id])->setSize($sizelist[$id]);
+                }
+                break;
+
+            case Horde_Imap_Client::FETCH_SEQ:
+                foreach ($seq_ids as $id) {
+                    $results->get($lookup[$id])->setSeq($id);
+                }
+                break;
+
+            case Horde_Imap_Client::FETCH_UID:
+                $uidllist = $this->_pop3Cache('uidl');
+                foreach ($seq_ids as $id) {
+                    if (isset($uidllist[$id])) {
+                        $results->get($lookup[$id])->setUid($uidllist[$id]);
+                    }
+                }
+                break;
+            }
+        }
+    }
+
+    /**
+     * Retrieve locally cached message data.
+     *
+     * @param string $type    Either 'hdr', 'hdrob', 'msg', 'size', 'stat',
+     *                        or 'uidl'.
+     * @param integer $index  The message index.
+     * @param mixed $data     Additional information needed.
+     *
+     * @return mixed  The cached data. 'msg' returns a stream resource. All
+     *                other types return strings.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    protected function _pop3Cache($type, $index = null, $data = null)
+    {
+        if (isset($this->_temp['pop3cache'][$index][$type])) {
+            if ($type == 'msg') {
+                rewind($this->_temp['pop3cache'][$index][$type]);
+            }
+            return $this->_temp['pop3cache'][$index][$type];
+        }
+
+        switch ($type) {
+        case 'hdr':
+            $data = null;
+            if ($this->queryCapability('TOP')) {
+                try {
+                    $res = $this->_sendLine('TOP ' . $index . ' 0', array(
+                        'multiline' => 'stream'
+                    ));
+                    rewind($res['data']);
+                    $data = stream_get_contents($res['data']);
+                    fclose($res['data']);
+                } catch (Horde_Imap_Client_Exception $e) {}
+            }
+
+            if (is_null($data)) {
+                $data = Horde_Mime_Part::getRawPartText(stream_get_contents($this->_pop3Cache('msg', $index)), 'header', 0);
+            }
+            break;
+
+        case 'hdrob':
+            $data = Horde_Mime_Headers::parseHeaders($this->_pop3Cache('hdr', $index));
+            break;
+
+        case 'msg':
+            $res = $this->_sendLine('RETR ' . $index, array(
+                'multiline' => 'stream'
+            ));
+            $data = $res['data'];
+            rewind($data);
+            break;
+
+        case 'size':
+        case 'uidl':
+            $data = array();
+            try {
+                $res = $this->_sendLine(($type == 'size') ? 'LIST' : 'UIDL', array(
+                    'multiline' => 'array'
+                ));
+                foreach ($res['data'] as $val) {
+                    $resp_data = explode(' ', $val, 2);
+                    $data[$resp_data[0]] = $resp_data[1];
+                }
+            } catch (Horde_Imap_Client_Exception $e) {}
+            break;
+
+        case 'stat':
+            $resp = $this->_sendLine('STAT');
+            $resp_data = explode(' ', $resp['resp'], 2);
+            $data = array('msgs' => $resp_data[0], 'size' => $resp_data[1]);
+            break;
+        }
+
+        $this->_temp['pop3cache'][$index][$type] = $data;
+
+        return $data;
+    }
+
+    /**
+     * Process a string response based on criteria options.
+     *
+     * @param string $str  The original string.
+     * @param array $opts  The criteria options.
+     *
+     * @return string  The requested string.
+     */
+    protected function _processString($str, $opts)
+    {
+        if (!empty($opts['length'])) {
+            return substr($str, empty($opts['start']) ? 0 : $opts['start'], $opts['length']);
+        } elseif (!empty($opts['start'])) {
+            return substr($str, $opts['start']);
+        }
+
+        return $str;
+    }
+
+    /**
+     * @throws Horde_Imap_Client_Exception_NoSupportPop3
+     */
+    protected function _vanished($modseq, Horde_Imap_Client_Ids $ids)
+    {
+        throw new Horde_Imap_Client_Exception_NoSupportPop3('QRESYNC commands');
+    }
+
+    /**
+     * @param array $options  Additional options. This driver does not support
+     *                        'unchangedsince'.
+     */
+    protected function _store($options)
+    {
+        $delete = $reset = false;
+
+        /* Only support deleting/undeleting messages. */
+        if (isset($options['replace'])) {
+            $delete = (bool)(count(array_intersect($options['replace'], array(
+                Horde_Imap_Client::FLAG_DELETED
+            ))));
+            $reset = !$delete;
+        } else {
+            if (!empty($options['add'])) {
+                $delete = (bool)(count(array_intersect($options['add'], array(
+                    Horde_Imap_Client::FLAG_DELETED
+                ))));
+            }
+
+            if (!empty($options['remove'])) {
+                $reset = !(bool)(count(array_intersect($options['remove'], array(
+                    Horde_Imap_Client::FLAG_DELETED
+                ))));
+            }
+        }
+
+        if ($reset) {
+            $this->_sendLine('RSET');
+        } elseif ($delete) {
+            foreach ($this->_getSeqIds($options['ids']) as $id) {
+                try {
+                    $this->_sendLine('DELE ' . $id);
+                    $this->_deleted[] = $id;
+                } catch (Horde_Imap_Client_Exception $e) {}
+            }
+        }
+
+        return $this->getIdsOb();
+    }
+
+    /**
+     * @throws Horde_Imap_Client_Exception_NoSupportPop3
+     */
+    protected function _copy(Horde_Imap_Client_Mailbox $dest, $options)
+    {
+        throw new Horde_Imap_Client_Exception_NoSupportPop3('Copying messages');
+    }
+
+    /**
+     * @throws Horde_Imap_Client_Exception_NoSupportPop3
+     */
+    protected function _setQuota(Horde_Imap_Client_Mailbox $root, $options)
+    {
+        throw new Horde_Imap_Client_Exception_NoSupportPop3('Quotas');
+    }
+
+    /**
+     * @throws Horde_Imap_Client_Exception_NoSupportPop3
+     */
+    protected function _getQuota(Horde_Imap_Client_Mailbox $root)
+    {
+        throw new Horde_Imap_Client_Exception_NoSupportPop3('Quotas');
+    }
+
+    /**
+     * @throws Horde_Imap_Client_Exception_NoSupportPop3
+     */
+    protected function _getQuotaRoot(Horde_Imap_Client_Mailbox $mailbox)
+    {
+        throw new Horde_Imap_Client_Exception_NoSupportPop3('Quotas');
+    }
+
+    /**
+     * @throws Horde_Imap_Client_Exception_NoSupportPop3
+     */
+    protected function _setACL(Horde_Imap_Client_Mailbox $mailbox, $identifier,
+                               $options)
+    {
+        throw new Horde_Imap_Client_Exception_NoSupportPop3('ACLs');
+    }
+
+    /**
+     * @throws Horde_Imap_Client_Exception_NoSupportPop3
+     */
+    protected function _deleteACL(Horde_Imap_Client_Mailbox $mailbox, $identifier)
+    {
+        throw new Horde_Imap_Client_Exception_NoSupportPop3('ACLs');
+    }
+
+    /**
+     * @throws Horde_Imap_Client_Exception_NoSupportPop3
+     */
+    protected function _getACL(Horde_Imap_Client_Mailbox $mailbox)
+    {
+        throw new Horde_Imap_Client_Exception_NoSupportPop3('ACLs');
+    }
+
+    /**
+     * @throws Horde_Imap_Client_Exception_NoSupportPop3
+     */
+    protected function _listACLRights(Horde_Imap_Client_Mailbox $mailbox,
+                                      $identifier)
+    {
+        throw new Horde_Imap_Client_Exception_NoSupportPop3('ACLs');
+    }
+
+    /**
+     * @throws Horde_Imap_Client_Exception_NoSupportPop3
+     */
+    protected function _getMyACLRights(Horde_Imap_Client_Mailbox $mailbox)
+    {
+        throw new Horde_Imap_Client_Exception_NoSupportPop3('ACLs');
+    }
+
+    /**
+     * @throws Horde_Imap_Client_Exception_NoSupportPop3
+     */
+    protected function _getMetadata(Horde_Imap_Client_Mailbox $mailbox,
+                                    $entries, $options)
+    {
+        throw new Horde_Imap_Client_Exception_NoSupportPop3('Metadata');
+    }
+
+    /**
+     * @throws Horde_Imap_Client_Exception_NoSupportPop3
+     */
+    protected function _setMetadata(Horde_Imap_Client_Mailbox $mailbox, $data)
+    {
+        throw new Horde_Imap_Client_Exception_NoSupportPop3('Metadata');
+    }
+
+    /**
+     */
+    protected function _getSearchCache($type, $options)
+    {
+        /* POP3 does not support search caching. */
+        return null;
+    }
+
+    /* Internal functions. */
+
+    /**
+     * Perform a command on the server. A connection to the server must have
+     * already been made.
+     *
+     * @param string $cmd     The command to execute.
+     * @param array $options  Additional options:
+     * <pre>
+     *   - debug: (string) When debugging, send this string instead of the
+     *            actual command/data sent.
+     *            DEFAULT: Raw data output to debug stream.
+     *   - multiline: (mixed) 'array', 'none', or 'stream'.
+     * </pre>
+     *
+     * @return array  See _getResponse().
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    protected function _sendLine($cmd, $options = array())
+    {
+        $old_debug = $this->_debug->debug;
+        if (!empty($options['debug'])) {
+            $this->_debug->raw($options['debug'] . "\n");
+            $this->_debug->debug = false;
+        }
+
+        try {
+            $this->_connection->write($cmd);
+        } catch (Horde_Imap_Client_Exception $e) {
+            $this->_debug->debug = $old_debug;
+            throw $e;
+        }
+
+        $this->_debug->debug = $old_debug;
+
+        return $this->_getResponse(
+            empty($options['multiline']) ? false : $options['multiline']
+        );
+    }
+
+    /**
+     * Gets a line from the stream and parses it.
+     *
+     * @param mixed $multiline  'array', 'none', 'stream', or null.
+     *
+     * @return array  An array with the following keys:
+     *   - data: (mixed) Stream, array, or null.
+     *   - resp: (string) The server response text.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    protected function _getResponse($multiline = false)
+    {
+        $ob = array('resp' => '');
+
+        $read = explode(' ', rtrim($this->_connection->read(), "\r\n"), 2);
+        if (!in_array($read[0], array('+OK', '-ERR'))) {
+            $this->_debug->info("ERROR: IMAP read/timeout error.");
+            throw new Horde_Imap_Client_Exception(
+                Horde_Imap_Client_Translation::t("Error when communicating with the mail server."),
+                Horde_Imap_Client_Exception::SERVER_READERROR
+            );
+        }
+
+        $respcode = null;
+        if (isset($read[1]) &&
+            isset($this->_init['capability']) &&
+            $this->queryCapability('RESP-CODES')) {
+            $respcode = $this->_parseResponseCode($read[1]);
+        }
+
+        switch ($read[0]) {
+        case '+OK':
+            if ($respcode) {
+                $ob['resp'] = $respcode->text;
+            } elseif (isset($read[1])) {
+                $ob['resp'] = $read[1];
+            }
+            break;
+
+        case '-ERR':
+            $errcode = 0;
+            if ($respcode) {
+                $errtext = $respcode->text;
+
+                if (isset($respcode->code)) {
+                    switch ($respcode->code) {
+                    // RFC 2449 [8.1.1]
+                    case 'IN-USE':
+                    // RFC 2449 [8.1.2]
+                    case 'LOGIN-DELAY':
+                        $errcode = Horde_Imap_Client_Exception::LOGIN_UNAVAILABLE;
+                        break;
+
+                    // RFC 3206 [4]
+                    case 'SYS/TEMP':
+                        $errcode = Horde_Imap_Client_Exception::POP3_TEMP_ERROR;
+                        break;
+
+                    // RFC 3206 [4]
+                    case 'SYS/PERM':
+                        $errcode = Horde_Imap_Client_Exception::POP3_PERM_ERROR;
+                        break;
+
+                    // RFC 3206 [5]
+                    case 'AUTH':
+                        $errcode = Horde_Imap_Client_Exception::LOGIN_AUTHENTICATIONFAILED;
+                        break;
+                    }
+                }
+            } elseif (isset($read[1])) {
+                $errtext = $read[1];
+            } else {
+                $errtext = '[No error message provided by server]';
+            }
+
+            $e = new Horde_Imap_Client_Exception(
+                Horde_Imap_Client_Translation::t("POP3 error reported by server."),
+                $errcode
+            );
+            $e->details = $errtext;
+            throw $e;
+        }
+
+        switch ($multiline) {
+        case 'array':
+            $ob['data'] = array();
+            break;
+
+        case 'none':
+            $ob['data'] = null;
+            break;
+
+        case 'stream':
+            $ob['data'] = fopen('php://temp', 'r+');
+            break;
+
+        default:
+            return $ob;
+        }
+
+        do {
+            $orig_read = $this->_connection->read();
+            $read = rtrim($orig_read, "\r\n");
+
+            if ($read == '.') {
+                break;
+            } elseif (substr($read, 0, 2) == '..') {
+                $read = substr($read, 1);
+            }
+
+            if (is_array($ob['data'])) {
+                $ob['data'][] = $read;
+            } elseif (!is_null($ob['data'])) {
+                fwrite($ob['data'], $orig_read);
+            }
+        } while (true);
+
+        return $ob;
+    }
+
+    /**
+     * Returns a list of sequence IDs.
+     *
+     * @param Horde_Imap_Client_Ids $ids  The ID list.
+     *
+     * @return array  A list of sequence IDs.
+     */
+    protected function _getSeqIds(Horde_Imap_Client_Ids $ids)
+    {
+        if (!count($ids)) {
+            $status = $this->status($this->_selected, Horde_Imap_Client::STATUS_MESSAGES);
+            return range(1, $status['messages']);
+        } elseif ($ids->sequence) {
+            return $ids->ids;
+        }
+
+        return array_keys(array_intersect($this->_pop3Cache('uidl'), $ids->ids));
+    }
+
+    /**
+     * Parses response text for response codes (RFC 2449 [8]).
+     *
+     * @param string $text  The response text.
+     *
+     * @return object  An object with the following properties:
+     *   - code: (string) The response code, if it exists.
+     *   - data: (string) The response code data, if it exists.
+     *   - text: (string) The human-readable response text.
+     */
+    protected function _parseResponseCode($text)
+    {
+        $ret = new stdClass;
+
+        $text = trim($text);
+        if ($text[0] == '[') {
+            $pos = strpos($text, ' ', 2);
+            $end_pos = strpos($text, ']', 2);
+            if ($pos > $end_pos) {
+                $ret->code = strtoupper(substr($text, 1, $end_pos - 1));
+            } else {
+                $ret->code = strtoupper(substr($text, 1, $pos - 1));
+                $ret->data = substr($text, $pos + 1, $end_pos - $pos - 1);
+            }
+            $ret->text = trim(substr($text, $end_pos + 1));
+        } else {
+            $ret->text = $text;
+        }
+
+        return $ret;
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientSocketphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientSocketphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Socket.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Socket.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Socket.php                          (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Socket.php     2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,4608 @@
</span><ins>+<?php
+/**
+ * Originally based on code from:
+ *   - auth.php (1.49)
+ *   - imap_general.php (1.212)
+ *   - imap_messages.php (revision 13038)
+ *   - strings.php (1.184.2.35)
+ * from the Squirrelmail project.
+ * Copyright (c) 1999-2007 The SquirrelMail Project Team
+ *
+ * Copyright 2005-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 1999-2007 The SquirrelMail Project Team
+ * @copyright 2005-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * An interface to an IMAP4rev1 server (RFC 3501) using built-in PHP features.
+ *
+ * Implements the following IMAP-related RFCs (see
+ * http://www.iana.org/assignments/imap4-capabilities):
+ *   - RFC 2086/4314: ACL
+ *   - RFC 2087: QUOTA
+ *   - RFC 2088: LITERAL+
+ *   - RFC 2195: AUTH=CRAM-MD5
+ *   - RFC 2221: LOGIN-REFERRALS
+ *   - RFC 2342: NAMESPACE
+ *   - RFC 2595/4616: TLS & AUTH=PLAIN
+ *   - RFC 2831: DIGEST-MD5 authentication mechanism (obsoleted by RFC 6331)
+ *   - RFC 2971: ID
+ *   - RFC 3348: CHILDREN
+ *   - RFC 3501: IMAP4rev1 specification
+ *   - RFC 3502: MULTIAPPEND
+ *   - RFC 3516: BINARY
+ *   - RFC 3691: UNSELECT
+ *   - RFC 4315: UIDPLUS
+ *   - RFC 4422: SASL Authentication (for DIGEST-MD5)
+ *   - RFC 4466: Collected extensions (updates RFCs 2088, 3501, 3502, 3516)
+ *   - RFC 4469/5550: CATENATE
+ *   - RFC 4551: CONDSTORE
+ *   - RFC 4731: ESEARCH
+ *   - RFC 4959: SASL-IR
+ *   - RFC 5032: WITHIN
+ *   - RFC 5161: ENABLE
+ *   - RFC 5162: QRESYNC
+ *   - RFC 5182: SEARCHRES
+ *   - RFC 5255: LANGUAGE/I18NLEVEL
+ *   - RFC 5256: THREAD/SORT
+ *   - RFC 5258: LIST-EXTENDED
+ *   - RFC 5267: ESORT; PARTIAL search return option
+ *   - RFC 5464: METADATA
+ *   - RFC 5530: IMAP Response Codes
+ *   - RFC 5819: LIST-STATUS
+ *   - RFC 5957: SORT=DISPLAY
+ *   - RFC 6154: SPECIAL-USE/CREATE-SPECIAL-USE
+ *   - RFC 6203: SEARCH=FUZZY
+ *   - RFC 6851: MOVE
+ *   - RFC 6858: DOWNGRADED response code
+ *
+ * Implements the following non-RFC extensions:
+ * <ul>
+ *  <li>draft-ietf-morg-inthread-01: THREAD=REFS</li>
+ *  <li>draft-daboo-imap-annotatemore-07: ANNOTATEMORE</li>
+ *  <li>draft-daboo-imap-annotatemore-08: ANNOTATEMORE2</li>
+ *  <li>XIMAPPROXY
+ *   <ul>
+ *    <li>Requires imapproxy v1.2.7-rc1 or later</li>
+ *    <li>
+ *     See https://squirrelmail.svn.sourceforge.net/svnroot/squirrelmail/trunk/imap_proxy/README
+ *    </li>
+ *   </ul>
+ *  </li>
+ * </ul>
+ *
+ * TODO (or not necessary?):
+ * <ul>
+ *  <li>RFC 2177: IDLE
+ *   <ul>
+ *    <li>
+ *     Probably not necessary due to the limited connection time of each
+ *     HTTP/PHP request
+ *    </li>
+ *   </ul>
+ *  <li>RFC 2193: MAILBOX-REFERRALS</li>
+ *  <li>
+ *   RFC 4467/5092/5524/5550/5593: URLAUTH, URLAUTH=BINARY, URL-PARTIAL
+ *  </li>
+ *  <li>RFC 4978: COMPRESS=DEFLATE
+ *   <ul>
+ *    <li>See: http://bugs.php.net/bug.php?id=48725</li>
+ *   </ul>
+ *  </li>
+ *  <li>RFC 5257: ANNOTATE (Experimental)</li>
+ *  <li>RFC 5259: CONVERT</li>
+ *  <li>RFC 5267: CONTEXT=SEARCH; CONTEXT=SORT</li>
+ *  <li>RFC 5465: NOTIFY</li>
+ *  <li>RFC 5466: FILTERS</li>
+ *  <li>RFC 6237: MULTISEARCH (Experimental)</li>
+ *  <li>RFC 6855: UTF8
+ * </ul>
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 1999-2007 The SquirrelMail Project Team
+ * @copyright 2005-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Socket extends Horde_Imap_Client_Base
+{
+    /* Cache names used exclusively within this class. */
+    const CACHE_FLAGS = 'HICflags';
+
+    /**
+     * Queued commands to send to the server.
+     *
+     * @var array
+     */
+    protected $_cmdQueue = array();
+
+    /**
+     * Mapping of status fields to IMAP names.
+     *
+     * @var array
+     */
+    protected $_statusFields = array(
+        'messages' => Horde_Imap_Client::STATUS_MESSAGES,
+        'recent' => Horde_Imap_Client::STATUS_RECENT,
+        'uidnext' => Horde_Imap_Client::STATUS_UIDNEXT,
+        'uidvalidity' => Horde_Imap_Client::STATUS_UIDVALIDITY,
+        'unseen' => Horde_Imap_Client::STATUS_UNSEEN,
+        'firstunseen' => Horde_Imap_Client::STATUS_FIRSTUNSEEN,
+        'flags' => Horde_Imap_Client::STATUS_FLAGS,
+        'permflags' => Horde_Imap_Client::STATUS_PERMFLAGS,
+        'uidnotsticky' => Horde_Imap_Client::STATUS_UIDNOTSTICKY,
+        'highestmodseq' => Horde_Imap_Client::STATUS_HIGHESTMODSEQ
+    );
+
+    /**
+     * The unique tag to use when making an IMAP query.
+     *
+     * @var integer
+     */
+    protected $_tag = 0;
+
+    /**
+     * @param array $params  A hash containing configuration parameters.
+     *                       Additional parameters to base driver:
+     *   - debug_literal: (boolean) If true, will output the raw text of
+     *                    literal responses to the debug stream. Otherwise,
+     *                    outputs a summary of the literal response.
+     *   - envelope_addrs: (integer) The maximum number of address entries to
+     *                     read for FETCH ENVELOPE address fields.
+     *                     DEFAULT: 1000
+     *   - envelope_string: (integer) The maximum length of string fields
+     *                      returned by the FETCH ENVELOPE command.
+     *                      DEFAULT: 2048
+     */
+    public function __construct(array $params = array())
+    {
+        parent::__construct(array_merge(array(
+            'debug_literal' => false,
+            'envelope_addrs' => 1000,
+            'envelope_string' => 2048
+        ), $params));
+    }
+
+    /**
+     */
+    protected function _capability()
+    {
+        // Need to use connect call here or else we run into loop issues
+        // because _connect() can call capability() internally.
+        $this->_connect();
+
+        // It is possible the server provided capability information on
+        // connect, so check for it now.
+        if (!isset($this->_init['capability'])) {
+            $this->_sendCmd($this->_command('CAPABILITY'));
+        }
+    }
+
+    /**
+     * Parse a CAPABILITY Response (RFC 3501 [7.2.1]).
+     *
+     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
+     *                                                          object.
+     * @param array $data  An array of CAPABILITY strings.
+     */
+    protected function _parseCapability(
+        Horde_Imap_Client_Interaction_Pipeline $pipeline,
+        $data
+    )
+    {
+        if (!empty($this->_temp['no_cap'])) {
+            return;
+        }
+
+        /* Assume capabilities are additive. */
+        $c = empty($this->_init['capability'])
+            ? array()
+            : $this->_init['capability'];
+
+        $pipeline->data['capabilties_set'] = true;
+
+        foreach ($data as $val) {
+            $cap_list = explode('=', $val);
+            $cap_list[0] = strtoupper($cap_list[0]);
+            if (isset($cap_list[1])) {
+                if (!isset($c[$cap_list[0]]) || !is_array($c[$cap_list[0]])) {
+                    $c[$cap_list[0]] = array();
+                }
+                $c[$cap_list[0]][] = $cap_list[1];
+            } elseif (!isset($c[$cap_list[0]])) {
+                $c[$cap_list[0]] = true;
+            }
+        }
+
+        $this->_setInit('capability', $c);
+    }
+
+    /**
+     * Unsets a capability.
+     *
+     * @param string $cap  Capability to unset.
+     */
+    protected function _unsetCapability($cap)
+    {
+        $cap_list = $this->capability();
+        unset($cap_list[$cap]);
+        $this->_setInit('capability', $cap_list);
+    }
+
+    /**
+     */
+    protected function _noop()
+    {
+        // NOOP doesn't return any specific response
+        $this->_sendCmd($this->_command('NOOP'));
+    }
+
+    /**
+     */
+    protected function _getNamespaces()
+    {
+        $data = $this->queryCapability('NAMESPACE')
+            ? $this->_sendCmd($this->_command('NAMESPACE'))->data
+            : array();
+
+        return isset($data['namespace'])
+            ? $data['namespace']
+            : array();
+    }
+
+    /**
+     * Parse a NAMESPACE response (RFC 2342 [5] & RFC 5255 [3.4]).
+     *
+     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
+     *                                                          object.
+     * @param Horde_Imap_Client_Tokenize $data  The NAMESPACE data.
+     */
+    protected function _parseNamespace(
+        Horde_Imap_Client_Interaction_Pipeline $pipeline,
+        Horde_Imap_Client_Tokenize $data
+    )
+    {
+        $namespace_array = array(
+            Horde_Imap_Client::NS_PERSONAL,
+            Horde_Imap_Client::NS_OTHER,
+            Horde_Imap_Client::NS_SHARED
+        );
+
+        $c = array();
+
+        // Per RFC 2342, response from NAMESPACE command is:
+        // (PERSONAL NAMESPACES) (OTHER_USERS NAMESPACE) (SHARED NAMESPACES)
+        foreach ($namespace_array as $val) {
+            $entry = $data->next();
+
+            if (is_null($entry)) {
+                continue;
+            }
+
+            while ($data->next() !== false) {
+                $ob = Horde_Imap_Client_Mailbox::get($data->next(), true);
+
+                $c[strval($ob)] = array(
+                    'delimiter' => $data->next(),
+                    'hidden' => false,
+                    'name' => strval($ob),
+                    'translation' => '',
+                    'type' => $val
+                );
+
+                // RFC 4466: NAMESPACE extensions
+                while (($ext = $data->next()) !== false) {
+                    switch (strtoupper($ext)) {
+                    case 'TRANSLATION':
+                        // RFC 5255 [3.4] - TRANSLATION extension
+                        $data->next();
+                        $c[strval($ob)]['translation'] = $data->next();
+                        $data->next();
+                        break;
+                    }
+                }
+            }
+        }
+
+        $pipeline->data['namespace'] = $c;
+    }
+
+    /**
+     */
+    public function alerts()
+    {
+        $alerts = empty($this->_temp['alerts'])
+            ? array()
+            : $this->_temp['alerts'];
+        $this->_temp['alerts'] = array();
+        return $alerts;
+    }
+
+    /**
+     */
+    protected function _login()
+    {
+        if (!empty($this->_temp['preauth'])) {
+            unset($this->_temp['preauth']);
+            return $this->_loginTasks();
+        }
+
+        $this->_connect();
+
+        $first_login = empty($this->_init['authmethod']);
+
+        // Switch to secure channel if using TLS.
+        if (!$this->isSecureConnection() &&
+            ($this->getParam('secure') == 'tls')) {
+            if ($first_login && !$this->queryCapability('STARTTLS')) {
+                // We should never hit this - STARTTLS is required pursuant
+                // to RFC 3501 [6.2.1].
+                throw new Horde_Imap_Client_Exception(
+                    Horde_Imap_Client_Translation::t("Server does not support TLS connections."),
+                    Horde_Imap_Client_Exception::LOGIN_TLSFAILURE
+                );
+            }
+
+            // Switch over to a TLS connection.
+            // STARTTLS returns no untagged response.
+            $this->_sendCmd($this->_Command('STARTTLS'));
+
+            if (!$this->_connection->startTls()) {
+                $this->logout();
+                throw new Horde_Imap_Client_Exception(
+                    Horde_Imap_Client_Translation::t("Could not open secure TLS connection to the IMAP server."),
+                    Horde_Imap_Client_Exception::LOGIN_TLSFAILURE
+                );
+            }
+
+            if ($first_login) {
+                // Expire cached CAPABILITY information (RFC 3501 [6.2.1])
+                $this->_setInit('capability');
+
+                // Reset language (RFC 5255 [3.1])
+                $this->_setInit('lang');
+            }
+
+            // Set language if using imapproxy
+            if (!empty($this->_init['imapproxy'])) {
+                $this->setLanguage();
+            }
+        }
+
+        if ($first_login) {
+            $imap_auth_mech = array();
+
+            $auth_methods = $this->queryCapability('AUTH');
+            if (!empty($auth_methods)) {
+                // Add SASL methods. Prefer CRAM-MD5 over DIGEST-MD5, as the
+                // latter has been obsoleted (RFC 6331).
+                $imap_auth_mech = array_intersect(array('CRAM-MD5', 'DIGEST-MD5'), $auth_methods);
+
+                // Next, try 'PLAIN' authentication.
+                if (in_array('PLAIN', $auth_methods)) {
+                    $imap_auth_mech[] = 'PLAIN';
+                }
+            }
+
+            // Fall back to 'LOGIN' if available.
+            if (!$this->queryCapability('LOGINDISABLED')) {
+                $imap_auth_mech[] = 'LOGIN';
+            }
+
+            if (empty($imap_auth_mech)) {
+                throw new Horde_Imap_Client_Exception(
+                    Horde_Imap_Client_Translation::t("No supported IMAP authentication method could be found."),
+                    Horde_Imap_Client_Exception::LOGIN_NOAUTHMETHOD
+                );
+            }
+
+            /* Use MD5 authentication first, if available. But no need to use
+             * special authentication if we are already using an encrypted
+             * connection. */
+            if ($this->isSecureConnection()) {
+                $imap_auth_mech = array_reverse($imap_auth_mech);
+            }
+        } else {
+            $imap_auth_mech = array($this->_init['authmethod']);
+        }
+
+        $login_err = null;
+
+        foreach ($imap_auth_mech as $method) {
+            try {
+                $resp = $this->_tryLogin($method);
+                $data = $resp->data;
+                $this->_setInit('authmethod', $method);
+                unset($this->_temp['referralcount']);
+            } catch (Horde_Imap_Client_Exception_ServerResponse $e) {
+                $data = $e->resp_data;
+                if (isset($data['loginerr'])) {
+                    $login_err = $data['loginerr'];
+                }
+                $resp = false;
+            } catch (Horde_Imap_Client_Exception $e) {
+                $resp = false;
+            }
+
+            // Check for login referral (RFC 2221) response - can happen for
+            // an OK, NO, or BYE response.
+            if (isset($data['referral'])) {
+                foreach (array('hostspec', 'port', 'username') as $val) {
+                    if (!is_null($data['referral']->$val)) {
+                        $this->setParam($val, $data['referral']->$val);
+                    }
+                }
+
+                if (!is_null($data['referral']->auth)) {
+                    $this->_setInit('authmethod', $data['referral']->auth);
+                }
+
+                if (!isset($this->_temp['referralcount'])) {
+                    $this->_temp['referralcount'] = 0;
+                }
+
+                // RFC 2221 [3] - Don't follow more than 10 levels of referral
+                // without consulting the user.
+                if (++$this->_temp['referralcount'] < 10) {
+                    $this->logout();
+                    $this->_setInit('capability');
+                    $this->_setInit('namespace', array());
+                    return $this->login();
+                }
+
+                unset($this->_temp['referralcount']);
+            }
+
+            if ($resp) {
+                return $this->_loginTasks($first_login, $resp->data);
+            }
+        }
+
+        /* Try again from scratch if authentication failed in an established,
+         * previously-authenticated object. */
+        if (!empty($this->_init['authmethod'])) {
+            $this->_setInit();
+            try {
+                return $this->login();
+            } catch (Horde_Imap_Client_Exception $e) {}
+        }
+
+        /* Default to AUTHENTICATIONFAILED error (see RFC 5530[3]). */
+        if (is_null($login_err)) {
+            throw new Horde_Imap_Client_Exception(
+                Horde_Imap_Client_Translation::t("Mail server denied authentication."),
+                Horde_Imap_Client_Exception::LOGIN_AUTHENTICATIONFAILED
+            );
+        }
+
+        throw $login_err;
+    }
+
+    /**
+     * Connects to the IMAP server.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    protected function _connect()
+    {
+        if (!is_null($this->_connection)) {
+            return;
+        }
+
+        $this->_connection = new Horde_Imap_Client_Socket_Connection_Socket($this, $this->_debug);
+
+        // If we already have capability information, don't re-set with
+        // (possibly) limited information sent in the initial banner.
+        if (isset($this->_init['capability'])) {
+            $this->_temp['no_cap'] = true;
+        }
+
+        /* Get greeting information.  This is untagged so we need to specially
+         * deal with it here. */
+        try {
+            $this->_getLine($this->_pipeline());
+        } catch (Horde_Imap_Client_Exception_ServerResponse $e) {
+            if ($e->status == Horde_Imap_Client_Interaction_Server::BYE) {
+                /* Server is explicitly rejecting our connection (RFC 3501
+                 * [7.1.5]). */
+                $e->setMessage(Horde_Imap_Client_Translation::t("Server rejected connection."));
+                $e->setCode(Horde_Imap_Client_Exception::SERVER_CONNECT);
+            }
+            throw $e;
+        }
+
+        // Check for IMAP4rev1 support
+        if (!$this->queryCapability('IMAP4REV1')) {
+            throw new Horde_Imap_Client_Exception(
+                Horde_Imap_Client_Translation::t("The mail server does not support IMAP4rev1 (RFC 3501)."),
+                Horde_Imap_Client_Exception::SERVER_CONNECT
+            );
+        }
+
+        // Set language if NOT using imapproxy
+        if (empty($this->_init['imapproxy'])) {
+            if ($this->queryCapability('XIMAPPROXY')) {
+                $this->_setInit('imapproxy', true);
+            } else {
+                $this->setLanguage();
+            }
+        }
+
+        // If pre-authenticated, we need to do all login tasks now.
+        if (!empty($this->_temp['preauth'])) {
+            $this->login();
+        }
+    }
+
+    /**
+     * Authenticate to the IMAP server.
+     *
+     * @param string $method  IMAP login method.
+     *
+     * @return Horde_Imap_Client_Interaction_Pipeline  Pipeline object.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    protected function _tryLogin($method)
+    {
+        $username = $this->getParam('username');
+        $password = $this->getParam('password');
+
+        switch ($method) {
+        case 'CRAM-MD5':
+        case 'CRAM-SHA1':
+        case 'CRAM-SHA256':
+            // RFC 2195: CRAM-MD5
+            // CRAM-SHA1 & CRAM-SHA256 supported by Courier SASL library
+
+            // Need $args because PHP 5.3 doesn't allow access to $this in
+            // anonymous functions.
+            $args = array(
+                $username,
+                strtolower(substr($method, 5)),
+                $password
+            );
+
+            $cmd = $this->_command('AUTHENTICATE')->add(array(
+                $method,
+                new Horde_Imap_Client_Interaction_Command_Continuation(function($ob) use ($args) {
+                    return new Horde_Imap_Client_Data_Format_List(
+                        base64_encode($args[0] . ' ' . hash_hmac($args[1], base64_decode($ob->token->current()), $args[2], false))
+                    );
+                })
+            ));
+            $cmd->debug = sprintf('[AUTHENTICATE %s Command - username: %s]', $method, $username);
+            break;
+
+        case 'DIGEST-MD5':
+            // RFC 2831/4422; obsoleted by RFC 6331
+
+            // Need $args because PHP 5.3 doesn't allow access to $this in
+            // anonymous functions.
+            $args = array(
+                $username,
+                $password,
+                $this->getParam('hostspec')
+            );
+
+            $cmd = $this->_command('AUTHENTICATE')->add(array(
+                $method,
+                new Horde_Imap_Client_Interaction_Command_Continuation(function($ob) use ($args) {
+                    return new Horde_Imap_Client_Data_Format_List(
+                        base64_encode(new Horde_Imap_Client_Auth_DigestMD5(
+                            $args[0],
+                            $args[1],
+                            base64_decode($ob->token->current()),
+                            $args[2],
+                            'imap'
+                        ))
+                    );
+                }),
+                new Horde_Imap_Client_Interaction_Command_Continuation(function($ob) {
+                    if (strpos(base64_decode($ob->token->current()), 'rspauth=') === false) {
+                        throw new Horde_Imap_Client_Exception(
+                            Horde_Imap_Client_Translation::t("Unexpected response from server when authenticating."),
+                            Horde_Imap_Client_Exception::SERVER_CONNECT
+                        );
+                    }
+
+                    return new Horde_Imap_Client_Data_Format_List();
+                })
+            ));
+            $cmd->debug = sprintf('[AUTHENTICATE DIGEST-MD5 Command - username: %s]', $username);
+            break;
+
+        case 'LOGIN':
+            $cmd = $this->_command('LOGIN')->add(array(
+                new Horde_Imap_Client_Data_Format_Astring($username),
+                new Horde_Imap_Client_Data_Format_Astring($password)
+            ));
+            $cmd->debug = sprintf('[LOGIN Command - username: %s]', $username);
+            break;
+
+        case 'PLAIN':
+            // RFC 2595/4616 - PLAIN SASL mechanism
+            $auth = base64_encode(implode("\0", array(
+                $username,
+                $username,
+                $password
+            )));
+            $cmd = $this->_command('AUTHENTICATE')->add('PLAIN');
+
+            if ($this->queryCapability('SASL-IR')) {
+                // IMAP Extension for SASL Initial Client Response (RFC 4959)
+                $cmd->add($auth);
+                $cmd->debug = sprintf('[SASL-IR AUTHENTICATE Command - username: %s]', $username);
+            } else {
+                $cmd->add(new Horde_Imap_Client_Interaction_Command_Continuation(function($ob) use ($auth) {
+                    return new Horde_Imap_Client_Data_Format_List($auth);
+                }));
+                $cmd->debug = sprintf('[AUTHENTICATE Command - username: %s]', $username);
+            }
+            break;
+
+        default:
+            throw new Horde_Imap_Client_Exception(
+                sprintf(Horde_Imap_Client_Translation::t("Unknown authentication method: %s"), $method),
+                Horde_Imap_Client_Exception::SERVER_CONNECT
+            );
+        }
+
+        $pipeline = $this->_pipeline($cmd);
+
+        /* Set a flag indicating whether we have received a CAPABILITY
+         * response after we successfully login. Since capabilities may
+         * be different after login, we need to merge this information into
+         * the current CAPABILITY array (since some servers, e.g. Cyrus,
+         * may not include authentication capabilities that are still
+         * needed in the event this object is eventually serialized). */
+        $pipeline->data['in_login'] = true;
+
+        return $this->_sendCmd($pipeline);
+    }
+
+    /**
+     * Perform login tasks.
+     *
+     * @param boolean $firstlogin  Is this the first login?
+     * @param array $resp          The data response from the login command.
+     *                             May include:
+     *   - logincapset: (boolean) True if CAPABILITY sent after login.
+     *   - proxyreuse: (boolean) True if re-used connection via imapproxy.
+     *
+     * @return boolean  True if global login tasks should be performed.
+     */
+    protected function _loginTasks($firstlogin = true, array $resp = array())
+    {
+        /* If reusing an imapproxy connection, no need to do any of these
+         * login tasks again. */
+        if (!$firstlogin && !empty($resp['proxyreuse'])) {
+            if (isset($this->_init['enabled'])) {
+                $this->_temp['enabled'] = $this->_init['enabled'];
+            }
+
+            // If we have not yet set the language, set it now.
+            if (!isset($this->_init['lang'])) {
+                $this->_temp['lang_queue'] = true;
+                $this->setLanguage();
+                unset($this->_temp['lang_queue']);
+            }
+            return false;
+        }
+
+        /* If we logged in for first time, and server did not return
+         * capability information, we need to mark for retrieval. */
+        if ($firstlogin && empty($resp['capabilities_set'])) {
+            $this->_setInit('capability');
+        }
+
+        $this->_temp['lang_queue'] = true;
+        $this->setLanguage();
+        unset($this->_temp['lang_queue']);
+
+        /* Only active QRESYNC/CONDSTORE if caching is enabled. */
+        if ($this->_initCache()) {
+            if ($this->queryCapability('QRESYNC')) {
+                $this->_enable(array('QRESYNC'));
+            } elseif ($this->queryCapability('CONDSTORE')) {
+                $this->_enable(array('CONDSTORE'));
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     */
+    protected function _logout()
+    {
+        if (empty($this->_temp['logout'])) {
+            /* If using imapproxy, force sending these commands, since they
+             * may not be sent again if they are (likely) initialization
+             * commands. */
+            if (!empty($this->_cmdQueue) &&
+                !empty($this->_init['imapproxy'])) {
+                $this->_sendCmd($this->_pipeline());
+            }
+
+            $this->_temp['logout'] = true;
+            try {
+                $this->_sendCmd($this->_command('LOGOUT'));
+            } catch (Horde_Imap_Client_Exception_ServerResponse $e) {
+                // Ignore server errors
+            }
+            unset($this->_temp['logout']);
+        }
+    }
+
+    /**
+     */
+    protected function _sendID($info)
+    {
+        $cmd = $this->_command('ID');
+
+        if (empty($info)) {
+            $cmd->add(new Horde_Imap_Client_Data_Format_Nil());
+        } else {
+            $tmp = new Horde_Imap_Client_Data_Format_List();
+            foreach ($info as $key => $val) {
+                $tmp->add(array(
+                    new Horde_Imap_Client_Data_Format_String(strtolower($key)),
+                    new Horde_Imap_Client_Data_Format_Nstring($val)
+                ));
+            }
+            $cmd->add($tmp);
+        }
+
+        $this->_temp['id'] = $this->_sendCmd($cmd)->data['id'];
+    }
+
+    /**
+     * Parse an ID response (RFC 2971 [3.2]).
+     *
+     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
+     *                                                          object.
+     * @param Horde_Imap_Client_Tokenize $data  The server response.
+     */
+    protected function _parseID(
+        Horde_Imap_Client_Interaction_Pipeline $pipeline,
+        Horde_Imap_Client_Tokenize $data
+    )
+    {
+        $ids = array();
+
+        if (!is_null($data->next())) {
+            while (($curr = $data->next()) !== false) {
+                if (!is_null($id = $data->next())) {
+                    $ids[$curr] = $id;
+                }
+            }
+        }
+
+        $pipeline->data['id'] = $ids;
+    }
+
+    /**
+     */
+    protected function _getID()
+    {
+        if (!isset($this->_temp['id'])) {
+            $this->sendID();
+        }
+
+        return $this->_temp['id'];
+    }
+
+    /**
+     */
+    protected function _setLanguage($langs)
+    {
+        $cmd = $this->_command('LANGUAGE');
+        foreach ($langs as $lang) {
+            $cmd->add(new Horde_Imap_Client_Data_Format_Astring($lang));
+        }
+
+        if (!empty($this->_temp['lang_queue'])) {
+            $this->_cmdQueue[] = $cmd;
+            return array();
+        }
+
+        try {
+            $this->_sendCmd($cmd);
+        } catch (Horde_Imap_Client_Exception $e) {
+            $this->_setInit('lang', false);
+            return null;
+        }
+
+        return $this->_init['lang'];
+    }
+
+    /**
+     */
+    protected function _getLanguage($list)
+    {
+        if (!$list) {
+            return empty($this->_init['lang'])
+                ? null
+                : $this->_init['lang'];
+        }
+
+        if (!isset($this->_init['langavail'])) {
+            try {
+                $this->_sendCmd($this->_command('LANGUAGE'));
+            } catch (Horde_Imap_Client_Exception $e) {
+                $this->_setInit('langavail', array());
+            }
+        }
+
+        return $this->_init['langavail'];
+    }
+
+    /**
+     * Parse a LANGUAGE response (RFC 5255 [3.3]).
+     *
+     * @param Horde_Imap_Client_Tokenize $data  The server response.
+     */
+    protected function _parseLanguage(Horde_Imap_Client_Tokenize $data)
+    {
+        $lang_list = $data->flushIterator();
+
+        if (count($lang_list) == 1) {
+            // This is the language that was set.
+            $this->_setInit('lang', reset($lang_list));
+        } else {
+            // These are the languages that are available.
+            $this->_setInit('langavail', $lang_list);
+        }
+    }
+
+    /**
+     * Enable an IMAP extension (see RFC 5161).
+     *
+     * @param array $exts  The extensions to enable.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    protected function _enable($exts)
+    {
+        if ($this->queryCapability('ENABLE')) {
+            // Only enable non-enabled extensions.
+            $exts = array_diff($exts, array_keys($this->_temp['enabled']));
+            if (!empty($exts)) {
+                $this->_cmdQueue[] = $this->_command('ENABLE')->add($exts);
+                $this->_enabled($exts, 1);
+            }
+        }
+    }
+
+    /**
+     * Parse an ENABLED response (RFC 5161 [3.2]).
+     *
+     * @param Horde_Imap_Client_Tokenize $data  The server response.
+     */
+    protected function _parseEnabled(Horde_Imap_Client_Tokenize $data)
+    {
+        $this->_enabled($data->flushIterator(), 2);
+    }
+
+    /**
+     */
+    protected function _enabled($exts, $status)
+    {
+        parent::_enabled($exts, $status);
+
+        if (($status == 2) && !empty($this->_init['imapproxy'])) {
+            $this->_setInit('enabled', $this->_temp['enabled']);
+        }
+    }
+
+    /**
+     */
+    protected function _openMailbox(Horde_Imap_Client_Mailbox $mailbox, $mode)
+    {
+        $qresync = isset($this->_temp['enabled']['QRESYNC']);
+
+        $cmd = $this->_command(
+            ($mode == Horde_Imap_Client::OPEN_READONLY) ? 'EXAMINE' : 'SELECT'
+        )->add(
+            new Horde_Imap_Client_Data_Format_Mailbox($mailbox)
+        );
+        $pipeline = $this->_pipeline($cmd);
+
+        /* If QRESYNC is available, synchronize the mailbox. */
+        if ($qresync) {
+            $this->_initCache();
+            $md = $this->_cache->getMetaData($mailbox, null, array(self::CACHE_MODSEQ, 'uidvalid'));
+
+            if (isset($md[self::CACHE_MODSEQ])) {
+                if ($uids = $this->_cache->get($mailbox)) {
+                    $uids = $this->getIdsOb($uids);
+
+                    /* Check for extra long UID string. Assume that any
+                     * server that can handle QRESYNC can also handle long
+                     * input strings (at least 8 KB), so 7 KB is as good as
+                     * any guess as to an upper limit. If this occurs, provide
+                     * a range string (min -> max) instead. */
+                    if (strlen($uid_str = strval($uids)) > 7000) {
+                        $uid_str = $uids->range_string;
+                    }
+                } else {
+                    $uid_str = null;
+                }
+
+                /* Several things can happen with a QRESYNC:
+                 * 1. UIDVALIDITY may have changed.  If so, we need to expire
+                 * the cache immediately (done below).
+                 * 2. NOMODSEQ may have been returned. We can keep current
+                 * message cache data but won't be able to do flag caching.
+                 * 3. VANISHED/FETCH information was returned. These responses
+                 * will have already been handled by those response handlers.
+                 * 4. We are already synced with the local server in which
+                 * case it acts like a normal EXAMINE/SELECT. */
+                $cmd->add(new Horde_Imap_Client_Data_Format_List(array(
+                    'QRESYNC',
+                    new Horde_Imap_Client_Data_Format_List(array_filter(array(
+                        $md['uidvalid'],
+                        $md[self::CACHE_MODSEQ],
+                        $uid_str
+                    )))
+                )));
+            }
+
+            /* Let the 'CLOSED' response code handle mailbox switching if
+             * QRESYNC is active. */
+            if ($this->_selected) {
+                $pipeline->data['qresyncmbox'] = array($mailbox, $mode);
+            } else {
+                $this->_changeSelected($mailbox, $mode);
+            }
+        } else {
+            if (!isset($this->_temp['enabled']['CONDSTORE']) &&
+                $this->_initCache() &&
+                $this->queryCapability('CONDSTORE')) {
+                /* Activate CONDSTORE now if ENABLE is not available. */
+                $cmd->add(new Horde_Imap_Client_Data_Format_List('CONDSTORE'));
+                $this->_enabled(array('CONDSTORE'), 2);
+            }
+
+            $this->_changeSelected($mailbox, $mode);
+        }
+
+        try {
+            $this->_sendCmd($pipeline);
+        } catch (Horde_Imap_Client_Exception_ServerResponse $e) {
+            // An EXAMINE/SELECT failure with a return of 'NO' will cause the
+            // current mailbox to be unselected.
+            if ($e->status == Horde_Imap_Client_Interaction_Server::NO) {
+                $this->_changeSelected(null);
+                $this->_mode = 0;
+                if (!$e->getCode()) {
+                    throw new Horde_Imap_Client_Exception(
+                        sprintf(Horde_Imap_Client_Translation::t("Could not open mailbox \"%s\"."), $mailbox),
+                        Horde_Imap_Client_Exception::MAILBOX_NOOPEN
+                    );
+                }
+            }
+            throw $e;
+        }
+
+        if ($qresync) {
+            /* Mailbox is fully sync'd. */
+            $this->_mailboxOb()->sync = true;
+        }
+    }
+
+    /**
+     */
+    protected function _createMailbox(Horde_Imap_Client_Mailbox $mailbox, $opts)
+    {
+        $cmd = $this->_command('CREATE')->add(
+            new Horde_Imap_Client_Data_Format_Mailbox($mailbox)
+        );
+
+        if (!empty($opts['special_use'])) {
+            $cmd->add(array(
+                'USE',
+                new Horde_Imap_Client_Data_Format_List($opts['special_use'])
+            ));
+        }
+
+        // CREATE returns no untagged information (RFC 3501 [6.3.3])
+        $this->_sendCmd($cmd);
+    }
+
+    /**
+     */
+    protected function _deleteMailbox(Horde_Imap_Client_Mailbox $mailbox)
+    {
+        // Some IMAP servers will not allow a delete of a currently open
+        // mailbox.
+        if ($mailbox->equals($this->_selected)) {
+            $this->close();
+        }
+
+        $cmd = $this->_command('DELETE')->add(
+            new Horde_Imap_Client_Data_Format_Mailbox($mailbox)
+        );
+
+        try {
+            // DELETE returns no untagged information (RFC 3501 [6.3.4])
+            $this->_sendCmd($cmd);
+        } catch (Horde_Imap_Client_Exception $e) {
+            // Some IMAP servers won't allow a mailbox delete unless all
+            // messages in that mailbox are deleted.
+            $this->expunge($mailbox, array(
+                'delete' => true
+            ));
+            $this->_sendCmd($cmd);
+        }
+    }
+
+    /**
+     */
+    protected function _renameMailbox(Horde_Imap_Client_Mailbox $old,
+                                      Horde_Imap_Client_Mailbox $new)
+    {
+        // Some IMAP servers will not allow a rename of a currently open
+        // mailbox.
+        if ($old->equals($this->_selected)) {
+            $this->close();
+        }
+
+        // RENAME returns no untagged information (RFC 3501 [6.3.5])
+        $this->_sendCmd(
+            $this->_command('RENAME')->add(array(
+                new Horde_Imap_Client_Data_Format_Mailbox($old),
+                new Horde_Imap_Client_Data_Format_Mailbox($new)
+            ))
+        );
+    }
+
+    /**
+     */
+    protected function _subscribeMailbox(Horde_Imap_Client_Mailbox $mailbox,
+                                         $subscribe)
+    {
+        // SUBSCRIBE/UNSUBSCRIBE returns no untagged information (RFC 3501
+        // [6.3.6 & 6.3.7])
+        $this->_sendCmd(
+            $this->_command(
+                $subscribe ? 'SUBSCRIBE' : 'UNSUBSCRIBE'
+            )->add(
+                new Horde_Imap_Client_Data_Format_Mailbox($mailbox)
+            )
+        );
+    }
+
+    /**
+     */
+    protected function _listMailboxes($pattern, $mode, $options)
+    {
+        // RFC 5258 [3.1]: Use LSUB for MBOX_SUBSCRIBED if no other server
+        // return options are specified.
+        if (($mode == Horde_Imap_Client::MBOX_SUBSCRIBED) &&
+            empty($options['attributes']) &&
+            empty($options['children']) &&
+            empty($options['recursivematch']) &&
+            empty($options['remote']) &&
+            empty($options['special_use']) &&
+            empty($options['status'])) {
+            return $this->_getMailboxList(
+                $pattern,
+                Horde_Imap_Client::MBOX_SUBSCRIBED,
+                array(
+                    'delimiter' => !empty($options['delimiter']),
+                    'flat' => !empty($options['flat']),
+                    'no_listext' => true
+                )
+            );
+        }
+
+        // Get the list of subscribed/unsubscribed mailboxes. Since LSUB is
+        // not guaranteed to have correct attributes, we must use LIST to
+        // ensure we receive the correct information.
+        if (($mode != Horde_Imap_Client::MBOX_ALL) &&
+            !$this->queryCapability('LIST-EXTENDED')) {
+            $subscribed = $this->_getMailboxList($pattern, Horde_Imap_Client::MBOX_SUBSCRIBED, array('flat' => true));
+
+            // If mode is subscribed, and 'flat' option is true, we can
+            // return now.
+            if (($mode == Horde_Imap_Client::MBOX_SUBSCRIBED) &&
+                !empty($options['flat'])) {
+                return $subscribed;
+            }
+        } else {
+            $subscribed = null;
+        }
+
+        return $this->_getMailboxList($pattern, $mode, $options, $subscribed);
+    }
+
+    /**
+     * Obtain a list of mailboxes.
+     *
+     * @param array $pattern     The mailbox search pattern(s).
+     * @param integer $mode      Which mailboxes to return.
+     * @param array $options     Additional options. 'no_listext' will skip
+     *                           using the LIST-EXTENDED capability.
+     * @param array $subscribed  A list of subscribed mailboxes.
+     *
+     * @return array  See listMailboxes(().
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    protected function _getMailboxList($pattern, $mode, $options,
+                                       $subscribed = null)
+    {
+        $check = (($mode != Horde_Imap_Client::MBOX_ALL) && !is_null($subscribed));
+
+        // Setup entry for use in _parseList().
+        $pipeline = $this->_pipeline();
+        $pipeline->data['mailboxlist'] = array(
+            'check' => $check,
+            'ext' => false,
+            'options' => $options,
+            'subexist' => ($mode == Horde_Imap_Client::MBOX_SUBSCRIBED_EXISTS),
+            'subscribed' => ($check ? array_flip(array_map('strval', $subscribed)) : null)
+        );
+        $pipeline->data['listresponse'] = array();
+
+        $cmds = array();
+        $return_opts = new Horde_Imap_Client_Data_Format_List();
+
+        if ($this->queryCapability('LIST-EXTENDED') &&
+            empty($options['no_listext'])) {
+            $cmd = $this->_command('LIST');
+            $pipeline->data['mailboxlist']['ext'] = true;
+
+            $select_opts = new Horde_Imap_Client_Data_Format_List();
+
+            if (($mode == Horde_Imap_Client::MBOX_SUBSCRIBED) ||
+                ($mode == Horde_Imap_Client::MBOX_SUBSCRIBED_EXISTS)) {
+                $select_opts->add('SUBSCRIBED');
+                $return_opts->add('SUBSCRIBED');
+            }
+
+            if (!empty($options['remote'])) {
+                $select_opts->add('REMOTE');
+            }
+
+            if (!empty($options['recursivematch'])) {
+                $select_opts->add('RECURSIVEMATCH');
+            }
+
+            $cmd->add(array(
+                $select_opts,
+                ''
+            ));
+
+            $tmp = new Horde_Imap_Client_Data_Format_List();
+            foreach ($pattern as $val) {
+                $tmp->add(new Horde_Imap_Client_Data_Format_ListMailbox($val));
+            }
+            $cmd->add($tmp);
+
+            if (!empty($options['children'])) {
+                $return_opts->add('CHILDREN');
+            }
+
+            if (!empty($options['special_use'])) {
+                $return_opts->add('SPECIAL-USE');
+            }
+
+            $cmds[] = $cmd;
+        } else {
+            foreach ($pattern as $val) {
+                $cmds[] = $this->_command(
+                    ($mode == Horde_Imap_Client::MBOX_SUBSCRIBED) ? 'LSUB' : 'LIST'
+                )->add(array(
+                    '',
+                    new Horde_Imap_Client_Data_Format_ListMailbox($val)
+                ));
+            }
+        }
+
+        /* LIST-STATUS does NOT depend on LIST-EXTENDED. */
+        if (!empty($options['status']) &&
+            $this->queryCapability('LIST-STATUS')) {
+            $available_status = array(
+                Horde_Imap_Client::STATUS_MESSAGES,
+                Horde_Imap_Client::STATUS_RECENT,
+                Horde_Imap_Client::STATUS_UIDNEXT,
+                Horde_Imap_Client::STATUS_UIDVALIDITY,
+                Horde_Imap_Client::STATUS_UNSEEN,
+                Horde_Imap_Client::STATUS_HIGHESTMODSEQ
+            );
+
+            $status_opts = array();
+            foreach (array_intersect($this->_statusFields, $available_status) as $key => $val) {
+                if ($options['status'] & $val) {
+                    $status_opts[] = $key;
+                }
+            }
+
+            if (count($status_opts)) {
+                $return_opts->add(array(
+                    'STATUS',
+                    new Horde_Imap_Client_Data_Format_List(
+                        array_map('strtoupper', $status_opts)
+                    )
+                ));
+            }
+        }
+
+        foreach ($cmds as $val) {
+            if (count($return_opts)) {
+                $val->add(array(
+                    'RETURN',
+                    $return_opts
+                ));
+            }
+
+            $pipeline->add($val);
+        }
+
+        try {
+            $lr = $this->_sendCmd($pipeline)->data['listresponse'];
+        } catch (Horde_Imap_Client_Exception_ServerResponse $e) {
+            /* Archiveopteryx 3.1.3 can't process empty list-select-opts list.
+             * Retry using base IMAP4rev1 functionality. */
+            if (($e->status == Horde_Imap_Client_Interaction_Server::BAD) &&
+                $this->queryCapability('LIST-EXTENDED')) {
+                $this->_unsetCapability('LIST-EXTENDED');
+                return $this->_listMailboxes($pattern, $mode, $options);
+            }
+
+            throw $e;
+        }
+
+        if (!empty($options['flat'])) {
+            return array_values($lr);
+        }
+
+        /* Add in STATUS return, if needed. */
+        if (!empty($options['status'])) {
+            foreach ($pattern as $val) {
+                $val_utf8 = Horde_Imap_Client_Utf7imap::Utf7ImapToUtf8($val);
+                if (isset($lr[$val_utf8])) {
+                    $lr[$val_utf8]['status'] = $this->_prepareStatusResponse($status_opts, $val_utf8);
+                }
+            }
+        }
+
+        return $lr;
+    }
+
+    /**
+     * Parse a LIST/LSUB response (RFC 3501 [7.2.2 & 7.2.3]).
+     *
+     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
+     *                                                          object.
+     * @param Horde_Imap_Client_Tokenize $data  The server response (includes
+     *                                          type as first token).
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    protected function _parseList(
+        Horde_Imap_Client_Interaction_Pipeline $pipeline,
+        Horde_Imap_Client_Tokenize $data
+    )
+    {
+        $data->next();
+        $attr = $data->flushIterator();
+        $delimiter = $data->next();
+        $mbox = Horde_Imap_Client_Mailbox::get($data->next(), true);
+        $ml = $pipeline->data['mailboxlist'];
+
+        if ($ml['check'] &&
+            $ml['subexist'] &&
+            // subscribed list is in UTF-8
+            !isset($ml['subscribed'][strval($mbox)])) {
+            return;
+        } elseif ((!$ml['check'] && $ml['subexist']) ||
+                   (empty($ml['options']['flat']) &&
+                    !empty($ml['options']['attributes']))) {
+            $attr = array_flip(array_map('strtolower', $attr));
+            if ($ml['subexist'] &&
+                !$ml['check'] &&
+                isset($attr['\\nonexistent'])) {
+                return;
+            }
+        }
+
+        if (empty($ml['options']['flat'])) {
+            $tmp = array(
+                'mailbox' => $mbox
+            );
+
+            if (!empty($ml['options']['attributes'])) {
+                /* RFC 5258 [3.4]: inferred attributes. */
+                if ($ml['ext']) {
+                    if (isset($attr['\\noinferiors'])) {
+                        $attr['\\hasnochildren'] = 1;
+                    }
+                    if (isset($attr['\\nonexistent'])) {
+                        $attr['\\noselect'] = 1;
+                    }
+                }
+                $tmp['attributes'] = array_keys($attr);
+            }
+            if (!empty($ml['options']['delimiter'])) {
+                $tmp['delimiter'] = $delimiter;
+            }
+            if ($data->next() !== false) {
+                $tmp['extended'] = $data->flushIterator();
+            }
+            $pipeline->data['listresponse'][strval($mbox)] = $tmp;
+        } else {
+            $pipeline->data['listresponse'][] = $mbox;
+        }
+    }
+
+    /**
+     */
+    protected function _status($mboxes, $flags)
+    {
+        $out = $to_process = array();
+        $pipeline = $this->_pipeline();
+        $unseen_flags = array(
+            Horde_Imap_Client::STATUS_FIRSTUNSEEN,
+            Horde_Imap_Client::STATUS_UNSEEN
+        );
+
+        foreach ($mboxes as $mailbox) {
+            /* If FLAGS/PERMFLAGS/UIDNOTSTICKY/FIRSTUNSEEN are needed, we must
+             * do a SELECT/EXAMINE to get this information (data will be
+             * caught in the code below). */
+            if (($flags & Horde_Imap_Client::STATUS_FIRSTUNSEEN) ||
+                ($flags & Horde_Imap_Client::STATUS_FLAGS) ||
+                ($flags & Horde_Imap_Client::STATUS_PERMFLAGS) ||
+                ($flags & Horde_Imap_Client::STATUS_UIDNOTSTICKY)) {
+                $this->openMailbox($mailbox);
+            }
+
+            $mbox_ob = $this->_mailboxOb($mailbox);
+            $data = $query = array();
+
+            foreach ($this->_statusFields as $key => $val) {
+                if (!($val & $flags)) {
+                    continue;
+                }
+
+                if ($val == Horde_Imap_Client::STATUS_HIGHESTMODSEQ) {
+                    /* Don't include modseq returns if server does not support
+                     * it. */
+                    if (!$this->queryCapability('CONDSTORE')) {
+                        continue;
+                    }
+
+                    /* Even though CONDSTORE is available, it may not yet have
+                     * been enabled. */
+                    if (!isset($this->_temp['enabled']['CONDSTORE'])) {
+                        $this->_enabled(array('CONDSTORE'), 2);
+                    }
+                }
+
+                if ($mailbox->equals($this->_selected)) {
+                    if (!is_null($tmp = $mbox_ob->getStatus($val))) {
+                        $data[$key] = $tmp;
+                    } elseif (($val == Horde_Imap_Client::STATUS_UIDNEXT) &&
+                              ($flags & Horde_Imap_Client::STATUS_UIDNEXT_FORCE)) {
+                        /* UIDNEXT is not mandatory. */
+                        if ($mbox_ob->getStatus(Horde_Imap_Client::STATUS_MESSAGES) == 0) {
+                            $data[$key] = 0;
+                        } else {
+                            $fquery = new Horde_Imap_Client_Fetch_Query();
+                            $fquery->uid();
+                            $fetch_res = $this->fetch($this->_selected, $fquery, array(
+                                'ids' => $this->getIdsOb(Horde_Imap_Client_Ids::LARGEST)
+                            ));
+                            $data[$key] = $fetch_res->first()->getUid() + 1;
+                        }
+                    } elseif (in_array($val, $unseen_flags)) {
+                        /* RFC 3501 [6.3.1] - FIRSTUNSEEN information is not
+                         * mandatory. If missing in EXAMINE/SELECT results, we
+                         * need to do a search. An UNSEEN count also requires
+                         * a search. */
+                        $squery = new Horde_Imap_Client_Search_Query();
+                        $squery->flag(Horde_Imap_Client::FLAG_SEEN, false);
+                        $search = $this->search($mailbox, $squery, array(
+                            'results' => array(
+                                Horde_Imap_Client::SEARCH_RESULTS_MIN,
+                                Horde_Imap_Client::SEARCH_RESULTS_COUNT
+                            ),
+                            'sequence' => true
+                        ));
+
+                        $mbox_ob->setStatus(Horde_Imap_Client::STATUS_FIRSTUNSEEN, $search['min']);
+                        $mbox_ob->setStatus(Horde_Imap_Client::STATUS_UNSEEN, $search['count']);
+
+                        $data[$key] = $mbox_ob->getStatus($val);
+                    }
+                } else {
+                    $query[] = $key;
+                }
+            }
+
+            $out[strval($mailbox)] = $data;
+
+            if (count($query)) {
+                $pipeline->add(
+                    $this->_command('STATUS')->add(array(
+                        new Horde_Imap_Client_Data_Format_Mailbox($mailbox),
+                        new Horde_Imap_Client_Data_Format_List(
+                            array_map('strtoupper', $query)
+                        )
+                    ))
+                );
+                $to_process[] = array($query, $mailbox);
+            }
+        }
+
+        if (count($pipeline)) {
+            $this->_sendCmd($pipeline);
+
+            foreach ($to_process as $val) {
+                $out[strval($val[1])] += $this->_prepareStatusResponse($val[0], $val[1]);
+            }
+        }
+
+        return $out;
+    }
+
+    /**
+     * Parse a STATUS response (RFC 3501 [7.2.4], RFC 4551 [3.6])
+     *
+     * @param Horde_Imap_Client_Tokenize $data  Token data
+     */
+    protected function _parseStatus(Horde_Imap_Client_Tokenize $data)
+    {
+        // Mailbox name is in UTF7-IMAP
+        $mbox_ob = $this->_mailboxOb(
+            Horde_Imap_Client_Mailbox::get($data->next(), true)
+        );
+
+        $data->next();
+
+        while (($k = $data->next()) !== false) {
+            $mbox_ob->setStatus(
+                $this->_statusFields[strtolower($k)],
+                $data->next()
+            );
+        }
+    }
+
+    /**
+     * Prepares a status response for a mailbox.
+     *
+     * @param array $request   The status keys to return.
+     * @param string $mailbox  The mailbox to query.
+     */
+    protected function _prepareStatusResponse($request, $mailbox)
+    {
+        $mbox_ob = $this->_mailboxOb($mailbox);
+        $out = array();
+
+        foreach ($request as $val) {
+            $out[$val] = $mbox_ob->getStatus($this->_statusFields[$val]);
+        }
+
+        return $out;
+    }
+
+    /**
+     */
+    protected function _append(Horde_Imap_Client_Mailbox $mailbox, $data,
+                               $options)
+    {
+        // Check for MULTIAPPEND extension (RFC 3502)
+        if ((count($data) > 1) && !$this->queryCapability('MULTIAPPEND')) {
+            $result = $this->getIdsOb();
+            foreach (array_keys($data) as $key) {
+                $res = $this->_append($mailbox, array($data[$key]), $options);
+                if (($res === true) || ($result === true)) {
+                    $result = true;
+                } else {
+                    $result->add($res);
+                }
+            }
+            return $result;
+        }
+
+        // Check for CATENATE extension (RFC 4469)
+        $catenate = $this->queryCapability('CATENATE');
+
+        $asize = 0;
+
+        $cmd = $this->_command('APPEND')->add(
+            new Horde_Imap_Client_Data_Format_Mailbox($mailbox)
+        );
+        $cmd->literal8 = true;
+
+        foreach (array_keys($data) as $key) {
+            if (!empty($data[$key]['flags'])) {
+                $tmp = new Horde_Imap_Client_Data_Format_List();
+                foreach ($data[$key]['flags'] as $val) {
+                    /* Ignore recent flag. RFC 3501 [9]: flag definition */
+                    if (strcasecmp($val, Horde_Imap_Client::FLAG_RECENT) !== 0) {
+                        $tmp->add($val);
+                    }
+                }
+                $cmd->add($tmp);
+            }
+
+            if (!empty($data[$key]['internaldate'])) {
+                $cmd->add(new Horde_Imap_Client_Data_Format_DateTime($data[$key]['internaldate']));
+            }
+
+            if (is_array($data[$key]['data'])) {
+                if ($catenate) {
+                    $cmd->add('CATENATE');
+                    $tmp = new Horde_Imap_Client_Data_Format_List();
+                } else {
+                    $data_stream = new Horde_Stream_Temp();
+                }
+
+                reset($data[$key]['data']);
+                while (list(,$v) = each($data[$key]['data'])) {
+                    switch ($v['t']) {
+                    case 'text':
+                        if ($catenate) {
+                            $tmp->add(array(
+                                'TEXT',
+                                $this->_appendData($v['v'], $asize)
+                            ));
+                        } else {
+                            if (is_resource($v['v'])) {
+                                rewind($v['v']);
+                            }
+                            $data_stream->add($v['v']);
+                        }
+                        break;
+
+                    case 'url':
+                        if ($catenate) {
+                            $tmp->add(array(
+                                'URL',
+                                new Horde_Imap_Client_Data_Format_Astring($v['v'])
+                            ));
+                        } else {
+                            $data_stream->add($this->_convertCatenateUrl($v['v']));
+                        }
+                        break;
+                    }
+                }
+
+                if ($catenate) {
+                    $cmd->add($tmp);
+                } else {
+                    $cmd->add($this->_appendData($data_stream->stream, $asize));
+                }
+            } else {
+                $cmd->add($this->_appendData($data[$key]['data'], $asize));
+            }
+        }
+
+        /* Although it is normally more efficient to use LITERAL+, disable if
+         * payload is over 0.5 MB because it allows the server to throw error
+         * before we potentially push a lot of data to server that would
+         * otherwise be ignored (see RFC 4549 [4.2.2.3]).
+         * Additionally, if using BINARY, since so many IMAP servers have
+         * issues with APPEND + BINARY, don't use LITERAL+ since servers may
+         * send BAD after initial command. */
+        $cmd->literalplus = (($asize < 524288) && !$this->queryCapability('BINARY'));
+
+        // If the mailbox is currently selected read-only, we need to close
+        // because some IMAP implementations won't allow an append. And some
+        // implementations don't support append on ANY open mailbox. Be safe
+        // and always make sure we are in a non-selected state.
+        $this->close();
+
+        try {
+            $resp = $this->_sendCmd($cmd);
+        } catch (Horde_Imap_Client_Exception $e) {
+            switch ($e->getCode()) {
+            case $e::CATENATE_BADURL:
+            case $e::CATENATE_TOOBIG:
+                /* Cyrus 2.4 (at least as of .14) has a broken CATENATE (see
+                 * Bug #11111). Regardless, if CATENATE is broken, we can try
+                 * to fallback to APPEND. */
+                $this->_unsetCapability('CATENATE');
+                return $this->_append($mailbox, $data, $options);
+
+            case $e::DISCONNECT:
+                /* Workaround broken literal8 on Cyrus. */
+                if ($this->queryCapability('BINARY')) {
+                    // Need to re-login first before removing capability.
+                    $this->login();
+                    $this->_unsetCapability('BINARY');
+                    return $this->_append($mailbox, $data, $options);
+                }
+                break;
+            }
+
+            if (!empty($options['create']) &&
+                !empty($e->resp_data['trycreate'])) {
+                $this->createMailbox($mailbox);
+                unset($options['create']);
+                return $this->_append($mailbox, $data, $options);
+            }
+
+            /* RFC 3516/4466 says we should be able to append binary data
+             * using literal8 "~{#} format", but it doesn't seem to work on
+             * all servers tried (UW-IMAP/Cyrus). Do a last-ditch check for
+             * broken BINARY and attempt to fix here. */
+            if ($this->queryCapability('BINARY') &&
+                ($e instanceof Horde_Imap_Client_Exception_ServerResponse)) {
+                switch ($e->status) {
+                case Horde_Imap_Client_Interaction_Server::BAD:
+                case Horde_Imap_Client_Interaction_Server::NO:
+                    $this->_unsetCapability('BINARY');
+                    return $this->_append($mailbox, $data, $options);
+                }
+            }
+
+            throw $e;
+        }
+
+        /* If we reach this point and have data in 'appenduid', UIDPLUS (RFC
+         * 4315) has done the dirty work for us. */
+        return isset($resp->data['appenduid'])
+            ? $resp->data['appenduid']
+            : true;
+    }
+
+    /**
+     * Prepares append message data for insertion into the IMAP command
+     * string.
+     *
+     * @param mixed $data      Either a resource or a string.
+     * @param integer &$asize  Total append size.
+     *
+     * @return Horde_Imap_Client_Data_Format_String  The data object.
+     */
+    protected function _appendData($data, &$asize)
+    {
+        if (is_resource($data)) {
+            rewind($data);
+        }
+
+        $ob = new Horde_Imap_Client_Data_Format_String($data, array(
+            'eol' => true,
+            'skipscan' => true
+        ));
+
+        // APPEND data MUST be sent in a literal (RFC 3501 [6.3.11]).
+        $ob->forceLiteral();
+
+        $asize += $ob->length();
+
+        return $ob;
+    }
+
+    /**
+     * Converts a CATENATE URL to stream data.
+     *
+     * @param string $url  The CATENATE URL.
+     *
+     * @return resource  A stream containing the data.
+     */
+    protected function _convertCatenateUrl($url)
+    {
+        $e = $part = null;
+        $url = new Horde_Imap_Client_Url($url);
+
+        if (!is_null($url->mailbox) && !is_null($url->uid)) {
+            try {
+                $status_res = is_null($url->uidvalidity)
+                    ? null
+                    : $this->status($url->mailbox, Horde_Imap_Client::STATUS_UIDVALIDITY);
+
+                if (is_null($status_res) ||
+                    ($status_res['uidvalidity'] == $url->uidvalidity)) {
+                    if (!isset($this->_temp['catenate_ob'])) {
+                        $this->_temp['catenate_ob'] = new Horde_Imap_Client_Socket_Catenate($this);
+                    }
+                    $part = $this->_temp['catenate_ob']->fetchFromUrl($url);
+                }
+            } catch (Horde_Imap_Client_Exception $e) {}
+        }
+
+        if (is_null($part)) {
+            $message = 'Bad IMAP URL given in CATENATE data: ' . strval($url);
+            if ($e) {
+                $message .= ' ' . $e->getMessage();
+            }
+
+            throw new InvalidArgumentException($message);
+        }
+
+        return $part;
+    }
+
+    /**
+     */
+    protected function _check()
+    {
+        // CHECK returns no untagged information (RFC 3501 [6.4.1])
+        $this->_sendCmd($this->_command('CHECK'));
+    }
+
+    /**
+     */
+    protected function _close($options)
+    {
+        if (empty($options['expunge'])) {
+            if ($this->queryCapability('UNSELECT')) {
+                // RFC 3691 defines 'UNSELECT' for precisely this purpose
+                $this->_sendCmd($this->_command('UNSELECT'));
+            } else {
+                // RFC 3501 [6.4.2]: to close a mailbox without expunge,
+                // select a non-existent mailbox. Selecting a null mailbox
+                // should do the trick.
+                try {
+                    $this->_sendCmd($this->_command('SELECT')->add(''));
+                } catch (Horde_Imap_Client_Exception_ServerResponse $e) {
+                    // Ignore error; it is expected.
+                }
+            }
+        } else {
+            // If caching, we need to know the UIDs being deleted, so call
+            // expunge() before calling close().
+            if ($this->_initCache(true)) {
+                $this->expunge($this->_selected);
+            }
+
+            // CLOSE returns no untagged information (RFC 3501 [6.4.2])
+            $this->_sendCmd($this->_command('CLOSE'));
+        }
+    }
+
+    /**
+     */
+    protected function _expunge($options)
+    {
+        $expunged_ob = $modseq = null;
+        $ids = $options['ids'];
+        $list_msgs = !empty($options['list']);
+        $uidplus = $this->queryCapability('UIDPLUS');
+        $unflag = array();
+        $use_cache = $this->_initCache(true);
+
+        if ($ids->all) {
+            if (!$uidplus && ($list_msgs || $use_cache)) {
+                $ids = $this->resolveIds($this->_selected, $ids, 2);
+            }
+        } elseif ($uidplus) {
+            /* If QRESYNC is not available, and we are returning the list of
+             * expunged messages (or we are caching), we have to make sure we
+             * have a mapping of Sequence -> UIDs. If we have QRESYNC, the
+             * server SHOULD return a VANISHED response with UIDs. However,
+             * even if the server returns EXPUNGEs instead, we can use
+             * vanished() to grab the list. */
+            unset($this->_temp['search_save']);
+            if (isset($this->_temp['enabled']['QRESYNC'])) {
+                $ids = $this->resolveIds($this->_selected, $ids, 1);
+                if ($list_msgs) {
+                    $modseq = $this->_mailboxOb()->getStatus(Horde_Imap_Client::STATUS_HIGHESTMODSEQ);
+                }
+            } else {
+                $ids = $this->resolveIds($this->_selected, $ids, ($list_msgs || $use_cache) ? 2 : 1);
+            }
+            if (!empty($this->_temp['search_save'])) {
+                $ids = $this->getIdsOb(Horde_Imap_Client_Ids::SEARCH_RES);
+            }
+        } else {
+            /* Without UIDPLUS, need to temporarily unflag all messages marked
+             * as deleted but not a part of requested IDs to delete. Use NOT
+             * searches to accomplish this goal. */
+            $squery = new Horde_Imap_Client_Search_Query();
+            $squery->flag(Horde_Imap_Client::FLAG_DELETED, true);
+            $squery->ids($ids, true);
+
+            $s_res = $this->search($this->_selected, $squery, array(
+                'results' => array(
+                    Horde_Imap_Client::SEARCH_RESULTS_MATCH,
+                    Horde_Imap_Client::SEARCH_RESULTS_SAVE
+                )
+            ));
+
+            $this->store($this->_selected, array(
+                'ids' => empty($s_res['save']) ? $s_res['match'] : $this->getIdsOb(Horde_Imap_Client_Ids::SEARCH_RES),
+                'remove' => array(Horde_Imap_Client::FLAG_DELETED)
+            ));
+
+            $unflag = $s_res['match'];
+        }
+
+        if ($list_msgs) {
+            $expunged_ob = $this->getIdsOb();
+            $this->_temp['expunged'] = $expunged_ob;
+        }
+
+        /* Always use UID EXPUNGE if available. */
+        if ($uidplus) {
+            /* We can only pipeline STORE w/ EXPUNGE if using UIDs and UIDPLUS
+             * is available. */
+            if (empty($options['delete'])) {
+                $pipeline = $this->_pipeline();
+            } else {
+                $pipeline = $this->_storeCmd(array(
+                    'add' => array(
+                        Horde_Imap_Client::FLAG_DELETED
+                    ),
+                    'ids' => $ids
+                ));
+            }
+
+            foreach ($ids->split(2000) as $val) {
+                $pipeline->add(
+                    $this->_command('UID EXPUNGE')->add($val)
+                );
+            }
+
+            $resp = $this->_sendCmd($pipeline);
+        } else {
+            if (!empty($options['delete'])) {
+                $this->store($this->_selected, array(
+                    'add' => array(Horde_Imap_Client::FLAG_DELETED),
+                    'ids' => $ids
+                ));
+            }
+
+            if ($use_cache || $list_msgs) {
+                $this->_sendCmd($this->_command('EXPUNGE'));
+            } else {
+                /* This is faster than an EXPUNGE because the server will not
+                 * return untagged EXPUNGE responses. We can only do this if
+                 * we are not updating cache information. */
+                $this->close(array('expunge' => true));
+            }
+        }
+
+        unset($this->_temp['expunged']);
+
+        if (!empty($unflag)) {
+            $this->store($this->_selected, array(
+                'add' => array(Horde_Imap_Client::FLAG_DELETED),
+                'ids' => $unflag
+            ));
+        }
+
+        if (!is_null($modseq) && !empty($resp->data['expunge_seen'])) {
+            /* There's a chance we actually did a full map of sequence -> UID,
+             * but this code should never be reached in the first place so
+             * be ultra-safe and just do a full VANISHED search. */
+            $expunged_ob = $this->vanished($this->_selected, $modseq, array(
+                'ids' => $ids
+            ));
+            $this->_deleteMsgs($this->_selected, $expunged_ob, array(
+                'pipeline' => $resp
+            ));
+        }
+
+        return $expunged_ob;
+    }
+
+    /**
+     * Parse a VANISHED response (RFC 5162 [3.6]).
+     *
+     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
+     *                                                          object.
+     * @param Horde_Imap_Client_Tokenize $data  The response data.
+     */
+    protected function _parseVanished(
+        Horde_Imap_Client_Interaction_Pipeline $pipeline,
+        Horde_Imap_Client_Tokenize $data
+    )
+    {
+        /* There are two forms of VANISHED.  VANISHED (EARLIER) will be sent
+         * in a FETCH (VANISHED) or SELECT/EXAMINE (QRESYNC) call.
+         * If this is the case, we can go ahead and update the cache
+         * immediately (we know we are caching or else QRESYNC would not be
+         * enabled). HIGHESTMODSEQ information will be updated via the tagged
+         * response. */
+        if (($curr = $data->next()) === true) {
+            if (strtoupper($data->next()) == 'EARLIER') {
+                /* Caching is guaranteed to be active if we are using
+                 * QRESYNC. */
+                $data->next();
+                $vanished = $this->getIdsOb($data->next());
+                if (isset($pipeline->data['vanished'])) {
+                    $pipeline->data['vanished']->add($vanished);
+                } else {
+                    $this->_deleteMsgs($this->_selected, $vanished, array(
+                        'pipeline' => $pipeline
+                    ));
+                }
+            }
+        } else {
+            /* The second form is just VANISHED. This is analogous to EXPUNGE
+             * and requires the message count to decrement. */
+            $this->_deleteMsgs($this->_selected, $this->getIdsOb($curr), array(
+                'decrement' => true,
+                'pipeline' => $pipeline
+            ));
+        }
+    }
+
+    /**
+     * Search a mailbox.  This driver supports all IMAP4rev1 search criteria
+     * as defined in RFC 3501.
+     */
+    protected function _search($query, $options)
+    {
+        $sort_criteria = array(
+            Horde_Imap_Client::SORT_ARRIVAL => 'ARRIVAL',
+            Horde_Imap_Client::SORT_CC => 'CC',
+            Horde_Imap_Client::SORT_DATE => 'DATE',
+            Horde_Imap_Client::SORT_DISPLAYFROM => 'DISPLAYFROM',
+            Horde_Imap_Client::SORT_DISPLAYTO => 'DISPLAYTO',
+            Horde_Imap_Client::SORT_FROM => 'FROM',
+            Horde_Imap_Client::SORT_REVERSE => 'REVERSE',
+            Horde_Imap_Client::SORT_RELEVANCY => 'RELEVANCY',
+            // This is a bogus entry to allow the sort options check to
+            // correctly work below.
+            Horde_Imap_Client::SORT_SEQUENCE => 'SEQUENCE',
+            Horde_Imap_Client::SORT_SIZE => 'SIZE',
+            Horde_Imap_Client::SORT_SUBJECT => 'SUBJECT',
+            Horde_Imap_Client::SORT_TO => 'TO'
+        );
+
+        $results_criteria = array(
+            Horde_Imap_Client::SEARCH_RESULTS_COUNT => 'COUNT',
+            Horde_Imap_Client::SEARCH_RESULTS_MATCH => 'ALL',
+            Horde_Imap_Client::SEARCH_RESULTS_MAX => 'MAX',
+            Horde_Imap_Client::SEARCH_RESULTS_MIN => 'MIN',
+            Horde_Imap_Client::SEARCH_RESULTS_RELEVANCY => 'RELEVANCY',
+            Horde_Imap_Client::SEARCH_RESULTS_SAVE => 'SAVE'
+        );
+
+        // Check if the server supports sorting (RFC 5256).
+        $esearch = $return_sort = $server_seq_sort = $server_sort = false;
+        if (!empty($options['sort'])) {
+            /* Make sure sort options are correct. If not, default to no
+             * sort. */
+            if (count(array_intersect($options['sort'], array_keys($sort_criteria))) === 0) {
+                unset($options['sort']);
+            } else {
+                $return_sort = true;
+
+                if ($server_sort = $this->queryCapability('SORT')) {
+                    /* Make sure server supports DISPLAYFROM & DISPLAYTO. */
+                    $server_sort =
+                        !array_intersect($options['sort'], array(Horde_Imap_Client::SORT_DISPLAYFROM, Horde_Imap_Client::SORT_DISPLAYTO)) ||
+                        (is_array($server_sort) &&
+                         in_array('DISPLAY', $server_sort));
+                }
+
+                /* If doing a sequence sort, need to do this on the client
+                 * side. */
+                if ($server_sort &&
+                    in_array(Horde_Imap_Client::SORT_SEQUENCE, $options['sort'])) {
+                    $server_sort = false;
+
+                    /* Optimization: If doing only a sequence sort, just do a
+                     * simple search and sort UIDs/sequences on client side. */
+                    switch (count($options['sort'])) {
+                    case 1:
+                        $server_seq_sort = true;
+                        break;
+
+                    case 2:
+                        $server_seq_sort = (reset($options['sort']) == Horde_Imap_Client::SORT_REVERSE);
+                        break;
+                    }
+                }
+            }
+        }
+
+        $charset = is_null($options['_query']['charset'])
+            ? 'US-ASCII'
+            : $options['_query']['charset'];
+
+        if ($server_sort) {
+            $cmd = $this->_command(
+                empty($options['sequence']) ? 'UID SORT' : 'SORT'
+            );
+            $results = array();
+
+            // Use ESEARCH (RFC 4466) response if server supports.
+            $esearch = false;
+
+            // Check for ESORT capability (RFC 5267)
+            if ($this->queryCapability('ESORT')) {
+                foreach ($options['results'] as $val) {
+                    if (isset($results_criteria[$val]) &&
+                        ($val != Horde_Imap_Client::SEARCH_RESULTS_SAVE)) {
+                        $results[] = $results_criteria[$val];
+                    }
+                }
+                $esearch = true;
+            }
+
+            // Add PARTIAL limiting (RFC 5267 [4.4])
+            if ((!$esearch || !empty($options['partial'])) &&
+                ($cap = $this->queryCapability('CONTEXT')) &&
+                in_array('SORT', $cap)) {
+                /* RFC 5267 indicates RFC 4466 ESEARCH support,
+                 * notwithstanding RFC 4731 support. */
+                $esearch = true;
+
+                if (!empty($options['partial'])) {
+                    /* Can't have both ALL and PARTIAL returns. */
+                    $results = array_diff($results, array('ALL'));
+
+                    $results[] = 'PARTIAL';
+                    $results[] = strval($this->getIdsOb($options['partial']));
+                }
+            }
+
+            if ($esearch && empty($this->_init['noesearch'])) {
+                $cmd->add(array(
+                    'RETURN',
+                    new Horde_Imap_Client_Data_Format_List($results)
+                ));
+            }
+
+            $tmp = new Horde_Imap_Client_Data_Format_List();
+            foreach ($options['sort'] as $val) {
+                if (isset($sort_criteria[$val])) {
+                    $tmp->add($sort_criteria[$val]);
+                }
+            }
+            $cmd->add($tmp);
+
+            // Charset is mandatory for SORT (RFC 5256 [3]).
+            $cmd->add($charset);
+        } else {
+            $cmd = $this->_command(
+                empty($options['sequence']) ? 'UID SEARCH' : 'SEARCH'
+            );
+            $esearch = false;
+            $results = array();
+
+            // Check if the server supports ESEARCH (RFC 4731).
+            if ($this->queryCapability('ESEARCH')) {
+                foreach ($options['results'] as $val) {
+                    if (isset($results_criteria[$val])) {
+                        $results[] = $results_criteria[$val];
+                    }
+                }
+                $esearch = true;
+            }
+
+            // Add PARTIAL limiting (RFC 5267 [4.4]).
+            if ((!$esearch || !empty($options['partial'])) &&
+                ($cap = $this->queryCapability('CONTEXT')) &&
+                in_array('SEARCH', $cap)) {
+                /* RFC 5267 indicates RFC 4466 ESEARCH support,
+                 * notwithstanding RFC 4731 support. */
+                $esearch = true;
+
+                if (!empty($options['partial'])) {
+                    // Can't have both ALL and PARTIAL returns.
+                    $results = array_diff($results, array('ALL'));
+
+                    $results[] = 'PARTIAL';
+                    $results[] = strval($this->getIdsOb($options['partial']));
+                }
+            }
+
+            if ($esearch && empty($this->_init['noesearch'])) {
+                // Always use ESEARCH if available because it returns results
+                // in a more compact sequence-set list
+                $cmd->add(array(
+                    'RETURN',
+                    new Horde_Imap_Client_Data_Format_List($results)
+                ));
+            }
+
+            // Charset is optional for SEARCH (RFC 3501 [6.4.4]).
+            if ($charset != 'US-ASCII') {
+                $cmd->add(array(
+                    'CHARSET',
+                    $options['_query']['charset']
+                ));
+            }
+        }
+
+        $cmd->add($options['_query']['query'], true);
+
+        $pipeline = $this->_pipeline($cmd);
+        $pipeline->data['esearchresp'] = array();
+        $er = &$pipeline->data['esearchresp'];
+        $pipeline->data['searchresp'] = $this->getIdsOb(array(), !empty($options['sequence']));
+        $sr = &$pipeline->data['searchresp'];
+
+        try {
+            $resp = $this->_sendCmd($pipeline);
+        } catch (Horde_Imap_Client_Exception $e) {
+            if (($e instanceof Horde_Imap_Client_Exception_ServerResponse) &&
+                ($e->status == Horde_Imap_Client_Interaction_Server::NO) &&
+                ($charset != 'US-ASCII')) {
+                /* RFC 3501 [6.4.4]: BADCHARSET response code is only a
+                 * SHOULD return. If it doesn't exist, need to check for
+                 * command status of 'NO'. List of supported charsets in
+                 * the BADCHARSET response has already been parsed and stored
+                 * at this point. */
+                $s_charset = $this->_init['s_charset'];
+                $s_charset[$charset] = false;
+                $this->_setInit('s_charset', $s_charset);
+                $e->setCode(Horde_Imap_Client_Exception::BADCHARSET);
+            }
+
+            if (empty($this->_temp['search_retry'])) {
+                $this->_temp['search_retry'] = true;
+
+                /* Bug #9842: Workaround broken Cyrus servers (as of
+                 * 2.4.7). */
+                if ($esearch && ($charset != 'US-ASCII')) {
+                    $this->_unsetCapability('ESEARCH');
+                    $this->_setInit('noesearch', true);
+
+                    try {
+                        return $this->_search($query, $options);
+                    } catch (Horde_Imap_Client_Exception $e) {}
+                }
+
+                /* Try to convert charset. */
+                if (($e->getCode() == Horde_Imap_Client_Exception::BADCHARSET) &&
+                    ($charset != 'US-ASCII')) {
+                    foreach (array_merge(array_keys(array_filter($this->_init['s_charset'])), array('US-ASCII')) as $val) {
+                        $this->_temp['search_retry'] = 1;
+                        $new_query = clone($query);
+                        try {
+                            $new_query->charset($val);
+                            $options['_query'] = $new_query->build($this->capability());
+                            return $this->_search($new_query, $options);
+                        } catch (Horde_Imap_Client_Exception $e) {}
+                    }
+                }
+
+                unset($this->_temp['search_retry']);
+            }
+
+            throw $e;
+        }
+
+        if ($return_sort && !$server_sort) {
+            if ($server_seq_sort) {
+                $sr->sort();
+                if (reset($options['sort']) == Horde_Imap_Client::SORT_REVERSE) {
+                    $sr->reverse();
+                }
+            } else {
+                if (!isset($this->_temp['clientsort'])) {
+                    $this->_temp['clientsort'] = new Horde_Imap_Client_Socket_ClientSort($this);
+                }
+                $sr = $this->getIdsOb($this->_temp['clientsort']->clientSort($sr, $options), !empty($options['sequence']));
+            }
+        }
+
+        $ret = array();
+        foreach ($options['results'] as $val) {
+            switch ($val) {
+            case Horde_Imap_Client::SEARCH_RESULTS_COUNT:
+                $ret['count'] = $esearch ? $er['count'] : count($sr);
+                break;
+
+            case Horde_Imap_Client::SEARCH_RESULTS_MATCH:
+                $ret['match'] = $sr;
+                break;
+
+            case Horde_Imap_Client::SEARCH_RESULTS_MAX:
+                $ret['max'] = $esearch
+                    ? (isset($er['max']) ? $er['max'] : null)
+                    : (count($sr) ? max($sr->ids) : null);
+                break;
+
+            case Horde_Imap_Client::SEARCH_RESULTS_MIN:
+                $ret['min'] = $esearch
+                    ? (isset($er['min']) ? $er['min'] : null)
+                    : (count($sr) ? min($sr->ids) : null);
+                break;
+
+            case Horde_Imap_Client::SEARCH_RESULTS_RELEVANCY:
+                $ret['relevancy'] = ($esearch && isset($er['relevancy'])) ? $er['relevancy'] : array();
+                break;
+
+            case Horde_Imap_Client::SEARCH_RESULTS_SAVE:
+                $this->_temp['search_save'] = $ret['save'] = $esearch ? empty($resp->data['searchnotsaved']) : false;
+                break;
+            }
+        }
+
+        // Add modseq data, if needed.
+        if (!empty($er['modseq'])) {
+            $ret['modseq'] = $er['modseq'];
+        }
+
+        unset($this->_temp['search_retry']);
+
+        /* Check for EXPUNGEISSUED (RFC 2180 [4.3]/RFC 5530 [3]). */
+        if (!empty($resp->data['expungeissued'])) {
+            $this->noop();
+        }
+
+        return $ret;
+    }
+
+    /**
+     * Parse a SEARCH/SORT response (RFC 3501 [7.2.5]; RFC 4466 [3];
+     * RFC 5256 [4]; RFC 5267 [3]).
+     *
+     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
+     *                                                          object.
+     * @param array $data  A list of IDs (message sequence numbers or UIDs).
+     */
+    protected function _parseSearch(
+        Horde_Imap_Client_Interaction_Pipeline $pipeline,
+        $data
+    )
+    {
+        /* More than one search response may be sent. */
+        $pipeline->data['searchresp']->add($data);
+    }
+
+    /**
+     * Parse an ESEARCH response (RFC 4466 [2.6.2])
+     * Format: (TAG "a567") UID COUNT 5 ALL 4:19,21,28
+     *
+     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
+     *                                                          object.
+     * @param Horde_Imap_Client_Tokenize $data  The server response.
+     */
+    protected function _parseEsearch(
+        Horde_Imap_Client_Interaction_Pipeline $pipeline,
+        Horde_Imap_Client_Tokenize $data
+    )
+    {
+        // Ignore search correlator information
+        if ($data->next() === true) {
+            $data->flushIterator(false);
+        }
+
+        // Ignore UID tag
+        $current = $data->next();
+        if (strtoupper($current) == 'UID') {
+            $current = $data->next();
+        }
+
+        do {
+            $val = $data->next();
+            $tag = strtoupper($current);
+
+            switch ($tag) {
+            case 'ALL':
+                $this->_parseSearch($pipeline, $val);
+                break;
+
+            case 'COUNT':
+            case 'MAX':
+            case 'MIN':
+            case 'MODSEQ':
+            case 'RELEVANCY':
+                $pipeline->data['esearchresp'][strtolower($tag)] = $val;
+                break;
+
+            case 'PARTIAL':
+                // RFC 5267 [4.4]
+                $partial = $val->flushIterator();
+                $this->_parseSearch($pipeline, end($partial));
+                break;
+            }
+        } while (($current = $data->next()) !== false);
+    }
+
+    /**
+     */
+    protected function _setComparator($comparator)
+    {
+        $cmd = $this->_command('COMPARATOR');
+        foreach ($comparator as $val) {
+            $cmd->add(new Horde_Imap_Client_Data_Format_Astring($val));
+        }
+        $this->_sendCmd($cmd);
+    }
+
+    /**
+     */
+    protected function _getComparator()
+    {
+        $resp = $this->_sendCmd($this->_command('COMPARATOR'));
+
+        return isset($resp->data['comparator'])
+            ? $resp->data['comparator']
+            : null;
+    }
+
+    /**
+     * Parse a COMPARATOR response (RFC 5255 [4.8])
+     *
+     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
+     *                                                          object.
+     * @param Horde_Imap_Client_Tokenize $data  The server response.
+     */
+    protected function _parseComparator(
+        Horde_Imap_Client_Interaction_Pipeline $pipeline,
+        $data
+    )
+    {
+        $pipeline->data['comparator'] = $data->next();
+        // Ignore optional matching comparator list
+    }
+
+    /**
+     * @throws Horde_Imap_Client_Exception_NoSupportExtension
+     */
+    protected function _thread($options)
+    {
+        $thread_criteria = array(
+            Horde_Imap_Client::THREAD_ORDEREDSUBJECT => 'ORDEREDSUBJECT',
+            Horde_Imap_Client::THREAD_REFERENCES => 'REFERENCES',
+            Horde_Imap_Client::THREAD_REFS => 'REFS'
+        );
+
+        $tsort = (isset($options['criteria']))
+            ? (is_string($options['criteria']) ? strtoupper($options['criteria']) : $thread_criteria[$options['criteria']])
+            : 'ORDEREDSUBJECT';
+
+        $cap = $this->queryCapability('THREAD');
+        if (!$cap || !in_array($tsort, $cap)) {
+            switch ($tsort) {
+            case 'ORDEREDSUBJECT':
+                if (empty($options['search'])) {
+                    $ids = $this->getIdsOb(Horde_Imap_Client_Ids::ALL, !empty($options['sequence']));
+                } else {
+                    $search_res = $this->search($this->_selected, $options['search'], array('sequence' => !empty($options['sequence'])));
+                    $ids = $search_res['match'];
+                }
+
+                /* Do client-side ORDEREDSUBJECT threading. */
+                $query = new Horde_Imap_Client_Fetch_Query();
+                $query->envelope();
+                $query->imapDate();
+
+                $fetch_res = $this->fetch($this->_selected, $query, array(
+                    'ids' => $ids
+                ));
+
+                if (!isset($this->_temp['clientsort'])) {
+                    $this->_temp['clientsort'] = new Horde_Imap_Client_Socket_ClientSort($this);
+                }
+                return $this->_temp['clientsort']->threadOrderedSubject($fetch_res, empty($options['sequence']));
+
+            case 'REFERENCES':
+            case 'REFS':
+                throw new Horde_Imap_Client_Exception_NoSupportExtension(
+                    'THREAD',
+                    sprintf('Server does not support "%s" thread sort.', $tsort)
+                );
+            }
+        }
+
+        $cmd = $this->_command(
+            empty($options['sequence']) ? 'UID THREAD' : 'THREAD'
+        )->add($tsort);
+
+        if (empty($options['search'])) {
+            $cmd->add(array(
+                'US-ASCII',
+                'ALL'
+            ));
+        } else {
+            $search_query = $options['search']->build();
+            $cmd->add(is_null($search_query['charset']) ? 'US-ASCII' : $search_query['charset']);
+            $cmd->add($search_query['query'], true);
+        }
+
+        return new Horde_Imap_Client_Data_Thread(
+            $this->_sendCmd($cmd)->data['threadparse'],
+            empty($options['sequence']) ? 'uid' : 'sequence'
+        );
+    }
+
+    /**
+     * Parse a THREAD response (RFC 5256 [4]).
+     *
+     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
+     *                                                          object.
+     * @param Horde_Imap_Client_Tokenize $data  Thread data.
+     */
+    protected function _parseThread(
+        Horde_Imap_Client_Interaction_Pipeline $pipeline,
+        Horde_Imap_Client_Tokenize $data
+    )
+    {
+        $out = array();
+
+        while ($data->next() !== false) {
+            $thread = array();
+            $this->_parseThreadLevel($thread, $data);
+            $out[] = $thread;
+        }
+
+        $pipeline->data['threadparse'] = $out;
+    }
+
+    /**
+     * Parse a level of a THREAD response (RFC 5256 [4]).
+     *
+     * @param array $thread                     Results.
+     * @param Horde_Imap_Client_Tokenize $data  Thread data.
+     * @param integer $level                    The current tree level.
+     */
+    protected function _parseThreadLevel(&$thread,
+                                         Horde_Imap_Client_Tokenize $data,
+                                         $level = 0)
+    {
+        while (($curr = $data->next()) !== false) {
+            if ($curr === true) {
+                $this->_parseThreadLevel($thread, $data, $level);
+            } elseif (!is_bool($curr)) {
+                $thread[$curr] = $level++;
+            }
+        }
+    }
+
+    /**
+     */
+    protected function _fetch(Horde_Imap_Client_Fetch_Results $results,
+                              $queries)
+    {
+        $pipeline = $this->_pipeline();
+        $pipeline->data['fetch_lookup'] = array();
+
+        foreach ($queries as $options) {
+            $this->_fetchCmd($pipeline, $options);
+            $sequence = $options['ids']->sequence;
+        }
+
+        try {
+            $resp = $this->_sendCmd($pipeline);
+
+            /* Check for EXPUNGEISSUED (RFC 2180 [4.1]/RFC 5530 [3]). */
+            if (!empty($resp->data['expungeissued'])) {
+                $this->noop();
+            }
+        } catch (Horde_Imap_Client_Exception_ServerResponse $e) {
+            // A NO response, when coupled with a sequence FETCH, most
+            // likely means that messages were expunged. RFC 2180 [4.1]
+            if ($sequence &&
+                ($e->status == Horde_Imap_Client_Interaction_Server::NO)) {
+                $this->noop();
+            }
+        } catch (Exception $e) {
+            // For any other error, ignore the Exception - fetch() is nice in
+            // that the return value explicitly handles missing data for any
+            // given message.
+        }
+
+        foreach ($resp->fetch as $k => $v) {
+            $results->get($sequence ? $k : $v->getUid())->merge($v);
+        }
+    }
+
+    /**
+     * Add a FETCH command to the given pipeline.
+     *
+     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
+     *                                                          object.
+     * @param array $options                                    Fetch query
+     *                                                          options
+     */
+    protected function _fetchCmd(
+        Horde_Imap_Client_Interaction_Pipeline $pipeline,
+        $options
+    )
+    {
+        $fetch = new Horde_Imap_Client_Data_Format_List();
+        $sequence = $options['ids']->sequence;
+
+        /* Build an IMAP4rev1 compliant FETCH query. We handle the following
+         * criteria:
+         *   BINARY[.PEEK][<section #>]<<partial>> (RFC 3516)
+         *     see BODY[] response
+         *   BINARY.SIZE[<section #>] (RFC 3516)
+         *   BODY[.PEEK][<section>]<<partial>>
+         *     <section> = HEADER, HEADER.FIELDS, HEADER.FIELDS.NOT, MIME,
+         *                 TEXT, empty
+         *     <<partial>> = 0.# (# of bytes)
+         *   BODYSTRUCTURE
+         *   ENVELOPE
+         *   FLAGS
+         *   INTERNALDATE
+         *   MODSEQ (RFC 4551)
+         *   RFC822.SIZE
+         *   UID
+         *
+         * No need to support these (can be built from other queries):
+         * ===========================================================
+         *   ALL macro => (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE)
+         *   BODY => Use BODYSTRUCTURE instead
+         *   FAST macro => (FLAGS INTERNALDATE RFC822.SIZE)
+         *   FULL macro => (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY)
+         *   RFC822 => BODY[]
+         *   RFC822.HEADER => BODY[HEADER]
+         *   RFC822.TEXT => BODY[TEXT]
+         */
+
+        foreach ($options['_query'] as $type => $c_val) {
+            switch ($type) {
+            case Horde_Imap_Client::FETCH_STRUCTURE:
+                $fetch->add('BODYSTRUCTURE');
+                break;
+
+            case Horde_Imap_Client::FETCH_FULLMSG:
+                if (empty($c_val['peek'])) {
+                    $this->openMailbox($this->_selected, Horde_Imap_Client::OPEN_READWRITE);
+                }
+                $fetch->add(
+                    'BODY' .
+                    (!empty($c_val['peek']) ? '.PEEK' : '') .
+                    '[]' .
+                    $this->_partialAtom($c_val)
+                );
+                break;
+
+            case Horde_Imap_Client::FETCH_HEADERTEXT:
+            case Horde_Imap_Client::FETCH_BODYTEXT:
+            case Horde_Imap_Client::FETCH_MIMEHEADER:
+            case Horde_Imap_Client::FETCH_BODYPART:
+            case Horde_Imap_Client::FETCH_HEADERS:
+                foreach ($c_val as $key => $val) {
+                    $cmd = ($key == 0)
+                        ? ''
+                        : $key . '.';
+                    $main_cmd = 'BODY';
+
+                    switch ($type) {
+                    case Horde_Imap_Client::FETCH_HEADERTEXT:
+                        $cmd .= 'HEADER';
+                        break;
+
+                    case Horde_Imap_Client::FETCH_BODYTEXT:
+                        $cmd .= 'TEXT';
+                        break;
+
+                    case Horde_Imap_Client::FETCH_MIMEHEADER:
+                        $cmd .= 'MIME';
+                        break;
+
+                    case Horde_Imap_Client::FETCH_BODYPART:
+                        // Remove the last dot from the string.
+                        $cmd = substr($cmd, 0, -1);
+
+                        if (!empty($val['decode']) &&
+                            $this->queryCapability('BINARY')) {
+                            $main_cmd = 'BINARY';
+                        }
+                        break;
+
+                    case Horde_Imap_Client::FETCH_HEADERS:
+                        $cmd .= 'HEADER.FIELDS';
+                        if (!empty($val['notsearch'])) {
+                            $cmd .= '.NOT';
+                        }
+                        $cmd .= ' (' . implode(' ', array_map('strtoupper', $val['headers'])) . ')';
+
+                        // Maintain a command -> label lookup so we can put
+                        // the results in the proper location.
+                        $pipeline->data['fetch_lookup'][$cmd] = $key;
+                    }
+
+                    if (empty($val['peek'])) {
+                        $this->openMailbox($this->_selected, Horde_Imap_Client::OPEN_READWRITE);
+                    }
+
+                    $fetch->add(
+                        $main_cmd .
+                        (!empty($val['peek']) ? '.PEEK' : '') .
+                        '[' . $cmd . ']' .
+                        $this->_partialAtom($val)
+                    );
+                }
+                break;
+
+            case Horde_Imap_Client::FETCH_BODYPARTSIZE:
+                if ($this->queryCapability('BINARY')) {
+                    foreach ($c_val as $val) {
+                        $fetch->add('BINARY.SIZE[' . $key . ']');
+                    }
+                }
+                break;
+
+            case Horde_Imap_Client::FETCH_ENVELOPE:
+                $fetch->add('ENVELOPE');
+                break;
+
+            case Horde_Imap_Client::FETCH_FLAGS:
+                $fetch->add('FLAGS');
+                break;
+
+            case Horde_Imap_Client::FETCH_IMAPDATE:
+                $fetch->add('INTERNALDATE');
+                break;
+
+            case Horde_Imap_Client::FETCH_SIZE:
+                $fetch->add('RFC822.SIZE');
+                break;
+
+            case Horde_Imap_Client::FETCH_UID:
+                /* A UID FETCH will always return UID information (RFC 3501
+                 * [6.4.8]). Don't add to query as it just creates a longer
+                 * FETCH command. */
+                if ($sequence || (count($options['_query']) == 1)) {
+                    $fetch->add('UID');
+                }
+                break;
+
+            case Horde_Imap_Client::FETCH_SEQ:
+                // Nothing we need to add to fetch request unless sequence is
+                // the only criteria.
+                if (count($options['_query']) == 1) {
+                    $fetch->add('UID');
+                }
+                break;
+
+            case Horde_Imap_Client::FETCH_MODSEQ:
+                /* The 'changedsince' modifier implicitly adds the MODSEQ
+                 * FETCH item (RFC 4551 [3.3.1]). Don't add to query as it
+                 * just creates a longer FETCH command. */
+                if (empty($options['changedsince'])) {
+                    $fetch->add('MODSEQ');
+                }
+                break;
+            }
+        }
+
+        /* Add changedsince parameters. */
+        if (empty($options['changedsince'])) {
+            $fetch_cmd = $fetch;
+        } else {
+            /* We might just want the list of UIDs changed since a given
+             * modseq. In that case, we don't have any other FETCH attributes,
+             * but RFC 3501 requires at least one specified attribute. */
+            $fetch_cmd = array(
+                count($fetch)
+                    ? $fetch
+                    : new Horde_Imap_Client_Data_Format_List('UID'),
+                new Horde_Imap_Client_Data_Format_List(array(
+                    'CHANGEDSINCE',
+                    new Horde_Imap_Client_Data_Format_Number($options['changedsince'])
+                ))
+            );
+        }
+
+        /* RFC 2683 [3.2.1.5] recommends that lines should be limited to
+         * "approximately 1000 octets". However, servers should allow a
+         * command line of at least "8000 octets". As a compromise, assume
+         * all modern IMAP servers handle ~2000 octets. The FETCH command
+         * should be the only command issued by this library that should ever
+         * approach this limit. For simplification, assume that the UID list
+         * is the limiting factor and split this list at a sequence comma
+         * delimiter if it exceeds 2000 characters. */
+        foreach ($options['ids']->split(2000) as $val) {
+            $cmd = $this->_command(
+                $sequence ? 'FETCH' : 'UID FETCH'
+            )->add(array(
+                $val,
+                $fetch_cmd
+            ));
+            $pipeline->add($cmd);
+        }
+    }
+
+    /**
+     * Add a partial atom to an IMAP command based on the criteria options.
+     *
+     * @param array $opts  Criteria options.
+     *
+     * @return string  The partial atom.
+     */
+    protected function _partialAtom($opts)
+    {
+        if (!empty($opts['length'])) {
+            return '<' . (empty($opts['start']) ? 0 : intval($opts['start'])) . '.' . intval($opts['length']) . '>';
+        }
+
+        return empty($opts['start'])
+            ? ''
+            : ('<' . intval($opts['start']) . '>');
+    }
+
+    /**
+     * Parse a FETCH response (RFC 3501 [7.4.2]). A FETCH response may occur
+     * due to a FETCH command, or due to a change in a message's state (i.e.
+     * the flags change).
+     *
+     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
+     *                                                          object.
+     * @param integer $id                       The message sequence number.
+     * @param Horde_Imap_Client_Tokenize $data  The server response.
+     */
+    protected function _parseFetch(
+        Horde_Imap_Client_Interaction_Pipeline $pipeline,
+        $id,
+        Horde_Imap_Client_Tokenize $data
+    )
+    {
+        if ($data->next() !== true) {
+            return;
+        }
+
+        $ob = $pipeline->fetch->get($id);
+        $ob->setSeq($id);
+
+        $flags = $modseq = $uid = false;
+
+        while (($tag = $data->next()) !== false) {
+            $tag = strtoupper($tag);
+
+            switch ($tag) {
+            case 'BODYSTRUCTURE':
+                $data->next();
+                $structure = $this->_parseBodystructure($data);
+                $structure->buildMimeIds();
+                $ob->setStructure($structure);
+                break;
+
+            case 'ENVELOPE':
+                $data->next();
+                $ob->setEnvelope($this->_parseEnvelope($data));
+                break;
+
+            case 'FLAGS':
+                $data->next();
+                $ob->setFlags($data->flushIterator());
+                $flags = true;
+                break;
+
+            case 'INTERNALDATE':
+                $ob->setImapDate($data->next());
+                break;
+
+            case 'RFC822.SIZE':
+                $ob->setSize($data->next());
+                break;
+
+            case 'UID':
+                $ob->setUid($data->next());
+                $uid = true;
+                break;
+
+            case 'MODSEQ':
+                $data->next();
+                $modseq = $data->next();
+                $data->next();
+
+                /* MODSEQ must be greater than 0, so do sanity checking. */
+                if ($modseq > 0) {
+                    $ob->setModSeq($modseq);
+
+                    /* Store MODSEQ value. It may be used as the highestmodseq
+                     * once a tagged response is received (RFC 5162 [5]). */
+                    $pipeline->data['modseqs'][] = $modseq;
+                }
+                break;
+
+            default:
+                // Catch BODY[*]<#> responses
+                if (strpos($tag, 'BODY[') === 0) {
+                    // Remove the beginning 'BODY['
+                    $tag = substr($tag, 5);
+
+                    // BODY[HEADER.FIELDS] request
+                    if (!empty($pipeline->data['fetch_lookup']) &&
+                        (strpos($tag, 'HEADER.FIELDS') !== false)) {
+                        $data->next();
+                        $sig = $tag . ' (' . implode(' ', array_map('strtoupper', $data->flushIterator())) . ')';
+
+                        // Ignore the trailing bracket
+                        $data->next();
+
+                        $ob->setHeaders($pipeline->data['fetch_lookup'][$sig], $data->next());
+                    } else {
+                        // Remove trailing bracket and octet start info
+                        $tag = substr($tag, 0, strrpos($tag, ']'));
+
+                        if (!strlen($tag)) {
+                            // BODY[] request
+                            if (!is_null($tmp = $data->next())) {
+                                $ob->setFullMsg($tmp);
+                            }
+                        } elseif (is_numeric(substr($tag, -1))) {
+                            // BODY[MIMEID] request
+                            if (!is_null($tmp = $data->next())) {
+                                $ob->setBodyPart($tag, $tmp);
+                            }
+                        } else {
+                            // BODY[HEADER|TEXT|MIME] request
+                            if (($last_dot = strrpos($tag, '.')) === false) {
+                                $mime_id = 0;
+                            } else {
+                                $mime_id = substr($tag, 0, $last_dot);
+                                $tag = substr($tag, $last_dot + 1);
+                            }
+
+                            if (!is_null($tmp = $data->next())) {
+                                switch ($tag) {
+                                case 'HEADER':
+                                    $ob->setHeaderText($mime_id, $tmp);
+                                    break;
+
+                                case 'TEXT':
+                                    $ob->setBodyText($mime_id, $tmp);
+                                    break;
+
+                                case 'MIME':
+                                    $ob->setMimeHeader($mime_id, $tmp);
+                                    break;
+                                }
+                            }
+                        }
+                    }
+                } elseif (strpos($tag, 'BINARY[') === 0) {
+                    // Catch BINARY[*]<#> responses
+                    // Remove the beginning 'BINARY[' and the trailing bracket
+                    // and octet start info
+                    $tag = substr($tag, 7, strrpos($tag, ']') - 7);
+                    $ob->setBodyPart($tag, $data->next(), empty($this->_temp['literal8']) ? '8bit' : 'binary');
+                } elseif (strpos($tag, 'BINARY.SIZE[') === 0) {
+                    // Catch BINARY.SIZE[*] responses
+                    // Remove the beginning 'BINARY.SIZE[' and the trailing
+                    // bracket and octet start info
+                    $tag = substr($tag, 12, strrpos($tag, ']') - 12);
+                    $ob->setBodyPartSize($tag, $data->next());
+                }
+                break;
+            }
+        }
+
+        /* MODSEQ issue: Oh joy. Per RFC 5162 (see Errata #1807), FETCH FLAGS
+         * responses are NOT required to provide UID information, even if
+         * QRESYNC is explicitly enabled. Caveat: the FLAGS information
+         * returned during a SELECT/EXAMINE MUST contain UIDs so we are OK
+         * there.
+         * The good news: all decent IMAP servers (Cyrus, Dovecot) will always
+         * provide UID information, so this is not normally an issue.
+         * The bad news: spec-wise, this behavior cannot be 100% guaranteed.
+         * Compromise: We will watch for a FLAGS response with a MODSEQ and
+         * check if a UID exists also. If not, put the sequence number in a
+         * queue - it is possible the UID information may appear later in an
+         * untagged response. When the command is over, double check to make
+         * sure there are none of these MODSEQ/FLAGS that are still UID-less.
+         * In the (rare) event that there is, don't cache anything and
+         * immediately close the mailbox: flags will be correctly sync'd next
+         * mailbox open so we only lose a bit of caching efficiency.
+         * Otherwise, we could end up with an inconsistent cached state. */
+        if ($flags && $modseq && !$uid) {
+            $pipeline->data['modseqs_nouid'][] = $id;
+        }
+    }
+
+    /**
+     * Recursively parse BODYSTRUCTURE data from a FETCH return (see
+     * RFC 3501 [7.4.2]).
+     *
+     * @param Horde_Imap_Client_Tokenize $data  Data returned from the server.
+     *
+     * @return Horde_Mime_Part  Mime part object.
+     */
+    protected function _parseBodystructure(Horde_Imap_Client_Tokenize $data)
+    {
+        $ob = new Horde_Mime_Part();
+
+        // If index 0 is an array, this is a multipart part.
+        if (($entry = $data->next()) === true) {
+            do {
+                $ob->addPart($this->_parseBodystructure($data));
+            } while (($entry = $data->next()) === true);
+
+            // The subpart type.
+            $ob->setType('multipart/' . $entry);
+
+            // After the subtype is further extension information. This
+            // information MAY appear for BODYSTRUCTURE requests.
+
+            // This is parameter information.
+            if (($tmp = $data->next()) === false) {
+                return $ob;
+            } elseif ($tmp === true) {
+                foreach ($this->_parseStructureParams($data, 'content-type') as $key => $val) {
+                    $ob->setContentTypeParameter($key, $val);
+                }
+            }
+        } else {
+            $ob->setType($entry . '/' . $data->next());
+
+            if ($data->next() === true) {
+                foreach ($this->_parseStructureParams($data, 'content-type') as $key => $val) {
+                    $ob->setContentTypeParameter($key, $val);
+                }
+            }
+
+            if (!is_null($tmp = $data->next())) {
+                $ob->setContentId($tmp);
+            }
+
+            if (!is_null($tmp = $data->next())) {
+                $ob->setDescription(Horde_Mime::decode($tmp));
+            }
+
+            if (!is_null($tmp = $data->next())) {
+                $ob->setTransferEncoding($tmp);
+            }
+
+            $ob->setBytes($data->next());
+
+            // If the type is 'message/rfc822' or 'text/*', several extra
+            // fields are included
+            switch ($ob->getPrimaryType()) {
+            case 'message':
+                if ($ob->getSubType() == 'rfc822') {
+                    if ($data->next() === true) {
+                        // Ignore: envelope
+                        $data->flushIterator(false);
+                    }
+                    if ($data->next() === true) {
+                        $ob->addPart($this->_parseBodystructure($data));
+                    }
+                    $data->next(); // Ignore: lines
+                }
+                break;
+
+            case 'text':
+                $data->next(); // Ignore: lines
+                break;
+            }
+
+            // After the subtype is further extension information. This
+            // information MAY appear for BODYSTRUCTURE requests.
+
+            // Ignore: MD5
+            if ($data->next() === false) {
+                return $ob;
+            }
+        }
+
+        // This is disposition information
+        if (($tmp = $data->next()) === false) {
+            return $ob;
+        } elseif ($tmp === true) {
+            $ob->setDisposition($data->next());
+
+            if ($data->next() === true) {
+                foreach ($this->_parseStructureParams($data, 'content-disposition') as $key => $val) {
+                    $ob->setDispositionParameter($key, $val);
+                }
+            }
+            $data->next();
+        }
+
+        // This is language information. It is either a single value or a list
+        // of values.
+        if (($tmp = $data->next()) === false) {
+            return $ob;
+        } elseif (!is_null($tmp)) {
+            $ob->setLanguage(($tmp === true) ? $data->flushIterator() : $tmp);
+        }
+
+        // Ignore location (RFC 2557) and consume closing paren.
+        $data->flushIterator(false);
+
+        return $ob;
+    }
+
+    /**
+     * Helper function to parse a parameters-like tokenized array.
+     *
+     * @param mixed $data   Message data. Either a Horde_Imap_Client_Tokenize
+     *                      object or null.
+     * @param string $type  The header name.
+     *
+     * @return array  The parameter array.
+     */
+    protected function _parseStructureParams($data, $type)
+    {
+        $params = array();
+
+        if (is_null($data)) {
+            return $params;
+        }
+
+        while (($name = $data->next()) !== false) {
+            $params[strtolower($name)] = $data->next();
+        }
+
+        $ret = Horde_Mime::decodeParam($type, $params);
+
+        return $ret['params'];
+    }
+
+    /**
+     * Parse ENVELOPE data from a FETCH return (see RFC 3501 [7.4.2]).
+     *
+     * @param Horde_Imap_Client_Tokenize $data  Data returned from the server.
+     *
+     * @return Horde_Imap_Client_Data_Envelope  An envelope object.
+     */
+    protected function _parseEnvelope(Horde_Imap_Client_Tokenize $data)
+    {
+        // 'route', the 2nd element, is deprecated by RFC 2822.
+        $addr_structure = array(
+            0 => 'personal',
+            2 => 'mailbox',
+            3 => 'host'
+        );
+        $env_data = array(
+            0 => 'date',
+            1 => 'subject',
+            2 => 'from',
+            3 => 'sender',
+            4 => 'reply_to',
+            5 => 'to',
+            6 => 'cc',
+            7 => 'bcc',
+            8 => 'in_reply_to',
+            9 => 'message_id'
+        );
+
+        $addr_ob = new Horde_Mail_Rfc822_Address();
+        $env_addrs = $this->getParam('envelope_addrs');
+        $env_str = $this->getParam('envelope_string');
+        $key = 0;
+        $ret = new Horde_Imap_Client_Data_Envelope();
+
+        while (($val = $data->next()) !== false) {
+            if (!isset($env_data[$key]) || is_null($val)) {
+                ++$key;
+                continue;
+            }
+
+            if (is_string($val)) {
+                // These entries are text fields.
+                $ret->$env_data[$key] = substr($val, 0, $env_str);
+            } else {
+                // These entries are address structures.
+                $group = null;
+                $key2 = 0;
+                $tmp = new Horde_Mail_Rfc822_List();
+
+                while ($data->next() !== false) {
+                    $a_val = $data->flushIterator();
+
+                    // RFC 3501 [7.4.2]: Group entry when host is NIL.
+                    // Group end when mailbox is NIL; otherwise, this is
+                    // mailbox name.
+                    if (is_null($a_val[3])) {
+                        if (is_null($a_val[2])) {
+                            $group = null;
+                        } else {
+                            $group = new Horde_Mail_Rfc822_Group($a_val[2]);
+                            $tmp->add($group);
+                        }
+                    } else {
+                        $addr = clone $addr_ob;
+
+                        foreach ($addr_structure as $add_key => $add_val) {
+                            if (!is_null($a_val[$add_key])) {
+                                $addr->$add_val = $a_val[$add_key];
+                            }
+                        }
+
+                        if ($group) {
+                            $group->addresses->add($addr);
+                        } else {
+                            $tmp->add($addr);
+                        }
+                    }
+
+                    if (++$key2 >= $env_addrs) {
+                        $data->flushIterator(false);
+                        break;
+                    }
+                }
+
+                $ret->$env_data[$key] = $tmp;
+            }
+
+            ++$key;
+        }
+
+        return $ret;
+    }
+
+    /**
+     */
+    protected function _vanished($modseq, Horde_Imap_Client_Ids $ids)
+    {
+        $pipeline = $this->_pipeline(
+            $this->_command('UID FETCH')->add(array(
+                strval($ids),
+                'UID',
+                new Horde_Imap_Client_Data_Format_List(array(
+                    'VANISHED',
+                    'CHANGEDSINCE',
+                    new Horde_Imap_Client_Data_Format_Number($modseq)
+                ))
+            ))
+        );
+        $pipeline->data['vanished'] = $this->getIdsOb();
+
+        return $this->_sendCmd($pipeline)->data['vanished'];
+    }
+
+    /**
+     */
+    protected function _store($options)
+    {
+        $pipeline = $this->_storeCmd($options);
+        $pipeline->data['modified'] = $this->getIdsOb();
+
+        try {
+            $resp = $this->_sendCmd($pipeline);
+
+            /* Check for EXPUNGEISSUED (RFC 2180 [4.2]/RFC 5530 [3]). */
+            if (!empty($resp->data['expungeissued'])) {
+                $this->noop();
+            }
+
+            return $resp->data['modified'];
+        } catch (Horde_Imap_Client_Exception_ServerResponse $e) {
+            /* A NO response, when coupled with a sequence STORE and
+             * non-SILENT behavior, most likely means that messages were
+             * expunged. RFC 2180 [4.2] */
+            if (empty($pipeline->data['store_silent']) &&
+                !empty($options['sequence']) &&
+                ($e->status == Horde_Imap_Client_Interaction_Server::NO)) {
+                $this->noop();
+            }
+
+            return $pipeline->data['modified'];
+        }
+    }
+
+    /**
+     * Create a store command.
+     *
+     * @param array $options  See Horde_Imap_Client_Base#_store().
+     *
+     * @return Horde_Imap_Client_Interaction_Pipeline  Pipeline object.
+     */
+    protected function _storeCmd($options)
+    {
+        $cmds = array();
+        $silent = empty($options['unchangedsince'])
+             ? !($this->_debug->debug || $this->_initCache(true))
+             : false;
+
+        if (!empty($options['replace'])) {
+            $cmds[] = array(
+                'FLAGS' . ($silent ? '.SILENT' : ''),
+                $options['replace']
+            );
+        } else {
+            foreach (array('add' => '+', 'remove' => '-') as $k => $v) {
+                if (!empty($options[$k])) {
+                    $cmds[] = array(
+                        $v . 'FLAGS' . ($silent ? '.SILENT' : ''),
+                        $options[$k]
+                    );
+                }
+            }
+        }
+
+        $pipeline = $this->_pipeline();
+        $pipeline->data['store_silent'] = $silent;
+
+        foreach ($cmds as $val) {
+            $cmd = $this->_command(
+                empty($options['sequence']) ? 'UID STORE' : 'STORE'
+            )->add(strval($options['ids']));
+            if (!empty($options['unchangedsince'])) {
+                $cmd->add(new Horde_Imap_Client_Data_Format_List(array(
+                    'UNCHANGEDSINCE',
+                    new Horde_Imap_Client_Data_Format_Number(intval($options['unchangedsince']))
+                )));
+            }
+            $cmd->add($val);
+
+            $pipeline->add($cmd);
+        }
+
+        return $pipeline;
+    }
+
+    /**
+     */
+    protected function _copy(Horde_Imap_Client_Mailbox $dest, $options)
+    {
+        /* Check for MOVE command (RFC 6851). */
+        $move_cmd = (!empty($options['move']) &&
+                     $this->queryCapability('MOVE'));
+
+        $cmd = $this->_pipeline(
+            $this->_command(
+                ($options['ids']->sequence ? '' : 'UID ') . ($move_cmd ? 'MOVE' : 'COPY')
+            )->add(array(
+                strval($options['ids']),
+                new Horde_Imap_Client_Data_Format_Mailbox($dest)
+            ))
+        );
+        $cmd->data['copydest'] = $dest;
+
+        // COPY returns no untagged information (RFC 3501 [6.4.7])
+        try {
+            $resp = $this->_sendCmd($cmd);
+        } catch (Horde_Imap_Client_Exception $e) {
+            if (!empty($options['create']) &&
+                !empty($e->resp_data['trycreate'])) {
+                $this->createMailbox($dest);
+                unset($options['create']);
+                return $this->_copy($dest, $options);
+            }
+            throw $e;
+        }
+
+        // If moving, delete the old messages now. Short-circuit if nothing
+        // was moved.
+        if (!$move_cmd &&
+            !empty($options['move']) &&
+            (isset($resp->data['copyuid']) ||
+             !$this->queryCapability('UIDPLUS'))) {
+            $this->expunge($this->_selected, array(
+                'delete' => true,
+                'ids' => $options['ids']
+            ));
+        }
+
+        return isset($resp->data['copyuid'])
+            ? $resp->data['copyuid']
+            : true;
+    }
+
+    /**
+     */
+    protected function _setQuota(Horde_Imap_Client_Mailbox $root, $resources)
+    {
+        $limits = new Horde_Imap_Client_Data_Format_List();
+
+        foreach ($resources as $key => $val) {
+            $limits->add(array(
+                strtoupper($key),
+                new Horde_Imap_Client_Data_Format_Number($val)
+            ));
+        }
+
+        $this->_sendCmd(
+            $this->_command('SETQUOTA')->add(array(
+                new Horde_Imap_Client_Data_Format_Mailbox($root),
+                $limits
+            ))
+        );
+    }
+
+    /**
+     */
+    protected function _getQuota(Horde_Imap_Client_Mailbox $root)
+    {
+        $pipeline = $this->_pipeline(
+            $this->_command('GETQUOTA')->add(
+                new Horde_Imap_Client_Data_Format_Mailbox($root)
+            )
+        );
+        $pipeline->data['quotaresp'] = array();
+
+        return reset($this->_sendCmd($pipeline)->data['quotaresp']);
+    }
+
+    /**
+     * Parse a QUOTA response (RFC 2087 [5.1]).
+     *
+     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
+     *                                                          object.
+     * @param Horde_Imap_Client_Tokenize $data  The server response.
+     */
+    protected function _parseQuota(
+        Horde_Imap_Client_Interaction_Pipeline $pipeline,
+        Horde_Imap_Client_Tokenize $data
+    )
+    {
+        $c = &$pipeline->data['quotaresp'];
+
+        $root = $data->next();
+        $c[$root] = array();
+
+        $data->next();
+
+        while (($curr = $data->next()) !== false) {
+            $c[$root][strtolower($curr)] = array(
+                'usage' => $data->next(),
+                'limit' => $data->next()
+            );
+        }
+    }
+
+    /**
+     */
+    protected function _getQuotaRoot(Horde_Imap_Client_Mailbox $mailbox)
+    {
+        $pipeline = $this->_pipeline(
+            $this->_command('GETQUOTAROOT')->add(
+                new Horde_Imap_Client_Data_Format_Mailbox($mailbox)
+            )
+        );
+        $pipeline->data['quotaresp'] = array();
+
+        return $this->_sendCmd($pipeline)->data['quotaresp'];
+    }
+
+    /**
+     */
+    protected function _setACL(Horde_Imap_Client_Mailbox $mailbox, $identifier,
+                               $options)
+    {
+        // SETACL returns no untagged information (RFC 4314 [3.1]).
+        $this->_sendCmd(
+            $this->_command('SETACL')->add(array(
+                new Horde_Imap_Client_Data_Format_Mailbox($mailbox),
+                new Horde_Imap_Client_Data_Format_Astring($identifier),
+                new Horde_Imap_Client_Data_Format_Astring($options['rights'])
+            ))
+        );
+    }
+
+    /**
+     */
+    protected function _deleteACL(Horde_Imap_Client_Mailbox $mailbox, $identifier)
+    {
+        // DELETEACL returns no untagged information (RFC 4314 [3.2]).
+        $this->_sendCmd(
+            $this->_command('DELETEACL')->add(array(
+                new Horde_Imap_Client_Data_Format_Mailbox($mailbox),
+                new Horde_Imap_Client_Data_Format_Astring($identifier)
+            ))
+        );
+    }
+
+    /**
+     */
+    protected function _getACL(Horde_Imap_Client_Mailbox $mailbox)
+    {
+        return $this->_sendCmd(
+            $this->_command('GETACL')->add(
+                new Horde_Imap_Client_Data_Format_Mailbox($mailbox)
+            )
+        )->data['getacl'];
+    }
+
+    /**
+     * Parse an ACL response (RFC 4314 [3.6]).
+     *
+     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
+     *                                                          object.
+     * @param Horde_Imap_Client_Tokenize $data  The server response.
+     */
+    protected function _parseACL(
+        Horde_Imap_Client_Interaction_Pipeline $pipeline,
+        Horde_Imap_Client_Tokenize $data
+    )
+    {
+        $acl = array();
+
+        // Ignore mailbox argument -> index 1
+        $data->next();
+
+        while (($curr = $data->next()) !== false) {
+            $acl[$curr] = ($curr[0] == '-')
+                ? new Horde_Imap_Client_Data_AclNegative($data->next())
+                : new Horde_Imap_Client_Data_Acl($data->next());
+        }
+
+        $pipeline->data['getacl'] = $acl;
+    }
+
+    /**
+     */
+    protected function _listACLRights(Horde_Imap_Client_Mailbox $mailbox,
+                                      $identifier)
+    {
+        $resp = $this->_sendCmd(
+            $this->_command('LISTRIGHTS')->add(array(
+                new Horde_Imap_Client_Data_Format_Mailbox($mailbox),
+                new Horde_Imap_Client_Data_Format_Astring($identifier)
+            ))
+        );
+
+        return isset($resp->data['listaclrights'])
+            ? $resp->data['listaclrights']
+            : new Horde_Imap_Client_Data_AclRights();
+    }
+
+    /**
+     * Parse a LISTRIGHTS response (RFC 4314 [3.7]).
+     *
+     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
+     *                                                          object.
+     * @param Horde_Imap_Client_Tokenize $data  The server response.
+     */
+    protected function _parseListRights(
+        Horde_Imap_Client_Interaction_Pipeline $pipeline,
+        Horde_Imap_Client_Tokenize $data
+    )
+    {
+        // Ignore mailbox and identifier arguments
+        $data->next();
+        $data->next();
+
+        $pipeline->data['listaclrights'] = new Horde_Imap_Client_Data_AclRights(
+            str_split($data->next()),
+            $data->flushIterator()
+        );
+    }
+
+    /**
+     */
+    protected function _getMyACLRights(Horde_Imap_Client_Mailbox $mailbox)
+    {
+        $resp = $this->_sendCmd(
+            $this->_command('MYRIGHTS')->add(
+                new Horde_Imap_Client_Data_Format_Mailbox($mailbox)
+            )
+        );
+
+        return isset($resp->data['myrights'])
+            ? $resp->data['myrights']
+            : new Horde_Imap_Client_Data_Acl();
+    }
+
+    /**
+     * Parse a MYRIGHTS response (RFC 4314 [3.8]).
+     *
+     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
+     *                                                          object.
+     * @param Horde_Imap_Client_Tokenize $data  The server response.
+     */
+    protected function _parseMyRights(
+        Horde_Imap_Client_Interaction_Pipeline $pipeline,
+        Horde_Imap_Client_Tokenize $data
+    )
+    {
+        // Ignore 1st token (mailbox name)
+        $data->next();
+
+        $pipeline->data['myrights'] = new Horde_Imap_Client_Data_Acl($data->next());
+    }
+
+    /**
+     */
+    protected function _getMetadata(Horde_Imap_Client_Mailbox $mailbox,
+                                    $entries, $options)
+    {
+        $pipeline = $this->_pipeline();
+        $pipeline->data['metadata'] = array();
+
+        if ($this->queryCapability('METADATA') ||
+            ((strlen($mailbox) == 0) &&
+             $this->queryCapability('METADATA-SERVER'))) {
+            $cmd_options = new Horde_Imap_Client_Data_Format_List();
+
+            if (!empty($options['maxsize'])) {
+                $cmd_options->add(array(
+                    'MAXSIZE',
+                    new Horde_Imap_Client_Data_Format_Number($options['maxsize'])
+                ));
+            }
+            if (!empty($options['depth'])) {
+                $cmd_options->add(array(
+                    'DEPTH',
+                    new Horde_Imap_Client_Data_Format_Number($options['depth'])
+                ));
+            }
+
+            $queries = new Horde_Imap_Client_Data_Format_List();
+            foreach ($entries as $md_entry) {
+                $queries->add(new Horde_Imap_Client_Data_Format_Astring($md_entry));
+            }
+
+            $cmd = $this->_command('GETMETADATA')->add(
+                new Horde_Imap_Client_Data_Format_Mailbox($mailbox)
+            );
+            if (count($cmd_options)) {
+                $cmd->add($cmd_options);
+            }
+            $cmd->add($queries);
+
+            $pipeline->add($cmd);
+        } else {
+            if (!$this->queryCapability('ANNOTATEMORE') &&
+                !$this->queryCapability('ANNOTATEMORE2')) {
+                throw new Horde_Imap_Client_Exception_NoSupportExtension('METADATA');
+            }
+
+            $queries = array();
+            foreach ($entries as $md_entry) {
+                list($entry, $type) = $this->_getAnnotateMoreEntry($md_entry);
+
+                if (!isset($queries[$type])) {
+                    $queries[$type] = new Horde_Imap_Client_Data_Format_List();
+                }
+                $queries[$type]->add(new Horde_Imap_Client_Data_Format_String($entry));
+            }
+
+            foreach ($queries as $key => $val) {
+                // TODO: Honor maxsize and depth options.
+                $pipeline->add(
+                    $this->_command('GETANNOTATION')->add(array(
+                        new Horde_Imap_Client_Data_Format_Mailbox($mailbox),
+                        $val,
+                        new Horde_Imap_Client_Data_Format_String($key)
+                    ))
+                );
+            }
+        }
+
+        return $this->_sendCmd($pipeline)->data['metadata'];
+    }
+
+    /**
+     * Split a name for the METADATA extension into the correct syntax for the
+     * older ANNOTATEMORE version.
+     *
+     * @param string $name  A name for a metadata entry.
+     *
+     * @return array  A list of two elements: The entry name and the value
+     *                type.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    protected function _getAnnotateMoreEntry($name)
+    {
+        if (substr($name, 0, 7) == '/shared') {
+            return array(substr($name, 7), 'value.shared');
+        } else if (substr($name, 0, 8) == '/private') {
+            return array(substr($name, 8), 'value.priv');
+        }
+
+        throw new Horde_Imap_Client_Exception(
+            sprintf(Horde_Imap_Client_Translation::t("Invalid METADATA entry: \"%s\"."), $name),
+            Horde_Imap_Client_Exception::METADATA_INVALID
+        );
+    }
+
+    /**
+     */
+    protected function _setMetadata(Horde_Imap_Client_Mailbox $mailbox, $data)
+    {
+        if ($this->queryCapability('METADATA') ||
+            ((strlen($mailbox) == 0) &&
+             $this->queryCapability('METADATA-SERVER'))) {
+            $data_elts = new Horde_Imap_Client_Data_Format_List();
+
+            foreach ($data as $key => $value) {
+                $data_elts->add(array(
+                    new Horde_Imap_Client_Data_Format_Astring($key),
+                    new Horde_Imap_Client_Data_Format_Nstring($value)
+                ));
+            }
+
+            $cmd = $this->_command('SETMETADATA')->add(array(
+                new Horde_Imap_Client_Data_Format_Mailbox($mailbox),
+                $data_elts
+            ));
+        } else {
+            if (!$this->queryCapability('ANNOTATEMORE') &&
+                !$this->queryCapability('ANNOTATEMORE2')) {
+                throw new Horde_Imap_Client_Exception_NoSupportExtension('METADATA');
+            }
+
+            $cmd = $this->_pipeline();
+
+            foreach ($data as $md_entry => $value) {
+                list($entry, $type) = $this->_getAnnotateMoreEntry($md_entry);
+
+                $cmd->add(
+                    $this->_command('SETANNOTATION')->add(array(
+                        new Horde_Imap_Client_Data_Format_Mailbox($mailbox),
+                        new Horde_Imap_Client_Data_Format_String($entry),
+                        new Horde_Imap_Client_Data_Format_List(array(
+                            new Horde_Imap_Client_Data_Format_String($type),
+                            new Horde_Imap_Client_Data_Format_Nstring($value)
+                        ))
+                    ))
+                );
+            }
+        }
+
+        $this->_sendCmd($cmd);
+    }
+
+    /**
+     * Parse a METADATA response (RFC 5464 [4.4]).
+     *
+     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
+     *                                                          object.
+     * @param Horde_Imap_Client_Tokenize $data  The server response.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    protected function _parseMetadata(
+        Horde_Imap_Client_Interaction_Pipeline $pipeline,
+        Horde_Imap_Client_Tokenize $data
+    )
+    {
+        switch ($data->current()) {
+        case 'ANNOTATION':
+            $mbox = $data->next();
+            $entry = $data->next();
+
+            // Ignore unsolicited responses.
+            if ($data->next() !== true) {
+                break;
+            }
+
+            while (($type = $data->next()) !== false) {
+                switch ($type) {
+                case 'value.priv':
+                    $pipeline->data['metadata'][$mbox]['/private' . $entry] = $data->next();
+                    break;
+
+                case 'value.shared':
+                    $pipeline->data['metadata'][$mbox]['/shared' . $entry] = $data->next();
+                    break;
+
+                default:
+                    throw new Horde_Imap_Client_Exception(
+                        sprintf(Horde_Imap_Client_Translation::t("Invalid METADATA value type \"%s\"."), $type),
+                        Horde_Imap_Client_Exception::METADATA_INVALID
+                    );
+                }
+            }
+            break;
+
+        case 'METADATA':
+            $mbox = $data->next();
+
+            // Ignore unsolicited responses.
+            if ($data->next() !== true) {
+                break;
+            }
+
+            while (($entry = $data->next()) !== false) {
+                $pipeline->data['metadata'][$mbox][$entry] = $data->next();
+            }
+            break;
+        }
+    }
+
+    /* Overriden methods. */
+
+    /**
+     * @param array $opts  Options:
+     *   - decrement: (boolean) If true, decrement the message count.
+     *   - pipeline: (Horde_Imap_Client_Interaction_Pipeline) Pipeline object.
+     */
+    protected function _deleteMsgs(Horde_Imap_Client_Mailbox $mailbox,
+                                   Horde_Imap_Client_Ids $ids,
+                                   array $opts = array())
+    {
+        /* If there are pending FETCH cache writes, we need to write them
+         * before the UID -> sequence number mapping changes. */
+        if (isset($opts['pipeline'])) {
+            $this->_updateCache($opts['pipeline']->fetch);
+        }
+
+        $res = parent::_deleteMsgs($mailbox, $ids);
+
+        if (isset($this->_temp['expunged'])) {
+            $this->_temp['expunged']->add($res);
+        }
+
+        if (!empty($opts['decrement'])) {
+            $mbox_ob = $this->_mailboxOb();
+            $mbox_ob->setStatus(
+                Horde_Imap_Client::STATUS_MESSAGES,
+                $mbox_ob->getStatus(Horde_Imap_Client::STATUS_MESSAGES) - count($ids)
+            );
+        }
+    }
+
+    /* Internal functions. */
+
+    /**
+     * Sends command(s) to the IMAP server. A connection to the server must
+     * have already been made.
+     *
+     * @param mixed $cmd  Either a Command object or a Pipeline object.
+     *
+     * @return Horde_Imap_Client_Interaction_Pipeline  A pipeline object.
+     * @throws Horde_Imap_Client_Exception
+     */
+    protected function _sendCmd($cmd)
+    {
+        $pipeline = ($cmd instanceof Horde_Imap_Client_Interaction_Command)
+            ? $this->_pipeline($cmd)
+            : $cmd;
+
+        if (!empty($this->_cmdQueue)) {
+            /* Add commands in reverse order. */
+            foreach (array_reverse($this->_cmdQueue) as $val) {
+                $pipeline->add($val, true);
+            }
+
+            $this->_cmdQueue = array();
+        }
+
+        $cmd_list = array();
+
+        foreach ($pipeline as $val) {
+            if ($val->continuation) {
+                $this->_sendCmdChunk($pipeline, $cmd_list);
+                $this->_sendCmdChunk($pipeline, array($val));
+                $cmd_list = array();
+            } else {
+                $cmd_list[] = $val;
+            }
+        }
+
+        $this->_sendCmdChunk($pipeline, $cmd_list);
+
+        /* If any FLAGS responses contain MODSEQs but not UIDs, don't
+         * cache any data and immediately close the mailbox. */
+        foreach ($pipeline->data['modseqs_nouid'] as $val) {
+            if (!$pipeline->fetch[$val]->getUid()) {
+                $this->_debug->info('Server provided FLAGS MODSEQ without providing UID.');
+                $this->close();
+                return $pipeline;
+            }
+        }
+
+        /* Update HIGHESTMODSEQ value. */
+        if (!empty($pipeline->data['modseqs'])) {
+            $modseq = max($pipeline->data['modseqs']);
+            $this->_mailboxOb()->setStatus(Horde_Imap_Client::STATUS_HIGHESTMODSEQ, $modseq);
+            $this->_updateModSeq($modseq);
+        }
+
+        /* Update cache items. */
+        $this->_updateCache($pipeline->fetch);
+
+        return $pipeline;
+    }
+
+    /**
+     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  The pipeline
+     *                                                          object.
+     * @param array $chunk  List of commands to send.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    protected function _sendCmdChunk($pipeline, $chunk)
+    {
+        if (empty($chunk)) {
+            return;
+        }
+
+        $cmd_count = count($chunk);
+        $exception = null;
+
+        foreach ($chunk as $val) {
+            try {
+                $old_debug = $this->_debug->debug;
+                if (!is_null($val->debug)) {
+                    $this->_debug->raw($val->tag . ' ' . $val->debug . "\n");
+                    $this->_debug->debug = false;
+                }
+                $this->_processCmd($pipeline, $val, $val);
+                $this->_connection->write('', true);
+                $this->_debug->debug = $old_debug;
+            } catch (Horde_Imap_Client_Exception $e) {
+                $this->_debug->debug = $old_debug;
+
+                switch ($e->getCode()) {
+                case Horde_Imap_Client_Exception::SERVER_WRITEERROR:
+                    $this->_temp['logout'] = true;
+                    $this->logout();
+                    break;
+                }
+
+                throw $e;
+            }
+        }
+
+        while ($cmd_count) {
+            try {
+                if ($this->_getLine($pipeline) instanceof Horde_Imap_Client_Interaction_Server_Tagged) {
+                    --$cmd_count;
+                }
+            } catch (Horde_Imap_Client_Exception $e) {
+                switch ($e->getCode()) {
+                case $e::DISCONNECT:
+                    $this->_temp['logout'] = true;
+                    // Fall-through
+
+                case $e::SERVER_READERROR:
+                    $this->logout();
+                    throw $e;
+                }
+
+                // Catch and store exception; don't throw until all input
+                // is read. (For now, only store first exception.)
+                if (is_null($exception)) {
+                    $exception = $e;
+                }
+
+                if (($e instanceof Horde_Imap_Client_Exception_ServerResponse) &&
+                    $e->command) {
+                    --$cmd_count;
+                }
+            }
+        }
+
+        if (!is_null($exception)) {
+            throw $exception;
+        }
+    }
+
+    /**
+     * Process/send a command to the remote server.
+     *
+     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline The pipeline
+     *                                                         object.
+     * @param Horde_Imap_Client_Interaction_Command $cmd  The master command.
+     * @param Horde_Imap_Client_Data_Format_List $data    Commands to send.
+     *
+     * @throws Horde_Imap_Client_Exception
+     * @throws Horde_Imap_Client_Exception_NoSupport
+     */
+    protected function _processCmd($pipeline, $cmd, $data)
+    {
+        foreach ($data as $key => $val) {
+            if ($val instanceof Horde_Imap_Client_Interaction_Command_Continuation) {
+                $this->_connection->write('', true);
+
+                $this->_processCmd(
+                    $pipeline,
+                    $cmd,
+                    $val->getCommands($this->_processCmdContinuation($pipeline))
+                );
+                continue;
+            }
+
+            if ($key) {
+                $this->_connection->write(' ');
+            }
+
+            if ($val instanceof Horde_Imap_Client_Data_Format_List) {
+                $this->_connection->write('(');
+                $this->_processCmd($pipeline, $cmd, $val);
+                $this->_connection->write(')');
+            } elseif (($val instanceof Horde_Imap_Client_Data_Format_String) &&
+                      $val->literal()) {
+                /* RFC 3516/4466: Send literal8 if we have binary data. */
+                if ($cmd->literal8 &&
+                    $val->binary() &&
+                    $this->queryCapability('BINARY')) {
+                    $binary = true;
+                    $this->_connection->write('~');
+                } else {
+                    $binary = false;
+                }
+
+                $literal_len = $val->length();
+                $this->_connection->write('{' . $literal_len);
+
+                /* RFC 2088 - If LITERAL+ is available, saves a roundtrip from
+                 * the server. */
+                if ($cmd->literalplus && $this->queryCapability('LITERAL+')) {
+                    $this->_connection->write('+}', true);
+                } else {
+                    $this->_connection->write('}', true);
+                    $this->_processCmdContinuation($pipeline);
+                }
+
+                $this->_connection->writeLiteral($val->getStream(), $literal_len, $binary);
+            } else {
+                $this->_connection->write($val->escape());
+            }
+        }
+    }
+
+    /**
+     * Process a command continuation response.
+     *
+     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline The pipeline
+     *                                                         object.
+     *
+     * @return Horde_Imap_Client_Interaction_Server_Continuation  Continuation
+     *                                                            object.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    protected function _processCmdContinuation($pipeline)
+    {
+        $ob = $this->_getLine($pipeline);
+        if ($ob instanceof Horde_Imap_Client_Interaction_Server_Continuation) {
+            return $ob;
+        }
+
+        $this->_debug->info("ERROR: Unexpected response from server while waiting for a continuation request.");
+        $e = new Horde_Imap_Client_Exception(
+            Horde_Imap_Client_Translation::t("Error when communicating with the mail server."),
+            Horde_Imap_Client_Exception::SERVER_READERROR
+        );
+        $e->details = strval($ob);
+
+        throw $e;
+    }
+
+    /**
+     * Shortcut to creating a new IMAP client command object.
+     *
+     * @param string $cmd  The IMAP command.
+     *
+     * @return Horde_Imap_Client_Interaction_Command  A command object.
+     */
+    protected function _command($cmd)
+    {
+        return new Horde_Imap_Client_Interaction_Command($cmd, ++$this->_tag);
+    }
+
+    /**
+     * Shortcut to creating a new pipeline object.
+     *
+     * @param Horde_Imap_Client_Interaction_Command $cmd  An IMAP command to
+     *                                                    add.
+     *
+     * @return Horde_Imap_Client_Interaction_Pipeline  A pipeline object.
+     */
+    protected function _pipeline($cmd = null)
+    {
+        if (!isset($this->_temp['fetchob'])) {
+            $this->_temp['fetchob'] = new Horde_Imap_Client_Fetch_Results(
+                $this->_fetchDataClass,
+                Horde_Imap_Client_Fetch_Results::SEQUENCE
+            );
+        }
+
+        $ob = new Horde_Imap_Client_Interaction_Pipeline(
+            clone $this->_temp['fetchob']
+        );
+
+        if (!is_null($cmd)) {
+            $ob->add($cmd);
+        }
+
+        return $ob;
+    }
+
+    /**
+     * Gets data from the IMAP server stream and parses it.
+     *
+     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
+     *                                                          object.
+     *
+     * @return Horde_Imap_Client_Interaction_Server  Server object.
+     *
+     * @throws Horde_Imap_Client_Exception
+     */
+    protected function _getLine(
+        Horde_Imap_Client_Interaction_Pipeline $pipeline
+    )
+    {
+        $server = Horde_Imap_Client_Interaction_Server::create(
+            $this->_connection->read()
+        );
+
+        switch (get_class($server)) {
+        case 'Horde_Imap_Client_Interaction_Server_Continuation':
+            $this->_responseCode($pipeline, $server);
+            break;
+
+        case 'Horde_Imap_Client_Interaction_Server_Tagged':
+            $pipeline->complete($server);
+            $this->_responseCode($pipeline, $server);
+            break;
+
+        case 'Horde_Imap_Client_Interaction_Server_Untagged':
+            if (is_null($server->status)) {
+                $this->_serverResponse($pipeline, $server);
+            } else {
+                $this->_responseCode($pipeline, $server);
+            }
+            break;
+        }
+
+        switch ($server->status) {
+        case $server::BAD:
+            /* A tagged BAD response indicates that the tagged command caused
+             * the error. This information is unknown if untagged (RFC 3501
+             * [7.1.3]). */
+            throw new Horde_Imap_Client_Exception_ServerResponse(
+                Horde_Imap_Client_Translation::t("IMAP error reported by server."),
+                0,
+                $server,
+                $pipeline
+            );
+
+        case $server::BYE:
+            /* A BYE response received as part of a logout command should be
+             * be treated like a regular command: a client MUST process the
+             * entire command until logging out (RFC 3501 [3.4; 7.1.5]). */
+            if (empty($this->_temp['logout'])) {
+                $e = new Horde_Imap_Client_Exception(
+                    Horde_Imap_Client_Translation::t("IMAP Server closed the connection."),
+                    Horde_Imap_Client_Exception::DISCONNECT
+                );
+                $e->details = strval($server);
+                throw $e;
+            }
+            break;
+
+        case $server::NO:
+            /* An untagged NO response indicates a warning; ignore and assume
+             * that it also included response text code that is handled
+             * elsewhere. Throw exception if tagged; command handlers can
+             * catch this if able to workaround this issue (RFC 3501
+             * [7.1.2]). */
+            if ($server instanceof Horde_Imap_Client_Interaction_Server_Tagged) {
+                throw new Horde_Imap_Client_Exception_ServerResponse(
+                    Horde_Imap_Client_Translation::t("IMAP error reported by server."),
+                    0,
+                    $server,
+                    $pipeline
+                );
+            }
+
+        case $server::PREAUTH:
+            /* The user was pre-authenticated. (RFC 3501 [7.1.4]) */
+            $this->_temp['preauth'] = true;
+            break;
+        }
+
+        return $server;
+    }
+
+    /**
+     * Handle untagged server responses (see RFC 3501 [2.2.2]).
+     *
+     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
+     *                                                          object.
+     * @param Horde_Imap_Client_Interaction_Server $ob          Server
+     *                                                          response.
+     */
+    protected function _serverResponse(
+        Horde_Imap_Client_Interaction_Pipeline $pipeline,
+        Horde_Imap_Client_Interaction_Server $ob
+    )
+    {
+        $token = $ob->token;
+
+        /* First, catch untagged responses where the name appears first on the
+         * line. */
+        switch ($first = strtoupper($token->current())) {
+        case 'CAPABILITY':
+            $this->_parseCapability($pipeline, $token->flushIterator());
+            break;
+
+        case 'LIST':
+        case 'LSUB':
+            $this->_parseList($pipeline, $token);
+            break;
+
+        case 'STATUS':
+            // Parse a STATUS response (RFC 3501 [7.2.4]).
+            $this->_parseStatus($token);
+            break;
+
+        case 'SEARCH':
+        case 'SORT':
+            // Parse a SEARCH/SORT response (RFC 3501 [7.2.5] & RFC 5256 [4]).
+            $this->_parseSearch($pipeline, $token->flushIterator());
+            break;
+
+        case 'ESEARCH':
+            // Parse an ESEARCH response (RFC 4466 [2.6.2]).
+            $this->_parseEsearch($pipeline, $token);
+            break;
+
+        case 'FLAGS':
+            $token->next();
+            $this->_mailboxOb()->setStatus(Horde_Imap_Client::STATUS_FLAGS, array_map('strtolower', $token->flushIterator()));
+            break;
+
+        case 'QUOTA':
+            $this->_parseQuota($pipeline, $token);
+            break;
+
+        case 'QUOTAROOT':
+            // Ignore this line - we can get this information from
+            // the untagged QUOTA responses.
+            break;
+
+        case 'NAMESPACE':
+            $this->_parseNamespace($pipeline, $token);
+            break;
+
+        case 'THREAD':
+            $this->_parseThread($pipeline, $token);
+            break;
+
+        case 'ACL':
+            $this->_parseACL($pipeline, $token);
+            break;
+
+        case 'LISTRIGHTS':
+            $this->_parseListRights($pipeline, $token);
+            break;
+
+        case 'MYRIGHTS':
+            $this->_parseMyRights($pipeline, $token);
+            break;
+
+        case 'ID':
+            // ID extension (RFC 2971)
+            $this->_parseID($pipeline, $token);
+            break;
+
+        case 'ENABLED':
+            // ENABLE extension (RFC 5161)
+            $this->_parseEnabled($token);
+            break;
+
+        case 'LANGUAGE':
+            // LANGUAGE extension (RFC 5255 [3.2])
+            $this->_parseLanguage($token);
+            break;
+
+        case 'COMPARATOR':
+            // I18NLEVEL=2 extension (RFC 5255 [4.7])
+            $this->_parseComparator($pipeline, $token);
+            break;
+
+        case 'VANISHED':
+            // QRESYNC extension (RFC 5162 [3.6])
+            $this->_parseVanished($pipeline, $token);
+            break;
+
+        case 'ANNOTATION':
+        case 'METADATA':
+            // Parse a ANNOTATEMORE/METADATA response.
+            $this->_parseMetadata($pipeline, $token);
+            break;
+
+        default:
+            // Next, look for responses where the keywords occur second.
+            switch (strtoupper($token->next())) {
+            case 'EXISTS':
+                // EXISTS response - RFC 3501 [7.3.2]
+                $mbox_ob = $this->_mailboxOb();
+
+                // Increment UIDNEXT if it is set.
+                if ($mbox_ob->open &&
+                    ($uidnext = $mbox_ob->getStatus(Horde_Imap_Client::STATUS_UIDNEXT))) {
+                    $mbox_ob->setStatus(Horde_Imap_Client::STATUS_UIDNEXT, $uidnext + $first - $mbox_ob->getStatus(Horde_Imap_Client::STATUS_MESSAGES));
+                }
+
+                $mbox_ob->setStatus(Horde_Imap_Client::STATUS_MESSAGES, $first);
+                break;
+
+            case 'RECENT':
+                // RECENT response - RFC 3501 [7.3.1]
+                $this->_mailboxOb()->setStatus(Horde_Imap_Client::STATUS_RECENT, $first);
+                break;
+
+            case 'EXPUNGE':
+                // EXPUNGE response - RFC 3501 [7.4.1]
+                $this->_deleteMsgs($this->_selected, $this->getIdsOb($first, true), array(
+                    'decrement' => true,
+                    'pipeline' => $pipeline
+                ));
+                $pipeline->data['expunge_seen'] = true;
+                break;
+
+            case 'FETCH':
+                // FETCH response - RFC 3501 [7.4.2]
+                $this->_parseFetch($pipeline, $first, $token);
+                break;
+            }
+            break;
+        }
+    }
+
+    /**
+     * Handle status responses (see RFC 3501 [7.1]).
+     *
+     * @param Horde_Imap_Client_Interaction_Pipeline $pipeline  Pipeline
+     *                                                          object.
+     * @param Horde_Imap_Client_Interaction_Server $ob          Server object.
+     *
+     * @throws Horde_Imap_Client_Exception_ServerResponse
+     */
+    protected function _responseCode(
+        Horde_Imap_Client_Interaction_Pipeline $pipeline,
+        Horde_Imap_Client_Interaction_Server $ob
+    )
+    {
+        if (is_null($ob->responseCode)) {
+            return;
+        }
+
+        $rc = $ob->responseCode;
+
+        switch ($rc->code) {
+        case 'ALERT':
+        // Defined by RFC 5530 [3] - Treat as an alert for now.
+        case 'CONTACTADMIN':
+            if (!isset($this->_temp['alerts'])) {
+                $this->_temp['alerts'] = array();
+            }
+            $this->_temp['alerts'][] = strval($ob->token);
+            break;
+
+        case 'BADCHARSET':
+            /* Store valid search charsets if returned by server. */
+            $s_charset = array();
+            foreach ($rc->data[0] as $val) {
+                $s_charset[$val] = true;
+            }
+
+            if (!empty($s_charset)) {
+                $this->_setInit('s_charset', array_merge(
+                    $this->_init['s_charset'],
+                    $s_charset
+                ));
+            }
+
+            throw new Horde_Imap_Client_Exception_ServerResponse(
+                Horde_Imap_Client_Translation::t("Charset used in search query is not supported on the mail server."),
+                Horde_Imap_Client_Exception::BADCHARSET,
+                $ob,
+                $pipeline
+            );
+
+        case 'CAPABILITY':
+            $this->_parseCapability($pipeline, $rc->data);
+            break;
+
+        case 'PARSE':
+            /* Only throw error on NO/BAD. Message is human readable. */
+            switch ($ob->status) {
+            case Horde_Imap_Client_Interaction_Server::BAD:
+            case Horde_Imap_Client_Interaction_Server::NO:
+                throw new Horde_Imap_Client_Exception_ServerResponse(
+                    sprintf(Horde_Imap_Client_Translation::t("The mail server was unable to parse the contents of the mail message: %s"), strval($ob->token)),
+                    Horde_Imap_Client_Exception::PARSEERROR,
+                    $ob,
+                    $pipeline
+                );
+            }
+            break;
+
+        case 'READ-ONLY':
+            $this->_mode = Horde_Imap_Client::OPEN_READONLY;
+            break;
+
+        case 'READ-WRITE':
+            $this->_mode = Horde_Imap_Client::OPEN_READWRITE;
+            break;
+
+        case 'TRYCREATE':
+            // RFC 3501 [7.1]
+            $pipeline->data['trycreate'] = true;
+            break;
+
+        case 'PERMANENTFLAGS':
+            $this->_mailboxOb()->setStatus(Horde_Imap_Client::STATUS_PERMFLAGS, array_map('strtolower', $rc->data[0]));
+            break;
+
+        case 'UIDNEXT':
+            $this->_mailboxOb()->setStatus(Horde_Imap_Client::STATUS_UIDNEXT, $rc->data[0]);
+            break;
+
+        case 'UIDVALIDITY':
+            $this->_mailboxOb()->setStatus(Horde_Imap_Client::STATUS_UIDVALIDITY, $rc->data[0]);
+            break;
+
+        case 'UNSEEN':
+            /* This is different from the STATUS UNSEEN response - this item,
+             * if defined, returns the first UNSEEN message in the mailbox. */
+            $this->_mailboxOb()->setStatus(Horde_Imap_Client::STATUS_FIRSTUNSEEN, $rc->data[0]);
+            break;
+
+        case 'REFERRAL':
+            // Defined by RFC 2221
+            $pipeline->data['referral'] = new Horde_Imap_Client_Url($rc->data[0]);
+            break;
+
+        case 'UNKNOWN-CTE':
+            // Defined by RFC 3516
+            throw new Horde_Imap_Client_Exception_ServerResponse(
+                Horde_Imap_Client_Translation::t("The mail server was unable to parse the contents of the mail message."),
+                Horde_Imap_Client_Exception::UNKNOWNCTE,
+                $ob,
+                $pipeline
+            );
+
+        case 'APPENDUID':
+            // Defined by RFC 4315
+            // APPENDUID: [0] = UIDVALIDITY, [1] = UID(s)
+            $pipeline->data['appenduid'] = $this->getIdsOb($rc->data[1]);
+            break;
+
+        case 'COPYUID':
+            // Defined by RFC 4315
+            // COPYUID: [0] = UIDVALIDITY, [1] = UIDFROM, [2] = UIDTO
+            $pipeline->data['copyuid'] = array_combine(
+                $this->getIdsOb($rc->data[1])->ids,
+                $this->getIdsOb($rc->data[2])->ids
+            );
+
+            /* Use UIDPLUS information to move cached data to new mailbox (see
+             * RFC 4549 [4.2.2.1]). Need to move now, because a MOVE might
+             * EXPUNGE immediately afterwards. */
+            $this->_moveCache($pipeline->data['copydest'], $pipeline->data['copyuid'], $rc->data[0]);
+            break;
+
+        case 'UIDNOTSTICKY':
+            // Defined by RFC 4315 [3]
+            $this->_mailboxOb()->setStatus(Horde_Imap_Client::STATUS_UIDNOTSTICKY, true);
+            break;
+
+        case 'BADURL':
+            // Defined by RFC 4469 [4.1]
+            throw new Horde_Imap_Client_Exception_ServerResponse(
+                Horde_Imap_Client_Translation::t("Could not save message on server."),
+                Horde_Imap_Client_Exception::CATENATE_BADURL,
+                $ob,
+                $pipeline
+            );
+
+        case 'TOOBIG':
+            // Defined by RFC 4469 [4.2]
+            throw new Horde_Imap_Client_Exception_ServerResponse(
+                Horde_Imap_Client_Translation::t("Could not save message data because it is too large."),
+                Horde_Imap_Client_Exception::CATENATE_TOOBIG,
+                $ob,
+                $pipeline
+            );
+
+        case 'HIGHESTMODSEQ':
+            // Defined by RFC 4551 [3.1.1]
+            $pipeline->data['modseqs'][] = $rc->data[0];
+            break;
+
+        case 'NOMODSEQ':
+            // Defined by RFC 4551 [3.1.2]
+            $pipeline->data['modseqs'][] = 0;
+            break;
+
+        case 'MODIFIED':
+            // Defined by RFC 4551 [3.2]
+            $pipeline->data['modified']->add($rc->data[0]);
+            break;
+
+        case 'CLOSED':
+            // Defined by RFC 5162 [3.7]
+            if (isset($pipeline->data['qresyncmbox'])) {
+                /* If there is any pending FETCH cache entries, flush them
+                 * now before changing mailboxes. */
+                $this->_updateCache($pipeline->fetch);
+                $pipeline->fetch->clear();
+
+                $this->_changeSelected(
+                    $pipeline->data['qresyncmbox'][0],
+                    $pipeline->data['qresyncmbox'][1]
+                );
+                unset($pipeline->data['qresyncmbox']);
+            }
+            break;
+
+        case 'NOTSAVED':
+            // Defined by RFC 5182 [2.5]
+            $pipeline->data['searchnotsaved'] = true;
+            break;
+
+        case 'BADCOMPARATOR':
+            // Defined by RFC 5255 [4.9]
+            throw new Horde_Imap_Client_Exception_ServerResponse(
+                Horde_Imap_Client_Translation::t("The comparison algorithm was not recognized by the server."),
+                Horde_Imap_Client_Exception::BADCOMPARATOR,
+                $ob,
+                $pipeline
+            );
+
+        case 'METADATA':
+            $md = $rc->data[0];
+
+            switch ($md[0]) {
+            case 'LONGENTRIES':
+                // Defined by RFC 5464 [4.2.1]
+                $pipeline->data['metadata']['*longentries'] = intval($md[1]);
+                break;
+
+            case 'MAXSIZE':
+                // Defined by RFC 5464 [4.3]
+                throw new Horde_Imap_Client_Exception_ServerResponse(
+                    Horde_Imap_Client_Translation::t("The metadata item could not be saved because it is too large."),
+                    Horde_Imap_Client_Exception::METADATA_MAXSIZE,
+                    $ob,
+                    $pipeline
+                );
+
+            case 'NOPRIVATE':
+                // Defined by RFC 5464 [4.3]
+                throw new Horde_Imap_Client_Exception_ServerResponse(
+                    Horde_Imap_Client_Translation::t("The metadata item could not be saved because the server does not support private annotations."),
+                    Horde_Imap_Client_Exception::METADATA_NOPRIVATE,
+                    $ob,
+                    $pipeline
+                );
+
+            case 'TOOMANY':
+                // Defined by RFC 5464 [4.3]
+                throw new Horde_Imap_Client_Exception_ServerResponse(
+                    Horde_Imap_Client_Translation::t("The metadata item could not be saved because the maximum number of annotations has been exceeded."),
+                    Horde_Imap_Client_Exception::METADATA_TOOMANY,
+                    $ob,
+                    $pipeline
+                );
+            }
+            break;
+
+        case 'UNAVAILABLE':
+            // Defined by RFC 5530 [3]
+            $pipeline->data['loginerr'] = new Horde_Imap_Client_Exception(
+                Horde_Imap_Client_Translation::t("Remote server is temporarily unavailable."),
+                Horde_Imap_Client_Exception::LOGIN_UNAVAILABLE
+            );
+            break;
+
+        case 'AUTHENTICATIONFAILED':
+            // Defined by RFC 5530 [3]
+            $pipeline->data['loginerr'] = new Horde_Imap_Client_Exception(
+                Horde_Imap_Client_Translation::t("Authentication failed."),
+                Horde_Imap_Client_Exception::LOGIN_AUTHENTICATIONFAILED
+            );
+            break;
+
+        case 'AUTHORIZATIONFAILED':
+            // Defined by RFC 5530 [3]
+            $pipeline->data['loginerr'] = new Horde_Imap_Client_Exception(
+                Horde_Imap_Client_Translation::t("Authentication was successful, but authorization failed."),
+                Horde_Imap_Client_Exception::LOGIN_AUTHORIZATIONFAILED
+            );
+            break;
+
+        case 'EXPIRED':
+            // Defined by RFC 5530 [3]
+            $pipeline->data['loginerr'] = new Horde_Imap_Client_Exception(
+                Horde_Imap_Client_Translation::t("Authentication credentials have expired."),
+                Horde_Imap_Client_Exception::LOGIN_EXPIRED
+            );
+            break;
+
+        case 'PRIVACYREQUIRED':
+            // Defined by RFC 5530 [3]
+            $pipeline->data['loginerr'] = new Horde_Imap_Client_Exception(
+                Horde_Imap_Client_Translation::t("Operation failed due to a lack of a secure connection."),
+                Horde_Imap_Client_Exception::LOGIN_PRIVACYREQUIRED
+            );
+            break;
+
+        case 'NOPERM':
+            // Defined by RFC 5530 [3]
+            throw new Horde_Imap_Client_Exception_ServerResponse(
+                Horde_Imap_Client_Translation::t("You do not have adequate permissions to carry out this operation."),
+                Horde_Imap_Client_Exception::NOPERM,
+                $ob,
+                $pipeline
+            );
+
+        case 'INUSE':
+            // Defined by RFC 5530 [3]
+            throw new Horde_Imap_Client_Exception_ServerResponse(
+                Horde_Imap_Client_Translation::t("There was a temporary issue when attempting this operation. Please try again later."),
+                Horde_Imap_Client_Exception::INUSE,
+                $ob,
+                $pipeline
+            );
+
+        case 'EXPUNGEISSUED':
+            // Defined by RFC 5530 [3]
+            $pipeline->data['expungeissued'] = true;
+            break;
+
+        case 'CORRUPTION':
+            // Defined by RFC 5530 [3]
+            throw new Horde_Imap_Client_Exception_ServerResponse(
+                Horde_Imap_Client_Translation::t("The mail server is reporting corrupt data in your mailbox."),
+                Horde_Imap_Client_Exception::CORRUPTION,
+                $ob,
+                $pipeline
+            );
+
+        case 'SERVERBUG':
+        case 'CLIENTBUG':
+        case 'CANNOT':
+            // Defined by RFC 5530 [3]
+            $this->_debug->info("ERROR: mail server explicitly reporting an error.");
+            break;
+
+        case 'LIMIT':
+            // Defined by RFC 5530 [3]
+            throw new Horde_Imap_Client_Exception_ServerResponse(
+                Horde_Imap_Client_Translation::t("The mail server has denied the request."),
+                Horde_Imap_Client_Exception::LIMIT,
+                $ob,
+                $pipeline
+            );
+
+        case 'OVERQUOTA':
+            // Defined by RFC 5530 [3]
+            throw new Horde_Imap_Client_Exception_ServerResponse(
+                Horde_Imap_Client_Translation::t("The operation failed because the quota has been exceeded on the mail server."),
+                Horde_Imap_Client_Exception::OVERQUOTA,
+                $ob,
+                $pipeline
+            );
+
+        case 'ALREADYEXISTS':
+            // Defined by RFC 5530 [3]
+            throw new Horde_Imap_Client_Exception_ServerResponse(
+                Horde_Imap_Client_Translation::t("The object could not be created because it already exists."),
+                Horde_Imap_Client_Exception::ALREADYEXISTS,
+                $ob,
+                $pipeline
+            );
+
+        case 'NONEXISTENT':
+            // Defined by RFC 5530 [3]
+            throw new Horde_Imap_Client_Exception_ServerResponse(
+                Horde_Imap_Client_Translation::t("The object could not be deleted because it does not exist."),
+                Horde_Imap_Client_Exception::NONEXISTENT,
+                $ob,
+                $pipeline
+            );
+
+        case 'USEATTR':
+            // Defined by RFC 6154 [3]
+            throw new Horde_Imap_Client_Exception_ServerResponse(
+                Horde_Imap_Client_Translation::t("The special-use attribute requested for the mailbox is not supported."),
+                Horde_Imap_Client_Exception::USEATTR,
+                $ob,
+                $pipeline
+            );
+
+        case 'DOWNGRADED':
+            // Defined by RFC 6858 [3]
+            $downgraded = $this->getIdsOb($rc->data[0]);
+            foreach ($pipeline->fetch as $val) {
+                if (in_array($val->getUid(), $downgraded)) {
+                    $val->setDowngraded(true);
+                }
+            }
+            break;
+
+        case 'XPROXYREUSE':
+            // The proxy connection was reused, so no need to do login tasks.
+            $pipeline->data['proxyreuse'] = true;
+            break;
+
+        default:
+            // Unknown response codes SHOULD be ignored - RFC 3501 [7.1]
+            break;
+        }
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientTokenizephpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientTokenizephp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Tokenize.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Tokenize.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Tokenize.php                                (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Tokenize.php   2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,310 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Tokenization of an IMAP data stream.
+ *
+ * NOTE: This class is NOT intended to be accessed outside of this package.
+ * There is NO guarantees that the API of this class will not change across
+ * versions.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @internal
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ *
+ * @property-read boolean $eos  Has the end of the stream been reached?
+ */
+class Horde_Imap_Client_Tokenize implements Iterator
+{
+    /**
+     * Current data.
+     *
+     * @var mixed
+     */
+    protected $_current = false;
+
+    /**
+     * Current key.
+     *
+     * @var integer
+     */
+    protected $_key = false;
+
+    /**
+     * Sublevel.
+     *
+     * @var integer
+     */
+    protected $_level = false;
+
+    /**
+     * Data stream.
+     *
+     * @var Horde_Stream
+     */
+    protected $_stream;
+
+    /**
+     * Constructor.
+     *
+     * @param mixed $data  Data to add (string, resource, or Horde_Stream
+     *                     object).
+     */
+    public function __construct($data = null)
+    {
+        $this->_stream = new Horde_Stream_Temp();
+
+        if (!is_null($data)) {
+            $this->add($data);
+        }
+    }
+
+    /**
+     */
+    public function __get($name)
+    {
+        switch ($name) {
+        case 'eos':
+            return feof($this->_stream->stream);
+        }
+    }
+
+    /**
+     */
+    public function __sleep()
+    {
+        throw new LogicException('Object can not be serialized.');
+    }
+
+    /**
+     */
+    public function __toString()
+    {
+        $pos = ftell($this->_stream->stream);
+        $out = $this->_current . ' ' . $this->_stream->getString();
+        fseek($this->_stream->stream, $pos);
+        return $out;
+    }
+
+    /**
+     * Add data to buffer.
+     *
+     * @param mixed $data  Data to add (string, resource, or Horde_Stream
+     *                     object).
+     */
+    public function add($data)
+    {
+        $this->_stream->add($data);
+    }
+
+    /**
+     * Flush the remaining entries left in the iterator.
+     *
+     * @param boolean $return    If true, return entries. Only returns entries
+     *                           on the current level.
+     * @param boolean $sublevel  Only flush items in current sublevel?
+     *
+     * @return array  The entries if $return is true.
+     */
+    public function flushIterator($return = true, $sublevel = true)
+    {
+        $out = array();
+
+        if ($return) {
+            $level = $sublevel ? $this->_level : 0;
+            do {
+                $curr = $this->next();
+                if ($this->_level < $level) {
+                    break;
+                }
+
+                if (!is_bool($curr) && ($level == $this->_level)) {
+                    $out[] = $curr;
+                }
+            } while (($curr !== false) || $this->_level || !$this->eos);
+        } elseif ($sublevel && $this->_level) {
+            $level = $this->_level;
+            while ($level <= $this->_level) {
+                $this->next();
+            }
+        } else {
+            fseek($this->_stream->stream, 0, SEEK_END);
+            fgetc($this->_stream->stream);
+            $this->_current = $this->_key = $this->_level = false;
+        }
+
+        return $out;
+    }
+
+    /**
+     * Return literal length data located at the end of the stream.
+     *
+     * @return mixed  Null if no literal data found, or an array with these
+     *                keys:
+     *   - binary: (boolean) True if this is a literal8.
+     *   - length: (integer) Length of the literal.
+     */
+    public function getLiteralLength()
+    {
+        fseek($this->_stream->stream, -1, SEEK_END);
+        if ($this->_stream->peek() == '}') {
+            $literal_data = $this->_stream->getString($this->_stream->search('{', true) - 1);
+            $literal_len = substr($literal_data, 2, -1);
+
+            if (is_numeric($literal_len)) {
+                return array(
+                    'binary' => ($literal_data[0] == '~'),
+                    'length' => intval($literal_len)
+                );
+            }
+        }
+
+        return null;
+    }
+
+    /* Iterator methods. */
+
+    /**
+     */
+    public function current()
+    {
+        return $this->_current;
+    }
+
+    /**
+     */
+    public function key()
+    {
+        return $this->_key;
+    }
+
+    /**
+     * @return mixed  Either a string, boolean (true for open paren, false for
+     *                close paren/EOS), or null.
+     */
+    public function next()
+    {
+        if ((($this->_current = $this->_parseStream()) === false) &&
+            $this->eos) {
+            $this->_key = $this->_level = false;
+        } else {
+            ++$this->_key;
+        }
+
+        return $this->_current;
+    }
+
+    /**
+     */
+    public function rewind()
+    {
+        fseek($this->_stream->stream, 0);
+        $this->_current = false;
+        $this->_key = -1;
+        $this->_level = 0;
+    }
+
+    /**
+     */
+    public function valid()
+    {
+        return ($this->_level !== false);
+    }
+
+    /**
+     * Returns the next token and increments the internal stream pointer.
+     *
+     * @see next()
+     */
+    protected function _parseStream()
+    {
+        $in_quote = false;
+        $stream = $this->_stream->stream;
+        $text = '';
+
+        while (($c = fgetc($stream)) !== false) {
+            switch ($c) {
+            case '\\':
+                $text .= $in_quote
+                    ? fgetc($stream)
+                    : $c;
+                break;
+
+            case '"':
+                if ($in_quote) {
+                    return $text;
+                } else {
+                    $in_quote = true;
+                }
+                break;
+
+            default:
+                if ($in_quote) {
+                    $text .= $c;
+                    break;
+                }
+
+                switch ($c) {
+                case '(':
+                    ++$this->_level;
+                    return true;
+
+                case ')':
+                    if (strlen($text)) {
+                        fseek($stream, -1, SEEK_CUR);
+                        break 3;
+                    }
+                    --$this->_level;
+                    return false;
+
+                case '~':
+                    // Ignore binary string identifier. PHP strings are
+                    // binary-safe.
+                    break;
+
+                case '{':
+                    return stream_get_contents($stream, $this->_stream->getToChar('}'));
+
+                case ' ':
+                    if (strlen($text)) {
+                        break 3;
+                    }
+                    break;
+
+                default:
+                    $text .= $c;
+                    break;
+                }
+                break;
+            }
+        }
+
+        switch (strlen($text)) {
+        case 0:
+            return false;
+
+        case 3:
+            if (strcasecmp($text, 'NIL') === 0) {
+                return null;
+            }
+            // Fall-through
+
+        default:
+            return $text;
+        }
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientTranslationphpfromrev22362013codebykatpostbyemailtrunkincludeclasshordeimapclienttranslationphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Translation.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/class-horde-imap-client-translation.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Translation.php                             (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Translation.php        2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,37 @@
</span><ins>+<?php
+/**
+ * Bogus translation wrapper class for Horde_Imap_Client.
+ *
+ * Uses WP __() function instead.
+ *
+ */
+class Horde_Imap_Client_Translation
+{
+    /**
+     * Returns the translation of a message.
+     *
+     * @var string $message  The string to translate.
+     *
+     * @return string  The string translation, or the original string if no
+     *                 translation exists.
+     */
+    static public function t($message)
+    {
+        return __($message);
+    }
+
+    /**
+     * Returns the plural translation of a message.
+     *
+     * @param string $singular  The singular version to translate.
+     * @param string $plural    The plural version to translate.
+     * @param integer $number   The number that determines singular vs. plural.
+     *
+     * @return string  The string translation, or the original string if no
+     *                 translation exists.
+     */
+    static public function ngettext($singular, $plural, $number)
+    {
+        return _n( $singular, $plural, $number );
+    }
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientUrlphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientUrlphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Url.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Url.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Url.php                             (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Url.php        2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,285 @@
</span><ins>+<?php
+/**
+ * Copyright 2008-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2008-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Object representation of a a POP3 (RFC 2384) or IMAP (RFC 5092/5593) URL.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2008-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ *
+ * @property-read boolean $relative  Is this a relative URL?
+ */
+class Horde_Imap_Client_Url implements Serializable
+{
+    /**
+     * The authentication method to use.
+     *
+     * @var string
+     */
+    public $auth = null;
+
+    /**
+     * The remote server (not present for relative URLs).
+     *
+     * @var string
+     */
+    public $hostspec = null;
+
+    /**
+     * The IMAP mailbox
+     *
+     * @var string
+     */
+    public $mailbox = null;
+
+    /**
+     * A byte range for use with IMAP FETCH.
+     *
+     * @var string
+     */
+    public $partial = null;
+
+    /**
+     * The remote port (not present for relative URLs).
+     *
+     * @var integer
+     */
+    public $port = null;
+
+    /**
+     * The protocol type. Either 'imap' or 'pop' (not present for relative
+     * URLs).
+     *
+     * @var string
+     */
+    public $protocol = null;
+
+    /**
+     * A search query to be run with IMAP SEARCH.
+     *
+     * @var string
+     */
+    public $search = null;
+
+    /**
+     * A MIME part ID.
+     *
+     * @var string
+     */
+    public $section = null;
+
+    /**
+     * The username to use on the remote server.
+     *
+     * @var string
+     */
+    public $username = null;
+
+    /**
+     * The IMAP UID.
+     *
+     * @var string
+     */
+    public $uid = null;
+
+    /**
+     * The IMAP UIDVALIDITY for the given mailbox.
+     *
+     * @var integer
+     */
+    public $uidvalidity = null;
+
+    /**
+     * URLAUTH info (not parsed).
+     *
+     * @var string
+     */
+    public $urlauth = null;
+
+    /**
+     * Constructor.
+     *
+     * Absolute IMAP URLs takes one of the following forms:
+     *   - imap://<iserver>[/]
+     *   - imap://<iserver>/<enc-mailbox>[<uidvalidity>][?<enc-search>]
+     *   - imap://<iserver>/<enc-mailbox>[<uidvalidity>]<iuid>[<isection>][<ipartial>][<iurlauth>]
+     *
+     * POP URLs take one of the following forms:
+     *   - pop://<user>;auth=<auth>@<host>:<port>
+     *
+     * @param string $url  A URL string.
+     */
+    public function __construct($url = null)
+    {
+        if (!is_null($url)) {
+            $this->_parse($url);
+        }
+    }
+
+    /**
+     * Create a POP3 (RFC 2384) or IMAP (RFC 5092/5593) URL.
+     *
+     * @return string  A URL string.
+     */
+    public function __toString()
+    {
+        $url = '';
+
+        if (!is_null($this->protocol)) {
+            $url = $this->protocol . '://';
+
+            if (!is_null($this->username)) {
+                $url .= $this->username;
+            }
+
+            if (!is_null($this->auth)) {
+                $url .= ';AUTH=' . $this->auth . '@';
+            } elseif (!is_null($this->username)) {
+                $url .= '@';
+            }
+
+            $url .= $this->hostspec;
+
+            if (!is_null($this->port) && ($this->port != 143)) {
+                $url .= ':' . $this->port;
+            }
+        }
+
+        $url .= '/';
+
+        if (is_null($this->protocol) || ($this->protocol == 'imap')) {
+            $url .= urlencode($this->mailbox);
+
+            if (!empty($this->uidvalidity)) {
+                $url .= ';UIDVALIDITY=' . $this->uidvalidity;
+            }
+
+            if (!is_null($this->search)) {
+                $url .= '?' . urlencode($this->search);
+            } else {
+                if (!is_null($this->uid)) {
+                    $url .= '/;UID=' . $this->uid;
+                }
+
+                if (!is_null($this->section)) {
+                    $url .= '/;SECTION=' . $this->section;
+                }
+
+                if (!is_null($this->partial)) {
+                    $url .= '/;PARTIAL=' . $this->partial;
+                }
+
+                if (!is_null($this->urlauth)) {
+                    $url .= '/;URLAUTH=' . $this->urlauth;
+                }
+            }
+        }
+
+        return $url;
+    }
+
+    /**
+     */
+    public function __get($name)
+    {
+        switch ($name) {
+        case 'relative':
+            return (is_null($this->hostspec) &&
+                is_null($this->port) &&
+                is_null($this->protocol));
+        }
+    }
+
+    /**
+     */
+    protected function _parse($url)
+    {
+        $data = parse_url(trim($url));
+
+        if (isset($data['scheme'])) {
+            $protocol = strtolower($data['scheme']);
+            if (!in_array($protocol, array('imap', 'pop'))) {
+                return;
+            }
+
+            if (isset($data['host'])) {
+                $this->hostspec = $data['host'];
+            }
+            $this->port = isset($data['port'])
+                ? $data['port']
+                : 143;
+            $this->protocol = $protocol;
+        }
+
+        /* Check for username/auth information. */
+        if (isset($data['user'])) {
+            if (($pos = stripos($data['user'], ';AUTH=')) !== false) {
+                $auth = substr($data['user'], $pos + 6);
+                if ($auth != '*') {
+                    $this->auth = $auth;
+                }
+                $data['user'] = substr($data['user'], 0, $pos);
+            }
+
+            if (strlen($data['user'])) {
+                $this->username = $data['user'];
+            }
+        }
+
+        /* IMAP-only information. */
+        if (is_null($this->protocol) || ($this->protocol == 'imap')) {
+            if (isset($data['path'])) {
+                $data['path'] = ltrim($data['path'], '/');
+                $parts = explode('/;', $data['path']);
+
+                $mbox = array_shift($parts);
+                if (($pos = stripos($mbox, ';UIDVALIDITY=')) !== false) {
+                    $this->uidvalidity = intval(substr($mbox, $pos + 13));
+                    $mbox = substr($mbox, 0, $pos);
+                }
+                $this->mailbox = urldecode($mbox);
+
+            }
+
+            if (count($parts)) {
+                foreach ($parts as $val) {
+                    list($k, $v) = explode('=', $val);
+                    $property = strtolower($k);
+                    $this->$property = $v;
+                }
+            } elseif (isset($data['query'])) {
+                $this->search = urldecode($data['query']);
+            }
+        }
+    }
+
+    /* Serializable methods. */
+
+    /**
+     */
+    public function serialize()
+    {
+        return strval($this);
+    }
+
+    /**
+     */
+    public function unserialize($data)
+    {
+        $this->_parse($data);
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientUtf7imapphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientUtf7imapphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Utf7imap.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client/Utf7imap.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Utf7imap.php                                (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client/Utf7imap.php   2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,302 @@
</span><ins>+<?php
+/**
+ * Originally based on code:
+ *
+ *  Copyright (C) 2000 Edmund Grimley Evans <edmundo@rano.org>
+ *  Released under the GPL (version 2)
+ *
+ *  Translated from C to PHP by Thomas Bruederli <roundcube@gmail.com>
+ *  Code extracted from the RoundCube Webmail (http://roundcube.net) project,
+ *    SVN revision 1757
+ *  The RoundCube project is released under the GPL (version 2)
+ *
+ * Copyright 2008-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2000 Edmund Grimley Evans <edmundo@rano.org>
+ * @copyright 2008-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Allows conversions between UTF-8 and UTF7-IMAP (RFC 3501 [5.1.3]).
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2000 Edmund Grimley Evans <edmundo@rano.org>
+ * @copyright 2008-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client_Utf7imap
+{
+    /**
+     * Lookup table for conversion.
+     *
+     * @var array
+     */
+    private static $_index64 = array(
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, 63, -1, -1, -1,
+        52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
+        -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
+        15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
+        -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+        41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1
+    );
+
+    /**
+     * Lookup table for conversion.
+     *
+     * @var array
+     */
+    private static $_base64 = 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', '+', ','
+    );
+
+    /**
+     * Is mbstring extension available?
+     *
+     * @var array
+     */
+    private static $_mbstring = null;
+
+    /**
+     * Convert a string from UTF7-IMAP to UTF-8.
+     *
+     * @param string $str  The UTF7-IMAP string.
+     *
+     * @return string  The converted UTF-8 string.
+     * @throws Horde_Imap_Client_Exception
+     */
+    public static function Utf7ImapToUtf8($str)
+    {
+        if ($str instanceof Horde_Imap_Client_Mailbox) {
+            return $str->utf8;
+        }
+
+        $str = strval($str);
+
+        /* Try mbstring, if available, which should be faster. Don't use the
+         * IMAP utf7_* functions because they are known to be buggy. */
+        if (is_null(self::$_mbstring)) {
+            self::$_mbstring = extension_loaded('mbstring');
+        }
+        if (self::$_mbstring) {
+            return @mb_convert_encoding($str, 'UTF-8', 'UTF7-IMAP');
+        }
+
+        $p = '';
+        $ptr = &self::$_index64;
+
+        for ($i = 0, $u7len = strlen($str); $u7len > 0; ++$i, --$u7len) {
+            $u7 = $str[$i];
+            if ($u7 == '&') {
+                $u7 = $str[++$i];
+                if (--$u7len && ($u7 == '-')) {
+                    $p .= '&';
+                    continue;
+                }
+
+                $ch = 0;
+                $k = 10;
+                for (; $u7len > 0; ++$i, --$u7len) {
+                    $u7 = $str[$i];
+
+                    if ((ord($u7) & 0x80) || ($b = $ptr[ord($u7)]) == -1) {
+                        break;
+                    }
+
+                    if ($k > 0) {
+                        $ch |= $b << $k;
+                        $k -= 6;
+                    } else {
+                        $ch |= $b >> (-$k);
+                        if ($ch < 0x80) {
+                            /* Printable US-ASCII */
+                            if ((0x20 <= $ch) && ($ch < 0x7f)) {
+                                throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::t("Error converting UTF7-IMAP string."), Horde_Imap_Client_Exception::UTF7IMAP_CONVERSION);
+                            }
+                            $p .= chr($ch);
+                        } else if ($ch < 0x800) {
+                            $p .= chr(0xc0 | ($ch >> 6)) .
+                                  chr(0x80 | ($ch & 0x3f));
+                        } else {
+                            $p .= chr(0xe0 | ($ch >> 12)) .
+                                  chr(0x80 | (($ch >> 6) & 0x3f)) .
+                                  chr(0x80 | ($ch & 0x3f));
+                        }
+
+                        $ch = ($b << (16 + $k)) & 0xffff;
+                        $k += 10;
+                    }
+                }
+
+                /* Non-zero or too many extra bits -OR-
+                 * Base64 not properly terminated -OR-
+                 * Adjacent Base64 sections. */
+                if (($ch || ($k < 6)) ||
+                    (!$u7len || $u7 != '-') ||
+                    (($u7len > 2) &&
+                     ($str[$i + 1] == '&') &&
+                     ($str[$i + 2] != '-'))) {
+                    throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::t("Error converting UTF7-IMAP string."), Horde_Imap_Client_Exception::UTF7IMAP_CONVERSION);
+                }
+            } elseif ((ord($u7) < 0x20) || (ord($u7) >= 0x7f)) {
+                /* Not printable US-ASCII */
+                throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::t("Error converting UTF7-IMAP string."), Horde_Imap_Client_Exception::UTF7IMAP_CONVERSION);
+            } else {
+                $p .= $u7;
+            }
+        }
+
+        return $p;
+    }
+
+    /**
+     * Convert a string from UTF-8 to UTF7-IMAP.
+     *
+     * @param string $str     The UTF-8 string.
+     * @param boolean $force  Assume $str is UTF-8 (no-autodetection)? If
+     *                        false, attempts to auto-detect if string is
+     *                        already in UTF7-IMAP.
+     *
+     * @return string  The converted UTF7-IMAP string.
+     * @throws Horde_Imap_Client_Exception
+     */
+    public static function Utf8ToUtf7Imap($str, $force = true)
+    {
+        if ($str instanceof Horde_Imap_Client_Mailbox) {
+            return $str->utf7imap;
+        }
+
+        $str = strval($str);
+
+        /* No need to do conversion if all chars are in US-ASCII range or if
+         * no ampersand is present. But will assume that an already encoded
+         * ampersand means string is in UTF7-IMAP already. */
+        if (!$force &&
+            !preg_match('/[\x80-\xff]|&$|&(?![,+A-Za-z0-9]*-)/', $str)) {
+            return $str;
+        }
+
+        /* Try mbstring, if available, which should be faster. Don't use the
+         * IMAP utf7_* functions because they are known to be buggy. */
+        if (is_null(self::$_mbstring)) {
+            self::$_mbstring = extension_loaded('mbstring');
+        }
+        if (self::$_mbstring) {
+            return @mb_convert_encoding($str, 'UTF7-IMAP', 'UTF-8');
+        }
+
+        $u8len = strlen($str);
+        $i = 0;
+        $base64 = false;
+        $p = '';
+        $ptr = &self::$_base64;
+
+        while ($u8len) {
+            $u8 = $str[$i];
+            $c = ord($u8);
+
+            if ($c < 0x80) {
+                $ch = $c;
+                $n = 0;
+            } elseif ($c < 0xc2) {
+                throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::t("Error converting UTF7-IMAP string."), Horde_Imap_Client_Exception::UTF7IMAP_CONVERSION);
+            } elseif ($c < 0xe0) {
+                $ch = $c & 0x1f;
+                $n = 1;
+            } elseif ($c < 0xf0) {
+                $ch = $c & 0x0f;
+                $n = 2;
+            } elseif ($c < 0xf8) {
+                $ch = $c & 0x07;
+                $n = 3;
+            } elseif ($c < 0xfc) {
+                $ch = $c & 0x03;
+                $n = 4;
+            } elseif ($c < 0xfe) {
+                $ch = $c & 0x01;
+                $n = 5;
+            } else {
+                throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::t("Error converting UTF7-IMAP string."), Horde_Imap_Client_Exception::UTF7IMAP_CONVERSION);
+            }
+
+            if ($n > --$u8len) {
+                throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::t("Error converting UTF7-IMAP string."), Horde_Imap_Client_Exception::UTF7IMAP_CONVERSION);
+            }
+
+            ++$i;
+
+            for ($j = 0; $j < $n; ++$j) {
+                $o = ord($str[$i + $j]);
+                if (($o & 0xc0) != 0x80) {
+                    throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::t("Error converting UTF7-IMAP string."), Horde_Imap_Client_Exception::UTF7IMAP_CONVERSION);
+                }
+                $ch = ($ch << 6) | ($o & 0x3f);
+            }
+
+            if (($n > 1) && !($ch >> ($n * 5 + 1))) {
+                throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::t("Error converting UTF7-IMAP string."), Horde_Imap_Client_Exception::UTF7IMAP_CONVERSION);
+            }
+
+            $i += $n;
+            $u8len -= $n;
+
+            if (($ch < 0x20) || ($ch >= 0x7f)) {
+                if (!$base64) {
+                    $p .= '&';
+                    $base64 = true;
+                    $b = 0;
+                    $k = 10;
+                }
+
+                if ($ch & ~0xffff) {
+                    $ch = 0xfffe;
+                }
+
+                $p .= $ptr[($b | $ch >> $k)];
+                $k -= 6;
+                for (; $k >= 0; $k -= 6) {
+                    $p .= $ptr[(($ch >> $k) & 0x3f)];
+                }
+
+                $b = ($ch << (-$k)) & 0x3f;
+                $k += 16;
+            } else {
+                if ($base64) {
+                    if ($k > 10) {
+                        $p .= $ptr[$b];
+                    }
+                    $p .= '-';
+                    $base64 = false;
+                }
+
+                $p .= chr($ch);
+                if (chr($ch) == '&') {
+                    $p .= '-';
+                }
+            }
+        }
+
+        if ($base64) {
+            if ($k > 10) {
+                $p .= $ptr[$b];
+            }
+            $p .= '-';
+        }
+
+        return $p;
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeImapClientphpfromrev22362013codebykatpostbyemailtrunkincludeHordeClientphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Client.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client.php                         (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Imap/Client.php    2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,197 @@
</span><ins>+<?php
+/**
+ * Copyright 2008-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2008-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+
+/**
+ * Base class for Horde_Imap_Client package. Defines common constants for use
+ * in the package.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2008-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Imap_Client
+ */
+class Horde_Imap_Client
+{
+    /* Constants for openMailbox() */
+    const OPEN_READONLY = 1;
+    const OPEN_READWRITE = 2;
+    const OPEN_AUTO = 3;
+
+    /* Constants for listMailboxes() */
+    const MBOX_SUBSCRIBED = 1;
+    const MBOX_SUBSCRIBED_EXISTS = 2;
+    const MBOX_UNSUBSCRIBED = 3;
+    const MBOX_ALL = 4;
+
+    /* Constants for status() */
+    const STATUS_MESSAGES = 1;
+    const STATUS_RECENT = 2;
+    const STATUS_UIDNEXT = 4;
+    const STATUS_UIDVALIDITY = 8;
+    const STATUS_UNSEEN = 16;
+    const STATUS_ALL = 32;
+    const STATUS_FIRSTUNSEEN = 64;
+    const STATUS_FLAGS = 128;
+    const STATUS_PERMFLAGS = 256;
+    const STATUS_HIGHESTMODSEQ = 512;
+    const STATUS_SYNCMODSEQ = 1024;
+    const STATUS_SYNCFLAGUIDS = 2048;
+    const STATUS_UIDNOTSTICKY = 4096;
+    const STATUS_UIDNEXT_FORCE = 8192;
+    const STATUS_SYNCVANISHED = 16384;
+    /* @since 2.12.0 */
+    const STATUS_RECENT_TOTAL = 32768;
+
+    /* Constants for search() */
+    const SORT_ARRIVAL = 1;
+    const SORT_CC = 2;
+    const SORT_DATE = 3;
+    const SORT_FROM = 4;
+    const SORT_REVERSE = 5;
+    const SORT_SIZE = 6;
+    const SORT_SUBJECT = 7;
+    const SORT_TO = 8;
+    /* SORT_THREAD provided for completeness - it is not a valid sort criteria
+     * for search() (use thread() instead). */
+    const SORT_THREAD = 9;
+    /* Sort criteria defined in RFC 5957 */
+    const SORT_DISPLAYFROM = 10;
+    const SORT_DISPLAYTO = 11;
+    /* SORT_SEQUENCE does a simple numerical sort on the returned
+     * UIDs/sequence numbers. */
+    const SORT_SEQUENCE = 12;
+    /* Fuzzy sort criteria defined in RFC 6203 */
+    const SORT_RELEVANCY = 13;
+    /* @since 2.4.0 */
+    const SORT_DISPLAYFROM_FALLBACK = 14;
+    /* @since 2.4.0 */
+    const SORT_DISPLAYTO_FALLBACK = 15;
+
+    /* Search results constants */
+    const SEARCH_RESULTS_COUNT = 1;
+    const SEARCH_RESULTS_MATCH = 2;
+    const SEARCH_RESULTS_MAX = 3;
+    const SEARCH_RESULTS_MIN = 4;
+    const SEARCH_RESULTS_SAVE = 5;
+    /* Fuzzy sort criteria defined in RFC 6203 */
+    const SEARCH_RESULTS_RELEVANCY = 6;
+
+    /* Constants for thread() */
+    const THREAD_ORDEREDSUBJECT = 1;
+    const THREAD_REFERENCES = 2;
+    const THREAD_REFS = 3;
+
+    /* Fetch criteria constants. */
+    const FETCH_STRUCTURE = 1;
+    const FETCH_FULLMSG = 2;
+    const FETCH_HEADERTEXT = 3;
+    const FETCH_BODYTEXT = 4;
+    const FETCH_MIMEHEADER = 5;
+    const FETCH_BODYPART = 6;
+    const FETCH_BODYPARTSIZE = 7;
+    const FETCH_HEADERS = 8;
+    const FETCH_ENVELOPE = 9;
+    const FETCH_FLAGS = 10;
+    const FETCH_IMAPDATE = 11;
+    const FETCH_SIZE = 12;
+    const FETCH_UID = 13;
+    const FETCH_SEQ = 14;
+    const FETCH_MODSEQ = 15;
+    /* @since 2.11.0 */
+    const FETCH_DOWNGRADED = 16;
+
+    /* Namespace constants. */
+    const NS_PERSONAL = 1;
+    const NS_OTHER = 2;
+    const NS_SHARED = 3;
+
+    /* ACL constants (RFC 4314 [2.1]). */
+    const ACL_LOOKUP = 'l';
+    const ACL_READ = 'r';
+    const ACL_SEEN = 's';
+    const ACL_WRITE = 'w';
+    const ACL_INSERT = 'i';
+    const ACL_POST = 'p';
+    const ACL_CREATEMBOX = 'k';
+    const ACL_DELETEMBOX = 'x';
+    const ACL_DELETEMSGS = 't';
+    const ACL_EXPUNGE = 'e';
+    const ACL_ADMINISTER = 'a';
+    // Old constants (RFC 2086 [3]; RFC 4314 [2.1.1])
+    const ACL_CREATE = 'c';
+    const ACL_DELETE = 'd';
+
+    /* System flags. */
+    // RFC 3501 [2.3.2]
+    const FLAG_ANSWERED = '\\answered';
+    const FLAG_DELETED = '\\deleted';
+    const FLAG_DRAFT = '\\draft';
+    const FLAG_FLAGGED = '\\flagged';
+    const FLAG_RECENT = '\\recent';
+    const FLAG_SEEN = '\\seen';
+    // RFC 3503 [3.3]
+    const FLAG_MDNSENT = '$mdnsent';
+    // RFC 5550 [2.8]
+    const FLAG_FORWARDED = '$forwarded';
+    // RFC 5788 registered keywords:
+    // http://www.ietf.org/mail-archive/web/morg/current/msg00441.html
+    const FLAG_JUNK = '$junk';
+    const FLAG_NOTJUNK = '$notjunk';
+
+    /* Special-use mailbox attributes (RFC 6154 [2]). */
+    const SPECIALUSE_ALL = '\\All';
+    const SPECIALUSE_ARCHIVE = '\\Archive';
+    const SPECIALUSE_DRAFTS = '\\Drafts';
+    const SPECIALUSE_FLAGGED = '\\Flagged';
+    const SPECIALUSE_JUNK = '\\Junk';
+    const SPECIALUSE_SENT = '\\Sent';
+    const SPECIALUSE_TRASH = '\\Trash';
+
+    /* Constants for sync(). */
+    const SYNC_UIDVALIDITY = 0;
+    const SYNC_FLAGS = 1;
+    const SYNC_FLAGSUIDS = 2;
+    const SYNC_NEWMSGS = 4;
+    const SYNC_NEWMSGSUIDS = 8;
+    const SYNC_VANISHED = 16;
+    const SYNC_VANISHEDUIDS = 32;
+    const SYNC_ALL = 64;
+
+    /**
+     * Capability dependencies.
+     *
+     * @var array
+     */
+    static public $capability_deps = array(
+        // RFC 5162 [1]
+        'QRESYNC' => array(
+            // QRESYNC requires CONDSTORE, but the latter is implied and is
+            // not required to be listed.
+            'ENABLE'
+        ),
+        // RFC 5182 [2.1]
+        'SEARCHRES' => array(
+            'ESEARCH'
+        ),
+        // RFC 5255 [3.1]
+        'LANGUAGE' => array(
+            'NAMESPACE'
+        ),
+        // RFC 5957 [1]
+        'SORT=DISPLAY' => array(
+            'SORT'
+        )
+    );
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeMailRfc822Addressphpfromrev22362013codebykatpostbyemailtrunkincludeHordeMailRfc822Addressphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Mail/Rfc822/Address.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Mail-Rfc822-Address.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Mail/Rfc822/Address.php                         (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Mail/Rfc822/Address.php    2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,220 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (BSD). If you
+ * did not receive this file, see http://www.horde.org/licenses/bsd.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/bsd New BSD License
+ * @package   Mail
+ */
+
+/**
+ * Object representation of a RFC 822 e-mail address.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/bsd New BSD License
+ * @package   Mail
+ *
+ * @property-read string $bare_address  The bare mailbox@host address.
+ * @property-read string $encoded  The full MIME/IDN encoded address (UTF-8).
+ * @property string $host  Returns the host part (UTF-8).
+ * @property-read string $host_idn  Returns the IDN encoded host part.
+ * @property-read string $label  The shorthand label for this address.
+ * @property string $personal  The personal part (UTF-8).
+ * @property-read string $personal_encoded  The MIME encoded personal part
+ *                                          (UTF-8).
+ * @property-read boolean $valid  Returns true if there is enough information
+ *                                in object to create a valid address.
+ */
+class Horde_Mail_Rfc822_Address extends Horde_Mail_Rfc822_Object
+{
+    /**
+     * Comments associated with the personal phrase.
+     *
+     * @var array
+     */
+    public $comment = array();
+
+    /**
+     * Local-part of the address.
+     *
+     * @var string
+     */
+    public $mailbox = null;
+
+    /**
+     * Hostname of the address.
+     *
+     * @var string
+     */
+    protected $_host = null;
+
+    /**
+     * Personal part of the address.
+     *
+     * @var string
+     */
+    protected $_personal = null;
+
+    /**
+     * Constructor.
+     *
+     * @param string $address  If set, address is parsed and used as the
+     *                         object address. Address is not validated;
+     *                         first e-mail address parsed is used.
+     */
+    public function __construct($address = null)
+    {
+        if (!is_null($address)) {
+            $rfc822 = new Horde_Mail_Rfc822();
+            $addr = $rfc822->parseAddressList($address);
+            if (count($addr)) {
+                foreach ($addr[0] as $key => $val) {
+                    $this->$key = $val;
+                }
+            }
+        }
+    }
+
+    /**
+     */
+    public function __set($name, $value)
+    {
+        switch ($name) {
+        case 'host':
+            $value = ltrim($value, '@');
+            $this->_host = function_exists('idn_to_utf8')
+                ? strtolower(idn_to_utf8($value))
+                : strtolower($value);
+            break;
+
+        case 'personal':
+            $this->_personal = strlen($value)
+                ? Horde_Mime::decode($value)
+                : null;
+            break;
+        }
+    }
+
+    /**
+     */
+    public function __get($name)
+    {
+        switch ($name) {
+        case 'bare_address':
+            return is_null($this->host)
+                ? $this->mailbox
+                : $this->mailbox . '@' . $this->host;
+
+        case 'encoded':
+            return $this->writeAddress(true);
+
+        case 'host':
+            return $this->_host;
+
+        case 'host_idn':
+            return function_exists('idn_to_ascii')
+                ? idn_to_ascii($this->_host)
+                : $this->host;
+
+        case 'label':
+            return is_null($this->_personal)
+                ? $this->bare_address
+                : $this->_personal;
+
+        case 'personal':
+            return $this->_personal;
+
+        case 'personal_encoded':
+            return Horde_Mime::encode($this->personal);
+
+        case 'valid':
+            return (bool)strlen($this->mailbox);
+
+        default:
+            return null;
+        }
+    }
+
+    /**
+     */
+    protected function _writeAddress($opts)
+    {
+        $rfc822 = new Horde_Mail_Rfc822();
+
+        $address = $rfc822->encode($this->mailbox, 'address');
+        $host = empty($opts['idn']) ? $this->host : $this->host_idn;
+        if (strlen($host)) {
+            $address .= '@' . $host;
+        }
+        $personal = $this->personal;
+        if (strlen($personal)) {
+            if (!empty($opts['encode'])) {
+                $personal = Horde_Mime::encode($this->personal, $opts['encode']);
+            }
+            $personal = $rfc822->encode($personal, 'personal');
+        }
+
+        return (strlen($personal) && ($personal != $address))
+            ? $personal . ' <' . $address . '>'
+            : $address;
+    }
+
+    /**
+     */
+    public function match($ob)
+    {
+        if (!($ob instanceof Horde_Mail_Rfc822_Address)) {
+            $ob = new Horde_Mail_Rfc822_Address($ob);
+        }
+
+        return ($this->bare_address == $ob->bare_address);
+    }
+
+    /**
+     * Do a case-insensitive match on the address. Per RFC 822/2822/5322,
+     * although the host portion of an address is case-insensitive, the
+     * mailbox portion is platform dependent.
+     *
+     * @param mixed $ob  Address data.
+     *
+     * @return boolean  True if the data reflects the same case-insensitive
+     *                  address.
+     */
+    public function matchInsensitive($ob)
+    {
+        if (!($ob instanceof Horde_Mail_Rfc822_Address)) {
+            $ob = new Horde_Mail_Rfc822_Address($ob);
+        }
+
+        return (Horde_String::lower($this->bare_address) == Horde_String::lower($ob->bare_address));
+    }
+
+    /**
+     * Do a case-insensitive match on the address for a given domain.
+     * Matches as many parts of the subdomain in the address as is given in
+     * the input.
+     *
+     * @param string $domain  Domain to match.
+     *
+     * @return boolean  True if the address matches the given domain.
+     */
+    public function matchDomain($domain)
+    {
+        $host = $this->host;
+        if (is_null($host)) {
+            return false;
+        }
+
+        $match_domain = explode('.', $domain);
+        $match_host = array_slice(explode('.', $host), count($match_domain) * -1);
+
+        return (strcasecmp($domain, implode('.', $match_host)) === 0);
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeMailRfc822Listphpfromrev22362013codebykatpostbyemailtrunkincludeHordeMailRfc822Listphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Mail/Rfc822/List.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Mail-Rfc822-List.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Mail/Rfc822/List.php                            (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Mail/Rfc822/List.php       2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,493 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (BSD). If you
+ * did not receive this file, see http://www.horde.org/licenses/bsd.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/bsd New BSD License
+ * @package   Mail
+ */
+
+/**
+ * Container object for a collection of RFC 822 elements.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/bsd New BSD License
+ * @package   Mail
+ *
+ * @property-read array $addresses  The list of all addresses (address
+ *                                  w/personal parts).
+ * @property-read array $bare_addresses  The list of all addresses (mail@host).
+ * @property-read array $base_addresses  The list of ONLY base addresses
+ *                                       (Address objects).
+ * @property-read array $raw_addresses  The list of all addresses (Address
+ *                                      objects).
+ */
+class Horde_Mail_Rfc822_List extends Horde_Mail_Rfc822_Object implements ArrayAccess, Countable, SeekableIterator, Serializable
+{
+    /** Filter masks. */
+    const HIDE_GROUPS = 1;
+    const BASE_ELEMENTS = 2;
+
+    /**
+     * List data.
+     *
+     * @var array
+     */
+    protected $_data = array();
+
+    /**
+     * Current Iterator filter.
+     *
+     * @var array
+     */
+    protected $_filter = array();
+
+    /**
+     * Current Iterator pointer.
+     *
+     * @var array
+     */
+    protected $_ptr;
+
+    /**
+     * Constructor.
+     *
+     * @param mixed $obs  Address data to store in this object.
+     */
+    public function __construct($obs = null)
+    {
+        if (!is_null($obs)) {
+            $this->add($obs);
+        }
+    }
+
+    /**
+     */
+    public function __get($name)
+    {
+        switch ($name) {
+        case 'addresses':
+        case 'bare_addresses':
+        case 'base_addresses':
+        case 'raw_addresses':
+            $old = $this->_filter;
+            $mask = ($name == 'base_addresses')
+                ? self::BASE_ELEMENTS
+                : self::HIDE_GROUPS;
+            $this->setIteratorFilter($mask, empty($old['filter']) ? null : $old['filter']);
+
+            $out = array();
+            foreach ($this as $val) {
+                switch ($name) {
+                case 'addresses':
+                    $out[] = strval($val);
+                    break;
+
+                case 'bare_addresses':
+                    $out[] = $val->bare_address;
+                    break;
+
+                case 'base_addresses':
+                case 'raw_addresses':
+                    $out[] = clone $val;
+                    break;
+                }
+            }
+
+            $this->_filter = $old;
+            return $out;
+        }
+    }
+
+    /**
+     * Add objects to the container.
+     *
+     * @param mixed $obs  Address data to store in this object.
+     */
+    public function add($obs)
+    {
+        foreach ($this->_normalize($obs) as $val) {
+            $this->_data[] = $val;
+        }
+    }
+
+    /**
+     * Remove addresses from the container. This method ignores Group objects.
+     *
+     * @param mixed $obs  Addresses to remove.
+     */
+    public function remove($obs)
+    {
+        $old = $this->_filter;
+        $this->setIteratorFilter(self::HIDE_GROUPS | self::BASE_ELEMENTS);
+
+        foreach ($this->_normalize($obs) as $val) {
+            $remove = array();
+
+            foreach ($this as $key => $val2) {
+                if ($val2->match($val)) {
+                    $remove[] = $key;
+                }
+            }
+
+            foreach (array_reverse($remove) as $key) {
+                unset($this[$key]);
+            }
+        }
+
+        $this->_filter = $old;
+    }
+
+    /**
+     * Removes duplicate addresses from list. This method ignores Group
+     * objects.
+     */
+    public function unique()
+    {
+        $exist = $remove = array();
+
+        $old = $this->_filter;
+        $this->setIteratorFilter(self::HIDE_GROUPS | self::BASE_ELEMENTS);
+
+        // For duplicates, we use the first address that contains personal
+        // information.
+        foreach ($this as $key => $val) {
+            $bare = $val->bare_address;
+            if (isset($exist[$bare])) {
+                if (($exist[$bare] == -1) || is_null($val->personal)) {
+                    $remove[] = $key;
+                } else {
+                    $remove[] = $exist[$bare];
+                    $exist[$bare] = -1;
+                }
+            } else {
+                $exist[$bare] = is_null($val->personal)
+                    ? $key
+                    : -1;
+            }
+        }
+
+        foreach (array_reverse($remove) as $key) {
+            unset($this[$key]);
+        }
+
+        $this->_filter = $old;
+    }
+
+    /**
+     * Group count.
+     *
+     * @return integer  The number of groups in the list.
+     */
+    public function groupCount()
+    {
+        $ret = 0;
+
+        foreach ($this->_data as $val) {
+            if ($val instanceof Horde_Mail_Rfc822_Group) {
+                ++$ret;
+            }
+        }
+
+        return $ret;
+    }
+
+    /**
+     * Set the Iterator filter.
+     *
+     * @param integer $mask  Filter masks.
+     * @param mixed $filter  An e-mail, or as list of e-mails, to filter by.
+     */
+    public function setIteratorFilter($mask = 0, $filter = null)
+    {
+        $this->_filter = array();
+
+        if ($mask) {
+            $this->_filter['mask'] = $mask;
+        }
+
+        if (!is_null($filter)) {
+            $rfc822 = new Horde_Mail_Rfc822();
+            $this->_filter['filter'] = $rfc822->parseAddressList($filter);
+        }
+    }
+
+    /**
+     */
+    protected function _writeAddress($opts)
+    {
+        $out = array();
+
+        foreach ($this->_data as $val) {
+            $out[] = $val->writeAddress($opts);
+        }
+
+        return implode(', ', $out);
+    }
+
+    /**
+     */
+    public function match($ob)
+    {
+        if (!($ob instanceof Horde_Mail_Rfc822_List)) {
+            $ob = new Horde_Mail_Rfc822_List($ob);
+        }
+
+        $a = $this->bare_addresses;
+        sort($a);
+        $b = $ob->bare_addresses;
+        sort($b);
+
+        return ($a == $b);
+    }
+
+    /**
+     * Does this list contain the given e-mail address?
+     *
+     * @param mixed $address  An e-mail address.
+     *
+     * @return boolean  True of the e-mail address is contained in the list.
+     */
+    public function contains($address)
+    {
+        $ob = new Horde_Mail_Rfc822_Address($address);
+
+        foreach ($this->raw_addresses as $val) {
+            if ($val->match($ob)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Normalize objects to add to list.
+     *
+     * @param mixed $obs  Address data to store in this object.
+     *
+     * @return array  Entries to add.
+     */
+    protected function _normalize($obs)
+    {
+        $add = array();
+
+        if (!($obs instanceof Horde_Mail_Rfc822_List) &&
+            !is_array($obs)) {
+            $obs = array($obs);
+        }
+
+        foreach ($obs as $val) {
+            if (is_string($val)) {
+                $rfc822 = new Horde_Mail_Rfc822();
+                $val = $rfc822->parseAddressList($val);
+            }
+
+            if ($val instanceof Horde_Mail_Rfc822_List) {
+                $val->setIteratorFilter(self::BASE_ELEMENTS);
+                foreach ($val as $val2) {
+                    $add[] = $val2;
+                }
+            } elseif ($val instanceof Horde_Mail_Rfc822_Object) {
+                $add[] = $val;
+            }
+        }
+
+        return $add;
+    }
+
+    /* ArrayAccess methods. */
+
+    /**
+     */
+    public function offsetExists($offset)
+    {
+        return !is_null($this[$offset]);
+    }
+
+    /**
+     */
+    public function offsetGet($offset)
+    {
+        try {
+            $this->seek($offset);
+            return $this->current();
+        } catch (OutOfBoundsException $e) {
+            return null;
+        }
+    }
+
+    /**
+     */
+    public function offsetSet($offset, $value)
+    {
+        if ($ob = $this[$offset]) {
+            if (is_null($this->_ptr['subidx'])) {
+                $tmp = $this->_normalize($value);
+                if (isset($tmp[0])) {
+                    $this->_data[$this->_ptr['idx']] = $tmp[0];
+                }
+            } else {
+                $ob[$offset] = $value;
+            }
+            $this->_ptr = null;
+        }
+    }
+
+    /**
+     */
+    public function offsetUnset($offset)
+    {
+        if ($ob = $this[$offset]) {
+            if (is_null($this->_ptr['subidx'])) {
+                unset($this->_data[$this->_ptr['idx']]);
+                $this->_data = array_values($this->_data);
+            } else {
+                unset($ob->addresses[$this->_ptr['subidx']]);
+            }
+            $this->_ptr = null;
+        }
+    }
+
+    /* Countable methods. */
+
+    /**
+     * Address count.
+     *
+     * @return integer  The number of addresses.
+     */
+    public function count()
+    {
+        return count($this->addresses);
+    }
+
+    /* Iterator methods. */
+
+    public function current()
+    {
+        if (!$this->valid()) {
+            return null;
+        }
+
+        $ob = $this->_data[$this->_ptr['idx']];
+
+        return is_null($this->_ptr['subidx'])
+            ? $ob
+            : $ob->addresses[$this->_ptr['subidx']];
+    }
+
+    public function key()
+    {
+        return $this->_ptr['key'];
+    }
+
+    public function next()
+    {
+        if (is_null($this->_ptr['subidx'])) {
+            $curr = $this->current();
+            if (($curr instanceof Horde_Mail_Rfc822_Group) && count($curr)) {
+                $this->_ptr['subidx'] = 0;
+            } else {
+                ++$this->_ptr['idx'];
+            }
+            $curr = $this->current();
+        } elseif (!($curr = $this->_data[$this->_ptr['idx']]->addresses[++$this->_ptr['subidx']])) {
+            $this->_ptr['subidx'] = null;
+            ++$this->_ptr['idx'];
+            $curr = $this->current();
+        }
+
+        if (!is_null($curr)) {
+            if (!empty($this->_filter) && $this->_iteratorFilter($curr)) {
+                $this->next();
+            } else {
+                ++$this->_ptr['key'];
+            }
+        }
+    }
+
+    public function rewind()
+    {
+        $this->_ptr = array(
+            'idx' => 0,
+            'key' => 0,
+            'subidx' => null
+        );
+
+        if ($this->valid() &&
+            !empty($this->_filter) &&
+            $this->_iteratorFilter($this->current())) {
+            $this->next();
+            $this->_ptr['key'] = 0;
+        }
+    }
+
+    public function valid()
+    {
+        return (!empty($this->_ptr) && isset($this->_data[$this->_ptr['idx']]));
+    }
+
+    public function seek($position)
+    {
+        if (!$this->valid() ||
+            ($position < $this->_ptr['key'])) {
+            $this->rewind();
+        }
+
+        for ($i = $this->_ptr['key']; ; ++$i) {
+            if ($i == $position) {
+                return;
+            }
+
+            $this->next();
+            if (!$this->valid()) {
+                throw new OutOfBoundsException('Position not found.');
+            }
+        }
+    }
+
+    protected function _iteratorFilter($ob)
+    {
+        if (!empty($this->_filter['mask'])) {
+            if (($this->_filter['mask'] & self::HIDE_GROUPS) &&
+                ($ob instanceof Horde_Mail_Rfc822_Group)) {
+                return true;
+            }
+
+            if (($this->_filter['mask'] & self::BASE_ELEMENTS) &&
+                !is_null($this->_ptr['subidx'])) {
+                return true;
+            }
+        }
+
+        if (!empty($this->_filter['filter']) &&
+            ($ob instanceof Horde_Mail_Rfc822_Address)) {
+            foreach ($this->_filter['filter'] as $val) {
+                if ($ob->match($val)) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    /* Serializable methods. */
+
+    public function serialize()
+    {
+        return serialize($this->_data);
+    }
+
+    public function unserialize($data)
+    {
+        $this->_data = unserialize($data);
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeMailRfc822Objectphpfromrev22362013codebykatpostbyemailtrunkincludeHordeMailRfc822Objectphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Mail/Rfc822/Object.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Mail-Rfc822-Object.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Mail/Rfc822/Object.php                          (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Mail/Rfc822/Object.php     2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,86 @@
</span><ins>+<?php
+/**
+ * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (BSD). If you
+ * did not receive this file, see http://www.horde.org/licenses/bsd.
+ *
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/bsd New BSD License
+ * @package   Mail
+ */
+
+/**
+ * Object representation of an RFC 822 element.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2012-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/bsd New BSD License
+ * @package   Mail
+ */
+abstract class Horde_Mail_Rfc822_Object
+{
+    /**
+     * String representation of object.
+     *
+     * @return string  Returns the full e-mail address.
+     */
+    public function __toString()
+    {
+        return $this->writeAddress();
+    }
+
+    /**
+     * Write an address given information in this part.
+     *
+     * @param mixed $opts  If boolean true, is equivalent to passing true for
+     *                     both 'encode' and 'idn'. If an array, these
+     *                     keys are supported:
+     *   - encode: (mixed) MIME encode the personal/groupname parts?
+     *             If boolean true, encodes in 'UTF-8'.
+     *             If a string, encodes using this charset.
+     *             DEFAULT: false
+     *   - idn: (boolean) If true, encodes IDN domain names
+     *          (Punycode/RFC 3490).
+     *          Requires the idn or intl PHP module.
+     *          DEFAULT: false
+     *
+     * @return string  The correctly escaped/quoted address.
+     */
+    public function writeAddress($opts = array())
+    {
+        if ($opts === true) {
+            $opts = array(
+                'encode' => 'UTF-8',
+                'idn' => true
+            );
+        } elseif (!empty($opts['encode']) && ($opts['encode'] === true)) {
+            $opts['encode'] = 'UTF-8';
+        }
+
+        return $this->_writeAddress($opts);
+    }
+
+    /**
+     * Class-specific implementation of writeAddress().
+     *
+     * @see writeAddress()
+     *
+     * @param array $opts  See writeAddress().
+     *
+     * @return string  The correctly escaped/quoted address.
+     */
+    abstract protected function _writeAddress($opts);
+
+    /**
+     * Compare this object against other data.
+     *
+     * @param mixed $ob  Address data.
+     *
+     * @return boolean  True if the data reflects the same canonical address.
+     */
+    abstract public function match($ob);
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeMailRfc822phpfromrev22362013codebykatpostbyemailtrunkincludeHordeMailRfc822php"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Mail/Rfc822.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Mail-Rfc822.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Mail/Rfc822.php                         (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Mail/Rfc822.php    2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,839 @@
</span><ins>+<?php
+/**
+ * Copyright (c) 2001-2010, Richard Heyes
+ * Copyright 2011-2013 Horde LLC (http://www.horde.org/)
+ * 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.
+ *
+ * RFC822 parsing code adapted from message-address.c and rfc822-parser.c
+ *   (Dovecot 2.1rc5)
+ *   Original code released under LGPL-2.1
+ *   Copyright (c) 2002-2011 Timo Sirainen <tss@iki.fi>
+ *
+ * @category  Horde
+ * @copyright 2001-2010 Richard Heyes
+ * @copyright 2002-2011 Timo Sirainen
+ * @copyright 2011-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/bsd New BSD License
+ * @package   Mail
+ */
+
+/**
+ * RFC 822/2822/3490/5322 Email parser/validator.
+ *
+ * @author    Richard Heyes <richard@phpguru.org>
+ * @author    Chuck Hagenbuch <chuck@horde.org>
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @author    Timo Sirainen <tss@iki.fi>
+ * @category  Horde
+ * @copyright 2001-2010 Richard Heyes
+ * @copyright 2002-2011 Timo Sirainen
+ * @copyright 2011-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/bsd New BSD License
+ * @package   Mail
+ */
+class Horde_Mail_Rfc822
+{
+    /**
+     * Valid atext characters.
+     *
+     * @since 2.0.3
+     */
+    const ATEXT = '!#$%&\'*+-./0123456789=?ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~';
+
+    /**
+     * Excluded (in ASCII): 0-8, 10-31, 34, 40-41, 44, 58-60, 62, 64,
+     * 91-93, 127
+     *
+     * @since 2.0.3
+     */
+    const ENCODE_FILTER = "\0\1\2\3\4\5\6\7\10\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\"(),:;<>@[\\]\177";
+
+    /**
+     * The address string to parse.
+     *
+     * @var string
+     */
+    protected $_data;
+
+    /**
+     * Length of the address string.
+     *
+     * @var integer
+     */
+    protected $_datalen;
+
+    /**
+     * Comment cache.
+     *
+     * @var string
+     */
+    protected $_comments = array();
+
+    /**
+     * List object to return in parseAddressList().
+     *
+     * @var Horde_Mail_Rfc822_List
+     */
+    protected $_listob;
+
+    /**
+     * Configuration parameters.
+     *
+     * @var array
+     */
+    protected $_params = array();
+
+    /**
+     * Data pointer.
+     *
+     * @var integer
+     */
+    protected $_ptr;
+
+    /**
+     * Starts the whole process.
+     *
+     * @param mixed $address   The address(es) to validate. Either a string,
+     *                         a Horde_Mail_Rfc822_Object, or an array of
+     *                         strings and/or Horde_Mail_Rfc822_Objects.
+     * @param array $params    Optional parameters:
+     *   - default_domain: (string) Default domain/host.
+     *                     DEFAULT: None
+     *   - group: (boolean) Return a GroupList object instead of a List object?
+     *            DEFAULT: false
+     *   - limit: (integer) Stop processing after this many addresses.
+     *            DEFAULT: No limit (0)
+     *   - validate: (boolean) Strict validation of personal part data? If
+     *               true, throws an Exception on error. If false, attempts
+     *               to allow non-ASCII characters and non-quoted strings in
+     *               the personal data, and will silently abort if an
+     *               unparseable address is found.
+     *               DEFAULT: false
+     *
+     * @return Horde_Mail_Rfc822_List  A list object.
+     *
+     * @throws Horde_Mail_Exception
+     */
+    public function parseAddressList($address, array $params = array())
+    {
+        if ($address instanceof Horde_Mail_Rfc822_List) {
+            return $address;
+        }
+
+        if (empty($params['limit'])) {
+            $params['limit'] = null;
+        }
+
+        $this->_params = array_merge(array(
+            'default_domain' => null,
+            'validate' => false
+        ), $params);
+
+        $this->_listob = empty($this->_params['group'])
+            ? new Horde_Mail_Rfc822_List()
+            : new Horde_Mail_Rfc822_GroupList();
+
+        if (!is_array($address)) {
+            $address = array($address);
+        }
+
+        $tmp = array();
+        foreach ($address as $val) {
+            if ($val instanceof Horde_Mail_Rfc822_Object) {
+                $this->_listob->add($val);
+            } else {
+                $tmp[] = rtrim(trim($val), ',');
+            }
+        }
+
+        if (!empty($tmp)) {
+            $this->_data = implode(',', $tmp);
+            $this->_datalen = strlen($this->_data);
+            $this->_ptr = 0;
+
+            $this->_parseAddressList();
+        }
+
+        return $this->_listob;
+    }
+
+   /**
+     * Quotes and escapes the given string if necessary using rules contained
+     * in RFC 2822 [3.2.5].
+     *
+     * @param string $str   The string to be quoted and escaped.
+     * @param string $type  Either 'address', or 'personal'.
+     *
+     * @return string  The correctly quoted and escaped string.
+     */
+    public function encode($str, $type = 'address')
+    {
+        switch ($type) {
+        case 'personal':
+            // RFC 2822 [3.4]: Period not allowed in display name
+            $filter = '.';
+            break;
+
+        case 'address':
+        default:
+            // RFC 2822 [3.4.1]: (HTAB, SPACE) not allowed in address
+            $filter = "\11\40";
+            break;
+        }
+
+        // Strip double quotes if they are around the string already.
+        // If quoted, we know that the contents are already escaped, so
+        // unescape now.
+        $str = trim($str);
+        if ($str && ($str[0] == '"') && (substr($str, -1) == '"')) {
+            $str = stripslashes(substr($str, 1, -1));
+        }
+
+        return (strcspn($str, self::ENCODE_FILTER . $filter) != strlen($str))
+            ? '"' . addcslashes($str, '\\"') . '"'
+            : $str;
+    }
+
+    /**
+     * If an email address has no personal information, get rid of any angle
+     * brackets (<>) around it.
+     *
+     * @param string $address  The address to trim.
+     *
+     * @return string  The trimmed address.
+     */
+    public function trimAddress($address)
+    {
+        $address = trim($address);
+
+        return (($address[0] == '<') && (substr($address, -1) == '>'))
+            ? substr($address, 1, -1)
+            : $address;
+    }
+
+    /* RFC 822 parsing methods. */
+
+    /**
+     * address-list = (address *("," address)) / obs-addr-list
+     */
+    protected function _parseAddressList()
+    {
+        $limit = $this->_params['limit'];
+
+        while (($this->_curr() !== false) &&
+               (is_null($limit) || ($limit > 0))) {
+            try {
+                $this->_parseAddress();
+            } catch (Horde_Mail_Exception $e) {
+               if ($this->_params['validate']) {
+                   throw $e;
+               }
+               ++$this->_ptr;
+            }
+
+            switch ($this->_curr()) {
+            case ',':
+                $this->_rfc822SkipLwsp(true);
+                break;
+
+            case false:
+                // No-op
+                break;
+
+            default:
+               if ($this->_params['validate']) {
+                    throw new Horde_Mail_Exception('Error when parsing address list.');
+               }
+               break;
+            }
+            $limit--;
+        }
+    }
+
+    /**
+     * address = mailbox / group
+     */
+    protected function _parseAddress()
+    {
+        $start = $this->_ptr;
+        if (!$this->_parseGroup()) {
+            $this->_ptr = $start;
+            if ($mbox = $this->_parseMailbox()) {
+                $this->_listob->add($mbox);
+            }
+        }
+    }
+
+    /**
+     * group           = display-name ":" [mailbox-list / CFWS] ";" [CFWS]
+     * display-name    = phrase
+     *
+     * @return boolean  True if a group was parsed.
+     *
+     * @throws Horde_Mail_Exception
+     */
+    protected function _parseGroup()
+    {
+        $this->_rfc822ParsePhrase($groupname);
+
+        if ($this->_curr(true) != ':') {
+            return false;
+        }
+
+        $addresses = new Horde_Mail_Rfc822_GroupList();
+
+        $this->_rfc822SkipLwsp();
+
+        while (($chr = $this->_curr()) !== false) {
+            if ($chr == ';') {
+                ++$this->_ptr;
+
+                if (count($addresses)) {
+                    $this->_listob->add(new Horde_Mail_Rfc822_Group($groupname, $addresses));
+                }
+
+                return true;
+            }
+
+            /* mailbox-list = (mailbox *("," mailbox)) / obs-mbox-list */
+            $addresses->add($this->_parseMailbox());
+
+            switch ($this->_curr()) {
+            case ',':
+                $this->_rfc822SkipLwsp(true);
+                break;
+
+            case ';':
+                // No-op
+                break;
+
+            default:
+                break 2;
+            }
+        }
+
+        throw new Horde_Mail_Exception('Error when parsing group.');
+    }
+
+    /**
+     * mailbox = name-addr / addr-spec
+     *
+     * @return mixed  Mailbox object if mailbox was parsed, or false.
+     */
+    protected function _parseMailbox()
+    {
+        $this->_comments = array();
+        $start = $this->_ptr;
+
+        if (!($ob = $this->_parseNameAddr())) {
+            $this->_comments = array();
+            $this->_ptr = $start;
+            $ob = $this->_parseAddrSpec();
+        }
+
+        if ($ob) {
+            $ob->comment = $this->_comments;
+        }
+
+        return $ob;
+    }
+
+    /**
+     * name-addr    = [display-name] angle-addr
+     * display-name = phrase
+     *
+     * @return mixed  Mailbox object, or false.
+     */
+    protected function _parseNameAddr()
+    {
+        $this->_rfc822ParsePhrase($personal);
+
+        if ($ob = $this->_parseAngleAddr()) {
+            $ob->personal = $personal;
+            return $ob;
+        }
+
+        return false;
+    }
+
+    /**
+     * addr-spec = local-part "@" domain
+     *
+     * @return mixed  Mailbox object.
+     *
+     * @throws Horde_Mail_Exception
+     */
+    protected function _parseAddrSpec()
+    {
+        $ob = new Horde_Mail_Rfc822_Address();
+        $ob->mailbox = $this->_parseLocalPart();
+
+        if ($this->_curr() == '@') {
+            $this->_rfc822ParseDomain($host);
+            if (strlen($host)) {
+                $ob->host = $host;
+            }
+        }
+
+        if (is_null($ob->host) &&
+            !is_null($this->_params['default_domain'])) {
+            $ob->host = $this->_params['default_domain'];
+        }
+
+        return $ob;
+    }
+
+    /**
+     * local-part      = dot-atom / quoted-string / obs-local-part
+     * obs-local-part  = word *("." word)
+     *
+     * @return string  The local part.
+     *
+     * @throws Horde_Mail_Exception
+     */
+    protected function _parseLocalPart()
+    {
+        if (($curr = $this->_curr()) === false) {
+            throw new Horde_Mail_Exception('Error when parsing local part.');
+        }
+
+        if ($curr == '"') {
+            $this->_rfc822ParseQuotedString($str);
+        } else {
+            $this->_rfc822ParseDotAtom($str, ',;@');
+        }
+
+        return $str;
+    }
+
+    /**
+     * "<" [ "@" route ":" ] local-part "@" domain ">"
+     *
+     * @return mixed  Mailbox object, or false.
+     *
+     * @throws Horde_Mail_Exception
+     */
+    protected function _parseAngleAddr()
+    {
+        if ($this->_curr() != '<') {
+            return false;
+        }
+
+        $this->_rfc822SkipLwsp(true);
+
+        if ($this->_curr() == '@') {
+            // Route information is ignored.
+            $this->_parseDomainList();
+            if ($this->_curr() != ':') {
+                throw new Horde_Mail_Exception('Invalid route.');
+            }
+
+            $this->_rfc822SkipLwsp(true);
+        }
+
+        $ob = $this->_parseAddrSpec();
+
+        if ($this->_curr() != '>') {
+            throw new Horde_Mail_Exception('Error when parsing angle address.');
+        }
+
+        $this->_rfc822SkipLwsp(true);
+
+        return $ob;
+    }
+
+    /**
+     * obs-domain-list = "@" domain *(*(CFWS / "," ) [CFWS] "@" domain)
+     *
+     * @return array  Routes.
+     *
+     * @throws Horde_Mail_Exception
+     */
+    protected function _parseDomainList()
+    {
+        $route = array();
+
+        while ($this->_curr() !== false) {
+            $this->_rfc822ParseDomain($str);
+            $route[] = '@' . $str;
+
+            $this->_rfc822SkipLwsp();
+            if ($this->_curr() != ',') {
+                return $route;
+            }
+            ++$this->_ptr;
+        }
+
+        throw new Horde_Mail_Exception('Invalid domain list.');
+    }
+
+    /* RFC 822 parsing methods. */
+
+    /**
+     * phrase     = 1*word / obs-phrase
+     * word       = atom / quoted-string
+     * obs-phrase = word *(word / "." / CFWS)
+     *
+     * @param string &$phrase  The phrase data.
+     *
+     * @throws Horde_Mail_Exception
+     */
+    protected function _rfc822ParsePhrase(&$phrase)
+    {
+        $curr = $this->_curr();
+        if (($curr === false) || ($curr == '.')) {
+            throw new Horde_Mail_Exception('Error when parsing a group.');
+        }
+
+        while (($curr = $this->_curr()) !== false) {
+            if ($curr == '"') {
+                $this->_rfc822ParseQuotedString($phrase);
+            } else {
+                $this->_rfc822ParseAtomOrDot($phrase);
+            }
+
+            $chr = $this->_curr();
+            if (!$this->_rfc822IsAtext($chr) &&
+                ($chr != '"') &&
+                ($chr != '.')) {
+                break;
+            }
+
+            $phrase .= ' ';
+        }
+
+        $this->_rfc822SkipLwsp();
+    }
+
+    /**
+     * @param string &$phrase  The quoted string data.
+     *
+     * @throws Horde_Mail_Exception
+     */
+    protected function _rfc822ParseQuotedString(&$str)
+    {
+        if ($this->_curr(true) != '"') {
+            throw new Horde_Mail_Exception('Error when parsing a quoted string.');
+        }
+
+        while (($chr = $this->_curr(true)) !== false) {
+            switch ($chr) {
+            case '"':
+                $this->_rfc822SkipLwsp();
+                return;
+
+            case "\n":
+                /* Folding whitespace, remove the (CR)LF. */
+                if ($str[strlen($str) - 1] == "\r") {
+                    $str = substr($str, 0, -1);
+                }
+                continue;
+
+            case '\\':
+                if (($chr = $this->_curr(true)) === false) {
+                    break 2;
+                }
+                break;
+            }
+
+            $str .= $chr;
+        }
+
+        /* Missing trailing '"', or partial quoted character. */
+        throw new Horde_Mail_Exception('Error when parsing a quoted string.');
+    }
+
+    /**
+     * dot-atom        = [CFWS] dot-atom-text [CFWS]
+     * dot-atom-text   = 1*atext *("." 1*atext)
+     *
+     * atext           = ; Any character except controls, SP, and specials.
+     *
+     * For RFC-822 compatibility allow LWSP around '.'
+     *
+     * @param string &$str      The atom/dot data.
+     * @param string $validate  Use these characters as delimiter.
+     *
+     * @throws Horde_Mail_Exception
+     */
+    protected function _rfc822ParseDotAtom(&$str, $validate = null)
+    {
+        $curr = $this->_curr();
+        if (($curr === false) || !$this->_rfc822IsAtext($curr, $validate)) {
+            throw new Horde_Mail_Exception('Error when parsing dot-atom.');
+        }
+
+        while (($chr = $this->_curr()) !== false) {
+            if ($this->_rfc822IsAtext($chr, $validate)) {
+                $str .= $chr;
+                ++$this->_ptr;
+            } else {
+                $this->_rfc822SkipLwsp();
+
+                if ($this->_curr() != '.') {
+                    return;
+                }
+                $str .= '.';
+
+                $this->_rfc822SkipLwsp(true);
+            }
+        }
+    }
+
+    /**
+     * atom  = [CFWS] 1*atext [CFWS]
+     * atext = ; Any character except controls, SP, and specials.
+     *
+     * This method doesn't just silently skip over WS.
+     *
+     * @param string &$str  The atom/dot data.
+     *
+     * @throws Horde_Mail_Exception
+     */
+    protected function _rfc822ParseAtomOrDot(&$str)
+    {
+        while (($chr = $this->_curr()) !== false) {
+            if (($chr != '.') && !$this->_rfc822IsAtext($chr, ',<:')) {
+                $this->_rfc822SkipLwsp();
+                if (!$this->_params['validate']) {
+                    $str = trim($str);
+                }
+                return;
+            }
+
+            $str .= $chr;
+            ++$this->_ptr;
+        }
+    }
+
+    /**
+     * domain          = dot-atom / domain-literal / obs-domain
+     * domain-literal  = [CFWS] "[" *([FWS] dcontent) [FWS] "]" [CFWS]
+     * obs-domain      = atom *("." atom)
+     *
+     * @param string &$str  The domain string.
+     *
+     * @throws Horde_Mail_Exception
+     */
+    protected function _rfc822ParseDomain(&$str)
+    {
+        if ($this->_curr(true) != '@') {
+            throw new Horde_Mail_Exception('Error when parsing domain.');
+        }
+
+        $this->_rfc822SkipLwsp();
+
+        if ($this->_curr() == '[') {
+            $this->_rfc822ParseDomainLiteral($str);
+        } else {
+            $this->_rfc822ParseDotAtom($str, ';,> ');
+        }
+    }
+
+    /**
+     * domain-literal  = [CFWS] "[" *([FWS] dcontent) [FWS] "]" [CFWS]
+     * dcontent        = dtext / quoted-pair
+     * dtext           = NO-WS-CTL /     ; Non white space controls
+     *           %d33-90 /       ; The rest of the US-ASCII
+     *           %d94-126        ;  characters not including "[",
+     *                   ;  "]", or "\"
+     *
+     * @param string &$str  The domain string.
+     *
+     * @throws Horde_Mail_Exception
+     */
+    protected function _rfc822ParseDomainLiteral(&$str)
+    {
+        if ($this->_curr(true) != '[') {
+            throw new Horde_Mail_Exception('Error parsing domain literal.');
+        }
+
+        while (($chr = $this->_curr(true)) !== false) {
+            switch ($chr) {
+            case '\\':
+                if (($chr = $this->_curr(true)) === false) {
+                    break 2;
+                }
+                break;
+
+            case ']':
+                $this->_rfc822SkipLwsp();
+                return;
+            }
+
+            $str .= $chr;
+        }
+
+        throw new Horde_Mail_Exception('Error parsing domain literal.');
+    }
+
+    /**
+     * @param boolean $advance  Advance cursor?
+     *
+     * @throws Horde_Mail_Exception
+     */
+    protected function _rfc822SkipLwsp($advance = false)
+    {
+        if ($advance) {
+            ++$this->_ptr;
+        }
+
+        while (($chr = $this->_curr()) !== false) {
+            switch ($chr) {
+            case ' ':
+            case "\n":
+            case "\r":
+            case "\t":
+                ++$this->_ptr;
+                continue;
+
+            case '(':
+                $this->_rfc822SkipComment();
+                break;
+
+            default:
+                return;
+            }
+        }
+    }
+
+    /**
+     * @throws Horde_Mail_Exception
+     */
+    protected function _rfc822SkipComment()
+    {
+        if ($this->_curr(true) != '(') {
+            throw new Horde_Mail_Exception('Error when parsing a comment.');
+        }
+
+        $comment = '';
+        $level = 1;
+
+        while (($chr = $this->_curr(true)) !== false) {
+            switch ($chr) {
+            case '(':
+                ++$level;
+                continue;
+
+            case ')':
+                if (--$level == 0) {
+                    $this->_comments[] = $comment;
+                    return;
+                }
+                break;
+
+            case '\\':
+                if (($chr = $this->_curr(true)) === false) {
+                    break 2;
+                }
+                break;
+            }
+
+            $comment .= $chr;
+        }
+
+        throw new Horde_Mail_Exception('Error when parsing a comment.');
+    }
+
+    /**
+     * Check if data is an atom.
+     *
+     * @param string $chr       The character to check.
+     * @param string $validate  If in non-validate mode, use these characters
+     *                          as the non-atom delimiters.
+     *
+     * @return boolean  True if an atom.
+     */
+    protected function _rfc822IsAtext($chr, $validate = null)
+    {
+        if (is_null($chr)) {
+            return false;
+        }
+
+        return ($this->_params['validate'] || is_null($validate))
+            ? !strcspn($chr, self::ATEXT)
+            : strcspn($chr, $validate);
+    }
+
+    /* Helper methods. */
+
+    /**
+     * Return current character.
+     *
+     * @param boolean $advance  If true, advance the cursor.
+     *
+     * @return string  The current character (false if EOF reached).
+     */
+    protected function _curr($advance = false)
+    {
+        return ($this->_ptr >= $this->_datalen)
+            ? false
+            : $this->_data[$advance ? $this->_ptr++ : $this->_ptr];
+    }
+
+    /* Other public methods. */
+
+    /**
+     * Returns an approximate count of how many addresses are in the string.
+     * This is APPROXIMATE as it only splits based on a comma which has no
+     * preceding backslash.
+     *
+     * @param string $data  Addresses to count.
+     *
+     * @return integer  Approximate count.
+     */
+    public function approximateCount($data)
+    {
+        return count(preg_split('/(?<!\\\\),/', $data));
+    }
+
+    /**
+     * Validates whether an email is of the common internet form:
+     * <user>@<domain>. This can be sufficient for most people.
+     *
+     * Optional stricter mode can be utilized which restricts mailbox
+     * characters allowed to: alphanumeric, full stop, hyphen, and underscore.
+     *
+     * @param string $data     Address to check.
+     * @param boolean $strict  Strict check?
+     *
+     * @return mixed  False if it fails, an indexed array username/domain if
+     *                it matches.
+     */
+    public function isValidInetAddress($data, $strict = false)
+    {
+        $regex = $strict
+            ? '/^([.0-9a-z_+-]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,})$/i'
+            : '/^([*+!.&#$|\'\\%\/0-9a-z^_`{}=?~:-]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,})$/i';
+
+        return preg_match($regex, trim($data), $matches)
+            ? array($matches[1], $matches[2])
+            : false;
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeMailRfc822Addressphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Mail-Rfc822-Address.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Mail-Rfc822-Address.php 2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Mail-Rfc822-Address.php    2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,220 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (BSD). If you
- * did not receive this file, see http://www.horde.org/licenses/bsd.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/bsd New BSD License
- * @package   Mail
- */
-
-/**
- * Object representation of a RFC 822 e-mail address.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/bsd New BSD License
- * @package   Mail
- *
- * @property-read string $bare_address  The bare mailbox@host address.
- * @property-read string $encoded  The full MIME/IDN encoded address (UTF-8).
- * @property string $host  Returns the host part (UTF-8).
- * @property-read string $host_idn  Returns the IDN encoded host part.
- * @property-read string $label  The shorthand label for this address.
- * @property string $personal  The personal part (UTF-8).
- * @property-read string $personal_encoded  The MIME encoded personal part
- *                                          (UTF-8).
- * @property-read boolean $valid  Returns true if there is enough information
- *                                in object to create a valid address.
- */
-class Horde_Mail_Rfc822_Address extends Horde_Mail_Rfc822_Object
-{
-    /**
-     * Comments associated with the personal phrase.
-     *
-     * @var array
-     */
-    public $comment = array();
-
-    /**
-     * Local-part of the address.
-     *
-     * @var string
-     */
-    public $mailbox = null;
-
-    /**
-     * Hostname of the address.
-     *
-     * @var string
-     */
-    protected $_host = null;
-
-    /**
-     * Personal part of the address.
-     *
-     * @var string
-     */
-    protected $_personal = null;
-
-    /**
-     * Constructor.
-     *
-     * @param string $address  If set, address is parsed and used as the
-     *                         object address. Address is not validated;
-     *                         first e-mail address parsed is used.
-     */
-    public function __construct($address = null)
-    {
-        if (!is_null($address)) {
-            $rfc822 = new Horde_Mail_Rfc822();
-            $addr = $rfc822->parseAddressList($address);
-            if (count($addr)) {
-                foreach ($addr[0] as $key => $val) {
-                    $this->$key = $val;
-                }
-            }
-        }
-    }
-
-    /**
-     */
-    public function __set($name, $value)
-    {
-        switch ($name) {
-        case 'host':
-            $value = ltrim($value, '@');
-            $this->_host = function_exists('idn_to_utf8')
-                ? strtolower(idn_to_utf8($value))
-                : strtolower($value);
-            break;
-
-        case 'personal':
-            $this->_personal = strlen($value)
-                ? Horde_Mime::decode($value)
-                : null;
-            break;
-        }
-    }
-
-    /**
-     */
-    public function __get($name)
-    {
-        switch ($name) {
-        case 'bare_address':
-            return is_null($this->host)
-                ? $this->mailbox
-                : $this->mailbox . '@' . $this->host;
-
-        case 'encoded':
-            return $this->writeAddress(true);
-
-        case 'host':
-            return $this->_host;
-
-        case 'host_idn':
-            return function_exists('idn_to_ascii')
-                ? idn_to_ascii($this->_host)
-                : $this->host;
-
-        case 'label':
-            return is_null($this->_personal)
-                ? $this->bare_address
-                : $this->_personal;
-
-        case 'personal':
-            return $this->_personal;
-
-        case 'personal_encoded':
-            return Horde_Mime::encode($this->personal);
-
-        case 'valid':
-            return (bool)strlen($this->mailbox);
-
-        default:
-            return null;
-        }
-    }
-
-    /**
-     */
-    protected function _writeAddress($opts)
-    {
-        $rfc822 = new Horde_Mail_Rfc822();
-
-        $address = $rfc822->encode($this->mailbox, 'address');
-        $host = empty($opts['idn']) ? $this->host : $this->host_idn;
-        if (strlen($host)) {
-            $address .= '@' . $host;
-        }
-        $personal = $this->personal;
-        if (strlen($personal)) {
-            if (!empty($opts['encode'])) {
-                $personal = Horde_Mime::encode($this->personal, $opts['encode']);
-            }
-            $personal = $rfc822->encode($personal, 'personal');
-        }
-
-        return (strlen($personal) && ($personal != $address))
-            ? $personal . ' <' . $address . '>'
-            : $address;
-    }
-
-    /**
-     */
-    public function match($ob)
-    {
-        if (!($ob instanceof Horde_Mail_Rfc822_Address)) {
-            $ob = new Horde_Mail_Rfc822_Address($ob);
-        }
-
-        return ($this->bare_address == $ob->bare_address);
-    }
-
-    /**
-     * Do a case-insensitive match on the address. Per RFC 822/2822/5322,
-     * although the host portion of an address is case-insensitive, the
-     * mailbox portion is platform dependent.
-     *
-     * @param mixed $ob  Address data.
-     *
-     * @return boolean  True if the data reflects the same case-insensitive
-     *                  address.
-     */
-    public function matchInsensitive($ob)
-    {
-        if (!($ob instanceof Horde_Mail_Rfc822_Address)) {
-            $ob = new Horde_Mail_Rfc822_Address($ob);
-        }
-
-        return (Horde_String::lower($this->bare_address) == Horde_String::lower($ob->bare_address));
-    }
-
-    /**
-     * Do a case-insensitive match on the address for a given domain.
-     * Matches as many parts of the subdomain in the address as is given in
-     * the input.
-     *
-     * @param string $domain  Domain to match.
-     *
-     * @return boolean  True if the address matches the given domain.
-     */
-    public function matchDomain($domain)
-    {
-        $host = $this->host;
-        if (is_null($host)) {
-            return false;
-        }
-
-        $match_domain = explode('.', $domain);
-        $match_host = array_slice(explode('.', $host), count($match_domain) * -1);
-
-        return (strcasecmp($domain, implode('.', $match_host)) === 0);
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeMailRfc822Listphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Mail-Rfc822-List.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Mail-Rfc822-List.php    2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Mail-Rfc822-List.php       2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,493 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (BSD). If you
- * did not receive this file, see http://www.horde.org/licenses/bsd.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/bsd New BSD License
- * @package   Mail
- */
-
-/**
- * Container object for a collection of RFC 822 elements.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/bsd New BSD License
- * @package   Mail
- *
- * @property-read array $addresses  The list of all addresses (address
- *                                  w/personal parts).
- * @property-read array $bare_addresses  The list of all addresses (mail@host).
- * @property-read array $base_addresses  The list of ONLY base addresses
- *                                       (Address objects).
- * @property-read array $raw_addresses  The list of all addresses (Address
- *                                      objects).
- */
-class Horde_Mail_Rfc822_List extends Horde_Mail_Rfc822_Object implements ArrayAccess, Countable, SeekableIterator, Serializable
-{
-    /** Filter masks. */
-    const HIDE_GROUPS = 1;
-    const BASE_ELEMENTS = 2;
-
-    /**
-     * List data.
-     *
-     * @var array
-     */
-    protected $_data = array();
-
-    /**
-     * Current Iterator filter.
-     *
-     * @var array
-     */
-    protected $_filter = array();
-
-    /**
-     * Current Iterator pointer.
-     *
-     * @var array
-     */
-    protected $_ptr;
-
-    /**
-     * Constructor.
-     *
-     * @param mixed $obs  Address data to store in this object.
-     */
-    public function __construct($obs = null)
-    {
-        if (!is_null($obs)) {
-            $this->add($obs);
-        }
-    }
-
-    /**
-     */
-    public function __get($name)
-    {
-        switch ($name) {
-        case 'addresses':
-        case 'bare_addresses':
-        case 'base_addresses':
-        case 'raw_addresses':
-            $old = $this->_filter;
-            $mask = ($name == 'base_addresses')
-                ? self::BASE_ELEMENTS
-                : self::HIDE_GROUPS;
-            $this->setIteratorFilter($mask, empty($old['filter']) ? null : $old['filter']);
-
-            $out = array();
-            foreach ($this as $val) {
-                switch ($name) {
-                case 'addresses':
-                    $out[] = strval($val);
-                    break;
-
-                case 'bare_addresses':
-                    $out[] = $val->bare_address;
-                    break;
-
-                case 'base_addresses':
-                case 'raw_addresses':
-                    $out[] = clone $val;
-                    break;
-                }
-            }
-
-            $this->_filter = $old;
-            return $out;
-        }
-    }
-
-    /**
-     * Add objects to the container.
-     *
-     * @param mixed $obs  Address data to store in this object.
-     */
-    public function add($obs)
-    {
-        foreach ($this->_normalize($obs) as $val) {
-            $this->_data[] = $val;
-        }
-    }
-
-    /**
-     * Remove addresses from the container. This method ignores Group objects.
-     *
-     * @param mixed $obs  Addresses to remove.
-     */
-    public function remove($obs)
-    {
-        $old = $this->_filter;
-        $this->setIteratorFilter(self::HIDE_GROUPS | self::BASE_ELEMENTS);
-
-        foreach ($this->_normalize($obs) as $val) {
-            $remove = array();
-
-            foreach ($this as $key => $val2) {
-                if ($val2->match($val)) {
-                    $remove[] = $key;
-                }
-            }
-
-            foreach (array_reverse($remove) as $key) {
-                unset($this[$key]);
-            }
-        }
-
-        $this->_filter = $old;
-    }
-
-    /**
-     * Removes duplicate addresses from list. This method ignores Group
-     * objects.
-     */
-    public function unique()
-    {
-        $exist = $remove = array();
-
-        $old = $this->_filter;
-        $this->setIteratorFilter(self::HIDE_GROUPS | self::BASE_ELEMENTS);
-
-        // For duplicates, we use the first address that contains personal
-        // information.
-        foreach ($this as $key => $val) {
-            $bare = $val->bare_address;
-            if (isset($exist[$bare])) {
-                if (($exist[$bare] == -1) || is_null($val->personal)) {
-                    $remove[] = $key;
-                } else {
-                    $remove[] = $exist[$bare];
-                    $exist[$bare] = -1;
-                }
-            } else {
-                $exist[$bare] = is_null($val->personal)
-                    ? $key
-                    : -1;
-            }
-        }
-
-        foreach (array_reverse($remove) as $key) {
-            unset($this[$key]);
-        }
-
-        $this->_filter = $old;
-    }
-
-    /**
-     * Group count.
-     *
-     * @return integer  The number of groups in the list.
-     */
-    public function groupCount()
-    {
-        $ret = 0;
-
-        foreach ($this->_data as $val) {
-            if ($val instanceof Horde_Mail_Rfc822_Group) {
-                ++$ret;
-            }
-        }
-
-        return $ret;
-    }
-
-    /**
-     * Set the Iterator filter.
-     *
-     * @param integer $mask  Filter masks.
-     * @param mixed $filter  An e-mail, or as list of e-mails, to filter by.
-     */
-    public function setIteratorFilter($mask = 0, $filter = null)
-    {
-        $this->_filter = array();
-
-        if ($mask) {
-            $this->_filter['mask'] = $mask;
-        }
-
-        if (!is_null($filter)) {
-            $rfc822 = new Horde_Mail_Rfc822();
-            $this->_filter['filter'] = $rfc822->parseAddressList($filter);
-        }
-    }
-
-    /**
-     */
-    protected function _writeAddress($opts)
-    {
-        $out = array();
-
-        foreach ($this->_data as $val) {
-            $out[] = $val->writeAddress($opts);
-        }
-
-        return implode(', ', $out);
-    }
-
-    /**
-     */
-    public function match($ob)
-    {
-        if (!($ob instanceof Horde_Mail_Rfc822_List)) {
-            $ob = new Horde_Mail_Rfc822_List($ob);
-        }
-
-        $a = $this->bare_addresses;
-        sort($a);
-        $b = $ob->bare_addresses;
-        sort($b);
-
-        return ($a == $b);
-    }
-
-    /**
-     * Does this list contain the given e-mail address?
-     *
-     * @param mixed $address  An e-mail address.
-     *
-     * @return boolean  True of the e-mail address is contained in the list.
-     */
-    public function contains($address)
-    {
-        $ob = new Horde_Mail_Rfc822_Address($address);
-
-        foreach ($this->raw_addresses as $val) {
-            if ($val->match($ob)) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    /**
-     * Normalize objects to add to list.
-     *
-     * @param mixed $obs  Address data to store in this object.
-     *
-     * @return array  Entries to add.
-     */
-    protected function _normalize($obs)
-    {
-        $add = array();
-
-        if (!($obs instanceof Horde_Mail_Rfc822_List) &&
-            !is_array($obs)) {
-            $obs = array($obs);
-        }
-
-        foreach ($obs as $val) {
-            if (is_string($val)) {
-                $rfc822 = new Horde_Mail_Rfc822();
-                $val = $rfc822->parseAddressList($val);
-            }
-
-            if ($val instanceof Horde_Mail_Rfc822_List) {
-                $val->setIteratorFilter(self::BASE_ELEMENTS);
-                foreach ($val as $val2) {
-                    $add[] = $val2;
-                }
-            } elseif ($val instanceof Horde_Mail_Rfc822_Object) {
-                $add[] = $val;
-            }
-        }
-
-        return $add;
-    }
-
-    /* ArrayAccess methods. */
-
-    /**
-     */
-    public function offsetExists($offset)
-    {
-        return !is_null($this[$offset]);
-    }
-
-    /**
-     */
-    public function offsetGet($offset)
-    {
-        try {
-            $this->seek($offset);
-            return $this->current();
-        } catch (OutOfBoundsException $e) {
-            return null;
-        }
-    }
-
-    /**
-     */
-    public function offsetSet($offset, $value)
-    {
-        if ($ob = $this[$offset]) {
-            if (is_null($this->_ptr['subidx'])) {
-                $tmp = $this->_normalize($value);
-                if (isset($tmp[0])) {
-                    $this->_data[$this->_ptr['idx']] = $tmp[0];
-                }
-            } else {
-                $ob[$offset] = $value;
-            }
-            $this->_ptr = null;
-        }
-    }
-
-    /**
-     */
-    public function offsetUnset($offset)
-    {
-        if ($ob = $this[$offset]) {
-            if (is_null($this->_ptr['subidx'])) {
-                unset($this->_data[$this->_ptr['idx']]);
-                $this->_data = array_values($this->_data);
-            } else {
-                unset($ob->addresses[$this->_ptr['subidx']]);
-            }
-            $this->_ptr = null;
-        }
-    }
-
-    /* Countable methods. */
-
-    /**
-     * Address count.
-     *
-     * @return integer  The number of addresses.
-     */
-    public function count()
-    {
-        return count($this->addresses);
-    }
-
-    /* Iterator methods. */
-
-    public function current()
-    {
-        if (!$this->valid()) {
-            return null;
-        }
-
-        $ob = $this->_data[$this->_ptr['idx']];
-
-        return is_null($this->_ptr['subidx'])
-            ? $ob
-            : $ob->addresses[$this->_ptr['subidx']];
-    }
-
-    public function key()
-    {
-        return $this->_ptr['key'];
-    }
-
-    public function next()
-    {
-        if (is_null($this->_ptr['subidx'])) {
-            $curr = $this->current();
-            if (($curr instanceof Horde_Mail_Rfc822_Group) && count($curr)) {
-                $this->_ptr['subidx'] = 0;
-            } else {
-                ++$this->_ptr['idx'];
-            }
-            $curr = $this->current();
-        } elseif (!($curr = $this->_data[$this->_ptr['idx']]->addresses[++$this->_ptr['subidx']])) {
-            $this->_ptr['subidx'] = null;
-            ++$this->_ptr['idx'];
-            $curr = $this->current();
-        }
-
-        if (!is_null($curr)) {
-            if (!empty($this->_filter) && $this->_iteratorFilter($curr)) {
-                $this->next();
-            } else {
-                ++$this->_ptr['key'];
-            }
-        }
-    }
-
-    public function rewind()
-    {
-        $this->_ptr = array(
-            'idx' => 0,
-            'key' => 0,
-            'subidx' => null
-        );
-
-        if ($this->valid() &&
-            !empty($this->_filter) &&
-            $this->_iteratorFilter($this->current())) {
-            $this->next();
-            $this->_ptr['key'] = 0;
-        }
-    }
-
-    public function valid()
-    {
-        return (!empty($this->_ptr) && isset($this->_data[$this->_ptr['idx']]));
-    }
-
-    public function seek($position)
-    {
-        if (!$this->valid() ||
-            ($position < $this->_ptr['key'])) {
-            $this->rewind();
-        }
-
-        for ($i = $this->_ptr['key']; ; ++$i) {
-            if ($i == $position) {
-                return;
-            }
-
-            $this->next();
-            if (!$this->valid()) {
-                throw new OutOfBoundsException('Position not found.');
-            }
-        }
-    }
-
-    protected function _iteratorFilter($ob)
-    {
-        if (!empty($this->_filter['mask'])) {
-            if (($this->_filter['mask'] & self::HIDE_GROUPS) &&
-                ($ob instanceof Horde_Mail_Rfc822_Group)) {
-                return true;
-            }
-
-            if (($this->_filter['mask'] & self::BASE_ELEMENTS) &&
-                !is_null($this->_ptr['subidx'])) {
-                return true;
-            }
-        }
-
-        if (!empty($this->_filter['filter']) &&
-            ($ob instanceof Horde_Mail_Rfc822_Address)) {
-            foreach ($this->_filter['filter'] as $val) {
-                if ($ob->match($val)) {
-                    return true;
-                }
-            }
-        }
-
-        return false;
-    }
-
-    /* Serializable methods. */
-
-    public function serialize()
-    {
-        return serialize($this->_data);
-    }
-
-    public function unserialize($data)
-    {
-        $this->_data = unserialize($data);
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeMailRfc822Objectphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Mail-Rfc822-Object.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Mail-Rfc822-Object.php  2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Mail-Rfc822-Object.php     2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,86 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2012-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (BSD). If you
- * did not receive this file, see http://www.horde.org/licenses/bsd.
- *
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/bsd New BSD License
- * @package   Mail
- */
-
-/**
- * Object representation of an RFC 822 element.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2012-2013 Horde LLC
- * @license   http://www.horde.org/licenses/bsd New BSD License
- * @package   Mail
- */
-abstract class Horde_Mail_Rfc822_Object
-{
-    /**
-     * String representation of object.
-     *
-     * @return string  Returns the full e-mail address.
-     */
-    public function __toString()
-    {
-        return $this->writeAddress();
-    }
-
-    /**
-     * Write an address given information in this part.
-     *
-     * @param mixed $opts  If boolean true, is equivalent to passing true for
-     *                     both 'encode' and 'idn'. If an array, these
-     *                     keys are supported:
-     *   - encode: (mixed) MIME encode the personal/groupname parts?
-     *             If boolean true, encodes in 'UTF-8'.
-     *             If a string, encodes using this charset.
-     *             DEFAULT: false
-     *   - idn: (boolean) If true, encodes IDN domain names
-     *          (Punycode/RFC 3490).
-     *          Requires the idn or intl PHP module.
-     *          DEFAULT: false
-     *
-     * @return string  The correctly escaped/quoted address.
-     */
-    public function writeAddress($opts = array())
-    {
-        if ($opts === true) {
-            $opts = array(
-                'encode' => 'UTF-8',
-                'idn' => true
-            );
-        } elseif (!empty($opts['encode']) && ($opts['encode'] === true)) {
-            $opts['encode'] = 'UTF-8';
-        }
-
-        return $this->_writeAddress($opts);
-    }
-
-    /**
-     * Class-specific implementation of writeAddress().
-     *
-     * @see writeAddress()
-     *
-     * @param array $opts  See writeAddress().
-     *
-     * @return string  The correctly escaped/quoted address.
-     */
-    abstract protected function _writeAddress($opts);
-
-    /**
-     * Compare this object against other data.
-     *
-     * @param mixed $ob  Address data.
-     *
-     * @return boolean  True if the data reflects the same canonical address.
-     */
-    abstract public function match($ob);
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeMailRfc822php"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Mail-Rfc822.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Mail-Rfc822.php 2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Mail-Rfc822.php    2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,839 +0,0 @@
</span><del>-<?php
-/**
- * Copyright (c) 2001-2010, Richard Heyes
- * Copyright 2011-2013 Horde LLC (http://www.horde.org/)
- * 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.
- *
- * RFC822 parsing code adapted from message-address.c and rfc822-parser.c
- *   (Dovecot 2.1rc5)
- *   Original code released under LGPL-2.1
- *   Copyright (c) 2002-2011 Timo Sirainen <tss@iki.fi>
- *
- * @category  Horde
- * @copyright 2001-2010 Richard Heyes
- * @copyright 2002-2011 Timo Sirainen
- * @copyright 2011-2013 Horde LLC
- * @license   http://www.horde.org/licenses/bsd New BSD License
- * @package   Mail
- */
-
-/**
- * RFC 822/2822/3490/5322 Email parser/validator.
- *
- * @author    Richard Heyes <richard@phpguru.org>
- * @author    Chuck Hagenbuch <chuck@horde.org>
- * @author    Michael Slusarz <slusarz@horde.org>
- * @author    Timo Sirainen <tss@iki.fi>
- * @category  Horde
- * @copyright 2001-2010 Richard Heyes
- * @copyright 2002-2011 Timo Sirainen
- * @copyright 2011-2013 Horde LLC
- * @license   http://www.horde.org/licenses/bsd New BSD License
- * @package   Mail
- */
-class Horde_Mail_Rfc822
-{
-    /**
-     * Valid atext characters.
-     *
-     * @since 2.0.3
-     */
-    const ATEXT = '!#$%&\'*+-./0123456789=?ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~';
-
-    /**
-     * Excluded (in ASCII): 0-8, 10-31, 34, 40-41, 44, 58-60, 62, 64,
-     * 91-93, 127
-     *
-     * @since 2.0.3
-     */
-    const ENCODE_FILTER = "\0\1\2\3\4\5\6\7\10\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\"(),:;<>@[\\]\177";
-
-    /**
-     * The address string to parse.
-     *
-     * @var string
-     */
-    protected $_data;
-
-    /**
-     * Length of the address string.
-     *
-     * @var integer
-     */
-    protected $_datalen;
-
-    /**
-     * Comment cache.
-     *
-     * @var string
-     */
-    protected $_comments = array();
-
-    /**
-     * List object to return in parseAddressList().
-     *
-     * @var Horde_Mail_Rfc822_List
-     */
-    protected $_listob;
-
-    /**
-     * Configuration parameters.
-     *
-     * @var array
-     */
-    protected $_params = array();
-
-    /**
-     * Data pointer.
-     *
-     * @var integer
-     */
-    protected $_ptr;
-
-    /**
-     * Starts the whole process.
-     *
-     * @param mixed $address   The address(es) to validate. Either a string,
-     *                         a Horde_Mail_Rfc822_Object, or an array of
-     *                         strings and/or Horde_Mail_Rfc822_Objects.
-     * @param array $params    Optional parameters:
-     *   - default_domain: (string) Default domain/host.
-     *                     DEFAULT: None
-     *   - group: (boolean) Return a GroupList object instead of a List object?
-     *            DEFAULT: false
-     *   - limit: (integer) Stop processing after this many addresses.
-     *            DEFAULT: No limit (0)
-     *   - validate: (boolean) Strict validation of personal part data? If
-     *               true, throws an Exception on error. If false, attempts
-     *               to allow non-ASCII characters and non-quoted strings in
-     *               the personal data, and will silently abort if an
-     *               unparseable address is found.
-     *               DEFAULT: false
-     *
-     * @return Horde_Mail_Rfc822_List  A list object.
-     *
-     * @throws Horde_Mail_Exception
-     */
-    public function parseAddressList($address, array $params = array())
-    {
-        if ($address instanceof Horde_Mail_Rfc822_List) {
-            return $address;
-        }
-
-        if (empty($params['limit'])) {
-            $params['limit'] = null;
-        }
-
-        $this->_params = array_merge(array(
-            'default_domain' => null,
-            'validate' => false
-        ), $params);
-
-        $this->_listob = empty($this->_params['group'])
-            ? new Horde_Mail_Rfc822_List()
-            : new Horde_Mail_Rfc822_GroupList();
-
-        if (!is_array($address)) {
-            $address = array($address);
-        }
-
-        $tmp = array();
-        foreach ($address as $val) {
-            if ($val instanceof Horde_Mail_Rfc822_Object) {
-                $this->_listob->add($val);
-            } else {
-                $tmp[] = rtrim(trim($val), ',');
-            }
-        }
-
-        if (!empty($tmp)) {
-            $this->_data = implode(',', $tmp);
-            $this->_datalen = strlen($this->_data);
-            $this->_ptr = 0;
-
-            $this->_parseAddressList();
-        }
-
-        return $this->_listob;
-    }
-
-   /**
-     * Quotes and escapes the given string if necessary using rules contained
-     * in RFC 2822 [3.2.5].
-     *
-     * @param string $str   The string to be quoted and escaped.
-     * @param string $type  Either 'address', or 'personal'.
-     *
-     * @return string  The correctly quoted and escaped string.
-     */
-    public function encode($str, $type = 'address')
-    {
-        switch ($type) {
-        case 'personal':
-            // RFC 2822 [3.4]: Period not allowed in display name
-            $filter = '.';
-            break;
-
-        case 'address':
-        default:
-            // RFC 2822 [3.4.1]: (HTAB, SPACE) not allowed in address
-            $filter = "\11\40";
-            break;
-        }
-
-        // Strip double quotes if they are around the string already.
-        // If quoted, we know that the contents are already escaped, so
-        // unescape now.
-        $str = trim($str);
-        if ($str && ($str[0] == '"') && (substr($str, -1) == '"')) {
-            $str = stripslashes(substr($str, 1, -1));
-        }
-
-        return (strcspn($str, self::ENCODE_FILTER . $filter) != strlen($str))
-            ? '"' . addcslashes($str, '\\"') . '"'
-            : $str;
-    }
-
-    /**
-     * If an email address has no personal information, get rid of any angle
-     * brackets (<>) around it.
-     *
-     * @param string $address  The address to trim.
-     *
-     * @return string  The trimmed address.
-     */
-    public function trimAddress($address)
-    {
-        $address = trim($address);
-
-        return (($address[0] == '<') && (substr($address, -1) == '>'))
-            ? substr($address, 1, -1)
-            : $address;
-    }
-
-    /* RFC 822 parsing methods. */
-
-    /**
-     * address-list = (address *("," address)) / obs-addr-list
-     */
-    protected function _parseAddressList()
-    {
-        $limit = $this->_params['limit'];
-
-        while (($this->_curr() !== false) &&
-               (is_null($limit) || ($limit > 0))) {
-            try {
-                $this->_parseAddress();
-            } catch (Horde_Mail_Exception $e) {
-               if ($this->_params['validate']) {
-                   throw $e;
-               }
-               ++$this->_ptr;
-            }
-
-            switch ($this->_curr()) {
-            case ',':
-                $this->_rfc822SkipLwsp(true);
-                break;
-
-            case false:
-                // No-op
-                break;
-
-            default:
-               if ($this->_params['validate']) {
-                    throw new Horde_Mail_Exception('Error when parsing address list.');
-               }
-               break;
-            }
-            $limit--;
-        }
-    }
-
-    /**
-     * address = mailbox / group
-     */
-    protected function _parseAddress()
-    {
-        $start = $this->_ptr;
-        if (!$this->_parseGroup()) {
-            $this->_ptr = $start;
-            if ($mbox = $this->_parseMailbox()) {
-                $this->_listob->add($mbox);
-            }
-        }
-    }
-
-    /**
-     * group           = display-name ":" [mailbox-list / CFWS] ";" [CFWS]
-     * display-name    = phrase
-     *
-     * @return boolean  True if a group was parsed.
-     *
-     * @throws Horde_Mail_Exception
-     */
-    protected function _parseGroup()
-    {
-        $this->_rfc822ParsePhrase($groupname);
-
-        if ($this->_curr(true) != ':') {
-            return false;
-        }
-
-        $addresses = new Horde_Mail_Rfc822_GroupList();
-
-        $this->_rfc822SkipLwsp();
-
-        while (($chr = $this->_curr()) !== false) {
-            if ($chr == ';') {
-                ++$this->_ptr;
-
-                if (count($addresses)) {
-                    $this->_listob->add(new Horde_Mail_Rfc822_Group($groupname, $addresses));
-                }
-
-                return true;
-            }
-
-            /* mailbox-list = (mailbox *("," mailbox)) / obs-mbox-list */
-            $addresses->add($this->_parseMailbox());
-
-            switch ($this->_curr()) {
-            case ',':
-                $this->_rfc822SkipLwsp(true);
-                break;
-
-            case ';':
-                // No-op
-                break;
-
-            default:
-                break 2;
-            }
-        }
-
-        throw new Horde_Mail_Exception('Error when parsing group.');
-    }
-
-    /**
-     * mailbox = name-addr / addr-spec
-     *
-     * @return mixed  Mailbox object if mailbox was parsed, or false.
-     */
-    protected function _parseMailbox()
-    {
-        $this->_comments = array();
-        $start = $this->_ptr;
-
-        if (!($ob = $this->_parseNameAddr())) {
-            $this->_comments = array();
-            $this->_ptr = $start;
-            $ob = $this->_parseAddrSpec();
-        }
-
-        if ($ob) {
-            $ob->comment = $this->_comments;
-        }
-
-        return $ob;
-    }
-
-    /**
-     * name-addr    = [display-name] angle-addr
-     * display-name = phrase
-     *
-     * @return mixed  Mailbox object, or false.
-     */
-    protected function _parseNameAddr()
-    {
-        $this->_rfc822ParsePhrase($personal);
-
-        if ($ob = $this->_parseAngleAddr()) {
-            $ob->personal = $personal;
-            return $ob;
-        }
-
-        return false;
-    }
-
-    /**
-     * addr-spec = local-part "@" domain
-     *
-     * @return mixed  Mailbox object.
-     *
-     * @throws Horde_Mail_Exception
-     */
-    protected function _parseAddrSpec()
-    {
-        $ob = new Horde_Mail_Rfc822_Address();
-        $ob->mailbox = $this->_parseLocalPart();
-
-        if ($this->_curr() == '@') {
-            $this->_rfc822ParseDomain($host);
-            if (strlen($host)) {
-                $ob->host = $host;
-            }
-        }
-
-        if (is_null($ob->host) &&
-            !is_null($this->_params['default_domain'])) {
-            $ob->host = $this->_params['default_domain'];
-        }
-
-        return $ob;
-    }
-
-    /**
-     * local-part      = dot-atom / quoted-string / obs-local-part
-     * obs-local-part  = word *("." word)
-     *
-     * @return string  The local part.
-     *
-     * @throws Horde_Mail_Exception
-     */
-    protected function _parseLocalPart()
-    {
-        if (($curr = $this->_curr()) === false) {
-            throw new Horde_Mail_Exception('Error when parsing local part.');
-        }
-
-        if ($curr == '"') {
-            $this->_rfc822ParseQuotedString($str);
-        } else {
-            $this->_rfc822ParseDotAtom($str, ',;@');
-        }
-
-        return $str;
-    }
-
-    /**
-     * "<" [ "@" route ":" ] local-part "@" domain ">"
-     *
-     * @return mixed  Mailbox object, or false.
-     *
-     * @throws Horde_Mail_Exception
-     */
-    protected function _parseAngleAddr()
-    {
-        if ($this->_curr() != '<') {
-            return false;
-        }
-
-        $this->_rfc822SkipLwsp(true);
-
-        if ($this->_curr() == '@') {
-            // Route information is ignored.
-            $this->_parseDomainList();
-            if ($this->_curr() != ':') {
-                throw new Horde_Mail_Exception('Invalid route.');
-            }
-
-            $this->_rfc822SkipLwsp(true);
-        }
-
-        $ob = $this->_parseAddrSpec();
-
-        if ($this->_curr() != '>') {
-            throw new Horde_Mail_Exception('Error when parsing angle address.');
-        }
-
-        $this->_rfc822SkipLwsp(true);
-
-        return $ob;
-    }
-
-    /**
-     * obs-domain-list = "@" domain *(*(CFWS / "," ) [CFWS] "@" domain)
-     *
-     * @return array  Routes.
-     *
-     * @throws Horde_Mail_Exception
-     */
-    protected function _parseDomainList()
-    {
-        $route = array();
-
-        while ($this->_curr() !== false) {
-            $this->_rfc822ParseDomain($str);
-            $route[] = '@' . $str;
-
-            $this->_rfc822SkipLwsp();
-            if ($this->_curr() != ',') {
-                return $route;
-            }
-            ++$this->_ptr;
-        }
-
-        throw new Horde_Mail_Exception('Invalid domain list.');
-    }
-
-    /* RFC 822 parsing methods. */
-
-    /**
-     * phrase     = 1*word / obs-phrase
-     * word       = atom / quoted-string
-     * obs-phrase = word *(word / "." / CFWS)
-     *
-     * @param string &$phrase  The phrase data.
-     *
-     * @throws Horde_Mail_Exception
-     */
-    protected function _rfc822ParsePhrase(&$phrase)
-    {
-        $curr = $this->_curr();
-        if (($curr === false) || ($curr == '.')) {
-            throw new Horde_Mail_Exception('Error when parsing a group.');
-        }
-
-        while (($curr = $this->_curr()) !== false) {
-            if ($curr == '"') {
-                $this->_rfc822ParseQuotedString($phrase);
-            } else {
-                $this->_rfc822ParseAtomOrDot($phrase);
-            }
-
-            $chr = $this->_curr();
-            if (!$this->_rfc822IsAtext($chr) &&
-                ($chr != '"') &&
-                ($chr != '.')) {
-                break;
-            }
-
-            $phrase .= ' ';
-        }
-
-        $this->_rfc822SkipLwsp();
-    }
-
-    /**
-     * @param string &$phrase  The quoted string data.
-     *
-     * @throws Horde_Mail_Exception
-     */
-    protected function _rfc822ParseQuotedString(&$str)
-    {
-        if ($this->_curr(true) != '"') {
-            throw new Horde_Mail_Exception('Error when parsing a quoted string.');
-        }
-
-        while (($chr = $this->_curr(true)) !== false) {
-            switch ($chr) {
-            case '"':
-                $this->_rfc822SkipLwsp();
-                return;
-
-            case "\n":
-                /* Folding whitespace, remove the (CR)LF. */
-                if ($str[strlen($str) - 1] == "\r") {
-                    $str = substr($str, 0, -1);
-                }
-                continue;
-
-            case '\\':
-                if (($chr = $this->_curr(true)) === false) {
-                    break 2;
-                }
-                break;
-            }
-
-            $str .= $chr;
-        }
-
-        /* Missing trailing '"', or partial quoted character. */
-        throw new Horde_Mail_Exception('Error when parsing a quoted string.');
-    }
-
-    /**
-     * dot-atom        = [CFWS] dot-atom-text [CFWS]
-     * dot-atom-text   = 1*atext *("." 1*atext)
-     *
-     * atext           = ; Any character except controls, SP, and specials.
-     *
-     * For RFC-822 compatibility allow LWSP around '.'
-     *
-     * @param string &$str      The atom/dot data.
-     * @param string $validate  Use these characters as delimiter.
-     *
-     * @throws Horde_Mail_Exception
-     */
-    protected function _rfc822ParseDotAtom(&$str, $validate = null)
-    {
-        $curr = $this->_curr();
-        if (($curr === false) || !$this->_rfc822IsAtext($curr, $validate)) {
-            throw new Horde_Mail_Exception('Error when parsing dot-atom.');
-        }
-
-        while (($chr = $this->_curr()) !== false) {
-            if ($this->_rfc822IsAtext($chr, $validate)) {
-                $str .= $chr;
-                ++$this->_ptr;
-            } else {
-                $this->_rfc822SkipLwsp();
-
-                if ($this->_curr() != '.') {
-                    return;
-                }
-                $str .= '.';
-
-                $this->_rfc822SkipLwsp(true);
-            }
-        }
-    }
-
-    /**
-     * atom  = [CFWS] 1*atext [CFWS]
-     * atext = ; Any character except controls, SP, and specials.
-     *
-     * This method doesn't just silently skip over WS.
-     *
-     * @param string &$str  The atom/dot data.
-     *
-     * @throws Horde_Mail_Exception
-     */
-    protected function _rfc822ParseAtomOrDot(&$str)
-    {
-        while (($chr = $this->_curr()) !== false) {
-            if (($chr != '.') && !$this->_rfc822IsAtext($chr, ',<:')) {
-                $this->_rfc822SkipLwsp();
-                if (!$this->_params['validate']) {
-                    $str = trim($str);
-                }
-                return;
-            }
-
-            $str .= $chr;
-            ++$this->_ptr;
-        }
-    }
-
-    /**
-     * domain          = dot-atom / domain-literal / obs-domain
-     * domain-literal  = [CFWS] "[" *([FWS] dcontent) [FWS] "]" [CFWS]
-     * obs-domain      = atom *("." atom)
-     *
-     * @param string &$str  The domain string.
-     *
-     * @throws Horde_Mail_Exception
-     */
-    protected function _rfc822ParseDomain(&$str)
-    {
-        if ($this->_curr(true) != '@') {
-            throw new Horde_Mail_Exception('Error when parsing domain.');
-        }
-
-        $this->_rfc822SkipLwsp();
-
-        if ($this->_curr() == '[') {
-            $this->_rfc822ParseDomainLiteral($str);
-        } else {
-            $this->_rfc822ParseDotAtom($str, ';,> ');
-        }
-    }
-
-    /**
-     * domain-literal  = [CFWS] "[" *([FWS] dcontent) [FWS] "]" [CFWS]
-     * dcontent        = dtext / quoted-pair
-     * dtext           = NO-WS-CTL /     ; Non white space controls
-     *           %d33-90 /       ; The rest of the US-ASCII
-     *           %d94-126        ;  characters not including "[",
-     *                   ;  "]", or "\"
-     *
-     * @param string &$str  The domain string.
-     *
-     * @throws Horde_Mail_Exception
-     */
-    protected function _rfc822ParseDomainLiteral(&$str)
-    {
-        if ($this->_curr(true) != '[') {
-            throw new Horde_Mail_Exception('Error parsing domain literal.');
-        }
-
-        while (($chr = $this->_curr(true)) !== false) {
-            switch ($chr) {
-            case '\\':
-                if (($chr = $this->_curr(true)) === false) {
-                    break 2;
-                }
-                break;
-
-            case ']':
-                $this->_rfc822SkipLwsp();
-                return;
-            }
-
-            $str .= $chr;
-        }
-
-        throw new Horde_Mail_Exception('Error parsing domain literal.');
-    }
-
-    /**
-     * @param boolean $advance  Advance cursor?
-     *
-     * @throws Horde_Mail_Exception
-     */
-    protected function _rfc822SkipLwsp($advance = false)
-    {
-        if ($advance) {
-            ++$this->_ptr;
-        }
-
-        while (($chr = $this->_curr()) !== false) {
-            switch ($chr) {
-            case ' ':
-            case "\n":
-            case "\r":
-            case "\t":
-                ++$this->_ptr;
-                continue;
-
-            case '(':
-                $this->_rfc822SkipComment();
-                break;
-
-            default:
-                return;
-            }
-        }
-    }
-
-    /**
-     * @throws Horde_Mail_Exception
-     */
-    protected function _rfc822SkipComment()
-    {
-        if ($this->_curr(true) != '(') {
-            throw new Horde_Mail_Exception('Error when parsing a comment.');
-        }
-
-        $comment = '';
-        $level = 1;
-
-        while (($chr = $this->_curr(true)) !== false) {
-            switch ($chr) {
-            case '(':
-                ++$level;
-                continue;
-
-            case ')':
-                if (--$level == 0) {
-                    $this->_comments[] = $comment;
-                    return;
-                }
-                break;
-
-            case '\\':
-                if (($chr = $this->_curr(true)) === false) {
-                    break 2;
-                }
-                break;
-            }
-
-            $comment .= $chr;
-        }
-
-        throw new Horde_Mail_Exception('Error when parsing a comment.');
-    }
-
-    /**
-     * Check if data is an atom.
-     *
-     * @param string $chr       The character to check.
-     * @param string $validate  If in non-validate mode, use these characters
-     *                          as the non-atom delimiters.
-     *
-     * @return boolean  True if an atom.
-     */
-    protected function _rfc822IsAtext($chr, $validate = null)
-    {
-        if (is_null($chr)) {
-            return false;
-        }
-
-        return ($this->_params['validate'] || is_null($validate))
-            ? !strcspn($chr, self::ATEXT)
-            : strcspn($chr, $validate);
-    }
-
-    /* Helper methods. */
-
-    /**
-     * Return current character.
-     *
-     * @param boolean $advance  If true, advance the cursor.
-     *
-     * @return string  The current character (false if EOF reached).
-     */
-    protected function _curr($advance = false)
-    {
-        return ($this->_ptr >= $this->_datalen)
-            ? false
-            : $this->_data[$advance ? $this->_ptr++ : $this->_ptr];
-    }
-
-    /* Other public methods. */
-
-    /**
-     * Returns an approximate count of how many addresses are in the string.
-     * This is APPROXIMATE as it only splits based on a comma which has no
-     * preceding backslash.
-     *
-     * @param string $data  Addresses to count.
-     *
-     * @return integer  Approximate count.
-     */
-    public function approximateCount($data)
-    {
-        return count(preg_split('/(?<!\\\\),/', $data));
-    }
-
-    /**
-     * Validates whether an email is of the common internet form:
-     * <user>@<domain>. This can be sufficient for most people.
-     *
-     * Optional stricter mode can be utilized which restricts mailbox
-     * characters allowed to: alphanumeric, full stop, hyphen, and underscore.
-     *
-     * @param string $data     Address to check.
-     * @param boolean $strict  Strict check?
-     *
-     * @return mixed  False if it fails, an indexed array username/domain if
-     *                it matches.
-     */
-    public function isValidInetAddress($data, $strict = false)
-    {
-        $regex = $strict
-            ? '/^([.0-9a-z_+-]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,})$/i'
-            : '/^([*+!.&#$|\'\\%\/0-9a-z^_`{}=?~:-]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,})$/i';
-
-        return preg_match($regex, trim($data), $matches)
-            ? array($matches[1], $matches[2])
-            : false;
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeMimeHeadersphpfromrev22362013codebykatpostbyemailtrunkincludeHordeMimeHeadersphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Mime/Headers.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Mime-Headers.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Mime/Headers.php                                (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Mime/Headers.php   2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,740 @@
</span><ins>+<?php
+/**
+ * This class contains functions related to handling the headers of MIME data.
+ *
+ * Copyright 2002-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @author   Michael Slusarz <slusarz@horde.org>
+ * @category Horde
+ * @license  http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package  Mime
+ */
+class Horde_Mime_Headers implements Serializable
+{
+    /* Serialized version. */
+    const VERSION = 2;
+
+    /* Constants for getValue(). */
+    const VALUE_STRING = 1;
+    const VALUE_BASE = 2;
+    const VALUE_PARAMS = 3;
+
+    /**
+     * The default charset to use when parsing text parts with no charset
+     * information.
+     *
+     * @var string
+     */
+    static public $defaultCharset = 'us-ascii';
+
+    /**
+     * The internal headers array.
+     *
+     * Keys are the lowercase header name.
+     * Values are:
+     *   - h: The case-sensitive header name.
+     *   - p: Parameters for this header.
+     *   - v: The value of the header. Values are stored in UTF-8.
+     *
+     * @var array
+     */
+    protected $_headers = array();
+
+    /**
+     * The sequence to use as EOL for the headers.
+     * The default is currently to output the EOL sequence internally as
+     * just "\n" instead of the canonical "\r\n" required in RFC 822 & 2045.
+     * To be RFC complaint, the full <CR><LF> EOL combination should be used
+     * when sending a message.
+     *
+     * @var string
+     */
+    protected $_eol = "\n";
+
+    /**
+     * The User-Agent string to use.
+     *
+     * @var string
+     */
+    protected $_agent = null;
+
+    /**
+     * List of single header fields.
+     *
+     * @var array
+     */
+    protected $_singleFields = array(
+        // Mail: RFC 5322
+        'to', 'from', 'cc', 'bcc', 'date', 'sender', 'reply-to',
+        'message-id', 'in-reply-to', 'references', 'subject', 'x-priority',
+        // MIME: RFC 1864
+        'content-md5',
+        // MIME: RFC 2045
+        'mime-version', 'content-type', 'content-transfer-encoding',
+        'content-id', 'content-description',
+        // MIME: RFC 2110
+        'content-base',
+        // MIME: RFC 2183
+        'content-disposition',
+        // MIME: RFC 2424
+        'content-duration',
+        // MIME: RFC 2557
+        'content-location',
+        // MIME: RFC 2912 [3]
+        'content-features',
+        // MIME: RFC 3282
+        'content-language',
+        // MIME: RFC 3297
+        'content-alternative'
+    );
+
+    /**
+     * Returns the internal header array in array format.
+     *
+     * @param array $opts  Optional parameters:
+     *   - canonical: (boolean) Use canonical (RFC 822/2045) line endings?
+     *                DEFAULT: Uses $this->_eol
+     *   - charset: (string) Encodes the headers using this charset. If empty,
+     *              encodes using internal charset (UTF-8).
+     *              DEFAULT: No encoding.
+     *   - defserver: (string) The default domain to append to mailboxes.
+     *                DEFAULT: No default name.
+     *   - nowrap: (integer) Don't wrap the headers.
+     *             DEFAULT: Headers are wrapped.
+     *
+     * @return array  The headers in array format.
+     */
+    public function toArray(array $opts = array())
+    {
+        $address_keys = $this->addressFields();
+        $charset = array_key_exists('charset', $opts)
+            ? (empty($opts['charset']) ? 'UTF-8' : $opts['charset'])
+            : null;
+        $eol = empty($opts['canonical'])
+            ? $this->_eol
+            : "\r\n";
+        $mime = $this->mimeParamFields();
+        $ret = array();
+
+        foreach ($this->_headers as $header => $ob) {
+            $val = is_array($ob['v']) ? $ob['v'] : array($ob['v']);
+
+            foreach (array_keys($val) as $key) {
+                if (in_array($header, $address_keys) ) {
+                    /* Address encoded headers. */
+                    $rfc822 = new Horde_Mail_Rfc822();
+                    $text = $rfc822->parseAddressList($val[$key], array(
+                        'default_domain' => empty($opts['defserver']) ? null : $opts['defserver']
+                    ))->writeAddress(array(
+                        'encode' => $charset,
+                        'idn' => true
+                    ));
+                } elseif (in_array($header, $mime) && !empty($ob['p'])) {
+                    /* MIME encoded headers (RFC 2231). */
+                    $text = $val[$key];
+                    foreach ($ob['p'] as $name => $param) {
+                        foreach (Horde_Mime::encodeParam($name, $param, array('charset' => $charset, 'escape' => true)) as $name2 => $param2) {
+                            $text .= '; ' . $name2 . '=' . $param2;
+                        }
+                    }
+                } else {
+                    $text = is_null($charset)
+                        ? $val[$key]
+                        : Horde_Mime::encode($val[$key], $charset);
+                }
+
+                if (empty($opts['nowrap'])) {
+                    /* Remove any existing linebreaks and wrap the line. */
+                    $header_text = $ob['h'] . ': ';
+                    $text = ltrim(substr(wordwrap($header_text . strtr(trim($text), array("\r" => '', "\n" => '')), 76, $eol . ' '), strlen($header_text)));
+                }
+
+                $val[$key] = $text;
+            }
+
+            $ret[$ob['h']] = (count($val) == 1) ? reset($val) : $val;
+        }
+
+        return $ret;
+    }
+
+    /**
+     * Returns the internal header array in string format.
+     *
+     * @param array $opts  Optional parameters:
+     *   - canonical: (boolean) Use canonical (RFC 822/2045) line endings?
+     *                DEFAULT: Uses $this->_eol
+     *   - charset: (string) Encodes the headers using this charset.
+     *              DEFAULT: No encoding.
+     *   - defserver: (string) The default domain to append to mailboxes.
+     *                DEFAULT: No default name.
+     *   - nowrap: (integer) Don't wrap the headers.
+     *             DEFAULT: Headers are wrapped.
+     *
+     * @return string  The headers in string format.
+     */
+    public function toString(array $opts = array())
+    {
+        $eol = empty($opts['canonical'])
+            ? $this->_eol
+            : "\r\n";
+        $text = '';
+
+        foreach ($this->toArray($opts) as $key => $val) {
+            if (!is_array($val)) {
+                $val = array($val);
+            }
+            foreach ($val as $entry) {
+                $text .= $key . ': ' . $entry . $eol;
+            }
+        }
+
+        return $text . $eol;
+    }
+
+    /**
+     * Generate the 'Received' header for the Web browser->Horde hop
+     * (attempts to conform to guidelines in RFC 5321 [4.4]).
+     *
+     * @param array $opts  Additional opts:
+     *   - dns: (Net_DNS2_Resolver) Use the DNS resolver object to lookup
+     *          hostnames.
+     *          DEFAULT: Use gethostbyaddr() function.
+     *   - server: (string) Use this server name.
+     *             DEFAULT: Auto-detect using current PHP values.
+     */
+    public function addReceivedHeader(array $opts = array())
+    {
+        $old_error = error_reporting(0);
+        if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
+            /* This indicates the user is connecting through a proxy. */
+            $remote_path = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
+            $remote_addr = $remote_path[0];
+            if (!empty($opts['dns'])) {
+                $remote = $remote_addr;
+                try {
+                    if ($response = $opts['dns']->query($remote_addr, 'PTR')) {
+                        foreach ($response->answer as $val) {
+                            if (isset($val->ptrdname)) {
+                                $remote = $val->ptrdname;
+                                break;
+                            }
+                        }
+                    }
+                } catch (Net_DNS2_Exception $e) {}
+            } else {
+                $remote = gethostbyaddr($remote_addr);
+            }
+        } else {
+            $remote_addr = $_SERVER['REMOTE_ADDR'];
+            if (empty($_SERVER['REMOTE_HOST'])) {
+                if (!empty($opts['dns'])) {
+                    $remote = $remote_addr;
+                    try {
+                        if ($response = $opts['dns']->query($remote_addr, 'PTR')) {
+                            foreach ($response->answer as $val) {
+                                if (isset($val->ptrdname)) {
+                                    $remote = $val->ptrdname;
+                                    break;
+                                }
+                            }
+                        }
+                    } catch (Net_DNS2_Exception $e) {}
+                } else {
+                    $remote = gethostbyaddr($remote_addr);
+                }
+            } else {
+                $remote = $_SERVER['REMOTE_HOST'];
+            }
+        }
+        error_reporting($old_error);
+
+        if (!empty($_SERVER['REMOTE_IDENT'])) {
+            $remote_ident = $_SERVER['REMOTE_IDENT'] . '@' . $remote . ' ';
+        } elseif ($remote != $_SERVER['REMOTE_ADDR']) {
+            $remote_ident = $remote . ' ';
+        } else {
+            $remote_ident = '';
+        }
+
+        if (!empty($opts['server'])) {
+            $server_name = $opts['server'];
+        } elseif (!empty($_SERVER['SERVER_NAME'])) {
+            $server_name = $_SERVER['SERVER_NAME'];
+        } elseif (!empty($_SERVER['HTTP_HOST'])) {
+            $server_name = $_SERVER['HTTP_HOST'];
+        } else {
+            $server_name = 'unknown';
+        }
+
+        $received = 'from ' . $remote . ' (' . $remote_ident .
+            '[' . $remote_addr . ']) ' .
+            'by ' . $server_name . ' (Horde Framework) with HTTP; ' .
+            date('r');
+
+        $this->addHeader('Received', $received);
+    }
+
+    /**
+     * Generate the 'Message-ID' header.
+     */
+    public function addMessageIdHeader()
+    {
+        $this->addHeader('Message-ID', Horde_Mime::generateMessageId());
+    }
+
+    /**
+     * Generate the user agent description header.
+     */
+    public function addUserAgentHeader()
+    {
+        $this->addHeader('User-Agent', $this->getUserAgent());
+    }
+
+    /**
+     * Returns the user agent description header.
+     *
+     * @return string  The user agent header.
+     */
+    public function getUserAgent()
+    {
+        if (is_null($this->_agent)) {
+            $this->_agent = 'Horde Application Framework 4';
+        }
+        return $this->_agent;
+    }
+
+    /**
+     * Explicitly sets the User-Agent string.
+     *
+     * @param string $agent  The User-Agent string to use.
+     */
+    public function setUserAgent($agent)
+    {
+        $this->_agent = $agent;
+    }
+
+    /**
+     * Add a header to the header array.
+     *
+     * @param string $header  The header name.
+     * @param string $value   The header value (UTF-8).
+     * @param array $opts     Additional options:
+     *   - params: (array) MIME parameters for Content-Type or
+     *             Content-Disposition.
+     *             DEFAULT: None
+     *   - sanity_check: (boolean) Do sanity-checking on header value?
+     *                   DEFAULT: false
+     */
+    public function addHeader($header, $value, array $opts = array())
+    {
+        $header = trim($header);
+        $lcHeader = Horde_String::lower($header);
+
+        if (!isset($this->_headers[$lcHeader])) {
+            $this->_headers[$lcHeader] = array(
+                'h' => $header
+            );
+        }
+        $ptr = &$this->_headers[$lcHeader];
+
+        if (!empty($opts['sanity_check'])) {
+            $value = $this->_sanityCheck($value);
+        }
+
+        // Fields defined in RFC 2822 that contain address information
+        if (in_array($lcHeader, $this->addressFields())) {
+            $rfc822 = new Horde_Mail_Rfc822();
+            $addr_list = $rfc822->parseAddressList($value);
+
+            switch ($lcHeader) {
+            case 'bcc':
+            case 'cc':
+            case 'from':
+            case 'to':
+                /* Catch malformed undisclosed-recipients entries. */
+                if ((count($addr_list) == 1) &&
+                    preg_match("/^\s*undisclosed-recipients:?\s*$/i", $addr_list[0]->bare_address)) {
+                    $addr_list = new Horde_Mail_Rfc822_List('undisclosed-recipients:;');
+                }
+                break;
+            }
+            $value = strval($addr_list);
+        } else {
+            $value = Horde_Mime::decode($value);
+        }
+
+        if (isset($ptr['v'])) {
+            if (!is_array($ptr['v'])) {
+                $ptr['v'] = array($ptr['v']);
+            }
+            $ptr['v'][] = $value;
+        } else {
+            $ptr['v'] = $value;
+        }
+
+        if (!empty($opts['params'])) {
+            $ptr['p'] = $opts['params'];
+        }
+    }
+
+    /**
+     * Remove a header from the header array.
+     *
+     * @param string $header  The header name.
+     */
+    public function removeHeader($header)
+    {
+        unset($this->_headers[Horde_String::lower(trim($header))]);
+    }
+
+    /**
+     * Replace a value of a header.
+     *
+     * @param string $header  The header name.
+     * @param string $value   The header value.
+     * @param array $opts     Additional options:
+     *   - params: (array) MIME parameters for Content-Type or
+     *             Content-Disposition.
+     *             DEFAULT: None
+     *   - sanity_check: (boolean) Do sanity-checking on header value?
+     *                   DEFAULT: false
+     */
+    public function replaceHeader($header, $value, array $opts = array())
+    {
+        $this->removeHeader($header);
+        $this->addHeader($header, $value, $opts);
+    }
+
+    /**
+     * Attempts to return the header in the correct case.
+     *
+     * @param string $header  The header to search for.
+     *
+     * @return string  The value for the given header.
+     *                 If the header is not found, returns null.
+     */
+    public function getString($header)
+    {
+        $lcHeader = Horde_String::lower($header);
+        return (isset($this->_headers[$lcHeader]))
+            ? $this->_headers[$lcHeader]['h']
+            : null;
+    }
+
+    /**
+     * Attempt to return the value for a given header.
+     * The following header fields can only have 1 entry, so if duplicate
+     * entries exist, the first value will be used:
+     *   * To, From, Cc, Bcc, Date, Sender, Reply-to, Message-ID, In-Reply-To,
+     *     References, Subject (RFC 2822 [3.6])
+     *   * All List Headers (RFC 2369 [3])
+     * The values are not MIME encoded.
+     *
+     * @param string $header  The header to search for.
+     * @param integer $type   The type of return:
+     *   - VALUE_STRING: Returns a string representation of the entire header.
+     *   - VALUE_BASE: Returns a string representation of the base value of
+     *                 the header. If this is not a header that allows
+     *                 parameters, this will be equivalent to VALUE_STRING.
+     *   - VALUE_PARAMS: Returns the list of parameters for this header. If
+     *                   this is not a header that allows parameters, this
+     *                   will be an empty array.
+     *
+     * @return mixed  The value for the given header.
+     *                If the header is not found, returns null.
+     */
+    public function getValue($header, $type = self::VALUE_STRING)
+    {
+        $header = Horde_String::lower($header);
+
+        if (!isset($this->_headers[$header])) {
+            return null;
+        }
+
+        $ptr = &$this->_headers[$header];
+        if (is_array($ptr['v']) &&
+            in_array($header, $this->singleFields(true))) {
+            if (in_array($header, $this->addressFields())) {
+                $base = str_replace(';,', ';', implode(', ', $ptr['v']));
+            } else {
+                $base = $ptr['v'][0];
+            }
+        } else {
+            $base = $ptr['v'];
+        }
+        $params = isset($ptr['p']) ? $ptr['p'] : array();
+
+        switch ($type) {
+        case self::VALUE_BASE:
+            return $base;
+
+        case self::VALUE_PARAMS:
+            return $params;
+
+        case self::VALUE_STRING:
+            foreach ($params as $key => $val) {
+                $base .= '; ' . $key . '=' . $val;
+            }
+            return $base;
+        }
+    }
+
+    /**
+     * Returns the list of RFC defined header fields that contain address
+     * info.
+     *
+     * @return array  The list of headers, in lowercase.
+     */
+    static public function addressFields()
+    {
+        return array(
+            'from', 'to', 'cc', 'bcc', 'reply-to', 'resent-to', 'resent-cc',
+            'resent-bcc', 'resent-from', 'sender'
+        );
+    }
+
+    /**
+     * Returns the list of RFC defined header fields that can only contain
+     * a single value.
+     *
+     * @param boolean $list  Return list-related headers also?
+     *
+     * @return array  The list of headers, in lowercase.
+     */
+    public function singleFields($list = true)
+    {
+        return $list
+            ? array_merge($this->_singleFields, array_keys($this->listHeaders()))
+            : $this->_singleFields;
+    }
+
+    /**
+     * Returns the list of RFC defined MIME header fields that may contain
+     * parameter info.
+     *
+     * @return array  The list of headers, in lowercase.
+     */
+    static public function mimeParamFields()
+    {
+        return array('content-type', 'content-disposition');
+    }
+
+    /**
+     * Returns the list of valid mailing list headers.
+     *
+     * @return array  The list of valid mailing list headers.
+     */
+    static public function listHeaders()
+    {
+        return array(
+            /* RFC 2369 */
+            'list-help'         =>  Horde_Mime_Translation::t("List-Help"),
+            'list-unsubscribe'  =>  Horde_Mime_Translation::t("List-Unsubscribe"),
+            'list-subscribe'    =>  Horde_Mime_Translation::t("List-Subscribe"),
+            'list-owner'        =>  Horde_Mime_Translation::t("List-Owner"),
+            'list-post'         =>  Horde_Mime_Translation::t("List-Post"),
+            'list-archive'      =>  Horde_Mime_Translation::t("List-Archive"),
+            /* RFC 2919 */
+            'list-id'           =>  Horde_Mime_Translation::t("List-Id")
+        );
+    }
+
+    /**
+     * Do any mailing list headers exist?
+     *
+     * @return boolean  True if any mailing list headers exist.
+     */
+    public function listHeadersExist()
+    {
+        return (bool)count(array_intersect(array_keys($this->listHeaders()), array_keys($this->_headers)));
+    }
+
+    /**
+     * Sets a new string to use for EOLs.
+     *
+     * @param string $eol  The string to use for EOLs.
+     */
+    public function setEOL($eol)
+    {
+        $this->_eol = $eol;
+    }
+
+    /**
+     * Get the string to use for EOLs.
+     *
+     * @return string  The string to use for EOLs.
+     */
+    public function getEOL()
+    {
+        return $this->_eol;
+    }
+
+    /**
+     * Returns an address object for a header.
+     *
+     * @param string $field  The header to return as an object.
+     *
+     * @return Horde_Mail_Rfc822_List  The object for the requested field.
+     *                                 Returns null if field doesn't exist.
+     */
+    public function getOb($field)
+    {
+        if (($value = $this->getValue($field)) === null) {
+            return null;
+        }
+
+        $rfc822 = new Horde_Mail_Rfc822();
+        return $rfc822->parseAddressList($value);
+    }
+
+    /**
+     * Perform sanity checking on a raw header (e.g. handle 8-bit characters).
+     *
+     * @param string $data  The header data.
+     *
+     * @return string  The cleaned header data.
+     */
+    protected function _sanityCheck($data)
+    {
+        $charset_test = array(
+            'windows-1252',
+            self::$defaultCharset
+        );
+
+        if (!Horde_String::validUtf8($data)) {
+            /* Appears to be a PHP error with the internal String structure
+             * which prevents accurate manipulation of the string. Copying
+             * the data to a new variable fixes things. */
+            $data = substr($data, 0);
+
+            /* Assumption: broken charset in headers is generally either
+             * UTF-8 or ISO-8859-1/Windows-1252. Test these charsets
+             * first before using default charset. This may be a
+             * Western-centric approach, but it's better than nothing. */
+            foreach ($charset_test as $charset) {
+                $tmp = Horde_String::convertCharset($data, $charset, 'UTF-8');
+                if (Horde_String::validUtf8($tmp)) {
+                    return $tmp;
+                }
+            }
+        }
+
+        return $data;
+    }
+
+    /* Static methods. */
+
+    /**
+     * Builds a Horde_Mime_Headers object from header text.
+     * This function can be called statically:
+     *   $headers = Horde_Mime_Headers::parseHeaders().
+     *
+     * @param string $text  A text string containing the headers.
+     *
+     * @return Horde_Mime_Headers  A new Horde_Mime_Headers object.
+     */
+    static public function parseHeaders($text)
+    {
+        $currheader = $currtext = null;
+        $mime = self::mimeParamFields();
+        $to_process = array();
+
+        foreach (explode("\n", $text) as $val) {
+            $val = rtrim($val, "\r\n");
+            if (empty($val)) {
+                break;
+            }
+
+            if (($val[0] == ' ') || ($val[0] == "\t")) {
+                $currtext .= ' ' . ltrim($val);
+            } else {
+                if (!is_null($currheader)) {
+                    $to_process[] = array($currheader, rtrim($currtext));
+                }
+
+                $pos = strpos($val, ':');
+                $currheader = substr($val, 0, $pos);
+                $currtext = ltrim(substr($val, $pos + 1));
+            }
+        }
+
+        if (!is_null($currheader)) {
+            $to_process[] = array($currheader, $currtext);
+        }
+
+        $headers = new Horde_Mime_Headers();
+
+        reset($to_process);
+        while (list(,$val) = each($to_process)) {
+            /* Ignore empty headers. */
+            if (!strlen($val[1])) {
+                continue;
+            }
+
+            if (in_array(Horde_String::lower($val[0]), $mime)) {
+                $res = Horde_Mime::decodeParam($val[0], $val[1]);
+                $headers->addHeader($val[0], $res['val'], array(
+                    'params' => $res['params'],
+                    'sanity_check' => true
+                ));
+            } else {
+                $headers->addHeader($val[0], $val[1], array(
+                    'sanity_check' => true
+                ));
+            }
+        }
+
+        return $headers;
+    }
+
+    /* Serializable methods. */
+
+    /**
+     * Serialization.
+     *
+     * @return string  Serialized data.
+     */
+    public function serialize()
+    {
+        $data = array(
+            // Serialized data ID.
+            self::VERSION,
+            $this->_headers,
+            $this->_eol
+        );
+
+        if (!is_null($this->_agent)) {
+            $data[] = $this->_agent;
+        }
+
+        return serialize($data);
+    }
+
+    /**
+     * Unserialization.
+     *
+     * @param string $data  Serialized data.
+     *
+     * @throws Exception
+     */
+    public function unserialize($data)
+    {
+        $data = @unserialize($data);
+        if (!is_array($data) ||
+            !isset($data[0]) ||
+            ($data[0] != self::VERSION)) {
+            throw new Horde_Mime_Exception('Cache version change');
+        }
+
+        $this->_headers = $data[1];
+        $this->_eol = $data[2];
+        if (isset($data[3])) {
+            $this->_agent = $data[3];
+        }
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeMimePartphpfromrev22362013codebykatpostbyemailtrunkincludeHordeMimePartphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Mime/Part.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Mime-Part.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Mime/Part.php                           (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Mime/Part.php      2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,2358 @@
</span><ins>+<?php
+/**
+ * This class provides an object-oriented representation of a MIME part
+ * (defined by RFC 2045).
+ *
+ * Copyright 1999-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @author   Chuck Hagenbuch <chuck@horde.org>
+ * @author   Michael Slusarz <slusarz@horde.org>
+ * @category Horde
+ * @license  http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package  Mime
+ */
+class Horde_Mime_Part implements ArrayAccess, Countable, Serializable
+{
+    /* Serialized version. */
+    const VERSION = 1;
+
+    /* The character(s) used internally for EOLs. */
+    const EOL = "\n";
+
+    /* The character string designated by RFC 2045 to designate EOLs in MIME
+     * messages. */
+    const RFC_EOL = "\r\n";
+
+    /* The default encoding. */
+    const DEFAULT_ENCODING = 'binary';
+
+    /* Constants indicating the valid transfer encoding allowed. */
+    const ENCODE_7BIT = 1;
+    const ENCODE_8BIT = 2;
+    const ENCODE_BINARY = 4;
+
+    /* Unknown types. */
+    const UNKNOWN = 'x-unknown';
+
+    /* MIME nesting limit. */
+    const NESTING_LIMIT = 100;
+
+    /**
+     * The default charset to use when parsing text parts with no charset
+     * information.
+     *
+     * @var string
+     */
+    static public $defaultCharset = 'us-ascii';
+
+    /**
+     * Valid encoding types.
+     *
+     * @var array
+     */
+    static public $encodingTypes = array(
+        '7bit', '8bit', 'base64', 'binary', 'quoted-printable',
+        // Non-RFC types, but old mailers may still use
+        'uuencode', 'x-uuencode', 'x-uue'
+    );
+
+    /**
+     * The memory limit for use with the PHP temp stream.
+     *
+     * @var integer
+     */
+    static public $memoryLimit = 2097152;
+
+    /**
+     * Valid MIME types.
+     *
+     * @var array
+     */
+    static public $mimeTypes = array(
+        'text', 'multipart', 'message', 'application', 'audio', 'image',
+        'video', 'model'
+    );
+
+    /**
+     * The type (ex.: text) of this part.
+     * Per RFC 2045, the default is 'application'.
+     *
+     * @var string
+     */
+    protected $_type = 'application';
+
+    /**
+     * The subtype (ex.: plain) of this part.
+     * Per RFC 2045, the default is 'octet-stream'.
+     *
+     * @var string
+     */
+    protected $_subtype = 'octet-stream';
+
+    /**
+     * The body of the part. Always stored in binary format.
+     *
+     * @var resource
+     */
+    protected $_contents;
+
+    /**
+     * The desired transfer encoding of this part.
+     *
+     * @var string
+     */
+    protected $_transferEncoding = self::DEFAULT_ENCODING;
+
+    /**
+     * The language(s) of this part.
+     *
+     * @var array
+     */
+    protected $_language = array();
+
+    /**
+     * The description of this part.
+     *
+     * @var string
+     */
+    protected $_description = '';
+
+    /**
+     * The disposition of this part (inline or attachment).
+     *
+     * @var string
+     */
+    protected $_disposition = '';
+
+    /**
+     * The disposition parameters of this part.
+     *
+     * @var array
+     */
+    protected $_dispParams = array();
+
+    /**
+     * The content type parameters of this part.
+     *
+     * @var Horde_Support_CaseInsensitiveArray
+     */
+    protected $_contentTypeParams;
+
+    /**
+     * The subparts of this part.
+     *
+     * @var array
+     */
+    protected $_parts = array();
+
+    /**
+     * The MIME ID of this part.
+     *
+     * @var string
+     */
+    protected $_mimeid = null;
+
+    /**
+     * The sequence to use as EOL for this part.
+     * The default is currently to output the EOL sequence internally as
+     * just "\n" instead of the canonical "\r\n" required in RFC 822 & 2045.
+     * To be RFC complaint, the full <CR><LF> EOL combination should be used
+     * when sending a message.
+     * It is not crucial here since the PHP/PEAR mailing functions will handle
+     * the EOL details.
+     *
+     * @var string
+     */
+    protected $_eol = self::EOL;
+
+    /**
+     * Internal temp array.
+     *
+     * @var array
+     */
+    protected $_temp = array();
+
+    /**
+     * Metadata.
+     *
+     * @var array
+     */
+    protected $_metadata = array();
+
+    /**
+     * Unique Horde_Mime_Part boundary string.
+     *
+     * @var string
+     */
+    protected $_boundary = null;
+
+    /**
+     * Default value for this Part's size.
+     *
+     * @var integer
+     */
+    protected $_bytes;
+
+    /**
+     * The content-ID for this part.
+     *
+     * @var string
+     */
+    protected $_contentid = null;
+
+    /**
+     * The duration of this part's media data (RFC 3803).
+     *
+     * @var integer
+     */
+    protected $_duration;
+
+    /**
+     * Do we need to reindex the current part?
+     *
+     * @var boolean
+     */
+    protected $_reindex = false;
+
+    /**
+     * Is this the base MIME part?
+     *
+     * @var boolean
+     */
+    protected $_basepart = false;
+
+    /**
+     * The charset to output the headers in.
+     *
+     * @var string
+     */
+    protected $_hdrCharset = null;
+
+    /**
+     * The list of member variables to serialize.
+     *
+     * @var array
+     */
+    protected $_serializedVars = array(
+        '_type',
+        '_subtype',
+        '_transferEncoding',
+        '_language',
+        '_description',
+        '_disposition',
+        '_dispParams',
+        '_contentTypeParams',
+        '_parts',
+        '_mimeid',
+        '_eol',
+        '_metadata',
+        '_boundary',
+        '_bytes',
+        '_contentid',
+        '_duration',
+        '_reindex',
+        '_basepart',
+        '_hdrCharset'
+    );
+
+    /**
+     * Constructor.
+     */
+    public function __construct()
+    {
+        $this->_init();
+    }
+
+    /**
+     * Initialization tasks.
+     */
+    protected function _init()
+    {
+        $this->_contentTypeParams = new Horde_Support_CaseInsensitiveArray();
+    }
+
+    /**
+     * Function to run on clone.
+     */
+    public function __clone()
+    {
+        reset($this->_parts);
+        while (list($k, $v) = each($this->_parts)) {
+            $this->_parts[$k] = clone $v;
+        }
+
+        $this->_contentTypeParams = clone $this->_contentTypeParams;
+    }
+
+    /**
+     * Set the content-disposition of this part.
+     *
+     * @param string $disposition  The content-disposition to set ('inline',
+     *                             'attachment', or an empty value).
+     */
+    public function setDisposition($disposition = null)
+    {
+        if (empty($disposition)) {
+            $this->_disposition = '';
+        } else {
+            $disposition = Horde_String::lower($disposition);
+            if (in_array($disposition, array('inline', 'attachment'))) {
+                $this->_disposition = $disposition;
+            }
+        }
+    }
+
+    /**
+     * Get the content-disposition of this part.
+     *
+     * @return string  The part's content-disposition.  An empty string means
+     *                 no desired disposition has been set for this part.
+     */
+    public function getDisposition()
+    {
+        return $this->_disposition;
+    }
+
+    /**
+     * Add a disposition parameter to this part.
+     *
+     * @param string $label  The disposition parameter label.
+     * @param string $data   The disposition parameter data.
+     */
+    public function setDispositionParameter($label, $data)
+    {
+        $this->_dispParams[$label] = $data;
+
+        switch ($label) {
+        case 'size':
+            // RFC 2183 [2.7] - size parameter
+            $this->_bytes = intval($data);
+            break;
+        }
+    }
+
+    /**
+     * Get a disposition parameter from this part.
+     *
+     * @param string $label  The disposition parameter label.
+     *
+     * @return string  The data requested.
+     *                 Returns null if $label is not set.
+     */
+    public function getDispositionParameter($label)
+    {
+        return (isset($this->_dispParams[$label]))
+            ? $this->_dispParams[$label]
+            : null;
+    }
+
+    /**
+     * Get all parameters from the Content-Disposition header.
+     *
+     * @return array  An array of all the parameters
+     *                Returns the empty array if no parameters set.
+     */
+    public function getAllDispositionParameters()
+    {
+        return $this->_dispParams;
+    }
+
+    /**
+     * Set the name of this part.
+     *
+     * @param string $name  The name to set.
+     */
+    public function setName($name)
+    {
+        $this->setDispositionParameter('filename', $name);
+        $this->setContentTypeParameter('name', $name);
+    }
+
+    /**
+     * Get the name of this part.
+     *
+     * @param boolean $default  If the name parameter doesn't exist, should we
+     *                          use the default name from the description
+     *                          parameter?
+     *
+     * @return string  The name of the part.
+     */
+    public function getName($default = false)
+    {
+        if (!($name = $this->getDispositionParameter('filename')) &&
+            !($name = $this->getContentTypeParameter('name')) &&
+            $default) {
+            $name = preg_replace('|\W|', '_', $this->getDescription(false));
+        }
+
+        return $name;
+    }
+
+    /**
+     * Set the body contents of this part.
+     *
+     * @param mixed $contents  The part body. Either a string or a stream
+     *                         resource, or an array containing both.
+     * @param array $options   Additional options:
+     *   - encoding: (string) The encoding of $contents.
+     *               DEFAULT: Current transfer encoding value.
+     *   - usestream: (boolean) If $contents is a stream, should we directly
+     *                use that stream?
+     *                DEFAULT: $contents copied to a new stream.
+     */
+    public function setContents($contents, $options = array())
+    {
+        $this->clearContents();
+        if (empty($options['encoding'])) {
+            $options['encoding'] = $this->_transferEncoding;
+        }
+
+        $fp = (empty($options['usestream']) || !is_resource($contents))
+            ? $this->_writeStream($contents)
+            : $contents;
+
+        $this->setTransferEncoding($options['encoding']);
+        $this->_contents = $this->_transferDecode($fp, $options['encoding']);
+    }
+
+    /**
+     * Add to the body contents of this part.
+     *
+     * @param mixed $contents   The part body. Either a string or a stream
+     *                          resource, or an array containing both.
+     *   - encoding: (string) The encoding of $contents.
+     *               DEFAULT: Current transfer encoding value.
+     *   - usestream: (boolean) If $contents is a stream, should we directly
+     *                use that stream?
+     *                DEFAULT: $contents copied to a new stream.
+     */
+    public function appendContents($contents, $options = array())
+    {
+        if (empty($this->_contents)) {
+            $this->setContents($contents, $options);
+        } else {
+            $fp = (empty($options['usestream']) || !is_resource($contents))
+                ? $this->_writeStream($contents)
+                : $contents;
+
+            $this->_writeStream((empty($options['encoding']) || ($options['encoding'] == $this->_transferEncoding)) ? $fp : $this->_transferDecode($fp, $options['encoding']), array('fp' => $this->_contents));
+            unset($this->_temp['sendTransferEncoding']);
+        }
+    }
+
+    /**
+     * Clears the body contents of this part.
+     */
+    public function clearContents()
+    {
+        if (!empty($this->_contents)) {
+            fclose($this->_contents);
+            $this->_contents = null;
+            unset($this->_temp['sendTransferEncoding']);
+        }
+    }
+
+    /**
+     * Return the body of the part.
+     *
+     * @param array $options  Additional options:
+     *   - canonical: (boolean) Returns the contents in strict RFC 822 &
+     *                2045 output - namely, all newlines end with the
+     *                canonical <CR><LF> sequence.
+     *                DEFAULT: No
+     *   - stream: (boolean) Return the body as a stream resource.
+     *             DEFAULT: No
+     *
+     * @return mixed  The body text (string) of the part, null if there is no
+     *                contents, and a stream resource if 'stream' is true.
+     */
+    public function getContents($options = array())
+    {
+        return empty($options['canonical'])
+            ? (empty($options['stream']) ? $this->_readStream($this->_contents) : $this->_contents)
+            : $this->replaceEOL($this->_contents, self::RFC_EOL, !empty($options['stream']));
+    }
+
+    /**
+     * Decodes the contents of the part to binary encoding.
+     *
+     * @param resource $fp      A stream containing the data to decode.
+     * @param string $encoding  The original file encoding.
+     *
+     * @return resource  A new file resource with the decoded data.
+     */
+    protected function _transferDecode($fp, $encoding)
+    {
+        /* If the contents are empty, return now. */
+        fseek($fp, 0, SEEK_END);
+        if (ftell($fp)) {
+            switch ($encoding) {
+            case 'base64':
+                try {
+                    return $this->_writeStream($fp, array(
+                        'error' => true,
+                        'filter' => array(
+                            'convert.base64-decode' => array()
+                        )
+                    ));
+                } catch (ErrorException $e) {}
+
+                rewind($fp);
+                return $this->_writeStream(base64_decode(stream_get_contents($fp)));
+
+            case 'quoted-printable':
+                try {
+                    return $this->_writeStream($fp, array(
+                        'error' => true,
+                        'filter' => array(
+                            'convert.quoted-printable-decode' => array()
+                        )
+                    ));
+                } catch (ErrorException $e) {}
+
+                // Workaround for Horde Bug #8747
+                rewind($fp);
+                return $this->_writeStream(quoted_printable_decode(stream_get_contents($fp)));
+
+            case 'uuencode':
+            case 'x-uuencode':
+            case 'x-uue':
+                /* Support for uuencoded encoding - although not required by
+                 * RFCs, some mailers may still encode this way. */
+                $res = Horde_Mime::uudecode($this->_readStream($fp));
+                return $this->_writeStream($res[0]['data']);
+            }
+        }
+
+        return $fp;
+    }
+
+    /**
+     * Encodes the contents of the part as necessary for transport.
+     *
+     * @param resource $fp      A stream containing the data to encode.
+     * @param string $encoding  The encoding to use.
+     * @param string $eol       EOL string.
+     *
+     * @return resource  A new file resource with the encoded data.
+     */
+    protected function _transferEncode($fp, $encoding, $eol)
+    {
+        $this->_temp['transferEncodeClose'] = true;
+
+        switch ($encoding) {
+        case 'base64':
+            /* Base64 Encoding: See RFC 2045, section 6.8 */
+            return $this->_writeStream($fp, array(
+                'filter' => array(
+                    'convert.base64-encode' => array(
+                        'line-break-chars' => $eol,
+                        'line-length' => 76
+                    )
+                )
+            ));
+
+        case 'quoted-printable':
+            /* Quoted-Printable Encoding: See RFC 2045, section 6.7 */
+            return $this->_writeStream($fp, array(
+                'filter' => array(
+                    'convert.quoted-printable-encode' => array(
+                        'line-break-chars' => $eol,
+                        'line-length' => 76
+                    )
+                )
+            ));
+
+        default:
+            $this->_temp['transferEncodeClose'] = false;
+            return $fp;
+        }
+    }
+
+    /**
+     * Set the MIME type of this part.
+     *
+     * @param string $type  The MIME type to set (ex.: text/plain).
+     */
+    public function setType($type)
+    {
+        /* RFC 2045: Any entity with unrecognized encoding must be treated
+         * as if it has a Content-Type of "application/octet-stream"
+         * regardless of what the Content-Type field actually says. */
+        if (($this->_transferEncoding == self::UNKNOWN) ||
+            (strpos($type, '/') === false)) {
+            return;
+        }
+
+        list($this->_type, $this->_subtype) = explode('/', Horde_String::lower($type));
+
+        if (in_array($this->_type, self::$mimeTypes)) {
+            /* Set the boundary string for 'multipart/*' parts. */
+            if ($this->_type == 'multipart') {
+                if (!$this->getContentTypeParameter('boundary')) {
+                    $this->setContentTypeParameter('boundary', $this->_generateBoundary());
+                }
+            } else {
+                $this->clearContentTypeParameter('boundary');
+            }
+        } else {
+            $this->_type = self::UNKNOWN;
+            $this->clearContentTypeParameter('boundary');
+        }
+    }
+
+     /**
+      * Get the full MIME Content-Type of this part.
+      *
+      * @param boolean $charset  Append character set information to the end
+      *                          of the content type if this is a text/* part?
+      *`
+      * @return string  The mimetype of this part (ex.: text/plain;
+      *                 charset=us-ascii) or false.
+      */
+    public function getType($charset = false)
+    {
+        if (empty($this->_type) || empty($this->_subtype)) {
+            return false;
+        }
+
+        $ptype = $this->getPrimaryType();
+        $type = $ptype . '/' . $this->getSubType();
+        if ($charset &&
+            ($ptype == 'text') &&
+            ($charset = $this->getCharset())) {
+            $type .= '; charset=' . $charset;
+        }
+
+        return $type;
+    }
+
+    /**
+     * If the subtype of a MIME part is unrecognized by an application, the
+     * default type should be used instead (See RFC 2046).  This method
+     * returns the default subtype for a particular primary MIME type.
+     *
+     * @return string  The default MIME type of this part (ex.: text/plain).
+     */
+    public function getDefaultType()
+    {
+        switch ($this->getPrimaryType()) {
+        case 'text':
+            /* RFC 2046 (4.1.4): text parts default to text/plain. */
+            return 'text/plain';
+
+        case 'multipart':
+            /* RFC 2046 (5.1.3): multipart parts default to multipart/mixed. */
+            return 'multipart/mixed';
+
+        default:
+            /* RFC 2046 (4.2, 4.3, 4.4, 4.5.3, 5.2.4): all others default to
+               application/octet-stream. */
+            return 'application/octet-stream';
+        }
+    }
+
+    /**
+     * Get the primary type of this part.
+     *
+     * @return string  The primary MIME type of this part.
+     */
+    public function getPrimaryType()
+    {
+        return $this->_type;
+    }
+
+    /**
+     * Get the subtype of this part.
+     *
+     * @return string  The MIME subtype of this part.
+     */
+    public function getSubType()
+    {
+        return $this->_subtype;
+    }
+
+    /**
+     * Set the character set of this part.
+     *
+     * @param string $charset  The character set of this part.
+     */
+    public function setCharset($charset)
+    {
+        $this->setContentTypeParameter('charset', $charset);
+    }
+
+    /**
+     * Get the character set to use for this part.
+     *
+     * @return string  The character set of this part. Returns null if there
+     *                 is no character set.
+     */
+    public function getCharset()
+    {
+        $charset = $this->getContentTypeParameter('charset');
+        if (is_null($charset) && $this->getPrimaryType() != 'text') {
+            return null;
+        }
+
+        $charset = Horde_String::lower($charset);
+
+        if ($this->getPrimaryType() == 'text') {
+            $d_charset = Horde_String::lower(self::$defaultCharset);
+            if ($d_charset != 'us-ascii' &&
+                (!$charset || $charset == 'us-ascii')) {
+                return $d_charset;
+            }
+        }
+
+        return $charset;
+    }
+
+    /**
+     * Set the character set to use when outputting MIME headers.
+     *
+     * @param string $charset  The character set.
+     */
+    public function setHeaderCharset($charset)
+    {
+        $this->_hdrCharset = $charset;
+    }
+
+    /**
+     * Get the character set to use when outputting MIME headers.
+     *
+     * @return string  The character set.
+     */
+    public function getHeaderCharset()
+    {
+        return is_null($this->_hdrCharset)
+            ? $this->getCharset()
+            : $this->_hdrCharset;
+    }
+
+    /**
+     * Set the language(s) of this part.
+     *
+     * @param mixed $lang  A language string, or an array of language
+     *                     strings.
+     */
+    public function setLanguage($lang)
+    {
+        $this->_language = is_array($lang)
+            ? $lang
+            : array($lang);
+    }
+
+    /**
+     * Get the language(s) of this part.
+     *
+     * @param array  The list of languages.
+     */
+    public function getLanguage()
+    {
+        return $this->_language;
+    }
+
+    /**
+     * Set the content duration of the data contained in this part (see RFC
+     * 3803).
+     *
+     * @param integer $duration  The duration of the data, in seconds. If
+     *                           null, clears the duration information.
+     */
+    public function setDuration($duration)
+    {
+        if (is_null($duration)) {
+            unset($this->_duration);
+        } else {
+            $this->_duration = intval($duration);
+        }
+    }
+
+    /**
+     * Get the content duration of the data contained in this part (see RFC
+     * 3803).
+     *
+     * @return integer  The duration of the data, in seconds. Returns null if
+     *                  there is no duration information.
+     */
+    public function getDuration()
+    {
+        return isset($this->_duration)
+            ? $this->_duration
+            : null;
+    }
+
+    /**
+     * Set the description of this part.
+     *
+     * @param string $description  The description of this part.
+     */
+    public function setDescription($description)
+    {
+        $this->_description = $description;
+    }
+
+    /**
+     * Get the description of this part.
+     *
+     * @param boolean $default  If the description parameter doesn't exist,
+     *                          should we use the name of the part?
+     *
+     * @return string  The description of this part.
+     */
+    public function getDescription($default = false)
+    {
+        $desc = $this->_description;
+
+        if ($default && empty($desc)) {
+            $desc = $this->getName();
+        }
+
+        return $desc;
+    }
+
+    /**
+     * Set the transfer encoding to use for this part. Only needed in the
+     * following circumstances:
+     * 1.) Indicate what the transfer encoding is if the data has not yet been
+     * set in the object (can only be set if there presently are not
+     * any contents).
+     * 2.) Force the encoding to a certain type on a toString() call (if
+     * 'send' is true).
+     *
+     * @param string $encoding  The transfer encoding to use.
+     * @param array $options    Additional options:
+     *   - send: (boolean) If true, use $encoding as the sending encoding.
+     *           DEFAULT: $encoding is used to change the base encoding.
+     */
+    public function setTransferEncoding($encoding, $options = array())
+    {
+        if (empty($encoding) ||
+            (empty($options['send']) && !empty($this->_contents))) {
+            return;
+        }
+
+        $encoding = Horde_String::lower($encoding);
+
+        if (in_array($encoding, self::$encodingTypes)) {
+            if (empty($options['send'])) {
+                $this->_transferEncoding = $encoding;
+            } else {
+                $this->_temp['sendEncoding'] = $encoding;
+            }
+        } elseif (empty($options['send'])) {
+            /* RFC 2045: Any entity with unrecognized encoding must be treated
+             * as if it has a Content-Type of "application/octet-stream"
+             * regardless of what the Content-Type field actually says. */
+            $this->setType('application/octet-stream');
+            $this->_transferEncoding = self::UNKNOWN;
+        }
+    }
+
+    /**
+     * Add a MIME subpart.
+     *
+     * @param Horde_Mime_Part $mime_part  Add a subpart to the current object.
+     */
+    public function addPart($mime_part)
+    {
+        $this->_parts[] = $mime_part;
+        $this->_reindex = true;
+    }
+
+    /**
+     * Get a list of all MIME subparts.
+     *
+     * @return array  An array of the Horde_Mime_Part subparts.
+     */
+    public function getParts()
+    {
+        return $this->_parts;
+    }
+
+    /**
+     * Retrieve a specific MIME part.
+     *
+     * @param string $id  The MIME ID to get.
+     *
+     * @return Horde_Mime_Part  The part requested or null if the part doesn't
+     *                          exist.
+     */
+    public function getPart($id)
+    {
+        return $this->_partAction($id, 'get');
+    }
+
+    /**
+     * Remove a subpart.
+     *
+     * @param string $id  The MIME ID to delete.
+     *
+     * @param boolean  Success status.
+     */
+    public function removePart($id)
+    {
+        return $this->_partAction($id, 'remove');
+    }
+
+    /**
+     * Alter a current MIME subpart.
+     *
+     * @param string $id                  The MIME ID to alter.
+     * @param Horde_Mime_Part $mime_part  The MIME part to store.
+     *
+     * @param boolean  Success status.
+     */
+    public function alterPart($id, $mime_part)
+    {
+        return $this->_partAction($id, 'alter', $mime_part);
+    }
+
+    /**
+     * Function used to find a specific MIME part by ID and perform an action
+     * on it.
+     *
+     * @param string $id                  The MIME ID.
+     * @param string $action              The action to perform ('get',
+     *                                    'remove', or 'alter').
+     * @param Horde_Mime_Part $mime_part  The object to use for 'alter'.
+     *
+     * @return mixed  See calling functions.
+     */
+    protected function _partAction($id, $action, $mime_part = null)
+    {
+        $this_id = $this->getMimeId();
+
+        /* Need strcmp() because, e.g., '2.0' == '2'. */
+        if (($action == 'get') && (strcmp($id, $this_id) === 0)) {
+            return $this;
+        }
+
+        if ($this->_reindex) {
+            $this->buildMimeIds(is_null($this_id) ? '1' : $this_id);
+        }
+
+        foreach (array_keys($this->_parts) as $val) {
+            $partid = $this->_parts[$val]->getMimeId();
+            if (strcmp($id, $partid) === 0) {
+                switch ($action) {
+                case 'alter':
+                    $mime_part->setMimeId($this->_parts[$val]->getMimeId());
+                    $this->_parts[$val] = $mime_part;
+                    return true;
+
+                case 'get':
+                    return $this->_parts[$val];
+
+                case 'remove':
+                    unset($this->_parts[$val]);
+                    $this->_reindex = true;
+                    return true;
+                }
+            }
+
+            if ((strpos($id, $partid . '.') === 0) ||
+                (strrchr($partid, '.') === '.0')) {
+                return $this->_parts[$val]->_partAction($id, $action, $mime_part);
+            }
+        }
+
+        return ($action == 'get') ? null : false;
+    }
+
+    /**
+     * Add a content type parameter to this part.
+     *
+     * @param string $label  The disposition parameter label.
+     * @param string $data   The disposition parameter data.
+     */
+    public function setContentTypeParameter($label, $data)
+    {
+        $this->_contentTypeParams[$label] = $data;
+    }
+
+    /**
+     * Clears a content type parameter from this part.
+     *
+     * @param string $label  The disposition parameter label.
+     * @param string $data   The disposition parameter data.
+     */
+    public function clearContentTypeParameter($label)
+    {
+        unset($this->_contentTypeParams[$label]);
+    }
+
+    /**
+     * Get a content type parameter from this part.
+     *
+     * @param string $label  The content type parameter label.
+     *
+     * @return string  The data requested.
+     *                 Returns null if $label is not set.
+     */
+    public function getContentTypeParameter($label)
+    {
+        return isset($this->_contentTypeParams[$label])
+            ? $this->_contentTypeParams[$label]
+            : null;
+    }
+
+    /**
+     * Get all parameters from the Content-Type header.
+     *
+     * @return array  An array of all the parameters
+     *                Returns the empty array if no parameters set.
+     */
+    public function getAllContentTypeParameters()
+    {
+        return $this->_contentTypeParams->getArrayCopy();
+    }
+
+    /**
+     * Sets a new string to use for EOLs.
+     *
+     * @param string $eol  The string to use for EOLs.
+     */
+    public function setEOL($eol)
+    {
+        $this->_eol = $eol;
+    }
+
+    /**
+     * Get the string to use for EOLs.
+     *
+     * @return string  The string to use for EOLs.
+     */
+    public function getEOL()
+    {
+        return $this->_eol;
+    }
+
+    /**
+     * Returns a Horde_Mime_Header object containing all MIME headers needed
+     * for the part.
+     *
+     * @param array $options  Additional options:
+     *   - encode: (integer) A mask of allowable encodings.
+     *             DEFAULT: See self::_getTransferEncoding()
+     *   - headers: (Horde_Mime_Headers) The object to add the MIME headers
+     *              to.
+     *              DEFAULT: Add headers to a new object
+     *
+     * @return Horde_Mime_Headers  A Horde_Mime_Headers object.
+     */
+    public function addMimeHeaders($options = array())
+    {
+        $headers = empty($options['headers'])
+            ? new Horde_Mime_Headers()
+            : $options['headers'];
+
+        /* Get the Content-Type itself. */
+        $ptype = $this->getPrimaryType();
+        $c_params = $this->getAllContentTypeParameters();
+        if ($ptype != 'text') {
+            unset($c_params['charset']);
+        }
+        $headers->replaceHeader('Content-Type', $this->getType(), array('params' => $c_params));
+
+        /* Add the language(s), if set. (RFC 3282 [2]) */
+        if ($langs = $this->getLanguage()) {
+            $headers->replaceHeader('Content-Language', implode(',', $langs));
+        }
+
+        /* Get the description, if any. */
+        if (($descrip = $this->getDescription())) {
+            $headers->replaceHeader('Content-Description', $descrip);
+        }
+
+        /* Set the duration, if it exists. (RFC 3803) */
+        if (($duration = $this->getDuration()) !== null) {
+            $headers->replaceHeader('Content-Duration', $duration);
+        }
+
+        /* Per RFC 2046 [4], this MUST appear in the base message headers. */
+        if ($this->_basepart) {
+            $headers->replaceHeader('MIME-Version', '1.0');
+        }
+
+        /* message/* parts require no additional header information. */
+        if ($ptype == 'message') {
+            return $headers;
+        }
+
+        /* Don't show Content-Disposition unless a disposition has explicitly
+         * been set or there are parameters.
+         * If there is a name, but no disposition, default to 'attachment'.
+         * RFC 2183 [2] indicates that default is no requested disposition -
+         * the receiving MUA is responsible for display choice. */
+        $disposition = $this->getDisposition();
+        $disp_params = $this->getAllDispositionParameters();
+        $name = $this->getName();
+        if ($disposition || !empty($name) || !empty($disp_params)) {
+            if (!$disposition) {
+                $disposition = 'attachment';
+            }
+            if ($name) {
+                $disp_params['filename'] = $name;
+            }
+            $headers->replaceHeader('Content-Disposition', $disposition, array('params' => $disp_params));
+        } else {
+            $headers->removeHeader('Content-Disposition');
+        }
+
+        /* Add transfer encoding information. RFC 2045 [6.1] indicates that
+         * default is 7bit. No need to send the header in this case. */
+        $encoding = $this->_getTransferEncoding(empty($options['encode']) ? null : $options['encode']);
+        if ($encoding == '7bit') {
+            $headers->removeHeader('Content-Transfer-Encoding');
+        } else {
+            $headers->replaceHeader('Content-Transfer-Encoding', $encoding);
+        }
+
+        /* Add content ID information. */
+        if (!is_null($this->_contentid)) {
+            $headers->replaceHeader('Content-ID', '<' . $this->_contentid . '>');
+        }
+
+        return $headers;
+    }
+
+    /**
+     * Return the entire part in MIME format.
+     *
+     * @param array $options  Additional options:
+     *   - canonical: (boolean) Returns the encoded part in strict RFC 822 &
+     *                2045 output - namely, all newlines end with the
+     *                canonical <CR><LF> sequence.
+     *                DEFAULT: false
+     *   - defserver: (string) The default server to use when creating the
+     *                header string.
+     *                DEFAULT: none
+     *   - encode: (integer) A mask of allowable encodings.
+     *             DEFAULT: self::ENCODE_7BIT
+     *   - headers: (mixed) Include the MIME headers? If true, create a new
+     *              headers object. If a Horde_Mime_Headers object, add MIME
+     *              headers to this object. If a string, use the string
+     *              verbatim.
+     *              DEFAULT: true
+     *   - id: (string) Return only this MIME ID part.
+     *         DEFAULT: Returns the base part.
+     *   - stream: (boolean) Return a stream resource.
+     *             DEFAULT: false
+     *
+     * @return mixed  The MIME string (returned as a resource if $stream is
+     *                true).
+     */
+    public function toString($options = array())
+    {
+        $eol = $this->getEOL();
+        $isbase = true;
+        $oldbaseptr = null;
+        $parts = $parts_close = array();
+
+        if (isset($options['id'])) {
+            $id = $options['id'];
+            if (!($part = $this->getPart($id))) {
+                return $part;
+            }
+            unset($options['id']);
+            $contents = $part->toString($options);
+
+            $prev_id = Horde_Mime::mimeIdArithmetic($id, 'up', array('norfc822' => true));
+            $prev_part = ($prev_id == $this->getMimeId())
+                ? $this
+                : $this->getPart($prev_id);
+            if (!$prev_part) {
+                return $contents;
+            }
+
+            $boundary = trim($this->getContentTypeParameter('boundary'), '"');
+            $parts = array(
+                $eol . '--' . $boundary . $eol,
+                $contents
+            );
+
+            if (!$this->getPart(Horde_Mime::mimeIdArithmetic($id, 'next'))) {
+                $parts[] = $eol . '--' . $boundary . '--' . $eol;
+            }
+        } else {
+            if ($isbase = empty($options['_notbase'])) {
+                $headers = !empty($options['headers'])
+                    ? $options['headers']
+                    : false;
+
+                if (empty($options['encode'])) {
+                    $options['encode'] = null;
+                }
+                if (empty($options['defserver'])) {
+                    $options['defserver'] = null;
+                }
+                $options['headers'] = true;
+                $options['_notbase'] = true;
+            } else {
+                $headers = true;
+                $oldbaseptr = &$options['_baseptr'];
+            }
+
+            $this->_temp['toString'] = '';
+            $options['_baseptr'] = &$this->_temp['toString'];
+
+            /* Any information about a message is embedded in the message
+             * contents themself. Simply output the contents of the part
+             * directly and return. */
+            $ptype = $this->getPrimaryType();
+            if ($ptype == 'message') {
+                $parts[] = $this->_contents;
+            } else {
+                if (!empty($this->_contents)) {
+                    $encoding = $this->_getTransferEncoding($options['encode']);
+                    switch ($encoding) {
+                    case '8bit':
+                        if (empty($options['_baseptr'])) {
+                            $options['_baseptr'] = '8bit';
+                        }
+                        break;
+
+                    case 'binary':
+                        $options['_baseptr'] = 'binary';
+                        break;
+                    }
+
+                    $parts[] = $this->_transferEncode(
+                        $this->_contents,
+                        $encoding,
+                        (empty($options['canonical']) ? $this->getEOL() : self::RFC_EOL)
+                    );
+
+                    /* If not using $this->_contents, we can close the stream
+                     * when finished. */
+                    if ($this->_temp['transferEncodeClose']) {
+                        $parts_close[] = end($parts);
+                    }
+                }
+
+                /* Deal with multipart messages. */
+                if ($ptype == 'multipart') {
+                    if (empty($this->_contents)) {
+                        $parts[] = 'This message is in MIME format.' . $eol;
+                    }
+
+                    $boundary = trim($this->getContentTypeParameter('boundary'), '"');
+
+                    reset($this->_parts);
+                    while (list(,$part) = each($this->_parts)) {
+                        $parts[] = $eol . '--' . $boundary . $eol;
+                        $tmp = $part->toString($options);
+                        if ($part->getEOL() != $eol) {
+                            $tmp = $this->replaceEOL($tmp, $eol, !empty($options['stream']));
+                        }
+                        if (!empty($options['stream'])) {
+                            $parts_close[] = $tmp;
+                        }
+                        $parts[] = $tmp;
+                    }
+                    $parts[] = $eol . '--' . $boundary . '--' . $eol;
+                }
+            }
+
+            if (is_string($headers)) {
+                array_unshift($parts, $headers);
+            } elseif ($headers) {
+                $hdr_ob = $this->addMimeHeaders(array('encode' => $options['encode'], 'headers' => ($headers === true) ? null : $headers));
+                $hdr_ob->setEOL($eol);
+                if (!empty($this->_temp['toString'])) {
+                    $hdr_ob->replaceHeader('Content-Transfer-Encoding', $this->_temp['toString']);
+                }
+                array_unshift($parts, $hdr_ob->toString(array('charset' => $this->getHeaderCharset(), 'defserver' => $options['defserver'])));
+            }
+        }
+
+        $newfp = $this->_writeStream($parts);
+
+        array_map('fclose', $parts_close);
+
+        if (!is_null($oldbaseptr)) {
+            switch ($this->_temp['toString']) {
+            case '8bit':
+                if (empty($oldbaseptr)) {
+                    $oldbaseptr = '8bit';
+                }
+                break;
+
+            case 'binary':
+                $oldbaseptr = 'binary';
+                break;
+            }
+        }
+
+        if ($isbase && !empty($options['canonical'])) {
+            return $this->replaceEOL($newfp, self::RFC_EOL, !empty($options['stream']));
+        }
+
+        return empty($options['stream'])
+            ? $this->_readStream($newfp)
+            : $newfp;
+    }
+
+    /**
+     * Get the transfer encoding for the part based on the user requested
+     * transfer encoding and the current contents of the part.
+     *
+     * @param integer $encode  A mask of allowable encodings.
+     *
+     * @return string  The transfer-encoding of this part.
+     */
+    protected function _getTransferEncoding($encode = self::ENCODE_7BIT)
+    {
+        if (!empty($this->_temp['sendEncoding'])) {
+            return $this->_temp['sendEncoding'];
+        } elseif (!empty($this->_temp['sendTransferEncoding'][$encode])) {
+            return $this->_temp['sendTransferEncoding'][$encode];
+        }
+
+        if (empty($this->_contents)) {
+            $encoding = '7bit';
+        } else {
+            $nobinary = false;
+
+            switch ($this->getPrimaryType()) {
+            case 'message':
+            case 'multipart':
+                /* RFC 2046 [5.2.1] - message/rfc822 messages only allow 7bit,
+                 * 8bit, and binary encodings. If the current encoding is
+                 * either base64 or q-p, switch it to 8bit instead.
+                 * RFC 2046 [5.2.2, 5.2.3, 5.2.4] - All other message/*
+                 * messages only allow 7bit encodings.
+                 *
+                 * TODO: What if message contains 8bit characters and we are
+                 * in strict 7bit mode? Not sure there is anything we can do
+                 * in that situation, especially for message/rfc822 parts.
+                 *
+                 * These encoding will be figured out later (via toString()).
+                 * They are limited to 7bit, 8bit, and binary. Default to
+                 * '7bit' per RFCs. */
+                $encoding = '7bit';
+                $nobinary = true;
+                break;
+
+            case 'text':
+                $eol = $this->getEOL();
+
+                if ($this->_scanStream($this->_contents, '8bit')) {
+                    $encoding = ($encode & self::ENCODE_8BIT || $encode & self::ENCODE_BINARY)
+                        ? '8bit'
+                        : 'quoted-printable';
+                } elseif ($this->_scanStream($this->_contents, 'preg', "/(?:" . $eol . "|^)[^" . $eol . "]{999,}(?:" . $eol . "|$)/")) {
+                    /* If the text is longer than 998 characters between
+                     * linebreaks, use quoted-printable encoding to ensure the
+                     * text will not be chopped (i.e. by sendmail if being
+                     * sent as mail text). */
+                    $encoding = 'quoted-printable';
+                } else {
+                    $encoding = '7bit';
+                }
+                break;
+
+            default:
+                /* If transfer encoding has changed from the default, use that
+                 * value. */
+                if ($this->_transferEncoding != self::DEFAULT_ENCODING) {
+                    $encoding = $this->_transferEncoding;
+                } else {
+                    $encoding = ($encode & self::ENCODE_8BIT || $encode & self::ENCODE_BINARY)
+                        ? '8bit'
+                        : 'base64';
+                }
+                break;
+            }
+
+            /* Need to do one last check for binary data if encoding is 7bit
+             * or 8bit.  If the message contains a NULL character at all, the
+             * message MUST be in binary format. RFC 2046 [2.7, 2.8, 2.9]. Q-P
+             * and base64 can handle binary data fine so no need to switch
+             * those encodings. */
+            if (!$nobinary &&
+                in_array($encoding, array('8bit', '7bit')) &&
+                $this->_scanStream($this->_contents, 'binary')) {
+                $encoding = ($encode & self::ENCODE_BINARY)
+                    ? 'binary'
+                    : 'base64';
+            }
+        }
+
+        $this->_temp['sendTransferEncoding'][$encode] = $encoding;
+
+        return $encoding;
+    }
+
+    /**
+     * Replace newlines in this part's contents with those specified by either
+     * the given newline sequence or the part's current EOL setting.
+     *
+     * @param mixed $text      The text to replace. Either a string or a
+     *                         stream resource. If a stream, and returning
+     *                         a string, will close the stream when done.
+     * @param string $eol      The EOL sequence to use. If not present, uses
+     *                         the part's current EOL setting.
+     * @param boolean $stream  If true, returns a stream resource.
+     *
+     * @return string  The text with the newlines replaced by the desired
+     *                 newline sequence (returned as a stream resource if
+     *                 $stream is true).
+     */
+    public function replaceEOL($text, $eol = null, $stream = false)
+    {
+        if (is_null($eol)) {
+            $eol = $this->getEOL();
+        }
+
+        $fp = $this->_writeStream($text);
+
+        stream_filter_register('horde_eol', 'Horde_Stream_Filter_Eol');
+        stream_filter_append($fp, 'horde_eol', STREAM_FILTER_READ, array('eol' => $eol));
+
+        return $stream ? $fp : $this->_readStream($fp, true);
+    }
+
+    /**
+     * Determine the size of this MIME part and its child members.
+     *
+     * @param boolean $approx  If true, determines an approximate size for
+     *                         parts consisting of base64 encoded data.
+     *
+     * @return integer  Size of the part, in bytes.
+     */
+    public function getBytes($approx = false)
+    {
+        $bytes = 0;
+
+        if (isset($this->_bytes)) {
+            $bytes = $this->_bytes;
+
+            /* Base64 transfer encoding is approx. 33% larger than original
+             * data size (RFC 2045 [6.8]). */
+            if ($approx && ($this->_transferEncoding == 'base64')) {
+                $bytes *= 0.75;
+            }
+        } elseif ($this->getPrimaryType() == 'multipart') {
+            reset($this->_parts);
+            while (list(,$part) = each($this->_parts)) {
+                $bytes += $part->getBytes($approx);
+            }
+        } elseif ($this->_contents) {
+            fseek($this->_contents, 0, SEEK_END);
+            $bytes = ftell($this->_contents);
+
+            /* Base64 transfer encoding is approx. 33% larger than original
+             * data size (RFC 2045 [6.8]). */
+            if ($approx && ($this->_transferEncoding == 'base64')) {
+                $bytes *= 0.75;
+            }
+        }
+
+        return $bytes;
+    }
+
+    /**
+     * Explicitly set the size (in bytes) of this part. This value will only
+     * be returned (via getBytes()) if there are no contents currently set.
+     * This function is useful for setting the size of the part when the
+     * contents of the part are not fully loaded (i.e. creating a
+     * Horde_Mime_Part object from IMAP header information without loading the
+     * data of the part).
+     *
+     * @param integer $bytes  The size of this part in bytes.
+     */
+    public function setBytes($bytes)
+    {
+        $this->setDispositionParameter('size', $bytes);
+    }
+
+    /**
+     * Output the size of this MIME part in KB.
+     *
+     * @param boolean $approx  If true, determines an approximate size for
+     *                         parts consisting of base64 encoded data.
+     *
+     * @return string  Size of the part in KB.
+     */
+    public function getSize($approx = false)
+    {
+        if (!($bytes = $this->getBytes($approx))) {
+            return 0;
+        }
+
+        $localeinfo = Horde_Nls::getLocaleInfo();
+
+        // TODO: Workaround broken number_format() prior to PHP 5.4.0.
+        return str_replace(
+            array('X', 'Y'),
+            array($localeinfo['decimal_point'], $localeinfo['thousands_sep']),
+            number_format(ceil($bytes / 1024), 0, 'X', 'Y')
+        );
+    }
+
+    /**
+     * Sets the Content-ID header for this part.
+     *
+     * @param string $cid  Use this CID (if not already set).  Else, generate
+     *                     a random CID.
+     *
+     * @return string  The Content-ID for this part.
+     */
+    public function setContentId($cid = null)
+    {
+        if (is_null($this->_contentid)) {
+            $this->_contentid = is_null($cid)
+                ? (strval(new Horde_Support_Randomid()) . '@' . $_SERVER['SERVER_NAME'])
+                : trim($cid, '<>');
+        }
+
+        return $this->_contentid;
+    }
+
+    /**
+     * Returns the Content-ID for this part.
+     *
+     * @return string  The Content-ID for this part.
+     */
+    public function getContentId()
+    {
+        return $this->_contentid;
+    }
+
+    /**
+     * Alter the MIME ID of this part.
+     *
+     * @param string $mimeid  The MIME ID.
+     */
+    public function setMimeId($mimeid)
+    {
+        $this->_mimeid = $mimeid;
+    }
+
+    /**
+     * Returns the MIME ID of this part.
+     *
+     * @return string  The MIME ID.
+     */
+    public function getMimeId()
+    {
+        return $this->_mimeid;
+    }
+
+    /**
+     * Build the MIME IDs for this part and all subparts.
+     *
+     * @param string $id       The ID of this part.
+     * @param boolean $rfc822  Is this a message/rfc822 part?
+     */
+    public function buildMimeIds($id = null, $rfc822 = false)
+    {
+        if (is_null($id)) {
+            $rfc822 = true;
+            $id = '';
+        }
+
+        if ($rfc822) {
+            if (empty($this->_parts)) {
+                $this->setMimeId($id . '1');
+            } else {
+                if (empty($id) && ($this->getType() == 'message/rfc822')) {
+                    $this->setMimeId('1');
+                    $id = '1.';
+                } else {
+                    $this->setMimeId($id . '0');
+                }
+                $i = 1;
+                foreach (array_keys($this->_parts) as $val) {
+                    $this->_parts[$val]->buildMimeIds($id . ($i++));
+                }
+            }
+        } else {
+            $this->setMimeId($id);
+            $id = $id
+                ? $id . '.'
+                : '';
+
+            if ($this->getType() == 'message/rfc822') {
+                if (count($this->_parts)) {
+                    reset($this->_parts);
+                    $this->_parts[key($this->_parts)]->buildMimeIds($id, true);
+                }
+            } elseif (!empty($this->_parts)) {
+                $i = 1;
+                foreach (array_keys($this->_parts) as $val) {
+                    $this->_parts[$val]->buildMimeIds($id . ($i++));
+                }
+            }
+        }
+
+        $this->_reindex = false;
+    }
+
+    /**
+     * Generate the unique boundary string (if not already done).
+     *
+     * @return string  The boundary string.
+     */
+    protected function _generateBoundary()
+    {
+        if (is_null($this->_boundary)) {
+            $this->_boundary = '=_' . strval(new Horde_Support_Randomid());
+        }
+        return $this->_boundary;
+    }
+
+    /**
+     * Returns a mapping of all MIME IDs to their content-types.
+     *
+     * @param boolean $sort  Sort by MIME ID?
+     *
+     * @return array  Keys: MIME ID; values: content type.
+     */
+    public function contentTypeMap($sort = true)
+    {
+        $map = array($this->getMimeId() => $this->getType());
+        foreach ($this->_parts as $val) {
+            $map += $val->contentTypeMap(false);
+        }
+
+        if ($sort) {
+            uksort($map, 'strnatcmp');
+        }
+
+        return $map;
+    }
+
+    /**
+     * Is this the base MIME part?
+     *
+     * @param boolean $base  True if this is the base MIME part.
+     */
+    public function isBasePart($base)
+    {
+        $this->_basepart = $base;
+    }
+
+    /**
+     * Set a piece of metadata on this object.
+     *
+     * @param string $key  The metadata key.
+     * @param mixed $data  The metadata. If null, clears the key.
+     */
+    public function setMetadata($key, $data = null)
+    {
+        if (is_null($data)) {
+            unset($this->_metadata[$key]);
+        } else {
+            $this->_metadata[$key] = $data;
+        }
+    }
+
+    /**
+     * Retrieves metadata from this object.
+     *
+     * @param string $key  The metadata key.
+     *
+     * @return mixed  The metadata, or null if it doesn't exist.
+     */
+    public function getMetadata($key)
+    {
+        return isset($this->_metadata[$key])
+            ? $this->_metadata[$key]
+            : null;
+    }
+
+    /**
+     * Sends this message.
+     *
+     * @param string $email                 The address list to send to.
+     * @param Horde_Mime_Headers $headers   The Horde_Mime_Headers object
+     *                                      holding this message's headers.
+     * @param Horde_Mail_Transport $mailer  A Horde_Mail_Transport object.
+     * @param array $opts                   Additional options:
+     *   - encode: (integer) The encoding to use. A mask of self::ENCODE_*
+     *             values.
+     *             DEFAULT: Auto-determined based on transport driver.
+     *
+     * @throws Horde_Mime_Exception
+     * @throws InvalidArgumentException
+     */
+    public function send($email, $headers, Horde_Mail_Transport $mailer,
+                         array $opts = array())
+    {
+        $old_basepart = $this->_basepart;
+        $this->_basepart = true;
+
+        /* Does the SMTP backend support 8BITMIME (RFC 1652) or
+         * BINARYMIME (RFC 3030) extensions? Requires Net_SMTP version
+         * 1.3+. */
+        $encode = self::ENCODE_7BIT;
+        if (isset($opts['encode'])) {
+            /* Always allow 7bit encoding. */
+            $encode |= $opts['encode'];
+        } elseif ($mailer instanceof Horde_Mail_Transport_Smtp) {
+            try {
+                $smtp_ext = $mailer->getSMTPObject()->getServiceExtensions();
+                if (isset($smtp_ext['8BITMIME'])) {
+                    $encode |= self::ENCODE_8BIT;
+                }
+                if (isset($smtp_ext['BINARYMIME'])) {
+                    $encode |= self::ENCODE_BINARY;
+                }
+            } catch (Horde_Mail_Exception $e) {}
+        }
+
+        $msg = $this->toString(array(
+            'canonical' => true,
+            'encode' => $encode,
+            'headers' => false,
+            'stream' => true
+        ));
+
+        /* Make sure the message has a trailing newline. */
+        fseek($msg, -1, SEEK_END);
+        switch (fgetc($msg)) {
+        case "\r":
+            if (fgetc($msg) != "\n") {
+                fputs($msg, "\n");
+            }
+            break;
+
+        default:
+            fputs($msg, "\r\n");
+            break;
+        }
+        rewind($msg);
+
+        /* Add MIME Headers if they don't already exist. */
+        if (!$headers->getValue('MIME-Version')) {
+            $headers = $this->addMimeHeaders(array('encode' => $encode, 'headers' => $headers));
+        }
+
+        if (!empty($this->_temp['toString'])) {
+            $headers->replaceHeader('Content-Transfer-Encoding', $this->_temp['toString']);
+            switch ($this->_temp['toString']) {
+            case 'binary':
+                $mailer->addServiceExtensionParameter('BODY', 'BINARYMIME');
+                break;
+
+            case '8bit':
+                $mailer->addServiceExtensionParameter('BODY', '8BITMIME');
+                break;
+            }
+        }
+
+        $this->_basepart = $old_basepart;
+        $rfc822 = new Horde_Mail_Rfc822();
+        try {
+            $mailer->send($rfc822->parseAddressList($email)->writeAddress(array(
+                'encode' => $this->getHeaderCharset(),
+                'idn' => true
+            )), $headers->toArray(array(
+                'canonical' => true,
+                'charset' => $this->getHeaderCharset()
+            )), $msg);
+        } catch (Horde_Mail_Exception $e) {
+            throw new Horde_Mime_Exception($e);
+        }
+    }
+
+    /**
+     * Finds the main "body" text part (if any) in a message.
+     * "Body" data is the first text part under this part.
+     *
+     * @param string $subtype  Specifically search for this subtype.
+     *
+     * @return mixed  The MIME ID of the main body part, or null if a body
+     *                part is not found.
+     */
+    public function findBody($subtype = null)
+    {
+        $initial_id = $this->getMimeId();
+        $this->buildMimeIds();
+
+        foreach ($this->contentTypeMap() as $mime_id => $mime_type) {
+            if ((strpos($mime_type, 'text/') === 0) &&
+                (!$initial_id || (intval($mime_id) == 1)) &&
+                (is_null($subtype) || (substr($mime_type, 5) == $subtype)) &&
+                ($part = $this->getPart($mime_id)) &&
+                ($part->getDisposition() != 'attachment')) {
+                return $mime_id;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Write data to a stream.
+     *
+     * @param array $data     The data to write. Either a stream resource or
+     *                        a string.
+     * @param array $options  Additional options:
+     *   - error: (boolean) Catch errors when writing to the stream. Throw an
+     *            ErrorException if an error is found.
+     *            DEFAULT: false
+     *   - filter: (array) Filter(s) to apply to the string. Keys are the
+     *             filter names, values are filter params.
+     *   - fp: (resource) Use this stream instead of creating a new one.
+     *
+     * @return resource  The stream resource.
+     * @throws ErrorException
+     */
+    protected function _writeStream($data, $options = array())
+    {
+        if (empty($options['fp'])) {
+            $fp = fopen('php://temp/maxmemory:' . self::$memoryLimit, 'r+');
+        } else {
+            $fp = $options['fp'];
+            fseek($fp, 0, SEEK_END);
+        }
+
+        if (!is_array($data)) {
+            $data = array($data);
+        }
+
+        if (!empty($options['filter'])) {
+            $append_filter = array();
+            foreach ($options['filter'] as $key => $val) {
+                $append_filter[] = stream_filter_append($fp, $key, STREAM_FILTER_WRITE, $val);
+            }
+        }
+
+        if (!empty($options['error'])) {
+            set_error_handler(array($this, '_writeStreamErrorHandler'));
+            $error = null;
+        }
+
+        try {
+            reset($data);
+            while (list(,$d) = each($data)) {
+                if (is_resource($d)) {
+                    rewind($d);
+                    while (!feof($d)) {
+                        fwrite($fp, fread($d, 8192));
+                    }
+                } else {
+                    $len = strlen($d);
+                    $i = 0;
+                    while ($i < $len) {
+                        fwrite($fp, substr($d, $i, 8192));
+                        $i += 8192;
+                    }
+                }
+            }
+        } catch (ErrorException $e) {
+            $error = $e;
+        }
+
+        if (!empty($options['filter'])) {
+            foreach ($append_filter as $val) {
+                stream_filter_remove($val);
+            }
+        }
+
+        if (!empty($options['error'])) {
+            restore_error_handler();
+            if ($error) {
+                throw $error;
+            }
+        }
+
+        return $fp;
+    }
+
+    /**
+     * Error handler for _writeStream().
+     *
+     * @param integer $errno  Error code.
+     * @param string $errstr  Error text.
+     *
+     * @throws ErrorException
+     */
+    protected function _writeStreamErrorHandler($errno, $errstr)
+    {
+        throw new ErrorException($errstr, $errno);
+    }
+
+    /**
+     * Read data from a stream.
+     *
+     * @param resource $fp    An active stream.
+     * @param boolean $close  Close the stream when done reading?
+     *
+     * @return string  The data from the stream.
+     */
+    protected function _readStream($fp, $close = false)
+    {
+        $out = '';
+
+        if (!is_resource($fp)) {
+            return $out;
+        }
+
+        rewind($fp);
+        while (!feof($fp)) {
+            $out .= fread($fp, 8192);
+        }
+
+        if ($close) {
+            fclose($fp);
+        }
+
+        return $out;
+    }
+
+    /**
+     * Scans a stream for the requested data.
+     *
+     * @param resource $fp  A stream resource.
+     * @param string $type  Either '8bit', 'binary', or 'preg'.
+     * @param mixed $data   Any additional data needed to do the scan.
+     *
+     * @param boolean  The result of the scan.
+     */
+    protected function _scanStream($fp, $type, $data = null)
+    {
+        rewind($fp);
+        while (is_resource($fp) && !feof($fp)) {
+            $line = fread($fp, 8192);
+            switch ($type) {
+            case '8bit':
+                if (Horde_Mime::is8bit($line)) {
+                    return true;
+                }
+                break;
+
+            case 'binary':
+                if (strpos($line, "\0") !== false) {
+                    return true;
+                }
+                break;
+
+            case 'preg':
+                if (preg_match($data, $line)) {
+                    return true;
+                }
+                break;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Attempts to build a Horde_Mime_Part object from message text.
+     * This function can be called statically via:
+     *    $mime_part = Horde_Mime_Part::parseMessage();
+     *
+     * @param string $text  The text of the MIME message.
+     * @param array $opts   Additional options:
+     *   - forcemime: (boolean) If true, the message data is assumed to be
+     *                MIME data. If not, a MIME-Version header must exist (RFC
+     *                2045 [4]) to be parsed as a MIME message.
+     *                DEFAULT: false
+     *   - level: (integer) Current nesting level of the MIME data.
+     *            DEFAULT: 0
+     *   - no_body: (boolean) If true, don't set body contents of parts (since
+     *              2.2.0).
+     *              DEFAULT: false
+     *
+     * @return Horde_Mime_Part  A MIME Part object.
+     * @throws Horde_Mime_Exception
+     */
+    static public function parseMessage($text, array $opts = array())
+    {
+        /* Find the header. */
+        list($hdr_pos, $eol) = self::_findHeader($text);
+
+        unset($opts['ctype']);
+        $ob = self::_getStructure(substr($text, 0, $hdr_pos), substr($text, $hdr_pos + $eol), $opts);
+        $ob->buildMimeIds();
+        return $ob;
+    }
+
+    /**
+     * Creates a MIME object from the text of one part of a MIME message.
+     *
+     * @param string $header  The header text.
+     * @param string $body    The body text.
+     * @param array $opts     Additional options:
+     * <pre>
+     *   - ctype: (string) The default content-type.
+     *   - forcemime: (boolean) If true, the message data is assumed to be
+     *                MIME data. If not, a MIME-Version header must exist to
+     *                be parsed as a MIME message.
+     *   - level: (integer) Current nesting level.
+     *   - no_body: (boolean) If true, don't set body contents of parts.
+     * </pre>
+     *
+     * @return Horde_Mime_Part  The MIME part object.
+     */
+    static protected function _getStructure($header, $body,
+                                            array $opts = array())
+    {
+        $opts = array_merge(array(
+            'ctype' => 'application/octet-stream',
+            'forcemime' => false,
+            'level' => 0,
+            'no_body' => false
+        ), $opts);
+
+        /* Parse headers text into a Horde_Mime_Headers object. */
+        $hdrs = Horde_Mime_Headers::parseHeaders($header);
+
+        $ob = new Horde_Mime_Part();
+
+        /* This is not a MIME message. */
+        if (!$opts['forcemime'] && !$hdrs->getValue('mime-version')) {
+            $ob->setType('text/plain');
+
+            if (!$opts['no_body'] && !empty($body)) {
+                $ob->setContents($body);
+            }
+
+            return $ob;
+        }
+
+        /* Content type. */
+        if ($tmp = $hdrs->getValue('content-type', Horde_Mime_Headers::VALUE_BASE)) {
+            $ob->setType($tmp);
+
+            $ctype_params = $hdrs->getValue('content-type', Horde_Mime_Headers::VALUE_PARAMS);
+            foreach ($ctype_params as $key => $val) {
+                $ob->setContentTypeParameter($key, $val);
+            }
+        } else {
+            $ob->setType($opts['ctype']);
+        }
+
+        /* Content transfer encoding. */
+        if ($tmp = $hdrs->getValue('content-transfer-encoding')) {
+            $ob->setTransferEncoding($tmp);
+        }
+
+        /* Content-Description. */
+        if ($tmp = $hdrs->getValue('content-description')) {
+            $ob->setDescription($tmp);
+        }
+
+        /* Content-Disposition. */
+        if ($tmp = $hdrs->getValue('content-disposition', Horde_Mime_Headers::VALUE_BASE)) {
+            $ob->setDisposition($tmp);
+            foreach ($hdrs->getValue('content-disposition', Horde_Mime_Headers::VALUE_PARAMS) as $key => $val) {
+                $ob->setDispositionParameter($key, $val);
+            }
+        }
+
+        /* Content-Duration */
+        if ($tmp = $hdrs->getValue('content-duration')) {
+            $ob->setDuration($tmp);
+        }
+
+        /* Content-ID. */
+        if ($tmp = $hdrs->getValue('content-id')) {
+            $ob->setContentId($tmp);
+        }
+
+        if (!$opts['no_body'] &&
+            !empty($body) &&
+            ($ob->getPrimaryType() != 'multipart')) {
+            $ob->setContents($body);
+        }
+
+        if (++$opts['level'] >= self::NESTING_LIMIT) {
+            return $ob;
+        }
+
+        /* Process subparts. */
+        switch ($ob->getPrimaryType()) {
+        case 'message':
+            if ($ob->getSubType() == 'rfc822') {
+                $ob->addPart(self::parseMessage($body, array('forcemime' => true)));
+            }
+            break;
+
+        case 'multipart':
+            $boundary = $ob->getContentTypeParameter('boundary');
+            if (!is_null($boundary)) {
+                foreach (self::_findBoundary($body, 0, $boundary) as $val) {
+                    $subpart = substr($body, $val['start'], $val['length']);
+                    list($hdr_pos, $eol) = self::_findHeader($subpart);
+                    $ob->addPart(self::_getStructure(substr($subpart, 0, $hdr_pos), substr($subpart, $hdr_pos + $eol), array(
+                        'ctype' => ($ob->getSubType() == 'digest') ? 'message/rfc822' : 'text/plain',
+                        'forcemime' => true,
+                        'level' => $opts['level'],
+                        'no_body' => $opts['no_body']
+                    )));
+                }
+            }
+            break;
+        }
+
+        return $ob;
+    }
+
+    /**
+     * Attempts to obtain the raw text of a MIME part.
+     * This function can be called statically via:
+     *    $data = Horde_Mime_Part::getRawPartText();
+     *
+     * @param mixed $text   The full text of the MIME message. The text is
+     *                      assumed to be MIME data (no MIME-Version checking
+     *                      is performed). It can be either a stream or a
+     *                      string.
+     * @param string $type  Either 'header' or 'body'.
+     * @param string $id    The MIME ID.
+     *
+     * @return string  The raw text.
+     * @throws Horde_Mime_Exception
+     */
+    static public function getRawPartText($text, $type, $id)
+    {
+        /* Mini-hack to get a blank Horde_Mime part so we can call
+         * replaceEOL(). From an API perspective, getRawPartText() should be
+         * static since it is not working on MIME part data. */
+        $part = new Horde_Mime_Part();
+        $rawtext = $part->replaceEOL($text, self::RFC_EOL);
+
+        /* We need to carry around the trailing "\n" because this is needed
+         * to correctly find the boundary string. */
+        list($hdr_pos, $eol) = self::_findHeader($rawtext);
+        $curr_pos = $hdr_pos + $eol - 1;
+
+        if ($id == 0) {
+            switch ($type) {
+            case 'body':
+                return substr($rawtext, $curr_pos + 1);
+
+            case 'header':
+                return trim(substr($rawtext, 0, $hdr_pos));
+            }
+        }
+
+        $hdr_ob = Horde_Mime_Headers::parseHeaders(trim(substr($rawtext, 0, $hdr_pos)));
+
+        /* If this is a message/rfc822, pass the body into the next loop.
+         * Don't decrement the ID here. */
+        if ($hdr_ob->getValue('Content-Type', Horde_Mime_Headers::VALUE_BASE) == 'message/rfc822') {
+            return self::getRawPartText(substr($rawtext, $curr_pos + 1), $type, $id);
+        }
+
+        $base_pos = strpos($id, '.');
+        $orig_id = $id;
+
+        if ($base_pos !== false) {
+            $base_pos = substr($id, 0, $base_pos);
+            $id = substr($id, $base_pos);
+        } else {
+            $base_pos = $id;
+            $id = 0;
+        }
+
+        $params = $hdr_ob->getValue('Content-Type', Horde_Mime_Headers::VALUE_PARAMS);
+        if (!isset($params['boundary'])) {
+            if ($orig_id == '1') {
+                return substr($rawtext, $curr_pos + 1);
+            }
+
+            throw new Horde_Mime_Exception('Could not find MIME part.');
+        }
+
+        $b_find = self::_findBoundary($rawtext, $curr_pos, $params['boundary'], $base_pos);
+
+        if (!isset($b_find[$base_pos])) {
+            throw new Horde_Mime_Exception('Could not find MIME part.');
+        }
+
+        return self::getRawPartText(substr($rawtext, $b_find[$base_pos]['start'], $b_find[$base_pos]['length'] - 1), $type, $id);
+    }
+
+    /**
+     * Find the location of the end of the header text.
+     *
+     * @param string $text  The text to search.
+     *
+     * @return array  1st element: Header position, 2nd element: Length of
+     *                trailing EOL.
+     */
+    static protected function _findHeader($text)
+    {
+        $hdr_pos = strpos($text, "\r\n\r\n");
+        if ($hdr_pos !== false) {
+            return array($hdr_pos, 4);
+        }
+
+        $hdr_pos = strpos($text, "\n\n");
+        return ($hdr_pos === false)
+            ? array(strlen($text), 0)
+            : array($hdr_pos, 2);
+    }
+
+    /**
+     * Find the location of the next boundary string.
+     *
+     * @param string $text      The text to search.
+     * @param integer $pos      The current position in $text.
+     * @param string $boundary  The boundary string.
+     * @param integer $end      If set, return after matching this many
+     *                          boundaries.
+     *
+     * @return array  Keys are the boundary number, values are an array with
+     *                two elements: 'start' and 'length'.
+     */
+    static protected function _findBoundary($text, $pos, $boundary,
+                                            $end = null)
+    {
+        $i = 0;
+        $out = array();
+
+        $search = "--" . $boundary;
+        $search_len = strlen($search);
+
+        while (($pos = strpos($text, $search, $pos)) !== false) {
+            /* Boundary needs to appear at beginning of string or right after
+             * a LF. */
+            if (($pos != 0) && ($text[$pos - 1] != "\n")) {
+                continue;
+            }
+
+            if (isset($out[$i])) {
+                $out[$i]['length'] = $pos - $out[$i]['start'] - 1;
+            }
+
+            if (!is_null($end) && ($end == $i)) {
+                break;
+            }
+
+            $pos += $search_len;
+            if (isset($text[$pos])) {
+                switch ($text[$pos]) {
+                case "\r":
+                    $pos += 2;
+                    $out[++$i] = array('start' => $pos);
+                    break;
+
+                case "\n":
+                    $out[++$i] = array('start' => ++$pos);
+                    break;
+
+                case '-':
+                    return $out;
+                }
+            }
+        }
+
+        return $out;
+    }
+
+    /* ArrayAccess methods. */
+
+    public function offsetExists($offset)
+    {
+        return ($this->getPart($offset) !== null);
+    }
+
+    public function offsetGet($offset)
+    {
+        return $this->getPart($offset);
+    }
+
+    public function offsetSet($offset, $value)
+    {
+        $this->alterPart($offset, $value);
+    }
+
+    public function offsetUnset($offset)
+    {
+        $this->removePart($offset);
+    }
+
+    /* Countable methods. */
+
+    /**
+     * Returns the number of message parts.
+     *
+     * @return integer  Number of message parts.
+     */
+    public function count()
+    {
+        return count($this->_parts);
+    }
+
+    /* Serializable methods. */
+
+    /**
+     * Serialization.
+     *
+     * @return string  Serialized data.
+     */
+    public function serialize()
+    {
+        $data = array(
+            // Serialized data ID.
+            self::VERSION
+        );
+
+        foreach ($this->_serializedVars as $val) {
+            switch ($val) {
+            case '_contentTypeParams':
+                $data[] = $this->$val->getArrayCopy();
+                break;
+
+            default:
+                $data[] = $this->$val;
+                break;
+            }
+        }
+
+        if (!empty($this->_contents)) {
+            $data[] = $this->_readStream($this->_contents);
+        }
+
+        return serialize($data);
+    }
+
+    /**
+     * Unserialization.
+     *
+     * @param string $data  Serialized data.
+     *
+     * @throws Exception
+     */
+    public function unserialize($data)
+    {
+        $data = @unserialize($data);
+        if (!is_array($data) ||
+            !isset($data[0]) ||
+            (array_shift($data) != self::VERSION)) {
+            throw new Exception('Cache version change');
+        }
+
+        $this->_init();
+
+        foreach ($this->_serializedVars as $key => $val) {
+            switch ($val) {
+            case '_contentTypeParams':
+                $this->$val = new Horde_Support_CaseInsensitiveArray($data[$key]);
+                break;
+
+            default:
+                $this->$val = $data[$key];
+                break;
+            }
+        }
+
+        // $key now contains the last index of _serializedVars.
+        if (isset($data[++$key])) {
+            $this->setContents($data[$key]);
+        }
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeMimeHeadersphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Mime-Headers.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Mime-Headers.php        2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Mime-Headers.php   2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,740 +0,0 @@
</span><del>-<?php
-/**
- * This class contains functions related to handling the headers of MIME data.
- *
- * Copyright 2002-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @author   Michael Slusarz <slusarz@horde.org>
- * @category Horde
- * @license  http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package  Mime
- */
-class Horde_Mime_Headers implements Serializable
-{
-    /* Serialized version. */
-    const VERSION = 2;
-
-    /* Constants for getValue(). */
-    const VALUE_STRING = 1;
-    const VALUE_BASE = 2;
-    const VALUE_PARAMS = 3;
-
-    /**
-     * The default charset to use when parsing text parts with no charset
-     * information.
-     *
-     * @var string
-     */
-    static public $defaultCharset = 'us-ascii';
-
-    /**
-     * The internal headers array.
-     *
-     * Keys are the lowercase header name.
-     * Values are:
-     *   - h: The case-sensitive header name.
-     *   - p: Parameters for this header.
-     *   - v: The value of the header. Values are stored in UTF-8.
-     *
-     * @var array
-     */
-    protected $_headers = array();
-
-    /**
-     * The sequence to use as EOL for the headers.
-     * The default is currently to output the EOL sequence internally as
-     * just "\n" instead of the canonical "\r\n" required in RFC 822 & 2045.
-     * To be RFC complaint, the full <CR><LF> EOL combination should be used
-     * when sending a message.
-     *
-     * @var string
-     */
-    protected $_eol = "\n";
-
-    /**
-     * The User-Agent string to use.
-     *
-     * @var string
-     */
-    protected $_agent = null;
-
-    /**
-     * List of single header fields.
-     *
-     * @var array
-     */
-    protected $_singleFields = array(
-        // Mail: RFC 5322
-        'to', 'from', 'cc', 'bcc', 'date', 'sender', 'reply-to',
-        'message-id', 'in-reply-to', 'references', 'subject', 'x-priority',
-        // MIME: RFC 1864
-        'content-md5',
-        // MIME: RFC 2045
-        'mime-version', 'content-type', 'content-transfer-encoding',
-        'content-id', 'content-description',
-        // MIME: RFC 2110
-        'content-base',
-        // MIME: RFC 2183
-        'content-disposition',
-        // MIME: RFC 2424
-        'content-duration',
-        // MIME: RFC 2557
-        'content-location',
-        // MIME: RFC 2912 [3]
-        'content-features',
-        // MIME: RFC 3282
-        'content-language',
-        // MIME: RFC 3297
-        'content-alternative'
-    );
-
-    /**
-     * Returns the internal header array in array format.
-     *
-     * @param array $opts  Optional parameters:
-     *   - canonical: (boolean) Use canonical (RFC 822/2045) line endings?
-     *                DEFAULT: Uses $this->_eol
-     *   - charset: (string) Encodes the headers using this charset. If empty,
-     *              encodes using internal charset (UTF-8).
-     *              DEFAULT: No encoding.
-     *   - defserver: (string) The default domain to append to mailboxes.
-     *                DEFAULT: No default name.
-     *   - nowrap: (integer) Don't wrap the headers.
-     *             DEFAULT: Headers are wrapped.
-     *
-     * @return array  The headers in array format.
-     */
-    public function toArray(array $opts = array())
-    {
-        $address_keys = $this->addressFields();
-        $charset = array_key_exists('charset', $opts)
-            ? (empty($opts['charset']) ? 'UTF-8' : $opts['charset'])
-            : null;
-        $eol = empty($opts['canonical'])
-            ? $this->_eol
-            : "\r\n";
-        $mime = $this->mimeParamFields();
-        $ret = array();
-
-        foreach ($this->_headers as $header => $ob) {
-            $val = is_array($ob['v']) ? $ob['v'] : array($ob['v']);
-
-            foreach (array_keys($val) as $key) {
-                if (in_array($header, $address_keys) ) {
-                    /* Address encoded headers. */
-                    $rfc822 = new Horde_Mail_Rfc822();
-                    $text = $rfc822->parseAddressList($val[$key], array(
-                        'default_domain' => empty($opts['defserver']) ? null : $opts['defserver']
-                    ))->writeAddress(array(
-                        'encode' => $charset,
-                        'idn' => true
-                    ));
-                } elseif (in_array($header, $mime) && !empty($ob['p'])) {
-                    /* MIME encoded headers (RFC 2231). */
-                    $text = $val[$key];
-                    foreach ($ob['p'] as $name => $param) {
-                        foreach (Horde_Mime::encodeParam($name, $param, array('charset' => $charset, 'escape' => true)) as $name2 => $param2) {
-                            $text .= '; ' . $name2 . '=' . $param2;
-                        }
-                    }
-                } else {
-                    $text = is_null($charset)
-                        ? $val[$key]
-                        : Horde_Mime::encode($val[$key], $charset);
-                }
-
-                if (empty($opts['nowrap'])) {
-                    /* Remove any existing linebreaks and wrap the line. */
-                    $header_text = $ob['h'] . ': ';
-                    $text = ltrim(substr(wordwrap($header_text . strtr(trim($text), array("\r" => '', "\n" => '')), 76, $eol . ' '), strlen($header_text)));
-                }
-
-                $val[$key] = $text;
-            }
-
-            $ret[$ob['h']] = (count($val) == 1) ? reset($val) : $val;
-        }
-
-        return $ret;
-    }
-
-    /**
-     * Returns the internal header array in string format.
-     *
-     * @param array $opts  Optional parameters:
-     *   - canonical: (boolean) Use canonical (RFC 822/2045) line endings?
-     *                DEFAULT: Uses $this->_eol
-     *   - charset: (string) Encodes the headers using this charset.
-     *              DEFAULT: No encoding.
-     *   - defserver: (string) The default domain to append to mailboxes.
-     *                DEFAULT: No default name.
-     *   - nowrap: (integer) Don't wrap the headers.
-     *             DEFAULT: Headers are wrapped.
-     *
-     * @return string  The headers in string format.
-     */
-    public function toString(array $opts = array())
-    {
-        $eol = empty($opts['canonical'])
-            ? $this->_eol
-            : "\r\n";
-        $text = '';
-
-        foreach ($this->toArray($opts) as $key => $val) {
-            if (!is_array($val)) {
-                $val = array($val);
-            }
-            foreach ($val as $entry) {
-                $text .= $key . ': ' . $entry . $eol;
-            }
-        }
-
-        return $text . $eol;
-    }
-
-    /**
-     * Generate the 'Received' header for the Web browser->Horde hop
-     * (attempts to conform to guidelines in RFC 5321 [4.4]).
-     *
-     * @param array $opts  Additional opts:
-     *   - dns: (Net_DNS2_Resolver) Use the DNS resolver object to lookup
-     *          hostnames.
-     *          DEFAULT: Use gethostbyaddr() function.
-     *   - server: (string) Use this server name.
-     *             DEFAULT: Auto-detect using current PHP values.
-     */
-    public function addReceivedHeader(array $opts = array())
-    {
-        $old_error = error_reporting(0);
-        if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
-            /* This indicates the user is connecting through a proxy. */
-            $remote_path = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
-            $remote_addr = $remote_path[0];
-            if (!empty($opts['dns'])) {
-                $remote = $remote_addr;
-                try {
-                    if ($response = $opts['dns']->query($remote_addr, 'PTR')) {
-                        foreach ($response->answer as $val) {
-                            if (isset($val->ptrdname)) {
-                                $remote = $val->ptrdname;
-                                break;
-                            }
-                        }
-                    }
-                } catch (Net_DNS2_Exception $e) {}
-            } else {
-                $remote = gethostbyaddr($remote_addr);
-            }
-        } else {
-            $remote_addr = $_SERVER['REMOTE_ADDR'];
-            if (empty($_SERVER['REMOTE_HOST'])) {
-                if (!empty($opts['dns'])) {
-                    $remote = $remote_addr;
-                    try {
-                        if ($response = $opts['dns']->query($remote_addr, 'PTR')) {
-                            foreach ($response->answer as $val) {
-                                if (isset($val->ptrdname)) {
-                                    $remote = $val->ptrdname;
-                                    break;
-                                }
-                            }
-                        }
-                    } catch (Net_DNS2_Exception $e) {}
-                } else {
-                    $remote = gethostbyaddr($remote_addr);
-                }
-            } else {
-                $remote = $_SERVER['REMOTE_HOST'];
-            }
-        }
-        error_reporting($old_error);
-
-        if (!empty($_SERVER['REMOTE_IDENT'])) {
-            $remote_ident = $_SERVER['REMOTE_IDENT'] . '@' . $remote . ' ';
-        } elseif ($remote != $_SERVER['REMOTE_ADDR']) {
-            $remote_ident = $remote . ' ';
-        } else {
-            $remote_ident = '';
-        }
-
-        if (!empty($opts['server'])) {
-            $server_name = $opts['server'];
-        } elseif (!empty($_SERVER['SERVER_NAME'])) {
-            $server_name = $_SERVER['SERVER_NAME'];
-        } elseif (!empty($_SERVER['HTTP_HOST'])) {
-            $server_name = $_SERVER['HTTP_HOST'];
-        } else {
-            $server_name = 'unknown';
-        }
-
-        $received = 'from ' . $remote . ' (' . $remote_ident .
-            '[' . $remote_addr . ']) ' .
-            'by ' . $server_name . ' (Horde Framework) with HTTP; ' .
-            date('r');
-
-        $this->addHeader('Received', $received);
-    }
-
-    /**
-     * Generate the 'Message-ID' header.
-     */
-    public function addMessageIdHeader()
-    {
-        $this->addHeader('Message-ID', Horde_Mime::generateMessageId());
-    }
-
-    /**
-     * Generate the user agent description header.
-     */
-    public function addUserAgentHeader()
-    {
-        $this->addHeader('User-Agent', $this->getUserAgent());
-    }
-
-    /**
-     * Returns the user agent description header.
-     *
-     * @return string  The user agent header.
-     */
-    public function getUserAgent()
-    {
-        if (is_null($this->_agent)) {
-            $this->_agent = 'Horde Application Framework 4';
-        }
-        return $this->_agent;
-    }
-
-    /**
-     * Explicitly sets the User-Agent string.
-     *
-     * @param string $agent  The User-Agent string to use.
-     */
-    public function setUserAgent($agent)
-    {
-        $this->_agent = $agent;
-    }
-
-    /**
-     * Add a header to the header array.
-     *
-     * @param string $header  The header name.
-     * @param string $value   The header value (UTF-8).
-     * @param array $opts     Additional options:
-     *   - params: (array) MIME parameters for Content-Type or
-     *             Content-Disposition.
-     *             DEFAULT: None
-     *   - sanity_check: (boolean) Do sanity-checking on header value?
-     *                   DEFAULT: false
-     */
-    public function addHeader($header, $value, array $opts = array())
-    {
-        $header = trim($header);
-        $lcHeader = Horde_String::lower($header);
-
-        if (!isset($this->_headers[$lcHeader])) {
-            $this->_headers[$lcHeader] = array(
-                'h' => $header
-            );
-        }
-        $ptr = &$this->_headers[$lcHeader];
-
-        if (!empty($opts['sanity_check'])) {
-            $value = $this->_sanityCheck($value);
-        }
-
-        // Fields defined in RFC 2822 that contain address information
-        if (in_array($lcHeader, $this->addressFields())) {
-            $rfc822 = new Horde_Mail_Rfc822();
-            $addr_list = $rfc822->parseAddressList($value);
-
-            switch ($lcHeader) {
-            case 'bcc':
-            case 'cc':
-            case 'from':
-            case 'to':
-                /* Catch malformed undisclosed-recipients entries. */
-                if ((count($addr_list) == 1) &&
-                    preg_match("/^\s*undisclosed-recipients:?\s*$/i", $addr_list[0]->bare_address)) {
-                    $addr_list = new Horde_Mail_Rfc822_List('undisclosed-recipients:;');
-                }
-                break;
-            }
-            $value = strval($addr_list);
-        } else {
-            $value = Horde_Mime::decode($value);
-        }
-
-        if (isset($ptr['v'])) {
-            if (!is_array($ptr['v'])) {
-                $ptr['v'] = array($ptr['v']);
-            }
-            $ptr['v'][] = $value;
-        } else {
-            $ptr['v'] = $value;
-        }
-
-        if (!empty($opts['params'])) {
-            $ptr['p'] = $opts['params'];
-        }
-    }
-
-    /**
-     * Remove a header from the header array.
-     *
-     * @param string $header  The header name.
-     */
-    public function removeHeader($header)
-    {
-        unset($this->_headers[Horde_String::lower(trim($header))]);
-    }
-
-    /**
-     * Replace a value of a header.
-     *
-     * @param string $header  The header name.
-     * @param string $value   The header value.
-     * @param array $opts     Additional options:
-     *   - params: (array) MIME parameters for Content-Type or
-     *             Content-Disposition.
-     *             DEFAULT: None
-     *   - sanity_check: (boolean) Do sanity-checking on header value?
-     *                   DEFAULT: false
-     */
-    public function replaceHeader($header, $value, array $opts = array())
-    {
-        $this->removeHeader($header);
-        $this->addHeader($header, $value, $opts);
-    }
-
-    /**
-     * Attempts to return the header in the correct case.
-     *
-     * @param string $header  The header to search for.
-     *
-     * @return string  The value for the given header.
-     *                 If the header is not found, returns null.
-     */
-    public function getString($header)
-    {
-        $lcHeader = Horde_String::lower($header);
-        return (isset($this->_headers[$lcHeader]))
-            ? $this->_headers[$lcHeader]['h']
-            : null;
-    }
-
-    /**
-     * Attempt to return the value for a given header.
-     * The following header fields can only have 1 entry, so if duplicate
-     * entries exist, the first value will be used:
-     *   * To, From, Cc, Bcc, Date, Sender, Reply-to, Message-ID, In-Reply-To,
-     *     References, Subject (RFC 2822 [3.6])
-     *   * All List Headers (RFC 2369 [3])
-     * The values are not MIME encoded.
-     *
-     * @param string $header  The header to search for.
-     * @param integer $type   The type of return:
-     *   - VALUE_STRING: Returns a string representation of the entire header.
-     *   - VALUE_BASE: Returns a string representation of the base value of
-     *                 the header. If this is not a header that allows
-     *                 parameters, this will be equivalent to VALUE_STRING.
-     *   - VALUE_PARAMS: Returns the list of parameters for this header. If
-     *                   this is not a header that allows parameters, this
-     *                   will be an empty array.
-     *
-     * @return mixed  The value for the given header.
-     *                If the header is not found, returns null.
-     */
-    public function getValue($header, $type = self::VALUE_STRING)
-    {
-        $header = Horde_String::lower($header);
-
-        if (!isset($this->_headers[$header])) {
-            return null;
-        }
-
-        $ptr = &$this->_headers[$header];
-        if (is_array($ptr['v']) &&
-            in_array($header, $this->singleFields(true))) {
-            if (in_array($header, $this->addressFields())) {
-                $base = str_replace(';,', ';', implode(', ', $ptr['v']));
-            } else {
-                $base = $ptr['v'][0];
-            }
-        } else {
-            $base = $ptr['v'];
-        }
-        $params = isset($ptr['p']) ? $ptr['p'] : array();
-
-        switch ($type) {
-        case self::VALUE_BASE:
-            return $base;
-
-        case self::VALUE_PARAMS:
-            return $params;
-
-        case self::VALUE_STRING:
-            foreach ($params as $key => $val) {
-                $base .= '; ' . $key . '=' . $val;
-            }
-            return $base;
-        }
-    }
-
-    /**
-     * Returns the list of RFC defined header fields that contain address
-     * info.
-     *
-     * @return array  The list of headers, in lowercase.
-     */
-    static public function addressFields()
-    {
-        return array(
-            'from', 'to', 'cc', 'bcc', 'reply-to', 'resent-to', 'resent-cc',
-            'resent-bcc', 'resent-from', 'sender'
-        );
-    }
-
-    /**
-     * Returns the list of RFC defined header fields that can only contain
-     * a single value.
-     *
-     * @param boolean $list  Return list-related headers also?
-     *
-     * @return array  The list of headers, in lowercase.
-     */
-    public function singleFields($list = true)
-    {
-        return $list
-            ? array_merge($this->_singleFields, array_keys($this->listHeaders()))
-            : $this->_singleFields;
-    }
-
-    /**
-     * Returns the list of RFC defined MIME header fields that may contain
-     * parameter info.
-     *
-     * @return array  The list of headers, in lowercase.
-     */
-    static public function mimeParamFields()
-    {
-        return array('content-type', 'content-disposition');
-    }
-
-    /**
-     * Returns the list of valid mailing list headers.
-     *
-     * @return array  The list of valid mailing list headers.
-     */
-    static public function listHeaders()
-    {
-        return array(
-            /* RFC 2369 */
-            'list-help'         =>  Horde_Mime_Translation::t("List-Help"),
-            'list-unsubscribe'  =>  Horde_Mime_Translation::t("List-Unsubscribe"),
-            'list-subscribe'    =>  Horde_Mime_Translation::t("List-Subscribe"),
-            'list-owner'        =>  Horde_Mime_Translation::t("List-Owner"),
-            'list-post'         =>  Horde_Mime_Translation::t("List-Post"),
-            'list-archive'      =>  Horde_Mime_Translation::t("List-Archive"),
-            /* RFC 2919 */
-            'list-id'           =>  Horde_Mime_Translation::t("List-Id")
-        );
-    }
-
-    /**
-     * Do any mailing list headers exist?
-     *
-     * @return boolean  True if any mailing list headers exist.
-     */
-    public function listHeadersExist()
-    {
-        return (bool)count(array_intersect(array_keys($this->listHeaders()), array_keys($this->_headers)));
-    }
-
-    /**
-     * Sets a new string to use for EOLs.
-     *
-     * @param string $eol  The string to use for EOLs.
-     */
-    public function setEOL($eol)
-    {
-        $this->_eol = $eol;
-    }
-
-    /**
-     * Get the string to use for EOLs.
-     *
-     * @return string  The string to use for EOLs.
-     */
-    public function getEOL()
-    {
-        return $this->_eol;
-    }
-
-    /**
-     * Returns an address object for a header.
-     *
-     * @param string $field  The header to return as an object.
-     *
-     * @return Horde_Mail_Rfc822_List  The object for the requested field.
-     *                                 Returns null if field doesn't exist.
-     */
-    public function getOb($field)
-    {
-        if (($value = $this->getValue($field)) === null) {
-            return null;
-        }
-
-        $rfc822 = new Horde_Mail_Rfc822();
-        return $rfc822->parseAddressList($value);
-    }
-
-    /**
-     * Perform sanity checking on a raw header (e.g. handle 8-bit characters).
-     *
-     * @param string $data  The header data.
-     *
-     * @return string  The cleaned header data.
-     */
-    protected function _sanityCheck($data)
-    {
-        $charset_test = array(
-            'windows-1252',
-            self::$defaultCharset
-        );
-
-        if (!Horde_String::validUtf8($data)) {
-            /* Appears to be a PHP error with the internal String structure
-             * which prevents accurate manipulation of the string. Copying
-             * the data to a new variable fixes things. */
-            $data = substr($data, 0);
-
-            /* Assumption: broken charset in headers is generally either
-             * UTF-8 or ISO-8859-1/Windows-1252. Test these charsets
-             * first before using default charset. This may be a
-             * Western-centric approach, but it's better than nothing. */
-            foreach ($charset_test as $charset) {
-                $tmp = Horde_String::convertCharset($data, $charset, 'UTF-8');
-                if (Horde_String::validUtf8($tmp)) {
-                    return $tmp;
-                }
-            }
-        }
-
-        return $data;
-    }
-
-    /* Static methods. */
-
-    /**
-     * Builds a Horde_Mime_Headers object from header text.
-     * This function can be called statically:
-     *   $headers = Horde_Mime_Headers::parseHeaders().
-     *
-     * @param string $text  A text string containing the headers.
-     *
-     * @return Horde_Mime_Headers  A new Horde_Mime_Headers object.
-     */
-    static public function parseHeaders($text)
-    {
-        $currheader = $currtext = null;
-        $mime = self::mimeParamFields();
-        $to_process = array();
-
-        foreach (explode("\n", $text) as $val) {
-            $val = rtrim($val, "\r\n");
-            if (empty($val)) {
-                break;
-            }
-
-            if (($val[0] == ' ') || ($val[0] == "\t")) {
-                $currtext .= ' ' . ltrim($val);
-            } else {
-                if (!is_null($currheader)) {
-                    $to_process[] = array($currheader, rtrim($currtext));
-                }
-
-                $pos = strpos($val, ':');
-                $currheader = substr($val, 0, $pos);
-                $currtext = ltrim(substr($val, $pos + 1));
-            }
-        }
-
-        if (!is_null($currheader)) {
-            $to_process[] = array($currheader, $currtext);
-        }
-
-        $headers = new Horde_Mime_Headers();
-
-        reset($to_process);
-        while (list(,$val) = each($to_process)) {
-            /* Ignore empty headers. */
-            if (!strlen($val[1])) {
-                continue;
-            }
-
-            if (in_array(Horde_String::lower($val[0]), $mime)) {
-                $res = Horde_Mime::decodeParam($val[0], $val[1]);
-                $headers->addHeader($val[0], $res['val'], array(
-                    'params' => $res['params'],
-                    'sanity_check' => true
-                ));
-            } else {
-                $headers->addHeader($val[0], $val[1], array(
-                    'sanity_check' => true
-                ));
-            }
-        }
-
-        return $headers;
-    }
-
-    /* Serializable methods. */
-
-    /**
-     * Serialization.
-     *
-     * @return string  Serialized data.
-     */
-    public function serialize()
-    {
-        $data = array(
-            // Serialized data ID.
-            self::VERSION,
-            $this->_headers,
-            $this->_eol
-        );
-
-        if (!is_null($this->_agent)) {
-            $data[] = $this->_agent;
-        }
-
-        return serialize($data);
-    }
-
-    /**
-     * Unserialization.
-     *
-     * @param string $data  Serialized data.
-     *
-     * @throws Exception
-     */
-    public function unserialize($data)
-    {
-        $data = @unserialize($data);
-        if (!is_array($data) ||
-            !isset($data[0]) ||
-            ($data[0] != self::VERSION)) {
-            throw new Horde_Mime_Exception('Cache version change');
-        }
-
-        $this->_headers = $data[1];
-        $this->_eol = $data[2];
-        if (isset($data[3])) {
-            $this->_agent = $data[3];
-        }
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeMimePartphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Mime-Part.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Mime-Part.php   2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Mime-Part.php      2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,2358 +0,0 @@
</span><del>-<?php
-/**
- * This class provides an object-oriented representation of a MIME part
- * (defined by RFC 2045).
- *
- * Copyright 1999-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @author   Chuck Hagenbuch <chuck@horde.org>
- * @author   Michael Slusarz <slusarz@horde.org>
- * @category Horde
- * @license  http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package  Mime
- */
-class Horde_Mime_Part implements ArrayAccess, Countable, Serializable
-{
-    /* Serialized version. */
-    const VERSION = 1;
-
-    /* The character(s) used internally for EOLs. */
-    const EOL = "\n";
-
-    /* The character string designated by RFC 2045 to designate EOLs in MIME
-     * messages. */
-    const RFC_EOL = "\r\n";
-
-    /* The default encoding. */
-    const DEFAULT_ENCODING = 'binary';
-
-    /* Constants indicating the valid transfer encoding allowed. */
-    const ENCODE_7BIT = 1;
-    const ENCODE_8BIT = 2;
-    const ENCODE_BINARY = 4;
-
-    /* Unknown types. */
-    const UNKNOWN = 'x-unknown';
-
-    /* MIME nesting limit. */
-    const NESTING_LIMIT = 100;
-
-    /**
-     * The default charset to use when parsing text parts with no charset
-     * information.
-     *
-     * @var string
-     */
-    static public $defaultCharset = 'us-ascii';
-
-    /**
-     * Valid encoding types.
-     *
-     * @var array
-     */
-    static public $encodingTypes = array(
-        '7bit', '8bit', 'base64', 'binary', 'quoted-printable',
-        // Non-RFC types, but old mailers may still use
-        'uuencode', 'x-uuencode', 'x-uue'
-    );
-
-    /**
-     * The memory limit for use with the PHP temp stream.
-     *
-     * @var integer
-     */
-    static public $memoryLimit = 2097152;
-
-    /**
-     * Valid MIME types.
-     *
-     * @var array
-     */
-    static public $mimeTypes = array(
-        'text', 'multipart', 'message', 'application', 'audio', 'image',
-        'video', 'model'
-    );
-
-    /**
-     * The type (ex.: text) of this part.
-     * Per RFC 2045, the default is 'application'.
-     *
-     * @var string
-     */
-    protected $_type = 'application';
-
-    /**
-     * The subtype (ex.: plain) of this part.
-     * Per RFC 2045, the default is 'octet-stream'.
-     *
-     * @var string
-     */
-    protected $_subtype = 'octet-stream';
-
-    /**
-     * The body of the part. Always stored in binary format.
-     *
-     * @var resource
-     */
-    protected $_contents;
-
-    /**
-     * The desired transfer encoding of this part.
-     *
-     * @var string
-     */
-    protected $_transferEncoding = self::DEFAULT_ENCODING;
-
-    /**
-     * The language(s) of this part.
-     *
-     * @var array
-     */
-    protected $_language = array();
-
-    /**
-     * The description of this part.
-     *
-     * @var string
-     */
-    protected $_description = '';
-
-    /**
-     * The disposition of this part (inline or attachment).
-     *
-     * @var string
-     */
-    protected $_disposition = '';
-
-    /**
-     * The disposition parameters of this part.
-     *
-     * @var array
-     */
-    protected $_dispParams = array();
-
-    /**
-     * The content type parameters of this part.
-     *
-     * @var Horde_Support_CaseInsensitiveArray
-     */
-    protected $_contentTypeParams;
-
-    /**
-     * The subparts of this part.
-     *
-     * @var array
-     */
-    protected $_parts = array();
-
-    /**
-     * The MIME ID of this part.
-     *
-     * @var string
-     */
-    protected $_mimeid = null;
-
-    /**
-     * The sequence to use as EOL for this part.
-     * The default is currently to output the EOL sequence internally as
-     * just "\n" instead of the canonical "\r\n" required in RFC 822 & 2045.
-     * To be RFC complaint, the full <CR><LF> EOL combination should be used
-     * when sending a message.
-     * It is not crucial here since the PHP/PEAR mailing functions will handle
-     * the EOL details.
-     *
-     * @var string
-     */
-    protected $_eol = self::EOL;
-
-    /**
-     * Internal temp array.
-     *
-     * @var array
-     */
-    protected $_temp = array();
-
-    /**
-     * Metadata.
-     *
-     * @var array
-     */
-    protected $_metadata = array();
-
-    /**
-     * Unique Horde_Mime_Part boundary string.
-     *
-     * @var string
-     */
-    protected $_boundary = null;
-
-    /**
-     * Default value for this Part's size.
-     *
-     * @var integer
-     */
-    protected $_bytes;
-
-    /**
-     * The content-ID for this part.
-     *
-     * @var string
-     */
-    protected $_contentid = null;
-
-    /**
-     * The duration of this part's media data (RFC 3803).
-     *
-     * @var integer
-     */
-    protected $_duration;
-
-    /**
-     * Do we need to reindex the current part?
-     *
-     * @var boolean
-     */
-    protected $_reindex = false;
-
-    /**
-     * Is this the base MIME part?
-     *
-     * @var boolean
-     */
-    protected $_basepart = false;
-
-    /**
-     * The charset to output the headers in.
-     *
-     * @var string
-     */
-    protected $_hdrCharset = null;
-
-    /**
-     * The list of member variables to serialize.
-     *
-     * @var array
-     */
-    protected $_serializedVars = array(
-        '_type',
-        '_subtype',
-        '_transferEncoding',
-        '_language',
-        '_description',
-        '_disposition',
-        '_dispParams',
-        '_contentTypeParams',
-        '_parts',
-        '_mimeid',
-        '_eol',
-        '_metadata',
-        '_boundary',
-        '_bytes',
-        '_contentid',
-        '_duration',
-        '_reindex',
-        '_basepart',
-        '_hdrCharset'
-    );
-
-    /**
-     * Constructor.
-     */
-    public function __construct()
-    {
-        $this->_init();
-    }
-
-    /**
-     * Initialization tasks.
-     */
-    protected function _init()
-    {
-        $this->_contentTypeParams = new Horde_Support_CaseInsensitiveArray();
-    }
-
-    /**
-     * Function to run on clone.
-     */
-    public function __clone()
-    {
-        reset($this->_parts);
-        while (list($k, $v) = each($this->_parts)) {
-            $this->_parts[$k] = clone $v;
-        }
-
-        $this->_contentTypeParams = clone $this->_contentTypeParams;
-    }
-
-    /**
-     * Set the content-disposition of this part.
-     *
-     * @param string $disposition  The content-disposition to set ('inline',
-     *                             'attachment', or an empty value).
-     */
-    public function setDisposition($disposition = null)
-    {
-        if (empty($disposition)) {
-            $this->_disposition = '';
-        } else {
-            $disposition = Horde_String::lower($disposition);
-            if (in_array($disposition, array('inline', 'attachment'))) {
-                $this->_disposition = $disposition;
-            }
-        }
-    }
-
-    /**
-     * Get the content-disposition of this part.
-     *
-     * @return string  The part's content-disposition.  An empty string means
-     *                 no desired disposition has been set for this part.
-     */
-    public function getDisposition()
-    {
-        return $this->_disposition;
-    }
-
-    /**
-     * Add a disposition parameter to this part.
-     *
-     * @param string $label  The disposition parameter label.
-     * @param string $data   The disposition parameter data.
-     */
-    public function setDispositionParameter($label, $data)
-    {
-        $this->_dispParams[$label] = $data;
-
-        switch ($label) {
-        case 'size':
-            // RFC 2183 [2.7] - size parameter
-            $this->_bytes = intval($data);
-            break;
-        }
-    }
-
-    /**
-     * Get a disposition parameter from this part.
-     *
-     * @param string $label  The disposition parameter label.
-     *
-     * @return string  The data requested.
-     *                 Returns null if $label is not set.
-     */
-    public function getDispositionParameter($label)
-    {
-        return (isset($this->_dispParams[$label]))
-            ? $this->_dispParams[$label]
-            : null;
-    }
-
-    /**
-     * Get all parameters from the Content-Disposition header.
-     *
-     * @return array  An array of all the parameters
-     *                Returns the empty array if no parameters set.
-     */
-    public function getAllDispositionParameters()
-    {
-        return $this->_dispParams;
-    }
-
-    /**
-     * Set the name of this part.
-     *
-     * @param string $name  The name to set.
-     */
-    public function setName($name)
-    {
-        $this->setDispositionParameter('filename', $name);
-        $this->setContentTypeParameter('name', $name);
-    }
-
-    /**
-     * Get the name of this part.
-     *
-     * @param boolean $default  If the name parameter doesn't exist, should we
-     *                          use the default name from the description
-     *                          parameter?
-     *
-     * @return string  The name of the part.
-     */
-    public function getName($default = false)
-    {
-        if (!($name = $this->getDispositionParameter('filename')) &&
-            !($name = $this->getContentTypeParameter('name')) &&
-            $default) {
-            $name = preg_replace('|\W|', '_', $this->getDescription(false));
-        }
-
-        return $name;
-    }
-
-    /**
-     * Set the body contents of this part.
-     *
-     * @param mixed $contents  The part body. Either a string or a stream
-     *                         resource, or an array containing both.
-     * @param array $options   Additional options:
-     *   - encoding: (string) The encoding of $contents.
-     *               DEFAULT: Current transfer encoding value.
-     *   - usestream: (boolean) If $contents is a stream, should we directly
-     *                use that stream?
-     *                DEFAULT: $contents copied to a new stream.
-     */
-    public function setContents($contents, $options = array())
-    {
-        $this->clearContents();
-        if (empty($options['encoding'])) {
-            $options['encoding'] = $this->_transferEncoding;
-        }
-
-        $fp = (empty($options['usestream']) || !is_resource($contents))
-            ? $this->_writeStream($contents)
-            : $contents;
-
-        $this->setTransferEncoding($options['encoding']);
-        $this->_contents = $this->_transferDecode($fp, $options['encoding']);
-    }
-
-    /**
-     * Add to the body contents of this part.
-     *
-     * @param mixed $contents   The part body. Either a string or a stream
-     *                          resource, or an array containing both.
-     *   - encoding: (string) The encoding of $contents.
-     *               DEFAULT: Current transfer encoding value.
-     *   - usestream: (boolean) If $contents is a stream, should we directly
-     *                use that stream?
-     *                DEFAULT: $contents copied to a new stream.
-     */
-    public function appendContents($contents, $options = array())
-    {
-        if (empty($this->_contents)) {
-            $this->setContents($contents, $options);
-        } else {
-            $fp = (empty($options['usestream']) || !is_resource($contents))
-                ? $this->_writeStream($contents)
-                : $contents;
-
-            $this->_writeStream((empty($options['encoding']) || ($options['encoding'] == $this->_transferEncoding)) ? $fp : $this->_transferDecode($fp, $options['encoding']), array('fp' => $this->_contents));
-            unset($this->_temp['sendTransferEncoding']);
-        }
-    }
-
-    /**
-     * Clears the body contents of this part.
-     */
-    public function clearContents()
-    {
-        if (!empty($this->_contents)) {
-            fclose($this->_contents);
-            $this->_contents = null;
-            unset($this->_temp['sendTransferEncoding']);
-        }
-    }
-
-    /**
-     * Return the body of the part.
-     *
-     * @param array $options  Additional options:
-     *   - canonical: (boolean) Returns the contents in strict RFC 822 &
-     *                2045 output - namely, all newlines end with the
-     *                canonical <CR><LF> sequence.
-     *                DEFAULT: No
-     *   - stream: (boolean) Return the body as a stream resource.
-     *             DEFAULT: No
-     *
-     * @return mixed  The body text (string) of the part, null if there is no
-     *                contents, and a stream resource if 'stream' is true.
-     */
-    public function getContents($options = array())
-    {
-        return empty($options['canonical'])
-            ? (empty($options['stream']) ? $this->_readStream($this->_contents) : $this->_contents)
-            : $this->replaceEOL($this->_contents, self::RFC_EOL, !empty($options['stream']));
-    }
-
-    /**
-     * Decodes the contents of the part to binary encoding.
-     *
-     * @param resource $fp      A stream containing the data to decode.
-     * @param string $encoding  The original file encoding.
-     *
-     * @return resource  A new file resource with the decoded data.
-     */
-    protected function _transferDecode($fp, $encoding)
-    {
-        /* If the contents are empty, return now. */
-        fseek($fp, 0, SEEK_END);
-        if (ftell($fp)) {
-            switch ($encoding) {
-            case 'base64':
-                try {
-                    return $this->_writeStream($fp, array(
-                        'error' => true,
-                        'filter' => array(
-                            'convert.base64-decode' => array()
-                        )
-                    ));
-                } catch (ErrorException $e) {}
-
-                rewind($fp);
-                return $this->_writeStream(base64_decode(stream_get_contents($fp)));
-
-            case 'quoted-printable':
-                try {
-                    return $this->_writeStream($fp, array(
-                        'error' => true,
-                        'filter' => array(
-                            'convert.quoted-printable-decode' => array()
-                        )
-                    ));
-                } catch (ErrorException $e) {}
-
-                // Workaround for Horde Bug #8747
-                rewind($fp);
-                return $this->_writeStream(quoted_printable_decode(stream_get_contents($fp)));
-
-            case 'uuencode':
-            case 'x-uuencode':
-            case 'x-uue':
-                /* Support for uuencoded encoding - although not required by
-                 * RFCs, some mailers may still encode this way. */
-                $res = Horde_Mime::uudecode($this->_readStream($fp));
-                return $this->_writeStream($res[0]['data']);
-            }
-        }
-
-        return $fp;
-    }
-
-    /**
-     * Encodes the contents of the part as necessary for transport.
-     *
-     * @param resource $fp      A stream containing the data to encode.
-     * @param string $encoding  The encoding to use.
-     * @param string $eol       EOL string.
-     *
-     * @return resource  A new file resource with the encoded data.
-     */
-    protected function _transferEncode($fp, $encoding, $eol)
-    {
-        $this->_temp['transferEncodeClose'] = true;
-
-        switch ($encoding) {
-        case 'base64':
-            /* Base64 Encoding: See RFC 2045, section 6.8 */
-            return $this->_writeStream($fp, array(
-                'filter' => array(
-                    'convert.base64-encode' => array(
-                        'line-break-chars' => $eol,
-                        'line-length' => 76
-                    )
-                )
-            ));
-
-        case 'quoted-printable':
-            /* Quoted-Printable Encoding: See RFC 2045, section 6.7 */
-            return $this->_writeStream($fp, array(
-                'filter' => array(
-                    'convert.quoted-printable-encode' => array(
-                        'line-break-chars' => $eol,
-                        'line-length' => 76
-                    )
-                )
-            ));
-
-        default:
-            $this->_temp['transferEncodeClose'] = false;
-            return $fp;
-        }
-    }
-
-    /**
-     * Set the MIME type of this part.
-     *
-     * @param string $type  The MIME type to set (ex.: text/plain).
-     */
-    public function setType($type)
-    {
-        /* RFC 2045: Any entity with unrecognized encoding must be treated
-         * as if it has a Content-Type of "application/octet-stream"
-         * regardless of what the Content-Type field actually says. */
-        if (($this->_transferEncoding == self::UNKNOWN) ||
-            (strpos($type, '/') === false)) {
-            return;
-        }
-
-        list($this->_type, $this->_subtype) = explode('/', Horde_String::lower($type));
-
-        if (in_array($this->_type, self::$mimeTypes)) {
-            /* Set the boundary string for 'multipart/*' parts. */
-            if ($this->_type == 'multipart') {
-                if (!$this->getContentTypeParameter('boundary')) {
-                    $this->setContentTypeParameter('boundary', $this->_generateBoundary());
-                }
-            } else {
-                $this->clearContentTypeParameter('boundary');
-            }
-        } else {
-            $this->_type = self::UNKNOWN;
-            $this->clearContentTypeParameter('boundary');
-        }
-    }
-
-     /**
-      * Get the full MIME Content-Type of this part.
-      *
-      * @param boolean $charset  Append character set information to the end
-      *                          of the content type if this is a text/* part?
-      *`
-      * @return string  The mimetype of this part (ex.: text/plain;
-      *                 charset=us-ascii) or false.
-      */
-    public function getType($charset = false)
-    {
-        if (empty($this->_type) || empty($this->_subtype)) {
-            return false;
-        }
-
-        $ptype = $this->getPrimaryType();
-        $type = $ptype . '/' . $this->getSubType();
-        if ($charset &&
-            ($ptype == 'text') &&
-            ($charset = $this->getCharset())) {
-            $type .= '; charset=' . $charset;
-        }
-
-        return $type;
-    }
-
-    /**
-     * If the subtype of a MIME part is unrecognized by an application, the
-     * default type should be used instead (See RFC 2046).  This method
-     * returns the default subtype for a particular primary MIME type.
-     *
-     * @return string  The default MIME type of this part (ex.: text/plain).
-     */
-    public function getDefaultType()
-    {
-        switch ($this->getPrimaryType()) {
-        case 'text':
-            /* RFC 2046 (4.1.4): text parts default to text/plain. */
-            return 'text/plain';
-
-        case 'multipart':
-            /* RFC 2046 (5.1.3): multipart parts default to multipart/mixed. */
-            return 'multipart/mixed';
-
-        default:
-            /* RFC 2046 (4.2, 4.3, 4.4, 4.5.3, 5.2.4): all others default to
-               application/octet-stream. */
-            return 'application/octet-stream';
-        }
-    }
-
-    /**
-     * Get the primary type of this part.
-     *
-     * @return string  The primary MIME type of this part.
-     */
-    public function getPrimaryType()
-    {
-        return $this->_type;
-    }
-
-    /**
-     * Get the subtype of this part.
-     *
-     * @return string  The MIME subtype of this part.
-     */
-    public function getSubType()
-    {
-        return $this->_subtype;
-    }
-
-    /**
-     * Set the character set of this part.
-     *
-     * @param string $charset  The character set of this part.
-     */
-    public function setCharset($charset)
-    {
-        $this->setContentTypeParameter('charset', $charset);
-    }
-
-    /**
-     * Get the character set to use for this part.
-     *
-     * @return string  The character set of this part. Returns null if there
-     *                 is no character set.
-     */
-    public function getCharset()
-    {
-        $charset = $this->getContentTypeParameter('charset');
-        if (is_null($charset) && $this->getPrimaryType() != 'text') {
-            return null;
-        }
-
-        $charset = Horde_String::lower($charset);
-
-        if ($this->getPrimaryType() == 'text') {
-            $d_charset = Horde_String::lower(self::$defaultCharset);
-            if ($d_charset != 'us-ascii' &&
-                (!$charset || $charset == 'us-ascii')) {
-                return $d_charset;
-            }
-        }
-
-        return $charset;
-    }
-
-    /**
-     * Set the character set to use when outputting MIME headers.
-     *
-     * @param string $charset  The character set.
-     */
-    public function setHeaderCharset($charset)
-    {
-        $this->_hdrCharset = $charset;
-    }
-
-    /**
-     * Get the character set to use when outputting MIME headers.
-     *
-     * @return string  The character set.
-     */
-    public function getHeaderCharset()
-    {
-        return is_null($this->_hdrCharset)
-            ? $this->getCharset()
-            : $this->_hdrCharset;
-    }
-
-    /**
-     * Set the language(s) of this part.
-     *
-     * @param mixed $lang  A language string, or an array of language
-     *                     strings.
-     */
-    public function setLanguage($lang)
-    {
-        $this->_language = is_array($lang)
-            ? $lang
-            : array($lang);
-    }
-
-    /**
-     * Get the language(s) of this part.
-     *
-     * @param array  The list of languages.
-     */
-    public function getLanguage()
-    {
-        return $this->_language;
-    }
-
-    /**
-     * Set the content duration of the data contained in this part (see RFC
-     * 3803).
-     *
-     * @param integer $duration  The duration of the data, in seconds. If
-     *                           null, clears the duration information.
-     */
-    public function setDuration($duration)
-    {
-        if (is_null($duration)) {
-            unset($this->_duration);
-        } else {
-            $this->_duration = intval($duration);
-        }
-    }
-
-    /**
-     * Get the content duration of the data contained in this part (see RFC
-     * 3803).
-     *
-     * @return integer  The duration of the data, in seconds. Returns null if
-     *                  there is no duration information.
-     */
-    public function getDuration()
-    {
-        return isset($this->_duration)
-            ? $this->_duration
-            : null;
-    }
-
-    /**
-     * Set the description of this part.
-     *
-     * @param string $description  The description of this part.
-     */
-    public function setDescription($description)
-    {
-        $this->_description = $description;
-    }
-
-    /**
-     * Get the description of this part.
-     *
-     * @param boolean $default  If the description parameter doesn't exist,
-     *                          should we use the name of the part?
-     *
-     * @return string  The description of this part.
-     */
-    public function getDescription($default = false)
-    {
-        $desc = $this->_description;
-
-        if ($default && empty($desc)) {
-            $desc = $this->getName();
-        }
-
-        return $desc;
-    }
-
-    /**
-     * Set the transfer encoding to use for this part. Only needed in the
-     * following circumstances:
-     * 1.) Indicate what the transfer encoding is if the data has not yet been
-     * set in the object (can only be set if there presently are not
-     * any contents).
-     * 2.) Force the encoding to a certain type on a toString() call (if
-     * 'send' is true).
-     *
-     * @param string $encoding  The transfer encoding to use.
-     * @param array $options    Additional options:
-     *   - send: (boolean) If true, use $encoding as the sending encoding.
-     *           DEFAULT: $encoding is used to change the base encoding.
-     */
-    public function setTransferEncoding($encoding, $options = array())
-    {
-        if (empty($encoding) ||
-            (empty($options['send']) && !empty($this->_contents))) {
-            return;
-        }
-
-        $encoding = Horde_String::lower($encoding);
-
-        if (in_array($encoding, self::$encodingTypes)) {
-            if (empty($options['send'])) {
-                $this->_transferEncoding = $encoding;
-            } else {
-                $this->_temp['sendEncoding'] = $encoding;
-            }
-        } elseif (empty($options['send'])) {
-            /* RFC 2045: Any entity with unrecognized encoding must be treated
-             * as if it has a Content-Type of "application/octet-stream"
-             * regardless of what the Content-Type field actually says. */
-            $this->setType('application/octet-stream');
-            $this->_transferEncoding = self::UNKNOWN;
-        }
-    }
-
-    /**
-     * Add a MIME subpart.
-     *
-     * @param Horde_Mime_Part $mime_part  Add a subpart to the current object.
-     */
-    public function addPart($mime_part)
-    {
-        $this->_parts[] = $mime_part;
-        $this->_reindex = true;
-    }
-
-    /**
-     * Get a list of all MIME subparts.
-     *
-     * @return array  An array of the Horde_Mime_Part subparts.
-     */
-    public function getParts()
-    {
-        return $this->_parts;
-    }
-
-    /**
-     * Retrieve a specific MIME part.
-     *
-     * @param string $id  The MIME ID to get.
-     *
-     * @return Horde_Mime_Part  The part requested or null if the part doesn't
-     *                          exist.
-     */
-    public function getPart($id)
-    {
-        return $this->_partAction($id, 'get');
-    }
-
-    /**
-     * Remove a subpart.
-     *
-     * @param string $id  The MIME ID to delete.
-     *
-     * @param boolean  Success status.
-     */
-    public function removePart($id)
-    {
-        return $this->_partAction($id, 'remove');
-    }
-
-    /**
-     * Alter a current MIME subpart.
-     *
-     * @param string $id                  The MIME ID to alter.
-     * @param Horde_Mime_Part $mime_part  The MIME part to store.
-     *
-     * @param boolean  Success status.
-     */
-    public function alterPart($id, $mime_part)
-    {
-        return $this->_partAction($id, 'alter', $mime_part);
-    }
-
-    /**
-     * Function used to find a specific MIME part by ID and perform an action
-     * on it.
-     *
-     * @param string $id                  The MIME ID.
-     * @param string $action              The action to perform ('get',
-     *                                    'remove', or 'alter').
-     * @param Horde_Mime_Part $mime_part  The object to use for 'alter'.
-     *
-     * @return mixed  See calling functions.
-     */
-    protected function _partAction($id, $action, $mime_part = null)
-    {
-        $this_id = $this->getMimeId();
-
-        /* Need strcmp() because, e.g., '2.0' == '2'. */
-        if (($action == 'get') && (strcmp($id, $this_id) === 0)) {
-            return $this;
-        }
-
-        if ($this->_reindex) {
-            $this->buildMimeIds(is_null($this_id) ? '1' : $this_id);
-        }
-
-        foreach (array_keys($this->_parts) as $val) {
-            $partid = $this->_parts[$val]->getMimeId();
-            if (strcmp($id, $partid) === 0) {
-                switch ($action) {
-                case 'alter':
-                    $mime_part->setMimeId($this->_parts[$val]->getMimeId());
-                    $this->_parts[$val] = $mime_part;
-                    return true;
-
-                case 'get':
-                    return $this->_parts[$val];
-
-                case 'remove':
-                    unset($this->_parts[$val]);
-                    $this->_reindex = true;
-                    return true;
-                }
-            }
-
-            if ((strpos($id, $partid . '.') === 0) ||
-                (strrchr($partid, '.') === '.0')) {
-                return $this->_parts[$val]->_partAction($id, $action, $mime_part);
-            }
-        }
-
-        return ($action == 'get') ? null : false;
-    }
-
-    /**
-     * Add a content type parameter to this part.
-     *
-     * @param string $label  The disposition parameter label.
-     * @param string $data   The disposition parameter data.
-     */
-    public function setContentTypeParameter($label, $data)
-    {
-        $this->_contentTypeParams[$label] = $data;
-    }
-
-    /**
-     * Clears a content type parameter from this part.
-     *
-     * @param string $label  The disposition parameter label.
-     * @param string $data   The disposition parameter data.
-     */
-    public function clearContentTypeParameter($label)
-    {
-        unset($this->_contentTypeParams[$label]);
-    }
-
-    /**
-     * Get a content type parameter from this part.
-     *
-     * @param string $label  The content type parameter label.
-     *
-     * @return string  The data requested.
-     *                 Returns null if $label is not set.
-     */
-    public function getContentTypeParameter($label)
-    {
-        return isset($this->_contentTypeParams[$label])
-            ? $this->_contentTypeParams[$label]
-            : null;
-    }
-
-    /**
-     * Get all parameters from the Content-Type header.
-     *
-     * @return array  An array of all the parameters
-     *                Returns the empty array if no parameters set.
-     */
-    public function getAllContentTypeParameters()
-    {
-        return $this->_contentTypeParams->getArrayCopy();
-    }
-
-    /**
-     * Sets a new string to use for EOLs.
-     *
-     * @param string $eol  The string to use for EOLs.
-     */
-    public function setEOL($eol)
-    {
-        $this->_eol = $eol;
-    }
-
-    /**
-     * Get the string to use for EOLs.
-     *
-     * @return string  The string to use for EOLs.
-     */
-    public function getEOL()
-    {
-        return $this->_eol;
-    }
-
-    /**
-     * Returns a Horde_Mime_Header object containing all MIME headers needed
-     * for the part.
-     *
-     * @param array $options  Additional options:
-     *   - encode: (integer) A mask of allowable encodings.
-     *             DEFAULT: See self::_getTransferEncoding()
-     *   - headers: (Horde_Mime_Headers) The object to add the MIME headers
-     *              to.
-     *              DEFAULT: Add headers to a new object
-     *
-     * @return Horde_Mime_Headers  A Horde_Mime_Headers object.
-     */
-    public function addMimeHeaders($options = array())
-    {
-        $headers = empty($options['headers'])
-            ? new Horde_Mime_Headers()
-            : $options['headers'];
-
-        /* Get the Content-Type itself. */
-        $ptype = $this->getPrimaryType();
-        $c_params = $this->getAllContentTypeParameters();
-        if ($ptype != 'text') {
-            unset($c_params['charset']);
-        }
-        $headers->replaceHeader('Content-Type', $this->getType(), array('params' => $c_params));
-
-        /* Add the language(s), if set. (RFC 3282 [2]) */
-        if ($langs = $this->getLanguage()) {
-            $headers->replaceHeader('Content-Language', implode(',', $langs));
-        }
-
-        /* Get the description, if any. */
-        if (($descrip = $this->getDescription())) {
-            $headers->replaceHeader('Content-Description', $descrip);
-        }
-
-        /* Set the duration, if it exists. (RFC 3803) */
-        if (($duration = $this->getDuration()) !== null) {
-            $headers->replaceHeader('Content-Duration', $duration);
-        }
-
-        /* Per RFC 2046 [4], this MUST appear in the base message headers. */
-        if ($this->_basepart) {
-            $headers->replaceHeader('MIME-Version', '1.0');
-        }
-
-        /* message/* parts require no additional header information. */
-        if ($ptype == 'message') {
-            return $headers;
-        }
-
-        /* Don't show Content-Disposition unless a disposition has explicitly
-         * been set or there are parameters.
-         * If there is a name, but no disposition, default to 'attachment'.
-         * RFC 2183 [2] indicates that default is no requested disposition -
-         * the receiving MUA is responsible for display choice. */
-        $disposition = $this->getDisposition();
-        $disp_params = $this->getAllDispositionParameters();
-        $name = $this->getName();
-        if ($disposition || !empty($name) || !empty($disp_params)) {
-            if (!$disposition) {
-                $disposition = 'attachment';
-            }
-            if ($name) {
-                $disp_params['filename'] = $name;
-            }
-            $headers->replaceHeader('Content-Disposition', $disposition, array('params' => $disp_params));
-        } else {
-            $headers->removeHeader('Content-Disposition');
-        }
-
-        /* Add transfer encoding information. RFC 2045 [6.1] indicates that
-         * default is 7bit. No need to send the header in this case. */
-        $encoding = $this->_getTransferEncoding(empty($options['encode']) ? null : $options['encode']);
-        if ($encoding == '7bit') {
-            $headers->removeHeader('Content-Transfer-Encoding');
-        } else {
-            $headers->replaceHeader('Content-Transfer-Encoding', $encoding);
-        }
-
-        /* Add content ID information. */
-        if (!is_null($this->_contentid)) {
-            $headers->replaceHeader('Content-ID', '<' . $this->_contentid . '>');
-        }
-
-        return $headers;
-    }
-
-    /**
-     * Return the entire part in MIME format.
-     *
-     * @param array $options  Additional options:
-     *   - canonical: (boolean) Returns the encoded part in strict RFC 822 &
-     *                2045 output - namely, all newlines end with the
-     *                canonical <CR><LF> sequence.
-     *                DEFAULT: false
-     *   - defserver: (string) The default server to use when creating the
-     *                header string.
-     *                DEFAULT: none
-     *   - encode: (integer) A mask of allowable encodings.
-     *             DEFAULT: self::ENCODE_7BIT
-     *   - headers: (mixed) Include the MIME headers? If true, create a new
-     *              headers object. If a Horde_Mime_Headers object, add MIME
-     *              headers to this object. If a string, use the string
-     *              verbatim.
-     *              DEFAULT: true
-     *   - id: (string) Return only this MIME ID part.
-     *         DEFAULT: Returns the base part.
-     *   - stream: (boolean) Return a stream resource.
-     *             DEFAULT: false
-     *
-     * @return mixed  The MIME string (returned as a resource if $stream is
-     *                true).
-     */
-    public function toString($options = array())
-    {
-        $eol = $this->getEOL();
-        $isbase = true;
-        $oldbaseptr = null;
-        $parts = $parts_close = array();
-
-        if (isset($options['id'])) {
-            $id = $options['id'];
-            if (!($part = $this->getPart($id))) {
-                return $part;
-            }
-            unset($options['id']);
-            $contents = $part->toString($options);
-
-            $prev_id = Horde_Mime::mimeIdArithmetic($id, 'up', array('norfc822' => true));
-            $prev_part = ($prev_id == $this->getMimeId())
-                ? $this
-                : $this->getPart($prev_id);
-            if (!$prev_part) {
-                return $contents;
-            }
-
-            $boundary = trim($this->getContentTypeParameter('boundary'), '"');
-            $parts = array(
-                $eol . '--' . $boundary . $eol,
-                $contents
-            );
-
-            if (!$this->getPart(Horde_Mime::mimeIdArithmetic($id, 'next'))) {
-                $parts[] = $eol . '--' . $boundary . '--' . $eol;
-            }
-        } else {
-            if ($isbase = empty($options['_notbase'])) {
-                $headers = !empty($options['headers'])
-                    ? $options['headers']
-                    : false;
-
-                if (empty($options['encode'])) {
-                    $options['encode'] = null;
-                }
-                if (empty($options['defserver'])) {
-                    $options['defserver'] = null;
-                }
-                $options['headers'] = true;
-                $options['_notbase'] = true;
-            } else {
-                $headers = true;
-                $oldbaseptr = &$options['_baseptr'];
-            }
-
-            $this->_temp['toString'] = '';
-            $options['_baseptr'] = &$this->_temp['toString'];
-
-            /* Any information about a message is embedded in the message
-             * contents themself. Simply output the contents of the part
-             * directly and return. */
-            $ptype = $this->getPrimaryType();
-            if ($ptype == 'message') {
-                $parts[] = $this->_contents;
-            } else {
-                if (!empty($this->_contents)) {
-                    $encoding = $this->_getTransferEncoding($options['encode']);
-                    switch ($encoding) {
-                    case '8bit':
-                        if (empty($options['_baseptr'])) {
-                            $options['_baseptr'] = '8bit';
-                        }
-                        break;
-
-                    case 'binary':
-                        $options['_baseptr'] = 'binary';
-                        break;
-                    }
-
-                    $parts[] = $this->_transferEncode(
-                        $this->_contents,
-                        $encoding,
-                        (empty($options['canonical']) ? $this->getEOL() : self::RFC_EOL)
-                    );
-
-                    /* If not using $this->_contents, we can close the stream
-                     * when finished. */
-                    if ($this->_temp['transferEncodeClose']) {
-                        $parts_close[] = end($parts);
-                    }
-                }
-
-                /* Deal with multipart messages. */
-                if ($ptype == 'multipart') {
-                    if (empty($this->_contents)) {
-                        $parts[] = 'This message is in MIME format.' . $eol;
-                    }
-
-                    $boundary = trim($this->getContentTypeParameter('boundary'), '"');
-
-                    reset($this->_parts);
-                    while (list(,$part) = each($this->_parts)) {
-                        $parts[] = $eol . '--' . $boundary . $eol;
-                        $tmp = $part->toString($options);
-                        if ($part->getEOL() != $eol) {
-                            $tmp = $this->replaceEOL($tmp, $eol, !empty($options['stream']));
-                        }
-                        if (!empty($options['stream'])) {
-                            $parts_close[] = $tmp;
-                        }
-                        $parts[] = $tmp;
-                    }
-                    $parts[] = $eol . '--' . $boundary . '--' . $eol;
-                }
-            }
-
-            if (is_string($headers)) {
-                array_unshift($parts, $headers);
-            } elseif ($headers) {
-                $hdr_ob = $this->addMimeHeaders(array('encode' => $options['encode'], 'headers' => ($headers === true) ? null : $headers));
-                $hdr_ob->setEOL($eol);
-                if (!empty($this->_temp['toString'])) {
-                    $hdr_ob->replaceHeader('Content-Transfer-Encoding', $this->_temp['toString']);
-                }
-                array_unshift($parts, $hdr_ob->toString(array('charset' => $this->getHeaderCharset(), 'defserver' => $options['defserver'])));
-            }
-        }
-
-        $newfp = $this->_writeStream($parts);
-
-        array_map('fclose', $parts_close);
-
-        if (!is_null($oldbaseptr)) {
-            switch ($this->_temp['toString']) {
-            case '8bit':
-                if (empty($oldbaseptr)) {
-                    $oldbaseptr = '8bit';
-                }
-                break;
-
-            case 'binary':
-                $oldbaseptr = 'binary';
-                break;
-            }
-        }
-
-        if ($isbase && !empty($options['canonical'])) {
-            return $this->replaceEOL($newfp, self::RFC_EOL, !empty($options['stream']));
-        }
-
-        return empty($options['stream'])
-            ? $this->_readStream($newfp)
-            : $newfp;
-    }
-
-    /**
-     * Get the transfer encoding for the part based on the user requested
-     * transfer encoding and the current contents of the part.
-     *
-     * @param integer $encode  A mask of allowable encodings.
-     *
-     * @return string  The transfer-encoding of this part.
-     */
-    protected function _getTransferEncoding($encode = self::ENCODE_7BIT)
-    {
-        if (!empty($this->_temp['sendEncoding'])) {
-            return $this->_temp['sendEncoding'];
-        } elseif (!empty($this->_temp['sendTransferEncoding'][$encode])) {
-            return $this->_temp['sendTransferEncoding'][$encode];
-        }
-
-        if (empty($this->_contents)) {
-            $encoding = '7bit';
-        } else {
-            $nobinary = false;
-
-            switch ($this->getPrimaryType()) {
-            case 'message':
-            case 'multipart':
-                /* RFC 2046 [5.2.1] - message/rfc822 messages only allow 7bit,
-                 * 8bit, and binary encodings. If the current encoding is
-                 * either base64 or q-p, switch it to 8bit instead.
-                 * RFC 2046 [5.2.2, 5.2.3, 5.2.4] - All other message/*
-                 * messages only allow 7bit encodings.
-                 *
-                 * TODO: What if message contains 8bit characters and we are
-                 * in strict 7bit mode? Not sure there is anything we can do
-                 * in that situation, especially for message/rfc822 parts.
-                 *
-                 * These encoding will be figured out later (via toString()).
-                 * They are limited to 7bit, 8bit, and binary. Default to
-                 * '7bit' per RFCs. */
-                $encoding = '7bit';
-                $nobinary = true;
-                break;
-
-            case 'text':
-                $eol = $this->getEOL();
-
-                if ($this->_scanStream($this->_contents, '8bit')) {
-                    $encoding = ($encode & self::ENCODE_8BIT || $encode & self::ENCODE_BINARY)
-                        ? '8bit'
-                        : 'quoted-printable';
-                } elseif ($this->_scanStream($this->_contents, 'preg', "/(?:" . $eol . "|^)[^" . $eol . "]{999,}(?:" . $eol . "|$)/")) {
-                    /* If the text is longer than 998 characters between
-                     * linebreaks, use quoted-printable encoding to ensure the
-                     * text will not be chopped (i.e. by sendmail if being
-                     * sent as mail text). */
-                    $encoding = 'quoted-printable';
-                } else {
-                    $encoding = '7bit';
-                }
-                break;
-
-            default:
-                /* If transfer encoding has changed from the default, use that
-                 * value. */
-                if ($this->_transferEncoding != self::DEFAULT_ENCODING) {
-                    $encoding = $this->_transferEncoding;
-                } else {
-                    $encoding = ($encode & self::ENCODE_8BIT || $encode & self::ENCODE_BINARY)
-                        ? '8bit'
-                        : 'base64';
-                }
-                break;
-            }
-
-            /* Need to do one last check for binary data if encoding is 7bit
-             * or 8bit.  If the message contains a NULL character at all, the
-             * message MUST be in binary format. RFC 2046 [2.7, 2.8, 2.9]. Q-P
-             * and base64 can handle binary data fine so no need to switch
-             * those encodings. */
-            if (!$nobinary &&
-                in_array($encoding, array('8bit', '7bit')) &&
-                $this->_scanStream($this->_contents, 'binary')) {
-                $encoding = ($encode & self::ENCODE_BINARY)
-                    ? 'binary'
-                    : 'base64';
-            }
-        }
-
-        $this->_temp['sendTransferEncoding'][$encode] = $encoding;
-
-        return $encoding;
-    }
-
-    /**
-     * Replace newlines in this part's contents with those specified by either
-     * the given newline sequence or the part's current EOL setting.
-     *
-     * @param mixed $text      The text to replace. Either a string or a
-     *                         stream resource. If a stream, and returning
-     *                         a string, will close the stream when done.
-     * @param string $eol      The EOL sequence to use. If not present, uses
-     *                         the part's current EOL setting.
-     * @param boolean $stream  If true, returns a stream resource.
-     *
-     * @return string  The text with the newlines replaced by the desired
-     *                 newline sequence (returned as a stream resource if
-     *                 $stream is true).
-     */
-    public function replaceEOL($text, $eol = null, $stream = false)
-    {
-        if (is_null($eol)) {
-            $eol = $this->getEOL();
-        }
-
-        $fp = $this->_writeStream($text);
-
-        stream_filter_register('horde_eol', 'Horde_Stream_Filter_Eol');
-        stream_filter_append($fp, 'horde_eol', STREAM_FILTER_READ, array('eol' => $eol));
-
-        return $stream ? $fp : $this->_readStream($fp, true);
-    }
-
-    /**
-     * Determine the size of this MIME part and its child members.
-     *
-     * @param boolean $approx  If true, determines an approximate size for
-     *                         parts consisting of base64 encoded data.
-     *
-     * @return integer  Size of the part, in bytes.
-     */
-    public function getBytes($approx = false)
-    {
-        $bytes = 0;
-
-        if (isset($this->_bytes)) {
-            $bytes = $this->_bytes;
-
-            /* Base64 transfer encoding is approx. 33% larger than original
-             * data size (RFC 2045 [6.8]). */
-            if ($approx && ($this->_transferEncoding == 'base64')) {
-                $bytes *= 0.75;
-            }
-        } elseif ($this->getPrimaryType() == 'multipart') {
-            reset($this->_parts);
-            while (list(,$part) = each($this->_parts)) {
-                $bytes += $part->getBytes($approx);
-            }
-        } elseif ($this->_contents) {
-            fseek($this->_contents, 0, SEEK_END);
-            $bytes = ftell($this->_contents);
-
-            /* Base64 transfer encoding is approx. 33% larger than original
-             * data size (RFC 2045 [6.8]). */
-            if ($approx && ($this->_transferEncoding == 'base64')) {
-                $bytes *= 0.75;
-            }
-        }
-
-        return $bytes;
-    }
-
-    /**
-     * Explicitly set the size (in bytes) of this part. This value will only
-     * be returned (via getBytes()) if there are no contents currently set.
-     * This function is useful for setting the size of the part when the
-     * contents of the part are not fully loaded (i.e. creating a
-     * Horde_Mime_Part object from IMAP header information without loading the
-     * data of the part).
-     *
-     * @param integer $bytes  The size of this part in bytes.
-     */
-    public function setBytes($bytes)
-    {
-        $this->setDispositionParameter('size', $bytes);
-    }
-
-    /**
-     * Output the size of this MIME part in KB.
-     *
-     * @param boolean $approx  If true, determines an approximate size for
-     *                         parts consisting of base64 encoded data.
-     *
-     * @return string  Size of the part in KB.
-     */
-    public function getSize($approx = false)
-    {
-        if (!($bytes = $this->getBytes($approx))) {
-            return 0;
-        }
-
-        $localeinfo = Horde_Nls::getLocaleInfo();
-
-        // TODO: Workaround broken number_format() prior to PHP 5.4.0.
-        return str_replace(
-            array('X', 'Y'),
-            array($localeinfo['decimal_point'], $localeinfo['thousands_sep']),
-            number_format(ceil($bytes / 1024), 0, 'X', 'Y')
-        );
-    }
-
-    /**
-     * Sets the Content-ID header for this part.
-     *
-     * @param string $cid  Use this CID (if not already set).  Else, generate
-     *                     a random CID.
-     *
-     * @return string  The Content-ID for this part.
-     */
-    public function setContentId($cid = null)
-    {
-        if (is_null($this->_contentid)) {
-            $this->_contentid = is_null($cid)
-                ? (strval(new Horde_Support_Randomid()) . '@' . $_SERVER['SERVER_NAME'])
-                : trim($cid, '<>');
-        }
-
-        return $this->_contentid;
-    }
-
-    /**
-     * Returns the Content-ID for this part.
-     *
-     * @return string  The Content-ID for this part.
-     */
-    public function getContentId()
-    {
-        return $this->_contentid;
-    }
-
-    /**
-     * Alter the MIME ID of this part.
-     *
-     * @param string $mimeid  The MIME ID.
-     */
-    public function setMimeId($mimeid)
-    {
-        $this->_mimeid = $mimeid;
-    }
-
-    /**
-     * Returns the MIME ID of this part.
-     *
-     * @return string  The MIME ID.
-     */
-    public function getMimeId()
-    {
-        return $this->_mimeid;
-    }
-
-    /**
-     * Build the MIME IDs for this part and all subparts.
-     *
-     * @param string $id       The ID of this part.
-     * @param boolean $rfc822  Is this a message/rfc822 part?
-     */
-    public function buildMimeIds($id = null, $rfc822 = false)
-    {
-        if (is_null($id)) {
-            $rfc822 = true;
-            $id = '';
-        }
-
-        if ($rfc822) {
-            if (empty($this->_parts)) {
-                $this->setMimeId($id . '1');
-            } else {
-                if (empty($id) && ($this->getType() == 'message/rfc822')) {
-                    $this->setMimeId('1');
-                    $id = '1.';
-                } else {
-                    $this->setMimeId($id . '0');
-                }
-                $i = 1;
-                foreach (array_keys($this->_parts) as $val) {
-                    $this->_parts[$val]->buildMimeIds($id . ($i++));
-                }
-            }
-        } else {
-            $this->setMimeId($id);
-            $id = $id
-                ? $id . '.'
-                : '';
-
-            if ($this->getType() == 'message/rfc822') {
-                if (count($this->_parts)) {
-                    reset($this->_parts);
-                    $this->_parts[key($this->_parts)]->buildMimeIds($id, true);
-                }
-            } elseif (!empty($this->_parts)) {
-                $i = 1;
-                foreach (array_keys($this->_parts) as $val) {
-                    $this->_parts[$val]->buildMimeIds($id . ($i++));
-                }
-            }
-        }
-
-        $this->_reindex = false;
-    }
-
-    /**
-     * Generate the unique boundary string (if not already done).
-     *
-     * @return string  The boundary string.
-     */
-    protected function _generateBoundary()
-    {
-        if (is_null($this->_boundary)) {
-            $this->_boundary = '=_' . strval(new Horde_Support_Randomid());
-        }
-        return $this->_boundary;
-    }
-
-    /**
-     * Returns a mapping of all MIME IDs to their content-types.
-     *
-     * @param boolean $sort  Sort by MIME ID?
-     *
-     * @return array  Keys: MIME ID; values: content type.
-     */
-    public function contentTypeMap($sort = true)
-    {
-        $map = array($this->getMimeId() => $this->getType());
-        foreach ($this->_parts as $val) {
-            $map += $val->contentTypeMap(false);
-        }
-
-        if ($sort) {
-            uksort($map, 'strnatcmp');
-        }
-
-        return $map;
-    }
-
-    /**
-     * Is this the base MIME part?
-     *
-     * @param boolean $base  True if this is the base MIME part.
-     */
-    public function isBasePart($base)
-    {
-        $this->_basepart = $base;
-    }
-
-    /**
-     * Set a piece of metadata on this object.
-     *
-     * @param string $key  The metadata key.
-     * @param mixed $data  The metadata. If null, clears the key.
-     */
-    public function setMetadata($key, $data = null)
-    {
-        if (is_null($data)) {
-            unset($this->_metadata[$key]);
-        } else {
-            $this->_metadata[$key] = $data;
-        }
-    }
-
-    /**
-     * Retrieves metadata from this object.
-     *
-     * @param string $key  The metadata key.
-     *
-     * @return mixed  The metadata, or null if it doesn't exist.
-     */
-    public function getMetadata($key)
-    {
-        return isset($this->_metadata[$key])
-            ? $this->_metadata[$key]
-            : null;
-    }
-
-    /**
-     * Sends this message.
-     *
-     * @param string $email                 The address list to send to.
-     * @param Horde_Mime_Headers $headers   The Horde_Mime_Headers object
-     *                                      holding this message's headers.
-     * @param Horde_Mail_Transport $mailer  A Horde_Mail_Transport object.
-     * @param array $opts                   Additional options:
-     *   - encode: (integer) The encoding to use. A mask of self::ENCODE_*
-     *             values.
-     *             DEFAULT: Auto-determined based on transport driver.
-     *
-     * @throws Horde_Mime_Exception
-     * @throws InvalidArgumentException
-     */
-    public function send($email, $headers, Horde_Mail_Transport $mailer,
-                         array $opts = array())
-    {
-        $old_basepart = $this->_basepart;
-        $this->_basepart = true;
-
-        /* Does the SMTP backend support 8BITMIME (RFC 1652) or
-         * BINARYMIME (RFC 3030) extensions? Requires Net_SMTP version
-         * 1.3+. */
-        $encode = self::ENCODE_7BIT;
-        if (isset($opts['encode'])) {
-            /* Always allow 7bit encoding. */
-            $encode |= $opts['encode'];
-        } elseif ($mailer instanceof Horde_Mail_Transport_Smtp) {
-            try {
-                $smtp_ext = $mailer->getSMTPObject()->getServiceExtensions();
-                if (isset($smtp_ext['8BITMIME'])) {
-                    $encode |= self::ENCODE_8BIT;
-                }
-                if (isset($smtp_ext['BINARYMIME'])) {
-                    $encode |= self::ENCODE_BINARY;
-                }
-            } catch (Horde_Mail_Exception $e) {}
-        }
-
-        $msg = $this->toString(array(
-            'canonical' => true,
-            'encode' => $encode,
-            'headers' => false,
-            'stream' => true
-        ));
-
-        /* Make sure the message has a trailing newline. */
-        fseek($msg, -1, SEEK_END);
-        switch (fgetc($msg)) {
-        case "\r":
-            if (fgetc($msg) != "\n") {
-                fputs($msg, "\n");
-            }
-            break;
-
-        default:
-            fputs($msg, "\r\n");
-            break;
-        }
-        rewind($msg);
-
-        /* Add MIME Headers if they don't already exist. */
-        if (!$headers->getValue('MIME-Version')) {
-            $headers = $this->addMimeHeaders(array('encode' => $encode, 'headers' => $headers));
-        }
-
-        if (!empty($this->_temp['toString'])) {
-            $headers->replaceHeader('Content-Transfer-Encoding', $this->_temp['toString']);
-            switch ($this->_temp['toString']) {
-            case 'binary':
-                $mailer->addServiceExtensionParameter('BODY', 'BINARYMIME');
-                break;
-
-            case '8bit':
-                $mailer->addServiceExtensionParameter('BODY', '8BITMIME');
-                break;
-            }
-        }
-
-        $this->_basepart = $old_basepart;
-        $rfc822 = new Horde_Mail_Rfc822();
-        try {
-            $mailer->send($rfc822->parseAddressList($email)->writeAddress(array(
-                'encode' => $this->getHeaderCharset(),
-                'idn' => true
-            )), $headers->toArray(array(
-                'canonical' => true,
-                'charset' => $this->getHeaderCharset()
-            )), $msg);
-        } catch (Horde_Mail_Exception $e) {
-            throw new Horde_Mime_Exception($e);
-        }
-    }
-
-    /**
-     * Finds the main "body" text part (if any) in a message.
-     * "Body" data is the first text part under this part.
-     *
-     * @param string $subtype  Specifically search for this subtype.
-     *
-     * @return mixed  The MIME ID of the main body part, or null if a body
-     *                part is not found.
-     */
-    public function findBody($subtype = null)
-    {
-        $initial_id = $this->getMimeId();
-        $this->buildMimeIds();
-
-        foreach ($this->contentTypeMap() as $mime_id => $mime_type) {
-            if ((strpos($mime_type, 'text/') === 0) &&
-                (!$initial_id || (intval($mime_id) == 1)) &&
-                (is_null($subtype) || (substr($mime_type, 5) == $subtype)) &&
-                ($part = $this->getPart($mime_id)) &&
-                ($part->getDisposition() != 'attachment')) {
-                return $mime_id;
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * Write data to a stream.
-     *
-     * @param array $data     The data to write. Either a stream resource or
-     *                        a string.
-     * @param array $options  Additional options:
-     *   - error: (boolean) Catch errors when writing to the stream. Throw an
-     *            ErrorException if an error is found.
-     *            DEFAULT: false
-     *   - filter: (array) Filter(s) to apply to the string. Keys are the
-     *             filter names, values are filter params.
-     *   - fp: (resource) Use this stream instead of creating a new one.
-     *
-     * @return resource  The stream resource.
-     * @throws ErrorException
-     */
-    protected function _writeStream($data, $options = array())
-    {
-        if (empty($options['fp'])) {
-            $fp = fopen('php://temp/maxmemory:' . self::$memoryLimit, 'r+');
-        } else {
-            $fp = $options['fp'];
-            fseek($fp, 0, SEEK_END);
-        }
-
-        if (!is_array($data)) {
-            $data = array($data);
-        }
-
-        if (!empty($options['filter'])) {
-            $append_filter = array();
-            foreach ($options['filter'] as $key => $val) {
-                $append_filter[] = stream_filter_append($fp, $key, STREAM_FILTER_WRITE, $val);
-            }
-        }
-
-        if (!empty($options['error'])) {
-            set_error_handler(array($this, '_writeStreamErrorHandler'));
-            $error = null;
-        }
-
-        try {
-            reset($data);
-            while (list(,$d) = each($data)) {
-                if (is_resource($d)) {
-                    rewind($d);
-                    while (!feof($d)) {
-                        fwrite($fp, fread($d, 8192));
-                    }
-                } else {
-                    $len = strlen($d);
-                    $i = 0;
-                    while ($i < $len) {
-                        fwrite($fp, substr($d, $i, 8192));
-                        $i += 8192;
-                    }
-                }
-            }
-        } catch (ErrorException $e) {
-            $error = $e;
-        }
-
-        if (!empty($options['filter'])) {
-            foreach ($append_filter as $val) {
-                stream_filter_remove($val);
-            }
-        }
-
-        if (!empty($options['error'])) {
-            restore_error_handler();
-            if ($error) {
-                throw $error;
-            }
-        }
-
-        return $fp;
-    }
-
-    /**
-     * Error handler for _writeStream().
-     *
-     * @param integer $errno  Error code.
-     * @param string $errstr  Error text.
-     *
-     * @throws ErrorException
-     */
-    protected function _writeStreamErrorHandler($errno, $errstr)
-    {
-        throw new ErrorException($errstr, $errno);
-    }
-
-    /**
-     * Read data from a stream.
-     *
-     * @param resource $fp    An active stream.
-     * @param boolean $close  Close the stream when done reading?
-     *
-     * @return string  The data from the stream.
-     */
-    protected function _readStream($fp, $close = false)
-    {
-        $out = '';
-
-        if (!is_resource($fp)) {
-            return $out;
-        }
-
-        rewind($fp);
-        while (!feof($fp)) {
-            $out .= fread($fp, 8192);
-        }
-
-        if ($close) {
-            fclose($fp);
-        }
-
-        return $out;
-    }
-
-    /**
-     * Scans a stream for the requested data.
-     *
-     * @param resource $fp  A stream resource.
-     * @param string $type  Either '8bit', 'binary', or 'preg'.
-     * @param mixed $data   Any additional data needed to do the scan.
-     *
-     * @param boolean  The result of the scan.
-     */
-    protected function _scanStream($fp, $type, $data = null)
-    {
-        rewind($fp);
-        while (is_resource($fp) && !feof($fp)) {
-            $line = fread($fp, 8192);
-            switch ($type) {
-            case '8bit':
-                if (Horde_Mime::is8bit($line)) {
-                    return true;
-                }
-                break;
-
-            case 'binary':
-                if (strpos($line, "\0") !== false) {
-                    return true;
-                }
-                break;
-
-            case 'preg':
-                if (preg_match($data, $line)) {
-                    return true;
-                }
-                break;
-            }
-        }
-
-        return false;
-    }
-
-    /**
-     * Attempts to build a Horde_Mime_Part object from message text.
-     * This function can be called statically via:
-     *    $mime_part = Horde_Mime_Part::parseMessage();
-     *
-     * @param string $text  The text of the MIME message.
-     * @param array $opts   Additional options:
-     *   - forcemime: (boolean) If true, the message data is assumed to be
-     *                MIME data. If not, a MIME-Version header must exist (RFC
-     *                2045 [4]) to be parsed as a MIME message.
-     *                DEFAULT: false
-     *   - level: (integer) Current nesting level of the MIME data.
-     *            DEFAULT: 0
-     *   - no_body: (boolean) If true, don't set body contents of parts (since
-     *              2.2.0).
-     *              DEFAULT: false
-     *
-     * @return Horde_Mime_Part  A MIME Part object.
-     * @throws Horde_Mime_Exception
-     */
-    static public function parseMessage($text, array $opts = array())
-    {
-        /* Find the header. */
-        list($hdr_pos, $eol) = self::_findHeader($text);
-
-        unset($opts['ctype']);
-        $ob = self::_getStructure(substr($text, 0, $hdr_pos), substr($text, $hdr_pos + $eol), $opts);
-        $ob->buildMimeIds();
-        return $ob;
-    }
-
-    /**
-     * Creates a MIME object from the text of one part of a MIME message.
-     *
-     * @param string $header  The header text.
-     * @param string $body    The body text.
-     * @param array $opts     Additional options:
-     * <pre>
-     *   - ctype: (string) The default content-type.
-     *   - forcemime: (boolean) If true, the message data is assumed to be
-     *                MIME data. If not, a MIME-Version header must exist to
-     *                be parsed as a MIME message.
-     *   - level: (integer) Current nesting level.
-     *   - no_body: (boolean) If true, don't set body contents of parts.
-     * </pre>
-     *
-     * @return Horde_Mime_Part  The MIME part object.
-     */
-    static protected function _getStructure($header, $body,
-                                            array $opts = array())
-    {
-        $opts = array_merge(array(
-            'ctype' => 'application/octet-stream',
-            'forcemime' => false,
-            'level' => 0,
-            'no_body' => false
-        ), $opts);
-
-        /* Parse headers text into a Horde_Mime_Headers object. */
-        $hdrs = Horde_Mime_Headers::parseHeaders($header);
-
-        $ob = new Horde_Mime_Part();
-
-        /* This is not a MIME message. */
-        if (!$opts['forcemime'] && !$hdrs->getValue('mime-version')) {
-            $ob->setType('text/plain');
-
-            if (!$opts['no_body'] && !empty($body)) {
-                $ob->setContents($body);
-            }
-
-            return $ob;
-        }
-
-        /* Content type. */
-        if ($tmp = $hdrs->getValue('content-type', Horde_Mime_Headers::VALUE_BASE)) {
-            $ob->setType($tmp);
-
-            $ctype_params = $hdrs->getValue('content-type', Horde_Mime_Headers::VALUE_PARAMS);
-            foreach ($ctype_params as $key => $val) {
-                $ob->setContentTypeParameter($key, $val);
-            }
-        } else {
-            $ob->setType($opts['ctype']);
-        }
-
-        /* Content transfer encoding. */
-        if ($tmp = $hdrs->getValue('content-transfer-encoding')) {
-            $ob->setTransferEncoding($tmp);
-        }
-
-        /* Content-Description. */
-        if ($tmp = $hdrs->getValue('content-description')) {
-            $ob->setDescription($tmp);
-        }
-
-        /* Content-Disposition. */
-        if ($tmp = $hdrs->getValue('content-disposition', Horde_Mime_Headers::VALUE_BASE)) {
-            $ob->setDisposition($tmp);
-            foreach ($hdrs->getValue('content-disposition', Horde_Mime_Headers::VALUE_PARAMS) as $key => $val) {
-                $ob->setDispositionParameter($key, $val);
-            }
-        }
-
-        /* Content-Duration */
-        if ($tmp = $hdrs->getValue('content-duration')) {
-            $ob->setDuration($tmp);
-        }
-
-        /* Content-ID. */
-        if ($tmp = $hdrs->getValue('content-id')) {
-            $ob->setContentId($tmp);
-        }
-
-        if (!$opts['no_body'] &&
-            !empty($body) &&
-            ($ob->getPrimaryType() != 'multipart')) {
-            $ob->setContents($body);
-        }
-
-        if (++$opts['level'] >= self::NESTING_LIMIT) {
-            return $ob;
-        }
-
-        /* Process subparts. */
-        switch ($ob->getPrimaryType()) {
-        case 'message':
-            if ($ob->getSubType() == 'rfc822') {
-                $ob->addPart(self::parseMessage($body, array('forcemime' => true)));
-            }
-            break;
-
-        case 'multipart':
-            $boundary = $ob->getContentTypeParameter('boundary');
-            if (!is_null($boundary)) {
-                foreach (self::_findBoundary($body, 0, $boundary) as $val) {
-                    $subpart = substr($body, $val['start'], $val['length']);
-                    list($hdr_pos, $eol) = self::_findHeader($subpart);
-                    $ob->addPart(self::_getStructure(substr($subpart, 0, $hdr_pos), substr($subpart, $hdr_pos + $eol), array(
-                        'ctype' => ($ob->getSubType() == 'digest') ? 'message/rfc822' : 'text/plain',
-                        'forcemime' => true,
-                        'level' => $opts['level'],
-                        'no_body' => $opts['no_body']
-                    )));
-                }
-            }
-            break;
-        }
-
-        return $ob;
-    }
-
-    /**
-     * Attempts to obtain the raw text of a MIME part.
-     * This function can be called statically via:
-     *    $data = Horde_Mime_Part::getRawPartText();
-     *
-     * @param mixed $text   The full text of the MIME message. The text is
-     *                      assumed to be MIME data (no MIME-Version checking
-     *                      is performed). It can be either a stream or a
-     *                      string.
-     * @param string $type  Either 'header' or 'body'.
-     * @param string $id    The MIME ID.
-     *
-     * @return string  The raw text.
-     * @throws Horde_Mime_Exception
-     */
-    static public function getRawPartText($text, $type, $id)
-    {
-        /* Mini-hack to get a blank Horde_Mime part so we can call
-         * replaceEOL(). From an API perspective, getRawPartText() should be
-         * static since it is not working on MIME part data. */
-        $part = new Horde_Mime_Part();
-        $rawtext = $part->replaceEOL($text, self::RFC_EOL);
-
-        /* We need to carry around the trailing "\n" because this is needed
-         * to correctly find the boundary string. */
-        list($hdr_pos, $eol) = self::_findHeader($rawtext);
-        $curr_pos = $hdr_pos + $eol - 1;
-
-        if ($id == 0) {
-            switch ($type) {
-            case 'body':
-                return substr($rawtext, $curr_pos + 1);
-
-            case 'header':
-                return trim(substr($rawtext, 0, $hdr_pos));
-            }
-        }
-
-        $hdr_ob = Horde_Mime_Headers::parseHeaders(trim(substr($rawtext, 0, $hdr_pos)));
-
-        /* If this is a message/rfc822, pass the body into the next loop.
-         * Don't decrement the ID here. */
-        if ($hdr_ob->getValue('Content-Type', Horde_Mime_Headers::VALUE_BASE) == 'message/rfc822') {
-            return self::getRawPartText(substr($rawtext, $curr_pos + 1), $type, $id);
-        }
-
-        $base_pos = strpos($id, '.');
-        $orig_id = $id;
-
-        if ($base_pos !== false) {
-            $base_pos = substr($id, 0, $base_pos);
-            $id = substr($id, $base_pos);
-        } else {
-            $base_pos = $id;
-            $id = 0;
-        }
-
-        $params = $hdr_ob->getValue('Content-Type', Horde_Mime_Headers::VALUE_PARAMS);
-        if (!isset($params['boundary'])) {
-            if ($orig_id == '1') {
-                return substr($rawtext, $curr_pos + 1);
-            }
-
-            throw new Horde_Mime_Exception('Could not find MIME part.');
-        }
-
-        $b_find = self::_findBoundary($rawtext, $curr_pos, $params['boundary'], $base_pos);
-
-        if (!isset($b_find[$base_pos])) {
-            throw new Horde_Mime_Exception('Could not find MIME part.');
-        }
-
-        return self::getRawPartText(substr($rawtext, $b_find[$base_pos]['start'], $b_find[$base_pos]['length'] - 1), $type, $id);
-    }
-
-    /**
-     * Find the location of the end of the header text.
-     *
-     * @param string $text  The text to search.
-     *
-     * @return array  1st element: Header position, 2nd element: Length of
-     *                trailing EOL.
-     */
-    static protected function _findHeader($text)
-    {
-        $hdr_pos = strpos($text, "\r\n\r\n");
-        if ($hdr_pos !== false) {
-            return array($hdr_pos, 4);
-        }
-
-        $hdr_pos = strpos($text, "\n\n");
-        return ($hdr_pos === false)
-            ? array(strlen($text), 0)
-            : array($hdr_pos, 2);
-    }
-
-    /**
-     * Find the location of the next boundary string.
-     *
-     * @param string $text      The text to search.
-     * @param integer $pos      The current position in $text.
-     * @param string $boundary  The boundary string.
-     * @param integer $end      If set, return after matching this many
-     *                          boundaries.
-     *
-     * @return array  Keys are the boundary number, values are an array with
-     *                two elements: 'start' and 'length'.
-     */
-    static protected function _findBoundary($text, $pos, $boundary,
-                                            $end = null)
-    {
-        $i = 0;
-        $out = array();
-
-        $search = "--" . $boundary;
-        $search_len = strlen($search);
-
-        while (($pos = strpos($text, $search, $pos)) !== false) {
-            /* Boundary needs to appear at beginning of string or right after
-             * a LF. */
-            if (($pos != 0) && ($text[$pos - 1] != "\n")) {
-                continue;
-            }
-
-            if (isset($out[$i])) {
-                $out[$i]['length'] = $pos - $out[$i]['start'] - 1;
-            }
-
-            if (!is_null($end) && ($end == $i)) {
-                break;
-            }
-
-            $pos += $search_len;
-            if (isset($text[$pos])) {
-                switch ($text[$pos]) {
-                case "\r":
-                    $pos += 2;
-                    $out[++$i] = array('start' => $pos);
-                    break;
-
-                case "\n":
-                    $out[++$i] = array('start' => ++$pos);
-                    break;
-
-                case '-':
-                    return $out;
-                }
-            }
-        }
-
-        return $out;
-    }
-
-    /* ArrayAccess methods. */
-
-    public function offsetExists($offset)
-    {
-        return ($this->getPart($offset) !== null);
-    }
-
-    public function offsetGet($offset)
-    {
-        return $this->getPart($offset);
-    }
-
-    public function offsetSet($offset, $value)
-    {
-        $this->alterPart($offset, $value);
-    }
-
-    public function offsetUnset($offset)
-    {
-        $this->removePart($offset);
-    }
-
-    /* Countable methods. */
-
-    /**
-     * Returns the number of message parts.
-     *
-     * @return integer  Number of message parts.
-     */
-    public function count()
-    {
-        return count($this->_parts);
-    }
-
-    /* Serializable methods. */
-
-    /**
-     * Serialization.
-     *
-     * @return string  Serialized data.
-     */
-    public function serialize()
-    {
-        $data = array(
-            // Serialized data ID.
-            self::VERSION
-        );
-
-        foreach ($this->_serializedVars as $val) {
-            switch ($val) {
-            case '_contentTypeParams':
-                $data[] = $this->$val->getArrayCopy();
-                break;
-
-            default:
-                $data[] = $this->$val;
-                break;
-            }
-        }
-
-        if (!empty($this->_contents)) {
-            $data[] = $this->_readStream($this->_contents);
-        }
-
-        return serialize($data);
-    }
-
-    /**
-     * Unserialization.
-     *
-     * @param string $data  Serialized data.
-     *
-     * @throws Exception
-     */
-    public function unserialize($data)
-    {
-        $data = @unserialize($data);
-        if (!is_array($data) ||
-            !isset($data[0]) ||
-            (array_shift($data) != self::VERSION)) {
-            throw new Exception('Cache version change');
-        }
-
-        $this->_init();
-
-        foreach ($this->_serializedVars as $key => $val) {
-            switch ($val) {
-            case '_contentTypeParams':
-                $this->$val = new Horde_Support_CaseInsensitiveArray($data[$key]);
-                break;
-
-            default:
-                $this->$val = $data[$key];
-                break;
-            }
-        }
-
-        // $key now contains the last index of _serializedVars.
-        if (isset($data[++$key])) {
-            $this->setContents($data[$key]);
-        }
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeStreamFilterEolphpfromrev22362013codebykatpostbyemailtrunkincludeHordeStreamFilterEolphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Stream/Filter/Eol.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Stream-Filter-Eol.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Stream/Filter/Eol.php                           (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Stream/Filter/Eol.php      2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,84 @@
</span><ins>+<?php
+/**
+ * Copyright 2009-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @category  Horde
+ * @copyright 2009-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Stream_Filter
+ */
+
+/**
+ * Stream filter class to convert EOL characters.
+ *
+ * Usage:
+ *   stream_filter_register('horde_eol', 'Horde_Stream_Filter_Eol');
+ *   stream_filter_[app|pre]pend($stream, 'horde_eol',
+ *                               [ STREAM_FILTER_[READ|WRITE|ALL] ],
+ *                               [ $params ]);
+ *
+ * $params is an array that can contain the following:
+ *   - eol: (string) The EOL string to use.
+ *          DEFAULT: <CR><LF> ("\r\n")
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2009-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package   Stream_Filter
+ */
+class Horde_Stream_Filter_Eol extends php_user_filter
+{
+    /**
+     * Search array.
+     *
+     * @param mixed
+     */
+    protected $_search;
+
+    /**
+     * Replacement data
+     *
+     * @param mixed
+     */
+    protected $_replace;
+
+    /**
+     * @see stream_filter_register()
+     */
+    public function onCreate()
+    {
+        $eol = isset($this->params['eol']) ? $this->params['eol'] : "\r\n";
+
+        if (!strlen($eol)) {
+            $this->_search = array("\r", "\n");
+            $this->_replace = '';
+        } elseif (in_array($eol, array("\r", "\n"))) {
+            $this->_search = array("\r\n", ($eol == "\r") ? "\n" : "\r");
+            $this->_replace = $eol;
+        } else {
+            $this->_search = array("\r\n", "\r", "\n");
+            $this->_replace = array("\n", "\n", $eol);
+        }
+
+        return true;
+    }
+
+    /**
+     * @see stream_filter_register()
+     */
+    public function filter($in, $out, &$consumed, $closing)
+    {
+        while ($bucket = stream_bucket_make_writeable($in)) {
+            $bucket->data = str_replace($this->_search, $this->_replace, $bucket->data);
+            $consumed += $bucket->datalen;
+            stream_bucket_append($out, $bucket);
+        }
+
+        return PSFS_PASS_ON;
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeStreamFilterEolphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Stream-Filter-Eol.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Stream-Filter-Eol.php   2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Stream-Filter-Eol.php      2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,84 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2009-2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.horde.org/licenses/lgpl21.
- *
- * @category  Horde
- * @copyright 2009-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Stream_Filter
- */
-
-/**
- * Stream filter class to convert EOL characters.
- *
- * Usage:
- *   stream_filter_register('horde_eol', 'Horde_Stream_Filter_Eol');
- *   stream_filter_[app|pre]pend($stream, 'horde_eol',
- *                               [ STREAM_FILTER_[READ|WRITE|ALL] ],
- *                               [ $params ]);
- *
- * $params is an array that can contain the following:
- *   - eol: (string) The EOL string to use.
- *          DEFAULT: <CR><LF> ("\r\n")
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2009-2013 Horde LLC
- * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
- * @package   Stream_Filter
- */
-class Horde_Stream_Filter_Eol extends php_user_filter
-{
-    /**
-     * Search array.
-     *
-     * @param mixed
-     */
-    protected $_search;
-
-    /**
-     * Replacement data
-     *
-     * @param mixed
-     */
-    protected $_replace;
-
-    /**
-     * @see stream_filter_register()
-     */
-    public function onCreate()
-    {
-        $eol = isset($this->params['eol']) ? $this->params['eol'] : "\r\n";
-
-        if (!strlen($eol)) {
-            $this->_search = array("\r", "\n");
-            $this->_replace = '';
-        } elseif (in_array($eol, array("\r", "\n"))) {
-            $this->_search = array("\r\n", ($eol == "\r") ? "\n" : "\r");
-            $this->_replace = $eol;
-        } else {
-            $this->_search = array("\r\n", "\r", "\n");
-            $this->_replace = array("\n", "\n", $eol);
-        }
-
-        return true;
-    }
-
-    /**
-     * @see stream_filter_register()
-     */
-    public function filter($in, $out, &$consumed, $closing)
-    {
-        while ($bucket = stream_bucket_make_writeable($in)) {
-            $bucket->data = str_replace($this->_search, $this->_replace, $bucket->data);
-            $consumed += $bucket->datalen;
-            stream_bucket_append($out, $bucket);
-        }
-
-        return PSFS_PASS_ON;
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeStubphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Stub.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Stub.php        2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Stub.php   2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,94 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2008-2013 Horde LLC (http://www.horde.org/)
- *
- * @category  Horde
- * @copyright 2008-2013 Horde LLC
- * @license   http://www.horde.org/licenses/bsd BSD
- * @package   Support
- */
-
-/**
- * Class that can substitute for any object and safely do nothing.
- *
- * @category  Horde
- * @copyright 2008-2013 Horde LLC
- * @license   http://www.horde.org/licenses/bsd BSD
- * @package   Support
- */
-class Horde_Support_Stub
-{
-    /**
-     * Cooerce to an empty string.
-     *
-     * @return string
-     */
-    public function __toString()
-    {
-        return '';
-    }
-
-    /**
-     * Ignore setting the requested property.
-     *
-     * @param string $key  The property.
-     * @param mixed $val   The property's value.
-     */
-    public function __set($key, $val)
-    {
-    }
-
-    /**
-     * Return null for any requested property.
-     *
-     * @param string $key  The requested object property.
-     *
-     * @return null  Null.
-     */
-    public function __get($key)
-    {
-        return null;
-    }
-
-    /**
-     * Property existence.
-     *
-     * @param string $key  The requested object property.
-     *
-     * @return boolean  False.
-     */
-    public function __isset($key)
-    {
-        return false;
-    }
-
-    /**
-     * Ignore unsetting a property.
-     *
-     * @param string $key  The requested object property.
-     */
-    public function __unset($key)
-    {
-    }
-
-    /**
-     * Gracefully accept any method call and do nothing.
-     *
-     * @param string $method  The method that was called.
-     * @param array $args     The method's arguments.
-     */
-    public function __call($method, $args)
-    {
-    }
-
-    /**
-     * Gracefully accept any static method call and do nothing.
-     *
-     * @param string $method  The method that was called.
-     * @param array $args     The method's arguments.
-     */
-    public static function __callStatic($method, $args)
-    {
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeSupportCaseInsensitiveArrayphpfromrev22362013codebykatpostbyemailtrunkincludeHordeSupportCaseInsensitiveArrayphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Support/CaseInsensitiveArray.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Support-CaseInsensitiveArray.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Support/CaseInsensitiveArray.php                                (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Support/CaseInsensitiveArray.php   2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,81 @@
</span><ins>+<?php
+/**
+ * Copyright 2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (BSD). If you
+ * did not receive this file, see http://www.horde.org/licenses/bsd.
+ *
+ * @category  Horde
+ * @copyright 2013 Horde LLC
+ * @license   http://www.horde.org/licenses/bsd BSD
+ * @package   Support
+ */
+
+/**
+ * An array implemented as an object that contains case-insensitive keys.
+ *
+ * @author    Michael Slusarz <slusarz@horde.org>
+ * @category  Horde
+ * @copyright 2013 Horde LLC
+ * @license   http://www.horde.org/licenses/bsd BSD
+ * @package   Support
+ */
+class Horde_Support_CaseInsensitiveArray extends ArrayIterator
+{
+    /**
+     */
+    public function offsetGet($offset)
+    {
+        return (is_null($offset = $this->_getRealOffset($offset)))
+            ? null
+            : parent::offsetGet($offset);
+    }
+
+    /**
+     */
+    public function offsetSet($offset, $value)
+    {
+        if (is_null($roffset = $this->_getRealOffset($offset))) {
+            parent::offsetSet($offset, $value);
+        } else {
+            parent::offsetSet($roffset, $value);
+        }
+    }
+
+    /**
+     */
+    public function offsetExists($offset)
+    {
+        return (is_null($offset = $this->_getRealOffset($offset)))
+            ? false
+            : parent::offsetExists($offset);
+    }
+
+    /**
+     */
+    public function offsetUnset($offset)
+    {
+        if (!is_null($offset = $this->_getRealOffset($offset))) {
+            parent::offsetUnset($offset);
+        }
+    }
+
+    /**
+     * Determines the actual array offset given the input offset.
+     *
+     * @param string $offset  Input offset.
+     *
+     * @return string  Real offset or null.
+     */
+    protected function _getRealOffset($offset)
+    {
+        foreach (array_keys($this->getArrayCopy()) as $key) {
+            if (strcasecmp($key, $offset) === 0) {
+                return $key;
+            }
+        }
+
+        return null;
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeSupportRandomidphpfromrev22362013codebykatpostbyemailtrunkincludeHordeSupportRandomidphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Support/Randomid.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Support-Randomid.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Support/Randomid.php                            (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Support/Randomid.php       2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,74 @@
</span><ins>+<?php
+/**
+ * Class for generating a 23-character random ID string. This string uses all
+ * characters in the class [-_0-9a-zA-Z].
+ *
+ * <code>
+ * $id = (string)new Horde_Support_Randomid();
+ * </code>
+ *
+ * Copyright 2010-2013 Horde LLC (http://www.horde.org/)
+ *
+ * @author   Michael Slusarz <slusarz@horde.org>
+ * @category Horde
+ * @license  http://www.horde.org/licenses/bsd BSD
+ * @package  Support
+ */
+class Horde_Support_Randomid
+{
+    /**
+     * Generated ID.
+     *
+     * @var string
+     */
+    private $_id;
+
+    /**
+     * New random ID.
+     */
+    public function __construct()
+    {
+        $this->_id = $this->generate();
+    }
+
+    /**
+     * Generate a random ID.
+     */
+    public function generate()
+    {
+        $r = mt_rand();
+
+        $elts = array(
+            $r,
+            uniqid(),
+            getmypid()
+        );
+        if (function_exists('zend_thread_id')) {
+            $elts[] = zend_thread_id();
+        }
+        if (function_exists('sys_getloadavg') &&
+            $loadavg = sys_getloadavg()) {
+            $elts = array_merge($elts, $loadavg);
+        }
+
+        shuffle($elts);
+
+        /* Base64 can have /, +, and = characters. Restrict to URL-safe
+         * characters. */
+        return substr(str_replace(
+            array('/', '+', '='),
+            array('-', '_', ''),
+            base64_encode(pack('H*', hash('md5', implode('', $elts))))
+        ) . $r, 0, 23);
+    }
+
+    /**
+     * Cooerce to string.
+     *
+     * @return string  The random ID.
+     */
+    public function __toString()
+    {
+        return $this->_id;
+    }
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeSupportStubphpfromrev22362013codebykatpostbyemailtrunkincludeHordeStubphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Support/Stub.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Stub.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Support/Stub.php                                (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Support/Stub.php   2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,94 @@
</span><ins>+<?php
+/**
+ * Copyright 2008-2013 Horde LLC (http://www.horde.org/)
+ *
+ * @category  Horde
+ * @copyright 2008-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/bsd BSD
+ * @package   Support
+ */
+
+/**
+ * Class that can substitute for any object and safely do nothing.
+ *
+ * @category  Horde
+ * @copyright 2008-2013 Horde LLC
+ * @license   http://www.horde.org/licenses/bsd BSD
+ * @package   Support
+ */
+class Horde_Support_Stub
+{
+    /**
+     * Cooerce to an empty string.
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        return '';
+    }
+
+    /**
+     * Ignore setting the requested property.
+     *
+     * @param string $key  The property.
+     * @param mixed $val   The property's value.
+     */
+    public function __set($key, $val)
+    {
+    }
+
+    /**
+     * Return null for any requested property.
+     *
+     * @param string $key  The requested object property.
+     *
+     * @return null  Null.
+     */
+    public function __get($key)
+    {
+        return null;
+    }
+
+    /**
+     * Property existence.
+     *
+     * @param string $key  The requested object property.
+     *
+     * @return boolean  False.
+     */
+    public function __isset($key)
+    {
+        return false;
+    }
+
+    /**
+     * Ignore unsetting a property.
+     *
+     * @param string $key  The requested object property.
+     */
+    public function __unset($key)
+    {
+    }
+
+    /**
+     * Gracefully accept any method call and do nothing.
+     *
+     * @param string $method  The method that was called.
+     * @param array $args     The method's arguments.
+     */
+    public function __call($method, $args)
+    {
+    }
+
+    /**
+     * Gracefully accept any static method call and do nothing.
+     *
+     * @param string $method  The method that was called.
+     * @param array $args     The method's arguments.
+     */
+    public static function __callStatic($method, $args)
+    {
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeSupportCaseInsensitiveArrayphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Support-CaseInsensitiveArray.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Support-CaseInsensitiveArray.php        2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Support-CaseInsensitiveArray.php   2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,81 +0,0 @@
</span><del>-<?php
-/**
- * Copyright 2013 Horde LLC (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (BSD). If you
- * did not receive this file, see http://www.horde.org/licenses/bsd.
- *
- * @category  Horde
- * @copyright 2013 Horde LLC
- * @license   http://www.horde.org/licenses/bsd BSD
- * @package   Support
- */
-
-/**
- * An array implemented as an object that contains case-insensitive keys.
- *
- * @author    Michael Slusarz <slusarz@horde.org>
- * @category  Horde
- * @copyright 2013 Horde LLC
- * @license   http://www.horde.org/licenses/bsd BSD
- * @package   Support
- */
-class Horde_Support_CaseInsensitiveArray extends ArrayIterator
-{
-    /**
-     */
-    public function offsetGet($offset)
-    {
-        return (is_null($offset = $this->_getRealOffset($offset)))
-            ? null
-            : parent::offsetGet($offset);
-    }
-
-    /**
-     */
-    public function offsetSet($offset, $value)
-    {
-        if (is_null($roffset = $this->_getRealOffset($offset))) {
-            parent::offsetSet($offset, $value);
-        } else {
-            parent::offsetSet($roffset, $value);
-        }
-    }
-
-    /**
-     */
-    public function offsetExists($offset)
-    {
-        return (is_null($offset = $this->_getRealOffset($offset)))
-            ? false
-            : parent::offsetExists($offset);
-    }
-
-    /**
-     */
-    public function offsetUnset($offset)
-    {
-        if (!is_null($offset = $this->_getRealOffset($offset))) {
-            parent::offsetUnset($offset);
-        }
-    }
-
-    /**
-     * Determines the actual array offset given the input offset.
-     *
-     * @param string $offset  Input offset.
-     *
-     * @return string  Real offset or null.
-     */
-    protected function _getRealOffset($offset)
-    {
-        foreach (array_keys($this->getArrayCopy()) as $key) {
-            if (strcasecmp($key, $offset) === 0) {
-                return $key;
-            }
-        }
-
-        return null;
-    }
-
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeSupportRandomidphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/Horde/Support-Randomid.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Support-Randomid.php    2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Support-Randomid.php       2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,74 +0,0 @@
</span><del>-<?php
-/**
- * Class for generating a 23-character random ID string. This string uses all
- * characters in the class [-_0-9a-zA-Z].
- *
- * <code>
- * $id = (string)new Horde_Support_Randomid();
- * </code>
- *
- * Copyright 2010-2013 Horde LLC (http://www.horde.org/)
- *
- * @author   Michael Slusarz <slusarz@horde.org>
- * @category Horde
- * @license  http://www.horde.org/licenses/bsd BSD
- * @package  Support
- */
-class Horde_Support_Randomid
-{
-    /**
-     * Generated ID.
-     *
-     * @var string
-     */
-    private $_id;
-
-    /**
-     * New random ID.
-     */
-    public function __construct()
-    {
-        $this->_id = $this->generate();
-    }
-
-    /**
-     * Generate a random ID.
-     */
-    public function generate()
-    {
-        $r = mt_rand();
-
-        $elts = array(
-            $r,
-            uniqid(),
-            getmypid()
-        );
-        if (function_exists('zend_thread_id')) {
-            $elts[] = zend_thread_id();
-        }
-        if (function_exists('sys_getloadavg') &&
-            $loadavg = sys_getloadavg()) {
-            $elts = array_merge($elts, $loadavg);
-        }
-
-        shuffle($elts);
-
-        /* Base64 can have /, +, and = characters. Restrict to URL-safe
-         * characters. */
-        return substr(str_replace(
-            array('/', '+', '='),
-            array('-', '_', ''),
-            base64_encode(pack('H*', hash('md5', implode('', $elts))))
-        ) . $r, 0, 23);
-    }
-
-    /**
-     * Cooerce to string.
-     *
-     * @return string  The random ID.
-     */
-    public function __toString()
-    {
-        return $this->_id;
-    }
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeHordeUtilphpfromrev22362013codebykatpostbyemailtrunkincludeHordeHordeUtilphp"></a>
<div class="copfile"><h4>Copied: 2013/codebykat/post-by-email/trunk/include/Horde/Util.php (from rev 2236, 2013/codebykat/post-by-email/trunk/include/Horde/Horde-Util.php) (0 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/Horde/Util.php                                (rev 0)
+++ 2013/codebykat/post-by-email/trunk/include/Horde/Util.php   2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -0,0 +1,564 @@
</span><ins>+<?php
+/**
+ * The Horde_Util:: class provides generally useful methods.
+ *
+ * Copyright 1999-2013 Horde LLC (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.horde.org/licenses/lgpl21.
+ *
+ * @author   Chuck Hagenbuch <chuck@horde.org>
+ * @author   Jon Parise <jon@horde.org>
+ * @category Horde
+ * @license  http://www.horde.org/licenses/lgpl21 LGPL 2.1
+ * @package  Util
+ */
+class Horde_Util
+{
+    /**
+     * A list of random patterns to use for overwriting purposes.
+     * See http://www.cs.auckland.ac.nz/~pgut001/pubs/secure_del.html.
+     * We save the random overwrites for efficiency reasons.
+     *
+     * @var array
+     */
+    static public $patterns = array(
+        "\x55", "\xaa", "\x92\x49\x24", "\x49\x24\x92", "\x24\x92\x49",
+        "\x00", "\x11", "\x22", "\x33", "\x44", "\x55", "\x66", "\x77",
+        "\x88", "\x99", "\xaa", "\xbb", "\xcc", "\xdd", "\xee", "\xff",
+        "\x92\x49\x24", "\x49\x24\x92", "\x24\x92\x49", "\x6d\xb6\xdb",
+        "\xb6\xdb\x6d", "\xdb\x6d\xb6"
+    );
+
+    /**
+     * Are magic quotes in use?
+     *
+     * @var boolean
+     */
+    static protected $_magicquotes = null;
+
+    /**
+     * TODO
+     *
+     * @var array
+     */
+    static protected $_shutdowndata = array(
+        'dirs' => array(),
+        'files' => array(),
+        'secure' => array()
+    );
+
+    /**
+     * Has the shutdown method been registered?
+     *
+     * @var boolean
+     */
+    static protected $_shutdownreg = false;
+
+    /**
+     * Cache for extensionExists().
+     *
+     * @var array
+     */
+    static protected $_cache = array();
+
+    /**
+     * Checks to see if a value has been set by the script and not by GET,
+     * POST, or cookie input. The value being checked MUST be in the global
+     * scope.
+     *
+     * @param string $varname  The variable name to check.
+     * @param mixed $default   Default value if the variable isn't present
+     *                         or was specified by the user. Defaults to null.
+     *
+     * @return mixed  $default if the var is in user input or not present,
+     *                the variable value otherwise.
+     */
+    static public function nonInputVar($varname, $default = null)
+    {
+        return (isset($_GET[$varname]) || isset($_POST[$varname]) || isset($_COOKIE[$varname]))
+            ? $default
+            : (isset($GLOBALS[$varname]) ? $GLOBALS[$varname] : $default);
+    }
+
+    /**
+     * Returns a hidden form input containing the session name and id.
+     *
+     * @param boolean $append_session  0 = only if needed, 1 = always.
+     *
+     * @return string  The hidden form input, if needed/requested.
+     */
+    static public function formInput($append_session = 0)
+    {
+        return (($append_session == 1) || !isset($_COOKIE[session_name()]))
+            ? '<input type="hidden" name="' . htmlspecialchars(session_name()) . '" value="' . htmlspecialchars(session_id()) . "\" />\n"
+            : '';
+    }
+
+    /**
+     * Prints a hidden form input containing the session name and id.
+     *
+     * @param boolean $append_session  0 = only if needed, 1 = always.
+     */
+    static public function pformInput($append_session = 0)
+    {
+        echo self::formInput($append_session);
+    }
+
+    /**
+     * If magic_quotes_gpc is in use, run stripslashes() on $var.
+     *
+     * @param mixed $var  The string, or an array of strings, to un-quote.
+     *
+     * @return mixed  $var, minus any magic quotes.
+     */
+    static public function dispelMagicQuotes($var)
+    {
+        if (is_null(self::$_magicquotes)) {
+            self::$_magicquotes = get_magic_quotes_gpc();
+        }
+
+        if (self::$_magicquotes) {
+            $var = is_array($var)
+                ? array_map(array(__CLASS__, 'dispelMagicQuotes'), $var)
+                : stripslashes($var);
+        }
+
+        return $var;
+    }
+
+    /**
+     * Gets a form variable from GET or POST data, stripped of magic quotes if
+     * necessary. If the variable is somehow set in both the GET data and the
+     * POST data, the value from the POST data will be returned and the GET
+     * value will be ignored.
+     *
+     * @param string $var      The name of the form variable to look for.
+     * @param string $default  The value to return if the variable is not
+     *                         there.
+     *
+     * @return string  The cleaned form variable, or $default.
+     */
+    static public function getFormData($var, $default = null)
+    {
+        return (($val = self::getPost($var)) !== null)
+            ? $val
+            : self::getGet($var, $default);
+    }
+
+    /**
+     * Gets a form variable from GET data, stripped of magic quotes if
+     * necessary. This function will NOT return a POST variable.
+     *
+     * @param string $var      The name of the form variable to look for.
+     * @param string $default  The value to return if the variable is not
+     *                         there.
+     *
+     * @return string  The cleaned form variable, or $default.
+     */
+    static public function getGet($var, $default = null)
+    {
+        return (isset($_GET[$var]))
+            ? self::dispelMagicQuotes($_GET[$var])
+            : $default;
+    }
+
+    /**
+     * Gets a form variable from POST data, stripped of magic quotes if
+     * necessary. This function will NOT return a GET variable.
+     *
+     * @param string $var      The name of the form variable to look for.
+     * @param string $default  The value to return if the variable is not
+     *                         there.
+     *
+     * @return string  The cleaned form variable, or $default.
+     */
+    static public function getPost($var, $default = null)
+    {
+        return (isset($_POST[$var]))
+            ? self::dispelMagicQuotes($_POST[$var])
+            : $default;
+    }
+
+    /**
+     * Creates a temporary filename for the lifetime of the script, and
+     * (optionally) registers it to be deleted at request shutdown.
+     *
+     * @param string $prefix   Prefix to make the temporary name more
+     *                         recognizable.
+     * @param boolean $delete  Delete the file at the end of the request?
+     * @param string $dir      Directory to create the temporary file in.
+     * @param boolean $secure  If deleting the file, should we securely delete
+     *                         the file by overwriting it with random data?
+     *
+     * @return string   Returns the full path-name to the temporary file.
+     *                  Returns false if a temp file could not be created.
+     */
+    static public function getTempFile($prefix = '', $delete = true, $dir = '',
+                                       $secure = false)
+    {
+        $tempDir = (empty($dir) || !is_dir($dir))
+            ? sys_get_temp_dir()
+            : $dir;
+
+        $tempFile = tempnam($tempDir, $prefix);
+
+        // If the file was created, then register it for deletion and return.
+        if (empty($tempFile)) {
+            return false;
+        }
+
+        if ($delete) {
+            self::deleteAtShutdown($tempFile, true, $secure);
+        }
+
+        return $tempFile;
+    }
+
+    /**
+     * Creates a temporary filename with a specific extension for the lifetime
+     * of the script, and (optionally) registers it to be deleted at request
+     * shutdown.
+     *
+     * @param string $extension  The file extension to use.
+     * @param string $prefix     Prefix to make the temporary name more
+     *                           recognizable.
+     * @param boolean $delete    Delete the file at the end of the request?
+     * @param string $dir        Directory to create the temporary file in.
+     * @param boolean $secure    If deleting file, should we securely delete
+     *                           the file by overwriting it with random data?
+     *
+     * @return string   Returns the full path-name to the temporary file.
+     *                  Returns false if a temporary file could not be created.
+     */
+    static public function getTempFileWithExtension($extension = '.tmp',
+                                                    $prefix = '',
+                                                    $delete = true, $dir = '',
+                                                    $secure = false)
+    {
+        $tempDir = (empty($dir) || !is_dir($dir))
+            ? sys_get_temp_dir()
+            : $dir;
+
+        if (empty($tempDir)) {
+            return false;
+        }
+
+        $windows = substr(PHP_OS, 0, 3) == 'WIN';
+        $tries = 1;
+        do {
+            // Get a known, unique temporary file name.
+            $sysFileName = tempnam($tempDir, $prefix);
+            if ($sysFileName === false) {
+                return false;
+            }
+
+            // tack on the extension
+            $tmpFileName = $sysFileName . $extension;
+            if ($sysFileName == $tmpFileName) {
+                return $sysFileName;
+            }
+
+            // Move or point the created temporary file to the full filename
+            // with extension. These calls fail if the new name already
+            // exists.
+            $fileCreated = ($windows ? @rename($sysFileName, $tmpFileName) : @link($sysFileName, $tmpFileName));
+            if ($fileCreated) {
+                if (!$windows) {
+                    unlink($sysFileName);
+                }
+
+                if ($delete) {
+                    self::deleteAtShutdown($tmpFileName, true, $secure);
+                }
+
+                return $tmpFileName;
+            }
+
+            unlink($sysFileName);
+        } while (++$tries <= 5);
+
+        return false;
+    }
+
+    /**
+     * Creates a temporary directory in the system's temporary directory.
+     *
+     * @param boolean $delete   Delete the temporary directory at the end of
+     *                          the request?
+     * @param string $temp_dir  Use this temporary directory as the directory
+     *                          where the temporary directory will be created.
+     *
+     * @return string  The pathname to the new temporary directory.
+     *                 Returns false if directory not created.
+     */
+    static public function createTempDir($delete = true, $temp_dir = null)
+    {
+        if (is_null($temp_dir)) {
+            $temp_dir = sys_get_temp_dir();
+        }
+
+        if (empty($temp_dir)) {
+            return false;
+        }
+
+        /* Get the first 8 characters of a random string to use as a temporary
+           directory name. */
+        do {
+            $new_dir = $temp_dir . '/' . substr(base_convert(uniqid(mt_rand()), 10, 36), 0, 8);
+        } while (file_exists($new_dir));
+
+        $old_umask = umask(0000);
+        if (!mkdir($new_dir, 0700)) {
+            $new_dir = false;
+        } elseif ($delete) {
+            self::deleteAtShutdown($new_dir);
+        }
+        umask($old_umask);
+
+        return $new_dir;
+    }
+
+    /**
+     * Returns the canonical path of the string.  Like PHP's built-in
+     * realpath() except the directory need not exist on the local server.
+     *
+     * Algorithim loosely based on code from the Perl File::Spec::Unix module
+     * (version 1.5).
+     *
+     * @param string $path  A file path.
+     *
+     * @return string  The canonicalized file path.
+     */
+    static public function realPath($path)
+    {
+        /* Standardize on UNIX directory separators. */
+        if (!strncasecmp(PHP_OS, 'WIN', 3)) {
+            $path = str_replace('\\', '/', $path);
+        }
+
+        /* xx////xx -> xx/xx
+         * xx/././xx -> xx/xx */
+        $path = preg_replace(array("|/+|", "@(/\.)+(/|\Z(?!\n))@"), array('/', '/'), $path);
+
+        /* ./xx -> xx */
+        if ($path != './') {
+            $path = preg_replace("|^(\./)+|", '', $path);
+        }
+
+        /* /../../xx -> xx */
+        $path = preg_replace("|^/(\.\./?)+|", '/', $path);
+
+        /* xx/ -> xx */
+        if ($path != '/') {
+            $path = preg_replace("|/\Z(?!\n)|", '', $path);
+        }
+
+        /* /xx/.. -> / */
+        while (strpos($path, '/..') !== false) {
+            $path = preg_replace("|/[^/]+/\.\.|", '', $path);
+        }
+
+        return empty($path) ? '/' : $path;
+    }
+
+    /**
+     * Removes given elements at request shutdown.
+     *
+     * If called with a filename will delete that file at request shutdown; if
+     * called with a directory will remove that directory and all files in that
+     * directory at request shutdown.
+     *
+     * If called with no arguments, return all elements to be deleted (this
+     * should only be done by Horde_Util::_deleteAtShutdown()).
+     *
+     * The first time it is called, it initializes the array and registers
+     * Horde_Util::_deleteAtShutdown() as a shutdown function - no need to do
+     * so manually.
+     *
+     * The second parameter allows the unregistering of previously registered
+     * elements.
+     *
+     * @param string $filename   The filename to be deleted at the end of the
+     *                           request.
+     * @param boolean $register  If true, then register the element for
+     *                           deletion, otherwise, unregister it.
+     * @param boolean $secure    If deleting file, should we securely delete
+     *                           the file?
+     */
+    static public function deleteAtShutdown($filename, $register = true,
+                                            $secure = false)
+    {
+        /* Initialization of variables and shutdown functions. */
+        if (!self::$_shutdownreg) {
+            register_shutdown_function(array(__CLASS__, 'shutdown'));
+            self::$_shutdownreg = true;
+        }
+
+        $ptr = &self::$_shutdowndata;
+        if ($register) {
+            if (@is_dir($filename)) {
+                $ptr['dirs'][$filename] = true;
+            } else {
+                $ptr['files'][$filename] = true;
+            }
+
+            if ($secure) {
+                $ptr['secure'][$filename] = true;
+            }
+        } else {
+            unset($ptr['dirs'][$filename], $ptr['files'][$filename], $ptr['secure'][$filename]);
+        }
+    }
+
+    /**
+     * Deletes registered files at request shutdown.
+     *
+     * This function should never be called manually; it is registered as a
+     * shutdown function by Horde_Util::deleteAtShutdown() and called
+     * automatically at the end of the request.
+     *
+     * Contains code from gpg_functions.php.
+     * Copyright 2002-2003 Braverock Ventures
+     */
+    static public function shutdown()
+    {
+        $ptr = &self::$_shutdowndata;
+
+        foreach ($ptr['files'] as $file => $val) {
+            /* Delete files */
+            if ($val && file_exists($file)) {
+                /* Should we securely delete the file by overwriting the data
+                   with a random string? */
+                if (isset($ptr['secure'][$file])) {
+                    $filesize = filesize($file);
+                    $fp = fopen($file, 'r+');
+                    foreach (self::$patterns as $pattern) {
+                        $pattern = substr(str_repeat($pattern, floor($filesize / strlen($pattern)) + 1), 0, $filesize);
+                        fwrite($fp, $pattern);
+                        fseek($fp, 0);
+                    }
+                    fclose($fp);
+                }
+                @unlink($file);
+            }
+        }
+
+        foreach ($ptr['dirs'] as $dir => $val) {
+            /* Delete directories */
+            if ($val && file_exists($dir)) {
+                /* Make sure directory is empty. */
+                $dir_class = dir($dir);
+                while (false !== ($entry = $dir_class->read())) {
+                    if ($entry != '.' && $entry != '..') {
+                        @unlink($dir . '/' . $entry);
+                    }
+                }
+                $dir_class->close();
+                @rmdir($dir);
+            }
+        }
+    }
+
+    /**
+     * Caches the result of extension_loaded() calls.
+     *
+     * @param string $ext  The extension name.
+     *
+     * @return boolean  Is the extension loaded?
+     */
+    static public function extensionExists($ext)
+    {
+        if (!isset(self::$_cache[$ext])) {
+            self::$_cache[$ext] = extension_loaded($ext);
+        }
+
+        return self::$_cache[$ext];
+    }
+
+    /**
+     * Tries to load a PHP extension, behaving correctly for all operating
+     * systems.
+     *
+     * @param string $ext  The extension to load.
+     *
+     * @return boolean  True if the extension is now loaded, false if not.
+     *                  True can mean that the extension was already loaded,
+     *                  OR was loaded dynamically.
+     */
+    static public function loadExtension($ext)
+    {
+        /* If $ext is already loaded, our work is done. */
+        if (self::extensionExists($ext)) {
+            return true;
+        }
+
+        /* See if we can call dl() at all, by the current ini settings.
+         * dl() has been removed in some PHP 5.3 SAPIs. */
+        if ((ini_get('enable_dl') != 1) ||
+            (ini_get('safe_mode') == 1) ||
+            !function_exists('dl')) {
+            return false;
+        }
+
+        if (!strncasecmp(PHP_OS, 'WIN', 3)) {
+            $suffix = 'dll';
+        } else {
+            switch (PHP_OS) {
+            case 'HP-UX':
+                $suffix = 'sl';
+                break;
+
+            case 'AIX':
+                $suffix = 'a';
+                break;
+
+            case 'OSX':
+                $suffix = 'bundle';
+                break;
+
+            default:
+                $suffix = 'so';
+            }
+        }
+
+        return dl($ext . '.' . $suffix) || dl('php_' . $ext . '.' . $suffix);
+    }
+
+    /**
+     * Utility function to obtain PATH_INFO information.
+     *
+     * @return string  The PATH_INFO string.
+     */
+    static public function getPathInfo()
+    {
+        if (isset($_SERVER['PATH_INFO']) &&
+            (strpos($_SERVER['SERVER_SOFTWARE'], 'lighttpd') === false)) {
+            return $_SERVER['PATH_INFO'];
+        } elseif (isset($_SERVER['REQUEST_URI']) &&
+                  isset($_SERVER['SCRIPT_NAME'])) {
+            $search = Horde_String::common($_SERVER['SCRIPT_NAME'], $_SERVER['REQUEST_URI']);
+            if (substr($search, -1) == '/') {
+                $search = substr($search, 0, -1);
+            }
+            $search = array($search);
+            if (!empty($_SERVER['QUERY_STRING'])) {
+                // We can't use QUERY_STRING directly because URL rewriting
+                // might add more parameters to the query string than those
+                // from the request URI.
+                $url = parse_url($_SERVER['REQUEST_URI']);
+                if (!empty($url['query'])) {
+                    $search[] = '?' . $url['query'];
+                }
+            }
+            $path = str_replace($search, '', $_SERVER['REQUEST_URI']);
+            if ($path == '/') {
+                $path = '';
+            }
+            return $path;
+        }
+
+        return '';
+    }
+
+}
</ins></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludeclasshordeimapclienttranslationphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/class-horde-imap-client-translation.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/class-horde-imap-client-translation.php       2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/class-horde-imap-client-translation.php  2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,37 +0,0 @@
</span><del>-<?php
-/**
- * Bogus translation wrapper class for Horde_Imap_Client.
- *
- * Uses WP __() function instead.
- *
- */
-class Horde_Imap_Client_Translation
-{
-    /**
-     * Returns the translation of a message.
-     *
-     * @var string $message  The string to translate.
-     *
-     * @return string  The string translation, or the original string if no
-     *                 translation exists.
-     */
-    static public function t($message)
-    {
-        return __($message);
-    }
-
-    /**
-     * Returns the plural translation of a message.
-     *
-     * @param string $singular  The singular version to translate.
-     * @param string $plural    The plural version to translate.
-     * @param integer $number   The number that determines singular vs. plural.
-     *
-     * @return string  The string translation, or the original string if no
-     *                 translation exists.
-     */
-    static public function ngettext($singular, $plural, $number)
-    {
-        return _n( $singular, $plural, $number );
-    }
-}
</del></span></pre></div>
<a id="2013codebykatpostbyemailtrunkincludehordewrapperphp"></a>
<div class="delfile"><h4>Deleted: 2013/codebykat/post-by-email/trunk/include/horde-wrapper.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/include/horde-wrapper.php     2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/include/horde-wrapper.php        2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -1,60 +0,0 @@
</span><del>-<?php
-
-/* Wrapper for Horde IMAP Client */
-
-require_once('Horde/Exception.php');
-require_once('Horde/Exception-Wrapped.php');
-
-require_once('Horde/Mail-Rfc822.php');
-require_once('Horde/Mail-Rfc822-Object.php');
-require_once('Horde/Mail-Rfc822-Address.php');
-require_once('Horde/Mail-Rfc822-List.php');
-
-require_once('Horde/Support-CaseInsensitiveArray.php');
-require_once('Horde/Support-Randomid.php');
-
-require_once('Horde/Stream-Filter-Eol.php');
-
-require_once('Horde/Mime.php');
-require_once('Horde/Mime-Headers.php');
-require_once('Horde/Mime-Part.php');
-
-require_once('Horde/Horde-Util.php');
-
-require_once('Horde/Stub.php');
-
-require_once('Horde/String.php');
-
-require_once('class-horde-imap-client-translation.php');
-
-require_once('Horde/Client.php');
-
-require_once('Horde/Client/Base.php');
-require_once('Horde/Client/Base/Mailbox.php');
-require_once('Horde/Client/Base/Connection.php');
-
-require_once('Horde/Client/Data/Fetch.php');
-require_once('Horde/Client/Data/Fetch/Pop3.php');
-
-require_once('Horde/Client/Data/Format.php');
-require_once('Horde/Client/Data/Format/Atom.php');
-require_once('Horde/Client/Data/Format/List.php');
-
-require_once('Horde/Client/Fetch/Query.php');
-require_once('Horde/Client/Fetch/Results.php');
-
-require_once('Horde/Client/Ids.php');
-require_once('Horde/Client/Ids/Map.php');
-require_once('Horde/Client/Ids/Pop3.php');
-
-require_once('Horde/Client/Search/Query.php');
-
-require_once('Horde/Client/Socket/Pop3.php');
-require_once('Horde/Client/Socket/Connection.php');
-require_once('Horde/Client/Socket/Connection/Pop3.php');
-
-require_once('Horde/Client/Mailbox.php');
-
-require_once('Horde/Client/Exception.php');
-
-?>
</del><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="2013codebykatpostbyemailtrunkpostbyemailphp"></a>
<div class="modfile"><h4>Modified: 2013/codebykat/post-by-email/trunk/post-by-email.php (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/post-by-email.php     2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/post-by-email.php        2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -12,7 +12,7 @@
</span><span class="cx">  * Plugin Name: Post By Email
</span><span class="cx">  * Plugin URI:  http://codebykat.wordpress.com
</span><span class="cx">  * Description: Gets email messages from the user's mailbox to add as WordPress posts.
</span><del>- * Version:     0.9.6
</del><ins>+ * Version:     0.9.7
</ins><span class="cx">  * Author:      Kat Hagan
</span><span class="cx">  * Author URI:  http://profiles.wordpress.org/codebykat
</span><span class="cx">  * Text Domain: post-by-email
</span></span></pre></div>
<a id="2013codebykatpostbyemailtrunkreadmetxt"></a>
<div class="modfile"><h4>Modified: 2013/codebykat/post-by-email/trunk/readme.txt (2243 => 2244)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/codebykat/post-by-email/trunk/readme.txt    2013-08-27 16:27:16 UTC (rev 2243)
+++ 2013/codebykat/post-by-email/trunk/readme.txt       2013-08-28 06:43:06 UTC (rev 2244)
</span><span class="lines">@@ -20,13 +20,15 @@
</span><span class="cx"> 
</span><span class="cx"> == Changelog ==
</span><span class="cx"> 
</span><ins>+= 0.9.7 =
+* Refactored Horde includes to autoload class files as needed.
+
</ins><span class="cx"> = 0.9.6 =
</span><span class="cx"> * Added workarounds to support PHP 5.2.
</span><span class="cx"> * Moved admin functions into a separate class.
</span><span class="cx"> 
</span><span class="cx"> = 0.9.5 =
</span><del>-* Using Horde IMAP library instead of old SquirrelMail class (still assumes POP3 server).  This fixes a
-  bug where post content was blank, and also lays some groundwork for later SSL/IMAP support.
</del><ins>+* Using Horde IMAP library instead of old SquirrelMail class (still assumes POP3 server).  This fixes a bug where post content was blank, and also lays some groundwork for later SSL/IMAP support.
</ins><span class="cx"> 
</span><span class="cx"> = 0.9 =
</span><span class="cx"> * Initial version (straight port from core)
</span><span class="cx">\ No newline at end of file
</span></span></pre>
</div>
</div>

</body>
</html>