Skip to content

Commit

Permalink
IMAP: Partial support for ANNOTATE-EXPERIMENT-1 extension (RFC 5257)
Browse files Browse the repository at this point in the history
  • Loading branch information
alecpl committed Dec 11, 2024
1 parent 94a6144 commit 120c640
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 9 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
- Advanced mail search syntax with more possibilities (without UI) (#8502)
- Added an option for a default mail search scope (#9077, #7556)
- Added option to define font list and font-size list for HTML editor - available_fonts/available_font_sizes (#5700)
- Support for HAproxy protocol header in IMAP connections (#8625)
- IMAP: Support for HAproxy protocol header in IMAP connections (#8625)
- IMAP: Partial support for ANNOTATE-EXPERIMENT-1 extension (RFC 5257)
- Change 'smtp_log' option default value to False
- Add 'php' log_driver value (#6138)
- Delete messages directly from Junk on folder purge if delete_junk is enabled (#8766)
Expand Down
39 changes: 37 additions & 2 deletions program/lib/Roundcube/rcube_imap.php
Original file line number Diff line number Diff line change
Expand Up @@ -1289,7 +1289,7 @@ public function fetch_headers($folder, $msgs, $sort = true, $force = false)
} else {
// fetch requested headers from server
$headers = $this->conn->fetchHeaders(
$folder, $msgs, true, false, $this->get_fetch_headers());
$folder, $msgs, true, false, $this->get_fetch_headers(), $this->get_fetch_items());
}

if (empty($headers)) {
Expand Down Expand Up @@ -1877,7 +1877,7 @@ public function get_message_headers($uid, $folder = null, $force = false)
$headers = false;
} else {
$headers = $this->conn->fetchHeader(
$folder, $uid, true, true, $this->get_fetch_headers());
$folder, $uid, true, true, $this->get_fetch_headers(), $this->get_fetch_items());

if (is_object($headers)) {
$headers->folder = $folder;
Expand Down Expand Up @@ -2788,6 +2788,31 @@ public function expunge_message($uids, $folder = null, $clear_cache = true)
return $result;
}

/**
* Annotate a message.
*
* @param array $annotation Message annotation key-value array
* @param mixed $uids Message UIDs as array or comma-separated string, or '*'
* @param string $folder Folder name
*
* @return bool True on success, False on failure
*/
#[Override]

Check failure on line 2800 in program/lib/Roundcube/rcube_imap.php

View workflow job for this annotation

GitHub Actions / Static Analysis

Method rcube_imap::annotate_message() has #[\Override] attribute but does not override any method.
public function annotate_message($annotation, $uids, $folder = null)
{
[$uids] = $this->parse_uids($uids);

if (!is_string($folder) || !strlen($folder)) {
$folder = $this->folder;
}

if (!$this->check_connection()) {
return false;
}

return $this->conn->storeMessageAnnotation($folder, $uids, $annotation);
}

/* --------------------------------
* folder management
* --------------------------------*/
Expand Down Expand Up @@ -3833,6 +3858,16 @@ protected function get_fetch_headers()
return $headers;
}

/**
* Get additional FETCH items for rcube_imap_generic::fetchHeader(s)
*
* @return array List of items
*/
protected function get_fetch_items()
{
return $this->options['fetch_items'] ?? [];
}

/* -----------------------------------------
* ACL and METADATA/ANNOTATEMORE methods
* ----------------------------------------*/
Expand Down
82 changes: 76 additions & 6 deletions program/lib/Roundcube/rcube_imap_generic.php
Original file line number Diff line number Diff line change
Expand Up @@ -2493,10 +2493,10 @@ public function fetch($mailbox, $message_set, $is_uid = false, $query_items = []
$result[$id]->id = $id;
$result[$id]->subject = '';
$result[$id]->messageID = 'mid:' . $id;
$result[$id]->folder = $mailbox;

$headers = null;
$line = substr($line, strlen($m[0]) + 2);

// Tokenize response and assign to object properties
while (($tokens = $this->tokenizeResponse($line, 2)) && count($tokens) == 2) {
[$name, $value] = $tokens;
Expand All @@ -2519,6 +2519,20 @@ public function fetch($mailbox, $message_set, $is_uid = false, $query_items = []
$result[$id]->flags[$flag] = true;
}
}
} elseif ($name == 'ANNOTATION') {
$result[$id]->annotations = [];
if (!empty($value) && is_array($value)) {
$n = 0;
while (!empty($value[$n]) && is_string($value[$n])) {
$name = $value[$n++];
$list = $value[$n++];
$result[$id]->annotations[$name] = [];
$c = 0;
while (!empty($list[$c]) && is_string($list[$c])) {
$result[$id]->annotations[$name][$list[$c++]] = $list[$c++];
}
}
}
} elseif ($name == 'MODSEQ') {
$result[$id]->modseq = $value[0];
} elseif ($name == 'ENVELOPE') {
Expand Down Expand Up @@ -2652,13 +2666,14 @@ public function fetch($mailbox, $message_set, $is_uid = false, $query_items = []
* @param mixed $message_set Message(s) sequence identifier(s) or UID(s)
* @param bool $is_uid True if $message_set contains UIDs
* @param bool $bodystr Enable to add BODYSTRUCTURE data to the result
* @param array $add_headers List of additional headers
* @param array $add_headers List of additional headers to fetch
* @param array $query_items List of additional items to fetch
*
* @return bool|array List of rcube_message_header elements, False on error
*/
public function fetchHeaders($mailbox, $message_set, $is_uid = false, $bodystr = false, $add_headers = [])
public function fetchHeaders($mailbox, $message_set, $is_uid = false, $bodystr = false, $add_headers = [], $query_items = [])
{
$query_items = ['UID', 'RFC822.SIZE', 'FLAGS', 'INTERNALDATE'];
$query_items = array_unique(array_merge($query_items, ['UID', 'RFC822.SIZE', 'FLAGS', 'INTERNALDATE']));
$headers = ['DATE', 'FROM', 'TO', 'SUBJECT', 'CONTENT-TYPE', 'CC', 'REPLY-TO',
'LIST-POST', 'DISPOSITION-NOTIFICATION-TO', 'X-PRIORITY'];

Expand All @@ -2684,12 +2699,13 @@ public function fetchHeaders($mailbox, $message_set, $is_uid = false, $bodystr =
* @param bool $is_uid True if $id is an UID
* @param bool $bodystr Enable to add BODYSTRUCTURE data to the result
* @param array $add_headers List of additional headers
* @param array $query_items List of additional items to fetch
*
* @return bool|rcube_message_header Message data, False on error
*/
public function fetchHeader($mailbox, $id, $is_uid = false, $bodystr = false, $add_headers = [])
public function fetchHeader($mailbox, $id, $is_uid = false, $bodystr = false, $add_headers = [], $query_items = [])
{
$a = $this->fetchHeaders($mailbox, $id, $is_uid, $bodystr, $add_headers);
$a = $this->fetchHeaders($mailbox, $id, $is_uid, $bodystr, $add_headers, $query_items);

if (is_array($a)) {
return array_first($a);
Expand Down Expand Up @@ -3750,6 +3766,60 @@ public function getAnnotation($mailbox, $entries, $attribs)
return null;
}

/**
* Send the STORE X ANNOTATION command (RFC5257)
*
* @param string $mailbox Mailbox name
* @param array $entries
*
* @return bool True on success, False on failure
*
* @since 1.6.10
*/
public function storeMessageAnnotation($mailbox, $uids, $entries)
{
if (!$this->hasCapability('ANNOTATE-EXPERIMENT-1')) {
return false;
}

if (empty($entries) || empty($uids)) {
$this->setError(self::ERROR_COMMAND, 'Wrong argument for STORE ANNOTATION command');
return false;
}

if (!$this->select($mailbox)) {
return false;
}

/* Example input compatible with rcube_message_header::$annotations:
$entries = [
'/comment' => [
'value.priv' => 'test1',
'value.shared' => null,
],
];
*/

$request = [];
foreach ($entries as $name => $annotation) {
if (!empty($annotation)) {
foreach ($annotation as $key => $value) {
$annotation[$key] = $this->escape($key) . ' ' . $this->escape($value, true);
}
$request[] = $this->escape($name);
$request[] = $annotation;
}
}

$result = $this->execute(
'UID STORE',
[$this->compressMessageSet($uids), 'ANNOTATION', $request],
self::COMMAND_NORESPONSE
);

return $result == self::ERROR_OK;
}

/**
* Returns BODYSTRUCTURE for the specified message.
*
Expand Down
7 changes: 7 additions & 0 deletions program/lib/Roundcube/rcube_message_header.php
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,13 @@ class rcube_message_header
*/
public $flags = [];

/**
* Message annotations (RFC 5257)
*
* @var ?array
*/
public $annotations;

/**
* Extra flags (for the messages list)
*
Expand Down

0 comments on commit 120c640

Please sign in to comment.