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