"Fossies" - the Fresh Open Source Software Archive

Member "evolution-mapi-3.46.1/src/libexchangemapi/e-mapi-book-utils.c" (2 Dec 2022, 39332 Bytes) of package /linux/misc/evolution-mapi-3.46.1.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "e-mapi-book-utils.c" see the Fossies "Dox" file reference documentation.

    1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
    2 /*
    3  * This program is free software; you can redistribute it and/or
    4  * modify it under the terms of the GNU Lesser General Public
    5  * License as published by the Free Software Foundation; either
    6  * version 2 of the License, or (at your option) version 3.
    7  *
    8  * This program is distributed in the hope that it will be useful,
    9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   11  * Lesser General Public License for more details.
   12  *
   13  * You should have received a copy of the GNU Lesser General Public
   14  * License along with the program; if not, see <http://www.gnu.org/licenses/>
   15  *
   16  *
   17  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
   18  *
   19  */
   20 
   21 #include "evolution-mapi-config.h"
   22 
   23 #include <libedataserver/libedataserver.h>
   24 
   25 #include "e-mapi-book-utils.h"
   26 
   27 #define ELEMENT_TYPE_MASK   0xF /* mask where the real type of the element is stored */
   28 
   29 #define ELEMENT_TYPE_SKIP_SET   0x00
   30 #define ELEMENT_TYPE_SIMPLE 0x01
   31 #define ELEMENT_TYPE_COMPLEX    0x02
   32 
   33 static const struct field_element_mapping {
   34     EContactField field_id;
   35     uint32_t mapi_id;
   36     gint element_type;
   37 } mappings [] = {
   38     { E_CONTACT_UID,        PidTagMid,          ELEMENT_TYPE_SKIP_SET },
   39     { E_CONTACT_REV,        PidTagLastModificationTime, ELEMENT_TYPE_SIMPLE },
   40 
   41     { E_CONTACT_FILE_AS,        PidLidFileUnder,        ELEMENT_TYPE_SIMPLE },
   42     { E_CONTACT_FULL_NAME,      PidTagDisplayName,      ELEMENT_TYPE_SIMPLE },
   43     { E_CONTACT_GIVEN_NAME,     PidTagGivenName,        ELEMENT_TYPE_SIMPLE },
   44     { E_CONTACT_FAMILY_NAME,    PidTagSurname,          ELEMENT_TYPE_SIMPLE },
   45     { E_CONTACT_NICKNAME,       PidTagNickname,         ELEMENT_TYPE_SIMPLE },
   46     { E_CONTACT_PHOTO,      PidLidHasPicture,       ELEMENT_TYPE_COMPLEX },
   47 
   48     { E_CONTACT_EMAIL_1,        PidLidEmail1OriginalDisplayName,ELEMENT_TYPE_SIMPLE },
   49     { E_CONTACT_EMAIL_2,        PidLidEmail2EmailAddress,   ELEMENT_TYPE_SIMPLE },
   50     { E_CONTACT_EMAIL_3,        PidLidEmail3EmailAddress,   ELEMENT_TYPE_SIMPLE },
   51     { E_CONTACT_IM_AIM,     PidLidInstantMessagingAddress,  ELEMENT_TYPE_COMPLEX },
   52 
   53     { E_CONTACT_PHONE_BUSINESS, PidTagBusinessTelephoneNumber,  ELEMENT_TYPE_SIMPLE },
   54     { E_CONTACT_PHONE_HOME,     PidTagHomeTelephoneNumber,  ELEMENT_TYPE_SIMPLE },
   55     { E_CONTACT_PHONE_MOBILE,   PidTagMobileTelephoneNumber,    ELEMENT_TYPE_SIMPLE },
   56     { E_CONTACT_PHONE_HOME_FAX, PidTagHomeFaxNumber,        ELEMENT_TYPE_SIMPLE },
   57     { E_CONTACT_PHONE_BUSINESS_FAX, PidTagBusinessFaxNumber,    ELEMENT_TYPE_SIMPLE },
   58     { E_CONTACT_PHONE_PAGER,    PidTagPagerTelephoneNumber, ELEMENT_TYPE_SIMPLE },
   59     { E_CONTACT_PHONE_ASSISTANT,    PidTagAssistantTelephoneNumber, ELEMENT_TYPE_SIMPLE },
   60     { E_CONTACT_PHONE_COMPANY,  PidTagCompanyMainTelephoneNumber,ELEMENT_TYPE_SIMPLE },
   61 
   62     { E_CONTACT_HOMEPAGE_URL,   PidLidHtml,         ELEMENT_TYPE_SIMPLE },
   63     { E_CONTACT_FREEBUSY_URL,   PidLidFreeBusyLocation,     ELEMENT_TYPE_SIMPLE },
   64 
   65     { E_CONTACT_ROLE,       PidTagProfession,       ELEMENT_TYPE_SIMPLE },
   66     { E_CONTACT_TITLE,      PidTagTitle,            ELEMENT_TYPE_SIMPLE },
   67     { E_CONTACT_ORG,        PidTagCompanyName,      ELEMENT_TYPE_SIMPLE },
   68     { E_CONTACT_ORG_UNIT,       PidTagDepartmentName,       ELEMENT_TYPE_SIMPLE },
   69     { E_CONTACT_MANAGER,        PidTagManagerName,      ELEMENT_TYPE_SIMPLE },
   70     { E_CONTACT_ASSISTANT,      PidTagAssistant,        ELEMENT_TYPE_SIMPLE },
   71 
   72     { E_CONTACT_OFFICE,     PidTagOfficeLocation,       ELEMENT_TYPE_SIMPLE },
   73     { E_CONTACT_SPOUSE,     PidTagSpouseName,       ELEMENT_TYPE_SIMPLE },
   74 
   75     { E_CONTACT_BIRTH_DATE,     PidTagBirthday,         ELEMENT_TYPE_COMPLEX },
   76     { E_CONTACT_ANNIVERSARY,    PidTagWeddingAnniversary,   ELEMENT_TYPE_COMPLEX },
   77 
   78     { E_CONTACT_NOTE,       PidTagBody,         ELEMENT_TYPE_SIMPLE },
   79 
   80     { E_CONTACT_ADDRESS_HOME,   PidLidHomeAddress,      ELEMENT_TYPE_COMPLEX },
   81     { E_CONTACT_ADDRESS_WORK,   PidLidOtherAddress,     ELEMENT_TYPE_COMPLEX }
   82 };
   83 
   84 /* extra properties used in ELEMENT_TYPE_COMPLEX types and some other etra properties */
   85 static const uint32_t extra_proptags[] = {
   86     PidTagHomeAddressPostOfficeBox,
   87     PidTagHomeAddressCity,
   88     PidTagHomeAddressStateOrProvince,
   89     PidTagHomeAddressPostalCode,
   90     PidTagHomeAddressCountry,
   91     PidTagPostOfficeBox,
   92     PidTagLocality,
   93     PidTagStateOrProvince,
   94     PidTagPostalCode,
   95     PidTagCountry,
   96     PidTagSmtpAddress,
   97     PidTagFolderId
   98 };
   99 
  100 static gchar *
  101 bin_to_string (const uint8_t *lpb, uint32_t cb)
  102 {
  103     gchar *res, *p;
  104     uint32_t i;
  105 
  106     g_return_val_if_fail (lpb != NULL, NULL);
  107     g_return_val_if_fail (cb > 0, NULL);
  108 
  109     res = g_new0 (gchar, cb * 2 + 1);
  110     for (i = 0, p = res; i < cb; i++, p += 2) {
  111         sprintf (p, "%02x", lpb[i] & 0xFF);
  112     }
  113 
  114     return res;
  115 }
  116 
  117 static const gchar *
  118 not_null (gconstpointer ptr)
  119 {
  120     return ptr ? (const gchar *) ptr : "";
  121 }
  122 
  123 EContact *
  124 e_mapi_book_utils_contact_from_object (EMapiConnection *conn,
  125                        EMapiObject *object,
  126                        const gchar *book_uri)
  127 {
  128     EContact *contact;
  129     gchar *email_1;
  130     const mapi_id_t *pmid;
  131     gint i;
  132 
  133     g_return_val_if_fail (object != NULL, NULL);
  134 
  135     if (e_mapi_debug_is_enabled ()) {
  136         printf ("%s:\n", G_STRFUNC);
  137         e_mapi_debug_dump_object (object, TRUE, 3);
  138     }
  139 
  140     contact = e_contact_new ();
  141     if (book_uri)
  142         e_contact_set (contact, E_CONTACT_BOOK_UID, book_uri);
  143 
  144     #define get_proptag(proptag) e_mapi_util_find_array_propval (&object->properties, proptag)
  145     #define get_str_proptag(proptag) not_null (get_proptag (proptag))
  146 
  147     pmid = get_proptag (PidTagMid);
  148     if (pmid) {
  149         gchar *suid = e_mapi_util_mapi_id_to_string (*pmid);
  150 
  151         e_contact_set (contact, E_CONTACT_UID, suid);
  152 
  153         g_free (suid);
  154     }
  155 
  156     if (g_str_equal (get_str_proptag (PidTagMessageClass), IPM_DISTLIST)) {
  157         const struct mapi_SBinaryArray *members, *members_dlist;
  158         const struct FILETIME *last_modification;
  159         GSList *attrs = NULL, *a;
  160         gint i;
  161 
  162         last_modification = get_proptag (PidTagLastModificationTime);
  163         if (last_modification) {
  164             gchar *buff = NULL;
  165 
  166             buff = e_mapi_book_utils_timet_to_string (e_mapi_util_filetime_to_time_t (last_modification));
  167             if (buff)
  168                 e_contact_set (contact, E_CONTACT_REV, buff);
  169 
  170             g_free (buff);
  171         }
  172 
  173         /* it's a contact list/distribution list, fetch members and return it */
  174         e_contact_set (contact, E_CONTACT_IS_LIST, GINT_TO_POINTER (TRUE));
  175         /* we do not support this option, same as GroupWise */
  176         e_contact_set (contact, E_CONTACT_LIST_SHOW_ADDRESSES, GINT_TO_POINTER (TRUE));
  177 
  178         e_contact_set (contact, E_CONTACT_FILE_AS, get_str_proptag (PidLidDistributionListName));
  179 
  180         members = get_proptag (PidLidDistributionListOneOffMembers);
  181         members_dlist = get_proptag (PidLidDistributionListMembers);
  182 
  183         g_return_val_if_fail (members != NULL, NULL);
  184         g_return_val_if_fail (members_dlist != NULL, NULL);
  185 
  186         /* these two lists should be in sync */
  187         g_return_val_if_fail (members_dlist->cValues == members->cValues, NULL);
  188 
  189         for (i = 0; i < members->cValues; i++) {
  190             struct Binary_r br;
  191             gchar *display_name = NULL, *email = NULL;
  192             gchar *str;
  193 
  194             br.lpb = members->bin[i].lpb;
  195             br.cb = members->bin[i].cb;
  196             if (e_mapi_util_recip_entryid_decode (conn, &br, &display_name, &email)) {
  197                 EVCardAttribute *attr;
  198                 gchar *value;
  199                 CamelInternetAddress *addr;
  200 
  201                 addr = camel_internet_address_new ();
  202                 attr = e_vcard_attribute_new (NULL, EVC_EMAIL);
  203 
  204                 camel_internet_address_add (addr, display_name, email);
  205 
  206                 value = camel_address_encode (CAMEL_ADDRESS (addr));
  207 
  208                 if (value)
  209                     e_vcard_attribute_add_value (attr, value);
  210 
  211                 g_free (value);
  212                 g_object_unref (addr);
  213 
  214                 str = g_strdup_printf ("%d", i + 1);
  215                 e_vcard_attribute_add_param_with_value (attr,
  216                         e_vcard_attribute_param_new (EMA_X_MEMBERID),
  217                         str);
  218                 g_free (str);
  219 
  220                 /* keep the value from ListMembers with the email, to not need to generate it on list changes;
  221                    new values added in evolution-mapi will be always SMTP addresses anyway */
  222                 str = bin_to_string (members_dlist->bin[i].lpb, members_dlist->bin[i].cb);
  223                 if (str) {
  224                     e_vcard_attribute_add_param_with_value (attr,
  225                         e_vcard_attribute_param_new (EMA_X_MEMBERVALUE),
  226                         str);
  227                     g_free (str);
  228                 }
  229 
  230                 attrs = g_slist_prepend (attrs, attr);
  231             }
  232 
  233             g_free (display_name);
  234             g_free (email);
  235         }
  236 
  237         for (a = attrs; a; a = a->next) {
  238             e_vcard_add_attribute (E_VCARD (contact), a->data);
  239         }
  240 
  241         g_slist_free (attrs);
  242 
  243         return contact;
  244     }
  245 
  246     for (i = 0; i < G_N_ELEMENTS (mappings); i++) {
  247         gpointer value;
  248         gint element_type;
  249 
  250         /* can cast value, no writing to the value; and it'll be freed not before the end of this function */
  251         value = (gpointer) get_proptag (mappings[i].mapi_id);
  252         element_type = mappings[i].element_type & ELEMENT_TYPE_MASK;
  253         if (element_type == ELEMENT_TYPE_SKIP_SET) {
  254             /* skip, when asked for */
  255         } else if (element_type == ELEMENT_TYPE_SIMPLE) {
  256             switch (mappings[i].mapi_id & 0xFFFF) {
  257                 case PT_UNICODE: {
  258                     const gchar *str = value;
  259                     if (str && *str)
  260                         e_contact_set (contact, mappings[i].field_id, str);
  261                 } break;
  262                 case PT_SYSTIME: {
  263                     const struct FILETIME *t = value;
  264                     gchar *buff = NULL;
  265 
  266                     buff = e_mapi_book_utils_timet_to_string (e_mapi_util_filetime_to_time_t (t));
  267                     if (buff)
  268                         e_contact_set (contact, mappings[i].field_id, buff);
  269 
  270                     g_free (buff);
  271                 } break;
  272                 default:
  273                     /* ignore everything else */
  274                     break;
  275             }
  276         } else if (element_type == ELEMENT_TYPE_COMPLEX) {
  277             if (mappings[i].field_id == E_CONTACT_IM_AIM) {
  278                 const gchar *str = value;
  279                 if (str && *str) {
  280                     GList *list = g_list_append (NULL, (gpointer) str);
  281 
  282                     e_contact_set (contact, mappings[i].field_id, list);
  283 
  284                     g_list_free (list);
  285                 }
  286             } else if (mappings[i].field_id == E_CONTACT_BIRTH_DATE
  287                    || mappings[i].field_id == E_CONTACT_ANNIVERSARY) {
  288                 const struct FILETIME *t = value;
  289                 time_t time;
  290                 struct tm * tmtime;
  291                 if (value) {
  292                     EContactDate date = {0};
  293 
  294                     time = e_mapi_util_filetime_to_time_t (t);
  295                     tmtime = gmtime (&time);
  296 
  297                     date.day = tmtime->tm_mday;
  298                     date.month = tmtime->tm_mon + 1;
  299                     date.year = tmtime->tm_year + 1900;
  300                     e_contact_set (contact, mappings[i].field_id, &date);
  301                 }
  302 
  303             } else if (mappings[i].field_id == E_CONTACT_ADDRESS_WORK
  304                    || mappings[i].field_id == E_CONTACT_ADDRESS_HOME) {
  305                 EContactAddress contact_addr = { 0 };
  306 
  307                 /* type-casting below to not allocate memory twice; e_contact_set will copy values itself. */
  308                 if (mappings[i].field_id == E_CONTACT_ADDRESS_HOME) {
  309                     contact_addr.address_format = NULL;
  310                     contact_addr.po = NULL;
  311                     contact_addr.street = (gchar *) value;
  312                     contact_addr.ext = (gchar *) get_str_proptag (PidTagHomeAddressPostOfficeBox);
  313                     contact_addr.locality = (gchar *) get_str_proptag (PidTagHomeAddressCity);
  314                     contact_addr.region = (gchar *) get_str_proptag (PidTagHomeAddressStateOrProvince);
  315                     contact_addr.code = (gchar *) get_str_proptag (PidTagHomeAddressPostalCode);
  316                     contact_addr.country = (gchar *) get_str_proptag (PidTagHomeAddressCountry);
  317                 } else {
  318                     contact_addr.address_format = NULL;
  319                     contact_addr.po = NULL;
  320                     contact_addr.street = (gchar *) value;
  321                     contact_addr.ext = (gchar *) get_str_proptag (PidTagPostOfficeBox);
  322                     contact_addr.locality = (gchar *) get_str_proptag (PidTagLocality);
  323                     contact_addr.region = (gchar *) get_str_proptag (PidTagStateOrProvince);
  324                     contact_addr.code = (gchar *) get_str_proptag (PidTagPostalCode);
  325                     contact_addr.country = (gchar *) get_str_proptag (PidTagCountry);
  326                 }
  327 
  328                 #define is_set(x) ((x) && *(x))
  329                 if (is_set (contact_addr.address_format) ||
  330                     is_set (contact_addr.po) ||
  331                     is_set (contact_addr.street) ||
  332                     is_set (contact_addr.ext) ||
  333                     is_set (contact_addr.locality) ||
  334                     is_set (contact_addr.region) ||
  335                     is_set (contact_addr.code) ||
  336                     is_set (contact_addr.country)) {
  337                     e_contact_set (contact, mappings[i].field_id, &contact_addr);
  338                 }
  339                 #undef is_set
  340             } else if (mappings[i].field_id == E_CONTACT_PHOTO) {
  341                 if (object->attachments) {
  342                     EMapiAttachment *attachment;
  343 
  344                     for (attachment = object->attachments; attachment; attachment = attachment->next) {
  345                         const uint8_t *bval = e_mapi_util_find_array_propval (&attachment->properties, PidTagAttachmentContactPhoto);
  346 
  347                         if (bval && *bval) {
  348                             uint64_t data_cb = 0;
  349                             const uint8_t *data_lpb = NULL;
  350 
  351                             if (e_mapi_attachment_get_bin_prop (attachment, PidTagAttachDataBinary, &data_cb, &data_lpb) && data_cb > 0) {
  352                                 EContactPhoto photo;
  353 
  354                                 photo.type = E_CONTACT_PHOTO_TYPE_INLINED;
  355                                 photo.data.inlined.mime_type = NULL;
  356                                 photo.data.inlined.length = data_cb;
  357                                 photo.data.inlined.data = (guchar *) data_lpb;
  358 
  359                                 e_contact_set (contact, E_CONTACT_PHOTO, &photo);
  360                             }
  361 
  362                             break;
  363                         }
  364                     }
  365                 }
  366             }
  367         }
  368     }
  369 
  370     email_1 = e_contact_get (contact, E_CONTACT_EMAIL_1);
  371     if (!email_1) {
  372         gconstpointer value = get_proptag (PidTagSmtpAddress);
  373 
  374         if (value)
  375             e_contact_set (contact, E_CONTACT_EMAIL_1, value);
  376     }
  377 
  378     g_free (email_1);
  379 
  380     #undef get_proptag
  381     #undef get_str_proptag
  382 
  383     return contact;
  384 }
  385 
  386 static uint32_t
  387 string_to_bin (TALLOC_CTX *mem_ctx, const gchar *str, uint8_t **lpb)
  388 {
  389     uint32_t len, i;
  390 
  391     g_return_val_if_fail (str != NULL, 0);
  392     g_return_val_if_fail (lpb != NULL, 0);
  393 
  394     len = strlen (str);
  395     g_return_val_if_fail ((len & 1) == 0, 0);
  396 
  397     len = len / 2;
  398     *lpb = talloc_zero_array (mem_ctx, uint8_t, len);
  399 
  400     i = 0;
  401     while (*str && i < len) {
  402         gchar c1 = str[0], c2 = str[1];
  403         str += 2;
  404 
  405         g_return_val_if_fail ((c1 >= '0' && c1 <= '9') || (c1 >= 'a' && c1 <= 'f') || (c1 >= 'A' && c1 <= 'F'), 0);
  406         g_return_val_if_fail ((c2 >= '0' && c2 <= '9') || (c2 >= 'a' && c2 <= 'f') || (c2 >= 'A' && c2 <= 'F'), 0);
  407 
  408         #define deHex(x) (((x) >= '0' && (x) <= '9') ? ((x) - '0') : (((x) >= 'a' && (x) <= 'f') ? (x) - 'a' + 10 : (x) - 'A' + 10))
  409         (*lpb)[i] = (deHex (c1) << 4) | (deHex (c2));
  410         #undef deHex
  411         i++;
  412     }
  413 
  414     return len;
  415 }
  416 
  417 static gint
  418 cmp_member_id (gconstpointer a, gconstpointer b, gpointer ht)
  419 {
  420     gchar *va, *vb;
  421     gint res;
  422 
  423     if (!a)
  424         return b ? -1 : 0;
  425     if (!b)
  426         return 1;
  427 
  428     va = e_vcard_attribute_get_value ((EVCardAttribute *) a);
  429     vb = e_vcard_attribute_get_value ((EVCardAttribute *) b);
  430 
  431     res = GPOINTER_TO_INT (g_hash_table_lookup (ht, va)) - GPOINTER_TO_INT (g_hash_table_lookup (ht, vb));
  432 
  433     g_free (va);
  434     g_free (vb);
  435 
  436     return res;
  437 }
  438 
  439 gboolean
  440 e_mapi_book_utils_contact_to_object (EContact *contact,
  441                      EContact *old_contact, /* can be NULL */
  442                      EMapiObject **pobject,
  443                      TALLOC_CTX *mem_ctx,
  444                      GCancellable *cancellable,
  445                      GError **perror)
  446 {
  447     EMapiObject *object;
  448     EContactPhoto *photo;
  449 
  450     #define set_value(hex, val) G_STMT_START { \
  451         if (!e_mapi_utils_add_property (&object->properties, hex, val, object)) \
  452             return FALSE;   \
  453         } G_STMT_END
  454 
  455     #define set_con_value(hex, field_id) G_STMT_START { \
  456         if (e_contact_get (contact, field_id)) { \
  457             set_value (hex, e_contact_get (contact, field_id)); \
  458         } } G_STMT_END
  459 
  460     #define set_attach_value(pt,vl) {                           \
  461         if (!e_mapi_utils_add_property (&attachment->properties, pt, vl, attachment)) { \
  462             g_warning ("%s: Failed to set property 0x%x", G_STRFUNC, pt);       \
  463             return FALSE;                               \
  464         }                                       \
  465     }
  466 
  467     g_return_val_if_fail (contact != NULL, FALSE);
  468     g_return_val_if_fail (mem_ctx != NULL, FALSE);
  469     g_return_val_if_fail (pobject != NULL, FALSE);
  470 
  471     object = e_mapi_object_new (mem_ctx);
  472     *pobject = object;
  473 
  474     if (GPOINTER_TO_INT (e_contact_get (contact, E_CONTACT_IS_LIST))) {
  475         GList *local, *l;
  476         struct BinaryArray_r *members, *oneoff_members;
  477         uint32_t u32, crc32 = 0;
  478         GHashTable *member_values = NULL, *member_ids = NULL;
  479 
  480         if (old_contact) {
  481             member_values = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
  482             member_ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
  483 
  484             local = e_contact_get_attributes (old_contact, E_CONTACT_EMAIL);
  485             for (l = local; l; l = l->next) {
  486                 EVCardAttribute *attr = l->data;
  487                 GList *param;
  488 
  489                 if (!attr)
  490                     continue;
  491 
  492                 param = e_vcard_attribute_get_param (attr, EMA_X_MEMBERVALUE);
  493                 if (param && param->data && !param->next) {
  494                     g_hash_table_insert (member_values, e_vcard_attribute_get_value (attr), g_strdup (param->data));
  495                 }
  496 
  497                 param = e_vcard_attribute_get_param (attr, EMA_X_MEMBERID);
  498                 if (param && param->data && !param->next) {
  499                     g_hash_table_insert (member_ids, e_vcard_attribute_get_value (attr), GINT_TO_POINTER (atoi (param->data)));
  500                 }
  501             }
  502 
  503             g_list_free_full (local, (GDestroyNotify) e_vcard_attribute_free);
  504         }
  505 
  506         set_value (PidTagMessageClass, IPM_DISTLIST);
  507         u32 = 0xFFFFFFFF;
  508         set_value (PidLidFileUnderId, &u32);
  509         set_con_value (PidLidFileUnder, E_CONTACT_FILE_AS);
  510         set_con_value (PidLidDistributionListName, E_CONTACT_FILE_AS);
  511         set_con_value (PidTagDisplayName, E_CONTACT_FILE_AS);
  512         set_con_value (PidTagNormalizedSubject, E_CONTACT_FILE_AS);
  513 
  514         local = e_contact_get_attributes (contact, E_CONTACT_EMAIL);
  515         if (member_ids)
  516             local = g_list_sort_with_data (local, cmp_member_id, member_ids);
  517 
  518         members = talloc_zero (mem_ctx, struct BinaryArray_r);
  519         members->cValues = 0;
  520         members->lpbin = talloc_zero_array (mem_ctx, struct Binary_r, g_list_length (local));
  521 
  522         oneoff_members = talloc_zero (mem_ctx, struct BinaryArray_r);
  523         oneoff_members->cValues = 0;
  524         oneoff_members->lpbin = talloc_zero_array (mem_ctx, struct Binary_r, g_list_length (local));
  525 
  526         for (l = local; l; l = l->next) {
  527             EVCardAttribute *attr = (EVCardAttribute *) l->data;
  528             gchar *raw;
  529             CamelInternetAddress *addr;
  530 
  531             if (!attr)
  532                 continue;
  533 
  534             raw = e_vcard_attribute_get_value (attr);
  535             if (!raw)
  536                 continue;
  537 
  538             addr = camel_internet_address_new ();
  539             if (camel_address_decode (CAMEL_ADDRESS (addr), raw) > 0) {
  540                 const gchar *nm = NULL, *eml = NULL;
  541 
  542                 if (camel_internet_address_get (addr, 0, &nm, &eml) && eml) {
  543                     /* keep both lists in sync */
  544                     if (member_values && g_hash_table_lookup (member_values, raw)) {
  545                         /* stored ListMembers values when contact's value didn't change */
  546                         members->lpbin[members->cValues].cb = string_to_bin (mem_ctx, g_hash_table_lookup (member_values, raw), &members->lpbin[members->cValues].lpb);
  547                         members->cValues++;
  548                     } else {
  549                         e_mapi_util_recip_entryid_generate_smtp (mem_ctx, &members->lpbin[members->cValues], nm ? nm : "", eml);
  550                         members->cValues++;
  551                     }
  552 
  553                     e_mapi_util_recip_entryid_generate_smtp (mem_ctx, &oneoff_members->lpbin[oneoff_members->cValues], nm ? nm : "", eml);
  554                     oneoff_members->cValues++;
  555 
  556                     crc32 = e_mapi_utils_push_crc32 (crc32, members->lpbin[members->cValues - 1].lpb, members->lpbin[members->cValues - 1].cb);
  557                 }
  558             }
  559 
  560             g_object_unref (addr);
  561             g_free (raw);
  562         }
  563 
  564         if (member_values)
  565             g_hash_table_destroy (member_values);
  566         if (member_ids)
  567             g_hash_table_destroy (member_ids);
  568         g_list_foreach (local, (GFunc)e_vcard_attribute_free, NULL);
  569         g_list_free (local);
  570 
  571         set_value (PidLidDistributionListOneOffMembers, oneoff_members);
  572         set_value (PidLidDistributionListMembers, members);
  573         set_value (PidLidDistributionListChecksum, &crc32);
  574 
  575         if (e_mapi_debug_is_enabled ()) {
  576             printf ("%s:\n", G_STRFUNC);
  577             e_mapi_debug_dump_object (object, TRUE, 3);
  578         }
  579 
  580         return TRUE;
  581     }
  582 
  583     set_value (PidTagMessageClass, IPM_CONTACT);
  584     set_con_value (PidLidFileUnder, E_CONTACT_FILE_AS);
  585 
  586     set_con_value (PidTagDisplayName, E_CONTACT_FULL_NAME);
  587     set_con_value (PidTagNormalizedSubject, E_CONTACT_FILE_AS);
  588     set_con_value (PidLidEmail1OriginalDisplayName, E_CONTACT_EMAIL_1);
  589     /*set_con_value (PidLidEmail1EmailAddress, E_CONTACT_EMAIL_1);*/
  590 
  591     /*set_con_value (0x8083001e, E_CONTACT_EMAIL_1);*/
  592     set_con_value (PidLidEmail2EmailAddress, E_CONTACT_EMAIL_2);
  593 
  594     set_con_value (PidLidEmail3EmailAddress, E_CONTACT_EMAIL_3);
  595     /*set_con_value (PidLidEmail3OriginalDisplayName, E_CONTACT_EMAIL_3);*/
  596 
  597     set_con_value (PidLidHtml, E_CONTACT_HOMEPAGE_URL);
  598     set_con_value (PidLidFreeBusyLocation, E_CONTACT_FREEBUSY_URL);
  599 
  600     set_con_value (PidTagBusinessTelephoneNumber, E_CONTACT_PHONE_BUSINESS);
  601     set_con_value (PidTagHomeTelephoneNumber, E_CONTACT_PHONE_HOME);
  602     set_con_value (PidTagMobileTelephoneNumber, E_CONTACT_PHONE_MOBILE);
  603     set_con_value (PidTagHomeFaxNumber, E_CONTACT_PHONE_HOME_FAX);
  604     set_con_value (PidTagBusinessFaxNumber, E_CONTACT_PHONE_BUSINESS_FAX);
  605     set_con_value (PidTagPagerTelephoneNumber, E_CONTACT_PHONE_PAGER);
  606     set_con_value (PidTagAssistantTelephoneNumber, E_CONTACT_PHONE_ASSISTANT);
  607     set_con_value (PidTagCompanyMainTelephoneNumber, E_CONTACT_PHONE_COMPANY);
  608 
  609     set_con_value (PidTagManagerName, E_CONTACT_MANAGER);
  610     set_con_value (PidTagAssistant, E_CONTACT_ASSISTANT);
  611     set_con_value (PidTagCompanyName, E_CONTACT_ORG);
  612     set_con_value (PidTagDepartmentName, E_CONTACT_ORG_UNIT);
  613     set_con_value (PidTagProfession, E_CONTACT_ROLE);
  614     set_con_value (PidTagTitle, E_CONTACT_TITLE);
  615 
  616     set_con_value (PidTagOfficeLocation, E_CONTACT_OFFICE);
  617     set_con_value (PidTagSpouseName, E_CONTACT_SPOUSE);
  618 
  619     set_con_value (PidTagBody, E_CONTACT_NOTE);
  620     set_con_value (PidTagNickname, E_CONTACT_NICKNAME);
  621 
  622     /* BDAY AND ANNV */
  623     if (e_contact_get (contact, E_CONTACT_BIRTH_DATE)) {
  624         EContactDate *date = e_contact_get (contact, E_CONTACT_BIRTH_DATE);
  625         struct tm tmtime = { 0 };
  626         struct FILETIME t;
  627 
  628         tmtime.tm_mday = date->day;
  629         tmtime.tm_mon = date->month - 1;
  630         tmtime.tm_year = date->year - 1900;
  631 
  632         e_mapi_util_time_t_to_filetime (mktime (&tmtime) + (24 * 60 * 60), &t);
  633 
  634         set_value (PidTagBirthday, &t);
  635     }
  636 
  637     if (e_contact_get (contact, E_CONTACT_ANNIVERSARY)) {
  638         EContactDate *date = e_contact_get (contact, E_CONTACT_ANNIVERSARY);
  639         struct tm tmtime = { 0 };
  640         struct FILETIME t;
  641 
  642         tmtime.tm_mday = date->day;
  643         tmtime.tm_mon = date->month - 1;
  644         tmtime.tm_year = date->year - 1900;
  645 
  646         e_mapi_util_time_t_to_filetime (mktime (&tmtime) + (24 * 60 * 60), &t);
  647 
  648         set_value (PidTagWeddingAnniversary, &t);
  649     }
  650 
  651     /* Home and Office address */
  652     if (e_contact_get (contact, E_CONTACT_ADDRESS_HOME)) {
  653         EContactAddress *contact_addr = e_contact_get (contact, E_CONTACT_ADDRESS_HOME);
  654 
  655         set_value (PidLidHomeAddress, contact_addr->street);
  656         set_value (PidTagHomeAddressPostOfficeBox, contact_addr->ext);
  657         set_value (PidTagHomeAddressCity, contact_addr->locality);
  658         set_value (PidTagHomeAddressStateOrProvince, contact_addr->region);
  659         set_value (PidTagHomeAddressPostalCode, contact_addr->code);
  660         set_value (PidTagHomeAddressCountry, contact_addr->country);
  661     }
  662 
  663     if (e_contact_get (contact, E_CONTACT_ADDRESS_WORK)) {
  664         EContactAddress *contact_addr = e_contact_get (contact, E_CONTACT_ADDRESS_WORK);
  665 
  666         set_value (PidLidWorkAddress, contact_addr->street);
  667         set_value (PidTagPostOfficeBox, contact_addr->ext);
  668         set_value (PidTagLocality, contact_addr->locality);
  669         set_value (PidTagStateOrProvince, contact_addr->region);
  670         set_value (PidTagPostalCode, contact_addr->code);
  671         set_value (PidTagCountry, contact_addr->country);
  672     }
  673 
  674     if (e_contact_get (contact, E_CONTACT_IM_AIM)) {
  675         GList *l = e_contact_get (contact, E_CONTACT_IM_AIM);
  676         set_value (PidLidInstantMessagingAddress, l->data);
  677     }
  678 
  679     photo = e_contact_get (contact, E_CONTACT_PHOTO);
  680     if (photo) {
  681         gchar *content = NULL;
  682         gsize length = 0;
  683         gboolean do_free = FALSE;
  684 
  685         if (photo->type == E_CONTACT_PHOTO_TYPE_INLINED) {
  686             content = (gchar *) photo->data.inlined.data;
  687             length = photo->data.inlined.length;
  688         } else if (photo->type == E_CONTACT_PHOTO_TYPE_URI) {
  689             gchar *filename = g_filename_from_uri (photo->data.uri, NULL, NULL);
  690             if (filename) {
  691                 if (!g_file_get_contents (filename, &content, &length, NULL)) {
  692                     content = NULL;
  693                     length = 0;
  694                 } else {
  695                     do_free = TRUE;
  696                 }
  697 
  698                 g_free (filename);
  699             }
  700         }
  701 
  702         if (content && length > 0) {
  703             EMapiAttachment *attachment = e_mapi_attachment_new (mem_ctx);
  704             if (attachment) {
  705                 uint32_t ui32;
  706                 uint8_t bval, *lpb;
  707 
  708                 ui32 = ATTACH_BY_VALUE;
  709                 set_attach_value (PidTagAttachMethod, &ui32);
  710                 ui32 = -1;
  711                 set_attach_value (PidTagRenderingPosition, &ui32);
  712 
  713                 /* intentionally not localized, these values are the requirement */
  714                 set_attach_value (PidTagDisplayName, "ContactPicture.jpg");
  715                 set_attach_value (PidTagAttachFilename, "ContactPicture.jpg");
  716                 set_attach_value (PidTagAttachExtension, ".jpg");
  717 
  718                 bval = 1;
  719                 set_attach_value (PidTagAttachmentContactPhoto, &bval);
  720 
  721                 lpb = talloc_realloc (attachment, NULL, uint8_t, length);
  722                 memcpy (lpb, content, length);
  723 
  724                 e_mapi_attachment_add_streamed (attachment, PidTagAttachDataBinary, (uint64_t) length, lpb);
  725 
  726                 e_mapi_object_add_attachment (object, attachment);
  727 
  728                 bval = 1;
  729                 set_value (PidLidHasPicture, &bval);
  730             }
  731         } else {
  732             uint8_t bval = 0;
  733             set_value (PidLidHasPicture, &bval);
  734         }
  735 
  736         if (do_free)
  737             g_free (content);
  738         e_contact_photo_free (photo);
  739     } else {
  740         uint8_t bval = 0;
  741         set_value (PidLidHasPicture, &bval);
  742     }
  743 
  744     #undef set_value
  745     #undef set_con_value
  746     #undef set_attach_value
  747 
  748     if (e_mapi_debug_is_enabled ()) {
  749         printf ("%s:\n", G_STRFUNC);
  750         e_mapi_debug_dump_object (object, TRUE, 3);
  751     }
  752 
  753     return TRUE;
  754 }
  755 
  756 gchar *
  757 e_mapi_book_utils_timet_to_string (time_t tt)
  758 {
  759     GTimeVal tv;
  760 
  761     tv.tv_sec = tt;
  762     tv.tv_usec = 0;
  763 
  764     return g_time_val_to_iso8601 (&tv);
  765 }
  766 
  767 struct EMapiSExpParserData
  768 {
  769     TALLOC_CTX *mem_ctx;
  770     /* parser results in ints, indexes to res_parts */
  771     GPtrArray *res_parts;
  772 };
  773 
  774 static ESExpResult *
  775 term_eval_and (struct _ESExp *f,
  776            gint argc,
  777            struct _ESExpResult **argv,
  778            gpointer user_data)
  779 {
  780     struct EMapiSExpParserData *esp = user_data;
  781     ESExpResult *r;
  782     gint ii, jj, valid = 0;
  783 
  784     r = e_sexp_result_new (f, ESEXP_RES_INT);
  785     r->value.number = -1;
  786 
  787     for (ii = 0; ii < argc; ii++) {
  788         if (argv[ii]->type == ESEXP_RES_INT &&
  789             argv[ii]->value.number >= 0 && 
  790             argv[ii]->value.number < esp->res_parts->len) {
  791             struct mapi_SRestriction *subres = g_ptr_array_index (esp->res_parts, argv[ii]->value.number);
  792 
  793             jj = argv[ii]->value.number;
  794             valid++;
  795 
  796             /* join two consecutive AND-s into one */
  797             if (subres->rt == RES_AND)
  798                 valid += subres->res.resAnd.cRes - 1;
  799         }
  800     }
  801 
  802     if (valid == 1) {
  803         r->value.number = jj;
  804     } else if (valid > 0) {
  805         struct mapi_SRestriction *res = talloc_zero (esp->mem_ctx, struct mapi_SRestriction);
  806         g_return_val_if_fail (res != NULL, NULL);
  807 
  808         res->rt = RES_AND;
  809         res->res.resAnd.cRes = valid;
  810         res->res.resAnd.res = talloc_zero_array (esp->mem_ctx, struct mapi_SRestriction_and, res->res.resAnd.cRes + 1);
  811 
  812         jj = 0;
  813 
  814         for (ii = 0; ii < argc; ii++) {
  815             if (argv[ii]->type == ESEXP_RES_INT &&
  816                 argv[ii]->value.number >= 0 && 
  817                 argv[ii]->value.number < esp->res_parts->len) {
  818                 struct mapi_SRestriction *subres = g_ptr_array_index (esp->res_parts, argv[ii]->value.number);
  819 
  820                 /* join two consecutive AND-s into one */
  821                 if (subres->rt == RES_AND) {
  822                     gint xx;
  823 
  824                     for (xx = 0; xx < subres->res.resAnd.cRes; xx++, jj++) {
  825                         res->res.resAnd.res[jj].rt = subres->res.resAnd.res[xx].rt;
  826                         res->res.resAnd.res[jj].res = subres->res.resAnd.res[xx].res;
  827                     }
  828                 } else {
  829                     res->res.resAnd.res[jj].rt = subres->rt;
  830                     res->res.resAnd.res[jj].res = subres->res;
  831 
  832                     jj++;
  833                 }
  834             }
  835         }
  836 
  837         g_ptr_array_add (esp->res_parts, res);
  838         r->value.number = esp->res_parts->len - 1;
  839     }
  840 
  841     return r;
  842 }
  843 
  844 static ESExpResult *
  845 term_eval_or (struct _ESExp *f,
  846           gint argc,
  847           struct _ESExpResult **argv,
  848           gpointer user_data)
  849 {
  850     struct EMapiSExpParserData *esp = user_data;
  851     ESExpResult *r;
  852     gint ii, jj = -1, valid = 0;
  853 
  854     r = e_sexp_result_new (f, ESEXP_RES_INT);
  855     r->value.number = -1;
  856 
  857     for (ii = 0; ii < argc; ii++) {
  858         if (argv[ii]->type == ESEXP_RES_INT &&
  859             argv[ii]->value.number >= 0 && 
  860             argv[ii]->value.number < esp->res_parts->len) {
  861             struct mapi_SRestriction *subres = g_ptr_array_index (esp->res_parts, argv[ii]->value.number);
  862 
  863             jj = argv[ii]->value.number;
  864             valid++;
  865 
  866             /* join two consecutive OR-s into one */
  867             if (subres->rt == RES_OR)
  868                 valid += subres->res.resOr.cRes - 1;
  869             }
  870     }
  871 
  872     if (valid == 1) {
  873         r->value.number = jj;
  874     } else if (valid > 0) {
  875         struct mapi_SRestriction *res = talloc_zero (esp->mem_ctx, struct mapi_SRestriction);
  876         g_return_val_if_fail (res != NULL, NULL);
  877 
  878         res->rt = RES_OR;
  879         res->res.resOr.cRes = valid;
  880         res->res.resOr.res = talloc_zero_array (esp->mem_ctx, struct mapi_SRestriction_or, res->res.resOr.cRes + 1);
  881 
  882         jj = 0;
  883 
  884         for (ii = 0; ii < argc; ii++) {
  885             if (argv[ii]->type == ESEXP_RES_INT &&
  886                 argv[ii]->value.number >= 0 && 
  887                 argv[ii]->value.number < esp->res_parts->len) {
  888                 struct mapi_SRestriction *subres = g_ptr_array_index (esp->res_parts, argv[ii]->value.number);
  889 
  890                 /* join two consecutive OR-s into one */
  891                 if (subres->rt == RES_OR) {
  892                     gint xx;
  893 
  894                     for (xx = 0; xx < subres->res.resOr.cRes; xx++, jj++) {
  895                         res->res.resOr.res[jj].rt = subres->res.resOr.res[xx].rt;
  896                         res->res.resOr.res[jj].res = subres->res.resOr.res[xx].res;
  897                     }
  898                 } else {
  899                     res->res.resOr.res[jj].rt = subres->rt;
  900                     res->res.resOr.res[jj].res = subres->res;
  901 
  902                     jj++;
  903                 }
  904             }
  905         }
  906 
  907         g_ptr_array_add (esp->res_parts, res);
  908         r->value.number = esp->res_parts->len - 1;
  909     }
  910 
  911     return r;
  912 }
  913 
  914 static ESExpResult *
  915 term_eval_not (struct _ESExp *f,
  916            gint argc,
  917            struct _ESExpResult **argv,
  918            gpointer user_data)
  919 {
  920     ESExpResult *r;
  921 
  922     r = e_sexp_result_new (f, ESEXP_RES_INT);
  923     r->value.number = -1;
  924 
  925     #ifdef HAVE_RES_NOT_SUPPORTED
  926     if (argc == 1 && argv[0]->type == ESEXP_RES_INT) {
  927         struct EMapiSExpParserData *esp = user_data;
  928         gint idx = argv[0]->value.number;
  929 
  930         if (esp && idx >= 0 && idx < esp->res_parts->len) {
  931             struct mapi_SRestriction *res = talloc_zero (esp->mem_ctx, struct mapi_SRestriction);
  932             g_return_val_if_fail (res != NULL, NULL);
  933 
  934             res->rt = RES_NOT;
  935             res->res.resNot.res = g_ptr_array_index (esp->res_parts, idx);
  936 
  937             g_ptr_array_add (esp->res_parts, res);
  938             r->value.number = esp->res_parts->len - 1;
  939         }
  940     }
  941     #endif
  942 
  943     return r;
  944 }
  945 
  946 static uint32_t
  947 get_proptag_from_field_name (const gchar *field_name, gboolean is_contact_field)
  948 {
  949     EContactField cfid;
  950     gint ii;
  951 
  952     if (is_contact_field)
  953         cfid = e_contact_field_id (field_name);
  954     else
  955         cfid = e_contact_field_id_from_vcard (field_name);
  956 
  957     for (ii = 0; ii < G_N_ELEMENTS (mappings); ii++) {
  958         if (mappings[ii].field_id == cfid) {
  959             return mappings[ii].mapi_id;
  960         }
  961     }
  962 
  963     return MAPI_E_RESERVED;
  964 }
  965 
  966 static ESExpResult *
  967 func_eval_text_compare (struct _ESExp *f,
  968             gint argc,
  969             struct _ESExpResult **argv,
  970             gpointer user_data,
  971             uint32_t fuzzy)
  972 {
  973     struct EMapiSExpParserData *esp = user_data;
  974     ESExpResult *r;
  975 
  976     r = e_sexp_result_new (f, ESEXP_RES_INT);
  977     r->value.number = -1;
  978 
  979     if (argc == 2
  980         && argv[0]->type == ESEXP_RES_STRING
  981         && argv[1]->type == ESEXP_RES_STRING) {
  982         const gchar *propname = argv[0]->value.string;
  983         const gchar *propvalue = argv[1]->value.string;
  984 
  985         if (propname && propvalue && g_ascii_strcasecmp (propname, "x-evolution-any-field") != 0) {
  986             uint32_t proptag = get_proptag_from_field_name (propname, TRUE);
  987 
  988             if (proptag != MAPI_E_RESERVED && ((proptag & 0xFFFF) == PT_UNICODE || (proptag & 0xFFFF) == PT_STRING8)) {
  989                 struct mapi_SRestriction *res = talloc_zero (esp->mem_ctx, struct mapi_SRestriction);
  990                 g_return_val_if_fail (res != NULL, NULL);
  991 
  992                 res->rt = RES_CONTENT;
  993                 res->res.resContent.fuzzy = fuzzy | FL_IGNORECASE;
  994                 res->res.resContent.ulPropTag = proptag;
  995                 res->res.resContent.lpProp.ulPropTag = proptag;
  996                 res->res.resContent.lpProp.value.lpszW = talloc_strdup (esp->mem_ctx, propvalue);
  997 
  998                 g_ptr_array_add (esp->res_parts, res);
  999                 r->value.number = esp->res_parts->len - 1;
 1000             } else if (g_ascii_strcasecmp (propname, "email") == 0) {
 1001                 uint32_t ii, jj;
 1002                 const gchar *emails[] = {"email_1", "email_2", "email_3", NULL};
 1003                 struct mapi_SRestriction *res = talloc_zero (esp->mem_ctx, struct mapi_SRestriction);
 1004                 g_return_val_if_fail (res != NULL, NULL);
 1005 
 1006                 res->rt = RES_OR;
 1007                 res->res.resOr.cRes = 4;
 1008                 res->res.resOr.res = talloc_zero_array (esp->mem_ctx, struct mapi_SRestriction_or, res->res.resOr.cRes + 1);
 1009 
 1010                 proptag = PidTagSmtpAddress;
 1011                 res->res.resOr.res[0].rt = RES_CONTENT;
 1012                 res->res.resOr.res[0].res.resContent.fuzzy = fuzzy | FL_IGNORECASE;
 1013                 res->res.resOr.res[0].res.resContent.ulPropTag = proptag;
 1014                 res->res.resOr.res[0].res.resContent.lpProp.ulPropTag = proptag;
 1015                 res->res.resOr.res[0].res.resContent.lpProp.value.lpszW = talloc_strdup (esp->mem_ctx, propvalue);
 1016 
 1017                 for (ii = 1, jj = 0; emails[jj]; jj++) {
 1018                     proptag = get_proptag_from_field_name (emails[jj], TRUE);
 1019                     if (proptag == MAPI_E_RESERVED)
 1020                         continue;
 1021 
 1022                     res->res.resOr.res[ii].rt = RES_CONTENT;
 1023                     res->res.resOr.res[ii].res.resContent.fuzzy = fuzzy | FL_IGNORECASE;
 1024                     res->res.resOr.res[ii].res.resContent.ulPropTag = proptag;
 1025                     res->res.resOr.res[ii].res.resContent.lpProp.ulPropTag = proptag;
 1026                     res->res.resOr.res[ii].res.resContent.lpProp.value.lpszW = talloc_strdup (esp->mem_ctx, propvalue);
 1027 
 1028                     ii++;
 1029                 }
 1030 
 1031                 res->res.resOr.cRes = ii;
 1032 
 1033                 g_ptr_array_add (esp->res_parts, res);
 1034                 r->value.number = esp->res_parts->len - 1;
 1035             }
 1036         }
 1037     }
 1038 
 1039     return r;
 1040 }
 1041 
 1042 static ESExpResult *
 1043 func_eval_contains (struct _ESExp *f,
 1044             gint argc,
 1045             struct _ESExpResult **argv,
 1046             gpointer user_data)
 1047 {
 1048     return func_eval_text_compare (f, argc, argv, user_data, FL_SUBSTRING);
 1049 }
 1050 
 1051 static ESExpResult *
 1052 func_eval_is (struct _ESExp *f,
 1053           gint argc,
 1054           struct _ESExpResult **argv,
 1055           gpointer user_data)
 1056 {
 1057     return func_eval_text_compare (f, argc, argv, user_data, FL_FULLSTRING);
 1058 }
 1059 
 1060 static ESExpResult *
 1061 func_eval_beginswith (struct _ESExp *f,
 1062               gint argc,
 1063               struct _ESExpResult **argv,
 1064               gpointer user_data)
 1065 {
 1066     return func_eval_text_compare (f, argc, argv, user_data, FL_PREFIX);
 1067 }
 1068 
 1069 static ESExpResult *
 1070 func_eval_endswith (struct _ESExp *f,
 1071             gint argc,
 1072             struct _ESExpResult **argv,
 1073             gpointer user_data)
 1074 {
 1075     /* no suffix, thus at least substring is used */
 1076     return func_eval_text_compare (f, argc, argv, user_data, FL_SUBSTRING);
 1077 }
 1078 
 1079 static ESExpResult *
 1080 func_eval_field_exists (struct _ESExp *f,
 1081             gint argc,
 1082             struct _ESExpResult **argv,
 1083             gpointer user_data,
 1084             gboolean is_contact_field)
 1085 {
 1086     struct EMapiSExpParserData *esp = user_data;
 1087     ESExpResult *r;
 1088 
 1089     r = e_sexp_result_new (f, ESEXP_RES_INT);
 1090     r->value.number = -1;
 1091 
 1092     if (argc == 1 && argv[0]->type == ESEXP_RES_STRING) {
 1093         const gchar *propname = argv[0]->value.string;
 1094         uint32_t proptag = get_proptag_from_field_name (propname, is_contact_field);
 1095 
 1096         if (proptag != MAPI_E_RESERVED) {
 1097             struct mapi_SRestriction *res = talloc_zero (esp->mem_ctx, struct mapi_SRestriction);
 1098             g_return_val_if_fail (res != NULL, NULL);
 1099 
 1100             res->rt = RES_EXIST;
 1101             res->res.resExist.ulPropTag = proptag;
 1102 
 1103             g_ptr_array_add (esp->res_parts, res);
 1104             r->value.number = esp->res_parts->len - 1;
 1105         } else if (g_ascii_strcasecmp (propname, "email") == 0) {
 1106             uint32_t ii, jj;
 1107             const gchar *emails[] = { "email_1", "email_2", "email_3", NULL };
 1108             struct mapi_SRestriction *res = talloc_zero (esp->mem_ctx, struct mapi_SRestriction);
 1109             g_return_val_if_fail (res != NULL, NULL);
 1110 
 1111             res->rt = RES_OR;
 1112             res->res.resOr.cRes = 4;
 1113             res->res.resOr.res = talloc_zero_array (esp->mem_ctx, struct mapi_SRestriction_or, res->res.resOr.cRes + 1);
 1114 
 1115             res->res.resOr.res[0].rt = RES_EXIST;
 1116             res->res.resOr.res[0].res.resExist.ulPropTag = PidTagSmtpAddress;
 1117 
 1118             for (ii = 1, jj = 0; emails[jj]; jj++) {
 1119                 proptag = get_proptag_from_field_name (emails[jj], TRUE);
 1120 
 1121                 if (proptag == MAPI_E_RESERVED)
 1122                     continue;
 1123 
 1124                 res->res.resOr.res[ii].rt = RES_EXIST;
 1125                 res->res.resOr.res[ii].res.resExist.ulPropTag = proptag;
 1126 
 1127                 ii++;
 1128             }
 1129 
 1130             res->res.resOr.cRes = ii;
 1131 
 1132             g_ptr_array_add (esp->res_parts, res);
 1133             r->value.number = esp->res_parts->len - 1;
 1134         }
 1135     }
 1136 
 1137     return r;
 1138 }
 1139 
 1140 static ESExpResult *
 1141 func_eval_exists (struct _ESExp *f,
 1142           gint argc,
 1143           struct _ESExpResult **argv,
 1144           gpointer user_data)
 1145 {
 1146     return func_eval_field_exists (f, argc, argv, user_data, TRUE);
 1147 }
 1148 
 1149 static ESExpResult *
 1150 func_eval_exists_vcard (struct _ESExp *f,
 1151             gint argc,
 1152             struct _ESExpResult **argv,
 1153             gpointer user_data)
 1154 {
 1155     return func_eval_field_exists (f, argc, argv, user_data, FALSE);
 1156 }
 1157 
 1158 static struct mapi_SRestriction *
 1159 mapi_book_utils_sexp_to_restriction (TALLOC_CTX *mem_ctx, const gchar *sexp_query)
 1160 {
 1161     /* 'builtin' functions */
 1162     static const struct {
 1163         const gchar *name;
 1164         ESExpFunc *func;
 1165         gint type;      /* set to 1 if a function can perform shortcut evaluation, or
 1166                        doesn't execute everything, 0 otherwise */
 1167     } check_symbols[] = {
 1168         { "and",        term_eval_and,      0 },
 1169         { "or",         term_eval_or,       0 },
 1170         { "not",        term_eval_not,      0 },
 1171 
 1172         { "contains",       func_eval_contains, 0 },
 1173         { "is",         func_eval_is,       0 },
 1174         { "beginswith",     func_eval_beginswith,   0 },
 1175         { "endswith",       func_eval_endswith, 0 },
 1176         { "exists",     func_eval_exists,   0 },
 1177         { "exists_vcard",   func_eval_exists_vcard, 0 }
 1178     };
 1179 
 1180     gint i;
 1181     ESExp *sexp;
 1182     ESExpResult *r;
 1183     struct EMapiSExpParserData esp;
 1184     struct mapi_SRestriction *restriction;
 1185 
 1186     g_return_val_if_fail (sexp_query != NULL, NULL);
 1187 
 1188     esp.mem_ctx = mem_ctx;
 1189     sexp = e_sexp_new ();
 1190 
 1191     for (i = 0; i < G_N_ELEMENTS (check_symbols); i++) {
 1192         if (check_symbols[i].type == 1) {
 1193             e_sexp_add_ifunction (sexp, 0, check_symbols[i].name,
 1194                           (ESExpIFunc *) check_symbols[i].func, &esp);
 1195         } else {
 1196             e_sexp_add_function (sexp, 0, check_symbols[i].name,
 1197                          check_symbols[i].func, &esp);
 1198         }
 1199     }
 1200 
 1201     e_sexp_input_text (sexp, sexp_query, strlen (sexp_query));
 1202     if (e_sexp_parse (sexp) == -1) {
 1203         g_object_unref (sexp);
 1204         return NULL;
 1205     }
 1206 
 1207     esp.res_parts = g_ptr_array_new ();
 1208     r = e_sexp_eval (sexp);
 1209 
 1210     restriction = NULL;
 1211     if (r && r->type == ESEXP_RES_INT && r->value.number >= 0 && r->value.number < esp.res_parts->len)
 1212         restriction = g_ptr_array_index (esp.res_parts, r->value.number);
 1213 
 1214     e_sexp_result_free (sexp, r);
 1215 
 1216     g_object_unref (sexp);
 1217     g_ptr_array_free (esp.res_parts, TRUE);
 1218 
 1219     return restriction;
 1220 }
 1221 
 1222 gboolean
 1223 e_mapi_book_utils_build_sexp_restriction (EMapiConnection *conn,
 1224                       TALLOC_CTX *mem_ctx,
 1225                       struct mapi_SRestriction **restrictions,
 1226                       gpointer user_data, /* const gchar *sexp */
 1227                       GCancellable *cancellable,
 1228                       GError **perror)
 1229 {
 1230     const gchar *sexp = user_data;
 1231 
 1232     g_return_val_if_fail (conn != NULL, FALSE);
 1233     g_return_val_if_fail (mem_ctx != NULL, FALSE);
 1234     g_return_val_if_fail (restrictions != NULL, FALSE);
 1235 
 1236     if (!sexp || !*sexp)
 1237         *restrictions = NULL;
 1238     else
 1239         *restrictions = mapi_book_utils_sexp_to_restriction (mem_ctx, sexp);
 1240 
 1241     return TRUE;
 1242 }
 1243 
 1244 /* return with g_slist_free(), 'data' pointers (strings) are not newly allocated */
 1245 GSList *
 1246 e_mapi_book_utils_get_supported_contact_fields (void)
 1247 {
 1248     gint ii;
 1249     GSList *fields = NULL;
 1250 
 1251     for (ii = 0; ii < G_N_ELEMENTS (mappings); ii++) {
 1252         fields = g_slist_append (fields, (gpointer) e_contact_field_name (mappings[ii].field_id));
 1253     }
 1254 
 1255     fields = g_slist_append (fields, (gpointer) e_contact_field_name (E_CONTACT_BOOK_UID));
 1256 
 1257     return fields;
 1258 }
 1259 
 1260 gboolean
 1261 e_mapi_book_utils_get_supported_mapi_proptags (TALLOC_CTX *mem_ctx,
 1262                            struct SPropTagArray **propTagArray)
 1263 {
 1264     gint ii;
 1265 
 1266     g_return_val_if_fail (mem_ctx != NULL, FALSE);
 1267     g_return_val_if_fail (propTagArray != NULL, FALSE);
 1268 
 1269     *propTagArray = set_SPropTagArray (mem_ctx, 1, PidTagObjectType);
 1270 
 1271     for (ii = 0; ii < G_N_ELEMENTS (mappings); ii++) {
 1272         SPropTagArray_add (mem_ctx, *propTagArray, mappings[ii].mapi_id);
 1273     }
 1274 
 1275     for (ii = 0; ii < G_N_ELEMENTS (extra_proptags); ii++) {
 1276         SPropTagArray_add (mem_ctx, *propTagArray, extra_proptags[ii]);
 1277     }
 1278 
 1279     return TRUE;
 1280 }