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