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