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