1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 /* Copyright (C) 2002-2004 Novell, Inc.
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of version 2 of the GNU Lesser General Public
7 * License as published by the Free Software Foundation.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this program; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
20 /* e2k-action.c: Exchange server-side rule actions */
28 #include "e2k-action.h"
29 #include "e2k-propnames.h"
30 #include "e2k-restriction.h"
32 #include "e2k-utils.h"
35 /* The apparently-constant store entryid prefix for a move or copy action */
36 #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"
37 #define E2K_ACTION_XFER_STORE_ENTRYID_PREFIX_LEN (sizeof (E2K_ACTION_XFER_STORE_ENTRYID_PREFIX) - 1)
40 copy_bytearray (GByteArray *ba)
44 copy = g_byte_array_sized_new (ba->len);
46 memcpy (copy->data, ba->data, copy->len);
52 xfer_action (E2kActionType type, GByteArray *store_entryid,
53 GByteArray *folder_source_key)
57 act = g_new0 (E2kAction, 1);
59 act->act.xfer.store_entryid = copy_bytearray (store_entryid);
60 act->act.xfer.folder_source_key = copy_bytearray (folder_source_key);
67 * @store_entryid: The PR_STORE_ENTRYID of the message store
68 * @folder_source_key: The PR_SOURCE_KEY of a folder in that store
70 * Creates a rule action to move a message into the indicated folder
72 * Return value: the new rule action
75 e2k_action_move (GByteArray *store_entryid, GByteArray *folder_source_key)
77 return xfer_action (E2K_ACTION_MOVE, store_entryid, folder_source_key);
82 * @store_entryid: The PR_STORE_ENTRYID of the message store
83 * @folder_source_key: The PR_SOURCE_KEY of a folder in that store
85 * Creates a rule action to copy a message into the indicated folder
87 * Return value: the new rule action
90 e2k_action_copy (GByteArray *store_entryid, GByteArray *folder_source_key)
92 return xfer_action (E2K_ACTION_COPY, store_entryid, folder_source_key);
96 reply_action (E2kActionType type, GByteArray *template_entryid,
97 guint8 template_guid[16])
101 act = g_new0 (E2kAction, 1);
103 act->act.reply.entryid = copy_bytearray (template_entryid);
104 memcpy (act->act.reply.reply_template_guid, template_guid, 16);
111 * @template_entryid: The entryid of the reply template
112 * @template_guid: The GUID of the reply template
114 * Creates a rule action to reply to a message using the indicated
117 * Return value: the new rule action
120 e2k_action_reply (GByteArray *template_entryid, guint8 template_guid[16])
122 return reply_action (E2K_ACTION_REPLY, template_entryid, template_guid);
126 * e2k_action_oof_reply:
127 * @template_entryid: The entryid of the reply template
128 * @template_guid: The GUID of the reply template
130 * Creates a rule action to send an Out-of-Office reply to a message
131 * using the indicated template
133 * Return value: the new rule action
136 e2k_action_oof_reply (GByteArray *template_entryid, guint8 template_guid[16])
138 return reply_action (E2K_ACTION_OOF_REPLY, template_entryid, template_guid);
143 * @data: data identifying the deferred action
145 * Creates a rule action to defer processing on a message
147 * Return value: the new rule action
150 e2k_action_defer (GByteArray *data)
154 act = g_new0 (E2kAction, 1);
155 act->type = E2K_ACTION_DEFER;
156 act->act.defer_data = copy_bytearray (data);
163 * @bounce_code: a bounce code
165 * Creates a rule action to bounce a message
167 * Return value: the new rule action
170 e2k_action_bounce (E2kActionBounceCode bounce_code)
174 act = g_new0 (E2kAction, 1);
175 act->type = E2K_ACTION_BOUNCE;
176 act->act.bounce_code = bounce_code;
182 forward_action (E2kActionType type, E2kAddrList *list)
186 g_return_val_if_fail (type == E2K_ACTION_FORWARD || type == E2K_ACTION_DELEGATE, NULL);
187 g_return_val_if_fail (list->nentries > 0, NULL);
189 act = g_new0 (E2kAction, 1);
191 act->act.addr_list = list;
197 * e2k_action_forward:
198 * @list: a list of recipients
200 * Creates a rule action to forward a message to the indicated list of
203 * Return value: the new rule action
206 e2k_action_forward (E2kAddrList *list)
208 return forward_action (E2K_ACTION_FORWARD, list);
212 * e2k_action_delegate:
213 * @list: a list of recipients
215 * Creates a rule action to delegate a meeting request to the
216 * indicated list of recipients
218 * Return value: the new rule action
221 e2k_action_delegate (E2kAddrList *list)
223 return forward_action (E2K_ACTION_DELEGATE, list);
228 * @propname: a MAPI property name
229 * @type: the type of @propname
230 * @value: the value for @propname
232 * Creates a rule action to set the given property to the given value
235 * Return value: the new rule action
238 e2k_action_tag (const char *propname, E2kPropType type, gpointer value)
242 act = g_new0 (E2kAction, 1);
243 act->type = E2K_ACTION_TAG;
244 e2k_rule_prop_set (&act->act.proptag.prop, propname);
245 act->act.proptag.type = type;
246 act->act.proptag.value = value; /* FIXME: copy? */
254 * Creates a rule action to permanently delete a message (ie, not just
255 * move it to the trash).
257 * Return value: the new rule action
260 e2k_action_delete (void)
264 act = g_new0 (E2kAction, 1);
265 act->type = E2K_ACTION_DELETE;
273 * @nentries: the number of entries
275 * Creates an address list for a forward or delegate rule, with
278 * Return value: the new address list
281 e2k_addr_list_new (int nentries)
285 list = g_malloc0 (sizeof (E2kAddrList) +
286 (nentries - 1) * sizeof (E2kAddrEntry));
287 list->nentries = nentries;
293 addr_entry_set_core (E2kPropValue *pv, GByteArray *entryid,
294 const char *display_name, const char *email_type,
295 const char *email_addr)
297 e2k_rule_prop_set (&pv[0].prop, PR_ENTRYID);
298 pv[0].type = E2K_PROP_TYPE_BINARY;
299 pv[0].value = entryid;
301 e2k_rule_prop_set (&pv[1].prop, PR_DISPLAY_NAME);
302 pv[1].type = E2K_PROP_TYPE_STRING;
303 pv[1].value = g_strdup (display_name);
305 e2k_rule_prop_set (&pv[2].prop, PR_OBJECT_TYPE);
306 pv[2].type = E2K_PROP_TYPE_INT;
307 pv[2].value = GINT_TO_POINTER (MAPI_MAILUSER);
309 e2k_rule_prop_set (&pv[3].prop, PR_DISPLAY_TYPE);
310 pv[3].type = E2K_PROP_TYPE_INT;
311 pv[3].value = GINT_TO_POINTER (DT_MAILUSER);
313 e2k_rule_prop_set (&pv[4].prop, PR_TRANSMITTABLE_DISPLAY_NAME);
314 pv[4].type = E2K_PROP_TYPE_STRING;
315 pv[4].value = g_strdup (display_name);
317 e2k_rule_prop_set (&pv[5].prop, PR_EMAIL_ADDRESS);
318 pv[5].type = E2K_PROP_TYPE_STRING;
319 pv[5].value = g_strdup (email_addr);
321 e2k_rule_prop_set (&pv[6].prop, PR_ADDRTYPE);
322 pv[6].type = E2K_PROP_TYPE_STRING;
323 pv[6].value = g_strdup (email_type);
325 e2k_rule_prop_set (&pv[7].prop, PR_SEND_INTERNET_ENCODING);
326 pv[7].type = E2K_PROP_TYPE_INT;
327 pv[7].value = GINT_TO_POINTER (0); /* "Let transport decide" */
329 e2k_rule_prop_set (&pv[8].prop, PR_RECIPIENT_TYPE);
330 pv[8].type = E2K_PROP_TYPE_INT;
331 pv[8].value = GINT_TO_POINTER (MAPI_TO);
333 e2k_rule_prop_set (&pv[9].prop, PR_SEARCH_KEY);
334 pv[9].type = E2K_PROP_TYPE_BINARY;
335 pv[9].value = e2k_search_key_generate (email_type, email_addr);
339 * e2k_addr_list_set_local:
340 * @list: the address list
341 * @entry_num: the list entry to set
342 * @display_name: the UTF-8 display name of the recipient
343 * @exchange_dn: the Exchange 5.5-style DN of the recipient
344 * @email: the SMTP email address of the recipient
346 * Sets entry number @entry_num of @list to refer to the indicated
347 * local Exchange user.
350 e2k_addr_list_set_local (E2kAddrList *list, int entry_num,
351 const char *display_name,
352 const char *exchange_dn,
357 list->entry[entry_num].nvalues = 12;
358 list->entry[entry_num].propval = pv = g_new0 (E2kPropValue, 12);
360 addr_entry_set_core (pv, e2k_entryid_generate_local (exchange_dn),
361 display_name, "EX", exchange_dn);
363 e2k_rule_prop_set (&pv[10].prop, PR_EMS_AB_DISPLAY_NAME_PRINTABLE);
364 pv[10].type = E2K_PROP_TYPE_STRING;
365 pv[10].value = g_strdup ("FIXME");
367 e2k_rule_prop_set (&pv[11].prop, PR_SMTP_ADDRESS);
368 pv[11].type = E2K_PROP_TYPE_STRING;
369 pv[11].value = g_strdup (email);
373 * e2k_addr_list_set_oneoff:
374 * @list: the address list
375 * @entry_num: the list entry to set
376 * @display_name: the UTF-8 display name of the recipient
377 * @email: the SMTP email address of the recipient
379 * Sets entry number @entry_num of @list to refer to the indicated
380 * "one-off" SMTP user.
383 e2k_addr_list_set_oneoff (E2kAddrList *list, int entry_num,
384 const char *display_name, const char *email)
388 list->entry[entry_num].nvalues = 12;
389 list->entry[entry_num].propval = pv = g_new0 (E2kPropValue, 12);
391 addr_entry_set_core (pv, e2k_entryid_generate_oneoff (display_name, email, TRUE),
392 display_name, "SMTP", email);
394 e2k_rule_prop_set (&pv[10].prop, PR_SEND_RICH_INFO);
395 pv[10].type = E2K_PROP_TYPE_BOOL;
396 pv[10].value = GINT_TO_POINTER (FALSE);
398 e2k_rule_prop_set (&pv[11].prop, PR_RECORD_KEY);
399 pv[11].type = E2K_PROP_TYPE_BINARY;
400 pv[11].value = e2k_entryid_generate_oneoff (display_name, email, FALSE);
404 * e2k_addr_list_free:
405 * @list: the address list
407 * Frees @list and all its entries.
410 e2k_addr_list_free (E2kAddrList *list)
415 for (i = 0; i < list->nentries; i++) {
416 entry = &list->entry[i];
418 for (j = 0; j < entry->nvalues; j++)
419 e2k_rule_free_propvalue (&entry->propval[j]);
420 g_free (entry->propval);
432 e2k_action_free (E2kAction *act)
435 case E2K_ACTION_MOVE:
436 case E2K_ACTION_COPY:
437 if (act->act.xfer.store_entryid)
438 g_byte_array_free (act->act.xfer.store_entryid, TRUE);
439 if (act->act.xfer.folder_source_key)
440 g_byte_array_free (act->act.xfer.folder_source_key, TRUE);
443 case E2K_ACTION_REPLY:
444 case E2K_ACTION_OOF_REPLY:
445 if (act->act.reply.entryid)
446 g_byte_array_free (act->act.reply.entryid, TRUE);
449 case E2K_ACTION_DEFER:
450 if (act->act.defer_data)
451 g_byte_array_free (act->act.defer_data, TRUE);
454 case E2K_ACTION_FORWARD:
455 case E2K_ACTION_DELEGATE:
456 if (act->act.addr_list)
457 e2k_addr_list_free (act->act.addr_list);
461 e2k_rule_free_propvalue (&act->act.proptag);
465 /* Nothing to free */
474 * @actions: an array of #E2kAction
476 * Frees @actions and all of its elements
479 e2k_actions_free (GPtrArray *actions)
483 for (i = 0; i < actions->len; i++)
484 e2k_action_free (actions->pdata[i]);
485 g_ptr_array_free (actions, TRUE);
489 extract_action (guint8 **data, int *len, E2kAction **act_ret)
496 if (!e2k_rule_extract_uint16 (data, len, &actlen))
511 act = g_new0 (E2kAction, 1);
516 if (!e2k_rule_extract_uint32 (data, len, &act->flavor))
518 if (!e2k_rule_extract_uint32 (data, len, &act->flags))
522 case E2K_ACTION_MOVE:
523 case E2K_ACTION_COPY:
524 /* FIXME: what is this? */
525 if (*len < 1 || **data != 1)
530 if (!e2k_rule_extract_binary (data, len, &act->act.xfer.store_entryid))
532 /* Remove the constant part */
533 if (act->act.xfer.store_entryid->len <= E2K_ACTION_XFER_STORE_ENTRYID_PREFIX_LEN ||
534 memcmp (act->act.xfer.store_entryid->data,
535 E2K_ACTION_XFER_STORE_ENTRYID_PREFIX,
536 E2K_ACTION_XFER_STORE_ENTRYID_PREFIX_LEN) != 0)
538 act->act.xfer.store_entryid->len -=
539 E2K_ACTION_XFER_STORE_ENTRYID_PREFIX_LEN;
540 memmove (act->act.xfer.store_entryid->data,
541 act->act.xfer.store_entryid->data +
542 E2K_ACTION_XFER_STORE_ENTRYID_PREFIX_LEN,
543 act->act.xfer.store_entryid->len);
545 if (!e2k_rule_extract_binary (data, len, &act->act.xfer.folder_source_key))
548 if (act->act.xfer.folder_source_key->len < 1 ||
549 act->act.xfer.folder_source_key->data[0] != MAPI_FOLDER)
551 memmove (act->act.xfer.folder_source_key->data,
552 act->act.xfer.folder_source_key->data + 1,
553 act->act.xfer.folder_source_key->len);
558 case E2K_ACTION_REPLY:
559 case E2K_ACTION_OOF_REPLY:
560 /* The reply template GUID is 16 bytes, the entryid
566 act->act.reply.entryid = g_byte_array_sized_new (*len - 16);
567 memcpy (act->act.reply.entryid->data, *data, *len - 16);
568 act->act.reply.entryid->len = *len - 16;
569 memcpy (act->act.reply.reply_template_guid, *data + *len - 16, 16);
574 case E2K_ACTION_DEFER:
575 act->act.defer_data = g_byte_array_sized_new (*len);
576 memcpy (act->act.defer_data->data, *data, *len);
577 act->act.defer_data->len = *len;
582 case E2K_ACTION_BOUNCE:
583 if (!e2k_rule_extract_uint32 (data, len, &act->act.bounce_code))
589 case E2K_ACTION_FORWARD:
590 case E2K_ACTION_DELEGATE:
592 guint16 nentries, nvalues;
595 if (!e2k_rule_extract_uint16 (data, len, &nentries))
597 act->act.addr_list = e2k_addr_list_new (nentries);
598 for (i = 0; i < nentries; i++) {
599 /* FIXME: what is this? */
600 if (*len < 1 || **data != 1)
605 if (!e2k_rule_extract_uint16 (data, len, &nvalues))
607 act->act.addr_list->entry[i].nvalues = nvalues;
608 act->act.addr_list->entry[i].propval = g_new0 (E2kPropValue, nvalues);
610 for (j = 0; j < nvalues; j++) {
611 if (!e2k_rule_extract_propvalue (data, len, &act->act.addr_list->entry[i].propval[j]))
621 if (!e2k_rule_extract_propvalue (data, len, &act->act.proptag))
627 case E2K_ACTION_DELETE:
631 case E2K_ACTION_MARK_AS_READ:
640 e2k_action_free (act);
645 * e2k_actions_extract:
646 * @data: pointer to data pointer
647 * @len: pointer to data length
648 * @actions: pointer to array to store actions in
650 * Attempts to extract a list of actions from *@data, which contains a
651 * binary-encoded list of actions from a server-side rule.
653 * On success, *@actions will contain the extracted list, *@data will
654 * be advanced past the end of the restriction data, and *@len will be
655 * decremented accordingly.
657 * Return value: success or failure
660 e2k_actions_extract (guint8 **data, int *len, GPtrArray **actions)
668 if (!e2k_rule_extract_uint32 (data, len, &actlen))
673 if (!e2k_rule_extract_uint16 (data, len, &nacts))
676 acts = g_ptr_array_new ();
677 for (i = 0; i < nacts; i++) {
678 if (!extract_action (data, len, &act)) {
679 e2k_actions_free (acts);
682 g_ptr_array_add (acts, act);
690 append_action (GByteArray *ba, E2kAction *act)
692 int actlen_offset, actlen;
695 /* Save space for length */
696 actlen_offset = ba->len;
697 e2k_rule_append_uint16 (ba, 0);
699 e2k_rule_append_byte (ba, act->type);
700 e2k_rule_append_uint32 (ba, act->flavor);
701 e2k_rule_append_uint32 (ba, act->flags);
704 case E2K_ACTION_MOVE:
705 case E2K_ACTION_COPY:
706 /* FIXME: what is this? */
707 e2k_rule_append_byte (ba, 1);
709 e2k_rule_append_uint16 (ba, act->act.xfer.store_entryid->len +
710 E2K_ACTION_XFER_STORE_ENTRYID_PREFIX_LEN);
711 g_byte_array_append (ba, E2K_ACTION_XFER_STORE_ENTRYID_PREFIX,
712 E2K_ACTION_XFER_STORE_ENTRYID_PREFIX_LEN);
713 g_byte_array_append (ba, act->act.xfer.store_entryid->data,
714 act->act.xfer.store_entryid->len);
716 e2k_rule_append_uint16 (ba, 49);
718 g_byte_array_append (ba, &type, 1);
719 g_byte_array_append (ba, act->act.xfer.folder_source_key->data,
720 act->act.xfer.folder_source_key->len);
723 case E2K_ACTION_REPLY:
724 case E2K_ACTION_OOF_REPLY:
725 g_byte_array_append (ba, act->act.reply.entryid->data,
726 act->act.reply.entryid->len);
727 g_byte_array_append (ba, act->act.reply.reply_template_guid, 16);
730 case E2K_ACTION_DEFER:
731 g_byte_array_append (ba, act->act.defer_data->data,
732 act->act.defer_data->len);
735 case E2K_ACTION_BOUNCE:
736 e2k_rule_append_uint32 (ba, act->act.bounce_code);
739 case E2K_ACTION_FORWARD:
740 case E2K_ACTION_DELEGATE:
746 list = act->act.addr_list;
747 e2k_rule_append_uint16 (ba, list->nentries);
748 for (i = 0; i < list->nentries; i++) {
749 /* FIXME: what is this? */
750 e2k_rule_append_byte (ba, 1);
752 entry = &list->entry[i];
753 e2k_rule_append_uint16 (ba, entry->nvalues);
754 for (j = 0; j < entry->nvalues; j++)
755 e2k_rule_append_propvalue (ba, &entry->propval[j]);
761 e2k_rule_append_propvalue (ba, &act->act.proptag);
764 case E2K_ACTION_DELETE:
767 case E2K_ACTION_MARK_AS_READ:
775 actlen = ba->len - actlen_offset - 2;
776 e2k_rule_write_uint16 (ba->data + actlen_offset, actlen);
780 * e2k_actions_append:
781 * @ba: a buffer into which a server-side rule is being constructed
782 * @actions: the actions to append to @ba
784 * Appends @actions to @ba as part of a server-side rule.
787 e2k_actions_append (GByteArray *ba, GPtrArray *actions)
789 int actlen_offset, actlen, i;
791 /* Save space for length */
792 actlen_offset = ba->len;
793 e2k_rule_append_uint32 (ba, 0);
795 e2k_rule_append_uint16 (ba, actions->len);
796 for (i = 0; i < actions->len; i++)
797 append_action (ba, actions->pdata[i]);
799 actlen = ba->len - actlen_offset - 4;
800 e2k_rule_write_uint32 (ba->data + actlen_offset, actlen);