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