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