Skip to content
This repository has been archived by the owner on Dec 10, 2024. It is now read-only.

Commit

Permalink
Config: add understanding of imapd.conf BITFIELDs
Browse files Browse the repository at this point in the history
This will let us treat 'httpmodules' et al as the set of flags it is,
rather than an opaque string
  • Loading branch information
elliefm committed Aug 30, 2021
1 parent 34d76d3 commit ccfa48e
Show file tree
Hide file tree
Showing 3 changed files with 384 additions and 25 deletions.
163 changes: 154 additions & 9 deletions Cassandane/Config.pm
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,45 @@ use Cassandane::Util::Log;

my $default;

# XXX Manually entered from lib/imapoptions in cyrus-imapd repo.
# XXX Once these repositories are merged, we'll be able to automate keeping
# XXX this synchronised...
my %bitfields = (
'calendar_component_set' => 'VEVENT VTODO VJOURNAL VFREEBUSY VAVAILABILITY VPOLL',
'event_extra_params' => 'bodyStructure clientAddress diskUsed flagNames messageContent messageSize messages modseq service timestamp uidnext vnd.cmu.midset vnd.cmu.unseenMessages vnd.cmu.envelope vnd.cmu.sessionId vnd.cmu.mailboxACL vnd.cmu.mbtype vnd.cmu.davFilename vnd.cmu.davUid vnd.fastmail.clientId vnd.fastmail.sessionId vnd.fastmail.convExists vnd.fastmail.convUnseen vnd.fastmail.cid vnd.fastmail.counters vnd.fastmail.jmapEmail vnd.fastmail.jmapStates vnd.cmu.emailid vnd.cmu.threadid',
'event_groups' => 'message quota flags access mailbox subscription calendar applepushservice',
'httpmodules' => 'admin caldav carddav cgi domainkey freebusy ischedule jmap prometheus rss tzdist webdav',
'metapartition_files' => 'header index cache expunge squat annotations lock dav archivecache',
'newsaddheaders' => 'to replyto',
'sieve_extensions' => 'fileinto reject vacation vacation-seconds notify include envelope environment body relational regex subaddress copy date index imap4flags imapflags mailbox mboxmetadata servermetadata variables editheader extlists duplicate ihave fcc special-use redirect-dsn redirect-deliverby mailboxid vnd.cyrus.log x-cyrus-log vnd.cyrus.jmapquery x-cyrus-jmapquery snooze vnd.cyrus.snooze x-cyrus-snooze',
);
my $bitfields_fixed = 0;

sub new
{
my $class = shift;

if (!$bitfields_fixed) {
while (my ($key, $allvalues) = each %bitfields) {
$bitfields{$key} = {};
foreach my $v (split /\s/, $allvalues) {
$bitfields{$key}->{$v} = 1;
}
}
$bitfields_fixed = 1;
}

my $self = {
parent => undef,
variables => {},
params => { @_ },
params => {},
};

bless $self, $class;

# any arguments are initial params, process them properly
$self->set(@_);

return $self;
}

Expand Down Expand Up @@ -105,22 +134,121 @@ sub clone
return $child;
}

sub _explode_bit_string
{
my ($s) = @_;
return split /[_ ]/, $s;
}

sub set
{
my ($self, %nv) = @_;
while (my ($n, $v) = each %nv)
{
$self->{params}->{$n} = $v;
if (exists $bitfields{$n}) {
# it's a bitfield, set exactly what's given (clearing others)
if (ref $v eq 'ARRAY') {
$self->clear_all_bits($n);
$self->set_bits($n, @{$v});
}
elsif (ref $v eq q{}) {
$self->clear_all_bits($n);
$self->set_bits($n, _explode_bit_string($v));
}
else {
die "don't know what to do with value '$v'";
}
}
else {
$self->{params}->{$n} = $v;
}
}
}

sub set_bits
{
my ($self, $name, @bits) = @_;

die "$name is not a bitfield option" if not exists $bitfields{$name};

# explode space- or underscore-delimited list as only bit
if (scalar @bits == 1 && $bits[0] =~ m/[_ ]/) {
@bits = _explode_bit_string($bits[0]);
}

foreach my $bit (@bits) {
die "$bit is not a $name value"
if not exists $bitfields{$name}->{$bit};

$self->{params}->{$name}->{$bit} = 1;
}
}

