Skip to content

Commit

Permalink
index.c: use libicalvcard if available
Browse files Browse the repository at this point in the history
  • Loading branch information
ksmurchison committed Jun 23, 2023
1 parent 3921d9b commit b3ceded
Show file tree
Hide file tree
Showing 2 changed files with 206 additions and 0 deletions.
86 changes: 86 additions & 0 deletions cassandane/Cassandane/Cyrus/SearchFuzzy.pm
Original file line number Diff line number Diff line change
Expand Up @@ -1234,6 +1234,92 @@ sub test_search_omit_ical
$self->assert_num_equals(2, scalar @$uids);
}

sub test_search_omit_vcard
:min_version_3_9 :needs_search_xapian
{
my ($self) = @_;

xlog $self, "Generate and index test messages.";

$self->make_message("test",
mime_type => "multipart/related",
mime_boundary => "boundary_1",
body => ""
. "\r\n--boundary_1\r\n"
. "Content-Type: text/plain\r\n"
. "\r\n"
. "txt body"
. "\r\n--boundary_1\r\n"
. "Content-Type: text/vcard;charset=utf-8\r\n"
. "Content-Transfer-Encoding: quoted-printable\r\n"
. "\r\n"
. "BEGIN:VCARD\r\n"
. "VERSION:3.0\r\n"
. "UID:1234567890\r\n"
. "BDAY:1944-06-07\r\n"
. "N:Gump;Forrest;;Mr.\r\n"
. "FN:Forrest Gump\r\n"
. "ORG;PROP-ID=O1:Bubba Gump Shrimp Co.\r\n"
. "TITLE;PROP-ID=T1:Shrimp Man\r\n"
. "PHOTO;PROP-ID=P1;ENCODING=b;TYPE=JPEG:c29tZSBwaG90bw==\r\n"
. "foo.ADR;PROP-ID=A1:;;1501 Broadway;New York;NY;10036;USA\r\n"
. "foo.GEO:40.7571383482188;-73.98695548990568\r\n"
. "foo.TZ:-05:00\r\n"
. "EMAIL;TYPE=PREF:bgump\@example.com\r\n"
. "X-SOCIAL-PROFILE:https://example.com/\@bubba"
. "REV:2008-04-24T19:52:43Z\r\n"
. "END:VCARD\r\n"
. "\r\n--boundary_1--\r\n"
) || die;

$self->make_message("top",
mime_type => "text/vcard",
body => ""
. "BEGIN:VCARD\r\n"
. "VERSION:3.0\r\n"
. "UID:1234567890\r\n"
. "BDAY:1944-06-07\r\n"
. "N:Gump;Forrest;;Mr.\r\n"
. "FN:Forrest Gump\r\n"
. "ORG;PROP-ID=O1:Bubba Gump Shrimp Co.\r\n"
. "TITLE;PROP-ID=T1:Shrimp Man\r\n"
. "PHOTO;PROP-ID=P1;ENCODING=b;TYPE=JPEG:c29tZSBwaG90bw==\r\n"
. "foo.ADR;PROP-ID=A1:;;1501 Broadway;New York;NY;10036;USA\r\n"
. "foo.GEO:40.7571383482188;-73.98695548990568\r\n"
. "foo.TZ:-05:00\r\n"
. "EMAIL;TYPE=PREF:bgump\@example.com\r\n"
. "X-SOCIAL-PROFILE:https://example.com/\@bubba"
. "REV:2008-04-24T19:52:43Z\r\n"
. "END:VCARD\r\n"
) || die;

$self->{instance}->run_command({cyrus => 1}, 'squatter');

my $talk = $self->{store}->get_client();

my $r = $talk->select("INBOX") || die;
my $uidvalidity = $talk->get_response_code('uidvalidity');
my $uids = $talk->search('1:*', 'NOT', 'DELETED');

$uids = $talk->search('fuzzy', 'text', '1944') || die;
$self->assert_num_equals(0, scalar @$uids);

$uids = $talk->search('fuzzy', 'text', 'Forrest') || die;
$self->assert_num_equals(2, scalar @$uids);

$uids = $talk->search('fuzzy', 'text', 'Mr.') || die;
$self->assert_num_equals(2, scalar @$uids);

$uids = $talk->search('fuzzy', 'text', 'Shrimp') || die;
$self->assert_num_equals(2, scalar @$uids);

$uids = $talk->search('fuzzy', 'text', 'example') || die;
$self->assert_num_equals(2, scalar @$uids);

$uids = $talk->search('fuzzy', 'text', 'https') || die;
$self->assert_num_equals(2, scalar @$uids);
}

