]> git.datanom.net - caldav.git/blame - src/caldav.c
First complete version
[caldav.git] / src / caldav.c
CommitLineData
e1b22e2b
MR
1/*
2 * caldav.c
3 *
4 * Copyright 2016 Michael Rasmussen <mir@datanom.net>
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 2 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., 51 Franklin Street, Fifth Floor, Boston,
19 * MA 02110-1301, USA.
20 */
21
22#ifdef HAVE_CONFIG_H
23# include "config.h"
24#endif
25
26#include <stdio.h>
27#include <unistd.h>
28#include "caldav.h"
29#include "curllib.h"
30#include "xml.h"
31
32static gchar* get_timerange(Runtime* runtime) {
33 gchar* timerange = NULL;
34
35 if (runtime->start && runtime->finish) {
36 GDateTime* s_utc = g_date_time_to_utc(runtime->start);
37 GDateTime* f_utc = g_date_time_to_utc(runtime->finish);
38 gchar* start = g_date_time_format(s_utc, "%Y%m%dT%H%M%SZ");
39 gchar* finish = g_date_time_format(f_utc, "%Y%m%dT%H%M%SZ");
40 g_date_time_unref(s_utc);
41 g_date_time_unref(f_utc);
42
43 timerange = g_strconcat(
44 "<c:time-range start=\"", start, "\"",
45 " end=\"", finish, "\"/>",
46 NULL);
47 g_free(start);
48 g_free(finish);
49 }
50
51 return timerange;
52}
53
54/*
55 * Operation Depth RequestType
56 * CALENDARINFO 0 PROPFIND
57 * GETOBJECTS 1 REPORT
58 * CHANGEINFO 1 REPORT
59 * UPDATEOBJECTS - PUT
60 * ADDOBJECTS - PUT
61 * DELETEOBJECTS - DELETE
62 * DISCOVER 0 PROPFIND
63 * OPTIONSINFO 0 OPTIONS
64 * LOCKING 0
65*/
66static Request* request_init(Runtime* r) {
67 Request* rq = request_new();
68 GString* href = g_string_new("");
69 gchar* timerange = NULL;
70
71 switch (*r->operation) {
72 case CALENDARINFO:
73 rq->request = PROPFIND;
74 rq->headers = g_slist_append(rq->headers, g_strdup("Depth: 0"));
75 rq->headers = g_slist_append(rq->headers, g_strdup("Prefer: return-minimal"));
76 rq->data = g_strconcat(
77 "<?xml version=\"1.0\" encoding=\"utf-8\" ?>",
78 "<d:propfind xmlns:d=\"DAV:\" xmlns:cs=\"http://calendarserver.org/ns/\">",
79 "<d:prop><d:displayname /><cs:getctag /></d:prop></d:propfind>",
80 NULL);
81 break;
82 case GETOBJECTS:
83 rq->request = REPORT;
84 rq->headers = g_slist_append(rq->headers, g_strdup("Depth: 1"));
85 rq->headers = g_slist_append(rq->headers, g_strdup("Prefer: return-minimal"));
86 if (r->hrefs) {
87 for (GSList* l = r->hrefs; l; l = g_slist_next(l)) {
88 HrefData* h = (HrefData *) l->data;
89 gchar* elem = g_strconcat("<d:href>", h->href, "</d:href>", NULL);
90 href = g_string_append(href, elem);
91 g_free(elem);
92 }
93 }
94 rq->data = g_strconcat(
95 "<?xml version=\"1.0\" encoding=\"utf-8\" ?>",
96 "<c:calendar-multiget xmlns:d=\"DAV:\" xmlns:c=\"urn:ietf:params:xml:ns:caldav\">",
97 "<d:prop><d:getetag /><c:calendar-data /></d:prop>",
98 g_string_free(href, FALSE), "</c:calendar-multiget>",
99 NULL);
100 break;
101 case GETALLOBJECTS:
102 rq->request = REPORT;
103 rq->headers = g_slist_append(rq->headers, g_strdup("Depth: 1"));
104 rq->headers = g_slist_append(rq->headers, g_strdup("Prefer: return-minimal"));
105 timerange = get_timerange(r);
106 rq->data = g_strconcat(
107 "<?xml version=\"1.0\" encoding=\"utf-8\" ?>",
108 "<c:calendar-query xmlns:d=\"DAV:\" xmlns:c=\"urn:ietf:params:xml:ns:caldav\">",
109 "<d:prop><d:getetag /><c:calendar-data /></d:prop>",
110 "<c:filter><c:comp-filter name=\"VCALENDAR\">",
111 "<c:comp-filter name=\"VEVENT\">",timerange ? timerange : "",
112 "</c:comp-filter></c:comp-filter>",
113 "</c:filter></c:calendar-query>",
114 NULL);
115 if (timerange)
116 g_free(timerange);
117 break;
118 case CHANGEINFO:
119 rq->request = REPORT;
120 rq->headers = g_slist_append(rq->headers, g_strdup("Depth: 1"));
121 rq->headers = g_slist_append(rq->headers, g_strdup("Prefer: return-minimal"));
122 timerange = get_timerange(r);
123 rq->data = g_strconcat(
124 "<?xml version=\"1.0\" encoding=\"utf-8\" ?>",
125 "<c:calendar-query xmlns:d=\"DAV:\" xmlns:c=\"urn:ietf:params:xml:ns:caldav\">",
126 "<d:prop><d:getetag /></d:prop><c:filter><c:comp-filter name=\"VCALENDAR\">",
127 "<c:comp-filter name=\"VEVENT\">",timerange ? timerange : "",
128 "</c:comp-filter></c:comp-filter>",
129 "</c:filter></c:calendar-query>",
130 NULL);
131 if (timerange)
132 g_free(timerange);
133 break;
134 case UPDATEOBJECTS:
135 rq->request = PUT;
136 rq->headers = g_slist_append(rq->headers, g_strconcat("If-Match: \"", r->etag ? r->etag : "", "\"", NULL));
137 rq->data = g_strdup(r->component ? r->component : "");
138 break;
139 case ADDOBJECTS:
140 rq->request = PUT;
141 rq->headers = g_slist_append(rq->headers, g_strdup("If-None-Match: *"));
142 rq->data = g_strdup(r->component ? r->component : "");
143 break;
144 case DELETEOBJECTS:
145 rq->request = DELETE;
146 rq->headers = g_slist_append(rq->headers, g_strconcat("If-Match: \"", r->etag ? r->etag : "", "\"", NULL));
147 break;
148 case DISCOVER:
149 rq->request = PROPFIND;
150 rq->headers = g_slist_append(rq->headers, g_strdup("Depth: 0"));
151 rq->headers = g_slist_append(rq->headers, g_strdup("Prefer: return-minimal"));
152 break;
153 case SIMPLEGET:
154 rq->request = GET;
155 rq->headers = g_slist_append(rq->headers, g_strdup("Depth: 0"));
156 rq->headers = g_slist_append(rq->headers, g_strdup("Prefer: return-minimal"));
157 break;
158 case OPTIONSINFO:
159 rq->request = OPTIONS;
160 rq->headers = g_slist_append(rq->headers, g_strdup("Depth: 0"));
161 rq->headers = g_slist_append(rq->headers, g_strdup("Prefer: return-minimal"));
162 break;
163 case LOCKING:
164 rq->request = LOCK;
165 rq->headers = g_slist_append(rq->headers, g_strdup("Depth: 0"));
166 rq->headers = g_slist_append(rq->headers, g_strdup("Prefer: return-minimal"));
167 rq->headers = g_slist_append(rq->headers, g_strdup("Timeout: Second-60"));
168 rq->data = g_strconcat(
169 "<?xml version=\"1.0\" encoding=\"utf-8\" ?>",
170 "<D:lockinfo xmlns:D=\"DAV:\"><D:lockscope><D:exclusive/></D:lockscope>",
171 " <D:locktype><D:write/></D:locktype></D:lockinfo>",
172 NULL);
173 break;
174 case UNLOCKING:
175 rq->request = UNLOCK;
176 rq->headers = g_slist_append(rq->headers, g_strdup("Depth: 0"));
177 rq->headers = g_slist_append(rq->headers, g_strdup("Prefer: return-minimal"));
178 break;
179 case FREEBUSY:
180 rq->request = REPORT;
181 rq->headers = g_slist_append(rq->headers, g_strdup("Depth: 1"));
182 rq->headers = g_slist_append(rq->headers, g_strdup("Prefer: return-minimal"));
183 timerange = get_timerange(r);
184 rq->data = g_strconcat(
185 "<?xml version=\"1.0\" encoding=\"utf-8\" ?>",
186 "<c:free-busy-query xmlns:D=\"DAV:\"",
187 " xmlns:c=\"urn:ietf:params:xml:ns:caldav\">",
188 timerange ? timerange : "", "</c:free-busy-query>",
189 NULL);
190 if (timerange)
191 g_free(timerange);
192 break;
193 default:
194 request_free(rq);
195 rq = NULL;
196 }
197
198 if (rq) {
199 rq->headers = g_slist_append(rq->headers, g_strdup("Content-Type: application/xml; charset=utf-8"));
200 rq->username = g_strdup(r->username);
201 rq->password = g_strdup(r->password);
202 rq->url = g_strdup(r->url);
203 }
204
205 return rq;
206}
207
208static gchar* get_host_part(const gchar* url) {
209 gchar* host_part = NULL;
210
211 gchar* s = g_strstr_len(url, -1, "://");
212 if (!s) {
213 s = (gchar *) url;
214 } else {
215 s += 3;
216 }
217
218 gchar* s1 = g_strstr_len(s, -1, "/");
219
220 if (s1) {
221 host_part = g_strndup(url, s1 - url);
222 } else {
223 host_part = g_strdup(url);
224 }
225
226 return host_part;
227}
228
229static GSList* gchar_list_copy(GSList* list) {
230 GSList* headers = NULL;
231 for (GSList* l = list; l; l = g_slist_next(l)) {
232 gchar* h = g_strdup((gchar *) l->data);
233 headers = g_slist_append(headers, h);
234 }
235
236 return headers;
237}
238
239static void debug_request(Request* rq) {
240 printf("HTTP Headers:\n");
241 for (GSList* l = rq->headers; l; l = g_slist_next(l)) {
242 printf("%s\n", (gchar *) l->data);
243 }
244 switch (rq->request) {
245 case DELETE: printf("DELETE "); break;
246 case PROPFIND: printf("PROPFIND "); break;
247 case PUT: printf("PUT "); break;
248 case REPORT: printf("REPORT "); break;
249 case GET: printf("GET "); break;
250 case OPTIONS: printf("OPTIONS "); break;
251 case LOCK: printf("LOCK "); break;
252 case UNLOCK: printf("UNLOCK "); break;
253 }
254 printf("%s\n", rq->url);
255 printf("DATA:\n%s\n", rq->data ? rq->data : "None");
256}
257
258static void debug_response(Response* r) {
259 printf("HTTP Status: %i\nHTTP headers:\n", r->status);
260 for (GSList* l = r->headers; l; l = g_slist_next(l)) {
261 printf("%s\n", (gchar *) l->data);
262 }
263}
264
265static guint execute_options(gpointer ptr);
266static void caldav_response_free(Operation op, CaldavResponse* cr);
267
268static gchar* execute_locking(Runtime* runtime, const gchar* lock_token, guint* status) {
269 gchar* token = NULL;
270 Request* rq = NULL;
271 Response* r = NULL;
272
273 Runtime* tmp = runtime_copy(runtime);
274 if (!tmp->options) {
275 *tmp->operation = OPTIONSINFO;
276 guint status = execute_options((gpointer) tmp);
277 if (status != 200)
278 return NULL;
279 caldav_response_free(*tmp->operation, tmp->output);
280 runtime->options = slist_copy_gchar(tmp->options);
281 }
282
283 if (tmp->options && has_option(tmp, "lock")) {
284 if (lock_token) {
285 *tmp->operation = UNLOCKING;
286 rq = request_init(tmp);
287 rq->headers = g_slist_append(rq->headers,
288 g_strconcat("Lock-Token: ", lock_token, NULL));
289 if (rq) {
290 if (runtime->debug) {
291 debug_request(rq);
292 }
293 r = request_send(rq, runtime->debug);
294
295 if (runtime->debug) {
296 debug_response(r);
297 }
298 }
299 } else {
300 *tmp->operation = LOCKING;
301 rq = request_init(tmp);
302 if (rq) {
303 if (runtime->debug) {
304 debug_request(rq);
305 }
306 r = request_send(rq, runtime->debug);
307
308 if (runtime->debug) {
309 debug_response(r);
310 }
311
312 if (r->status != 423)
313 token = find_header(r->headers, "Lock-Token");
314 else
315 token = g_strdup(r->data);
316 }
317 }
318 }
319
320 if (status)
321 *status = r->status;
322
323 response_free(r);
324 request_free(rq);
325 runtime_free(tmp);
326
327 return token;
328}
329
330static guint execute_default(gpointer ptr) {
331 Runtime* runtime = (Runtime *) ptr;
332 Request* rq = request_init(runtime);
333 guint status = 500;
334 gchar* lock_token = NULL;
335
336 if (rq) {
337 if (runtime->debug) {
338 debug_request(rq);
339 }
340
341 if (*runtime->operation == DELETEOBJECTS) {
342 lock_token = execute_locking(runtime, NULL, &status);
343 if (status == 423) {
344 runtime->output = g_new0(CaldavResponse, 1);
345 runtime->output->data = (gpointer) g_strdup(lock_token);
346 runtime->output->status = status;
347 g_free(lock_token);
348 request_free(rq);
349 return status;
350 }
351 }
352 if (lock_token) {
353 rq->headers = g_slist_append(rq->headers,
354 g_strconcat("Lock-Token: ", lock_token, NULL));
355 }
356 Response* r = request_send(rq, runtime->debug);
357 if (lock_token) {
358 execute_locking(runtime, lock_token, &status);
359 g_free(lock_token);
360 lock_token = NULL;
361 }
362 request_free(rq);
363
364 if (runtime->debug) {
365 debug_response(r);
366 }
367
368 runtime->output = g_new0(CaldavResponse, 1);
369 runtime->output->data = (gpointer) g_strdup(r->data);
370
371 if (runtime->file)
372 fprintf(runtime->file, r->data);
373
374 runtime->output->status = status = r->status;
375
376 runtime->output->headers = gchar_list_copy(r->headers);
377
378 response_free(r);
379 }
380
381 return status;
382}
383
384static guint execute_options(gpointer ptr) {
385 Runtime* runtime = (Runtime *) ptr;
386 Request* rq = request_init(runtime);
387 guint status = 500;
388
389 if (rq) {
390 if (runtime->debug) {
391 debug_request(rq);
392 }
393 Response* r = request_send(rq, runtime->debug);
394 request_free(rq);
395
396 if (runtime->debug) {
397 debug_response(r);
398 }
399
400 gchar* headers = find_header(r->headers, "Allow");
401 if (headers) {
402 gchar** options = g_strsplit(headers, ",", 0);
403 for (int i = 0; options[i]; i++) {
404 gchar* tmp = g_strdup(options[i]);
405 tmp = g_strstrip(tmp);
406 runtime->options = g_slist_append(runtime->options, g_strdup(tmp));
407 g_free(tmp);
408 }
409 g_strfreev(options);
410 g_free(headers);
411 }
412
413 if (runtime->file) {
414 for (GSList* l = runtime->options; l; l = g_slist_next(l)) {
415 fprintf(runtime->file, "%s\n", (gchar *) l->data);
416 }
417 }
418
419 runtime->output = g_new0(CaldavResponse, 1);
420 runtime->output->status = status = r->status;
421
422 runtime->output->headers = gchar_list_copy(r->headers);
423
424 response_free(r);
425 }
426
427 return status;
428}
429
430static guint execute_freebusy(gpointer ptr) {
431 Runtime* runtime = (Runtime *) ptr;
432
433 Request* rq = request_init(runtime);
434 guint status = 500;
435
436 if (rq) {
437 if (runtime->debug) {
438 debug_request(rq);
439 }
440 Response* r = request_send(rq, runtime->debug);
441 request_free(rq);
442
443 if (runtime->debug) {
444 debug_response(r);
445 }
446
447 runtime->output = g_new0(CaldavResponse, 1);
448 runtime->output->data = (gpointer) g_strdup(r->data);
449
450 if (runtime->file)
451 fprintf(runtime->file, r->data);
452
453 runtime->output->status = status = r->status;
454
455 runtime->output->headers = gchar_list_copy(r->headers);
456
457 response_free(r);
458 }
459
460 return status;
461}
462
463static guint execute_discover(gpointer ptr) {
464 Runtime* runtime = (Runtime *) ptr;
465 Response* r = NULL;
466 Request* rq = NULL;
467 GSList* list = NULL;
468 guint status = 500;
469
470 rq = request_init(runtime);
471 if (rq) {
472 rq->data = g_strconcat(
473 "<?xml version=\"1.0\" encoding=\"utf-8\" ?>",
474 "<d:propfind xmlns:d=\"DAV:\">",
475 "<d:prop><d:current-user-principal /></d:prop></d:propfind>",
476 NULL);
477 if (runtime->debug) {
478 debug_request(rq);
479 }
480 r = request_send(rq, runtime->debug);
481
482 if (runtime->debug) {
483 debug_response(r);
484 }
485 list = find_element("//d:current-user-principal/*", r->data, "d", "DAV:");
486 response_free(r);
487 if (list) {
488 /* Change URL to where "current-user-principal" is located */
489 gchar* s = get_host_part(runtime->url);
490 g_free(runtime->url);
491 runtime->url = g_strconcat(s, list->data, NULL);
492 g_free(s);
493 slist_free_gchar(list);
494 list = NULL;
495 }
496 g_free(rq->data);
497 rq->data = g_strconcat(
498 "<?xml version=\"1.0\" encoding=\"utf-8\" ?>",
499 "<d:propfind xmlns:d=\"DAV:\" xmlns:c=\"urn:ietf:params:xml:ns:caldav\">",
500 "<d:prop><c:calendar-home-set /></d:prop></d:propfind>",
501 NULL);
502 r = request_send(rq, runtime->debug);
503 list = find_element("//c:calendar-home-set/*", r->data,
504 "c", "urn:ietf:params:xml:ns:caldav");
505 response_free(r);
506 if (list) {
507 /* Change URL to where "calendar-home-set" is located */
508 gchar* s = get_host_part(runtime->url);
509 g_free(runtime->url);
510 runtime->url = g_strconcat(s, list->data, NULL);
511 g_free(s);
512 slist_free_gchar(list);
513 list = NULL;
514 }
515 g_free(rq->data);
516 rq->data = g_strconcat(
517 "<?xml version=\"1.0\" encoding=\"utf-8\" ?>",
518 "<d:propfind xmlns:d=\"DAV:\" xmlns:cs=\"http://calendarserver.org/ns/\" ",
519 "xmlns:c=\"urn:ietf:params:xml:ns:caldav\">",
520 "<d:prop><d:resourcetype/><d:displayname/><cs:getctag/>",
521 "<c:supported-calendar-component-set/></d:prop></d:propfind>",
522 NULL);
523 for (GSList* l = rq->headers; l; l = g_slist_next(l)) {
524 if (g_strcmp0(l->data, "Depth: 0") == 0) {
525 g_free(l->data);
526 l->data = g_strdup("Depth: 1");
527 }
528
529 }
530 r = request_send(rq, runtime->debug);
531 gchar* s = get_host_part(runtime->url);
532 list = find_calendars(s, r->data);
533 g_free(s);
534
535 runtime->output = g_new0(CaldavResponse, 1);
536 GSList* data = NULL;
537 for (GSList* l = list; l; l = g_slist_next(l)) {
538 Calendar* c = calendar_copy((Calendar *) l->data);
539 data = g_slist_append(data, c);
540 }
541 runtime->output->data = (gpointer) data;
542
543 if (runtime->file) {
544 for (GSList* l = list; l; l = g_slist_next(l)) {
545 calendar_dump(runtime->file, (Calendar *) l->data);
546 }
547 }
548 g_slist_foreach(list, (GFunc)calendar_free, NULL);
549 g_slist_free(list);
550 list = NULL;
551
552 runtime->output->headers = gchar_list_copy(r->headers);
553
554 runtime->output->status = status = r->status;
555
556 response_free(r);
557 request_free(rq);
558 }
559
560 return status;
561}
562
563static guint execute_add_update(gpointer ptr) {
564 Runtime* runtime = (Runtime *) ptr;
565 Request* rq = request_init(runtime);
566 guint status = 500;
567 gchar* lock_token = NULL;
568
569 if (rq) {
570 if (runtime->debug) {
571 debug_request(rq);
572 }
573
574 if (*runtime->operation == UPDATEOBJECTS) {
575 lock_token = execute_locking(runtime, NULL, &status);
576 if (status == 423) {
577 runtime->output = g_new0(CaldavResponse, 1);
578 runtime->output->data = (gpointer) g_strdup(lock_token);
579 runtime->output->status = status;
580 g_free(lock_token);
581 request_free(rq);
582 return status;
583 }
584 }
585 if (lock_token) {
586 rq->headers = g_slist_append(rq->headers,
587 g_strconcat("Lock-Token: ", lock_token, NULL));
588 }
589 Response* r = request_send(rq, runtime->debug);
590 if (lock_token) {
591 execute_locking(runtime, lock_token, &status);
592 g_free(lock_token);
593 lock_token = NULL;
594 }
595 request_free(rq);
596
597 if (runtime->debug) {
598 debug_response(r);
599 }
600
601 runtime->output = g_new0(CaldavResponse, 1);
602 if (r->status == 201 || r->status == 204) {
603 gchar* etag = find_header(r->headers, "ETag");
604 if (etag == NULL) {
605 Runtime* r_new = runtime_copy(runtime);
606 *r_new->operation = SIMPLEGET;
607 guint s = execute(r_new);
608 if (s == 200 && r_new->output && r_new->output->headers) {
609 etag = find_header(r_new->output->headers, "ETag");
610 }
611 runtime_free(r_new);
612 }
613 runtime->output->data = (gpointer) g_strdup(etag);
614 if (runtime->file) {
615 if (etag)
616 fprintf(runtime->file, "ETag: %s\n", etag);
617 else
618 fprintf(runtime->file, "%s\n", "No ETag returned");
619 }
620 g_free(etag);
621 } else {
622 runtime->output->data = (gpointer) g_strdup(r->data);
623 if (runtime->file)
624 fprintf(runtime->file, "%s\n", r->data);
625 }
626
627 runtime->output->status = status = r->status;
628
629 runtime->output->headers = gchar_list_copy(r->headers);
630
631 response_free(r);
632 }
633
634 return status;
635}
636
637static void caldav_response_free(Operation op, CaldavResponse* cr) {
638 GSList* list = NULL;
639
640 if (cr) {
641 if (cr->headers) {
642 slist_free_gchar(cr->headers);
643 cr->headers = NULL;
644 }
645 if (cr->data) {
646 switch (op) {
647 case DISCOVER:
648 list = (GSList *) cr->data;
649 g_slist_foreach(list, (GFunc)calendar_free, NULL);
650 g_slist_free(list);
651 break;
652 case OPTIONSINFO:
653 list = (GSList *) cr->data;
654 slist_free_gchar(list);
655 break;
656 default:
657 g_free((gchar *)cr->data);
658 }
659 cr->data = NULL;
660 }
661 }
662}
663
664gchar* find_header(GSList* list, const gchar* header) {
665 g_return_val_if_fail(header != NULL, NULL);
666
667 GString* hit = NULL;
668
669 for (GSList* l = list; l; l = g_slist_next(l)) {
670 gchar* s = (gchar *) l->data;
671 gchar** t = g_strsplit(s, ":", 2);
672 for (int i = 0; t[i]; i++) {
673 gchar* s1 = g_utf8_casefold(g_strstrip(t[i]), -1);
674 gchar* s2 = g_utf8_casefold(header, -1);
675 if (g_strcmp0(s1, s2) == 0) {
676 if (hit) {
677 hit = g_string_append(hit, ", ");
678 hit = g_string_append(hit, g_strstrip(t[i+1]));
679 } else {
680 hit = g_string_new(g_strstrip(t[i+1]));
681 }
682 }
683 g_free(s1);
684 g_free(s2);
685 }
686 }
687
688 return (hit) ? g_string_free(hit, FALSE) : NULL;
689}
690
691void slist_free_gchar(GSList* list) {
692 if (list) {
693 g_slist_foreach(list, (GFunc)g_free, NULL);
694 g_slist_free(list);
695 list = NULL;
696 }
697}
698
699Runtime* runtime_new() {
700 Runtime* r = g_new0(Runtime, 1);
701 r->default_executor = TRUE;
702
703 return r;
704}
705
706Calendar* calendar_copy(Calendar* cal) {
707 Calendar* new_cal = NULL;
708
709 if (!cal)
710 return NULL;
711
712 new_cal = g_new0(Calendar, 1);
713 new_cal->displayname = g_strdup(cal->displayname);
714 new_cal->url = g_strdup(cal->url);
715 new_cal->ctag = g_strdup(cal->ctag);
716 new_cal->components = g_slist_copy(cal->components);
717
718 return new_cal;
719}
720
721void calendar_dump(FILE* file, Calendar* cal) {
722 g_return_if_fail(file && cal);
723
724 fprintf(file, "------- Display Name: %s -------\n", cal->displayname);
725 fprintf(file, "URL: %s\n", cal->url);
726 fprintf(file, "CTAG: %s\n", cal->ctag);
727 fprintf(file, "Supported components\n");
728 for (GSList* l = cal->components; l; l = g_slist_next(l)) {
729 Component c = GPOINTER_TO_UINT(l->data);
730 gchar* value = component_to_string(c);
731 fprintf(file, "\t%s\n", value);
732 g_free(value);
733 }
734}
735
736void calendar_free(Calendar* cal) {
737 if (cal) {
738 if (cal->url) {
739 g_free(cal->url);
740 cal->url = NULL;
741 }
742 if (cal->ctag) {
743 g_free(cal->ctag);
744 cal->ctag = NULL;
745 }
746 if (cal->displayname) {
747 g_free(cal->displayname);
748 cal->displayname = NULL;
749 }
750 if (cal->components) {
751 g_slist_free(cal->components);
752 cal->components = NULL;
753 }
754 g_free(cal);
755 cal = NULL;
756 }
757}
758
759void runtime_free(Runtime* runtime) {
760 if (runtime) {
761 if (runtime->username) {
762 g_free(runtime->username);
763 runtime->username = NULL;
764 }
765 if (runtime->password) {
766 g_free(runtime->password);
767 runtime->password = NULL;
768 }
769 if (runtime->url) {
770 g_free(runtime->url);
771 runtime->url = NULL;
772 }
773 if (runtime->etag) {
774 g_free(runtime->etag);
775 runtime->etag = NULL;
776 }
777 if (runtime->component) {
778 g_free(runtime->component);
779 runtime->component = NULL;
780 }
781 if (runtime->hrefs) {
782 slist_free_gchar(runtime->hrefs);
783 runtime->hrefs = NULL;
784 }
785 if (runtime->options) {
786 slist_free_gchar(runtime->options);
787 runtime->options = NULL;
788 }
789 if (runtime->output && runtime->default_executor) {
790 caldav_response_free(*runtime->operation, runtime->output);
791 runtime->output = NULL;
792 }
793 if (runtime->operation) {
794 g_free(runtime->operation);
795 runtime->operation = NULL;
796 }
797 if (runtime->executor) {
798 runtime->executor = NULL;
799 }
800 if (runtime->start) {
801 g_date_time_unref(runtime->start);
802 runtime->start = NULL;
803 }
804 if (runtime->finish) {
805 g_date_time_unref(runtime->finish);
806 runtime->finish = NULL;
807 }
808 g_free(runtime);
809 runtime = NULL;
810 }
811}
812
813guint execute(Runtime* runtime) {
814 g_return_val_if_fail(runtime && runtime->operation, 500);
815
816 switch (*runtime->operation) {
817 case DISCOVER:
818 runtime->executor = execute_discover;
819 break;
820 case ADDOBJECTS:
821 case UPDATEOBJECTS:
822 runtime->executor = execute_add_update;
823 break;
824 case OPTIONSINFO:
825 runtime->executor = execute_options;
826 break;
827 case FREEBUSY:
828 runtime->executor = execute_freebusy;
829 break;
830 default:
831 runtime->executor = execute_default;
832 }
833
834 return runtime->executor(runtime);
835}
836
837gboolean is_component(const gchar* value, Component component) {
838 g_return_val_if_fail(value != NULL, FALSE);
839
840 if (g_strcmp0(value, "VEVENT") == 0 && component == VEVENT)
841 return TRUE;
842 else if (g_strcmp0(value, "VTODO") == 0 && component == VTODO)
843 return TRUE;
844 else if (g_strcmp0(value, "VFREEBUSY") == 0 && component == VFREEBUSY)
845 return TRUE;
846 else if (g_strcmp0(value, "VJOURNAL") == 0 && component == VJOURNAL)
847 return TRUE;
848 else
849 return FALSE;
850}
851
852gchar* component_to_string(Component component) {
853 if (component == VEVENT)
854 return g_strdup("VEVENT");
855 else if (component == VTODO)
856 return g_strdup("VTODO");
857 else if (component == VFREEBUSY)
858 return g_strdup("VFREEBUSY");
859 else if (component == VJOURNAL)
860 return g_strdup("VJOURNAL");
861 else
862 return NULL;
863}
864
865Component string_to_component(const gchar* value) {
866 g_return_val_if_fail(value != NULL, VUNKNOWN);
867
868 if (g_strcmp0(value, "VEVENT") == 0)
869 return VEVENT;
870 else if (g_strcmp0(value, "VTODO") == 0)
871 return VTODO;
872 else if (g_strcmp0(value, "VFREEBUSY") == 0)
873 return VFREEBUSY;
874 else if (g_strcmp0(value, "VJOURNAL") == 0)
875 return VJOURNAL;
876 else
877 return VUNKNOWN;
878}
879
880gchar* status_str(Runtime* runtime) {
881 gchar* str = NULL;
882
883 g_return_val_if_fail(runtime && runtime->output, NULL);
884
885 switch (runtime->output->status) {
886 case 200: str = g_strdup("OK"); break;
887 case 201: str = g_strdup("Created"); break;
888 case 204: str = g_strdup("No Content"); break;
889 case 207: str = g_strdup("Multi-Status"); break;
890 case 400: str = g_strdup("Bad Request"); break;
891 case 401: str = g_strdup("Unauthorized"); break;
892 case 403: str = g_strdup("Forbidden"); break;
893 case 404: str = g_strdup("Not Found"); break;
894 case 405: str = g_strdup("Method Not Allowed"); break;
895 case 406: str = g_strdup("Not Acceptable"); break;
896 case 409: str = g_strdup("Conflict"); break;
897 case 410: str = g_strdup("Gone"); break;
898 case 412: str = g_strdup("Precondition Failed"); break;
899 case 415: str = g_strdup("Unsupported Media Type"); break;
900 case 423: str = g_strdup("Locked"); break;
901 case 424: str = g_strdup("Failed Dependency"); break;
902 case 500: str = g_strdup("Internal Server Error"); break;
903 case 501: str = g_strdup("Not Implemented"); break;
904 case 503: str = g_strdup("Service Unavailable"); break;
905 case 507: str = g_strdup("Insufficient Storage"); break;
906 default: str = g_strdup_printf("%d: Unexpected status", runtime->output->status);
907 }
908
909 return str;
910}
911
912HrefData* href_data_new(const gchar* href, const gchar* data) {
913 g_return_val_if_fail(href != NULL, NULL);
914
915 HrefData* h = g_new0(HrefData, 1);
916 h->href = g_strdup(href);
917 h->data = data ? g_strdup(data) : NULL;
918
919 return h;
920}
921
922void href_data_free(HrefData* href_data) {
923 if (href_data) {
924 if (href_data->href) {
925 g_free(href_data->href);
926 href_data->href = NULL;
927 }
928 if (href_data->data) {
929 g_free(href_data->data);
930 href_data->data = NULL;
931 }
932 g_free(href_data);
933 href_data = NULL;
934 }
935}
936
937gboolean has_option(Runtime* runtime, const gchar* option) {
938 g_return_val_if_fail(runtime && option, FALSE);
939 gboolean has = FALSE;
940
941 gchar* s2 = g_utf8_casefold(option, -1);
942 if (runtime->options) {
943 for (GSList* l = runtime->options; l && ! has; l = g_slist_next(l)) {
944 gchar* s1 = g_utf8_casefold((gchar *) l->data, -1);
945 if (g_strcmp0(s1, s2) == 0) {
946 has = TRUE;
947 }
948 g_free(s1);
949 }
950 }
951 g_free(s2);
952
953 return has;
954}
955
956Runtime* runtime_copy(Runtime* runtime) {
957 Runtime* new = NULL;
958
959 g_return_val_if_fail(runtime != NULL, NULL);
960
961 new = runtime_new();
962 new->username = g_strdup(runtime->username);
963 new->password = g_strdup(runtime->password);
964 new->url = g_strdup(runtime->url);
965 new->debug = runtime->debug;
966 new->operation = g_new0(Operation, 1);
967 *new->operation = *runtime->operation;
968 new->executor = runtime->executor;
969 new->default_executor = runtime->default_executor;
970 new->etag = g_strdup(runtime->etag);
971 new->component = g_strdup(runtime->component);
972 new->hrefs = slist_copy_gchar(runtime->hrefs);
973 new->options = slist_copy_gchar(runtime->options);
974
975 return new;
976}
977
978GSList* slist_copy_gchar(GSList* list) {
979 GSList* new = NULL;
980
981 for (GSList* l = list; l; l = g_slist_next(l)) {
982 new = g_slist_append(new, g_strdup(l->data));
983 }
984
985 return new;
986}
987
988GDateTime* get_date_time_from_string(const gchar* datetime) {
989 GDateTime* dt = NULL;
990 g_return_val_if_fail(datetime != NULL, NULL);
991
992 gchar** dt_part = g_strsplit(datetime, "T", 0);
993 if (!dt_part || dt_part[2] != NULL) {
994 g_strfreev(dt_part);
995 return NULL;
996 }
997
998 gchar** d_part = g_strsplit(dt_part[0], "-", 0);
999 if (!d_part || d_part[3] != NULL) {
1000 g_strfreev(dt_part);
1001 g_strfreev(d_part);
1002 return NULL;
1003 }
1004
1005 gchar** t_part = g_strsplit(dt_part[1], ":", 0);
1006 if (!t_part || t_part[3] != NULL) {
1007 g_strfreev(dt_part);
1008 g_strfreev(t_part);
1009 return NULL;
1010 }
1011
1012 g_strfreev(dt_part);
1013 char* endptr;
1014 dt = g_date_time_new_local(
1015 atoi(d_part[0]), atoi(d_part[1]), atoi(d_part[2]),
1016 atoi(t_part[0]), atoi(t_part[1]), strtod(t_part[2], &endptr));
1017
1018 g_strfreev(d_part);
1019 g_strfreev(t_part);
1020
1021 return dt;
1022}
This page took 0.157821 seconds and 5 git commands to generate.