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