sub test_xapian_index_partid
:min_version_3_0 :needs_search_xapian :needs_component_jmap
{
Expand Down
120 changes: 120 additions & 0 deletions imap/index.c
Original file line number Diff line number Diff line change
Expand Up @@ -5444,6 +5444,125 @@ static int extract_icalbuf(struct buf *raw, charset_t charset, int encoding,
return r;
}

#ifdef HAVE_LIBICALVCARD

static int extract_vcardbuf(struct buf *raw, charset_t charset, int encoding,
struct getsearchtext_rock *str)
{
vcardcomponent *vcard = NULL;
vcardproperty *prop;
int r = 0;
struct buf buf = BUF_INITIALIZER;

syslog(LOG_INFO, "XXXXXXXX extract_vcardbuf()");
/* Parse the message into a vcard object */
const struct buf *vcardbuf = NULL;
if (encoding || strcasecmp(charset_canon_name(charset), "utf-8")) {
char *tmp = charset_to_utf8(buf_cstring(raw),
buf_len(raw), charset, encoding);
if (!tmp) return 0; /* could be a bogus header - ignore */
buf_initm(&buf, tmp, strlen(tmp));
vcardbuf = &buf;
}
else {
vcardbuf = raw;
}

syslog(LOG_INFO, "XXXXXXXX new_from_string()");
vcard = vcardcomponent_new_from_string(buf_cstring(vcardbuf));
if (!vcard) {
r = IMAP_INTERNAL;
goto done;
}

buf_reset(&buf);

syslog(LOG_INFO, "XXXXXXXX iterate");
// these are all the things that we think might be interesting
for (prop = vcardcomponent_get_first_property(vcard, VCARD_ANY_PROPERTY);
prop;
prop = vcardcomponent_get_next_property(vcard, VCARD_ANY_PROPERTY)) {
vcardstructuredtype *stp = NULL;
vcardstructuredtype st = { 1, { 0 } };
const char *val;

switch (vcardproperty_isa(prop)) {
case VCARD_X_PROPERTY: {
const char *propname = vcardproperty_get_property_name(prop);

if (strcasecmp(propname, "x-social-profile") &&
strcasecmp(propname, "x-fm-online-other")) {
break;
}

GCC_FALLTHROUGH
}

case VCARD_FN_PROPERTY:
case VCARD_EMAIL_PROPERTY:
case VCARD_TEL_PROPERTY:
case VCARD_URL_PROPERTY:
case VCARD_IMPP_PROPERTY:
case VCARD_SOCIALPROFILE_PROPERTY:
case VCARD_NICKNAME_PROPERTY:
case VCARD_NOTE_PROPERTY:
val = vcardproperty_get_value_as_string(prop);
if (val && val[0]) {
if (buf_len(&buf)) buf_putc(&buf, ' ');
buf_appendcstr(&buf, val);
}
break;

case VCARD_ORG_PROPERTY: {
unsigned f;
size_t v;

st.field[0] = vcardproperty_get_org(prop);
stp = &st;

GCC_FALLTHROUGH

case VCARD_N_PROPERTY:
case VCARD_ADR_PROPERTY:
if (!stp) stp = vcardproperty_get_adr(prop);

for (f = 0; f < stp->num_fields; f++) {
vcardstrarray *vals = stp->field[f];

for (v = 0; vals && v < vcardstrarray_size(vals); v++) {
val = vcardstrarray_element_at(vals, v);
if (val && val[0]) {
if (buf_len(&buf)) buf_putc(&buf, ' ');
buf_appendcstr(&buf, val);
}
}
}
break;
}

default:
break;
}
}