Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / servers / exchange / lib / e2k-action.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /* Copyright (C) 2002-2004 Novell, Inc.
4  *
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.
8  *
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.
13  *
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.
18  */
19
20 /* e2k-action.c: Exchange server-side rule actions */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <string.h>
27
28 #include "e2k-action.h"
29 #include "e2k-propnames.h"
30 #include "e2k-restriction.h"
31 #include "e2k-rule.h"
32 #include "e2k-utils.h"
33 #include "mapi.h"
34
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)
38
39 static GByteArray *
40 copy_bytearray (GByteArray *ba)
41 {
42         GByteArray *copy;
43
44         copy = g_byte_array_sized_new (ba->len);
45         copy->len = ba->len;
46         memcpy (copy->data, ba->data, copy->len);
47
48         return copy;
49 }
50
51 static E2kAction *
52 xfer_action (E2kActionType type, GByteArray *store_entryid,
53              GByteArray *folder_source_key)
54 {
55         E2kAction *act;
56
57         act = g_new0 (E2kAction, 1);
58         act->type = type;
59         act->act.xfer.store_entryid = copy_bytearray (store_entryid);
60         act->act.xfer.folder_source_key = copy_bytearray (folder_source_key);
61
62         return act;
63 }
64
65 /**
66  * e2k_action_move:
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
69  *
70  * Creates a rule action to move a message into the indicated folder
71  *
72  * Return value: the new rule action
73  **/
74 E2kAction *
75 e2k_action_move (GByteArray *store_entryid, GByteArray *folder_source_key)
76 {
77         return xfer_action (E2K_ACTION_MOVE, store_entryid, folder_source_key);
78 }
79
80 /**
81  * e2k_action_copy:
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
84  *
85  * Creates a rule action to copy a message into the indicated folder
86  *
87  * Return value: the new rule action
88  **/
89 E2kAction *
90 e2k_action_copy (GByteArray *store_entryid, GByteArray *folder_source_key)
91 {
92         return xfer_action (E2K_ACTION_COPY, store_entryid, folder_source_key);
93 }
94
95 static E2kAction *
96 reply_action (E2kActionType type, GByteArray *template_entryid,
97               guint8 template_guid[16])
98 {
99         E2kAction *act;
100
101         act = g_new0 (E2kAction, 1);
102         act->type = type;
103         act->act.reply.entryid = copy_bytearray (template_entryid);
104         memcpy (act->act.reply.reply_template_guid, template_guid, 16);
105
106         return act;
107 }
108
109 /**
110  * e2k_action_reply:
111  * @template_entryid: The entryid of the reply template
112  * @template_guid: The GUID of the reply template
113  *
114  * Creates a rule action to reply to a message using the indicated
115  * template
116  *
117  * Return value: the new rule action
118  **/
119 E2kAction *
120 e2k_action_reply (GByteArray *template_entryid, guint8 template_guid[16])
121 {
122         return reply_action (E2K_ACTION_REPLY, template_entryid, template_guid);
123 }
124
125 /**
126  * e2k_action_oof_reply:
127  * @template_entryid: The entryid of the reply template
128  * @template_guid: The GUID of the reply template
129  *
130  * Creates a rule action to send an Out-of-Office reply to a message
131  * using the indicated template
132  *
133  * Return value: the new rule action
134  **/
135 E2kAction *
136 e2k_action_oof_reply (GByteArray *template_entryid, guint8 template_guid[16])
137 {
138         return reply_action (E2K_ACTION_OOF_REPLY, template_entryid, template_guid);
139 }
140
141 /**
142  * e2k_action_defer:
143  * @data: data identifying the deferred action
144  *
145  * Creates a rule action to defer processing on a message
146  *
147  * Return value: the new rule action
148  **/
149 E2kAction *
150 e2k_action_defer (GByteArray *data)
151 {
152         E2kAction *act;
153
154         act = g_new0 (E2kAction, 1);
155         act->type = E2K_ACTION_DEFER;
156         act->act.defer_data = copy_bytearray (data);
157
158         return act;
159 }
160
161 /**
162  * e2k_action_bounce:
163  * @bounce_code: a bounce code
164  *
165  * Creates a rule action to bounce a message
166  *
167  * Return value: the new rule action
168  **/
169 E2kAction *
170 e2k_action_bounce (E2kActionBounceCode bounce_code)
171 {
172         E2kAction *act;
173
174         act = g_new0 (E2kAction, 1);
175         act->type = E2K_ACTION_BOUNCE;
176         act->act.bounce_code = bounce_code;
177
178         return act;
179 }
180
181 static E2kAction *
182 forward_action (E2kActionType type, E2kAddrList *list)
183 {
184         E2kAction *act;
185
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);
188
189         act = g_new0 (E2kAction, 1);
190         act->type = type;
191         act->act.addr_list = list;
192
193         return act;
194 }
195
196 /**
197  * e2k_action_forward:
198  * @list: a list of recipients
199  *
200  * Creates a rule action to forward a message to the indicated list of
201  * recipients
202  *
203  * Return value: the new rule action
204  **/
205 E2kAction *
206 e2k_action_forward (E2kAddrList *list)
207 {
208         return forward_action (E2K_ACTION_FORWARD, list);
209 }
210
211 /**
212  * e2k_action_delegate:
213  * @list: a list of recipients
214  *
215  * Creates a rule action to delegate a meeting request to the
216  * indicated list of recipients
217  *
218  * Return value: the new rule action
219  **/
220 E2kAction *
221 e2k_action_delegate (E2kAddrList *list)
222 {
223         return forward_action (E2K_ACTION_DELEGATE, list);
224 }
225
226 /**
227  * e2k_action_tag:
228  * @propname: a MAPI property name
229  * @type: the type of @propname
230  * @value: the value for @propname
231  *
232  * Creates a rule action to set the given property to the given value
233  * on a message.
234  *
235  * Return value: the new rule action
236  **/
237 E2kAction *
238 e2k_action_tag (const char *propname, E2kPropType type, gpointer value)
239 {
240         E2kAction *act;
241
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? */
247
248         return act;
249 }
250
251 /**
252  * e2k_action_delete:
253  *
254  * Creates a rule action to permanently delete a message (ie, not just
255  * move it to the trash).
256  *
257  * Return value: the new rule action
258  **/
259 E2kAction *
260 e2k_action_delete (void)
261 {
262         E2kAction *act;
263
264         act = g_new0 (E2kAction, 1);
265         act->type = E2K_ACTION_DELETE;
266
267         return act;
268 }
269
270
271 /**
272  * e2k_addr_list_new:
273  * @nentries: the number of entries
274  *
275  * Creates an address list for a forward or delegate rule, with
276  * @nentries slots
277  *
278  * Return value: the new address list
279  **/
280 E2kAddrList *
281 e2k_addr_list_new (int nentries)
282 {
283         E2kAddrList *list;
284
285         list = g_malloc0 (sizeof (E2kAddrList) +
286                           (nentries - 1) * sizeof (E2kAddrEntry));
287         list->nentries = nentries;
288
289         return list;
290 }
291
292 static void
293 addr_entry_set_core (E2kPropValue *pv, GByteArray *entryid,
294                      const char *display_name, const char *email_type,
295                      const char *email_addr)
296 {
297         e2k_rule_prop_set (&pv[0].prop, PR_ENTRYID);
298         pv[0].type = E2K_PROP_TYPE_BINARY;
299         pv[0].value = entryid;
300
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);
304
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);
308
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);
312
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);
316
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);
320
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);
324
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" */
328
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);
332
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);
336 }
337
338 /**
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
345  *
346  * Sets entry number @entry_num of @list to refer to the indicated
347  * local Exchange user.
348  **/
349 void
350 e2k_addr_list_set_local (E2kAddrList *list, int entry_num,
351                          const char *display_name,
352                          const char *exchange_dn,
353                          const char *email)
354 {
355         E2kPropValue *pv;
356
357         list->entry[entry_num].nvalues = 12;
358         list->entry[entry_num].propval = pv = g_new0 (E2kPropValue, 12);
359
360         addr_entry_set_core (pv, e2k_entryid_generate_local (exchange_dn),
361                              display_name, "EX", exchange_dn);
362
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");
366
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);
370 }
371
372 /**
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
378  *
379  * Sets entry number @entry_num of @list to refer to the indicated
380  * "one-off" SMTP user.
381  **/
382 void
383 e2k_addr_list_set_oneoff (E2kAddrList *list, int entry_num,
384                           const char *display_name, const char *email)
385 {
386         E2kPropValue *pv;
387
388         list->entry[entry_num].nvalues = 12;
389         list->entry[entry_num].propval = pv = g_new0 (E2kPropValue, 12);
390
391         addr_entry_set_core (pv, e2k_entryid_generate_oneoff (display_name, email, TRUE),
392                              display_name, "SMTP", email);
393
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);
397
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);
401 }
402
403 /**
404  * e2k_addr_list_free:
405  * @list: the address list
406  *
407  * Frees @list and all its entries.
408  **/
409 void
410 e2k_addr_list_free (E2kAddrList *list)
411 {
412         int i, j;
413         E2kAddrEntry *entry;
414
415         for (i = 0; i < list->nentries; i++) {
416                 entry = &list->entry[i];
417
418                 for (j = 0; j < entry->nvalues; j++)
419                         e2k_rule_free_propvalue (&entry->propval[j]);
420                 g_free (entry->propval);
421         }
422         g_free (list);
423 }
424
425 /**
426  * e2k_action_free:
427  * @act: the action
428  *
429  * Frees @act
430  **/
431 void
432 e2k_action_free (E2kAction *act)
433 {
434         switch (act->type) {
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);
441                 break;
442
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);
447                 break;
448
449         case E2K_ACTION_DEFER:
450                 if (act->act.defer_data)
451                         g_byte_array_free (act->act.defer_data, TRUE);
452                 break;
453
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);
458                 break;
459
460         case E2K_ACTION_TAG:
461                 e2k_rule_free_propvalue (&act->act.proptag);
462                 break;
463
464         default:
465                 /* Nothing to free */
466                 break;
467         }
468
469         g_free (act);
470 }
471
472 /**
473  * e2k_actions_free:
474  * @actions: an array of #E2kAction
475  *
476  * Frees @actions and all of its elements
477  **/
478 void
479 e2k_actions_free (GPtrArray *actions)
480 {
481         int i;
482
483         for (i = 0; i < actions->len; i++)
484                 e2k_action_free (actions->pdata[i]);
485         g_ptr_array_free (actions, TRUE);
486 }
487
488 static gboolean
489 extract_action (guint8 **data, int *len, E2kAction **act_ret)
490 {
491         int my_len;
492         guint8 *my_data;
493         guint16 actlen;
494         E2kAction *act;
495
496         if (!e2k_rule_extract_uint16 (data, len, &actlen))
497                 return FALSE;
498
499         my_data = *data;
500         my_len = actlen;
501
502         *data += actlen;
503         *len -= actlen;
504
505         data = &my_data;
506         len = &my_len;
507
508         if (*len < 1)
509                 return FALSE;
510
511         act = g_new0 (E2kAction, 1);
512         act->type = **data;
513         (*data)++;
514         (*len)--;
515
516         if (!e2k_rule_extract_uint32 (data, len, &act->flavor))
517                 goto lose;
518         if (!e2k_rule_extract_uint32 (data, len, &act->flags))
519                 goto lose;
520
521         switch (act->type) {
522         case E2K_ACTION_MOVE:
523         case E2K_ACTION_COPY:
524                 /* FIXME: what is this? */
525                 if (*len < 1 || **data != 1)
526                         goto lose;
527                 (*len)--;
528                 (*data)++;
529
530                 if (!e2k_rule_extract_binary (data, len, &act->act.xfer.store_entryid))
531                         goto lose;
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)
537                         goto lose;
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);
544
545                 if (!e2k_rule_extract_binary (data, len, &act->act.xfer.folder_source_key))
546                         goto lose;
547                 /* Likewise */
548                 if (act->act.xfer.folder_source_key->len < 1 ||
549                     act->act.xfer.folder_source_key->data[0] != MAPI_FOLDER)
550                         goto lose;
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);
554
555                 *act_ret = act;
556                 return TRUE;
557
558         case E2K_ACTION_REPLY:
559         case E2K_ACTION_OOF_REPLY:
560                 /* The reply template GUID is 16 bytes, the entryid
561                  * is the rest.
562                  */
563                 if (*len <= 16)
564                         goto lose;
565
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);
570
571                 *act_ret = act;
572                 return TRUE;
573
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;
578
579                 *act_ret = act;
580                 return TRUE;
581
582         case E2K_ACTION_BOUNCE:
583                 if (!e2k_rule_extract_uint32 (data, len, &act->act.bounce_code))
584                         goto lose;
585
586                 *act_ret = act;
587                 return TRUE;
588
589         case E2K_ACTION_FORWARD:
590         case E2K_ACTION_DELEGATE:
591         {
592                 guint16 nentries, nvalues;
593                 int i, j;
594
595                 if (!e2k_rule_extract_uint16 (data, len, &nentries))
596                         goto lose;
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)
601                                 goto lose;
602                         (*len)--;
603                         (*data)++;
604
605                         if (!e2k_rule_extract_uint16 (data, len, &nvalues))
606                                 goto lose;
607                         act->act.addr_list->entry[i].nvalues = nvalues;
608                         act->act.addr_list->entry[i].propval = g_new0 (E2kPropValue, nvalues);
609
610                         for (j = 0; j < nvalues; j++) {
611                                 if (!e2k_rule_extract_propvalue (data, len, &act->act.addr_list->entry[i].propval[j]))
612                                         goto lose;
613                         }
614                 }
615
616                 *act_ret = act;
617                 return TRUE;
618         }
619
620         case E2K_ACTION_TAG:
621                 if (!e2k_rule_extract_propvalue (data, len, &act->act.proptag))
622                         goto lose;
623
624                 *act_ret = act;
625                 return TRUE;
626
627         case E2K_ACTION_DELETE:
628                 *act_ret = act;
629                 return TRUE;
630
631         case E2K_ACTION_MARK_AS_READ:
632                 /* FIXME */
633                 return FALSE;
634
635         default:
636                 break;
637         }
638
639  lose:
640         e2k_action_free (act);
641         return FALSE;   
642 }
643
644 /**
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
649  *
650  * Attempts to extract a list of actions from *@data, which contains a
651  * binary-encoded list of actions from a server-side rule.
652  *
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.
656  *
657  * Return value: success or failure
658  **/
659 gboolean
660 e2k_actions_extract (guint8 **data, int *len, GPtrArray **actions)
661 {
662         GPtrArray *acts;
663         E2kAction *act;
664         guint32 actlen;
665         guint16 nacts;
666         int i;
667
668         if (!e2k_rule_extract_uint32 (data, len, &actlen))
669                 return FALSE;
670         if (actlen > *len)
671                 return FALSE;
672
673         if (!e2k_rule_extract_uint16 (data, len, &nacts))
674                 return FALSE;
675
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);
680                         return FALSE;
681                 } else
682                         g_ptr_array_add (acts, act);
683         }
684
685         *actions = acts;
686         return TRUE;
687 }
688
689 static void
690 append_action (GByteArray *ba, E2kAction *act)
691 {
692         int actlen_offset, actlen;
693         char type;
694
695         /* Save space for length */
696         actlen_offset = ba->len;
697         e2k_rule_append_uint16 (ba, 0);
698
699         e2k_rule_append_byte (ba, act->type);
700         e2k_rule_append_uint32 (ba, act->flavor);
701         e2k_rule_append_uint32 (ba, act->flags);
702
703         switch (act->type) {
704         case E2K_ACTION_MOVE:
705         case E2K_ACTION_COPY:
706                 /* FIXME: what is this? */
707                 e2k_rule_append_byte (ba, 1);
708
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);
715
716                 e2k_rule_append_uint16 (ba, 49);
717                 type = MAPI_FOLDER;
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);
721                 break;
722
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);
728                 break;
729
730         case E2K_ACTION_DEFER:
731                 g_byte_array_append (ba, act->act.defer_data->data,
732                                      act->act.defer_data->len);
733                 break;
734
735         case E2K_ACTION_BOUNCE:
736                 e2k_rule_append_uint32 (ba, act->act.bounce_code);
737                 break;
738
739         case E2K_ACTION_FORWARD:
740         case E2K_ACTION_DELEGATE:
741         {
742                 int i, j;
743                 E2kAddrList *list;
744                 E2kAddrEntry *entry;
745
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);
751
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]);
756                 }
757                 break;
758         }
759
760         case E2K_ACTION_TAG:
761                 e2k_rule_append_propvalue (ba, &act->act.proptag);
762                 break;
763
764         case E2K_ACTION_DELETE:
765                 break;
766
767         case E2K_ACTION_MARK_AS_READ:
768                 /* FIXME */
769                 break;
770
771         default:
772                 break;
773         }
774
775         actlen = ba->len - actlen_offset - 2;
776         e2k_rule_write_uint16 (ba->data + actlen_offset, actlen);
777 }
778
779 /**
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
783  *
784  * Appends @actions to @ba as part of a server-side rule.
785  **/
786 void
787 e2k_actions_append (GByteArray *ba, GPtrArray *actions)
788 {
789         int actlen_offset, actlen, i;
790
791         /* Save space for length */
792         actlen_offset = ba->len;
793         e2k_rule_append_uint32 (ba, 0);
794
795         e2k_rule_append_uint16 (ba, actions->len);
796         for (i = 0; i < actions->len; i++)
797                 append_action (ba, actions->pdata[i]);
798
799         actlen = ba->len - actlen_offset - 4;
800         e2k_rule_write_uint32 (ba->data + actlen_offset, actlen);
801 }