]> Pileus Git - lackey/blob - src/cal.c
Add event parsing for EWS calendars
[lackey] / src / cal.c
1 /*
2  * Copyright (C) 2012-2013 Andy Spencer <andy753421@gmail.com>
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include <stdlib.h>
19
20 #include "util.h"
21 #include "date.h"
22 #include "cal.h"
23
24 /* Macros */
25 #define CAL(name) \
26         void     name##_config(const char *group, const char *name, const char *key, const char *value); \
27         cal_t   *name##_cals(void); \
28         event_t *name##_events(date_t start, date_t end); \
29         todo_t  *name##_todos(date_t start, date_t end)
30
31 /* Prototypes */
32 CAL(dummy);
33 CAL(ical);
34 CAL(ews);
35
36 /* Global data */
37 cal_t   *CAL,   *CALS;
38 event_t *EVENT, *EVENTS;
39 todo_t  *TODO,  *TODOS;
40
41 /* Local data */
42 static date_t start;
43 static date_t end;
44
45 /* Merge events and todos */
46 static void add_event(event_t **first, event_t **last, event_t **next)
47 {
48         if (*last)
49                 (*last)->next = *next;
50         else
51                 (*first) = *next;
52         (*next)->prev = *last;
53         (*last) = (*next);
54         (*next) = (*next)->next;
55 }
56
57 static void add_todo(todo_t **first, todo_t **last, todo_t **next)
58 {
59         if (*last)
60                 (*last)->next = *next;
61         else
62                 (*first) = *next;
63         (*next)->prev = *last;
64         (*last) = (*next);
65         (*next) = (*next)->next;
66 }
67
68 /* Merge functions */
69 cal_t *merge_cals(cal_t *a, cal_t *b)
70 {
71         // TODO - we should sort these
72         if (!a) return b;
73         if (!b) return a;
74         cal_t *last = a;
75         while (last->next)
76                 last = last->next;
77         last->next = b;
78         return a;
79 }
80
81 event_t *merge_events(event_t *a, event_t *b)
82 {
83         event_t *first = NULL, *last = NULL;
84         while (a && b)
85                 if (compare(&a->start, &b->start) <= 0)
86                         add_event(&first, &last, &a);
87                 else
88                         add_event(&first, &last, &b);
89         while (a) add_event(&first, &last, &a);
90         while (b) add_event(&first, &last, &b);
91         return first;
92 }
93
94 todo_t *merge_todos(todo_t *a, todo_t *b)
95 {
96         todo_t *first = NULL, *last = NULL;
97         while (a && b)
98                 if (compare(&a->start, &b->start) <= 0)
99                         add_todo(&first, &last, &a);
100                 else
101                         add_todo(&first, &last, &b);
102         while (a) add_todo(&first, &last, &a);
103         while (b) add_todo(&first, &last, &b);
104         return first;
105 }
106
107 /* Initialize */
108 void cal_init(void)
109 {
110         /* Load calendars */
111         CALS = merge_cals(CALS, dummy_cals());
112         CALS = merge_cals(CALS, ical_cals());
113         CALS = merge_cals(CALS, ews_cals());
114
115         /* Load data */
116         cal_load(SEL.year, SEL.month, SEL.day, 1);
117
118         /* Debug */
119 #ifdef CAL_DEBUG
120         for (event_t *e = EVENTS; e; e = e->next)
121                 debug("event: %04d-%02d-%02d %02d:%02d: %s - %s",
122                                 e->start.year, e->start.month, e->start.day,
123                                 e->start.hour, e->start.min, e->name, e->desc);
124         for (todo_t *e = TODOS; e; e = e->next)
125                 debug("todo: %04d-%02d-%02d %02d:%02d: %s - %s",
126                                 e->start.year, e->start.month, e->start.day,
127                                 e->start.hour, e->start.min, e->name, e->desc);
128 #endif
129 }
130
131 /* Load events and todos */
132 void cal_load(year_t year, month_t month, day_t day, int days)
133 {
134         year_t  eyear  = year;
135         month_t emonth = month;
136         day_t   eday   = day;
137         add_days(&eyear, &emonth, &eday, days);
138
139         /* Skip if we already loaded enough info */
140         if (before(&start, year,  month,  day,  0, 0) &&
141            !before(&end,  eyear, emonth, eday, 24, 0))
142                 return;
143
144         /* Free uneeded data */
145         for (event_t *next, *cur = EVENTS; cur; cur = next) {
146                 next = cur->next;
147                 if (cur->name) free(cur->name);
148                 if (cur->desc) free(cur->desc);
149                 if (cur->loc)  free(cur->loc);
150                 if (cur->cat)  free(cur->cat);
151                 free(cur);
152         }
153         for (todo_t *next, *cur = TODOS; cur; cur = next) {
154                 next = cur->next;
155                 if (cur->name) free(cur->name);
156                 if (cur->desc) free(cur->desc);
157                 if (cur->cat)  free(cur->cat);
158                 free(cur);
159         }
160
161         /* Push dates out a bit to avoid reloading,
162          * enough to at least cover the current year */
163         add_days(&year,  &month,  &day, -366);
164         add_days(&eyear, &emonth, &eday, 366);
165         start = (date_t){year,  month,  day};
166         end   = (date_t){eyear, emonth, eday};
167
168         /* Load events */
169         EVENTS = merge_events(NULL,   dummy_events(start, end));
170         EVENTS = merge_events(EVENTS, ical_events(start, end));
171         EVENTS = merge_events(EVENTS, ews_events(start, end));
172
173         /* Load todos */
174         TODOS = merge_todos(NULL,  dummy_todos(start, end));
175         TODOS = merge_todos(TODOS, ical_todos(start, end));
176         TODOS = merge_todos(TODOS, ews_todos(start, end));
177
178         /* Verify events and todos*/
179 #ifdef CAL_ERROR
180         for (event_t *cur = EVENTS; cur; cur = cur->next) {
181                 if (!cur->cal)
182                         error("Missing cal in event '%s'", cur->name);
183                 if ((cur->next && cur->next->prev != cur) ||
184                     (cur->prev && cur->prev->next != cur))
185                         error("Broken link in event '%s'", cur->name);
186         }
187         for (todo_t *cur = TODOS; cur; cur = cur->next) {
188                 if (!cur->cal)
189                         error("Missing cal in todo '%s'", cur->name);
190                 if ((cur->next && cur->next->prev != cur) ||
191                     (cur->prev && cur->prev->next != cur))
192                         error("Broken link in todo '%s'", cur->name);
193         }
194 #endif
195 }
196
197 /* Config parser */
198 void cal_config(const char *group, const char *name, const char *key, const char *value)
199 {
200         if (match(group, "dummy"))
201                 dummy_config(group, name, key, value);
202         else if (match(group, "ical"))
203                 ical_config(group, name, key, value);
204         else if (match(group, "ews"))
205                 ews_config(group, name, key, value);
206 }
207
208 /* Find event for matching target date */
209 event_t *find_event(date_t *target)
210 {
211         int      min   = 0;
212         event_t *event = NULL;
213
214         if (EVENT && compare(&EVENT->start, target) == 0)
215                 return EVENT;
216
217         for (event_t *cur = EVENTS; cur; cur = cur->next) {
218                 // Skip events that are on the wrong day
219                 if ((target->year  != cur->start.year) ||
220                     (target->month != cur->start.month) ||
221                     (target->day   != cur->start.day))
222                         continue;
223
224                 // We don't want time change or leap seconds here
225                 int diff = (target->hour - cur->start.hour) * 60 * 24 +
226                            (target->min  - cur->start.min)  * 60 +
227                            (target->sec  - cur->start.sec);
228
229                 if (event == NULL || ABS(diff) < min) {
230                         min   = ABS(diff);
231                         event = cur;
232                 }
233         }
234
235         return event;
236 }