017aa8c444bebb8052d812aada1f2b9c06ba451c
[claws.git] / src / plugins / vcalendar / vcal_manager.c
1 /*
2  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2007 Colin Leroy <colin@colino.net> and 
4  * the Claws Mail team
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #  include "config.h"
23 #include "claws-features.h"
24 #endif
25
26 #include <stddef.h>
27 #include <glib.h>
28 #include <glib/gi18n.h>
29
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #ifdef USE_PTHREAD
33 #include <pthread.h>
34 #endif
35 #include <ical.h>
36 #include "vcalendar.h"
37 #include "vcal_folder.h"
38 #include "vcal_manager.h"
39 #include "vcal_meeting_gtk.h"
40 #include "vcal_prefs.h"
41 #include "xml.h"
42 #include "xmlprops.h"
43 #include "prefs.h"
44 #include "prefs_common.h"
45 #include "prefs_account.h"
46 #include "alertpanel.h"
47 #include "account.h"
48 #include "codeconv.h"
49 #include <time.h>
50 #include "folder.h"
51 #include "quoted-printable.h"
52 #include "utils.h"
53
54 #ifdef G_OS_WIN32
55 #define getuid() 0
56 #endif
57
58 Answer *answer_new(const gchar *attendee, 
59                           const gchar *name,
60                           enum icalparameter_partstat ans,
61                           enum icalparameter_cutype cutype)
62 {
63         Answer *answer = g_new0(Answer, 1);
64         answer->attendee = g_strdup(attendee);
65         answer->name = g_strdup(name);
66         if (!answer->name)
67                 answer->name = g_strdup("");
68         if (!answer->attendee)
69                 answer->attendee = g_strdup("");
70         answer->answer = ans;
71         answer->cutype = cutype;
72         return answer;
73 }
74
75 static void answer_free(Answer *answer)
76 {
77         g_free(answer->attendee);
78         g_free(answer->name);
79         g_free(answer);
80 }
81
82 static GSList *answer_find(VCalEvent *event, Answer *answer)
83 {
84         GSList *cur = event->answers;
85         while (cur && cur->data) {
86                 Answer *b = (Answer *)cur->data;
87                 if (!strcasecmp(b->attendee, answer->attendee))
88                         return cur;
89                 cur = cur->next;
90         }
91         return NULL;
92 }
93
94 void vcal_manager_copy_attendees(VCalEvent *src, VCalEvent *dest)
95 {
96         GSList *cur = src->answers;
97         while (cur && cur->data) {
98                 Answer *a = (Answer *)cur->data;
99                 Answer *b = answer_new(a->attendee, a->name, a->answer, a->cutype);
100                 dest->answers = g_slist_prepend(dest->answers, b);
101                 cur = cur->next;
102         }
103         dest->answers = g_slist_reverse(dest->answers);
104 }
105
106 gchar *vcal_manager_answer_get_text(enum icalparameter_partstat ans) 
107 {
108         static gchar *replies[5]={
109                 N_("accepted"),
110                 N_("tentatively accepted"),
111                 N_("declined"),
112                 N_("did not answer"),
113                 N_("unknown")
114         };
115
116         switch (ans) {
117         case ICAL_PARTSTAT_ACCEPTED:
118                 return _(replies[0]);
119                 break;
120         case ICAL_PARTSTAT_DECLINED:
121                 return _(replies[2]);
122                 break;
123         case ICAL_PARTSTAT_TENTATIVE:
124                 return _(replies[1]);
125                 break;
126         case ICAL_PARTSTAT_NEEDSACTION:
127                 return _(replies[3]);
128         case ICAL_PARTSTAT_DELEGATED:
129         case ICAL_PARTSTAT_COMPLETED:
130         case ICAL_PARTSTAT_X:
131         case ICAL_PARTSTAT_INPROCESS:
132         case ICAL_PARTSTAT_NONE:
133                 return _(replies[4]);
134                 break;                  
135         }
136         return NULL;
137 }
138
139 gchar *vcal_manager_cutype_get_text(enum icalparameter_cutype type) 
140 {
141         static gchar *replies[5]={
142                 N_("individual"),
143                 N_("group"),
144                 N_("resource"),
145                 N_("room"),
146                 N_("unknown")
147         };
148
149         switch (type) {
150         case ICAL_CUTYPE_INDIVIDUAL:
151                 return _(replies[0]);
152                 break;
153         case ICAL_CUTYPE_GROUP:
154                 return _(replies[1]);
155                 break;
156         case ICAL_CUTYPE_RESOURCE:
157                 return _(replies[2]);
158                 break;
159         case ICAL_CUTYPE_ROOM:
160                 return _(replies[3]);
161         default:
162                 return _(replies[4]);
163                 break;                  
164         }
165         return NULL;
166 }
167
168 static GSList *answer_remove(VCalEvent *event, Answer *answer)
169 {
170         GSList *cur = answer_find(event, answer);
171         if (cur) {
172                 Answer *b = (Answer *)cur->data;
173                 event->answers = g_slist_remove(event->answers, b);
174                 answer_free(b);
175         }
176         return event->answers;
177 }
178
179 static GSList *answer_add(VCalEvent *event, Answer *answer)
180 {
181         event->answers = g_slist_append(event->answers, answer);
182         return event->answers;
183 }
184
185 GSList *vcal_manager_get_answers_emails(VCalEvent *event)
186 {
187         GSList *new = NULL;
188         GSList *cur = event->answers;
189         while (cur && cur->data) {
190                 Answer *b = (Answer *)cur->data;
191                 new = g_slist_prepend(new, b->attendee);
192                 cur = cur->next;
193         }
194         new = g_slist_reverse(new);
195         return new;     
196 }
197
198 enum icalparameter_partstat vcal_manager_get_reply_for_attendee(VCalEvent *event, const gchar *att)
199 {
200         Answer *a = answer_new(att, NULL, 0, 0);
201         GSList *ans = answer_find(event, a);
202         enum icalparameter_partstat res = 0;
203         if (ans) {
204                 Answer *b = (Answer *)ans->data;
205                 res = b->answer;
206         }
207         answer_free(a);
208         return res;
209 }
210
211 gchar *vcal_manager_get_cutype_text_for_attendee(VCalEvent *event, const gchar *att)
212 {
213         enum icalparameter_cutype status = vcal_manager_get_cutype_for_attendee(event, att);
214         gchar *res = NULL;
215         if (status != 0) 
216                 res = g_strdup(vcal_manager_cutype_get_text(status));
217
218         return res;
219 }
220
221 enum icalparameter_partstat vcal_manager_get_cutype_for_attendee(VCalEvent *event, const gchar *att)
222 {
223         Answer *a = answer_new(att, NULL, 0, 0);
224         GSList *ans = answer_find(event, a);
225         enum icalparameter_cutype res = 0;
226         if (ans) {
227                 Answer *b = (Answer *)ans->data;
228                 res = b->cutype;
229         }
230         answer_free(a);
231         return res;
232 }
233
234 gchar *vcal_manager_get_reply_text_for_attendee(VCalEvent *event, const gchar *att)
235 {
236         enum icalparameter_partstat status = vcal_manager_get_reply_for_attendee(event, att);
237         gchar *res = NULL;
238         if (status != 0) 
239                 res = g_strdup(vcal_manager_answer_get_text(status));
240
241         return res;
242 }
243
244 gchar *vcal_manager_get_attendee_name(VCalEvent *event, const gchar *att)
245 {
246         Answer *a = answer_new(att, NULL, 0, 0);
247         GSList *ans = answer_find(event, a);
248         gchar *res = NULL;
249         if (ans) {
250                 Answer *b = (Answer *)ans->data;
251                 if (b->name)
252                         res = g_strdup(b->name);
253         }
254         answer_free(a);
255         return res;
256 }
257
258 void vcal_manager_event_print(VCalEvent *event)
259 {
260         GSList *list = event->answers;
261         printf( "event->uid\t\t%s\n"
262                 "event->organizer\t\t%s\n"
263                 "event->start\t\t%s\n"
264                 "event->end\t\t%s\n"
265                 "event->location\t\t%s\n"
266                 "event->summary\t\t%s\n"
267                 "event->description\t%s\n"
268                 "event->url\t%s\n"
269                 "event->dtstart\t\t%s\n"
270                 "event->dtend\t\t%s\n"
271                 "event->recur\t\t%s\n"
272                 "event->tzid\t\t%s\n"
273                 "event->method\t\t%d\n"
274                 "event->sequence\t\t%d\n",
275                 event->uid,
276                 event->organizer,
277                 event->start,
278                 event->end,
279                 event->location,
280                 event->summary,
281                 event->description,
282                 event->url,
283                 event->dtstart,
284                 event->dtend,
285                 event->recur,
286                 event->tzid,
287                 event->method,
288                 event->sequence);
289         while (list && list->data) {
290                 Answer *a = (Answer *)list->data;
291                 printf(" ans: %s %s, %s\n", a->name, a->attendee, vcal_manager_answer_get_text(a->answer));
292                 list = list->next;
293         }
294         
295 }
296
297 static gchar *write_headers(PrefsAccount        *account, 
298                             VCalEvent           *event,
299                             gboolean             short_headers,
300                             gboolean             is_reply, 
301                             gboolean             is_pseudo_event);
302
303 gchar *vcal_manager_event_dump(VCalEvent *event, gboolean is_reply, gboolean is_pseudo_event,
304                                 icalcomponent *use_calendar, gboolean modif)
305 {
306         gchar *organizer = g_strdup_printf("MAILTO:%s", event->organizer);
307         PrefsAccount *account = vcal_manager_get_account_from_event(event);
308         gchar *attendee  = NULL;
309         gchar *body, *headers;
310         gchar *tmpfile = NULL;
311         icalcomponent *calendar, *ievent, *timezone, *tzc;
312         icalproperty *attprop;
313         icalproperty *orgprop;
314         enum icalparameter_partstat status = ICAL_PARTSTAT_NEEDSACTION;
315         gchar *sanitized_uid = g_strdup(event->uid);
316         
317         subst_for_filename(sanitized_uid);
318
319         tmpfile = g_strdup_printf("%s%cevt-%d-%s", get_tmp_dir(),
320                                       G_DIR_SEPARATOR, getuid(), sanitized_uid);
321         g_free(sanitized_uid);
322
323         if (!account) {
324                 g_free(organizer);
325                 g_free(tmpfile);
326                 debug_print("no account found\n");
327                 return NULL;
328         }
329
330         attendee = g_strdup_printf("MAILTO:%s", account->address);
331         
332         if (vcal_manager_get_reply_for_attendee(event, account->address) != 0)
333                 status = vcal_manager_get_reply_for_attendee(event, account->address);
334         
335         tzset();
336         
337         if (use_calendar != NULL) {
338                 calendar = use_calendar;
339                 g_free(tmpfile);
340                 tmpfile = NULL;
341         } else
342                 calendar = 
343                         icalcomponent_vanew(
344                             ICAL_VCALENDAR_COMPONENT,
345                             icalproperty_new_version("2.0"),
346                             icalproperty_new_prodid(
347                                  "-//Claws Mail//NONSGML Claws Mail Calendar//EN"),
348                             icalproperty_new_calscale("GREGORIAN"),
349                             icalproperty_new_method(is_reply ? ICAL_METHOD_REPLY:event->method),
350                             (void*)0
351                             );  
352
353         if (!calendar) {
354                 g_warning ("can't generate calendar");
355                 g_free(organizer);
356                 g_free(tmpfile);
357                 g_free(attendee);
358                 return NULL;
359         }
360
361         orgprop = icalproperty_new_organizer(organizer);
362         
363         timezone = icalcomponent_new(ICAL_VTIMEZONE_COMPONENT);
364         
365         icalcomponent_add_property(timezone,
366                 icalproperty_new_tzid("UTC")); /* free */
367         
368         tzc = icalcomponent_new(ICAL_XSTANDARD_COMPONENT);
369         icalcomponent_add_property(tzc,
370                 icalproperty_new_dtstart(
371                         icaltime_from_string("19700101T000000")));
372         icalcomponent_add_property(tzc,
373                 icalproperty_new_tzoffsetfrom(0.0));
374         icalcomponent_add_property(tzc,
375                 icalproperty_new_tzoffsetto(0.0));
376         icalcomponent_add_property(tzc,
377                 icalproperty_new_tzname("Greenwich meridian time"));
378
379         icalcomponent_add_component(timezone, tzc);
380
381         ievent = 
382             icalcomponent_vanew(
383                 ICAL_VEVENT_COMPONENT, (void*)0);
384
385         if (!ievent) {
386                 g_warning ("can't generate event");
387                 g_free(organizer);
388                 g_free(tmpfile);
389                 g_free(attendee);
390                 return NULL;
391         }
392         
393         icalcomponent_add_property(ievent,
394                 icalproperty_new_uid(event->uid));
395         icalcomponent_add_property(ievent,
396                 icalproperty_vanew_dtstamp(icaltime_from_timet(time(NULL), TRUE), 0));
397         icalcomponent_add_property(ievent,
398                 icalproperty_vanew_dtstart((icaltime_from_string(event->dtstart)), 0));
399         icalcomponent_add_property(ievent,
400                 icalproperty_vanew_dtend((icaltime_from_string(event->dtend)), 0));
401         if (event->recur && *(event->recur)) {
402                 icalcomponent_add_property(ievent,
403                         icalproperty_vanew_rrule((icalrecurrencetype_from_string(event->recur)), 0));
404         }
405         icalcomponent_add_property(ievent,
406                 icalproperty_new_description(event->description));
407         icalcomponent_add_property(ievent,
408                 icalproperty_new_summary(event->summary));
409         icalcomponent_add_property(ievent,
410                 icalproperty_new_sequence(modif && !is_reply ? event->sequence + 1 : event->sequence));
411         icalcomponent_add_property(ievent,
412                 icalproperty_new_class("PUBLIC"));
413         icalcomponent_add_property(ievent,
414                 icalproperty_new_transp("OPAQUE"));
415         if (event->location && *event->location)
416                 icalcomponent_add_property(ievent,
417                         icalproperty_new_location(event->location));
418         else
419                 icalcomponent_add_property(ievent,
420                         icalproperty_new_location(""));
421         icalcomponent_add_property(ievent,
422                 icalproperty_new_status(ICAL_STATUS_CONFIRMED));
423         icalcomponent_add_property(ievent,
424                 icalproperty_vanew_created(icaltime_from_timet(time(NULL), TRUE), 0));
425         icalcomponent_add_property(ievent,
426                 icalproperty_vanew_lastmodified(icaltime_from_timet(time(NULL), TRUE), 0));
427         icalcomponent_add_property(ievent,              
428                 orgprop);
429
430         if (!icalcomponent_get_first_component(calendar, ICAL_VTIMEZONE_COMPONENT))
431                 icalcomponent_add_component(calendar, timezone);
432         else 
433                 icalcomponent_free(timezone);
434
435         icalcomponent_add_component(calendar, ievent);
436
437         if (event->url && *(event->url)) {
438                 attprop = icalproperty_new_url(event->url);
439                 icalcomponent_add_property(ievent, attprop);
440         }
441
442         if (is_reply) {
443                 /* dump only this attendee */
444                 attprop =
445                         icalproperty_vanew_attendee(
446                             attendee,
447                             icalparameter_new_role(
448                                 ICAL_ROLE_REQPARTICIPANT),
449                             icalparameter_new_rsvp(ICAL_RSVP_TRUE),
450                             icalparameter_new_partstat(status),
451                             0
452                             );
453                 icalcomponent_add_property(ievent, attprop);
454         } else {
455                 /* dump all attendees */
456                 GSList *cur = event->answers;
457                 while (cur && cur->data) {
458                         Answer *a = (Answer *)cur->data;
459
460                         if (a->cutype == 0)
461                                 a->cutype = ICAL_CUTYPE_INDIVIDUAL;
462                         if (a->answer == 0)
463                                 a->answer = ICAL_PARTSTAT_NEEDSACTION;
464
465                         attprop =
466                                 icalproperty_vanew_attendee(
467                                     a->attendee,
468                                     icalparameter_new_role(
469                                         ICAL_ROLE_REQPARTICIPANT),
470                                     icalparameter_new_rsvp(ICAL_RSVP_TRUE),
471                                     icalparameter_new_cutype(a->cutype),
472                                     icalparameter_new_partstat(a->answer),
473                                     0
474                                     );
475
476                         icalcomponent_add_property(ievent, attprop);
477                         cur = cur->next;
478                 }
479         }
480
481         if (use_calendar) {
482                 g_free(organizer);
483                 g_free(tmpfile);
484                 g_free(attendee);
485                 return NULL;
486         }
487
488         headers = write_headers(account, event, is_pseudo_event, is_reply, is_pseudo_event);
489
490         if (!headers) {
491                 g_warning("can't get headers");
492                 g_free(organizer);
493                 g_free(tmpfile);
494                 g_free(attendee);
495                 return NULL;
496         }
497
498         body = g_strdup_printf("%s"
499                                "\n"
500                                "%s", headers, icalcomponent_as_ical_string(calendar));
501         
502         if (str_write_to_file(body, tmpfile) < 0) {
503                 g_free(tmpfile);
504                 tmpfile = NULL;
505         }
506
507         chmod(tmpfile, S_IRUSR|S_IWUSR);
508
509         g_free(body);
510         g_free(headers);
511         icalcomponent_free(calendar);
512         g_free(attendee);
513         g_free(organizer);
514         
515         tzset();
516
517         return tmpfile; 
518 }
519
520 static void get_rfc822_date_from_time_t(gchar *buf, gint len, time_t t)
521 {
522 #ifndef G_OS_WIN32
523         struct tm *lt;
524         gchar day[4], mon[4];
525         gint dd, hh, mm, ss, yyyy;
526         gchar buft1[512];
527         struct tm buft2;
528
529         lt = localtime_r(&t, &buft2);
530         sscanf(asctime_r(lt, buft1), "%3s %3s %d %d:%d:%d %d\n",
531                day, mon, &dd, &hh, &mm, &ss, &yyyy);
532         g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
533                    day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
534 #else
535         GDateTime *dt = g_date_time_new_from_unix_local(t);
536         gchar *buf2 = g_date_time_format(dt, "%a, %e %b %Y %H:%M:%S %z");
537         g_date_time_unref(dt);
538         strncpy(buf, buf2, len);
539         g_free(buf2);
540 #endif
541 }
542
543 static gchar *write_headers_date(const gchar *uid)
544 {
545         gchar subject[512];
546         gchar *t_subject;
547         gchar date[128];
548         time_t t;
549         struct tm lt;
550
551         memset(subject, 0, sizeof(subject));
552         memset(date, 0, sizeof(date));
553         
554         if (!strcmp(uid, EVENT_PAST_ID)) {
555                 t = 1;
556                 t_subject = _("Past");
557         } else if (!strcmp(uid, EVENT_TODAY_ID)) {
558                 t = time(NULL);
559                 t_subject = _("Today");
560         }  else if (!strcmp(uid, EVENT_TOMORROW_ID)) {
561                 t = time(NULL) + 86400;
562                 t_subject = _("Tomorrow");
563         }  else if (!strcmp(uid, EVENT_THISWEEK_ID)) {
564                 t = time(NULL) + (86400*2);
565                 t_subject = _("This week");
566         }  else if (!strcmp(uid, EVENT_LATER_ID)) {
567                 t = time(NULL) + (86400*7);
568                 t_subject = _("Later");
569         }  else {
570                 g_warning("unknown spec date");
571                 return NULL;
572         } 
573         
574 #ifndef G_OS_WIN32
575         struct tm buft;
576         lt = *localtime_r(&t, &buft);
577 #else
578         if (t < 0)
579                 t = 1;
580         lt = *localtime(&t);
581 #endif
582         lt.tm_hour = lt.tm_min = lt.tm_sec = 0;
583         t = mktime(&lt);
584         get_rfc822_date_from_time_t(date, sizeof(date), t);
585         conv_encode_header(subject, 511, t_subject, strlen("Subject: "), FALSE);
586                                 
587         return g_strdup_printf("From: -\n"
588                                 "To: -\n"
589                                 "Subject: %s\n"
590                                 "Date: %s\n"
591                                 "MIME-Version: 1.0\n"
592                                 "Content-Type: text/plain; charset=\"UTF-8\";\n"
593                                 "Content-Transfer-Encoding: quoted-printable\n"
594                                 "Message-ID: <%s>\n",
595                                 subject,
596                                 date,
597                                 uid);
598 }
599
600 gchar *vcal_manager_dateevent_dump(const gchar *uid, FolderItem *item)
601 {
602         gchar *sanitized_uid = NULL;
603         gchar *headers = NULL;
604         gchar *lines, *body, *tmpfile;
605         EventTime date;
606         sanitized_uid = g_strdup(uid);
607         subst_for_filename(sanitized_uid);
608         
609         tmpfile = g_strdup_printf("%s%cevt-%d-%s", get_tmp_dir(),
610                                    G_DIR_SEPARATOR, getuid(), sanitized_uid);
611         g_free(sanitized_uid);
612
613         headers = write_headers_date(uid);
614
615         if (!headers) {
616                 g_warning("can't get headers");
617                 g_free(tmpfile);
618                 return NULL;
619         }
620
621         if (!strcmp(uid, EVENT_PAST_ID))
622                 date = EVENT_PAST;
623         else if (!strcmp(uid, EVENT_TODAY_ID))
624                 date = EVENT_TODAY;
625         else if (!strcmp(uid, EVENT_TOMORROW_ID))
626                 date = EVENT_TOMORROW;
627         else if (!strcmp(uid, EVENT_THISWEEK_ID))
628                 date = EVENT_THISWEEK;
629         else if (!strcmp(uid, EVENT_LATER_ID))
630                 date = EVENT_LATER;
631         else
632                 date = EVENT_PAST;
633
634         lines = get_item_event_list_for_date(item, date);
635         body = g_strdup_printf("%s"
636                                "\n"
637                                "%s", headers, lines);
638         g_free(lines);
639         if (str_write_to_file(body, tmpfile) < 0) {
640                 g_free(tmpfile);
641                 tmpfile = NULL;
642         } else
643                 chmod(tmpfile, S_IRUSR|S_IWUSR);
644
645         g_free(body);
646         g_free(headers);
647         
648         return tmpfile; 
649 }
650
651 static gchar *write_headers_ical(PrefsAccount   *account, 
652                             icalcomponent       *ievent,
653                             gchar               *orga);
654
655 gchar *vcal_manager_icalevent_dump(icalcomponent *event, gchar *orga, icalcomponent *use_calendar)
656 {
657         PrefsAccount *account = account_get_cur_account();
658         gchar *body, *headers, *qpbody;
659         gchar **lines = NULL;
660         gchar *tmpfile = NULL;
661         icalcomponent *calendar;
662         icalproperty *prop;
663         icalcomponent *ievent = NULL;
664         int i = 0;
665
666         ievent = icalcomponent_new_clone(event);
667
668         prop = icalcomponent_get_first_property(ievent, ICAL_UID_PROPERTY);
669         if (prop) {
670                 gchar *sanitized_uid = g_strdup(icalproperty_get_uid(prop));
671
672                 subst_for_filename(sanitized_uid);
673
674                 tmpfile = g_strdup_printf("%s%cevt-%d-%s", get_tmp_dir(),
675                                               G_DIR_SEPARATOR, getuid(), sanitized_uid);
676                 g_free(sanitized_uid);
677                 icalproperty_free(prop);
678         } else {
679                 tmpfile = g_strdup_printf("%s%cevt-%d-%p", get_tmp_dir(),
680                                       G_DIR_SEPARATOR, getuid(), ievent);
681         }
682
683         if (!account) {
684                 g_free(tmpfile);
685                 icalcomponent_free(ievent);
686                 return NULL;
687         }
688
689         tzset();
690         
691         if (use_calendar != NULL) {
692                 calendar = use_calendar;
693                 g_free(tmpfile);
694                 tmpfile = NULL;
695         } else
696                 calendar = 
697                         icalcomponent_vanew(
698                             ICAL_VCALENDAR_COMPONENT,
699                             icalproperty_new_version("2.0"),
700                             icalproperty_new_prodid(
701                                  "-//Claws Mail//NONSGML Claws Mail Calendar//EN"),
702                             icalproperty_new_calscale("GREGORIAN"),
703                             icalproperty_new_method(ICAL_METHOD_PUBLISH),
704                             (void*)0
705                             );  
706
707         if (!calendar) {
708                 g_warning ("can't generate calendar");
709                 g_free(tmpfile);
710                 icalcomponent_free(ievent);
711                 return NULL;
712         }
713         
714         icalcomponent_add_component(calendar, ievent);
715
716         if (use_calendar)
717                 return NULL;
718
719         headers = write_headers_ical(account, ievent, orga);
720
721         if (!headers) {
722                 g_warning("can't get headers");
723                 g_free(tmpfile);
724                 icalcomponent_free(calendar);
725                 return NULL;
726         }
727
728         lines = g_strsplit(icalcomponent_as_ical_string(calendar), "\n", 0);
729         qpbody = g_strdup("");
730         
731         /* encode to quoted-printable */
732         while (lines[i]) {
733                 gint e_len = strlen(qpbody), n_len = 0;
734                 gchar *outline = conv_codeset_strdup(lines[i], CS_UTF_8, conv_get_outgoing_charset_str());
735                 gchar *qpoutline = g_malloc(strlen(outline)*8 + 1);
736                 
737                 qp_encode_line(qpoutline, (guchar *)outline);
738                 n_len = strlen(qpoutline);
739                 
740                 qpbody = g_realloc(qpbody, e_len + n_len + 1);
741                 strcpy(qpbody+e_len, qpoutline);
742                 *(qpbody+n_len+e_len) = '\0';
743                 
744                 g_free(outline);
745                 g_free(qpoutline);
746                 i++;
747         }
748         
749         body = g_strdup_printf("%s"
750                                "\n"
751                                "%s", headers, qpbody);
752         
753         if (str_write_to_file(body, tmpfile) < 0) {
754                 g_free(tmpfile);
755                 tmpfile = NULL;
756         } else
757                 chmod(tmpfile, S_IRUSR|S_IWUSR);
758
759         g_strfreev(lines);
760         g_free(body);
761         g_free(qpbody);
762         g_free(headers);
763         icalcomponent_free(calendar);
764         
765         return tmpfile; 
766 }
767
768 VCalEvent * vcal_manager_new_event      (const gchar    *uid, 
769                                          const gchar    *organizer,
770                                          const gchar    *orgname,
771                                          const gchar    *location,
772                                          const gchar    *summary,
773                                          const gchar    *description,
774                                          const gchar    *dtstart,
775                                          const gchar    *dtend,
776                                          const gchar    *recur,
777                                          const gchar    *tzid,
778                                          const gchar    *url,
779                                          enum icalproperty_method method,
780                                          gint            sequence,
781                                          enum icalcomponent_kind type)
782 {
783         VCalEvent *event = g_new0(VCalEvent, 1);
784
785         event->uid              = g_strdup(uid?uid:"");
786         event->organizer        = g_strdup(organizer?organizer:"");
787         event->orgname          = g_strdup(orgname?orgname:"");
788
789         if (dtend && *(dtend)) {
790                 time_t tmp = icaltime_as_timet((icaltime_from_string(dtend)));
791 #ifdef G_OS_WIN32
792                 GDateTime *dt = g_date_time_new_from_unix_local(tmp);
793                 event->end = g_date_time_format(dt, "%a, %e %b %Y %H:%M:%S %z");
794                 g_date_time_unref(dt);
795 #else
796                 gchar buft[512];
797                 tzset();
798                 event->end      = g_strdup(ctime_r(&tmp, buft));
799 #endif
800         }
801         
802         if (dtstart && *(dtstart)) {
803                 time_t tmp = icaltime_as_timet((icaltime_from_string(dtstart)));
804 #ifdef G_OS_WIN32
805                 GDateTime *dt = g_date_time_new_from_unix_local(tmp);
806                 event->start = g_date_time_format(dt, "%a, %e %b %Y %H:%M:%S %z");
807                 g_date_time_unref(dt);
808 #else
809                 gchar buft[512];
810                 tzset();
811                 event->start    = g_strdup(ctime_r(&tmp, buft));
812 #endif
813         }
814         event->dtstart          = g_strdup(dtstart?dtstart:"");
815         event->dtend            = g_strdup(dtend?dtend:"");
816         event->recur            = g_strdup(recur?recur:"");
817         event->location         = g_strdup(location?location:"");
818         event->summary          = g_strdup(summary?summary:"");
819         event->description      = g_strdup(description?description:"");
820         event->url              = g_strdup(url?url:"");
821         event->tzid             = g_strdup(tzid?tzid:"");
822         event->method           = method;
823         event->sequence         = sequence;
824         event->type             = type;
825         event->rec_occurence            = FALSE;
826         while (strchr(event->summary, '\n'))
827                 *(strchr(event->summary, '\n')) = ' ';
828
829         return event;
830 }
831                                          
832 void vcal_manager_free_event (VCalEvent *event)
833 {
834         GSList *cur;
835         if (!event)
836                 return;
837
838         g_free(event->uid);
839         g_free(event->organizer);
840         g_free(event->orgname);
841         g_free(event->start);
842         g_free(event->end);
843         g_free(event->location);
844         g_free(event->summary);
845         g_free(event->dtstart);
846         g_free(event->dtend);
847         g_free(event->recur);
848         g_free(event->tzid);
849         g_free(event->description);
850         g_free(event->url);
851         for (cur = event->answers; cur; cur = cur->next) {
852                 answer_free((Answer *)cur->data);
853         }
854         g_slist_free(event->answers);
855         g_free(event);
856 }
857
858 gchar *vcal_manager_get_event_path(void)
859 {
860         static gchar *event_path = NULL;
861         if (!event_path)
862                 event_path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
863                                         "vcalendar", NULL);
864         
865         return event_path;
866 }
867
868 gchar *vcal_manager_get_event_file(const gchar *uid)
869 {
870         gchar *tmp = g_strdup(uid);
871         gchar *res = NULL;
872
873         subst_for_filename(tmp);
874         res = g_strconcat(vcal_manager_get_event_path(), G_DIR_SEPARATOR_S,
875                            tmp, NULL);
876         g_free(tmp);
877         return res;
878 }
879
880 PrefsAccount *vcal_manager_get_account_from_event(VCalEvent *event)
881 {
882         GSList *list = vcal_manager_get_answers_emails(event);
883         GSList *cur = list;
884         
885         /* find an attendee corresponding to one of our accounts */
886         while (cur && cur->data) {
887                 gchar *email = (gchar *)cur->data;
888                 if (account_find_from_address(email, FALSE)) {
889                         g_slist_free(list);
890                         return account_find_from_address(email, FALSE);
891                 }
892                 cur = cur->next;
893         }
894         g_slist_free(list);
895         return NULL;
896 }
897
898 void vcal_manager_save_event (VCalEvent *event, gboolean export_after)
899 {
900         XMLTag *tag = NULL;
901         XMLNode *xmlnode = NULL;
902         GNode *rootnode = NULL;
903         PrefFile *pfile;
904         gchar *path = NULL;
905         GSList *answers = event->answers;
906         gchar *tmp = NULL;
907         gint tmp_method = event->method;
908         
909         tag = xml_tag_new("event");
910         xml_tag_add_attr(tag, xml_attr_new("organizer", event->organizer));
911         xml_tag_add_attr(tag, xml_attr_new("orgname", event->orgname));
912         xml_tag_add_attr(tag, xml_attr_new("location", event->location));
913         xml_tag_add_attr(tag, xml_attr_new("summary", event->summary));
914         xml_tag_add_attr(tag, xml_attr_new("description", event->description));
915         xml_tag_add_attr(tag, xml_attr_new("url", event->url));
916         xml_tag_add_attr(tag, xml_attr_new("dtstart", event->dtstart));
917         xml_tag_add_attr(tag, xml_attr_new("dtend", event->dtend));
918         xml_tag_add_attr(tag, xml_attr_new("recur", event->recur));
919         xml_tag_add_attr(tag, xml_attr_new("tzid", event->tzid));
920         
921         /* updating answers saves events, don't save them with reply type */
922         if (tmp_method == ICAL_METHOD_REPLY)
923                 tmp_method = ICAL_METHOD_REQUEST;
924         
925         tmp = g_strdup_printf("%d", tmp_method);
926         xml_tag_add_attr(tag, xml_attr_new("method", tmp));
927         g_free(tmp);
928
929         tmp = g_strdup_printf("%d", event->sequence);
930         xml_tag_add_attr(tag, xml_attr_new("sequence", tmp));
931         g_free(tmp);
932         
933         tmp = g_strdup_printf("%d", event->type);
934         xml_tag_add_attr(tag, xml_attr_new("type", tmp));
935         g_free(tmp);
936         
937         tmp = g_strdup_printf("%lld", (long long)event->postponed);
938         xml_tag_add_attr(tag, xml_attr_new("postponed", tmp));
939         g_free(tmp);
940         
941         tmp = g_strdup_printf("%d", event->rec_occurence);
942         xml_tag_add_attr(tag, xml_attr_new("rec_occurence", tmp));
943         g_free(tmp);
944         
945         xmlnode = xml_node_new(tag, NULL);
946         rootnode = g_node_new(xmlnode);
947         
948         while (answers && answers->data) {
949                 XMLNode *ansxmlnode = NULL;
950                 GNode *ansnode = NULL;
951                 XMLTag *anstag = xml_tag_new("answer");
952                 Answer *a = (Answer *)answers->data;
953                 xml_tag_add_attr(anstag, xml_attr_new("attendee", a->attendee));
954                 xml_tag_add_attr(anstag, xml_attr_new("name", a->name?a->name:""));
955                 tmp = g_strdup_printf("%d", a->answer);
956                 xml_tag_add_attr(anstag, xml_attr_new("answer", tmp));
957                 g_free(tmp);
958                 tmp = g_strdup_printf("%d", a->cutype);
959                 xml_tag_add_attr(anstag, xml_attr_new("cutype", tmp));
960                 g_free(tmp);
961                 ansxmlnode = xml_node_new(anstag, NULL);
962                 ansnode = g_node_new(ansxmlnode);
963                 g_node_append(rootnode, ansnode);
964                 answers = answers->next;
965         }
966         
967         path = vcal_manager_get_event_file(event->uid);
968                                         
969         if ((pfile = prefs_write_open(path)) == NULL) {
970                 gchar *dir_path = vcal_manager_get_event_path();
971                 if (!is_dir_exist(dir_path) && make_dir(vcal_manager_get_event_path()) != 0) {
972                         g_free(dir_path);
973                         g_free(path);
974                         return;
975                 }
976                 g_free(dir_path);
977                 if ((pfile = prefs_write_open(path)) == NULL) {
978                         g_free(path);
979                         return;
980                 }
981         }
982         
983         g_free(path);
984         xml_file_put_xml_decl(pfile->fp);
985         xml_write_tree(rootnode, pfile->fp);
986         xml_free_tree(rootnode);
987
988         if (prefs_file_close(pfile) < 0) {
989                 g_warning("failed to write event.");
990                 return;
991         }
992  
993         if (export_after)
994                 vcal_folder_export(NULL);
995 }
996
997 static VCalEvent *event_get_from_xml (const gchar *uid, GNode *node)
998 {
999         XMLNode *xmlnode;
1000         GList *list;
1001         gchar *org = NULL, *location = NULL, *summary = NULL, *orgname = NULL;
1002         gchar *dtstart = NULL, *dtend = NULL, *tzid = NULL;
1003         gchar *description = NULL, *url = NULL, *recur = NULL;
1004         VCalEvent *event = NULL;
1005         enum icalproperty_method method = ICAL_METHOD_REQUEST;
1006         enum icalcomponent_kind type = ICAL_VEVENT_COMPONENT;
1007         gint sequence = 0, rec_occurence = 0;
1008         time_t postponed = (time_t)0;
1009         
1010         g_return_val_if_fail(node->data != NULL, NULL);
1011
1012         xmlnode = node->data;
1013         if (strcmp2(xmlnode->tag->tag, "event") != 0) {
1014                 g_warning("tag name != \"event\"");
1015                 return NULL;
1016         }
1017         
1018         list = xmlnode->tag->attr;
1019         for (; list != NULL; list = list->next) {
1020                 XMLAttr *attr = list->data;
1021
1022                 if (!attr || !attr->name || !attr->value) continue;
1023                 if (!strcmp(attr->name, "organizer"))
1024                         org = g_strdup(attr->value);
1025                 if (!strcmp(attr->name, "orgname"))
1026                         orgname = g_strdup(attr->value);
1027                 if (!strcmp(attr->name, "location"))
1028                         location = g_strdup(attr->value);
1029                 if (!strcmp(attr->name, "summary"))
1030                         summary = g_strdup(attr->value);
1031                 if (!strcmp(attr->name, "description"))
1032                         description = g_strdup(attr->value);
1033                 if (!strcmp(attr->name, "url"))
1034                         url = g_strdup(attr->value);
1035                 if (!strcmp(attr->name, "dtstart"))
1036                         dtstart = g_strdup(attr->value);
1037                 if (!strcmp(attr->name, "dtend"))
1038                         dtend = g_strdup(attr->value);
1039                 if (!strcmp(attr->name, "recur"))
1040                         recur = g_strdup(attr->value);
1041                 if (!strcmp(attr->name, "tzid"))
1042                         tzid = g_strdup(attr->value);
1043                 if (!strcmp(attr->name, "type"))
1044                         type = atoi(attr->value);
1045                 if (!strcmp(attr->name, "method"))
1046                         method = atoi(attr->value);
1047                 if (!strcmp(attr->name, "sequence"))
1048                         sequence = atoi(attr->value);
1049                 if (!strcmp(attr->name, "postponed"))
1050                         postponed = atoi(attr->value);
1051                 if (!strcmp(attr->name, "rec_occurence"))
1052                         rec_occurence = atoi(attr->value);
1053         }
1054
1055         event = vcal_manager_new_event(uid, org, orgname, location, summary, description, 
1056                                         dtstart, dtend, recur, tzid, url, method, 
1057                                         sequence, type);
1058
1059         event->postponed = postponed;
1060         event->rec_occurence = rec_occurence;
1061
1062         g_free(org); 
1063         g_free(orgname); 
1064         g_free(location);
1065         g_free(summary); 
1066         g_free(description); 
1067         g_free(url); 
1068         g_free(dtstart); 
1069         g_free(dtend);
1070         g_free(recur);
1071         g_free(tzid);
1072
1073         node = node->children;
1074         while (node != NULL) {
1075                 gchar *attendee = NULL;
1076                 gchar *name = NULL;
1077                 enum icalparameter_partstat answer = ICAL_PARTSTAT_NEEDSACTION;
1078                 enum icalparameter_cutype cutype   = ICAL_CUTYPE_INDIVIDUAL;
1079                 
1080                 xmlnode = node->data;
1081                 if (strcmp2(xmlnode->tag->tag, "answer") != 0) {
1082                         g_warning("tag name != \"answer\"");
1083                         return event;
1084                 }
1085                 list = xmlnode->tag->attr;
1086                 for (; list != NULL; list = list->next) {
1087                         XMLAttr *attr = list->data;
1088
1089                         if (!attr || !attr->name || !attr->value) continue;
1090                         if (!strcmp(attr->name, "attendee"))
1091                                 attendee = g_strdup(attr->value);
1092                         if (!strcmp(attr->name, "name"))
1093                                 name = g_strdup(attr->value);
1094                         if (!strcmp(attr->name, "answer"))
1095                                 answer = atoi(attr->value);
1096                         if (!strcmp(attr->name, "cutype"))
1097                                 cutype = atoi(attr->value);
1098                 }
1099
1100                 event->answers = g_slist_prepend(event->answers, answer_new(attendee, name, answer, cutype));
1101                 g_free(attendee);
1102                 g_free(name);
1103                 node = node->next;
1104         }
1105         event->answers = g_slist_reverse(event->answers);
1106         
1107         return event;
1108 }
1109
1110 VCalEvent *vcal_manager_load_event (const gchar *uid)
1111 {
1112         GNode *node;
1113         gchar *path = NULL;
1114         VCalEvent *event = NULL;
1115
1116         path = vcal_manager_get_event_file(uid);
1117         
1118         if (!is_file_exist(path)) {
1119                 g_free(path);
1120                 return NULL;
1121         }
1122         
1123         node = xml_parse_file(path);
1124         
1125         g_free(path);
1126         
1127         if (!node) {
1128                 g_warning("no node");
1129                 return NULL;
1130         }
1131         
1132         event = event_get_from_xml(uid, node);
1133         /* vcal_manager_event_print(event); */
1134         xml_free_tree(node);
1135
1136         if (!event)
1137                 return NULL;
1138
1139         while (strchr(event->summary, '\n'))
1140                 *(strchr(event->summary, '\n')) = ' ';
1141
1142         return event;
1143         
1144 }
1145
1146 void vcal_manager_update_answer (VCalEvent      *event, 
1147                                  const gchar    *attendee,
1148                                  const gchar    *name,
1149                                  enum icalparameter_partstat ans,
1150                                  enum icalparameter_cutype cutype)
1151 {
1152         Answer *answer = NULL;
1153         GSList *existing = NULL;
1154         Answer *existing_a = NULL;
1155         
1156         if (!ans)
1157                 return;
1158                 
1159         answer = answer_new(attendee, name, ans, cutype);
1160         existing = answer_find(event, answer);
1161                 
1162         if (existing) {
1163                 existing_a = (Answer *)existing->data;
1164         
1165                 if (!answer->name && existing_a->name)
1166                         answer->name = g_strdup(existing_a->name);
1167                 if (!answer->cutype && existing_a->cutype)
1168                         answer->cutype = existing_a->cutype;
1169
1170                 answer_remove(event, answer);
1171         }
1172
1173         answer_add(event, answer);
1174         
1175         vcal_manager_save_event(event, FALSE);
1176 }
1177
1178 static gchar *write_headers(PrefsAccount        *account, 
1179                             VCalEvent           *event,
1180                             gboolean             short_headers,
1181                             gboolean             is_reply, 
1182                             gboolean             is_pseudo_display)
1183 {
1184         gchar *subject = NULL;
1185         gchar date[128];
1186         gchar *save_folder = NULL;
1187         gchar *result = NULL;
1188         gchar *queue_headers = NULL;
1189         gchar *method_str = NULL;
1190         gchar *attendees = NULL;
1191         enum icalparameter_partstat status;
1192         gchar *prefix = NULL;
1193         gchar enc_subject[512], enc_from[512], *from = NULL;
1194         gchar msgid[128];       
1195         gchar *calmsgid = NULL;
1196
1197         cm_return_val_if_fail(account != NULL, NULL);
1198
1199         memset(date, 0, sizeof(date));
1200         
1201         if (is_pseudo_display) {
1202                 struct icaltimetype itt = (icaltime_from_string(event->dtstart));
1203                 time_t t = icaltime_as_timet(itt);
1204                 get_rfc822_date_from_time_t(date, sizeof(date), t);
1205         } else {
1206                 get_rfc822_date(date, sizeof(date));
1207         }
1208         
1209         if (account_get_special_folder(account, F_OUTBOX)) {
1210                 save_folder = folder_item_get_identifier(account_get_special_folder
1211                                   (account, F_OUTBOX));
1212         }
1213         
1214         if (!is_reply) {
1215                 GSList *cur = event->answers;
1216                 while (cur && cur->data) {
1217                         gchar *tmp = NULL;
1218                         Answer *a = (Answer *)cur->data;
1219                         
1220                         if (strcasecmp(a->attendee, event->organizer)) {
1221                                 if (attendees) {
1222                                         tmp = g_strdup_printf("%s>,\n <%s", attendees, a->attendee);
1223                                         g_free(attendees);
1224                                         attendees = tmp;
1225                                 } else {
1226                                         attendees = g_strdup_printf("%s", a->attendee);
1227                                 }
1228                         }
1229                         cur = cur->next;
1230                 }
1231         }
1232         
1233         if (!short_headers) {
1234                 queue_headers = g_strdup_printf("S:%s\n"
1235                                 "SSV:%s\n"
1236                                 "R:<%s>\n"
1237                                 "MAID:%d\n"
1238                                 "%s%s%s"
1239                                 "X-Claws-End-Special-Headers: 1\n",
1240                                 account->address,
1241                                 account->smtp_server,
1242                                 is_reply ? event->organizer:attendees,
1243                                 account->account_id,
1244                                 save_folder?"SCF:":"",
1245                                 save_folder?save_folder:"",
1246                                 save_folder?"\n":"");
1247         } else {
1248                 queue_headers = g_strdup("");
1249         }
1250         
1251         prefix = "";
1252         if (is_reply) {
1253                 method_str = "REPLY";
1254                 status = vcal_manager_get_reply_for_attendee(event, account->address);
1255                 if (status == ICAL_PARTSTAT_ACCEPTED)
1256                         prefix = _("Accepted: ");
1257                 else if (status == ICAL_PARTSTAT_DECLINED)
1258                         prefix = _("Declined: ");
1259                 else if (status == ICAL_PARTSTAT_TENTATIVE)
1260                         prefix = _("Tentatively Accepted: ");
1261                 else 
1262                         prefix = "Re: "; 
1263         } else if (event->method == ICAL_METHOD_PUBLISH) {
1264                 method_str = "PUBLISH";
1265         } else if (event->method == ICAL_METHOD_CANCEL) {
1266                 method_str = "CANCEL";
1267         } else {
1268                 method_str = "REQUEST";
1269         }
1270         
1271         subject = g_strdup_printf("%s%s", prefix, event->summary);
1272
1273         conv_encode_header_full(enc_subject, sizeof(enc_subject), subject, strlen("Subject: "), 
1274                         FALSE, conv_get_outgoing_charset_str());
1275         from = is_reply?account->name:(event->orgname?event->orgname:"");
1276         conv_encode_header_full(enc_from, sizeof(enc_from), from, strlen("From: "), 
1277                         TRUE, conv_get_outgoing_charset_str());
1278
1279         if (is_pseudo_display && event->uid) {
1280                 calmsgid = g_strdup_printf("Message-ID: <%s>\n",event->uid);
1281         } else {
1282                 calmsgid = g_strdup("");
1283         }
1284
1285         if (account && account->set_domain && account->domain) {
1286                 g_snprintf(msgid, sizeof(msgid), "%s", account->domain); 
1287         } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
1288                 g_snprintf(msgid, sizeof(msgid), "%s", 
1289                         strchr(account->address, '@') ?
1290                                 strchr(account->address, '@')+1 :
1291                                 account->address);
1292         } else {
1293                 g_snprintf(msgid, sizeof(msgid), "%s", "");
1294         }
1295
1296         generate_msgid(msgid, sizeof(msgid), account->address);
1297
1298         result = g_strdup_printf("%s"
1299                                 "From: %s <%s>\n"
1300                                 "To: <%s>\n"
1301                                 "Subject: %s\n"
1302                                 "Date: %s\n"
1303                                 "MIME-Version: 1.0\n"
1304                                 "Content-Type: text/calendar; method=%s; charset=\"%s\"\n"
1305                                 "Content-Transfer-Encoding: 8bit\n"
1306                                 "%s"
1307                                 "%s: <%s>\n",
1308                                 queue_headers,
1309                                 enc_from,
1310                                 is_reply ? account->address:event->organizer,
1311                                 is_reply ? event->organizer:(attendees?attendees:event->organizer),
1312                                 enc_subject,
1313                                 date,
1314                                 method_str,
1315                                 CS_UTF_8,
1316                                 calmsgid,
1317                                 is_pseudo_display?
1318                                         "In-Reply-To":"Message-ID",
1319                                 is_pseudo_display?
1320                                         event_to_today_str(event, 0):msgid);
1321         g_free(calmsgid);
1322         g_free(subject);
1323         g_free(save_folder);
1324         g_free(queue_headers);
1325         g_free(attendees);
1326         return result;                  
1327                                                                                
1328
1329 }
1330
1331 static gchar *write_headers_ical(PrefsAccount   *account, 
1332                             icalcomponent       *ievent,
1333                             gchar               *orga)
1334 {
1335         gchar subject[512];
1336         gchar date[128];
1337         gchar *result = NULL;
1338         gchar *method_str = NULL;
1339         gchar *summary = NULL;
1340         gchar *organizer = NULL;
1341         gchar *orgname = NULL;
1342         icalproperty *prop = NULL;
1343         gchar *calmsgid = NULL;
1344
1345         time_t t = (time_t)0;
1346
1347         memset(subject, 0, sizeof(subject));
1348         memset(date, 0, sizeof(date));
1349         
1350         prop = icalcomponent_get_first_property(ievent, ICAL_SUMMARY_PROPERTY);
1351         if (prop) {
1352                 summary = g_strdup(icalproperty_get_summary(prop));
1353                 icalproperty_free(prop);
1354         } else {
1355                 summary = g_strdup("");
1356         }
1357         
1358         while (strchr(summary, '\n'))
1359                 *(strchr(summary, '\n')) = ' ';
1360
1361         prop = icalcomponent_get_first_property(ievent, ICAL_ORGANIZER_PROPERTY);
1362         if (prop) {
1363                 organizer = g_strdup(icalproperty_get_organizer(prop));
1364                 if (icalproperty_get_parameter_as_string(prop, "CN") != NULL)
1365                         orgname = g_strdup(icalproperty_get_parameter_as_string(prop, "CN"));
1366
1367                 icalproperty_free(prop);
1368         } else {
1369                 organizer = orga? g_strdup(orga):g_strdup("");
1370         }
1371
1372         prop = icalcomponent_get_first_property(ievent, ICAL_DTSTART_PROPERTY);
1373         if (prop) {
1374                 t = icaltime_as_timet(icalproperty_get_dtstart(prop));
1375                 get_rfc822_date_from_time_t(date, sizeof(date), t);
1376         } else {
1377                 get_rfc822_date(date, sizeof(date));
1378         }
1379
1380         conv_encode_header(subject, 511, summary, strlen("Subject: "), FALSE);
1381                         
1382         method_str = "PUBLISH";
1383
1384         prop = icalcomponent_get_first_property(ievent, ICAL_UID_PROPERTY);
1385         if (prop) {
1386                 calmsgid = g_strdup_printf("Message-ID: <%s>\n",icalproperty_get_uid(prop));
1387                 icalproperty_free(prop);
1388         } else {
1389                 calmsgid = g_strdup("");
1390         }
1391         
1392
1393         result = g_strdup_printf("From: %s <%s>\n"
1394                                 "To: <%s>\n"
1395                                 "Subject: %s%s\n"
1396                                 "Date: %s\n"
1397                                 "MIME-Version: 1.0\n"
1398                                 "Content-Type: text/calendar; method=%s; charset=\"%s\"; vcalsave=\"no\"\n"
1399                                 "Content-Transfer-Encoding: quoted-printable\n"
1400                                 "%s"
1401                                 "In-Reply-To: <%s>\n",
1402                                 orgname?orgname:"",
1403                                 !strncmp(organizer, "MAILTO:", 7) ? organizer+7 : organizer,
1404                                 account->address,
1405                                 "",
1406                                 subject,
1407                                 date,
1408                                 method_str,
1409                                 conv_get_outgoing_charset_str(),
1410                                 calmsgid,
1411                                 event_to_today_str(NULL, t));
1412         
1413         g_free(calmsgid);
1414         g_free(orgname);
1415         g_free(organizer);
1416         g_free(summary);
1417
1418         return result;                  
1419                                                                                
1420
1421 }
1422
1423 static gboolean vcal_manager_send (PrefsAccount         *account, 
1424                                      VCalEvent          *event,
1425                                      gboolean            is_reply)
1426 {
1427         gchar *tmpfile = NULL;
1428         gint msgnum;
1429         FolderItem *folderitem;
1430         gchar *msgpath = NULL;
1431         Folder *folder = NULL;
1432         
1433         tmpfile = vcal_manager_event_dump(event, is_reply, FALSE, NULL, TRUE);
1434
1435         if (!tmpfile)
1436                 return FALSE;
1437
1438         folderitem = account_get_special_folder(account, F_QUEUE);
1439         if (!folderitem) {
1440                 g_warning("can't find queue folder for %s", account->address);
1441                 g_unlink(tmpfile);
1442                 g_free(tmpfile);
1443                 return FALSE;
1444         }
1445         folder_item_scan(folderitem);
1446         
1447         if ((msgnum = folder_item_add_msg(folderitem, tmpfile, NULL, TRUE)) < 0) {
1448                 g_warning("can't queue the message");
1449                 g_unlink(tmpfile);
1450                 g_free(tmpfile);
1451                 return FALSE;
1452         }
1453
1454         msgpath = folder_item_fetch_msg(folderitem, msgnum);
1455         
1456         if (!prefs_common_get_prefs()->work_offline) {
1457                 gchar *err = NULL;
1458                 gboolean queued_removed = FALSE;
1459                 gint val = procmsg_send_message_queue_with_lock(msgpath, &err, folderitem, msgnum, &queued_removed);
1460                 if (val == 0) {
1461                         if (!queued_removed)
1462                                 folder_item_remove_msg(folderitem, msgnum);
1463                         folder_item_scan(folderitem);
1464                 } else if (err) {
1465                         alertpanel_error_log("%s", err);
1466                         g_free(err);
1467                 }
1468         }
1469         g_unlink(tmpfile);
1470         g_free(tmpfile);
1471         g_free(msgpath);
1472
1473         folder = folder_find_from_name ("vCalendar", vcal_folder_get_class());
1474         if (folder)
1475                 folder_item_scan(folder->inbox);
1476
1477         vcalviewer_reload(folder->inbox);
1478         return TRUE;
1479 }
1480
1481 gboolean vcal_manager_reply (PrefsAccount       *account, 
1482                              VCalEvent          *event)
1483 {
1484         return vcal_manager_send(account, event, TRUE);
1485 }
1486
1487 gboolean vcal_manager_request (PrefsAccount     *account, 
1488                                VCalEvent        *event)
1489 {
1490         return vcal_manager_send(account, event, FALSE);
1491 }
1492
1493 EventTime event_to_today(VCalEvent *event, time_t t)
1494 {
1495         struct tm evtstart, today;
1496         time_t evtstart_t, today_t;
1497         struct icaltimetype itt;
1498
1499         tzset();
1500         
1501         today_t = time(NULL);
1502         if (event) {
1503                 itt = icaltime_from_string(event->dtstart);
1504                 evtstart_t = icaltime_as_timet(itt);
1505         } else {
1506                 evtstart_t = t;
1507         }
1508         
1509 #ifndef G_OS_WIN32
1510         struct tm buft;
1511         today = *localtime_r(&today_t, &buft);
1512         localtime_r(&evtstart_t, &evtstart);
1513 #else
1514         if (today_t < 0)
1515                 today_t = 1;
1516         if (evtstart_t < 0)
1517                 evtstart_t = 1;
1518         today = *localtime(&today_t);
1519         evtstart = *localtime(&evtstart_t);
1520 #endif
1521         
1522         if (today.tm_year == evtstart.tm_year) {
1523                 int days = evtstart.tm_yday - today.tm_yday;
1524                 if (days < 0) {
1525                         return EVENT_PAST;
1526                 } else if (days == 0) {
1527                         return EVENT_TODAY;
1528                 } else if (days == 1) {
1529                         return EVENT_TOMORROW;
1530                 } else if (days > 1 && days < 7) {
1531                         return EVENT_THISWEEK;
1532                 } else {
1533                         return EVENT_LATER;
1534                 }
1535         } else if (today.tm_year > evtstart.tm_year) {
1536                 return EVENT_PAST;
1537         } else if (today.tm_year == evtstart.tm_year - 1) {
1538                 int days = ((365 - today.tm_yday) + evtstart.tm_yday);
1539                 if (days == 0) {
1540                         return EVENT_TODAY;
1541                 } else if (days == 1) {
1542                         return EVENT_TOMORROW;
1543                 } else if (days > 1 && days < 7) {
1544                         return EVENT_THISWEEK;
1545                 } else {
1546                         return EVENT_LATER;
1547                 }
1548         } else 
1549                 return EVENT_LATER;
1550 }
1551
1552 const gchar *event_to_today_str(VCalEvent *event, time_t t)
1553 {
1554         EventTime days = event_to_today(event, t);
1555         switch(days) {
1556         case EVENT_PAST:
1557                 return EVENT_PAST_ID;
1558         case EVENT_TODAY:
1559                 return EVENT_TODAY_ID;
1560         case EVENT_TOMORROW:
1561                 return EVENT_TOMORROW_ID;
1562         case EVENT_THISWEEK:
1563                 return EVENT_THISWEEK_ID;
1564         case EVENT_LATER:
1565                 return EVENT_LATER_ID;
1566         }
1567         return NULL;
1568 }