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