Wed Jan 8 2020 09:49:49

Asterisk developer's documentation


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