One last and small patchset to cyradm from cyrus imapd 2.5.0. Both patches have been only tested against cyrus imapd 2.5.0.
* 0001-cyradm-add-LIST-EXTENDED-and-SPECIAL-USE-support.patch (with a server that supports MAILBOX-REFERRALS, LIST-EXTENDED, SPECIAL-USE): cyradm> lm # REMOTE selection option instead of RLIST # LIST (REMOTE) "" "*" RETURN (SUBSCRIBED SPECIAL-USE) cyradm> lm --subscribed # same as without patch: # RLSUB "*" "*" cyradm> lm --subscribed --specialuse # LIST (REMOTE SUBSCRIBED SPECIAL-USE) "" "*" RETURN (SUBSCRIBED SPECIAL-USE) cyradm> lm --specialuse # LIST (REMOTE SPECIAL-USE) "" "*" RETURN (SUBSCRIBED SPECIAL-USE) cyradm> lm --subscribed --recursivematch # LIST (REMOTE RECURSIVEMATCH SUBSCRIBED) "" "*" RETURN (SUBSCRIBED SPECIAL-USE) * 0002-cyradm-add-CREATE-SPECIAL-USE-support.patch cyradm> cm --specialuse \\Drafts INBOX.Drafts # CREATE INBOX.Drafts (USE (\Drafts)) Notes: The first patch adds but does not document --recursivematch because I don't know how to describe it in simple words and without copying several paragraphs from the RFC. I've used "specialuse" although the newer RFCs always talk about "special-use" to be consistent with existing usage in cyradm (e.g. setmetadata uses "specialuse" in the user interface. Regards, Norbert
>From 4077f7dd8da8c779a08636e593b13a9388e0658a Mon Sep 17 00:00:00 2001 From: Norbert Warmuth <n...@arcor.de> Date: Mon, 6 Apr 2015 14:32:42 +0200 Subject: [PATCH 1/2] cyradm: add LIST-EXTENDED and SPECIAL-USE support to listmailbox command If the server supports LIST-EXTENDED (RFC 5258 IMAPv4 - LIST Command Extensions): - use LIST and REMOTE selection option instead of RLIST command - always request to return subscription state (SUBSCRIBED return option) - extract and output optional mbox-list-extended, i.e. the CHILDINFO extented data item in <1428759112<6 LIST (REMOTE) "" "*" RETURN (SUBSCRIBED SPECIAL-USE) >1428759112>* LIST (\HasChildren) "." INBOX (CHILDINFO ("SUBSCRIBED")) is output as: INBOX (\HasChildren) (CHILDINFO ("SUBSCRIBED")) If additionally SPECIAL-USE is supported (RFC 6154 - IMAP LIST Extension or Special-Use Mailboxes): - always request return of special-use attributes - optionally only request mailboxes with special use attributes set (SPECIAL-USE select option) Example: lm --specialuse - if both --subscribed and --specialuse is requestet: use LIST instead of LSUB command --- perl/imap/IMAP/Admin.pm | 103 ++++++++++++++++++++++++++++++++++++++++++------ perl/imap/IMAP/Shell.pm | 43 ++++++++++++++------ perl/imap/cyradm.sh | 12 +++--- 3 files changed, 130 insertions(+), 28 deletions(-) diff --git a/perl/imap/IMAP/Admin.pm b/perl/imap/IMAP/Admin.pm index 4582932..402441d 100644 --- a/perl/imap/IMAP/Admin.pm +++ b/perl/imap/IMAP/Admin.pm @@ -76,15 +76,25 @@ sub new { if(defined($self)) { $self->{support_referrals} = 0; $self->{support_annotatatemore} = 0; + $self->{support_list_extended} = 0; + $self->{support_list_special_use} = 0; $self->{authopts} = []; $self->addcallback({-trigger => 'CAPABILITY', -callback => sub {my %a = @_; - map { $self->{support_referrals} = 1 + map { + # RFC 2193 IMAP4 Mailbox Referrals + $self->{support_referrals} = 1 if /^MAILBOX-REFERRALS$/i; $self->{support_annotatemore} = 1 if /^ANNOTATEMORE$/i; $self->{support_metadata} = 1 if /^METADATA$/i; + # RFC 5258 IMAPv4 - LIST Command Extensions + $self->{support_list_extended} = 1 + if /^LIST-EXTENDED$/i; + # RFC 6154 - IMAP LIST Extension for Special-Use Mailboxes + $self->{support_list_special_use} = 1 + if /^SPECIAL-USE$/i; } split(/ /, $a{-text})}}); $self->send(undef, undef, 'CAPABILITY'); @@ -324,15 +334,65 @@ sub listaclmailbox { *listacl = *listaclmailbox; sub listmailbox { - my ($self, $pat, $ref) = @_; + my ($self, $pat, $ref, $opts) = @_; $ref ||= ""; my @info = (); my $list_cmd; + my @list_sel; + my @list_ret; if($self->{support_referrals}) { - $list_cmd = 'RLIST'; - } else { - $list_cmd = 'LIST'; + if ($self->{support_list_extended}) { + $list_cmd = 'LIST'; + push @list_sel, "REMOTE"; + } else { + $list_cmd = 'RLIST'; + } + } + + if(defined ($$opts{'-sel-special-use'}) && !$self->{support_list_special_use}) { + $self->{error} = "Remote does not support SPECIAL-USE."; + return undef; + } + + if((defined ($$opts{'-sel-special-use'}) || + defined ($$opts{'-sel-recursivematch'}) || + defined ($$opts{'-sel-subscribed'})) + && !$self->{support_list_extended}) { + $self->{error} = "Remote does not support LIST-EXTENDED."; + return undef; } + + if ($self->{support_list_extended}) { + push @list_ret, "SUBSCRIBED"; + # "The RECURSIVEMATCH option MUST NOT occur as the only selection + # option (or only with REMOTE), as it only makes sense when other + # selection options are also used." + push @list_sel, "RECURSIVEMATCH" + if defined ($$opts{'-sel-recursivematch'}); + + push @list_sel, "SUBSCRIBED" + if defined ($$opts{'-sel-subscribed'}); + + if($self->{support_list_special_use}) { + # always return special-use flags + push @list_ret, "SPECIAL-USE"; + push @list_sel, "SPECIAL-USE" + if defined ($$opts{'-sel-special-use'}); + } + } + + # RFC 5258: + # "By adding options to the LIST command, we are announcing the intent + # to phase out and eventually to deprecate the RLIST and RLSUB commands + # described in [MBRef])." + # + # This should never trigger: MAILBOX-REFERRALS and SPECIAL-USE but no + # LIST-EXTENDED. + if ($list_cmd eq "RLIST" && (scalar (@list_ret) > 0 || scalar (@list_sel) > 0)) { + $self->{error} = "Invalid capabilities: MAILBOX-REFERRALS and SPECIAL-USE but no LIST-EXTENDED."; + return undef; + } + $self->addcallback({-trigger => 'LIST', -callback => sub { my %d = @_; @@ -340,6 +400,7 @@ sub listmailbox { my $attrs = $1; my $sep = ''; my $mbox; + my $extended; # NIL or (attrs) "sep" "str" if ($d{-text} =~ /^N/) { return if $d{-text} !~ s/^NIL//; @@ -351,16 +412,36 @@ sub listmailbox { if ($d{-text} =~ /{\d+}(.*)/) { # cope with literals (?) (undef, $mbox) = split(/\n/, $d{-text}); - } elsif ($d{-text} =~ /\"(([^\\\"]*\\)*[^\\\"]*)\"/) { + } elsif ($d{-text} =~ /^\"(([^\\\"]*\\)*[^\\\"]*)\"/) { ($mbox = $1) =~ s/\\(.)/$1/g; } else { - $d{-text} =~ /^([]!\#-[^-~]+)/; + $d{-text} =~ s/^([]!\#-[^-~]+)//; $mbox = $1; } - push @{$d{-rock}}, [$mbox, $attrs, $sep]; + if ($d{-text} =~ s/^ \(("{0,1}[^" ]+"{0,1} \("[^"]*"\))\)//) { + # RFC 5258: mbox-list-extended = "(" [mbox-list-extended-item + # *(SP mbox-list-extended-item)] ")" + $extended = $1; + } + push @{$d{-rock}}, [$mbox, $attrs, $sep, $extended]; }, -rock => \@info}); - my ($rc, $msg) = $self->send('', '', "$list_cmd %s %s", $ref, $pat); + + # list = "LIST" [SP list-select-opts] SP mailbox SP mbox-or-pat + # [SP list-return-opts] + my @args = (); + my $cmd = $list_cmd; + if (scalar (@list_sel) > 0) { + $cmd .= " (%a)"; + push @args, join (" ", @list_sel); + } + $cmd .= " %s %s"; + push @args, ($ref, $pat); + if (scalar (@list_ret) > 0) { + $cmd .= " RETURN (%a)"; + push @args, join (" ", @list_ret); + } + my ($rc, $msg) = $self->send('', '', $cmd, @args); $self->addcallback({-trigger => $list_cmd}); if ($rc eq 'OK') { $self->{error} = undef; @@ -1278,9 +1359,9 @@ Delete one or more ACL from a mailbox. Returns a hash of mailbox ACLs, with each key being a Cyrus user and the corresponding value being the ACL. -=item listmailbox($pattern[, $reference]) +=item listmailbox($pattern[[, $reference], \%opts]) -=item list($pattern[, $reference]) +=item list($pattern[[, $reference], \%opts]) List mailboxes matching the specified pattern, starting from the specified reference. The result is a list; each element is an array containing the diff --git a/perl/imap/IMAP/Shell.pm b/perl/imap/IMAP/Shell.pm index 9fb4a3a..c58e4df 100644 --- a/perl/imap/IMAP/Shell.pm +++ b/perl/imap/IMAP/Shell.pm @@ -80,7 +80,7 @@ my %builtins = (exit => listaclmailbox => 'listacl', lm => 'listmailbox', listmailbox => - [\&_sc_list, '[-subscribed] [pattern [base]]', + [\&_sc_list, '[-subscribed] [-specialuse] [pattern [base]]', 'list mailboxes'], server => [\&_sc_server, '[-noauthenticate] [server]', @@ -608,27 +608,45 @@ sub _sc_exit { sub _sc_list { my ($cyrref, $name, $fh, $lfh, @argv) = @_; my $cmd = 'listmailbox'; - my (@nargv, $opt); + my (@nargv, $opt, %opts, $subscribed); shift(@argv); while (defined ($opt = shift(@argv))) { # gack. bloody tcl. last if $opt eq '--'; if ($opt ne '' && '-subscribed' =~ /^\Q$opt/ || $opt eq '--subscribed') { - $cmd = 'listsubscribed'; + $subscribed = 1; + } elsif ($opt ne '' && '-specialuse' =~ /^\Q$opt/ || $opt eq '--specialuse') { + $opts{'-sel-special-use'} = 1; + } elsif ($opt ne '' && '-recursivematch' =~ /^\Q$opt/ || $opt eq '--recursivematch') { + $opts{'-sel-recursivematch'} = 1; } elsif ($opt =~ /^-/) { - die "usage: listmailbox [-subscribed] [pattern [base]]\n"; + die "usage: listmailbox [-subscribed] [-specialuse] [pattern [base]]\n"; } else { push(@nargv, $opt); last; } } + + if ($subscribed) { + if (scalar (keys %opts) > 0 ) { + # LIST + LIST-EXTENED + $opts{'-sel-subscribed'} = 1; + } else { + # LSUB + $cmd = 'listsubscribed'; + # undef %opts; + } + } + push(@nargv, @argv); if (@nargv > 2) { - die "usage: listmailbox [-subscribed] [pattern [base]]\n"; + die "usage: listmailbox [-subscribed] [-specialuse] [pattern [base]]\n"; } push(@nargv, '*') if !@nargv; + push(@nargv, undef) if scalar (@nargv) < 2; # no ref + push(@nargv, \%opts); if (!$cyrref || !$$cyrref) { die "listmailbox: no connection to server\n"; } @@ -643,6 +661,9 @@ sub _sc_list { if ($mbx->[1] ne '') { $l .= ' (' . $mbx->[1] . ')'; } + if (defined ($mbx->[3])) { + $l .= ' (' . $mbx->[3] . ')'; + } if (length($l) + 1 > $w) { $w = length($l) + 1; } @@ -1744,15 +1765,15 @@ Display the mailbox/server metadata. List ACLs on the specified mailbox. -=item C<listmailbox> [C<--subscribed>] [I<pattern> [I<reference>]] +=item C<listmailbox> [C<--subscribed>] [C<--specialuse>] [I<pattern> [I<reference>]] -=item C<list> [C<--subscribed>] [I<pattern> [I<reference>]] +=item C<list> [C<--subscribed>] [C<--specialuse>] [I<pattern> [I<reference>]] -=item C<lm> [C<--subscribed>] [I<pattern> [I<reference>]] +=item C<lm> [C<--subscribed>] [C<--specialuse>] [I<pattern> [I<reference>]] -List all, or all subscribed, mailboxes matching the specified pattern. -The pattern may have embedded wildcards C<'*'> or C<'%'>, which match -anything or anything except the separator character, respectively. +List all, or all subscribed or special-use, mailboxes matching the specified +pattern. The pattern may have embedded wildcards C<'*'> or C<'%'>, which +match anything or anything except the separator character, respectively. Mailboxes returned will be relative to the specified reference if one is specified. This allows a mailbox list to be limited to a particular diff --git a/perl/imap/cyradm.sh b/perl/imap/cyradm.sh index 4a7caaa..5a6bc0f 100644 --- a/perl/imap/cyradm.sh +++ b/perl/imap/cyradm.sh @@ -162,15 +162,15 @@ Display the mailbox/server metadata. List ACLs on the specified mailbox. -=item C<listmailbox> [C<--subscribed>] [I<pattern> [I<reference>]] +=item C<listmailbox> [C<--subscribed>] [C<--specialuse>] [I<pattern> [I<reference>]] -=item C<list> [C<--subscribed>] [I<pattern> [I<reference>]] +=item C<list> [C<--subscribed>] [C<--specialuse>] [I<pattern> [I<reference>]] -=item C<lm> [C<--subscribed>] [I<pattern> [I<reference>]] +=item C<lm> [C<--subscribed>] [C<--specialuse>] [I<pattern> [I<reference>]] -List all, or all subscribed, mailboxes matching the specified pattern. -The pattern may have embedded wildcards C<'*'> or C<'%'>, which match -anything or anything except the separator character, respectively. +List all, or all subscribed or special-use, mailboxes matching the specified +pattern. The pattern may have embedded wildcards C<'*'> or C<'%'>, which +match anything or anything except the separator character, respectively. Mailboxes returned will be relative to the specified reference if one is specified. This allows a mailbox list to be limited to a particular -- 2.1.4
>From 0a7e1d4e3d098a1486621606b9ae6bea1f1fc31c Mon Sep 17 00:00:00 2001 From: Norbert Warmuth <n...@arcor.de> Date: Mon, 6 Apr 2015 14:43:40 +0200 Subject: [PATCH 2/2] cyradm: add CREATE-SPECIAL-USE support createmailbox: add option to assign special use attribute if remote server supports CREATE-SPECIAL-USE (RFC 6154 - IMAP LIST Extension for Special- Use Mailboxes). Example: cm --specialuse \\Trash INBOX.Trash --- perl/imap/IMAP/Admin.pm | 33 ++++++++++++++++++++++++++++----- perl/imap/IMAP/Shell.pm | 37 +++++++++++++++++++++++-------------- perl/imap/cyradm.sh | 13 ++++++------- 3 files changed, 57 insertions(+), 26 deletions(-) diff --git a/perl/imap/IMAP/Admin.pm b/perl/imap/IMAP/Admin.pm index 402441d..a9289f6 100644 --- a/perl/imap/IMAP/Admin.pm +++ b/perl/imap/IMAP/Admin.pm @@ -78,6 +78,7 @@ sub new { $self->{support_annotatatemore} = 0; $self->{support_list_extended} = 0; $self->{support_list_special_use} = 0; + $self->{support_create_special_use} = 0; $self->{authopts} = []; $self->addcallback({-trigger => 'CAPABILITY', -callback => sub {my %a = @_; @@ -95,6 +96,9 @@ sub new { # RFC 6154 - IMAP LIST Extension for Special-Use Mailboxes $self->{support_list_special_use} = 1 if /^SPECIAL-USE$/i; + # RFC 6154 - IMAP LIST Extension for Special-Use Mailboxes + $self->{support_create_special_use} = 1 + if /^CREATE-SPECIAL-USE$/i; } split(/ /, $a{-text})}}); $self->send(undef, undef, 'CAPABILITY'); @@ -190,10 +194,29 @@ sub reconstruct { } sub createmailbox { - my ($self, $mbx, $partition) = @_; - $partition = '' if !defined($partition); - my ($rc, $msg) = $self->send('', '', 'CREATE %s%a%a', $mbx, - $partition? ' ': '', $partition); + my ($self, $mbx, $partition, $opts) = @_; + my $cmd = "CREATE %s"; + my @args = (); + # RFC 3501 + cyrus: CREATE mailbox [partition] + # RFC 4466 + RFC 6154: CREATE mailbox ([PARTITION partition ]USE (special-use)) + if (defined ($$opts{'-specialuse'})) { + if($self->{support_create_special_use}) { + if (defined ($partition)) { + $cmd .= " (PARTITION %a USE (%a))" ; + push @args, ($partition, $$opts{'-specialuse'}); + } else { + $cmd .= " (USE (%a))" ; + push @args, $$opts{'-specialuse'}; + } + } else { + $self->{error} = "Remote does not support CREATE-SPECIAL-USE."; + return undef; + } + } elsif (defined ($partition)) { + $cmd .= " %a"; + push @args, $partition; + } + my ($rc, $msg) = $self->send('', '', $cmd, $mbx, @args); if ($rc eq 'OK') { $self->{error} = undef; 1; @@ -1333,7 +1356,7 @@ Calling C<error> does not reset the error state, so it is legal to write: @folders = $cyradm->list($spec); print STDERR "Error: ", $cyradm->error if $cyradm->error; -=item createmailbox($mailbox[, $partition]) +=item createmailbox($mailbox[[, $partition], \%opts]) =item create($mailbox[, $partition]) diff --git a/perl/imap/IMAP/Shell.pm b/perl/imap/IMAP/Shell.pm index c58e4df..22da834 100644 --- a/perl/imap/IMAP/Shell.pm +++ b/perl/imap/IMAP/Shell.pm @@ -108,7 +108,7 @@ my %builtins = (exit => [\&_sc_chdir, 'directory', 'change current directory'], cd => 'chdir', createmailbox => - [\&_sc_create, '[--partition partition] mailbox [partition]', + [\&_sc_create, '[--partition partition] [--specialuse specialuse] mailbox [partition]', 'create mailbox'], create => 'createmailbox', cm => 'createmailbox', @@ -993,21 +993,29 @@ sub _sc_chdir { sub _sc_create { my ($cyrref, $name, $fh, $lfh, @argv) = @_; - my (@nargv, $opt, $part, $want); + my (@nargv, $opt, $part, $want, %opts); shift(@argv); while (defined ($opt = shift(@argv))) { if ($want) { - $part = $opt; + if ($want eq '-partition') { + $part = $opt; + } else { + $opts{$want} = $opt; + } $want = undef; next; } if ($opt ne '' && '-partition' =~ /^\Q$opt/ || $opt eq '--partition') { - $want = 1; + $want = '-partition'; + next; + } + if ($opt ne '' && '-specialuse' =~ /^\Q$opt/ || $opt eq '--specialuse') { + $want = '-specialuse'; next; } last if $opt eq '--'; if ($opt =~ /^-/) { - die "usage: createmailbox [--partition partition] mailbox [partition]\n"; + die "usage: createmailbox [--partition partition] [--specialuse specialuse] mailbox [partition]\n"; } else { push(@nargv, $opt); @@ -1018,9 +1026,9 @@ sub _sc_create { if (!@nargv || @nargv > 2) { die "usage: createmailbox [--partition partition] mailbox [partition]\n"; } - if (defined($part)) { - push(@nargv, $part) - } + push(@nargv, $part) if (defined ($part)); + push(@nargv, undef) if (@nargv < 2); + push(@nargv, \%opts); if (!$cyrref || !$$cyrref) { die "createmailbox: no connection to server\n"; } @@ -1695,20 +1703,21 @@ authenticated once. Change directory. A C<pwd> builtin is not provided, but the default command action will run C<pwd> from a shell if invoked. -=item C<createmailbox> [C<--partition> I<partition>] I<mailbox> +=item C<createmailbox> [C<--partition> I<partition>] [C<--specialuse> I<specialuse>] I<mailbox> -=item C<createmailbox> I<mailbox> I<partition> +=item C<createmailbox> [C<--specialuse> I<specialuse>] I<mailbox> I<partition> -=item C<create> [C<--partition> I<partition>] I<mailbox> +=item C<create> [C<--partition> I<partition>] [C<--specialuse> I<specialuse>] I<mailbox> -=item C<create> I<mailbox> I<partition> +=item C<create> [C<--specialuse> I<specialuse>] I<mailbox> I<partition> -=item C<cm> [C<--partition> I<partition>] I<mailbox> +=item C<cm> [C<--partition> I<partition>] [C<--specialuse> I<specialuse>] I<mailbox> -=item C<cm> I<mailbox> I<partition> +=item C<cm> [C<--specialuse> I<specialuse>] I<mailbox> I<partition> Create a mailbox on the default or a specified partition. Both old-style and getopt-style usages are accepted (combining them will produce an error). +Optionally assign a special use to the mailbox. =item C<deleteaclmailbox> I<mailbox> I<id> [...] diff --git a/perl/imap/cyradm.sh b/perl/imap/cyradm.sh index 5a6bc0f..e8e64c3 100644 --- a/perl/imap/cyradm.sh +++ b/perl/imap/cyradm.sh @@ -92,20 +92,19 @@ authenticated once. Change directory. A C<pwd> builtin is not provided, but the default command action will run C<pwd> from a shell if invoked. -=item C<createmailbox> [C<--partition> I<partition>] I<mailbox> +=item C<createmailbox> [C<--partition> I<partition>] [C<--specialuse> I<specialuse>] I<mailbox> -=item C<createmailbox> I<mailbox> I<partition> +=item C<create> [C<--partition> I<partition>] [C<--specialuse> I<specialuse>] I<mailbox> -=item C<create> [C<--partition> I<partition>] I<mailbox> +=item C<create> [C<--specialuse> I<specialuse>] I<mailbox> I<partition> -=item C<create> I<mailbox> I<partition> +=item C<cm> [C<--partition> I<partition>] [C<--specialuse> I<specialuse>] I<mailbox> -=item C<cm> [C<--partition> I<partition>] I<mailbox> - -=item C<cm> I<mailbox> I<partition> +=item C<cm> [C<--specialuse> I<specialuse>] I<mailbox> I<partition> Create a mailbox on the default or a specified partition. Both old-style and getopt-style usages are accepted (combining them will produce an error). +Optionally assign a special use to the mailbox. =item C<deleteaclmailbox> I<mailbox> I<id> [...] -- 2.1.4