Skip to content

Commit

Permalink
jmap_mail: return lowest createdmodseq in Email/get
Browse files Browse the repository at this point in the history
Signed-off-by: Robert Stepanek <[email protected]>
  • Loading branch information
rsto committed Aug 17, 2023
1 parent a70ef12 commit a99fccf
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 0 deletions.
52 changes: 52 additions & 0 deletions cassandane/tiny-tests/JMAPEmail/email_get_createdmodseq
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#!perl
use Cassandane::Tiny;
use Time::HiRes qw( usleep gettimeofday );

sub test_email_get_createdmodseq
:min_version_3_9 :needs_component_jmap :JMAPExtensions
{
my ($self) = @_;
my $jmap = $self->{jmap};
my $imap = $self->{store}->get_client();
$jmap->AddUsing('https://cyrusimap.org/ns/jmap/mail');

xlog $self, "Append duplicate messages";
$mimeMessage = <<'EOF';
From: <from@local>
To: to@local
Subject: test
Date: Mon, 13 Apr 2020 15:34:03 +0200
MIME-Version: 1.0
Content-Type: text/plain
test
EOF
$mimeMessage =~ s/\r?\n/\r\n/gs;
$imap->append('INBOX', '17-Aug-2023 15:13:54 +0200', $mimeMessage) || die $@;
$imap->append('INBOX', '17-Aug-2023 15:13:54 +0200', $mimeMessage) || die $@;

$imap->select('INBOX');
$fetch = $imap->fetch('1:2', ['INTERNALDATE', 'CREATEDMODSEQ']);
$self->assert_str_equals(
$fetch->{1}{internaldate}, $fetch->{2}{internaldate}
);
$self->assert_num_lt(
$fetch->{2}{createdmodseq}[0], $fetch->{1}{createdmodseq}[0]
);

# The createdModseq must be the lower modseq.
$res = $jmap->CallMethods([
['Email/query', { }, "R1"],
['Email/get', {
'#ids' => {
resultOf => 'R1',
name => 'Email/query',
path => '/ids'
},
properties => ['createdModseq'],
}, 'R2' ],
]);
$self->assert_num_equals(1, scalar @{$res->[1][1]{list}});
$self->assert_num_equals($fetch->{1}{createdmodseq}[0],
$res->[1][1]{list}[0]{createdModseq});
}
60 changes: 60 additions & 0 deletions imap/jmap_mail.c
Original file line number Diff line number Diff line change
Expand Up @@ -6949,6 +6949,46 @@ static const char *_encode_emailheader_blobid(const char *emailid,
return buf_cstring(dst);
}

struct email_get_createdmodseq_rock {
jmap_req_t *req;
modseq_t modseq;
};

static int _email_get_createdmodseq_cb(const conv_guidrec_t *rec, void *vrock)
{
struct email_get_createdmodseq_rock *rock = vrock;
mbentry_t *mbentry = NULL;

if (rec->part) return 0;

if ((rec->system_flags & FLAG_DELETED) ||
(rec->internal_flags & FLAG_INTERNAL_EXPUNGED)) {
goto done;
}

conv_guidrec_mbentry(rec, &mbentry);

if (!mbentry || mboxname_isnondeliverymailbox(mbentry->name, mbentry->mbtype) ||
!jmap_hasrights_mbentry(rock->req, mbentry, JACL_READITEMS)) {
goto done;
}

struct mailbox *mbox = NULL;
if (!jmap_openmbox_by_uniqueid(rock->req, mbentry->uniqueid, &mbox, 0)) {
struct index_record index_record = {0};
if (!mailbox_find_index_record(mbox, rec->uid, &index_record)) {
if (!rock->modseq || rock->modseq > index_record.createdmodseq) {
rock->modseq = index_record.createdmodseq;
}
}
jmap_closembox(rock->req, &mbox);
}

done:
mboxlist_entry_free(&mbentry);
return 0;
}

static int _email_get_meta(jmap_req_t *req,
struct email_getargs *args,
struct cyrusmsg *msg,
Expand Down Expand Up @@ -6979,6 +7019,11 @@ static int _email_get_meta(jmap_req_t *req,
}
if (jmap_wantprop(props, "receivedAt"))
json_object_set_new(email, "receivedAt", json_null());

if (jmap_wantprop(props, "createdModseq")) {
json_object_set_new(email, "createdModseq", json_integer(0));
}

return 0;
}

Expand Down Expand Up @@ -7126,6 +7171,16 @@ static int _email_get_meta(jmap_req_t *req,
buf_free(&buf);
}

if (jmap_wantprop(props, "createdModseq")) {
struct email_get_createdmodseq_rock rock = { req, 0 };

/* Look for the smalled createdmodseq for all index records */
conversations_guid_foreach(req->cstate, _guid_from_id(email_id),
_email_get_createdmodseq_cb, &rock);

json_object_set_new(email, "createdModseq", json_integer(rock.modseq));
}

done:
return r;
}
Expand Down Expand Up @@ -8295,6 +8350,11 @@ static const jmap_property_t email_props[] = {
JMAP_MAIL_EXTENSION,
JMAP_PROP_SERVER_SET | JMAP_PROP_IMMUTABLE | JMAP_PROP_SKIP_GET
},
{
"createdModseq",
JMAP_MAIL_EXTENSION,
JMAP_PROP_SERVER_SET | JMAP_PROP_IMMUTABLE
},
{ NULL, NULL, 0 }
};

Expand Down

0 comments on commit a99fccf

Please sign in to comment.