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