sub clear_bits
{
my ($self, $name, @bits) = @_;

die "$name is not a bitfield option" if not exists $bitfields{$name};

# explode space- or underscore-delimited list as only bit
if (scalar @bits == 1 && $bits[0] =~ m/[_ ]/) {
@bits = _explode_bit_string($bits[0]);
}

foreach my $bit (@bits) {
die "$bit is not a $name value"
if not exists $bitfields{$name}->{$bit};

$self->{params}->{$name}->{$bit} = 0;
}
}

sub clear_all_bits
{
my ($self, $name) = @_;

die "$name is not a bitfield option" if not exists $bitfields{$name};

$self->{params}->{$name}->{$_} = 0 for keys %{$bitfields{$name}};
}

sub get
{
my ($self, $n) = @_;
while (defined $self)
{
return $self->{params}->{$n}
if exists $self->{params}->{$n};
if (exists $bitfields{$n}) {
my %bits;
while (defined $self) {
if (exists $self->{params}->{$n}) {
while (my ($bit, $val) = each %{$self->{params}->{$n}}) {
$bits{$bit} //= $val;
}
}
$self = $self->{parent};
}
my @v = grep { $bits{$_} } sort keys %bits;
return wantarray ? @v : join q{ }, @v;
}
else {
while (defined $self)
{
return $self->{params}->{$n}
if exists $self->{params}->{$n};
$self = $self->{parent};
}
}
return undef;
}

sub get_bit
{
my ($self, $name, $bit) = @_;

die "$name is not a bitfield option" if not exists $bitfields{$name};
die "$bit is not a $name value" if not exists $bitfields{$name}->{$bit};

while (defined $self) {
return $self->{params}->{$name}->{$bit}
if exists $self->{params}->{$name}->{$bit};
$self = $self->{parent};
}
return undef;
Expand All @@ -129,6 +257,9 @@ sub get
sub get_bool
{
my ($self, $n, $def) = @_;

die "bitfield $n cannot be boolean" if exists $bitfields{$n};

$def = 'no' if !defined $def;
my $v = $self->get($n);
$v = $def if !defined $v;
Expand Down Expand Up @@ -199,8 +330,16 @@ sub _flatten
{
foreach my $n (keys %{$conf->{params}})
{
$nv{$n} = $self->substitute($conf->{params}->{$n})
unless exists $nv{$n};
if (exists $bitfields{$n}) {
# no variable substitution on bitfields
while (my ($bit, $val) = each %{$conf->{params}->{$n}}) {
$nv{$n}->{$bit} //= $val;
}
}
else {
$nv{$n} = $self->substitute($conf->{params}->{$n})
unless exists $nv{$n};
}
}
}
return \%nv;
Expand All @@ -216,7 +355,13 @@ sub generate
while (my ($n, $v) = each %$nv)
{
next unless defined $v;
print CONF "$n: $v\n";
if (exists $bitfields{$n}) {
my @bits = grep { $nv->{$n}->{$_} } sort keys %{$nv->{$n}};
print CONF "$n: " . join(q{ }, @bits) . "\n";
}
else {
print CONF "$n: $v\n";
}
}
close CONF;
}
Expand Down
15 changes: 4 additions & 11 deletions Cassandane/Instance.pm
Original file line number Diff line number Diff line change
Expand Up @@ -651,12 +651,9 @@ sub _generate_imapd_conf
event_notifier => 'pusher',
);
if ($cyrus_major_version >= 3) {
my $event_groups = $self->{config}->get('event_groups') || '';
$event_groups .= ' mailbox message flags calendar';
$self->{config}->set(
imipnotifier => 'imip',
event_groups => $event_groups,
);
$self->{config}->set(imipnotifier => 'imip');
$self->{config}->set_bits('event_groups',
'mailbox message flags calendar');

if ($cyrus_major_version > 3 || $cyrus_minor_version >= 1) {
$self->{config}->set(
Expand All @@ -666,11 +663,7 @@ sub _generate_imapd_conf
}
}
else {
my $event_groups = $self->{config}->get('event_groups') || '';
$event_groups .= ' mailbox message flags';
$self->{config}->set(
event_groups => 'mailbox message flags',
);
$self->{config}->set_bits('event_groups', 'mailbox message flags');
}
if ($self->{buildinfo}->get('search', 'xapian')) {
my %xapian_defaults = (
Expand Down
Loading

0 comments on commit ccfa48e

Please sign in to comment.