Add Calendaring support for Asterisk
[asterisk/asterisk.git] / res / res_calendar.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2008 - 2009, Digium, Inc.
5  *
6  * Terry Wilson <twilson@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  * \brief Calendaring API
21  */
22
23 #include "asterisk.h"
24
25 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
26
27 #include "asterisk/_private.h"
28 #include "asterisk/calendar.h"
29 #include "asterisk/utils.h"
30 #include "asterisk/astobj2.h"
31 #include "asterisk/module.h"
32 #include "asterisk/config.h"
33 #include "asterisk/channel.h"
34 #include "asterisk/devicestate.h"
35 #include "asterisk/linkedlists.h"
36 #include "asterisk/sched.h"
37 #include "asterisk/dial.h"
38 #include "asterisk/cli.h"
39 #include "asterisk/pbx.h"
40 #include "asterisk/app.h"
41
42 /*** DOCUMENTATION
43         <function name="CALENDAR_BUSY" language="en_US">
44                 <synopsis>
45                         Determine if the calendar is marked busy at this time.
46                 </synopsis>
47                 <syntax>
48                         <parameter name="calendar" required="true" />
49                 </syntax>
50         <description>
51                         <para>Check the specified calendar's current busy status.</para>
52                 </description>
53         </function>
54         <function name="CALENDAR_EVENT" language="en_US">
55                 <synopsis>
56                         Get calendar event notification data from a notification call.
57                 </synopsis>
58                 <syntax>
59                         <parameter name="field" required="true">
60                                 <enumlist>
61                                         <enum name="summary"><para>The VEVENT SUMMARY property or Exchange event 'subject'</para></enum>
62                                         <enum name="description"><para>The text description of the event</para></enum>
63                                         <enum name="organizer"><para>The organizer of the event</para></enum>
64                                         <enum name="location"><para>The location of the eventt</para></enum>
65                                         <enum name="calendar"><para>The name of the calendar associated with the event</para></enum>
66                                         <enum name="uid"><para>The unique identifier for this event</para></enum>
67                                         <enum name="start"><para>The start time of the event</para></enum>
68                                         <enum name="end"><para>The end time of the event</para></enum>
69                                         <enum name="busystate"><para>The busy state of the event 0=FREE, 1=TENTATIVE, 2=BUSY</para></enum>
70                                 </enumlist>
71                         </parameter>
72                 </syntax>
73                 <description>
74                         <para>Whenever a calendar event notification call is made, the event data
75                         may be accessed with this function.</para>
76                 </description>
77         </function>
78         <function name="CALENDAR_QUERY" language="en_US">
79                 <synopsis>Query a calendar server and store the data on a channel
80                 </synopsis>
81                 <syntax>
82                         <parameter name="calendar" required="true">
83                                 <para>The calendar that should be queried</para>
84                         </parameter>
85                         <parameter name="start" required="false">
86                                 <para>The start time of the query (in seconds since epoch)</para>
87                         </parameter>
88                         <parameter name="end" required="false">
89                                 <para>The end time of the query (in seconds since epoch)</para>
90                         </parameter>
91                 </syntax>
92                 <description>
93                         <para>Get a list of events in the currently accessible timeframe of the <replaceable>calendar</replaceable>
94                         The function returns the id for accessing the result with CALENDAR_QUERY_RESULT()</para>
95                 </description>
96         </function>
97         <function name="CALENDAR_QUERY_RESULT" language="en_US">
98                 <synopsis>
99                         Retrieve data from a previously run CALENDAR_QUERY() call
100                 </synopsis>
101                 <syntax>
102                         <parameter name="id" required="true">
103                                 <para>The query ID returned by <literal>CALENDAR_QUERY</literal></para>
104                         </parameter>
105                         <parameter name="field" required="true">
106                                 <enumlist>
107                                         <enum name="getnum"><para>number of events occurring during time range</para></enum>
108                                         <enum name="summary"><para>A summary of the event</para></enum>
109                                         <enum name="description"><para>The full event description</para></enum>
110                                         <enum name="organizer"><para>The event organizer</para></enum>
111                                         <enum name="location"><para>The event location</para></enum>
112                                         <enum name="calendar"><para>The name of the calendar associted with the event</para></enum>
113                                         <enum name="uid"><para>The unique identifier for the event</para></enum>
114                                         <enum name="start"><para>The start time of the event (in seconds since epoch)</para></enum>
115                                         <enum name="end"><para>The end time of the event (in seconds since epoch)</para></enum>
116                                         <enum name="busystate"><para>The busy status of the event 0=FREE, 1=TENTATIVE, 2=BUSY</para></enum>
117                                 </enumlist>
118                         </parameter>
119                         <parameter name="entry" required="false" default="1">
120                                 <para>Return data from a specific event returned by the query</para>
121                         </parameter>
122                 </syntax>
123                 <description>
124                         <para>After running CALENDAR_QUERY and getting a result <replaceable>id</replaceable>, calling
125                         <literal>CALENDAR_QUERY</literal> with that <replaceable>id</replaceable> and a <replaceable>field</replaceable>
126                         will return the data for that field. If multiple events matched the query, and <replaceable>entry</replaceable>
127                         is provided, information from that event will be returned.</para>
128                 </description>
129         </function>
130         <function name="CALENDAR_WRITE" language="en_US">
131                 <synopsis>Write an event to a calendar</synopsis>
132                 <syntax>
133                         <parameter name="calendar" required="true">
134                                 <para>The calendar to write to</para>
135                         </parameter>
136                         <parameter name="field" multiple="true" required="true">
137                                 <enumlist>
138                                         <enum name="summary"><para>A summary of the event</para></enum>
139                                         <enum name="description"><para>The full event description</para></enum>
140                                         <enum name="organizer"><para>The event organizer</para></enum>
141                                         <enum name="location"><para>The event location</para></enum>
142                                         <enum name="uid"><para>The unique identifier for the event</para></enum>
143                                         <enum name="start"><para>The start time of the event (in seconds since epoch)</para></enum>
144                                         <enum name="end"><para>The end time of the event (in seconds since epoch)</para></enum>
145                                         <enum name="busystate"><para>The busy status of the event 0=FREE, 1=TENTATIVE, 2=BUSY</para></enum>
146                                 </enumlist>
147                         </parameter>
148                 </syntax>
149                 <description>
150                         <para>Example: CALENDAR_WRITE(calendar,field1,field2,field3)=val1,val2,val3</para>
151                         <para>The field and value arguments can easily be set/passed using the HASHKEYS() and HASH() functions</para>
152                 </description>
153         </function>
154
155 ***/
156 #define CALENDAR_BUCKETS 19
157
158 static struct ao2_container *calendars;
159 static struct sched_context *sched;
160 static pthread_t refresh_thread = AST_PTHREADT_NULL;
161 static ast_mutex_t refreshlock;
162 static ast_cond_t refresh_condition;
163 static ast_mutex_t reloadlock;
164
165 static void event_notification_destroy(void *data);
166 static void *event_notification_duplicate(void *data);
167 static void eventlist_destroy(void *data);
168 static void *eventlist_duplicate(void *data);
169
170 static const struct ast_datastore_info event_notification_datastore = {
171         .type = "EventNotification",
172         .destroy = event_notification_destroy,
173         .duplicate = event_notification_duplicate,
174 };
175
176 static const struct ast_datastore_info eventlist_datastore_info = {
177         .type = "CalendarEventList",
178         .destroy = eventlist_destroy,
179         .duplicate = eventlist_duplicate,
180 };
181
182 struct evententry {
183         struct ast_calendar_event *event;
184         AST_LIST_ENTRY(evententry) list;
185 };
186
187 AST_LIST_HEAD_STATIC(techs, ast_calendar_tech);
188 AST_LIST_HEAD_NOLOCK(eventlist, evententry); /* define the type */
189
190 struct ast_config *calendar_config = NULL;
191
192 static struct ast_calendar *unref_calendar(struct ast_calendar *cal)
193 {
194         ao2_ref(cal, -1);
195         return NULL;
196 }
197
198 static int calendar_hash_fn(const void *obj, const int flags)
199 {
200         const struct ast_calendar *cal = obj;
201         return ast_str_case_hash(cal->name);
202 }
203
204 static int calendar_cmp_fn(void *obj, void *arg, int flags)
205 {
206         const struct ast_calendar *one = obj, *two = arg;
207         return !strcasecmp(one->name, two->name) ? CMP_MATCH | CMP_STOP: 0;
208 }
209
210 static struct ast_calendar *find_calendar(const char *name)
211 {
212         struct ast_calendar tmp = {
213                 .name = name,
214         };
215         return ao2_find(calendars, &tmp, OBJ_POINTER);
216 }
217
218 static int event_hash_fn(const void *obj, const int flags)
219 {
220         const struct ast_calendar_event *event = obj;
221         return ast_str_hash(event->uid);
222 }
223
224 static int event_cmp_fn(void *obj, void *arg, int flags)
225 {
226         const struct ast_calendar_event *one = obj, *two = arg;
227         return !strcmp(one->uid, two->uid) ? CMP_MATCH | CMP_STOP : 0;
228 }
229
230 static struct ast_calendar_event *find_event(struct ao2_container *events, const char *uid)
231 {
232         struct ast_calendar_event tmp = {
233                 .uid = uid,
234         };
235         return ao2_find(events, &tmp, OBJ_POINTER);
236 }
237
238 struct ast_calendar_event *ast_calendar_unref_event(struct ast_calendar_event *event)
239 {
240         ao2_ref(event, -1);
241         return NULL;
242 }
243
244 static void calendar_destructor(void *obj)
245 {
246         struct ast_calendar *cal = obj;
247
248         ast_debug(3, "Destroying calendar %s\n", cal->name);
249
250         ao2_lock(cal);
251         cal->unloading = 1;
252         ast_cond_signal(&cal->unload);
253         pthread_join(cal->thread, NULL);
254         if (cal->tech_pvt) {
255                 cal->tech_pvt = cal->tech->unref_calendar(cal->tech_pvt);
256         }
257         ast_calendar_clear_events(cal);
258         ast_string_field_free_memory(cal);
259         ao2_ref(cal->events, -1);
260         ao2_unlock(cal);
261 }
262
263 static void eventlist_destructor(void *obj)
264 {
265         struct eventlist *events = obj;
266         struct evententry *entry;
267
268         while ((entry = AST_LIST_REMOVE_HEAD(events, list))) {
269                 ao2_ref(entry->event, -1);
270                 ast_free(entry);
271         }
272 }
273
274 static int calendar_busy_callback(void *obj, void *arg, int flags)
275 {
276         struct ast_calendar_event *event = obj;
277         int *is_busy = arg;
278         struct timeval tv = ast_tvnow();
279
280         if (tv.tv_sec >= event->start && tv.tv_sec <= event->end && event->busy_state > AST_CALENDAR_BS_FREE) {
281                 *is_busy = 1;
282                 return CMP_STOP;
283         }
284
285         return 0;
286 }
287
288 static int calendar_is_busy(struct ast_calendar *cal)
289 {
290         int is_busy = 0;
291
292         ao2_callback(cal->events, OBJ_NODATA, calendar_busy_callback, &is_busy);
293
294         return is_busy;
295 }
296
297 static enum ast_device_state calendarstate(const char *data)
298 {
299         struct ast_calendar *cal;
300
301         if (ast_strlen_zero(data) || (!(cal = find_calendar(data)))) {
302                 return AST_DEVICE_INVALID;
303         }
304
305         if (cal->tech->is_busy) {
306                 return cal->tech->is_busy(cal) ? AST_DEVICE_INUSE : AST_DEVICE_NOT_INUSE;
307         }
308
309         return calendar_is_busy(cal) ? AST_DEVICE_INUSE : AST_DEVICE_NOT_INUSE;
310 }
311
312 static struct ast_calendar *build_calendar(struct ast_config *cfg, const char *cat, const struct ast_calendar_tech *tech)
313 {
314         struct ast_calendar *cal;
315         struct ast_variable *v;
316         int new_calendar = 0;
317
318         if (!(cal = find_calendar(cat))) {
319                 new_calendar = 1;
320                 if (!(cal = ao2_alloc(sizeof(*cal), calendar_destructor))) {
321                         ast_log(LOG_ERROR, "Could not allocate calendar structure. Stopping.\n");
322                         return NULL;
323                 }
324
325                 if (!(cal->events = ao2_container_alloc(CALENDAR_BUCKETS, event_hash_fn, event_cmp_fn))) {
326                         ast_log(LOG_ERROR, "Could not allocate events container for %s\n", cat);
327                         cal = unref_calendar(cal);
328                         return NULL;
329                 }
330
331                 if (ast_string_field_init(cal, 32)) {
332                         ast_log(LOG_ERROR, "Couldn't create string fields for %s\n", cat);
333                         cal = unref_calendar(cal);
334                         return NULL;
335                 }
336         } else {
337                 cal->pending_deletion = 0;
338         }
339
340         ast_string_field_set(cal, name, cat);
341         cal->tech = tech;
342
343         cal->refresh = 3600;
344         cal->timeframe = 60;
345
346         for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
347                 if (!strcasecmp(v->name, "autoreminder")) {
348                         cal->autoreminder = atoi(v->value);
349                 } else if (!strcasecmp(v->name, "channel")) {
350                         ast_string_field_set(cal, notify_channel, v->value);
351                 } else if (!strcasecmp(v->name, "context")) {
352                         ast_string_field_set(cal, notify_context, v->value);
353                 } else if (!strcasecmp(v->name, "extension")) {
354                         ast_string_field_set(cal, notify_extension, v->value);
355                 } else if (!strcasecmp(v->name, "waittime")) {
356                         cal->notify_waittime = atoi(v->value);
357                 } else if (!strcasecmp(v->name, "app")) {
358                         ast_string_field_set(cal, notify_app, v->value);
359                 } else if (!strcasecmp(v->name, "appdata")) {
360                         ast_string_field_set(cal, notify_appdata, v->value);
361                 } else if (!strcasecmp(v->name, "refresh")) {
362                         cal->refresh = atoi(v->value);
363                 } else if (!strcasecmp(v->name, "timeframe")) {
364                         cal->timeframe = atoi(v->value);
365                 }
366         }
367
368         if (new_calendar) {
369                 cal->thread = AST_PTHREADT_NULL;
370                 ast_cond_init(&cal->unload, NULL);
371                 ao2_link(calendars, cal);
372                 if (ast_pthread_create(&cal->thread, NULL, cal->tech->load_calendar, cal)) {
373                         /* If we start failing to create threads, go ahead and return NULL
374                          * and the tech module will be unregistered
375                          */ 
376                         ao2_unlink(calendars, cal);
377                         cal = unref_calendar(cal);
378                 }
379         }
380
381         return cal;
382 }
383
384 static int load_tech_calendars(struct ast_calendar_tech *tech)
385 {
386         struct ast_calendar *cal;
387         const char *cat = NULL;
388         const char *val;
389
390         if (!calendar_config) {
391                 ast_log(LOG_WARNING, "Calendar support disabled, not loading %s calendar module\n", tech->type);
392                 return -1;
393         }
394
395         while ((cat = ast_category_browse(calendar_config, cat))) {
396                 if (!strcasecmp(cat, "general")) {
397                         continue;
398                 }
399
400                 if (!(val = ast_variable_retrieve(calendar_config, cat, "type")) || strcasecmp(val, tech->type)) {
401                         continue;
402                 }
403
404                 /* A serious error occurred loading calendars from this tech and it should be disabled */
405                 if (!(cal = build_calendar(calendar_config, cat, tech))) {
406                         ast_calendar_unregister(tech);
407                         return -1;
408                 }
409
410                 cal = unref_calendar(cal);
411         }
412
413         return 0;
414 }
415
416 int ast_calendar_register(struct ast_calendar_tech *tech)
417 {
418         struct ast_calendar_tech *iter;
419
420         AST_LIST_LOCK(&techs);
421         AST_LIST_TRAVERSE(&techs, iter, list) {
422                 if(!strcasecmp(tech->type, iter->type)) {
423                         ast_log(LOG_WARNING, "Already have a handler for calendar type '%s'\n", tech->type);
424                         AST_LIST_UNLOCK(&techs);
425                         return -1;
426                 }
427         }
428         AST_LIST_INSERT_HEAD(&techs, tech, list);
429         AST_LIST_UNLOCK(&techs);
430
431         ast_verb(2, "Registered calendar type '%s' (%s)\n", tech->type, tech->description);
432
433         return load_tech_calendars(tech);
434 }
435
436 static int match_caltech_cb(void *user_data, void *arg, int flags)
437 {
438         struct ast_calendar *cal = user_data;
439         struct ast_calendar_tech *tech = arg;
440
441         if (cal->tech == tech) {
442                 return CMP_MATCH;
443         }
444
445         return 0;
446 }
447
448 void ast_calendar_unregister(struct ast_calendar_tech *tech)
449 {
450         struct ast_calendar_tech *iter;
451
452         AST_LIST_LOCK(&techs);
453         AST_LIST_TRAVERSE_SAFE_BEGIN(&techs, iter, list) {
454                 if (iter != tech) {
455                         continue;
456                 }
457
458                 ao2_callback(calendars, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, match_caltech_cb, tech);
459
460                 AST_LIST_REMOVE_CURRENT(list);
461                 ast_verb(2, "Unregistered calendar type '%s'\n", tech->type);
462                 break;
463         }
464         AST_LIST_TRAVERSE_SAFE_END;
465         AST_LIST_UNLOCK(&techs);
466
467 }
468
469 static void calendar_event_destructor(void *obj)
470 {
471         struct ast_calendar_event *event = obj;
472         struct ast_calendar_attendee *attendee;
473
474         ast_debug(3, "Destroying event for calendar '%s'\n", event->owner->name);
475         ast_string_field_free_memory(event);
476         while ((attendee = AST_LIST_REMOVE_HEAD(&event->attendees, next))) {
477                 if (attendee->data) {
478                         ast_free(attendee->data);
479                 }
480                 ast_free(attendee);
481         }
482 }
483
484 /* This is only called from ao2_callbacks that are going to unref the event for us,
485  * so we don't unref the event here.  */
486 static struct ast_calendar_event *destroy_event(struct ast_calendar_event *event)
487 {
488         if (event->notify_sched > -1 && ast_sched_del(sched, event->notify_sched)) {
489                 ast_debug(3, "Notification running, can't delete sched entry\n");
490         }
491         if (event->bs_start_sched > -1 && ast_sched_del(sched, event->bs_start_sched)) {
492                 ast_debug(3, "Devicestate update (start) running, can't delete sched entry\n");
493         }
494         if (event->bs_end_sched > -1 && ast_sched_del(sched, event->bs_end_sched)) {
495                 ast_debug(3, "Devicestate update (end) running, can't delete sched entry\n");
496         }
497
498         /* If an event is being deleted and we've fired an event changing the status at the beginning,
499          * but haven't hit the end event yet, go ahead and set the devicestate to the current busy status */
500         if (event->bs_start_sched < 0 && event->bs_end_sched >= 0) {
501                 if (!calendar_is_busy(event->owner)) {
502                         ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Calendar/%s", event->owner->name);
503                 } else {
504                         ast_devstate_changed(AST_DEVICE_BUSY, "Calendar/%s", event->owner->name);
505                 }
506         }
507
508         return NULL;
509 }
510
511 static int clear_events_cb(void *user_data, void *arg, int flags)
512 {
513         struct ast_calendar_event *event = user_data;
514
515         event = destroy_event(event);
516
517         return CMP_MATCH;
518 }
519
520 void ast_calendar_clear_events(struct ast_calendar *cal)
521 {
522         ast_debug(3, "Clearing all events for calendar %s\n", cal->name);
523
524         ao2_callback(cal->events, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, clear_events_cb, NULL);
525 }
526
527 struct ast_calendar_event *ast_calendar_event_alloc(struct ast_calendar *cal)
528 {
529         struct ast_calendar_event *event;
530         if (!(event = ao2_alloc(sizeof(*event), calendar_event_destructor))) {
531                 return NULL;
532         }
533
534         if (ast_string_field_init(event, 32)) {
535                 event = ast_calendar_unref_event(event);
536                 return NULL;
537         }
538
539         event->owner = cal;
540         event->notify_sched = -1;
541         event->bs_start_sched = -1;
542         event->bs_end_sched = -1;
543
544         AST_LIST_HEAD_INIT_NOLOCK(&event->attendees);
545
546         return event;
547 }
548
549 struct ao2_container *ast_calendar_event_container_alloc(void)
550 {
551         return ao2_container_alloc(CALENDAR_BUCKETS, event_hash_fn, event_cmp_fn);
552 }
553
554 static void event_notification_destroy(void *data)
555 {
556         struct ast_calendar_event *event = data;
557
558         event = ast_calendar_unref_event(event);
559
560 }
561
562 static void *event_notification_duplicate(void *data)
563 {
564         struct ast_calendar_event *event = data;
565
566         if (!event) {
567                 return NULL;
568         }
569
570         ao2_ref(event, +1);
571
572         return event;
573 }
574
575 /*! \brief Generate 32 byte random string (stolen from chan_sip.c)*/
576 static char *generate_random_string(char *buf, size_t size)
577 {
578         long val[4];
579         int x;
580
581         for (x = 0; x < 4; x++) {
582                 val[x] = ast_random();
583         }
584         snprintf(buf, size, "%08lx%08lx%08lx%08lx", val[0], val[1], val[2], val[3]);
585
586         return buf;
587 }
588
589 static int calendar_event_notify(const void *data)
590 {
591         struct ast_calendar_event *event = (void *)data;
592         char tech[256], dest[256], buf[8], *tmp;
593         struct ast_dial *dial = NULL;
594         struct ast_channel *chan = NULL;
595         struct ast_str *apptext = NULL;
596         int res = -1;
597         char start[12], end[12], busystate[2];
598         struct ast_datastore *datastore;
599
600         if (!(event && event->owner)) {
601                 ast_log(LOG_ERROR, "Extremely low-cal...in fact cal is NULL!\n");
602                 goto notify_cleanup;
603         }
604
605         ao2_ref(event, +1);
606         event->notify_sched = -1;
607
608         ast_copy_string(tech, event->owner->notify_channel, sizeof(tech));
609
610         if ((tmp = strchr(tech, '/'))) {
611                 *tmp = '\0';
612                 tmp++;
613                 ast_copy_string(dest, tmp, sizeof(dest));
614         } else {
615                 ast_log(LOG_WARNING, "Channel should be in form Tech/Dest\n");
616                 goto notify_cleanup;
617         }
618
619         if (!(dial = ast_dial_create())) {
620                 ast_log(LOG_ERROR, "Could not create dial structure\n");
621                 goto notify_cleanup;
622         }
623
624         if (ast_dial_append(dial, tech, dest) < 0) {
625                 ast_log(LOG_ERROR, "Could not append channel\n");
626                 goto notify_cleanup;
627         }
628
629         ast_dial_set_global_timeout(dial, event->owner->notify_waittime);
630         generate_random_string(buf, sizeof(buf));
631         if (!(chan = ast_channel_alloc(1, AST_STATE_DOWN, 0, 0, 0, 0, 0, 0, "Calendar/%s-%s", event->owner->name, buf))) {
632                 ast_log(LOG_ERROR, "Could not allocate notification channel\n");
633                 goto notify_cleanup;
634         }
635
636         snprintf(busystate, sizeof(busystate), "%d", event->busy_state);
637         snprintf(start, sizeof(start), "%lu", event->start);
638         snprintf(end, sizeof(end), "%lu", event->end);
639
640         chan->nativeformats = AST_FORMAT_SLINEAR;
641
642         if (!(datastore = ast_datastore_alloc(&event_notification_datastore, NULL))) {
643                 ast_log(LOG_ERROR, "Could not allocate datastore, notification not being sent!\n");
644                 goto notify_cleanup;
645         }
646
647         datastore->data = event;
648         datastore->inheritance = DATASTORE_INHERIT_FOREVER;
649
650         ao2_ref(event, +1);
651         res = ast_channel_datastore_add(chan, datastore);
652
653         if (!(apptext = ast_str_create(32))) {
654                 goto notify_cleanup;
655         }
656
657         if (!ast_strlen_zero(event->owner->notify_app)) {
658                 ast_str_set(&apptext, 0, "%s,%s", event->owner->notify_app, event->owner->notify_appdata);
659         } else {
660                 ast_str_set(&apptext, 0, "Dial,Local/%s@%s", event->owner->notify_extension, event->owner->notify_context);
661         }
662         ast_dial_option_global_enable(dial, AST_DIAL_OPTION_ANSWER_EXEC, ast_str_buffer(apptext));
663
664         ast_dial_run(dial, chan, 1);
665         res = 0;
666
667 notify_cleanup:
668         event = ast_calendar_unref_event(event);
669         if (res == -1 && dial) {
670                 ast_dial_destroy(dial);
671         }
672         if (apptext) {
673                 ast_free(apptext);
674         }
675         if (chan) {
676                 ast_channel_release(chan);
677         }
678
679         return res;
680 }
681
682 static int calendar_devstate_change(const void *data)
683 {
684         struct ast_calendar_event *event = (struct ast_calendar_event *)data;
685         struct timeval now = ast_tvnow();
686         int is_end_event;
687
688         if (!event) {
689                 ast_log(LOG_WARNING, "Event was NULL!\n");
690                 return 0;
691         }
692
693         ao2_ref(event, +1);
694
695         is_end_event = event->end <= now.tv_sec;
696
697         if (is_end_event) {
698                 event->bs_end_sched = -1;
699         } else {
700                 event->bs_start_sched = -1;
701         }
702
703         /* We can have overlapping events, so ignore the event->busy_state and check busy state
704          * based on all events in the calendar */
705         if (!calendar_is_busy(event->owner)) {
706                 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Calendar/%s", event->owner->name);
707         } else {
708                 ast_devstate_changed(AST_DEVICE_BUSY, "Calendar/%s", event->owner->name);
709         }
710
711         event = ast_calendar_unref_event(event);
712
713         return 0;
714 }
715
716 static void copy_event_data(struct ast_calendar_event *dst, struct ast_calendar_event *src)
717 {
718         struct ast_calendar_attendee *attendee;
719
720         ast_string_field_set(dst, summary, src->summary);
721         ast_string_field_set(dst, description, src->description);
722         ast_string_field_set(dst, organizer, src->organizer);
723         ast_string_field_set(dst, location, src->location);
724         ast_string_field_set(dst, uid, src->uid);
725         dst->owner = src->owner;
726         dst->start = src->start;
727         dst->end = src->end;
728         dst->alarm = src->alarm;
729         dst->busy_state = src->busy_state;
730
731         while ((attendee = AST_LIST_REMOVE_HEAD(&src->attendees, next))) {
732                 AST_LIST_INSERT_TAIL(&dst->attendees, attendee, next);
733         }
734 }
735
736 static int schedule_calendar_event(struct ast_calendar *cal, struct ast_calendar_event *old_event, struct ast_calendar_event *cmp_event)
737 {
738         struct timeval now = ast_tvnow();
739         struct ast_calendar_event *event;
740         time_t alarm_notify_sched = 0, devstate_sched_start, devstate_sched_end;
741         int changed = 0;
742
743         event = cmp_event ? cmp_event : old_event;
744
745         ao2_lock(event);
746         if (!cmp_event || old_event->alarm != event->alarm) {
747                 changed = 1;
748                 if (cal->autoreminder) {
749                         alarm_notify_sched = (event->start - (60 * cal->autoreminder) - now.tv_sec) * 1000;
750                 } else if (event->alarm) {
751                         alarm_notify_sched = (event->alarm - now.tv_sec) * 1000;
752                 }
753
754                 /* For now, send the notification if we missed it, but the meeting hasn't happened yet */
755                 if (event->start >=  now.tv_sec) {
756                         if (alarm_notify_sched <= 0) {
757                                 alarm_notify_sched = 1;
758                         }
759                         ast_mutex_lock(&refreshlock);
760                         AST_SCHED_REPLACE(old_event->notify_sched, sched, alarm_notify_sched, calendar_event_notify, old_event);
761                         ast_mutex_unlock(&refreshlock);
762                         ast_debug(3, "Calendar alarm event notification scheduled to happen in %ld ms\n", alarm_notify_sched);
763                 }
764         }
765
766         if (!cmp_event || old_event->start != event->start) {
767                 changed = 1;
768                 devstate_sched_start = (event->start - now.tv_sec) * 1000;
769
770                 if (devstate_sched_start < 1) {
771                         devstate_sched_start = 1;
772                 }
773
774                 ast_mutex_lock(&refreshlock);
775                 AST_SCHED_REPLACE(old_event->bs_start_sched, sched, devstate_sched_start, calendar_devstate_change, old_event);
776                 ast_mutex_unlock(&refreshlock);
777                 ast_debug(3, "Calendar bs_start event notification scheduled to happen in %ld ms\n", devstate_sched_start);
778         }
779
780         if (!cmp_event || old_event->end != event->end) {
781                 changed = 1;
782                 devstate_sched_end = (event->end - now.tv_sec) * 1000;
783                 ast_mutex_lock(&refreshlock);
784                 AST_SCHED_REPLACE(old_event->bs_end_sched, sched, devstate_sched_end, calendar_devstate_change, old_event);
785                 ast_mutex_unlock(&refreshlock);
786                 ast_debug(3, "Calendar bs_end event notification scheduled to happen in %ld ms\n", devstate_sched_end);
787         }
788
789         if (changed) {
790                 ast_cond_signal(&refresh_condition);
791         }
792
793         ao2_unlock(event);
794
795         return 0;
796 }
797
798 static int merge_events_cb(void *obj, void *arg, int flags)
799 {
800         struct ast_calendar_event *old_event = obj, *new_event;
801         struct ao2_container *new_events = arg;
802
803         /* If we don't find the old_event in new_events, then we can safely delete the old_event */
804         if (!(new_event = find_event(new_events, old_event->uid))) {
805                 old_event = destroy_event(old_event);
806                 return CMP_MATCH;
807         }
808
809         /* We have events to merge.  If any data that will affect a scheduler event has changed,
810          * then we need to replace the scheduler event */
811         schedule_calendar_event(old_event->owner, old_event, new_event);
812
813         /* Since we don't want to mess with cancelling sched events and adding new ones, just
814          * copy the internals of the new_event to the old_event */
815         copy_event_data(old_event, new_event);
816
817         /* Now we can go ahead and unlink the new_event from new_events and unref it so that only completely
818          * new events remain in the container */
819         ao2_unlink(new_events, new_event);
820         new_event = ast_calendar_unref_event(new_event);
821
822         return 0;
823 }
824
825 static int add_new_event_cb(void *obj, void *arg, int flags)
826 {
827         struct ast_calendar_event *new_event = obj;
828         struct ao2_container *events = arg;
829
830         ao2_link(events, new_event);
831         schedule_calendar_event(new_event->owner, new_event, NULL);
832         return CMP_MATCH;
833 }
834
835 void ast_calendar_merge_events(struct ast_calendar *cal, struct ao2_container *new_events)
836 {
837         /* Loop through all events attached to the calendar.  If there is a matching new event
838          * merge its data over and handle any schedule changes that need to be made.  Then remove
839          * the new_event from new_events so that we are left with only new_events that we can add later. */
840         ao2_callback(cal->events, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, merge_events_cb, new_events);
841
842         /* Now, we should only have completely new events in new_events.  Loop through and add them */
843         ao2_callback(new_events, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, add_new_event_cb, cal->events);
844 }
845
846
847 static int load_config(void *data)
848 {
849         struct ast_flags config_flags = { CONFIG_FLAG_FILEUNCHANGED };
850         struct ast_config *tmpcfg;
851
852         if (!(tmpcfg = ast_config_load2("calendar.conf", "calendar", config_flags)) ||
853                 tmpcfg == CONFIG_STATUS_FILEINVALID) {
854                 ast_log(LOG_ERROR, "Unable to load config calendar.conf\n");
855                 return -1;
856         }
857
858         if (tmpcfg == CONFIG_STATUS_FILEUNCHANGED) {
859                 return 0;
860         }
861
862         if (calendar_config) {
863                 ast_config_destroy(calendar_config);
864         }
865
866         calendar_config = tmpcfg;
867
868         return 0;
869 }
870
871 /*! \brief A dialplan function that can be used to determine the busy status of a calendar */
872 static int calendar_busy_exec(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
873 {
874         struct ast_calendar *cal;
875
876         if (ast_strlen_zero(data)) {
877                 ast_log(LOG_WARNING, "CALENDAR_BUSY requires an argument: CALENDAR_BUSY(<calendar_name>)\n");
878                 return -1;
879         }
880
881         cal = find_calendar(data);
882
883         if (!cal) {
884                 ast_log(LOG_WARNING, "Could not find calendar '%s'\n", data);
885                 return -1;
886         }
887
888         strcpy(buf, calendar_is_busy(cal) ? "1" : "0");
889
890         return 0;
891 }
892
893 static struct ast_custom_function calendar_busy_function = {
894     .name = "CALENDAR_BUSY",
895     .read = calendar_busy_exec,
896 };
897
898 static int add_event_to_list(struct eventlist *events, struct ast_calendar_event *event, time_t start, time_t end)
899 {
900         struct evententry *entry, *iter;
901         int event_startdiff = abs(start - event->start);
902         int event_enddiff = abs(end - event->end);
903         int i = 0;
904
905         if (!(entry = ast_calloc(1, sizeof(*entry)))) {
906                 ast_log(LOG_ERROR, "Unable to allocate memory for event list\n");
907                 return -1;
908         }
909
910         entry->event = event;
911         ao2_ref(event, +1);
912
913         if (start == end) {
914                 AST_LIST_TRAVERSE_SAFE_BEGIN(events, iter, list) {
915                         int startdiff = abs(iter->event->start - start);
916
917                         ast_debug(10, "Comparing %s with startdiff %d to %s with startdiff %d\n", event->summary, event_startdiff, iter->event->summary, startdiff);
918                         ++i;
919                         if (startdiff > event_startdiff) {
920                                 AST_LIST_INSERT_BEFORE_CURRENT(entry, list);
921                                 return i;
922                         }
923                         if (startdiff == event_startdiff) {
924                                 int enddiff = abs(iter->event->end - end);
925
926                                 if (enddiff > event_enddiff) {
927                                         AST_LIST_INSERT_BEFORE_CURRENT(entry, list);
928                                         return i;
929                                 }
930                                 if (event_startdiff == enddiff) {
931                                         if (strcmp(event->uid, iter->event->uid) < 0) {
932                                                 AST_LIST_INSERT_BEFORE_CURRENT(entry, list);
933                                                 return i;
934                                         }
935                                 }
936                         }
937                 }
938                 AST_LIST_TRAVERSE_SAFE_END;
939
940                 AST_LIST_INSERT_TAIL(events, entry, list);
941
942                 return i;
943         }
944
945         AST_LIST_TRAVERSE_SAFE_BEGIN(events, iter, list) {
946                 ++i;
947                 if (iter->event->start > event->start) {
948                         AST_LIST_INSERT_BEFORE_CURRENT(entry, list);
949                         return i;
950                 }
951
952                 if (iter->event->start == event->start) {
953                         if ((iter->event->end - iter->event->start) == (event->end - event->start)) {
954                                 if (strcmp(event->uid, iter->event->uid) < 0) {
955                                         AST_LIST_INSERT_BEFORE_CURRENT(entry, list);
956                                         return i;
957                                 }
958                         }
959                         if ((iter->event->end - iter->event->start) < (event->end - event->start)) {
960                                 AST_LIST_INSERT_BEFORE_CURRENT(entry, list);
961                                 return i;
962                         }
963                 }
964         }
965         AST_LIST_TRAVERSE_SAFE_END;
966
967         AST_LIST_INSERT_TAIL(events, entry, list);
968
969         return i;
970 }
971
972 static void eventlist_destroy(void *data)
973 {
974         struct eventlist *events = data;
975
976         ao2_ref(events, -1);
977 }
978
979 static void *eventlist_duplicate(void *data)
980 {
981         struct eventlist *events = data;
982
983         if (!events) {
984                 return NULL;
985         }
986
987         ao2_ref(events, +1);
988
989         return events;
990 }
991
992 static int calendar_query_exec(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
993 {
994         struct ast_calendar *cal;
995         struct ao2_iterator i;
996         struct ast_calendar_event *event;
997         struct eventlist *events;
998         time_t start = INT_MIN, end = INT_MAX;
999         struct ast_datastore *eventlist_datastore;
1000         AST_DECLARE_APP_ARGS(args,
1001                 AST_APP_ARG(calendar);
1002                 AST_APP_ARG(start);
1003                 AST_APP_ARG(end);
1004         );
1005
1006         if (!chan) {
1007                 ast_log(LOG_WARNING, "%s requires a channel to store the data on\n", cmd);
1008                 return -1;
1009         }
1010
1011         AST_STANDARD_APP_ARGS(args, data);
1012
1013         if (ast_strlen_zero(args.calendar)) {
1014                 ast_log(LOG_WARNING, "%s requires a calendar argument\n", cmd);
1015                 return -1;
1016         }
1017
1018         if (!(cal = find_calendar(args.calendar))) {
1019                 ast_log(LOG_WARNING, "Unknown calendar '%s'\n", args.calendar);
1020                 return -1;
1021         }
1022
1023         if (!(events = ao2_alloc(sizeof(*events), eventlist_destructor))) {
1024                 ast_log(LOG_ERROR, "Unable to allocate memory for event list\n");
1025                 cal = unref_calendar(cal);
1026                 return -1;
1027         }
1028
1029         if (!ast_strlen_zero(args.start)) {
1030                 start = atoi(args.start);
1031         }
1032
1033         if (!ast_strlen_zero(args.end)) {
1034                 end = atoi(args.end);
1035         }
1036
1037         i = ao2_iterator_init(cal->events, 0);
1038         while ((event = ao2_iterator_next(&i))) {
1039                 if (!(start > event->end || end < event->start)) {
1040                         ast_debug(10, "%s (%ld - %ld) overlapped with (%ld - %ld)\n", event->summary, event->start, event->end, start, end);
1041                         if (add_event_to_list(events, event, start, end) < 0) {
1042                                 event = ast_calendar_unref_event(event);
1043                                 return -1;
1044                         }
1045                 }
1046
1047                 event = ast_calendar_unref_event(event);
1048         }
1049
1050         ast_channel_lock(chan);
1051         do {
1052                 generate_random_string(buf, len);
1053         } while (ast_channel_datastore_find(chan, &eventlist_datastore_info, buf));
1054         ast_channel_unlock(chan);
1055
1056         if (!(eventlist_datastore = ast_datastore_alloc(&eventlist_datastore_info, buf))) {
1057                 ast_log(LOG_ERROR, "Could not allocate datastore!\n");
1058                 return -1;
1059         }
1060
1061         eventlist_datastore->inheritance = DATASTORE_INHERIT_FOREVER;
1062         eventlist_datastore->data = events;
1063
1064         ast_channel_lock(chan);
1065         ast_channel_datastore_add(chan, eventlist_datastore);
1066         ast_channel_unlock(chan);
1067
1068         return 0;
1069 }
1070
1071 static struct ast_custom_function calendar_query_function = {
1072     .name = "CALENDAR_QUERY",
1073     .read = calendar_query_exec,
1074 };
1075
1076 static void calendar_join_attendees(struct ast_calendar_event *event, char *buf, size_t len)
1077 {
1078         struct ast_str *tmp;
1079         struct ast_calendar_attendee *attendee;
1080
1081         if (!(tmp = ast_str_create(32))) {
1082                 ast_log(LOG_ERROR, "Could not allocate memory for attendees!\n");
1083                 return;
1084         }
1085
1086         AST_LIST_TRAVERSE(&event->attendees, attendee, next) {
1087                 ast_str_append(&tmp, 0, "%s%s", attendee == AST_LIST_FIRST(&event->attendees) ? "" : ",", attendee->data);
1088         }
1089
1090         ast_copy_string(buf, ast_str_buffer(tmp), len);
1091         ast_free(tmp);
1092 }
1093
1094 static int calendar_query_result_exec(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1095 {
1096         struct ast_datastore *datastore;
1097         struct eventlist *events;
1098         struct evententry *entry;
1099         int row = 1;
1100         AST_DECLARE_APP_ARGS(args,
1101                 AST_APP_ARG(id);
1102                 AST_APP_ARG(field);
1103                 AST_APP_ARG(row);
1104         );
1105
1106         if (!chan) {
1107                 ast_log(LOG_WARNING, "%s requires a channel\n", cmd);
1108                 return -1;
1109         }
1110
1111         AST_STANDARD_APP_ARGS(args, data);
1112
1113         if (ast_strlen_zero(args.id) || ast_strlen_zero(args.field)) {
1114                 ast_log(LOG_WARNING, "%s requires an id and a field", cmd);
1115                 return -1;
1116         }
1117
1118         ast_channel_lock(chan);
1119         if (!(datastore = ast_channel_datastore_find(chan, &eventlist_datastore_info, args.id))) {
1120                 ast_log(LOG_WARNING, "There is no event notification datastore with id '%s' on '%s'!\n", args.id, chan->name);
1121                 ast_channel_unlock(chan);
1122                 return -1;
1123         }
1124         ast_channel_unlock(chan);
1125
1126         if (!(events = datastore->data)) {
1127                 ast_log(LOG_WARNING, "The datastore contains no data!\n");
1128                 return -1;
1129         }
1130
1131         if (!ast_strlen_zero(args.row)) {
1132                 row = atoi(args.row);
1133         }
1134
1135         AST_LIST_TRAVERSE(events, entry, list) {
1136                 if (--row) {
1137                         continue;
1138                 }
1139                 if (!strcasecmp(args.field, "summary")) {
1140                         ast_copy_string(buf, entry->event->summary, len);
1141                 } else if (!strcasecmp(args.field, "description")) {
1142                         ast_copy_string(buf, entry->event->description, len);
1143                 } else if (!strcasecmp(args.field, "organizer")) {
1144                         ast_copy_string(buf, entry->event->organizer, len);
1145                 } else if (!strcasecmp(args.field, "location")) {
1146                         ast_copy_string(buf, entry->event->location, len);
1147                 } else if (!strcasecmp(args.field, "calendar")) {
1148                         ast_copy_string(buf, entry->event->owner->name, len);
1149                 } else if (!strcasecmp(args.field, "uid")) {
1150                         ast_copy_string(buf, entry->event->uid, len);
1151                 } else if (!strcasecmp(args.field, "start")) {
1152                         snprintf(buf, len, "%ld", entry->event->start);
1153                 } else if (!strcasecmp(args.field, "end")) {
1154                         snprintf(buf, len, "%ld", entry->event->end);
1155                 } else if (!strcasecmp(args.field, "busystate")) {
1156                         snprintf(buf, len, "%d", entry->event->busy_state);
1157                 } else if (!strcasecmp(args.field, "attendees")) {
1158                         calendar_join_attendees(entry->event, buf, len);
1159                 } else {
1160                         ast_log(LOG_WARNING, "Unknown field '%s'\n", args.field);
1161                 }
1162                 break;
1163         }
1164
1165         return 0;
1166 }
1167
1168 static struct ast_custom_function calendar_query_result_function = {
1169         .name = "CALENDAR_QUERY_RESULT",
1170         .read = calendar_query_result_exec,
1171 };
1172
1173 static int calendar_write_exec(struct ast_channel *chan, const char *cmd, char *data, const char *value)
1174 {
1175         int i, j, ret = -1;
1176         char *val_dup = NULL;
1177         struct ast_calendar *cal = NULL;
1178         struct ast_calendar_event *event = NULL;
1179         AST_DECLARE_APP_ARGS(fields,
1180                 AST_APP_ARG(field)[10];
1181         );
1182         AST_DECLARE_APP_ARGS(values,
1183                 AST_APP_ARG(value)[10];
1184         );
1185
1186         if (!(val_dup = ast_strdup(value))) {
1187                 ast_log(LOG_ERROR, "Could not allocate memory for values\n");
1188                 return -1;
1189         }
1190
1191         AST_STANDARD_APP_ARGS(fields, data);
1192         AST_STANDARD_APP_ARGS(values, val_dup);
1193
1194         /* XXX Eventually we will support unnamed calendars, so if we don't find one, we parse
1195          * for a calendar type and create it */
1196         if (!(cal = find_calendar(fields.field[0]))) {
1197                 ast_log(LOG_WARNING, "Couldn't find calendar '%s'\n", fields.field[0]);
1198                 goto write_cleanup;
1199         }
1200
1201         if (!(cal->tech->write_event)) {
1202                 ast_log(LOG_WARNING, "Calendar '%s' has no write function!\n", cal->name);
1203                 goto write_cleanup;
1204         }
1205
1206         if (!(event = ast_calendar_event_alloc(cal))) {
1207                 goto write_cleanup;
1208         }
1209
1210         if (ast_strlen_zero(fields.field[0])) {
1211                 ast_log(LOG_WARNING, "CALENDAR_WRITE requires a calendar name!\n");
1212                 goto write_cleanup;
1213         }
1214
1215         if (fields.argc - 1 != values.argc) {
1216                 ast_log(LOG_WARNING, "CALENDAR_WRITE should have the same number of fields (%d) and values (%d)!\n", fields.argc - 1, values.argc);
1217                 goto write_cleanup;
1218         }
1219
1220         event->owner = cal;
1221
1222         for (i = 1, j = 0; i < fields.argc; i++, j++) {
1223                 if (!strcasecmp(fields.field[i], "summary")) {
1224                         ast_string_field_set(event, summary, values.value[j]);
1225                 } else if (!strcasecmp(fields.field[i], "description")) {
1226                         ast_string_field_set(event, description, values.value[j]);
1227                 } else if (!strcasecmp(fields.field[i], "organizer")) {
1228                         ast_string_field_set(event, organizer, values.value[j]);
1229                 } else if (!strcasecmp(fields.field[i], "location")) {
1230                         ast_string_field_set(event, location, values.value[j]);
1231                 } else if (!strcasecmp(fields.field[i], "uid")) {
1232                         ast_string_field_set(event, uid, values.value[j]);
1233                 } else if (!strcasecmp(fields.field[i], "start")) {
1234                         event->start = atoi(values.value[j]);
1235                 } else if (!strcasecmp(fields.field[i], "end")) {
1236                         event->end = atoi(values.value[j]);
1237                 } else if (!strcasecmp(fields.field[i], "busystate")) {
1238                         event->busy_state = atoi(values.value[j]);
1239                 } else {
1240                         ast_log(LOG_WARNING, "Unknown calendar event field '%s'\n", fields.field[i]);
1241                 }
1242         }
1243
1244         if((ret = cal->tech->write_event(event))) {
1245                 ast_log(LOG_WARNING, "Writing event to calendar '%s' failed!\n", cal->name);
1246         }
1247
1248 write_cleanup:
1249         if (cal) {
1250                 cal = unref_calendar(cal);
1251         }
1252         if (event) {
1253                 event = ast_calendar_unref_event(event);
1254         }
1255         if (val_dup) {
1256                 ast_free(val_dup);
1257         }
1258
1259         return ret;
1260 }
1261
1262 static struct ast_custom_function calendar_write_function = {
1263         .name = "CALENDAR_WRITE",
1264         .write = calendar_write_exec,
1265 };
1266
1267 /*! \brief CLI command to list available calendars */
1268 static char *handle_show_calendars(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1269 {
1270 #define FORMAT "%-20.20s %-10.10s %-6.6s\n"
1271         struct ao2_iterator i;
1272         struct ast_calendar *cal;
1273
1274         switch(cmd) {
1275         case CLI_INIT:
1276                 e->command = "calendar show calendars";
1277                 e->usage =
1278                         "Usage: calendar show calendars\n"
1279                         "       Lists all registered calendars.\n";
1280                 return NULL;
1281         case CLI_GENERATE:
1282                 return NULL;
1283         }
1284
1285         ast_cli(a->fd, FORMAT, "Calendar", "Type", "Status");
1286         ast_cli(a->fd, FORMAT, "--------", "----", "------");
1287         i = ao2_iterator_init(calendars, 0);
1288         while ((cal = ao2_iterator_next(&i))) {
1289                 ast_cli(a->fd, FORMAT, cal->name, cal->tech->type, calendar_is_busy(cal) ? "busy" : "free");
1290                 cal = unref_calendar(cal);
1291         }
1292
1293         return CLI_SUCCESS;
1294 #undef FORMAT
1295 }
1296
1297 static char *epoch_to_string(char *buf, size_t buflen, time_t epoch)
1298 {
1299         struct ast_tm tm;
1300         struct timeval tv = {
1301                 .tv_sec = epoch,
1302         };
1303
1304         if (!epoch) {
1305                 *buf = '\0';
1306                 return buf;
1307         }
1308         ast_localtime(&tv, &tm, NULL);
1309         ast_strftime(buf, buflen, "%F %r", &tm);
1310
1311         return buf;
1312 }
1313
1314 static char *handle_show_calendar(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1315 {
1316 #define FORMAT "%-17.17s : %-20.20s\n"
1317 #define FORMAT2 "%-12.12s: %-40.60s\n"
1318         struct ao2_iterator i;
1319         struct ast_calendar *cal;
1320         struct ast_calendar_event *event;
1321         int which = 0;
1322         char *ret = NULL;
1323
1324         switch(cmd) {
1325         case CLI_INIT:
1326                 e->command = "calendar show calendar";
1327                 e->usage =
1328                         "Usage: calendar show calendar <calendar name>\n"
1329                         "       Displays information about a calendar\n";
1330                 return NULL;
1331
1332         case CLI_GENERATE:
1333                 if (a->pos != 3) {
1334                         return NULL;
1335                 }
1336                 i = ao2_iterator_init(calendars, 0);
1337                 while ((cal = ao2_iterator_next(&i))) {
1338                         if (!strncasecmp(a->word, cal->name, strlen(a->word)) && ++which > a->n) {
1339                                 ret = ast_strdup(cal->name);
1340                                 cal = unref_calendar(cal);
1341                                 break;
1342                         }
1343                         cal = unref_calendar(cal);
1344                 }
1345                 return ret;
1346         }
1347
1348         if (a->argc != 4) {
1349                 return CLI_SHOWUSAGE;
1350         }
1351
1352         if (!(cal = find_calendar(a->argv[3]))) {
1353                 return NULL;
1354         }
1355
1356         ast_cli(a->fd, FORMAT, "Name", cal->name);
1357         ast_cli(a->fd, FORMAT, "Notify channel", cal->notify_channel);
1358         ast_cli(a->fd, FORMAT, "Notify context", cal->notify_context);
1359         ast_cli(a->fd, FORMAT, "Notify extension", cal->notify_extension);
1360         ast_cli(a->fd, FORMAT, "Notify application", cal->notify_app);
1361         ast_cli(a->fd, FORMAT, "Notify appdata", cal->notify_appdata);
1362         ast_cli(a->fd, "%-17.17s : %d\n", "Refresh time", cal->refresh);
1363         ast_cli(a->fd, "%-17.17s : %d\n", "Timeframe", cal->timeframe);
1364         ast_cli(a->fd, "%-17.17s : %d\n", "Autoreminder", cal->autoreminder);
1365         ast_cli(a->fd, "%s\n", "Events");
1366         ast_cli(a->fd, "%s\n", "------");
1367
1368         i = ao2_iterator_init(cal->events, 0);
1369         while ((event = ao2_iterator_next(&i))) {
1370                 char buf[100];
1371
1372                 ast_cli(a->fd, FORMAT2, "Summary", event->summary);
1373                 ast_cli(a->fd, FORMAT2, "Description", event->description);
1374                 ast_cli(a->fd, FORMAT2, "Organizer", event->organizer);
1375                 ast_cli(a->fd, FORMAT2, "Location", event->location);
1376                 ast_cli(a->fd, FORMAT2, "UID", event->uid);
1377                 ast_cli(a->fd, FORMAT2, "Start", epoch_to_string(buf, sizeof(buf), event->start));
1378                 ast_cli(a->fd, FORMAT2, "End", epoch_to_string(buf, sizeof(buf), event->end));
1379                 ast_cli(a->fd, FORMAT2, "Alarm", epoch_to_string(buf, sizeof(buf), event->alarm));
1380                 ast_cli(a->fd, "\n");
1381
1382                 event = ast_calendar_unref_event(event);
1383         }
1384         cal = unref_calendar(cal);
1385         return CLI_SUCCESS;
1386 #undef FORMAT
1387 #undef FORMAT2
1388 }
1389
1390 static char *handle_dump_sched(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1391 {
1392         switch(cmd) {
1393         case CLI_INIT:
1394                 e->command = "calendar dump sched";
1395                 e->usage =
1396                         "Usage: calendar dump sched\n"
1397                         "       Dump the calendar sched context";
1398                 return NULL;
1399
1400         case CLI_GENERATE:
1401                 return NULL;
1402         }
1403
1404         ast_sched_dump(sched);
1405
1406         return CLI_SUCCESS;
1407 }
1408
1409 static struct ast_cli_entry calendar_cli[] = {
1410         AST_CLI_DEFINE(handle_show_calendar, "Display information about a calendar"),
1411         AST_CLI_DEFINE(handle_show_calendars, "Show registered calendars"),
1412         AST_CLI_DEFINE(handle_dump_sched, "Dump calendar sched context"),
1413 };
1414
1415 static int calendar_event_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1416 {
1417         struct ast_datastore *datastore;
1418         struct ast_calendar_event *event;
1419
1420         if (ast_strlen_zero(data)) {
1421                 ast_log(LOG_WARNING, "%s requires an argument\n", cmd);
1422                 return -1;
1423         }
1424
1425         ast_channel_lock(chan);
1426         if (!(datastore = ast_channel_datastore_find(chan, &event_notification_datastore, NULL))) {
1427                 ast_log(LOG_WARNING, "There is no event notification datastore on '%s'!\n", chan->name);
1428                 ast_channel_unlock(chan);
1429                 return -1;
1430         }
1431         ast_channel_unlock(chan);
1432
1433         if (!(event = datastore->data)) {
1434                 ast_log(LOG_WARNING, "The datastore contains no data!\n");
1435                 return -1;
1436         }
1437
1438         if (!strcasecmp(data, "summary")) {
1439                 ast_copy_string(buf, event->summary, len);
1440         } else if (!strcasecmp(data, "description")) {
1441                 ast_copy_string(buf, event->description, len);
1442         } else if (!strcasecmp(data, "organizer")) {
1443                 ast_copy_string(buf, event->organizer, len);
1444         } else if (!strcasecmp(data, "location")) {
1445                 ast_copy_string(buf, event->location, len);
1446         } else if (!strcasecmp(data, "calendar")) {
1447                 ast_copy_string(buf, event->owner->name, len);
1448         } else if (!strcasecmp(data, "uid")) {
1449                 ast_copy_string(buf, event->uid, len);
1450         } else if (!strcasecmp(data, "start")) {
1451                 snprintf(buf, len, "%ld", (long)event->start);
1452         } else if (!strcasecmp(data, "end")) {
1453                 snprintf(buf, len, "%ld", (long)event->end);
1454         } else if (!strcasecmp(data, "busystate")) {
1455                 snprintf(buf, len, "%d", event->busy_state);
1456         } else if (!strcasecmp(data, "attendees")) {
1457                 calendar_join_attendees(event, buf, len);
1458         }
1459
1460
1461         return 0;
1462 }
1463
1464 static struct ast_custom_function calendar_event_function = {
1465         .name = "CALENDAR_EVENT",
1466         .read = calendar_event_read,
1467 };
1468
1469 static int cb_pending_deletion(void *user_data, void *arg, int flags)
1470 {
1471         struct ast_calendar *cal = user_data;
1472
1473         cal->pending_deletion = 1;
1474
1475         return CMP_MATCH;
1476 }
1477
1478 static int cb_rm_pending_deletion(void *user_data, void *arg, int flags)
1479 {
1480         struct ast_calendar *cal = user_data;
1481
1482         return cal->pending_deletion ? CMP_MATCH : 0;
1483 }
1484
1485 static int reload(void)
1486 {
1487         struct ast_calendar_tech *iter;
1488
1489         ast_mutex_lock(&reloadlock);
1490
1491         /* Mark existing calendars for deletion */
1492         ao2_callback(calendars, OBJ_NODATA | OBJ_MULTIPLE, cb_pending_deletion, NULL);
1493         load_config(NULL);
1494
1495         AST_LIST_LOCK(&techs);
1496         AST_LIST_TRAVERSE(&techs, iter, list) {
1497                 if (load_tech_calendars(iter)) {
1498                         ast_log(LOG_WARNING, "Failed to reload %s calendars, module disabled\n", iter->type);
1499                 }
1500         }
1501         AST_LIST_UNLOCK(&techs);
1502
1503         /* Delete calendars that no longer show up in the config */
1504         ao2_callback(calendars, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, cb_rm_pending_deletion, NULL);
1505
1506         ast_mutex_unlock(&reloadlock);
1507
1508         return 0;
1509 }
1510
1511 static void *do_refresh(void *data)
1512 {
1513         for (;;) {
1514                 struct timeval now = ast_tvnow();
1515                 struct timespec ts = {0,};
1516                 int res, wait;
1517
1518                 ast_mutex_lock(&refreshlock);
1519
1520                 if ((wait = ast_sched_wait(sched)) < 0) {
1521                         wait = 1000;
1522                 }
1523
1524                 ts.tv_sec = (now.tv_sec + wait / 1000) + 1;
1525                 res = ast_cond_timedwait(&refresh_condition, &refreshlock, &ts);
1526
1527                 ast_mutex_unlock(&refreshlock);
1528
1529                 ast_sched_runq(sched);
1530         }
1531
1532         return NULL;
1533 }
1534
1535 /* If I were to allow unloading it would look something like this */
1536 static int unload_module(void)
1537 {
1538         struct ast_calendar_tech *tech;
1539
1540         ast_devstate_prov_del("calendar");
1541         ast_custom_function_unregister(&calendar_busy_function);
1542         ast_custom_function_unregister(&calendar_event_function);
1543         ast_custom_function_unregister(&calendar_query_function);
1544         ast_custom_function_unregister(&calendar_query_result_function);
1545         ast_custom_function_unregister(&calendar_write_function);
1546         ast_cli_unregister_multiple(calendar_cli, ARRAY_LEN(calendar_cli));
1547
1548         /* Remove all calendars */
1549         ao2_callback(calendars, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL);
1550
1551         AST_LIST_LOCK(&techs);
1552         AST_LIST_TRAVERSE_SAFE_BEGIN(&techs, tech, list) {
1553                 ast_unload_resource(tech->module, 0);
1554         }
1555         AST_LIST_TRAVERSE_SAFE_END;
1556         AST_LIST_UNLOCK(&techs);
1557
1558         return 0;
1559 }
1560
1561 static int load_module(void)
1562 {
1563         if (load_config(NULL)) {
1564                 /* We don't have calendar support enabled */
1565                 return 0;
1566         }
1567
1568         if (!(calendars = ao2_container_alloc(CALENDAR_BUCKETS, calendar_hash_fn, calendar_cmp_fn))) {
1569                 ast_log(LOG_ERROR, "Unable to allocate calendars container!\n");
1570                 return -1;
1571         }
1572
1573         ast_mutex_init(&refreshlock);
1574         ast_cond_init(&refresh_condition, NULL);
1575         ast_mutex_init(&reloadlock);
1576
1577         if (!(sched = sched_context_create())) {
1578                 ast_log(LOG_ERROR, "Unable to create sched context\n");
1579                 return -1;
1580         }
1581
1582         if (ast_pthread_create_background(&refresh_thread, NULL, do_refresh, NULL) < 0) {
1583                 ast_log(LOG_ERROR, "Unable to start refresh thread--notifications disabled!\n");
1584         }
1585
1586         ast_custom_function_register(&calendar_busy_function);
1587         ast_custom_function_register(&calendar_event_function);
1588         ast_custom_function_register(&calendar_query_function);
1589         ast_custom_function_register(&calendar_query_result_function);
1590         ast_custom_function_register(&calendar_write_function);
1591         ast_cli_register_multiple(calendar_cli, ARRAY_LEN(calendar_cli));
1592
1593         ast_devstate_prov_add("Calendar", calendarstate);
1594
1595         /* Since other modules depend on this, disable unloading */
1596         ast_module_ref(ast_module_info->self);
1597
1598         return 0;
1599 }
1600 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Asterisk Calendar integration",
1601                 .load = load_module,
1602                 .unload = unload_module,
1603                 .reload = reload,
1604         );