00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030 #include "asterisk.h"
00031
00032 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 413586 $")
00033
00034 #include "asterisk/_private.h"
00035 #include "asterisk/calendar.h"
00036 #include "asterisk/utils.h"
00037 #include "asterisk/astobj2.h"
00038 #include "asterisk/module.h"
00039 #include "asterisk/config.h"
00040 #include "asterisk/channel.h"
00041 #include "asterisk/devicestate.h"
00042 #include "asterisk/linkedlists.h"
00043 #include "asterisk/sched.h"
00044 #include "asterisk/dial.h"
00045 #include "asterisk/cli.h"
00046 #include "asterisk/pbx.h"
00047 #include "asterisk/app.h"
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199 #define CALENDAR_BUCKETS 19
00200
00201 static struct ao2_container *calendars;
00202 static struct sched_context *sched;
00203 static pthread_t refresh_thread = AST_PTHREADT_NULL;
00204 static ast_mutex_t refreshlock;
00205 static ast_cond_t refresh_condition;
00206 static ast_mutex_t reloadlock;
00207 static int module_unloading;
00208
00209 static void event_notification_destroy(void *data);
00210 static void *event_notification_duplicate(void *data);
00211 static void eventlist_destroy(void *data);
00212 static void *eventlist_duplicate(void *data);
00213
00214 static const struct ast_datastore_info event_notification_datastore = {
00215 .type = "EventNotification",
00216 .destroy = event_notification_destroy,
00217 .duplicate = event_notification_duplicate,
00218 };
00219
00220 static const struct ast_datastore_info eventlist_datastore_info = {
00221 .type = "CalendarEventList",
00222 .destroy = eventlist_destroy,
00223 .duplicate = eventlist_duplicate,
00224 };
00225
00226 struct evententry {
00227 struct ast_calendar_event *event;
00228 AST_LIST_ENTRY(evententry) list;
00229 };
00230
00231 static AST_LIST_HEAD_STATIC(techs, ast_calendar_tech);
00232 AST_LIST_HEAD_NOLOCK(eventlist, evententry);
00233
00234 static struct ast_config *calendar_config;
00235 AST_RWLOCK_DEFINE_STATIC(config_lock);
00236
00237 const struct ast_config *ast_calendar_config_acquire(void)
00238 {
00239 ast_rwlock_rdlock(&config_lock);
00240
00241 if (!calendar_config) {
00242 ast_rwlock_unlock(&config_lock);
00243 return NULL;
00244 }
00245
00246 return calendar_config;
00247 }
00248
00249 void ast_calendar_config_release(void)
00250 {
00251 ast_rwlock_unlock(&config_lock);
00252 }
00253
00254 static struct ast_calendar *unref_calendar(struct ast_calendar *cal)
00255 {
00256 ao2_ref(cal, -1);
00257 return NULL;
00258 }
00259
00260 static int calendar_hash_fn(const void *obj, const int flags)
00261 {
00262 const struct ast_calendar *cal = obj;
00263 return ast_str_case_hash(cal->name);
00264 }
00265
00266 static int calendar_cmp_fn(void *obj, void *arg, int flags)
00267 {
00268 const struct ast_calendar *one = obj, *two = arg;
00269 return !strcasecmp(one->name, two->name) ? CMP_MATCH | CMP_STOP: 0;
00270 }
00271
00272 static struct ast_calendar *find_calendar(const char *name)
00273 {
00274 struct ast_calendar tmp = {
00275 .name = name,
00276 };
00277 return ao2_find(calendars, &tmp, OBJ_POINTER);
00278 }
00279
00280 static int event_hash_fn(const void *obj, const int flags)
00281 {
00282 const struct ast_calendar_event *event = obj;
00283 return ast_str_hash(event->uid);
00284 }
00285
00286 static int event_cmp_fn(void *obj, void *arg, int flags)
00287 {
00288 const struct ast_calendar_event *one = obj, *two = arg;
00289 return !strcmp(one->uid, two->uid) ? CMP_MATCH | CMP_STOP : 0;
00290 }
00291
00292 static struct ast_calendar_event *find_event(struct ao2_container *events, const char *uid)
00293 {
00294 struct ast_calendar_event tmp = {
00295 .uid = uid,
00296 };
00297 return ao2_find(events, &tmp, OBJ_POINTER);
00298 }
00299
00300 struct ast_calendar_event *ast_calendar_unref_event(struct ast_calendar_event *event)
00301 {
00302 ao2_ref(event, -1);
00303 return NULL;
00304 }
00305
00306 static void calendar_destructor(void *obj)
00307 {
00308 struct ast_calendar *cal = obj;
00309
00310 ast_debug(3, "Destroying calendar %s\n", cal->name);
00311
00312 ao2_lock(cal);
00313 cal->unloading = 1;
00314 ast_cond_signal(&cal->unload);
00315 pthread_join(cal->thread, NULL);
00316 if (cal->tech_pvt) {
00317 cal->tech_pvt = cal->tech->unref_calendar(cal->tech_pvt);
00318 }
00319 ast_calendar_clear_events(cal);
00320 ast_string_field_free_memory(cal);
00321 ao2_ref(cal->events, -1);
00322 ao2_unlock(cal);
00323 }
00324
00325 static void eventlist_destructor(void *obj)
00326 {
00327 struct eventlist *events = obj;
00328 struct evententry *entry;
00329
00330 while ((entry = AST_LIST_REMOVE_HEAD(events, list))) {
00331 ao2_ref(entry->event, -1);
00332 ast_free(entry);
00333 }
00334 }
00335
00336 static int calendar_busy_callback(void *obj, void *arg, int flags)
00337 {
00338 struct ast_calendar_event *event = obj;
00339 int *is_busy = arg;
00340 struct timeval tv = ast_tvnow();
00341
00342 if (tv.tv_sec >= event->start && tv.tv_sec <= event->end && event->busy_state > AST_CALENDAR_BS_FREE) {
00343 *is_busy = 1;
00344 return CMP_STOP;
00345 }
00346
00347 return 0;
00348 }
00349
00350 static int calendar_is_busy(struct ast_calendar *cal)
00351 {
00352 int is_busy = 0;
00353
00354 ao2_callback(cal->events, OBJ_NODATA, calendar_busy_callback, &is_busy);
00355
00356 return is_busy;
00357 }
00358
00359 static enum ast_device_state calendarstate(const char *data)
00360 {
00361 enum ast_device_state state;
00362 struct ast_calendar *cal;
00363
00364 if (ast_strlen_zero(data) || (!(cal = find_calendar(data)))) {
00365 return AST_DEVICE_INVALID;
00366 }
00367
00368 if (cal->tech->is_busy) {
00369 state = cal->tech->is_busy(cal) ? AST_DEVICE_INUSE : AST_DEVICE_NOT_INUSE;
00370 } else {
00371 state = calendar_is_busy(cal) ? AST_DEVICE_INUSE : AST_DEVICE_NOT_INUSE;
00372 }
00373
00374 cal = unref_calendar(cal);
00375 return state;
00376 }
00377
00378 static struct ast_calendar *build_calendar(struct ast_config *cfg, const char *cat, const struct ast_calendar_tech *tech)
00379 {
00380 struct ast_calendar *cal;
00381 struct ast_variable *v;
00382 int new_calendar = 0;
00383
00384 if (!(cal = find_calendar(cat))) {
00385 new_calendar = 1;
00386 if (!(cal = ao2_alloc(sizeof(*cal), calendar_destructor))) {
00387 ast_log(LOG_ERROR, "Could not allocate calendar structure. Stopping.\n");
00388 return NULL;
00389 }
00390
00391 if (!(cal->events = ao2_container_alloc(CALENDAR_BUCKETS, event_hash_fn, event_cmp_fn))) {
00392 ast_log(LOG_ERROR, "Could not allocate events container for %s\n", cat);
00393 cal = unref_calendar(cal);
00394 return NULL;
00395 }
00396
00397 if (ast_string_field_init(cal, 32)) {
00398 ast_log(LOG_ERROR, "Couldn't create string fields for %s\n", cat);
00399 cal = unref_calendar(cal);
00400 return NULL;
00401 }
00402 } else {
00403 cal->pending_deletion = 0;
00404 }
00405
00406 ast_string_field_set(cal, name, cat);
00407 cal->tech = tech;
00408
00409 cal->refresh = 3600;
00410 cal->timeframe = 60;
00411 cal->notify_waittime = 30000;
00412
00413 for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
00414 if (!strcasecmp(v->name, "autoreminder")) {
00415 cal->autoreminder = atoi(v->value);
00416 } else if (!strcasecmp(v->name, "channel")) {
00417 ast_string_field_set(cal, notify_channel, v->value);
00418 } else if (!strcasecmp(v->name, "context")) {
00419 ast_string_field_set(cal, notify_context, v->value);
00420 } else if (!strcasecmp(v->name, "extension")) {
00421 ast_string_field_set(cal, notify_extension, v->value);
00422 } else if (!strcasecmp(v->name, "waittime")) {
00423 int i = atoi(v->value);
00424 if (i > 0) {
00425 cal->notify_waittime = 1000 * i;
00426 }
00427 } else if (!strcasecmp(v->name, "app")) {
00428 ast_string_field_set(cal, notify_app, v->value);
00429 } else if (!strcasecmp(v->name, "appdata")) {
00430 ast_string_field_set(cal, notify_appdata, v->value);
00431 } else if (!strcasecmp(v->name, "refresh")) {
00432 cal->refresh = atoi(v->value);
00433 } else if (!strcasecmp(v->name, "timeframe")) {
00434 cal->timeframe = atoi(v->value);
00435 }
00436 }
00437
00438 if (new_calendar) {
00439 cal->thread = AST_PTHREADT_NULL;
00440 ast_cond_init(&cal->unload, NULL);
00441 ao2_link(calendars, cal);
00442 if (ast_pthread_create(&cal->thread, NULL, cal->tech->load_calendar, cal)) {
00443
00444
00445
00446 ao2_unlink(calendars, cal);
00447 cal = unref_calendar(cal);
00448 }
00449 }
00450
00451 return cal;
00452 }
00453
00454 static int load_tech_calendars(struct ast_calendar_tech *tech)
00455 {
00456 struct ast_calendar *cal;
00457 const char *cat = NULL;
00458 const char *val;
00459
00460 if (!calendar_config) {
00461 ast_log(LOG_WARNING, "Calendar support disabled, not loading %s calendar module\n", tech->type);
00462 return -1;
00463 }
00464
00465 ast_rwlock_wrlock(&config_lock);
00466 while ((cat = ast_category_browse(calendar_config, cat))) {
00467 if (!strcasecmp(cat, "general")) {
00468 continue;
00469 }
00470
00471 if (!(val = ast_variable_retrieve(calendar_config, cat, "type")) || strcasecmp(val, tech->type)) {
00472 continue;
00473 }
00474
00475
00476 if (!(cal = build_calendar(calendar_config, cat, tech))) {
00477 ast_calendar_unregister(tech);
00478 ast_rwlock_unlock(&config_lock);
00479 return -1;
00480 }
00481
00482 cal = unref_calendar(cal);
00483 }
00484
00485 ast_rwlock_unlock(&config_lock);
00486
00487 return 0;
00488 }
00489
00490 int ast_calendar_register(struct ast_calendar_tech *tech)
00491 {
00492 struct ast_calendar_tech *iter;
00493
00494 AST_LIST_LOCK(&techs);
00495 AST_LIST_TRAVERSE(&techs, iter, list) {
00496 if(!strcasecmp(tech->type, iter->type)) {
00497 ast_log(LOG_WARNING, "Already have a handler for calendar type '%s'\n", tech->type);
00498 AST_LIST_UNLOCK(&techs);
00499 return -1;
00500 }
00501 }
00502 AST_LIST_INSERT_HEAD(&techs, tech, list);
00503 tech->user = ast_module_user_add(NULL);
00504 AST_LIST_UNLOCK(&techs);
00505
00506 ast_verb(2, "Registered calendar type '%s' (%s)\n", tech->type, tech->description);
00507
00508 return load_tech_calendars(tech);
00509 }
00510
00511 static int match_caltech_cb(void *user_data, void *arg, int flags)
00512 {
00513 struct ast_calendar *cal = user_data;
00514 struct ast_calendar_tech *tech = arg;
00515
00516 if (cal->tech == tech) {
00517 return CMP_MATCH;
00518 }
00519
00520 return 0;
00521 }
00522
00523 void ast_calendar_unregister(struct ast_calendar_tech *tech)
00524 {
00525 struct ast_calendar_tech *iter;
00526
00527 AST_LIST_LOCK(&techs);
00528 AST_LIST_TRAVERSE_SAFE_BEGIN(&techs, iter, list) {
00529 if (iter != tech) {
00530 continue;
00531 }
00532
00533 ao2_callback(calendars, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, match_caltech_cb, tech);
00534
00535 AST_LIST_REMOVE_CURRENT(list);
00536 ast_module_user_remove(iter->user);
00537 ast_verb(2, "Unregistered calendar type '%s'\n", tech->type);
00538 break;
00539 }
00540 AST_LIST_TRAVERSE_SAFE_END;
00541 AST_LIST_UNLOCK(&techs);
00542
00543 }
00544
00545 static void calendar_event_destructor(void *obj)
00546 {
00547 struct ast_calendar_event *event = obj;
00548 struct ast_calendar_attendee *attendee;
00549
00550 ast_debug(3, "Destroying event for calendar '%s'\n", event->owner->name);
00551 ast_string_field_free_memory(event);
00552 while ((attendee = AST_LIST_REMOVE_HEAD(&event->attendees, next))) {
00553 if (attendee->data) {
00554 ast_free(attendee->data);
00555 }
00556 ast_free(attendee);
00557 }
00558 }
00559
00560
00561
00562 static struct ast_calendar_event *destroy_event(struct ast_calendar_event *event)
00563 {
00564 if (event->notify_sched > -1 && ast_sched_del(sched, event->notify_sched)) {
00565 ast_debug(3, "Notification running, can't delete sched entry\n");
00566 }
00567 if (event->bs_start_sched > -1 && ast_sched_del(sched, event->bs_start_sched)) {
00568 ast_debug(3, "Devicestate update (start) running, can't delete sched entry\n");
00569 }
00570 if (event->bs_end_sched > -1 && ast_sched_del(sched, event->bs_end_sched)) {
00571 ast_debug(3, "Devicestate update (end) running, can't delete sched entry\n");
00572 }
00573
00574
00575
00576 if (event->bs_start_sched < 0 && event->bs_end_sched >= 0) {
00577 if (!calendar_is_busy(event->owner)) {
00578 ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Calendar:%s", event->owner->name);
00579 } else {
00580 ast_devstate_changed(AST_DEVICE_BUSY, AST_DEVSTATE_CACHABLE, "Calendar:%s", event->owner->name);
00581 }
00582 }
00583
00584 return NULL;
00585 }
00586
00587 static int clear_events_cb(void *user_data, void *arg, int flags)
00588 {
00589 struct ast_calendar_event *event = user_data;
00590
00591 event = destroy_event(event);
00592
00593 return CMP_MATCH;
00594 }
00595
00596 void ast_calendar_clear_events(struct ast_calendar *cal)
00597 {
00598 ast_debug(3, "Clearing all events for calendar %s\n", cal->name);
00599
00600 ao2_callback(cal->events, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, clear_events_cb, NULL);
00601 }
00602
00603 struct ast_calendar_event *ast_calendar_event_alloc(struct ast_calendar *cal)
00604 {
00605 struct ast_calendar_event *event;
00606 if (!(event = ao2_alloc(sizeof(*event), calendar_event_destructor))) {
00607 return NULL;
00608 }
00609
00610 if (ast_string_field_init(event, 32)) {
00611 event = ast_calendar_unref_event(event);
00612 return NULL;
00613 }
00614
00615 event->owner = cal;
00616 event->notify_sched = -1;
00617 event->bs_start_sched = -1;
00618 event->bs_end_sched = -1;
00619
00620 AST_LIST_HEAD_INIT_NOLOCK(&event->attendees);
00621
00622 return event;
00623 }
00624
00625 struct ao2_container *ast_calendar_event_container_alloc(void)
00626 {
00627 return ao2_container_alloc(CALENDAR_BUCKETS, event_hash_fn, event_cmp_fn);
00628 }
00629
00630 static void event_notification_destroy(void *data)
00631 {
00632 struct ast_calendar_event *event = data;
00633
00634 event = ast_calendar_unref_event(event);
00635
00636 }
00637
00638 static void *event_notification_duplicate(void *data)
00639 {
00640 struct ast_calendar_event *event = data;
00641
00642 if (!event) {
00643 return NULL;
00644 }
00645
00646 ao2_ref(event, +1);
00647
00648 return event;
00649 }
00650
00651
00652 static char *generate_random_string(char *buf, size_t size)
00653 {
00654 unsigned long val[4];
00655 int x;
00656
00657 for (x = 0; x < 4; x++) {
00658 val[x] = ast_random();
00659 }
00660 snprintf(buf, size, "%08lx%08lx%08lx%08lx", val[0], val[1], val[2], val[3]);
00661
00662 return buf;
00663 }
00664
00665 static int null_chan_write(struct ast_channel *chan, struct ast_frame *frame)
00666 {
00667 return 0;
00668 }
00669
00670 static const struct ast_channel_tech null_tech = {
00671 .type = "NULL",
00672 .description = "Null channel (should not see this)",
00673 .write = null_chan_write,
00674 };
00675
00676 static void *do_notify(void *data)
00677 {
00678 struct ast_calendar_event *event = data;
00679 struct ast_dial *dial = NULL;
00680 struct ast_str *apptext = NULL;
00681 struct ast_datastore *datastore;
00682 enum ast_dial_result res;
00683 struct ast_channel *chan = NULL;
00684 char *tech, *dest;
00685 char buf[8];
00686
00687 tech = ast_strdupa(event->owner->notify_channel);
00688
00689 if ((dest = strchr(tech, '/'))) {
00690 *dest = '\0';
00691 dest++;
00692 } else {
00693 ast_log(LOG_WARNING, "Channel should be in form Tech/Dest (was '%s')\n", tech);
00694 goto notify_cleanup;
00695 }
00696
00697 if (!(dial = ast_dial_create())) {
00698 ast_log(LOG_ERROR, "Could not create dial structure\n");
00699 goto notify_cleanup;
00700 }
00701
00702 if (ast_dial_append(dial, tech, dest) < 0) {
00703 ast_log(LOG_ERROR, "Could not append channel\n");
00704 goto notify_cleanup;
00705 }
00706
00707 ast_dial_set_global_timeout(dial, event->owner->notify_waittime);
00708 generate_random_string(buf, sizeof(buf));
00709
00710 if (!(chan = ast_channel_alloc(1, AST_STATE_DOWN, 0, 0, 0, 0, 0, 0, 0, "Calendar/%s-%s", event->owner->name, buf))) {
00711 ast_log(LOG_ERROR, "Could not allocate notification channel\n");
00712 goto notify_cleanup;
00713 }
00714
00715 chan->tech = &null_tech;
00716 chan->nativeformats = chan->writeformat = chan->rawwriteformat =
00717 chan->readformat = chan->rawreadformat = AST_FORMAT_SLINEAR;
00718
00719 if (!(datastore = ast_datastore_alloc(&event_notification_datastore, NULL))) {
00720 ast_log(LOG_ERROR, "Could not allocate datastore, notification not being sent!\n");
00721 goto notify_cleanup;
00722 }
00723
00724 datastore->data = event;
00725 datastore->inheritance = DATASTORE_INHERIT_FOREVER;
00726
00727 ao2_ref(event, +1);
00728
00729 ast_channel_lock(chan);
00730 res = ast_channel_datastore_add(chan, datastore);
00731 ast_channel_unlock(chan);
00732
00733 if (!(apptext = ast_str_create(32))) {
00734 goto notify_cleanup;
00735 }
00736
00737 if (!ast_strlen_zero(event->owner->notify_app)) {
00738 ast_str_set(&apptext, 0, "%s,%s", event->owner->notify_app, event->owner->notify_appdata);
00739 ast_dial_option_global_enable(dial, AST_DIAL_OPTION_ANSWER_EXEC, ast_str_buffer(apptext));
00740 } else {
00741 }
00742
00743 ast_verb(3, "Dialing %s for notification on calendar %s\n", event->owner->notify_channel, event->owner->name);
00744 res = ast_dial_run(dial, chan, 0);
00745
00746 if (res != AST_DIAL_RESULT_ANSWERED) {
00747 ast_verb(3, "Notification call for %s was not completed\n", event->owner->name);
00748 } else {
00749 struct ast_channel *answered;
00750
00751 answered = ast_dial_answered_steal(dial);
00752 if (ast_strlen_zero(event->owner->notify_app)) {
00753 ast_copy_string(answered->context, event->owner->notify_context, sizeof(answered->context));
00754 ast_copy_string(answered->exten, event->owner->notify_extension, sizeof(answered->exten));
00755 answered->priority = 1;
00756 ast_pbx_run(answered);
00757 }
00758 }
00759
00760 notify_cleanup:
00761 if (apptext) {
00762 ast_free(apptext);
00763 }
00764 if (dial) {
00765 ast_dial_destroy(dial);
00766 }
00767 if (chan) {
00768 ast_channel_release(chan);
00769 }
00770
00771 event = ast_calendar_unref_event(event);
00772
00773 return NULL;
00774 }
00775
00776 static int calendar_event_notify(const void *data)
00777 {
00778 struct ast_calendar_event *event = (void *)data;
00779 int res = -1;
00780 pthread_t notify_thread = AST_PTHREADT_NULL;
00781
00782 if (!(event && event->owner)) {
00783 ast_log(LOG_ERROR, "Extremely low-cal...in fact cal is NULL!\n");
00784 return res;
00785 }
00786
00787 ao2_ref(event, +1);
00788 event->notify_sched = -1;
00789
00790 if (ast_pthread_create_background(¬ify_thread, NULL, do_notify, event) < 0) {
00791 ast_log(LOG_ERROR, "Could not create notification thread\n");
00792 return res;
00793 }
00794
00795 res = 0;
00796
00797 return res;
00798 }
00799
00800 static int calendar_devstate_change(const void *data)
00801 {
00802 struct ast_calendar_event *event = (struct ast_calendar_event *)data;
00803 struct timeval now = ast_tvnow();
00804 int is_end_event;
00805
00806 if (!event) {
00807 ast_log(LOG_WARNING, "Event was NULL!\n");
00808 return 0;
00809 }
00810
00811 ao2_ref(event, +1);
00812
00813 is_end_event = event->end <= now.tv_sec;
00814
00815 if (is_end_event) {
00816 event->bs_end_sched = -1;
00817 } else {
00818 event->bs_start_sched = -1;
00819 }
00820
00821
00822
00823 if (!calendar_is_busy(event->owner)) {
00824 ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Calendar:%s", event->owner->name);
00825 } else {
00826 ast_devstate_changed(AST_DEVICE_BUSY, AST_DEVSTATE_CACHABLE, "Calendar:%s", event->owner->name);
00827 }
00828
00829 event = ast_calendar_unref_event(event);
00830
00831 return 0;
00832 }
00833
00834 static void copy_event_data(struct ast_calendar_event *dst, struct ast_calendar_event *src)
00835 {
00836 struct ast_calendar_attendee *attendee;
00837
00838 ast_string_field_set(dst, summary, src->summary);
00839 ast_string_field_set(dst, description, src->description);
00840 ast_string_field_set(dst, organizer, src->organizer);
00841 ast_string_field_set(dst, location, src->location);
00842 ast_string_field_set(dst, uid, src->uid);
00843 ast_string_field_set(dst, categories, src->categories);
00844 dst->priority = src->priority;
00845 dst->owner = src->owner;
00846 dst->start = src->start;
00847 dst->end = src->end;
00848 dst->alarm = src->alarm;
00849 dst->busy_state = src->busy_state;
00850
00851
00852 while ((attendee = AST_LIST_REMOVE_HEAD(&dst->attendees, next))) {
00853 ast_free(attendee);
00854 }
00855
00856
00857 while ((attendee = AST_LIST_REMOVE_HEAD(&src->attendees, next))) {
00858 AST_LIST_INSERT_TAIL(&dst->attendees, attendee, next);
00859 }
00860 }
00861
00862 static int schedule_calendar_event(struct ast_calendar *cal, struct ast_calendar_event *old_event, struct ast_calendar_event *cmp_event)
00863 {
00864 struct timeval now = ast_tvnow();
00865 struct ast_calendar_event *event;
00866 time_t alarm_notify_sched = 0, devstate_sched_start, devstate_sched_end;
00867 int changed = 0;
00868
00869 event = cmp_event ? cmp_event : old_event;
00870
00871 ao2_lock(event);
00872 if (!cmp_event || old_event->alarm != event->alarm) {
00873 changed = 1;
00874 if (cal->autoreminder) {
00875 alarm_notify_sched = (event->start - (60 * cal->autoreminder) - now.tv_sec) * 1000;
00876 } else if (event->alarm) {
00877 alarm_notify_sched = (event->alarm - now.tv_sec) * 1000;
00878 }
00879
00880
00881 if (event->start >= now.tv_sec) {
00882 if (alarm_notify_sched <= 0) {
00883 alarm_notify_sched = 1;
00884 }
00885 ast_mutex_lock(&refreshlock);
00886 AST_SCHED_REPLACE(old_event->notify_sched, sched, alarm_notify_sched, calendar_event_notify, old_event);
00887 ast_mutex_unlock(&refreshlock);
00888 ast_debug(3, "Calendar alarm event notification scheduled to happen in %ld ms\n", (long) alarm_notify_sched);
00889 }
00890 }
00891
00892 if (!cmp_event || old_event->start != event->start) {
00893 changed = 1;
00894 devstate_sched_start = (event->start - now.tv_sec) * 1000;
00895
00896 if (devstate_sched_start < 1) {
00897 devstate_sched_start = 1;
00898 }
00899
00900 ast_mutex_lock(&refreshlock);
00901 AST_SCHED_REPLACE(old_event->bs_start_sched, sched, devstate_sched_start, calendar_devstate_change, old_event);
00902 ast_mutex_unlock(&refreshlock);
00903 ast_debug(3, "Calendar bs_start event notification scheduled to happen in %ld ms\n", (long) devstate_sched_start);
00904 }
00905
00906 if (!cmp_event || old_event->end != event->end) {
00907 changed = 1;
00908 devstate_sched_end = (event->end - now.tv_sec) * 1000;
00909 ast_mutex_lock(&refreshlock);
00910 AST_SCHED_REPLACE(old_event->bs_end_sched, sched, devstate_sched_end, calendar_devstate_change, old_event);
00911 ast_mutex_unlock(&refreshlock);
00912 ast_debug(3, "Calendar bs_end event notification scheduled to happen in %ld ms\n", (long) devstate_sched_end);
00913 }
00914
00915 if (changed) {
00916 ast_cond_signal(&refresh_condition);
00917 }
00918
00919 ao2_unlock(event);
00920
00921 return 0;
00922 }
00923
00924 static int merge_events_cb(void *obj, void *arg, int flags)
00925 {
00926 struct ast_calendar_event *old_event = obj, *new_event;
00927 struct ao2_container *new_events = arg;
00928
00929
00930 if (!(new_event = find_event(new_events, old_event->uid))) {
00931 old_event = destroy_event(old_event);
00932 return CMP_MATCH;
00933 }
00934
00935
00936
00937 schedule_calendar_event(old_event->owner, old_event, new_event);
00938
00939
00940
00941 copy_event_data(old_event, new_event);
00942
00943
00944
00945 ao2_unlink(new_events, new_event);
00946 new_event = ast_calendar_unref_event(new_event);
00947
00948 return 0;
00949 }
00950
00951 static int add_new_event_cb(void *obj, void *arg, int flags)
00952 {
00953 struct ast_calendar_event *new_event = obj;
00954 struct ao2_container *events = arg;
00955
00956 ao2_link(events, new_event);
00957 schedule_calendar_event(new_event->owner, new_event, NULL);
00958 return CMP_MATCH;
00959 }
00960
00961 void ast_calendar_merge_events(struct ast_calendar *cal, struct ao2_container *new_events)
00962 {
00963
00964
00965
00966 ao2_callback(cal->events, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, merge_events_cb, new_events);
00967
00968
00969 ao2_callback(new_events, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, add_new_event_cb, cal->events);
00970 }
00971
00972
00973 static int load_config(int reload)
00974 {
00975 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
00976 struct ast_config *tmpcfg;
00977
00978 if (!(tmpcfg = ast_config_load2("calendar.conf", "calendar", config_flags)) ||
00979 tmpcfg == CONFIG_STATUS_FILEINVALID) {
00980 ast_log(LOG_ERROR, "Unable to load config calendar.conf\n");
00981 return -1;
00982 }
00983
00984 if (tmpcfg == CONFIG_STATUS_FILEUNCHANGED) {
00985 return 0;
00986 }
00987
00988 ast_rwlock_wrlock(&config_lock);
00989 if (calendar_config) {
00990 ast_config_destroy(calendar_config);
00991 }
00992
00993 calendar_config = tmpcfg;
00994 ast_rwlock_unlock(&config_lock);
00995
00996 return 0;
00997 }
00998
00999
01000 static int calendar_busy_exec(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
01001 {
01002 struct ast_calendar *cal;
01003
01004 if (ast_strlen_zero(data)) {
01005 ast_log(LOG_WARNING, "CALENDAR_BUSY requires an argument: CALENDAR_BUSY(<calendar_name>)\n");
01006 return -1;
01007 }
01008
01009 cal = find_calendar(data);
01010
01011 if (!cal) {
01012 ast_log(LOG_WARNING, "Could not find calendar '%s'\n", data);
01013 return -1;
01014 }
01015
01016 strcpy(buf, calendar_is_busy(cal) ? "1" : "0");
01017 cal = unref_calendar(cal);
01018
01019 return 0;
01020 }
01021
01022 static struct ast_custom_function calendar_busy_function = {
01023 .name = "CALENDAR_BUSY",
01024 .read = calendar_busy_exec,
01025 };
01026
01027 static int add_event_to_list(struct eventlist *events, struct ast_calendar_event *event, time_t start, time_t end)
01028 {
01029 struct evententry *entry, *iter;
01030 int event_startdiff = abs(start - event->start);
01031 int event_enddiff = abs(end - event->end);
01032 int i = 0;
01033
01034 if (!(entry = ast_calloc(1, sizeof(*entry)))) {
01035 ast_log(LOG_ERROR, "Unable to allocate memory for event list\n");
01036 return -1;
01037 }
01038
01039 entry->event = event;
01040 ao2_ref(event, +1);
01041
01042 if (start == end) {
01043 AST_LIST_TRAVERSE_SAFE_BEGIN(events, iter, list) {
01044 int startdiff = abs(iter->event->start - start);
01045
01046 ast_debug(10, "Comparing %s with startdiff %d to %s with startdiff %d\n", event->summary, event_startdiff, iter->event->summary, startdiff);
01047 ++i;
01048 if (startdiff > event_startdiff) {
01049 AST_LIST_INSERT_BEFORE_CURRENT(entry, list);
01050 return i;
01051 }
01052 if (startdiff == event_startdiff) {
01053 int enddiff = abs(iter->event->end - end);
01054
01055 if (enddiff > event_enddiff) {
01056 AST_LIST_INSERT_BEFORE_CURRENT(entry, list);
01057 return i;
01058 }
01059 if (event_startdiff == enddiff) {
01060 if (strcmp(event->uid, iter->event->uid) < 0) {
01061 AST_LIST_INSERT_BEFORE_CURRENT(entry, list);
01062 return i;
01063 }
01064 }
01065 }
01066 }
01067 AST_LIST_TRAVERSE_SAFE_END;
01068
01069 AST_LIST_INSERT_TAIL(events, entry, list);
01070
01071 return i;
01072 }
01073
01074 AST_LIST_TRAVERSE_SAFE_BEGIN(events, iter, list) {
01075 ++i;
01076 if (iter->event->start > event->start) {
01077 AST_LIST_INSERT_BEFORE_CURRENT(entry, list);
01078 return i;
01079 }
01080
01081 if (iter->event->start == event->start) {
01082 if ((iter->event->end - iter->event->start) == (event->end - event->start)) {
01083 if (strcmp(event->uid, iter->event->uid) < 0) {
01084 AST_LIST_INSERT_BEFORE_CURRENT(entry, list);
01085 return i;
01086 }
01087 }
01088 if ((iter->event->end - iter->event->start) < (event->end - event->start)) {
01089 AST_LIST_INSERT_BEFORE_CURRENT(entry, list);
01090 return i;
01091 }
01092 }
01093 }
01094 AST_LIST_TRAVERSE_SAFE_END;
01095
01096 AST_LIST_INSERT_TAIL(events, entry, list);
01097
01098 return i;
01099 }
01100
01101 static void eventlist_destroy(void *data)
01102 {
01103 struct eventlist *events = data;
01104
01105 ao2_ref(events, -1);
01106 }
01107
01108 static void *eventlist_duplicate(void *data)
01109 {
01110 struct eventlist *events = data;
01111
01112 if (!events) {
01113 return NULL;
01114 }
01115
01116 ao2_ref(events, +1);
01117
01118 return events;
01119 }
01120
01121 static int calendar_query_exec(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
01122 {
01123 struct ast_calendar *cal;
01124 struct ao2_iterator i;
01125 struct ast_calendar_event *event;
01126 struct eventlist *events;
01127 time_t start = INT_MIN, end = INT_MAX;
01128 struct ast_datastore *eventlist_datastore;
01129 AST_DECLARE_APP_ARGS(args,
01130 AST_APP_ARG(calendar);
01131 AST_APP_ARG(start);
01132 AST_APP_ARG(end);
01133 );
01134
01135 if (!chan) {
01136 ast_log(LOG_WARNING, "%s requires a channel to store the data on\n", cmd);
01137 return -1;
01138 }
01139
01140 AST_STANDARD_APP_ARGS(args, data);
01141
01142 if (ast_strlen_zero(args.calendar)) {
01143 ast_log(LOG_WARNING, "%s requires a calendar argument\n", cmd);
01144 return -1;
01145 }
01146
01147 if (!(cal = find_calendar(args.calendar))) {
01148 ast_log(LOG_WARNING, "Unknown calendar '%s'\n", args.calendar);
01149 return -1;
01150 }
01151
01152 if (!(events = ao2_alloc(sizeof(*events), eventlist_destructor))) {
01153 ast_log(LOG_ERROR, "Unable to allocate memory for event list\n");
01154 cal = unref_calendar(cal);
01155 return -1;
01156 }
01157
01158 if (!ast_strlen_zero(args.start)) {
01159 start = atoi(args.start);
01160 }
01161
01162 if (!ast_strlen_zero(args.end)) {
01163 end = atoi(args.end);
01164 }
01165
01166 i = ao2_iterator_init(cal->events, 0);
01167 while ((event = ao2_iterator_next(&i))) {
01168 if (!(start > event->end || end < event->start)) {
01169 ast_debug(10, "%s (%ld - %ld) overlapped with (%ld - %ld)\n", event->summary, (long) event->start, (long) event->end, (long) start, (long) end);
01170 if (add_event_to_list(events, event, start, end) < 0) {
01171 event = ast_calendar_unref_event(event);
01172 cal = unref_calendar(cal);
01173 ao2_ref(events, -1);
01174 ao2_iterator_destroy(&i);
01175 return -1;
01176 }
01177 }
01178
01179 event = ast_calendar_unref_event(event);
01180 }
01181 ao2_iterator_destroy(&i);
01182
01183 ast_channel_lock(chan);
01184 do {
01185 generate_random_string(buf, len);
01186 } while (ast_channel_datastore_find(chan, &eventlist_datastore_info, buf));
01187 ast_channel_unlock(chan);
01188
01189 if (!(eventlist_datastore = ast_datastore_alloc(&eventlist_datastore_info, buf))) {
01190 ast_log(LOG_ERROR, "Could not allocate datastore!\n");
01191 cal = unref_calendar(cal);
01192 ao2_ref(events, -1);
01193 return -1;
01194 }
01195
01196 eventlist_datastore->inheritance = DATASTORE_INHERIT_FOREVER;
01197 eventlist_datastore->data = events;
01198
01199 ast_channel_lock(chan);
01200 ast_channel_datastore_add(chan, eventlist_datastore);
01201 ast_channel_unlock(chan);
01202
01203 cal = unref_calendar(cal);
01204 return 0;
01205 }
01206
01207 static struct ast_custom_function calendar_query_function = {
01208 .name = "CALENDAR_QUERY",
01209 .read = calendar_query_exec,
01210 };
01211
01212 static void calendar_join_attendees(struct ast_calendar_event *event, char *buf, size_t len)
01213 {
01214 struct ast_str *tmp;
01215 struct ast_calendar_attendee *attendee;
01216
01217 if (!(tmp = ast_str_create(32))) {
01218 ast_log(LOG_ERROR, "Could not allocate memory for attendees!\n");
01219 return;
01220 }
01221
01222 AST_LIST_TRAVERSE(&event->attendees, attendee, next) {
01223 ast_str_append(&tmp, 0, "%s%s", attendee == AST_LIST_FIRST(&event->attendees) ? "" : ",", attendee->data);
01224 }
01225
01226 ast_copy_string(buf, ast_str_buffer(tmp), len);
01227 ast_free(tmp);
01228 }
01229
01230 static int calendar_query_result_exec(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
01231 {
01232 struct ast_datastore *datastore;
01233 struct eventlist *events;
01234 struct evententry *entry;
01235 int row = 1;
01236 size_t listlen = 0;
01237 AST_DECLARE_APP_ARGS(args,
01238 AST_APP_ARG(id);
01239 AST_APP_ARG(field);
01240 AST_APP_ARG(row);
01241 );
01242
01243 if (!chan) {
01244 ast_log(LOG_WARNING, "%s requires a channel\n", cmd);
01245 return -1;
01246 }
01247
01248 AST_STANDARD_APP_ARGS(args, data);
01249
01250 if (ast_strlen_zero(args.id) || ast_strlen_zero(args.field)) {
01251 ast_log(LOG_WARNING, "%s requires an id and a field", cmd);
01252 return -1;
01253 }
01254
01255 ast_channel_lock(chan);
01256 if (!(datastore = ast_channel_datastore_find(chan, &eventlist_datastore_info, args.id))) {
01257 ast_log(LOG_WARNING, "There is no event notification datastore with id '%s' on '%s'!\n", args.id, chan->name);
01258 ast_channel_unlock(chan);
01259 return -1;
01260 }
01261 ast_channel_unlock(chan);
01262
01263 if (!(events = datastore->data)) {
01264 ast_log(LOG_WARNING, "The datastore contains no data!\n");
01265 return -1;
01266 }
01267
01268 if (!ast_strlen_zero(args.row)) {
01269 row = atoi(args.row);
01270 }
01271
01272 AST_LIST_TRAVERSE(events, entry, list) {
01273 listlen++;
01274 }
01275
01276 if (!strcasecmp(args.field, "getnum")) {
01277 snprintf(buf, len, "%zu", listlen);
01278 return 0;
01279 }
01280
01281 AST_LIST_TRAVERSE(events, entry, list) {
01282 if (--row) {
01283 continue;
01284 }
01285 if (!strcasecmp(args.field, "summary")) {
01286 ast_copy_string(buf, entry->event->summary, len);
01287 } else if (!strcasecmp(args.field, "description")) {
01288 ast_copy_string(buf, entry->event->description, len);
01289 } else if (!strcasecmp(args.field, "organizer")) {
01290 ast_copy_string(buf, entry->event->organizer, len);
01291 } else if (!strcasecmp(args.field, "location")) {
01292 ast_copy_string(buf, entry->event->location, len);
01293 } else if (!strcasecmp(args.field, "categories")) {
01294 ast_copy_string(buf, entry->event->categories, len);
01295 } else if (!strcasecmp(args.field, "priority")) {
01296 snprintf(buf, len, "%d", entry->event->priority);
01297 } else if (!strcasecmp(args.field, "calendar")) {
01298 ast_copy_string(buf, entry->event->owner->name, len);
01299 } else if (!strcasecmp(args.field, "uid")) {
01300 ast_copy_string(buf, entry->event->uid, len);
01301 } else if (!strcasecmp(args.field, "start")) {
01302 snprintf(buf, len, "%ld", (long) entry->event->start);
01303 } else if (!strcasecmp(args.field, "end")) {
01304 snprintf(buf, len, "%ld", (long) entry->event->end);
01305 } else if (!strcasecmp(args.field, "busystate")) {
01306 snprintf(buf, len, "%u", entry->event->busy_state);
01307 } else if (!strcasecmp(args.field, "attendees")) {
01308 calendar_join_attendees(entry->event, buf, len);
01309 } else {
01310 ast_log(LOG_WARNING, "Unknown field '%s'\n", args.field);
01311 }
01312 break;
01313 }
01314
01315 return 0;
01316 }
01317
01318 static struct ast_custom_function calendar_query_result_function = {
01319 .name = "CALENDAR_QUERY_RESULT",
01320 .read = calendar_query_result_exec,
01321 };
01322
01323 static int calendar_write_exec(struct ast_channel *chan, const char *cmd, char *data, const char *value)
01324 {
01325 int i, j, ret = -1;
01326 char *val_dup = NULL;
01327 struct ast_calendar *cal = NULL;
01328 struct ast_calendar_event *event = NULL;
01329 struct timeval tv = ast_tvnow();
01330 AST_DECLARE_APP_ARGS(fields,
01331 AST_APP_ARG(field)[10];
01332 );
01333 AST_DECLARE_APP_ARGS(values,
01334 AST_APP_ARG(value)[10];
01335 );
01336
01337 if (!(val_dup = ast_strdup(value))) {
01338 ast_log(LOG_ERROR, "Could not allocate memory for values\n");
01339 return -1;
01340 }
01341
01342 AST_STANDARD_APP_ARGS(fields, data);
01343 AST_STANDARD_APP_ARGS(values, val_dup);
01344
01345
01346
01347 if (!(cal = find_calendar(fields.field[0]))) {
01348 ast_log(LOG_WARNING, "Couldn't find calendar '%s'\n", fields.field[0]);
01349 goto write_cleanup;
01350 }
01351
01352 if (!(cal->tech->write_event)) {
01353 ast_log(LOG_WARNING, "Calendar '%s' has no write function!\n", cal->name);
01354 goto write_cleanup;
01355 }
01356
01357 if (!(event = ast_calendar_event_alloc(cal))) {
01358 goto write_cleanup;
01359 }
01360
01361 if (ast_strlen_zero(fields.field[0])) {
01362 ast_log(LOG_WARNING, "CALENDAR_WRITE requires a calendar name!\n");
01363 goto write_cleanup;
01364 }
01365
01366 if (fields.argc - 1 != values.argc) {
01367 ast_log(LOG_WARNING, "CALENDAR_WRITE should have the same number of fields (%u) and values (%u)!\n", fields.argc - 1, values.argc);
01368 goto write_cleanup;
01369 }
01370
01371 event->owner = cal;
01372
01373 for (i = 1, j = 0; i < fields.argc; i++, j++) {
01374 if (!strcasecmp(fields.field[i], "summary")) {
01375 ast_string_field_set(event, summary, values.value[j]);
01376 } else if (!strcasecmp(fields.field[i], "description")) {
01377 ast_string_field_set(event, description, values.value[j]);
01378 } else if (!strcasecmp(fields.field[i], "organizer")) {
01379 ast_string_field_set(event, organizer, values.value[j]);
01380 } else if (!strcasecmp(fields.field[i], "location")) {
01381 ast_string_field_set(event, location, values.value[j]);
01382 } else if (!strcasecmp(fields.field[i], "categories")) {
01383 ast_string_field_set(event, categories, values.value[j]);
01384 } else if (!strcasecmp(fields.field[i], "priority")) {
01385 event->priority = atoi(values.value[j]);
01386 } else if (!strcasecmp(fields.field[i], "uid")) {
01387 ast_string_field_set(event, uid, values.value[j]);
01388 } else if (!strcasecmp(fields.field[i], "start")) {
01389 event->start = atoi(values.value[j]);
01390 } else if (!strcasecmp(fields.field[i], "end")) {
01391 event->end = atoi(values.value[j]);
01392 } else if (!strcasecmp(fields.field[i], "busystate")) {
01393 event->busy_state = atoi(values.value[j]);
01394 } else {
01395 ast_log(LOG_WARNING, "Unknown calendar event field '%s'\n", fields.field[i]);
01396 }
01397 }
01398
01399 if (!event->start) {
01400 event->start = tv.tv_sec;
01401 }
01402
01403 if (!event->end) {
01404 event->end = tv.tv_sec;
01405 }
01406
01407 if((ret = cal->tech->write_event(event))) {
01408 ast_log(LOG_WARNING, "Writing event to calendar '%s' failed!\n", cal->name);
01409 }
01410
01411 write_cleanup:
01412 if (cal) {
01413 cal = unref_calendar(cal);
01414 }
01415 if (event) {
01416 event = ast_calendar_unref_event(event);
01417 }
01418 if (val_dup) {
01419 ast_free(val_dup);
01420 }
01421
01422 return ret;
01423 }
01424
01425 static struct ast_custom_function calendar_write_function = {
01426 .name = "CALENDAR_WRITE",
01427 .write = calendar_write_exec,
01428 };
01429
01430
01431 static char *handle_show_calendars(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01432 {
01433 #define FORMAT "%-20.20s %-10.10s %-6.6s\n"
01434 struct ao2_iterator i;
01435 struct ast_calendar *cal;
01436
01437 switch(cmd) {
01438 case CLI_INIT:
01439 e->command = "calendar show calendars";
01440 e->usage =
01441 "Usage: calendar show calendars\n"
01442 " Lists all registered calendars.\n";
01443 return NULL;
01444 case CLI_GENERATE:
01445 return NULL;
01446 }
01447
01448 ast_cli(a->fd, FORMAT, "Calendar", "Type", "Status");
01449 ast_cli(a->fd, FORMAT, "--------", "----", "------");
01450 i = ao2_iterator_init(calendars, 0);
01451 while ((cal = ao2_iterator_next(&i))) {
01452 ast_cli(a->fd, FORMAT, cal->name, cal->tech->type, calendar_is_busy(cal) ? "busy" : "free");
01453 cal = unref_calendar(cal);
01454 }
01455 ao2_iterator_destroy(&i);
01456
01457 return CLI_SUCCESS;
01458 #undef FORMAT
01459 }
01460
01461 static char *epoch_to_string(char *buf, size_t buflen, time_t epoch)
01462 {
01463 struct ast_tm tm;
01464 struct timeval tv = {
01465 .tv_sec = epoch,
01466 };
01467
01468 if (!epoch) {
01469 *buf = '\0';
01470 return buf;
01471 }
01472 ast_localtime(&tv, &tm, NULL);
01473 ast_strftime(buf, buflen, "%F %r %z", &tm);
01474
01475 return buf;
01476 }
01477
01478 static char *handle_show_calendar(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01479 {
01480 #define FORMAT "%-17.17s : %-20.20s\n"
01481 #define FORMAT2 "%-12.12s: %-40.60s\n"
01482 struct ao2_iterator i;
01483 struct ast_calendar *cal;
01484 struct ast_calendar_event *event;
01485 int which = 0;
01486 char *ret = NULL;
01487
01488 switch(cmd) {
01489 case CLI_INIT:
01490 e->command = "calendar show calendar";
01491 e->usage =
01492 "Usage: calendar show calendar <calendar name>\n"
01493 " Displays information about a calendar\n";
01494 return NULL;
01495
01496 case CLI_GENERATE:
01497 if (a->pos != 3) {
01498 return NULL;
01499 }
01500 i = ao2_iterator_init(calendars, 0);
01501 while ((cal = ao2_iterator_next(&i))) {
01502 if (!strncasecmp(a->word, cal->name, strlen(a->word)) && ++which > a->n) {
01503 ret = ast_strdup(cal->name);
01504 cal = unref_calendar(cal);
01505 break;
01506 }
01507 cal = unref_calendar(cal);
01508 }
01509 ao2_iterator_destroy(&i);
01510 return ret;
01511 }
01512
01513 if (a->argc != 4) {
01514 return CLI_SHOWUSAGE;
01515 }
01516
01517 if (!(cal = find_calendar(a->argv[3]))) {
01518 return NULL;
01519 }
01520
01521 ast_cli(a->fd, FORMAT, "Name", cal->name);
01522 ast_cli(a->fd, FORMAT, "Notify channel", cal->notify_channel);
01523 ast_cli(a->fd, FORMAT, "Notify context", cal->notify_context);
01524 ast_cli(a->fd, FORMAT, "Notify extension", cal->notify_extension);
01525 ast_cli(a->fd, FORMAT, "Notify application", cal->notify_app);
01526 ast_cli(a->fd, FORMAT, "Notify appdata", cal->notify_appdata);
01527 ast_cli(a->fd, "%-17.17s : %d\n", "Refresh time", cal->refresh);
01528 ast_cli(a->fd, "%-17.17s : %d\n", "Timeframe", cal->timeframe);
01529 ast_cli(a->fd, "%-17.17s : %d\n", "Autoreminder", cal->autoreminder);
01530 ast_cli(a->fd, "%s\n", "Events");
01531 ast_cli(a->fd, "%s\n", "------");
01532
01533 i = ao2_iterator_init(cal->events, 0);
01534 while ((event = ao2_iterator_next(&i))) {
01535 char buf[100];
01536
01537 ast_cli(a->fd, FORMAT2, "Summary", event->summary);
01538 ast_cli(a->fd, FORMAT2, "Description", event->description);
01539 ast_cli(a->fd, FORMAT2, "Organizer", event->organizer);
01540 ast_cli(a->fd, FORMAT2, "Location", event->location);
01541 ast_cli(a->fd, FORMAT2, "Categories", event->categories);
01542 ast_cli(a->fd, "%-12.12s: %d\n", "Priority", event->priority);
01543 ast_cli(a->fd, FORMAT2, "UID", event->uid);
01544 ast_cli(a->fd, FORMAT2, "Start", epoch_to_string(buf, sizeof(buf), event->start));
01545 ast_cli(a->fd, FORMAT2, "End", epoch_to_string(buf, sizeof(buf), event->end));
01546 ast_cli(a->fd, FORMAT2, "Alarm", epoch_to_string(buf, sizeof(buf), event->alarm));
01547 ast_cli(a->fd, "\n");
01548
01549 event = ast_calendar_unref_event(event);
01550 }
01551 ao2_iterator_destroy(&i);
01552 cal = unref_calendar(cal);
01553 return CLI_SUCCESS;
01554 #undef FORMAT
01555 #undef FORMAT2
01556 }
01557
01558 static char *handle_dump_sched(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01559 {
01560 switch(cmd) {
01561 case CLI_INIT:
01562 e->command = "calendar dump sched";
01563 e->usage =
01564 "Usage: calendar dump sched\n"
01565 " Dump the calendar sched context";
01566 return NULL;
01567
01568 case CLI_GENERATE:
01569 return NULL;
01570 }
01571
01572 ast_sched_dump(sched);
01573
01574 return CLI_SUCCESS;
01575 }
01576
01577 static struct ast_cli_entry calendar_cli[] = {
01578 AST_CLI_DEFINE(handle_show_calendar, "Display information about a calendar"),
01579 AST_CLI_DEFINE(handle_show_calendars, "Show registered calendars"),
01580 AST_CLI_DEFINE(handle_dump_sched, "Dump calendar sched context"),
01581 };
01582
01583 static int calendar_event_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
01584 {
01585 struct ast_datastore *datastore;
01586 struct ast_calendar_event *event;
01587
01588 if (!chan) {
01589 ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
01590 return -1;
01591 }
01592
01593 if (ast_strlen_zero(data)) {
01594 ast_log(LOG_WARNING, "%s requires an argument\n", cmd);
01595 return -1;
01596 }
01597
01598 ast_channel_lock(chan);
01599 if (!(datastore = ast_channel_datastore_find(chan, &event_notification_datastore, NULL))) {
01600 ast_log(LOG_WARNING, "There is no event notification datastore on '%s'!\n", chan->name);
01601 ast_channel_unlock(chan);
01602 return -1;
01603 }
01604 ast_channel_unlock(chan);
01605
01606 if (!(event = datastore->data)) {
01607 ast_log(LOG_WARNING, "The datastore contains no data!\n");
01608 return -1;
01609 }
01610
01611 if (!strcasecmp(data, "summary")) {
01612 ast_copy_string(buf, event->summary, len);
01613 } else if (!strcasecmp(data, "description")) {
01614 ast_copy_string(buf, event->description, len);
01615 } else if (!strcasecmp(data, "organizer")) {
01616 ast_copy_string(buf, event->organizer, len);
01617 } else if (!strcasecmp(data, "location")) {
01618 ast_copy_string(buf, event->location, len);
01619 } else if (!strcasecmp(data, "categories")) {
01620 ast_copy_string(buf, event->categories, len);
01621 } else if (!strcasecmp(data, "priority")) {
01622 snprintf(buf, len, "%d", event->priority);
01623 } else if (!strcasecmp(data, "calendar")) {
01624 ast_copy_string(buf, event->owner->name, len);
01625 } else if (!strcasecmp(data, "uid")) {
01626 ast_copy_string(buf, event->uid, len);
01627 } else if (!strcasecmp(data, "start")) {
01628 snprintf(buf, len, "%ld", (long)event->start);
01629 } else if (!strcasecmp(data, "end")) {
01630 snprintf(buf, len, "%ld", (long)event->end);
01631 } else if (!strcasecmp(data, "busystate")) {
01632 snprintf(buf, len, "%u", event->busy_state);
01633 } else if (!strcasecmp(data, "attendees")) {
01634 calendar_join_attendees(event, buf, len);
01635 }
01636
01637
01638 return 0;
01639 }
01640
01641 static struct ast_custom_function calendar_event_function = {
01642 .name = "CALENDAR_EVENT",
01643 .read = calendar_event_read,
01644 };
01645
01646 static int cb_pending_deletion(void *user_data, void *arg, int flags)
01647 {
01648 struct ast_calendar *cal = user_data;
01649
01650 cal->pending_deletion = 1;
01651
01652 return CMP_MATCH;
01653 }
01654
01655 static int cb_rm_pending_deletion(void *user_data, void *arg, int flags)
01656 {
01657 struct ast_calendar *cal = user_data;
01658
01659 return cal->pending_deletion ? CMP_MATCH : 0;
01660 }
01661
01662 static int reload(void)
01663 {
01664 struct ast_calendar_tech *iter;
01665
01666 ast_mutex_lock(&reloadlock);
01667
01668
01669 ao2_callback(calendars, OBJ_NODATA | OBJ_MULTIPLE, cb_pending_deletion, NULL);
01670 load_config(1);
01671
01672 AST_LIST_LOCK(&techs);
01673 AST_LIST_TRAVERSE(&techs, iter, list) {
01674 if (load_tech_calendars(iter)) {
01675 ast_log(LOG_WARNING, "Failed to reload %s calendars, module disabled\n", iter->type);
01676 }
01677 }
01678 AST_LIST_UNLOCK(&techs);
01679
01680
01681 ao2_callback(calendars, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, cb_rm_pending_deletion, NULL);
01682
01683 ast_mutex_unlock(&reloadlock);
01684
01685 return 0;
01686 }
01687
01688 static void *do_refresh(void *data)
01689 {
01690 for (;;) {
01691 struct timeval now = ast_tvnow();
01692 struct timespec ts = {0,};
01693 int wait;
01694
01695 ast_mutex_lock(&refreshlock);
01696
01697 while (!module_unloading) {
01698 if ((wait = ast_sched_wait(sched)) < 0) {
01699 wait = 1000;
01700 }
01701
01702 ts.tv_sec = (now.tv_sec + wait / 1000) + 1;
01703 if (ast_cond_timedwait(&refresh_condition, &refreshlock, &ts) == ETIMEDOUT) {
01704 break;
01705 }
01706 }
01707 ast_mutex_unlock(&refreshlock);
01708
01709 if (module_unloading) {
01710 break;
01711 }
01712 ast_sched_runq(sched);
01713 }
01714
01715 return NULL;
01716 }
01717
01718
01719 static int unload_module(void)
01720 {
01721 struct ast_calendar_tech *tech;
01722
01723 ast_devstate_prov_del("calendar");
01724 ast_custom_function_unregister(&calendar_busy_function);
01725 ast_custom_function_unregister(&calendar_event_function);
01726 ast_custom_function_unregister(&calendar_query_function);
01727 ast_custom_function_unregister(&calendar_query_result_function);
01728 ast_custom_function_unregister(&calendar_write_function);
01729 ast_cli_unregister_multiple(calendar_cli, ARRAY_LEN(calendar_cli));
01730
01731
01732 ao2_callback(calendars, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL);
01733
01734 ast_mutex_lock(&refreshlock);
01735 module_unloading = 1;
01736 ast_cond_signal(&refresh_condition);
01737 ast_mutex_unlock(&refreshlock);
01738 pthread_join(refresh_thread, NULL);
01739
01740 AST_LIST_LOCK(&techs);
01741 AST_LIST_TRAVERSE_SAFE_BEGIN(&techs, tech, list) {
01742 ast_unload_resource(tech->module, 0);
01743 }
01744 AST_LIST_TRAVERSE_SAFE_END;
01745 AST_LIST_UNLOCK(&techs);
01746
01747 ast_config_destroy(calendar_config);
01748 calendar_config = NULL;
01749
01750 return 0;
01751 }
01752
01753 static int load_module(void)
01754 {
01755 if (!(calendars = ao2_container_alloc(CALENDAR_BUCKETS, calendar_hash_fn, calendar_cmp_fn))) {
01756 ast_log(LOG_ERROR, "Unable to allocate calendars container!\n");
01757 return AST_MODULE_LOAD_FAILURE;
01758 }
01759
01760 if (load_config(0)) {
01761
01762 return AST_MODULE_LOAD_DECLINE;
01763 }
01764
01765 ast_mutex_init(&refreshlock);
01766 ast_cond_init(&refresh_condition, NULL);
01767 ast_mutex_init(&reloadlock);
01768
01769 if (!(sched = sched_context_create())) {
01770 ast_log(LOG_ERROR, "Unable to create sched context\n");
01771 return AST_MODULE_LOAD_FAILURE;
01772 }
01773
01774 if (ast_pthread_create_background(&refresh_thread, NULL, do_refresh, NULL) < 0) {
01775 ast_log(LOG_ERROR, "Unable to start refresh thread--notifications disabled!\n");
01776 }
01777
01778 ast_custom_function_register(&calendar_busy_function);
01779 ast_custom_function_register(&calendar_event_function);
01780 ast_custom_function_register(&calendar_query_function);
01781 ast_custom_function_register(&calendar_query_result_function);
01782 ast_custom_function_register(&calendar_write_function);
01783 ast_cli_register_multiple(calendar_cli, ARRAY_LEN(calendar_cli));
01784
01785 ast_devstate_prov_add("Calendar", calendarstate);
01786
01787 return AST_MODULE_LOAD_SUCCESS;
01788 }
01789 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Asterisk Calendar integration",
01790 .load = load_module,
01791 .unload = unload_module,
01792 .reload = reload,
01793 .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,
01794 );