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