protected function _append(Horde_Imap_Client_Mailbox $mailbox, $data, $options)
{
$c = $this->_capability();
// Check for MULTIAPPEND extension (RFC 3502)
if (count($data) > 1 && !$c->query('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 extensions.
$binary = $c->query('BINARY');
$catenate = $c->query('CATENATE');
$utf8 = $c->isEnabled('UTF8=ACCEPT');
$asize = 0;
$cmd = $this->_command('APPEND')->add($this->_getMboxFormatOb($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']));
}
$adata = null;
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) {
$tdata = $this->_appendData($v['v'], $asize);
if ($utf8) {
/* RFC 6855 [4]: CATENATE UTF8 extension. */
$tdata->forceBinary();
$tmp->add(array('UTF8', new Horde_Imap_Client_Data_Format_List($tdata)));
} else {
$tmp->add(array('TEXT', $tdata));
}
} 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 {
$adata = $this->_appendData($data_stream->stream, $asize);
}
} else {
$adata = $this->_appendData($data[$key]['data'], $asize);
}
if (!is_null($adata)) {
if ($utf8) {
/* RFC 6855 [4]: APPEND UTF8 extension. */
$adata->forceBinary();
$cmd->add(array('UTF8', new Horde_Imap_Client_Data_Format_List($adata)));
} else {
$cmd->add($adata);
}
}
}
/* Although it is normally more efficient to use LITERAL+, disable if
* payload is over 50 KB 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, since so many IMAP servers have issues with APPEND
* + BINARY, don't use LITERAL+ since servers may send BAD
* (incorrectly) after initial command. */
$cmd->literalplus = $asize < 1024 * 50 && !$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. */
$c->remove('CATENATE');
return $this->_append($mailbox, $data, $options);
case $e::DISCONNECT:
/* Workaround broken literal8 on Cyrus. */
if ($binary) {
// Need to re-login first before removing capability.
$this->login();
$c->remove('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 ($c->query('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:
$c->remove('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;
}