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