/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* Copyright (C) 2002-2004 Novell, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU Lesser General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ /* e2k-action.c: Exchange server-side rule actions */ #ifdef HAVE_CONFIG_H #include #endif #include #include "e2k-action.h" #include "e2k-propnames.h" #include "e2k-restriction.h" #include "e2k-rule.h" #include "e2k-utils.h" #include "mapi.h" /* The apparently-constant store entryid prefix for a move or copy action */ #define E2K_ACTION_XFER_STORE_ENTRYID_PREFIX "\x00\x00\x00\x00\x38\xa1\xbb\x10\x05\xe5\x10\x1a\xa1\xbb\x08\x00\x2b\x2a\x56\xc2\x00\x00\x45\x4d\x53\x4d\x44\x42\x2e\x44\x4c\x4c\x00\x00\x00\x00" #define E2K_ACTION_XFER_STORE_ENTRYID_PREFIX_LEN (sizeof (E2K_ACTION_XFER_STORE_ENTRYID_PREFIX) - 1) static GByteArray * copy_bytearray (GByteArray *ba) { GByteArray *copy; copy = g_byte_array_sized_new (ba->len); copy->len = ba->len; memcpy (copy->data, ba->data, copy->len); return copy; } static E2kAction * xfer_action (E2kActionType type, GByteArray *store_entryid, GByteArray *folder_source_key) { E2kAction *act; act = g_new0 (E2kAction, 1); act->type = type; act->act.xfer.store_entryid = copy_bytearray (store_entryid); act->act.xfer.folder_source_key = copy_bytearray (folder_source_key); return act; } /** * e2k_action_move: * @store_entryid: The PR_STORE_ENTRYID of the message store * @folder_source_key: The PR_SOURCE_KEY of a folder in that store * * Creates a rule action to move a message into the indicated folder * * Return value: the new rule action **/ E2kAction * e2k_action_move (GByteArray *store_entryid, GByteArray *folder_source_key) { return xfer_action (E2K_ACTION_MOVE, store_entryid, folder_source_key); } /** * e2k_action_copy: * @store_entryid: The PR_STORE_ENTRYID of the message store * @folder_source_key: The PR_SOURCE_KEY of a folder in that store * * Creates a rule action to copy a message into the indicated folder * * Return value: the new rule action **/ E2kAction * e2k_action_copy (GByteArray *store_entryid, GByteArray *folder_source_key) { return xfer_action (E2K_ACTION_COPY, store_entryid, folder_source_key); } static E2kAction * reply_action (E2kActionType type, GByteArray *template_entryid, guint8 template_guid[16]) { E2kAction *act; act = g_new0 (E2kAction, 1); act->type = type; act->act.reply.entryid = copy_bytearray (template_entryid); memcpy (act->act.reply.reply_template_guid, template_guid, 16); return act; } /** * e2k_action_reply: * @template_entryid: The entryid of the reply template * @template_guid: The GUID of the reply template * * Creates a rule action to reply to a message using the indicated * template * * Return value: the new rule action **/ E2kAction * e2k_action_reply (GByteArray *template_entryid, guint8 template_guid[16]) { return reply_action (E2K_ACTION_REPLY, template_entryid, template_guid); } /** * e2k_action_oof_reply: * @template_entryid: The entryid of the reply template * @template_guid: The GUID of the reply template * * Creates a rule action to send an Out-of-Office reply to a message * using the indicated template * * Return value: the new rule action **/ E2kAction * e2k_action_oof_reply (GByteArray *template_entryid, guint8 template_guid[16]) { return reply_action (E2K_ACTION_OOF_REPLY, template_entryid, template_guid); } /** * e2k_action_defer: * @data: data identifying the deferred action * * Creates a rule action to defer processing on a message * * Return value: the new rule action **/ E2kAction * e2k_action_defer (GByteArray *data) { E2kAction *act; act = g_new0 (E2kAction, 1); act->type = E2K_ACTION_DEFER; act->act.defer_data = copy_bytearray (data); return act; } /** * e2k_action_bounce: * @bounce_code: a bounce code * * Creates a rule action to bounce a message * * Return value: the new rule action **/ E2kAction * e2k_action_bounce (E2kActionBounceCode bounce_code) { E2kAction *act; act = g_new0 (E2kAction, 1); act->type = E2K_ACTION_BOUNCE; act->act.bounce_code = bounce_code; return act; } static E2kAction * forward_action (E2kActionType type, E2kAddrList *list) { E2kAction *act; g_return_val_if_fail (type == E2K_ACTION_FORWARD || type == E2K_ACTION_DELEGATE, NULL); g_return_val_if_fail (list->nentries > 0, NULL); act = g_new0 (E2kAction, 1); act->type = type; act->act.addr_list = list; return act; } /** * e2k_action_forward: * @list: a list of recipients * * Creates a rule action to forward a message to the indicated list of * recipients * * Return value: the new rule action **/ E2kAction * e2k_action_forward (E2kAddrList *list) { return forward_action (E2K_ACTION_FORWARD, list); } /** * e2k_action_delegate: * @list: a list of recipients * * Creates a rule action to delegate a meeting request to the * indicated list of recipients * * Return value: the new rule action **/ E2kAction * e2k_action_delegate (E2kAddrList *list) { return forward_action (E2K_ACTION_DELEGATE, list); } /** * e2k_action_tag: * @propname: a MAPI property name * @type: the type of @propname * @value: the value for @propname * * Creates a rule action to set the given property to the given value * on a message. * * Return value: the new rule action **/ E2kAction * e2k_action_tag (const char *propname, E2kPropType type, gpointer value) { E2kAction *act; act = g_new0 (E2kAction, 1); act->type = E2K_ACTION_TAG; e2k_rule_prop_set (&act->act.proptag.prop, propname); act->act.proptag.type = type; act->act.proptag.value = value; /* FIXME: copy? */ return act; } /** * e2k_action_delete: * * Creates a rule action to permanently delete a message (ie, not just * move it to the trash). * * Return value: the new rule action **/ E2kAction * e2k_action_delete (void) { E2kAction *act; act = g_new0 (E2kAction, 1); act->type = E2K_ACTION_DELETE; return act; } /** * e2k_addr_list_new: * @nentries: the number of entries * * Creates an address list for a forward or delegate rule, with * @nentries slots * * Return value: the new address list **/ E2kAddrList * e2k_addr_list_new (int nentries) { E2kAddrList *list; list = g_malloc0 (sizeof (E2kAddrList) + (nentries - 1) * sizeof (E2kAddrEntry)); list->nentries = nentries; return list; } static void addr_entry_set_core (E2kPropValue *pv, GByteArray *entryid, const char *display_name, const char *email_type, const char *email_addr) { e2k_rule_prop_set (&pv[0].prop, PR_ENTRYID); pv[0].type = E2K_PROP_TYPE_BINARY; pv[0].value = entryid; e2k_rule_prop_set (&pv[1].prop, PR_DISPLAY_NAME); pv[1].type = E2K_PROP_TYPE_STRING; pv[1].value = g_strdup (display_name); e2k_rule_prop_set (&pv[2].prop, PR_OBJECT_TYPE); pv[2].type = E2K_PROP_TYPE_INT; pv[2].value = GINT_TO_POINTER (MAPI_MAILUSER); e2k_rule_prop_set (&pv[3].prop, PR_DISPLAY_TYPE); pv[3].type = E2K_PROP_TYPE_INT; pv[3].value = GINT_TO_POINTER (DT_MAILUSER); e2k_rule_prop_set (&pv[4].prop, PR_TRANSMITTABLE_DISPLAY_NAME); pv[4].type = E2K_PROP_TYPE_STRING; pv[4].value = g_strdup (display_name); e2k_rule_prop_set (&pv[5].prop, PR_EMAIL_ADDRESS); pv[5].type = E2K_PROP_TYPE_STRING; pv[5].value = g_strdup (email_addr); e2k_rule_prop_set (&pv[6].prop, PR_ADDRTYPE); pv[6].type = E2K_PROP_TYPE_STRING; pv[6].value = g_strdup (email_type); e2k_rule_prop_set (&pv[7].prop, PR_SEND_INTERNET_ENCODING); pv[7].type = E2K_PROP_TYPE_INT; pv[7].value = GINT_TO_POINTER (0); /* "Let transport decide" */ e2k_rule_prop_set (&pv[8].prop, PR_RECIPIENT_TYPE); pv[8].type = E2K_PROP_TYPE_INT; pv[8].value = GINT_TO_POINTER (MAPI_TO); e2k_rule_prop_set (&pv[9].prop, PR_SEARCH_KEY); pv[9].type = E2K_PROP_TYPE_BINARY; pv[9].value = e2k_search_key_generate (email_type, email_addr); } /** * e2k_addr_list_set_local: * @list: the address list * @entry_num: the list entry to set * @display_name: the UTF-8 display name of the recipient * @exchange_dn: the Exchange 5.5-style DN of the recipient * @email: the SMTP email address of the recipient * * Sets entry number @entry_num of @list to refer to the indicated * local Exchange user. **/ void e2k_addr_list_set_local (E2kAddrList *list, int entry_num, const char *display_name, const char *exchange_dn, const char *email) { E2kPropValue *pv; list->entry[entry_num].nvalues = 12; list->entry[entry_num].propval = pv = g_new0 (E2kPropValue, 12); addr_entry_set_core (pv, e2k_entryid_generate_local (exchange_dn), display_name, "EX", exchange_dn); e2k_rule_prop_set (&pv[10].prop, PR_EMS_AB_DISPLAY_NAME_PRINTABLE); pv[10].type = E2K_PROP_TYPE_STRING; pv[10].value = g_strdup ("FIXME"); e2k_rule_prop_set (&pv[11].prop, PR_SMTP_ADDRESS); pv[11].type = E2K_PROP_TYPE_STRING; pv[11].value = g_strdup (email); } /** * e2k_addr_list_set_oneoff: * @list: the address list * @entry_num: the list entry to set * @display_name: the UTF-8 display name of the recipient * @email: the SMTP email address of the recipient * * Sets entry number @entry_num of @list to refer to the indicated * "one-off" SMTP user. **/ void e2k_addr_list_set_oneoff (E2kAddrList *list, int entry_num, const char *display_name, const char *email) { E2kPropValue *pv; list->entry[entry_num].nvalues = 12; list->entry[entry_num].propval = pv = g_new0 (E2kPropValue, 12); addr_entry_set_core (pv, e2k_entryid_generate_oneoff (display_name, email, TRUE), display_name, "SMTP", email); e2k_rule_prop_set (&pv[10].prop, PR_SEND_RICH_INFO); pv[10].type = E2K_PROP_TYPE_BOOL; pv[10].value = GINT_TO_POINTER (FALSE); e2k_rule_prop_set (&pv[11].prop, PR_RECORD_KEY); pv[11].type = E2K_PROP_TYPE_BINARY; pv[11].value = e2k_entryid_generate_oneoff (display_name, email, FALSE); } /** * e2k_addr_list_free: * @list: the address list * * Frees @list and all its entries. **/ void e2k_addr_list_free (E2kAddrList *list) { int i, j; E2kAddrEntry *entry; for (i = 0; i < list->nentries; i++) { entry = &list->entry[i]; for (j = 0; j < entry->nvalues; j++) e2k_rule_free_propvalue (&entry->propval[j]); g_free (entry->propval); } g_free (list); } /** * e2k_action_free: * @act: the action * * Frees @act **/ void e2k_action_free (E2kAction *act) { switch (act->type) { case E2K_ACTION_MOVE: case E2K_ACTION_COPY: if (act->act.xfer.store_entryid) g_byte_array_free (act->act.xfer.store_entryid, TRUE); if (act->act.xfer.folder_source_key) g_byte_array_free (act->act.xfer.folder_source_key, TRUE); break; case E2K_ACTION_REPLY: case E2K_ACTION_OOF_REPLY: if (act->act.reply.entryid) g_byte_array_free (act->act.reply.entryid, TRUE); break; case E2K_ACTION_DEFER: if (act->act.defer_data) g_byte_array_free (act->act.defer_data, TRUE); break; case E2K_ACTION_FORWARD: case E2K_ACTION_DELEGATE: if (act->act.addr_list) e2k_addr_list_free (act->act.addr_list); break; case E2K_ACTION_TAG: e2k_rule_free_propvalue (&act->act.proptag); break; default: /* Nothing to free */ break; } g_free (act); } /** * e2k_actions_free: * @actions: an array of #E2kAction * * Frees @actions and all of its elements **/ void e2k_actions_free (GPtrArray *actions) { int i; for (i = 0; i < actions->len; i++) e2k_action_free (actions->pdata[i]); g_ptr_array_free (actions, TRUE); } static gboolean extract_action (guint8 **data, int *len, E2kAction **act_ret) { int my_len; guint8 *my_data; guint16 actlen; E2kAction *act; if (!e2k_rule_extract_uint16 (data, len, &actlen)) return FALSE; my_data = *data; my_len = actlen; *data += actlen; *len -= actlen; data = &my_data; len = &my_len; if (*len < 1) return FALSE; act = g_new0 (E2kAction, 1); act->type = **data; (*data)++; (*len)--; if (!e2k_rule_extract_uint32 (data, len, &act->flavor)) goto lose; if (!e2k_rule_extract_uint32 (data, len, &act->flags)) goto lose; switch (act->type) { case E2K_ACTION_MOVE: case E2K_ACTION_COPY: /* FIXME: what is this? */ if (*len < 1 || **data != 1) goto lose; (*len)--; (*data)++; if (!e2k_rule_extract_binary (data, len, &act->act.xfer.store_entryid)) goto lose; /* Remove the constant part */ if (act->act.xfer.store_entryid->len <= E2K_ACTION_XFER_STORE_ENTRYID_PREFIX_LEN || memcmp (act->act.xfer.store_entryid->data, E2K_ACTION_XFER_STORE_ENTRYID_PREFIX, E2K_ACTION_XFER_STORE_ENTRYID_PREFIX_LEN) != 0) goto lose; act->act.xfer.store_entryid->len -= E2K_ACTION_XFER_STORE_ENTRYID_PREFIX_LEN; memmove (act->act.xfer.store_entryid->data, act->act.xfer.store_entryid->data + E2K_ACTION_XFER_STORE_ENTRYID_PREFIX_LEN, act->act.xfer.store_entryid->len); if (!e2k_rule_extract_binary (data, len, &act->act.xfer.folder_source_key)) goto lose; /* Likewise */ if (act->act.xfer.folder_source_key->len < 1 || act->act.xfer.folder_source_key->data[0] != MAPI_FOLDER) goto lose; memmove (act->act.xfer.folder_source_key->data, act->act.xfer.folder_source_key->data + 1, act->act.xfer.folder_source_key->len); *act_ret = act; return TRUE; case E2K_ACTION_REPLY: case E2K_ACTION_OOF_REPLY: /* The reply template GUID is 16 bytes, the entryid * is the rest. */ if (*len <= 16) goto lose; act->act.reply.entryid = g_byte_array_sized_new (*len - 16); memcpy (act->act.reply.entryid->data, *data, *len - 16); act->act.reply.entryid->len = *len - 16; memcpy (act->act.reply.reply_template_guid, *data + *len - 16, 16); *act_ret = act; return TRUE; case E2K_ACTION_DEFER: act->act.defer_data = g_byte_array_sized_new (*len); memcpy (act->act.defer_data->data, *data, *len); act->act.defer_data->len = *len; *act_ret = act; return TRUE; case E2K_ACTION_BOUNCE: if (!e2k_rule_extract_uint32 (data, len, &act->act.bounce_code)) goto lose; *act_ret = act; return TRUE; case E2K_ACTION_FORWARD: case E2K_ACTION_DELEGATE: { guint16 nentries, nvalues; int i, j; if (!e2k_rule_extract_uint16 (data, len, &nentries)) goto lose; act->act.addr_list = e2k_addr_list_new (nentries); for (i = 0; i < nentries; i++) { /* FIXME: what is this? */ if (*len < 1 || **data != 1) goto lose; (*len)--; (*data)++; if (!e2k_rule_extract_uint16 (data, len, &nvalues)) goto lose; act->act.addr_list->entry[i].nvalues = nvalues; act->act.addr_list->entry[i].propval = g_new0 (E2kPropValue, nvalues); for (j = 0; j < nvalues; j++) { if (!e2k_rule_extract_propvalue (data, len, &act->act.addr_list->entry[i].propval[j])) goto lose; } } *act_ret = act; return TRUE; } case E2K_ACTION_TAG: if (!e2k_rule_extract_propvalue (data, len, &act->act.proptag)) goto lose; *act_ret = act; return TRUE; case E2K_ACTION_DELETE: *act_ret = act; return TRUE; case E2K_ACTION_MARK_AS_READ: /* FIXME */ return FALSE; default: break; } lose: e2k_action_free (act); return FALSE; } /** * e2k_actions_extract: * @data: pointer to data pointer * @len: pointer to data length * @actions: pointer to array to store actions in * * Attempts to extract a list of actions from *@data, which contains a * binary-encoded list of actions from a server-side rule. * * On success, *@actions will contain the extracted list, *@data will * be advanced past the end of the restriction data, and *@len will be * decremented accordingly. * * Return value: success or failure **/ gboolean e2k_actions_extract (guint8 **data, int *len, GPtrArray **actions) { GPtrArray *acts; E2kAction *act; guint32 actlen; guint16 nacts; int i; if (!e2k_rule_extract_uint32 (data, len, &actlen)) return FALSE; if (actlen > *len) return FALSE; if (!e2k_rule_extract_uint16 (data, len, &nacts)) return FALSE; acts = g_ptr_array_new (); for (i = 0; i < nacts; i++) { if (!extract_action (data, len, &act)) { e2k_actions_free (acts); return FALSE; } else g_ptr_array_add (acts, act); } *actions = acts; return TRUE; } static void append_action (GByteArray *ba, E2kAction *act) { int actlen_offset, actlen; char type; /* Save space for length */ actlen_offset = ba->len; e2k_rule_append_uint16 (ba, 0); e2k_rule_append_byte (ba, act->type); e2k_rule_append_uint32 (ba, act->flavor); e2k_rule_append_uint32 (ba, act->flags); switch (act->type) { case E2K_ACTION_MOVE: case E2K_ACTION_COPY: /* FIXME: what is this? */ e2k_rule_append_byte (ba, 1); e2k_rule_append_uint16 (ba, act->act.xfer.store_entryid->len + E2K_ACTION_XFER_STORE_ENTRYID_PREFIX_LEN); g_byte_array_append (ba, E2K_ACTION_XFER_STORE_ENTRYID_PREFIX, E2K_ACTION_XFER_STORE_ENTRYID_PREFIX_LEN); g_byte_array_append (ba, act->act.xfer.store_entryid->data, act->act.xfer.store_entryid->len); e2k_rule_append_uint16 (ba, 49); type = MAPI_FOLDER; g_byte_array_append (ba, &type, 1); g_byte_array_append (ba, act->act.xfer.folder_source_key->data, act->act.xfer.folder_source_key->len); break; case E2K_ACTION_REPLY: case E2K_ACTION_OOF_REPLY: g_byte_array_append (ba, act->act.reply.entryid->data, act->act.reply.entryid->len); g_byte_array_append (ba, act->act.reply.reply_template_guid, 16); break; case E2K_ACTION_DEFER: g_byte_array_append (ba, act->act.defer_data->data, act->act.defer_data->len); break; case E2K_ACTION_BOUNCE: e2k_rule_append_uint32 (ba, act->act.bounce_code); break; case E2K_ACTION_FORWARD: case E2K_ACTION_DELEGATE: { int i, j; E2kAddrList *list; E2kAddrEntry *entry; list = act->act.addr_list; e2k_rule_append_uint16 (ba, list->nentries); for (i = 0; i < list->nentries; i++) { /* FIXME: what is this? */ e2k_rule_append_byte (ba, 1); entry = &list->entry[i]; e2k_rule_append_uint16 (ba, entry->nvalues); for (j = 0; j < entry->nvalues; j++) e2k_rule_append_propvalue (ba, &entry->propval[j]); } break; } case E2K_ACTION_TAG: e2k_rule_append_propvalue (ba, &act->act.proptag); break; case E2K_ACTION_DELETE: break; case E2K_ACTION_MARK_AS_READ: /* FIXME */ break; default: break; } actlen = ba->len - actlen_offset - 2; e2k_rule_write_uint16 (ba->data + actlen_offset, actlen); } /** * e2k_actions_append: * @ba: a buffer into which a server-side rule is being constructed * @actions: the actions to append to @ba * * Appends @actions to @ba as part of a server-side rule. **/ void e2k_actions_append (GByteArray *ba, GPtrArray *actions) { int actlen_offset, actlen, i; /* Save space for length */ actlen_offset = ba->len; e2k_rule_append_uint32 (ba, 0); e2k_rule_append_uint16 (ba, actions->len); for (i = 0; i < actions->len; i++) append_action (ba, actions->pdata[i]); actlen = ba->len - actlen_offset - 4; e2k_rule_write_uint32 (ba->data + actlen_offset, actlen); }