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