Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / libedataserver / e-account.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2003 Ximian, Inc.
4  *
5  * This library 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 library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "e-account.h"
25
26 #include "e-uid.h"
27
28 #include <string.h>
29
30 #include <libxml/parser.h>
31 #include <libxml/tree.h>
32 #include <libxml/xmlmemory.h>
33
34 #include <gconf/gconf-client.h>
35
36 #define d(x)
37
38 enum {
39         CHANGED,
40         LAST_SIGNAL
41 };
42
43 static guint signals[LAST_SIGNAL];
44
45 static void finalize (GObject *);
46
47 G_DEFINE_TYPE (EAccount, e_account, G_TYPE_OBJECT);
48
49 /*
50 lock mail accounts      Relatively difficult -- involves redesign of the XML blobs which describe accounts
51 disable adding mail accounts    Simple -- can be done with just a Gconf key and some UI work to make assoc. widgets unavailable
52 disable editing mail accounts   Relatively difficult -- involves redesign of the XML blobs which describe accounts
53 disable removing mail accounts  
54 lock default character encoding Simple -- Gconf key + a little UI work to desensitize widgets, etc
55 disable free busy publishing    
56 disable specific mime types (from being viewed) 90% done already (Unknown MIME types still pose a problem)
57 lock image loading preference   
58 lock junk mail filtering settings       
59 **  junk mail per account
60 lock work week  
61 lock first day of work week     
62 lock working hours      
63 disable forward as icalendar    
64 lock color options for tasks    
65 lock default contact filing format      
66 * forbid signatures     Simple -- can be done with just a Gconf key and some UI work to make assoc. widgets unavailable
67 * lock user to having 1 specific signature      Simple -- can be done with just a Gconf key and some UI work to make assoc. widgets unavailable
68 * forbid adding/removing signatures     Simple -- can be done with just a Gconf key and some UI work to make assoc. widgets unavailable
69 * lock each account to a certain signature      Relatively difficult -- involved redesign of the XML blobs which describe accounts 
70 * set default folders   
71 set trash emptying frequency    
72 * lock displayed mail headers   Simple -- can be done with just a Gconf key and some UI work to make assoc. widgets unavailable
73 * lock authentication type (for incoming mail)  Relatively difficult -- involves redesign of the XML blobs which describe accounts
74 * lock authentication type (for outgoing mail)  Relatively difficult -- involves redesign of the XML blobs which describe accounts
75 * lock minimum check mail on server frequency   Simple -- can be done with just a Gconf key and some UI work to make assoc. widgets unavailable
76 ** lock save password
77 * require ssl always    Relatively difficult -- involves redesign of the XML blobs which describe accounts
78 ** lock imap subscribed folder option
79 ** lock filtering of inbox
80 ** lock source account/options
81 ** lock destination account/options
82 */
83
84 static void
85 e_account_class_init (EAccountClass *account_class)
86 {
87         GObjectClass *object_class = G_OBJECT_CLASS (account_class);
88
89         /* virtual method override */
90         object_class->finalize = finalize;
91
92         signals[CHANGED] =
93                 g_signal_new("changed",
94                              G_OBJECT_CLASS_TYPE (object_class),
95                              G_SIGNAL_RUN_LAST,
96                              G_STRUCT_OFFSET (EAccountClass, changed),
97                              NULL, NULL,
98                              g_cclosure_marshal_VOID__INT,
99                              G_TYPE_NONE, 1,
100                              G_TYPE_INT);
101 }
102
103 static void
104 e_account_init (EAccount *account)
105 {
106         account->id = g_new0 (EAccountIdentity, 1);
107         account->source = g_new0 (EAccountService, 1);
108         account->transport = g_new0 (EAccountService, 1);
109
110         account->parent_uid = NULL;
111         
112         account->source->auto_check = FALSE;
113         account->source->auto_check_time = 10;
114 }
115
116 static void
117 identity_destroy (EAccountIdentity *id)
118 {
119         if (!id)
120                 return;
121
122         g_free (id->name);
123         g_free (id->address);
124         g_free (id->reply_to);
125         g_free (id->organization);
126         g_free (id->sig_uid);
127
128         g_free (id);
129 }
130
131 static void
132 service_destroy (EAccountService *service)
133 {
134         if (!service)
135                 return;
136
137         g_free (service->url);
138
139         g_free (service);
140 }
141
142 static void
143 finalize (GObject *object)
144 {
145         EAccount *account = E_ACCOUNT (object);
146
147         g_free (account->name);
148         g_free (account->uid);
149
150         identity_destroy (account->id);
151         service_destroy (account->source);
152         service_destroy (account->transport);
153
154         g_free (account->drafts_folder_uri);
155         g_free (account->sent_folder_uri);
156
157         g_free (account->cc_addrs);
158         g_free (account->bcc_addrs);
159
160         g_free (account->pgp_key);
161         g_free (account->smime_sign_key);
162         g_free (account->smime_encrypt_key);
163
164         g_free (account->parent_uid);
165
166         G_OBJECT_CLASS (e_account_parent_class)->finalize (object);
167 }
168
169 /**
170  * e_account_new:
171  *
172  * Return value: a blank new account which can be filled in and
173  * added to an #EAccountList.
174  **/
175 EAccount *
176 e_account_new (void)
177 {
178         EAccount *account;
179
180         account = g_object_new (E_TYPE_ACCOUNT, NULL);
181         account->uid = e_uid_new ();
182
183         return account;
184 }
185
186 /**
187  * e_account_new_from_xml:
188  * @xml: an XML account description
189  *
190  * Return value: a new #EAccount based on the data in @xml, or %NULL
191  * if @xml could not be parsed as valid account data.
192  **/
193 EAccount *
194 e_account_new_from_xml (const char *xml)
195 {
196         EAccount *account;
197
198         account = g_object_new (E_TYPE_ACCOUNT, NULL);
199         if (!e_account_set_from_xml (account, xml)) {
200                 g_object_unref (account);
201                 return NULL;
202         }
203
204         return account;
205 }
206
207
208 static gboolean
209 xml_set_bool (xmlNodePtr node, const char *name, gboolean *val)
210 {
211         gboolean bool;
212         xmlChar *buf;
213
214         if ((buf = xmlGetProp (node, (xmlChar*)name))) {
215                 bool = (!strcmp ((char*)buf, "true") || !strcmp ((char*)buf, "yes"));
216                 xmlFree (buf);
217
218                 if (bool != *val) {
219                         *val = bool;
220                         return TRUE;
221                 }
222         }
223
224         return FALSE;
225 }
226
227 static gboolean
228 xml_set_int (xmlNodePtr node, const char *name, int *val)
229 {
230         int number;
231         xmlChar *buf;
232
233         if ((buf = xmlGetProp (node, (xmlChar*)name))) {
234                 number = strtol ((char*)buf, NULL, 10);
235                 xmlFree (buf);
236
237                 if (number != *val) {
238                         *val = number;
239                         return TRUE;
240                 }
241         }
242
243         return FALSE;
244 }
245
246 static gboolean
247 xml_set_prop (xmlNodePtr node, const char *name, char **val)
248 {
249         xmlChar *buf;
250         int res;
251
252         buf = xmlGetProp (node, (xmlChar*)name);
253         if (buf == NULL) {
254                 res = (*val != NULL);
255                 if (res) {
256                         g_free(*val);
257                         *val = NULL;
258                 }
259         } else {
260                 res = *val == NULL || strcmp(*val, (char*)buf) != 0;
261                 if (res) {
262                         g_free(*val);
263                         *val = g_strdup((char*)buf);
264                 }
265                 xmlFree(buf);
266         }
267
268         return res;
269 }
270
271 static EAccountReceiptPolicy
272 str_to_receipt_policy (const xmlChar *str)
273 {
274         if (!strcmp ((char*)str, "ask"))
275                 return E_ACCOUNT_RECEIPT_ASK;
276         if (!strcmp ((char*)str, "always"))
277                 return E_ACCOUNT_RECEIPT_ALWAYS;
278
279         return E_ACCOUNT_RECEIPT_NEVER;
280 }
281
282 static xmlChar*
283 receipt_policy_to_str (EAccountReceiptPolicy val)
284 {
285         char *ret = NULL;
286         
287         switch (val) {
288         case E_ACCOUNT_RECEIPT_NEVER:
289                 ret = "never";
290                 break;
291         case E_ACCOUNT_RECEIPT_ASK:
292                 ret = "ask";
293                 break;
294         case E_ACCOUNT_RECEIPT_ALWAYS:
295                 ret = "always";
296                 break;
297         }
298
299         return (xmlChar*)ret;
300 }
301
302 static gboolean
303 xml_set_receipt_policy (xmlNodePtr node, const char *name, EAccountReceiptPolicy *val)
304 {
305         EAccountReceiptPolicy new_val;
306         xmlChar *buf;
307
308         if ((buf = xmlGetProp (node, (xmlChar*)name))) {
309                 new_val = str_to_receipt_policy (buf);
310                 xmlFree (buf);
311
312                 if (new_val != *val) {
313                         *val = new_val;
314                         return TRUE;
315                 }
316         }
317
318         return FALSE;
319 }
320
321 static gboolean
322 xml_set_content (xmlNodePtr node, char **val)
323 {
324         xmlChar *buf;
325         int res;
326
327         buf = xmlNodeGetContent(node);
328         if (buf == NULL) {
329                 res = (*val != NULL);
330                 if (res) {
331                         g_free(*val);
332                         *val = NULL;
333                 }
334         } else {
335                 res = *val == NULL || strcmp(*val, (char*)buf) != 0;
336                 if (res) {
337                         g_free(*val);
338                         *val = g_strdup((char*)buf);
339                 }
340                 xmlFree(buf);
341         }
342
343         return res;
344 }
345
346 static gboolean
347 xml_set_identity (xmlNodePtr node, EAccountIdentity *id)
348 {
349         gboolean changed = FALSE;
350
351         for (node = node->children; node; node = node->next) {
352                 if (!strcmp ((char*)node->name, "name"))
353                         changed |= xml_set_content (node, &id->name);
354                 else if (!strcmp ((char*)node->name, "addr-spec"))
355                         changed |= xml_set_content (node, &id->address);
356                 else if (!strcmp ((char*)node->name, "reply-to"))
357                         changed |= xml_set_content (node, &id->reply_to);
358                 else if (!strcmp ((char*)node->name, "organization"))
359                         changed |= xml_set_content (node, &id->organization);
360                 else if (!strcmp ((char*)node->name, "signature")) {
361                         changed |= xml_set_prop (node, "uid", &id->sig_uid);
362                         if (!id->sig_uid) {
363
364                                 /* WTF is this shit doing here?  Migrate is supposed to "handle this" */
365
366                                 /* set a fake sig uid so the migrate code can handle this */
367                                 gboolean autogen = FALSE;
368                                 int sig_id = 0;
369                                 
370                                 xml_set_bool (node, "auto", &autogen);
371                                 xml_set_int (node, "default", &sig_id);
372                                 
373                                 if (autogen) {
374                                         id->sig_uid = g_strdup ("::0");
375                                         changed = TRUE;
376                                 } else if (sig_id) {
377                                         id->sig_uid = g_strdup_printf ("::%d", sig_id + 1);
378                                         changed = TRUE;
379                                 }
380                         }
381                 }
382         }
383
384         return changed;
385 }
386
387 static gboolean
388 xml_set_service (xmlNodePtr node, EAccountService *service)
389 {
390         gboolean changed = FALSE;
391
392         changed |= xml_set_bool (node, "save-passwd", &service->save_passwd);
393         changed |= xml_set_bool (node, "keep-on-server", &service->keep_on_server);
394
395         changed |= xml_set_bool (node, "auto-check", &service->auto_check);
396         changed |= xml_set_int (node, "auto-check-timeout", &service->auto_check_time);
397         if (service->auto_check && service->auto_check_time <= 0) {
398                 service->auto_check = FALSE;
399                 service->auto_check_time = 0;
400         }
401
402         for (node = node->children; node; node = node->next) {
403                 if (!strcmp ((char*)node->name, "url")) {
404                         changed |= xml_set_content (node, &service->url);
405                         break;
406                 }
407         }
408
409         return changed;
410 }
411
412 /**
413  * e_account_set_from_xml:
414  * @account: an #EAccount
415  * @xml: an XML account description.
416  *
417  * Changes @account to match @xml.
418  *
419  * Return value: %TRUE if @account was changed, %FALSE if @account
420  * already matched @xml or @xml could not be parsed
421  **/
422 gboolean
423 e_account_set_from_xml (EAccount *account, const char *xml)
424 {
425         xmlNodePtr node, cur;
426         xmlDocPtr doc;
427         gboolean changed = FALSE;
428
429         if (!(doc = xmlParseDoc ((xmlChar*)xml)))
430                 return FALSE;
431
432         node = doc->children;
433         if (strcmp ((char*)node->name, "account") != 0) {
434                 xmlFreeDoc (doc);
435                 return FALSE;
436         }
437
438         if (!account->uid)
439                 xml_set_prop (node, "uid", &account->uid);
440
441         changed |= xml_set_prop (node, "name", &account->name);
442         changed |= xml_set_bool (node, "enabled", &account->enabled);
443
444         for (node = node->children; node; node = node->next) {
445                 if (!strcmp ((char*)node->name, "identity")) {
446                         changed |= xml_set_identity (node, account->id);
447                 } else if (!strcmp ((char*)node->name, "source")) {
448                         changed |= xml_set_service (node, account->source);
449                 } else if (!strcmp ((char*)node->name, "transport")) {
450                         changed |= xml_set_service (node, account->transport);
451                 } else if (!strcmp ((char*)node->name, "drafts-folder")) {
452                         changed |= xml_set_content (node, &account->drafts_folder_uri);
453                 } else if (!strcmp ((char*)node->name, "sent-folder")) {
454                         changed |= xml_set_content (node, &account->sent_folder_uri);
455                 } else if (!strcmp ((char*)node->name, "auto-cc")) {
456                         changed |= xml_set_bool (node, "always", &account->always_cc);
457                         changed |= xml_set_content (node, &account->cc_addrs);
458                 } else if (!strcmp ((char*)node->name, "auto-bcc")) {
459                         changed |= xml_set_bool (node, "always", &account->always_bcc);
460                         changed |= xml_set_content (node, &account->bcc_addrs);
461                 } else if (!strcmp ((char*)node->name, "receipt-policy")) {
462                         changed |= xml_set_receipt_policy (node, "policy", &account->receipt_policy);
463                 } else if (!strcmp ((char*)node->name, "pgp")) {
464                         changed |= xml_set_bool (node, "encrypt-to-self", &account->pgp_encrypt_to_self);
465                         changed |= xml_set_bool (node, "always-trust", &account->pgp_always_trust);
466                         changed |= xml_set_bool (node, "always-sign", &account->pgp_always_sign);
467                         changed |= xml_set_bool (node, "no-imip-sign", &account->pgp_no_imip_sign);
468
469                         if (node->children) {
470                                 for (cur = node->children; cur; cur = cur->next) {
471                                         if (!strcmp ((char*)cur->name, "key-id")) {
472                                                 changed |= xml_set_content (cur, &account->pgp_key);
473                                                 break;
474                                         }
475                                 }
476                         }
477                 } else if (!strcmp ((char*)node->name, "smime")) {
478                         changed |= xml_set_bool (node, "sign-default", &account->smime_sign_default);
479                         changed |= xml_set_bool (node, "encrypt-to-self", &account->smime_encrypt_to_self);
480                         changed |= xml_set_bool (node, "encrypt-default", &account->smime_encrypt_default);
481
482                         if (node->children) {
483                                 for (cur = node->children; cur; cur = cur->next) {
484                                         if (!strcmp ((char*)cur->name, "sign-key-id")) {
485                                                 changed |= xml_set_content (cur, &account->smime_sign_key);
486                                         } else if (!strcmp ((char*)cur->name, "encrypt-key-id")) {
487                                                 changed |= xml_set_content (cur, &account->smime_encrypt_key);
488                                                 break;
489                                         }
490                                 }
491                         }
492                 } else if (!strcmp ((char*)node->name, "proxy")) {
493                         if (node->children) {
494                                 for (cur = node->children; cur; cur = cur->next) {
495                                         if (!strcmp ((char*)cur->name, "parent-uid")) {
496                                                 changed |= xml_set_content (cur, &account->parent_uid);
497                                                 break;
498                                         }
499                                 }
500                         }
501                 }
502         }
503
504         xmlFreeDoc (doc);
505
506         g_signal_emit(account, signals[CHANGED], 0, -1);
507
508         return changed;
509 }
510
511
512 /**
513  * e_account_import:
514  * @dest: destination account object
515  * @src: source account object
516  *
517  * Import the settings from @src to @dest.
518  **/
519 void
520 e_account_import (EAccount *dest, EAccount *src)
521 {
522         g_free (dest->name);
523         dest->name = g_strdup (src->name);
524         
525         dest->enabled = src->enabled;
526         
527         g_free (dest->id->name);
528         dest->id->name = g_strdup (src->id->name);
529         g_free (dest->id->address);
530         dest->id->address = g_strdup (src->id->address);
531         g_free (dest->id->reply_to);
532         dest->id->reply_to = g_strdup (src->id->reply_to);
533         g_free (dest->id->organization);
534         dest->id->organization = g_strdup (src->id->organization);
535         dest->id->sig_uid = g_strdup (src->id->sig_uid);
536         
537         g_free (dest->source->url);
538         dest->source->url = g_strdup (src->source->url);
539         dest->source->keep_on_server = src->source->keep_on_server;
540         dest->source->auto_check = src->source->auto_check;
541         dest->source->auto_check_time = src->source->auto_check_time;
542         dest->source->save_passwd = src->source->save_passwd;
543         
544         g_free (dest->transport->url);
545         dest->transport->url = g_strdup (src->transport->url);
546         dest->transport->save_passwd = src->transport->save_passwd;
547         
548         g_free (dest->drafts_folder_uri);
549         dest->drafts_folder_uri = g_strdup (src->drafts_folder_uri);
550         
551         g_free (dest->sent_folder_uri);
552         dest->sent_folder_uri = g_strdup (src->sent_folder_uri);
553         
554         dest->always_cc = src->always_cc;
555         g_free (dest->cc_addrs);
556         dest->cc_addrs = g_strdup (src->cc_addrs);
557         
558         dest->always_bcc = src->always_bcc;
559         g_free (dest->bcc_addrs);
560         dest->bcc_addrs = g_strdup (src->bcc_addrs);
561         
562         dest->receipt_policy = src->receipt_policy;
563         
564         g_free (dest->pgp_key);
565         dest->pgp_key = g_strdup (src->pgp_key);
566         dest->pgp_encrypt_to_self = src->pgp_encrypt_to_self;
567         dest->pgp_always_sign = src->pgp_always_sign;
568         dest->pgp_no_imip_sign = src->pgp_no_imip_sign;
569         dest->pgp_always_trust = src->pgp_always_trust;
570         
571         dest->smime_sign_default = src->smime_sign_default;
572         g_free (dest->smime_sign_key);
573         dest->smime_sign_key = g_strdup (src->smime_sign_key);
574
575         dest->smime_encrypt_default = src->smime_encrypt_default;
576         dest->smime_encrypt_to_self = src->smime_encrypt_to_self;
577         g_free (dest->smime_encrypt_key);
578         dest->smime_encrypt_key = g_strdup (src->smime_encrypt_key);
579         
580         g_signal_emit(dest, signals[CHANGED], 0, -1);
581 }
582
583
584 /**
585  * e_account_to_xml:
586  * @account: an #EAccount
587  *
588  * Return value: an XML representation of @account, which the caller
589  * must free.
590  **/
591 char *
592 e_account_to_xml (EAccount *account)
593 {
594         xmlNodePtr root, node, id, src, xport;
595         char *tmp, buf[20];
596         xmlChar *xmlbuf;
597         xmlDocPtr doc;
598         int n;
599
600         doc = xmlNewDoc ((xmlChar*)"1.0");
601
602         root = xmlNewDocNode (doc, NULL, (xmlChar*)"account", NULL);
603         xmlDocSetRootElement (doc, root);
604
605         xmlSetProp (root, (xmlChar*)"name", (xmlChar*)account->name);
606         xmlSetProp (root, (xmlChar*)"uid", (xmlChar*)account->uid);
607         xmlSetProp (root, (xmlChar*)"enabled", (xmlChar*)(account->enabled ? "true" : "false"));
608
609         id = xmlNewChild (root, NULL, (xmlChar*)"identity", NULL);
610         if (account->id->name)
611                 xmlNewTextChild (id, NULL, (xmlChar*)"name", (xmlChar*)account->id->name);
612         if (account->id->address)
613                 xmlNewTextChild (id, NULL, (xmlChar*)"addr-spec", (xmlChar*)account->id->address);
614         if (account->id->reply_to)
615                 xmlNewTextChild (id, NULL, (xmlChar*)"reply-to", (xmlChar*)account->id->reply_to);
616         if (account->id->organization)
617                 xmlNewTextChild (id, NULL, (xmlChar*)"organization", (xmlChar*)account->id->organization);
618
619         node = xmlNewChild (id, NULL, (xmlChar*)"signature",NULL);
620         xmlSetProp (node, (xmlChar*)"uid", (xmlChar*)account->id->sig_uid);
621
622         src = xmlNewChild (root, NULL, (xmlChar*)"source", NULL);
623         xmlSetProp (src, (xmlChar*)"save-passwd", (xmlChar*)(account->source->save_passwd ? "true" : "false"));
624         xmlSetProp (src, (xmlChar*)"keep-on-server", (xmlChar*)(account->source->keep_on_server ? "true" : "false"));
625         xmlSetProp (src, (xmlChar*)"auto-check", (xmlChar*)(account->source->auto_check ? "true" : "false"));
626         sprintf (buf, "%d", account->source->auto_check_time);
627         xmlSetProp (src, (xmlChar*)"auto-check-timeout", (xmlChar*)buf);
628         if (account->source->url)
629                 xmlNewTextChild (src, NULL, (xmlChar*)"url", (xmlChar*)account->source->url);
630
631         xport = xmlNewChild (root, NULL, (xmlChar*)"transport", NULL);
632         xmlSetProp (xport, (xmlChar*)"save-passwd", (xmlChar*)(account->transport->save_passwd ? "true" : "false"));
633         if (account->transport->url)
634                 xmlNewTextChild (xport, NULL, (xmlChar*)"url", (xmlChar*)account->transport->url);
635
636         xmlNewTextChild (root, NULL, (xmlChar*)"drafts-folder", (xmlChar*)account->drafts_folder_uri);
637         xmlNewTextChild (root, NULL, (xmlChar*)"sent-folder", (xmlChar*)account->sent_folder_uri);
638
639         node = xmlNewChild (root, NULL, (xmlChar*)"auto-cc", NULL);
640         xmlSetProp (node, (xmlChar*)"always", (xmlChar*)(account->always_cc ? "true" : "false"));
641         if (account->cc_addrs)
642                 xmlNewTextChild (node, NULL, (xmlChar*)"recipients", (xmlChar*)account->cc_addrs);
643
644         node = xmlNewChild (root, NULL, (xmlChar*)"auto-bcc", NULL);
645         xmlSetProp (node, (xmlChar*)"always", (xmlChar*)(account->always_bcc ? "true" : "false"));
646         if (account->bcc_addrs)
647                 xmlNewTextChild (node, NULL, (xmlChar*)"recipients", (xmlChar*)account->bcc_addrs);
648
649         node = xmlNewChild (root, NULL, (xmlChar*)"receipt-policy", NULL);
650         xmlSetProp (node, (xmlChar*)"policy", receipt_policy_to_str (account->receipt_policy));
651         
652         node = xmlNewChild (root, NULL, (xmlChar*)"pgp", NULL);
653         xmlSetProp (node, (xmlChar*)"encrypt-to-self", (xmlChar*)(account->pgp_encrypt_to_self ? "true" : "false"));
654         xmlSetProp (node, (xmlChar*)"always-trust", (xmlChar*)(account->pgp_always_trust ? "true" : "false"));
655         xmlSetProp (node, (xmlChar*)"always-sign", (xmlChar*)(account->pgp_always_sign ? "true" : "false"));
656         xmlSetProp (node, (xmlChar*)"no-imip-sign", (xmlChar*)(account->pgp_no_imip_sign ? "true" : "false"));
657         if (account->pgp_key)
658                 xmlNewTextChild (node, NULL, (xmlChar*)"key-id", (xmlChar*)account->pgp_key);
659
660         node = xmlNewChild (root, NULL, (xmlChar*)"smime", NULL);
661         xmlSetProp (node, (xmlChar*)"sign-default", (xmlChar*)(account->smime_sign_default ? "true" : "false"));
662         xmlSetProp (node, (xmlChar*)"encrypt-default", (xmlChar*)(account->smime_encrypt_default ? "true" : "false"));
663         xmlSetProp (node, (xmlChar*)"encrypt-to-self", (xmlChar*)(account->smime_encrypt_to_self ? "true" : "false"));
664         if (account->smime_sign_key)
665                 xmlNewTextChild (node, NULL, (xmlChar*)"sign-key-id", (xmlChar*)account->smime_sign_key);
666         if (account->smime_encrypt_key)
667                 xmlNewTextChild (node, NULL, (xmlChar*)"encrypt-key-id", (xmlChar*)account->smime_encrypt_key);
668
669         if (account->parent_uid) {
670                 node = xmlNewChild (root, NULL, (xmlChar*)"proxy", NULL);
671                 xmlNewTextChild (node, NULL, (xmlChar*)"parent-uid", (xmlChar*)account->parent_uid);
672         }       
673
674         xmlDocDumpMemory (doc, &xmlbuf, &n);
675         xmlFreeDoc (doc);
676
677         /* remap to glib memory */
678         tmp = g_malloc (n + 1);
679         memcpy (tmp, xmlbuf, n);
680         tmp[n] = '\0';
681         xmlFree (xmlbuf);
682
683         return tmp;
684 }
685
686
687 /**
688  * e_account_uid_from_xml:
689  * @xml: an XML account description
690  *
691  * Return value: the permanent UID of the account described by @xml
692  * (or %NULL if @xml could not be parsed or did not contain a uid).
693  * The caller must free this string.
694  **/
695 char *
696 e_account_uid_from_xml (const char *xml)
697 {
698         xmlNodePtr node;
699         xmlDocPtr doc;
700         char *uid = NULL;
701
702         if (!(doc = xmlParseDoc ((xmlChar *)xml)))
703                 return NULL;
704
705         node = doc->children;
706         if (strcmp ((char*)node->name, "account") != 0) {
707                 xmlFreeDoc (doc);
708                 return NULL;
709         }
710
711         xml_set_prop (node, "uid", &uid);
712         xmlFreeDoc (doc);
713
714         return uid;
715 }
716
717 enum {
718         EAP_IMAP_SUBSCRIBED = 0,
719         EAP_IMAP_NAMESPACE,
720         EAP_FILTER_INBOX,
721         EAP_FILTER_JUNK,
722         EAP_FORCE_SSL,
723         EAP_LOCK_SIGNATURE,
724         EAP_LOCK_AUTH,
725         EAP_LOCK_AUTOCHECK,
726         EAP_LOCK_DEFAULT_FOLDERS,
727         EAP_LOCK_SAVE_PASSWD,
728         EAP_LOCK_SOURCE,
729         EAP_LOCK_TRANSPORT,
730 };
731
732 static struct _system_info {
733         const char *key;
734         guint32 perm;
735 } system_perms[] = {
736         { "imap_subscribed", 1<<EAP_IMAP_SUBSCRIBED },
737         { "imap_namespace", 1<<EAP_IMAP_NAMESPACE },
738         { "filter_inbox", 1<<EAP_FILTER_INBOX },
739         { "filter_junk", 1<<EAP_FILTER_JUNK },
740         { "ssl", 1<<EAP_FORCE_SSL },
741         { "signature", 1<<EAP_LOCK_SIGNATURE },
742         { "authtype", 1<<EAP_LOCK_AUTH },
743         { "autocheck", 1<<EAP_LOCK_AUTOCHECK },
744         { "default_folders", 1<<EAP_LOCK_DEFAULT_FOLDERS },
745         { "save_passwd" , 1<<EAP_LOCK_SAVE_PASSWD },
746         { "source", 1<<EAP_LOCK_SOURCE },
747         { "transport", 1<<EAP_LOCK_TRANSPORT },
748 };
749
750 #define TYPE_STRING (1)
751 #define TYPE_INT (2)
752 #define TYPE_BOOL (3)
753 #define TYPE_MASK (0xff)
754 #define TYPE_STRUCT (1<<8)
755
756 static struct _account_info {
757         guint32 perms;
758         guint32 type;
759         unsigned int offset;
760         unsigned int struct_offset;
761 } account_info[E_ACCOUNT_ITEM_LAST] = {
762         { /* E_ACCOUNT_NAME */ 0, TYPE_STRING, G_STRUCT_OFFSET(EAccount, name) },
763
764         { /* E_ACCOUNT_ID_NAME, */ 0, TYPE_STRING|TYPE_STRUCT, G_STRUCT_OFFSET(EAccount, id), G_STRUCT_OFFSET(EAccountIdentity, name) },
765         { /* E_ACCOUNT_ID_ADDRESS, */ 0, TYPE_STRING|TYPE_STRUCT, G_STRUCT_OFFSET(EAccount, id), G_STRUCT_OFFSET(EAccountIdentity, address) },
766         { /* E_ACCOUNT_ID_REPLY_TO, */ 0, TYPE_STRING|TYPE_STRUCT, G_STRUCT_OFFSET(EAccount, id), G_STRUCT_OFFSET(EAccountIdentity, reply_to) },
767         { /* E_ACCOUNT_ID_ORGANIZATION */ 0, TYPE_STRING|TYPE_STRUCT, G_STRUCT_OFFSET(EAccount, id), G_STRUCT_OFFSET(EAccountIdentity, organization) },
768         { /* E_ACCOUNT_ID_SIGNATURE */ 1<<EAP_LOCK_SIGNATURE, TYPE_STRING|TYPE_STRUCT, G_STRUCT_OFFSET(EAccount, id), G_STRUCT_OFFSET(EAccountIdentity, sig_uid) },
769
770         { /* E_ACCOUNT_SOURCE_URL */ 1<<EAP_LOCK_SOURCE, TYPE_STRING|TYPE_STRUCT, G_STRUCT_OFFSET(EAccount, source), G_STRUCT_OFFSET(EAccountService, url) },
771         { /* E_ACCOUNT_SOURCE_KEEP_ON_SERVER */ 0, TYPE_BOOL|TYPE_STRUCT, G_STRUCT_OFFSET(EAccount, source), G_STRUCT_OFFSET(EAccountService, keep_on_server) },
772         { /* E_ACCOUNT_SOURCE_AUTO_CHECK */ 1<<EAP_LOCK_AUTOCHECK, TYPE_BOOL|TYPE_STRUCT, G_STRUCT_OFFSET(EAccount, source), G_STRUCT_OFFSET(EAccountService, auto_check) },
773         { /* E_ACCOUNT_SOURCE_AUTO_CHECK_TIME */ 1<<EAP_LOCK_AUTOCHECK, TYPE_INT|TYPE_STRUCT, G_STRUCT_OFFSET(EAccount, source), G_STRUCT_OFFSET(EAccountService, auto_check_time) },
774         { /* E_ACCOUNT_SOURCE_SAVE_PASSWD */ 1<<EAP_LOCK_SAVE_PASSWD, TYPE_BOOL|TYPE_STRUCT, G_STRUCT_OFFSET(EAccount, source), G_STRUCT_OFFSET(EAccountService, save_passwd) },
775
776         { /* E_ACCOUNT_TRANSPORT_URL */ 1<<EAP_LOCK_TRANSPORT, TYPE_STRING|TYPE_STRUCT, G_STRUCT_OFFSET(EAccount, transport), G_STRUCT_OFFSET(EAccountService, url) },
777         { /* E_ACCOUNT_TRANSPORT_SAVE_PASSWD */ 1<<EAP_LOCK_SAVE_PASSWD, TYPE_BOOL|TYPE_STRUCT, G_STRUCT_OFFSET(EAccount, transport), G_STRUCT_OFFSET(EAccountService, save_passwd) },
778
779         { /* E_ACCOUNT_DRAFTS_FOLDER_URI */ 1<<EAP_LOCK_DEFAULT_FOLDERS, TYPE_STRING, G_STRUCT_OFFSET(EAccount, drafts_folder_uri) },
780         { /* E_ACCOUNT_SENT_FOLDER_URI */ 1<<EAP_LOCK_DEFAULT_FOLDERS, TYPE_STRING, G_STRUCT_OFFSET(EAccount, sent_folder_uri) },
781
782         { /* E_ACCOUNT_CC_ALWAYS */ 0, TYPE_BOOL, G_STRUCT_OFFSET(EAccount, always_cc) },
783         { /* E_ACCOUNT_CC_ADDRS */ 0, TYPE_STRING, G_STRUCT_OFFSET(EAccount, cc_addrs) },
784
785         { /* E_ACCOUNT_BCC_ALWAYS */ 0, TYPE_BOOL, G_STRUCT_OFFSET(EAccount, always_bcc) },
786         { /* E_ACCOUNT_BCC_ADDRS */ 0, TYPE_STRING, G_STRUCT_OFFSET(EAccount, bcc_addrs) },
787
788         { /* E_ACCOUNT_RECEIPT_POLICY */ 0, TYPE_INT, G_STRUCT_OFFSET(EAccount, receipt_policy) },
789         
790         { /* E_ACCOUNT_PGP_KEY */ 0, TYPE_STRING, G_STRUCT_OFFSET(EAccount, pgp_key) },
791         { /* E_ACCOUNT_PGP_ENCRYPT_TO_SELF */ 0, TYPE_BOOL, G_STRUCT_OFFSET(EAccount, pgp_encrypt_to_self) },
792         { /* E_ACCOUNT_PGP_ALWAYS_SIGN */ 0, TYPE_BOOL, G_STRUCT_OFFSET(EAccount, pgp_always_sign) },
793         { /* E_ACCOUNT_PGP_NO_IMIP_SIGN */ 0, TYPE_BOOL, G_STRUCT_OFFSET(EAccount, pgp_no_imip_sign) },
794         { /* E_ACCOUNT_PGP_ALWAYS_TRUST */ 0, TYPE_BOOL, G_STRUCT_OFFSET(EAccount, pgp_always_trust) },
795
796         { /* E_ACCOUNT_SMIME_SIGN_KEY */ 0, TYPE_STRING, G_STRUCT_OFFSET(EAccount, smime_sign_key) },
797         { /* E_ACCOUNT_SMIME_ENCRYPT_KEY */ 0, TYPE_STRING, G_STRUCT_OFFSET(EAccount, smime_encrypt_key) },
798         { /* E_ACCOUNT_SMIME_SIGN_DEFAULT */ 0, TYPE_BOOL, G_STRUCT_OFFSET(EAccount, smime_sign_default) },
799         { /* E_ACCOUNT_SMIME_ENCRYPT_TO_SELF */ 0, TYPE_BOOL, G_STRUCT_OFFSET(EAccount, smime_encrypt_to_self) },
800         { /* E_ACCOUNT_SMIME_ENCRYPT_DEFAULT */ 0, TYPE_BOOL, G_STRUCT_OFFSET(EAccount, smime_encrypt_default) },
801
802         { /* E_ACCOUNT_PROXY_PARENT_UID, */ 0, TYPE_STRING, G_STRUCT_OFFSET(EAccount, parent_uid) },
803 };
804
805 static GHashTable *ea_option_table;
806 static GHashTable *ea_system_table;
807 static guint32 ea_perms;
808
809 static struct _option_info {
810         char *key;
811         guint32 perms;
812 } ea_option_list[] = {
813         { "imap_use_lsub", 1<<EAP_IMAP_SUBSCRIBED },
814         { "imap_override_namespace", 1<<EAP_IMAP_NAMESPACE },
815         { "imap_filter", 1<<EAP_FILTER_INBOX },
816         { "imap_filter_junk", 1<<EAP_FILTER_JUNK },
817         { "imap_filter_junk_inbox", 1<<EAP_FILTER_JUNK },
818         { "*_use_ssl", 1<<EAP_FORCE_SSL },
819         { "*_auth", 1<<EAP_LOCK_AUTH },
820 };
821
822 #define LOCK_BASE "/apps/evolution/lock/mail/accounts"
823
824 static void
825 ea_setting_notify(GConfClient *gconf, guint cnxn_id, GConfEntry *entry, void *crap)
826 {
827         GConfValue *value;
828         char *tkey;
829         struct _system_info *info;
830
831         g_return_if_fail (gconf_entry_get_key (entry) != NULL);
832         
833         if (!(value = gconf_entry_get_value (entry)))
834                 return;
835         
836         tkey = strrchr(entry->key, '/');
837         g_return_if_fail (tkey != NULL);
838
839         info = g_hash_table_lookup(ea_system_table, tkey+1);
840         if (info) {
841                 if (gconf_value_get_bool(value))
842                         ea_perms |= info->perm;
843                 else
844                         ea_perms &= ~info->perm;
845         }
846 }
847
848 static void
849 ea_setting_setup(void)
850 {
851         GConfClient *gconf = gconf_client_get_default();
852         GConfEntry *entry;
853         GError *err = NULL;
854         int i;
855         char key[64];
856
857         if (ea_option_table != NULL)
858                 return;
859
860         ea_option_table = g_hash_table_new(g_str_hash, g_str_equal);
861         for (i=0;i<sizeof(ea_option_list)/sizeof(ea_option_list[0]);i++)
862                 g_hash_table_insert(ea_option_table, ea_option_list[i].key, &ea_option_list[i]);
863
864         gconf_client_add_dir(gconf, LOCK_BASE, GCONF_CLIENT_PRELOAD_NONE, NULL);
865
866         ea_system_table = g_hash_table_new(g_str_hash, g_str_equal);
867         for (i=0;i<sizeof(system_perms)/sizeof(system_perms[0]);i++) {
868                 g_hash_table_insert(ea_system_table, (char *)system_perms[i].key, &system_perms[i]);
869                 sprintf(key, LOCK_BASE "/%s", system_perms[i].key);
870                 entry = gconf_client_get_entry(gconf, key, NULL, TRUE, &err);
871                 if (entry)
872                         ea_setting_notify(gconf, 0, entry, NULL);
873                 gconf_entry_free(entry);
874         }
875
876         if (err) {
877                 g_warning("Could not load account lock settings: %s", err->message);
878                 g_error_free(err);
879         }
880
881         gconf_client_notify_add(gconf, LOCK_BASE, (GConfClientNotifyFunc)ea_setting_notify, NULL, NULL, NULL);
882         g_object_unref(gconf);
883 }
884
885 /* look up the item in the structure or the substructure using our table of reflection data */
886 #define addr(ea, type) \
887         ((account_info[type].type & TYPE_STRUCT)? \
888         (((char **)(((char *)ea)+account_info[type].offset))[0] + account_info[type].struct_offset): \
889         (((char *)ea)+account_info[type].offset))
890         
891 const char *e_account_get_string(EAccount *ea, e_account_item_t type)
892 {
893         return *((const char **)addr(ea, type));
894 }
895
896 int e_account_get_int(EAccount *ea, e_account_item_t type)
897 {
898         return *((int *)addr(ea, type));
899 }
900
901 gboolean e_account_get_bool(EAccount *ea, e_account_item_t type)
902 {
903         return *((gboolean *)addr(ea, type));
904 }
905
906 #if d(!)0
907 static void
908 dump_account(EAccount *ea)
909 {
910         char *xml;
911
912         printf("Account changed\n");
913         xml = e_account_to_xml(ea);
914         printf(" ->\n%s\n", xml);
915         g_free(xml);
916 }
917 #endif
918
919 /* TODO: should it return true if it changed? */
920 void e_account_set_string(EAccount *ea, e_account_item_t type, const char *val)
921 {
922         char **p;
923
924         if (!e_account_writable(ea, type)) {
925                 g_warning("Trying to set non-writable option account value");
926         } else {
927                 p = (char **)addr(ea, type);
928                 d(printf("Setting string %d: old '%s' new '%s'\n", type, *p, val));
929                 if (*p != val
930                     && (*p == NULL || val == NULL || strcmp(*p, val) != 0)) {
931                         g_free(*p);
932                         *p = g_strdup(val);
933                         d(dump_account(ea));
934                         g_signal_emit(ea, signals[CHANGED], 0, type);
935                 }
936         }
937 }
938
939 void e_account_set_int(EAccount *ea, e_account_item_t type, int val)
940 {
941         if (!e_account_writable(ea, type)) {
942                 g_warning("Trying to set non-writable option account value");
943         } else {
944                 int *p = (int *)addr(ea, type);
945
946                 if (*p != val) {
947                         *p = val;
948                         d(dump_account(ea));
949                         g_signal_emit(ea, signals[CHANGED], 0, type);
950                 }
951         }
952 }
953
954 void e_account_set_bool(EAccount *ea, e_account_item_t type, gboolean val)
955 {
956         if (!e_account_writable(ea, type)) {
957                 g_warning("Trying to set non-writable option account value");
958         } else {
959                 gboolean *p = (gboolean *)addr(ea, type);
960
961                 if (*p != val) {
962                         *p = val;
963                         d(dump_account(ea));
964                         g_signal_emit(ea, signals[CHANGED], 0, type);
965                 }
966         }
967 }
968
969 gboolean
970 e_account_writable_option(EAccount *ea, const char *protocol, const char *option)
971 {
972         char *key;
973         struct _option_info *info;
974
975         ea_setting_setup();
976
977         key = alloca(strlen(protocol)+strlen(option)+2);
978         sprintf(key, "%s_%s", protocol, option);
979
980         info = g_hash_table_lookup(ea_option_table, key);
981         if (info == NULL) {
982                 sprintf(key, "*_%s", option);
983                 info = g_hash_table_lookup(ea_option_table, key);
984         }
985
986         d(printf("checking writable option '%s' perms=%08x\n", option, info?info->perms:0));
987
988         return info == NULL
989                 || (info->perms & ea_perms) == 0;
990 }
991
992 gboolean
993 e_account_writable(EAccount *ea, e_account_item_t type)
994 {
995         ea_setting_setup();
996
997         return (account_info[type].perms & ea_perms) == 0;
998 }