#include "asterisk.h"
#include <libical/ical.h>
#include <ne_session.h>
#include <ne_uri.h>
#include <ne_request.h>
#include <ne_auth.h>
#include <ne_redirect.h>
#include "asterisk/module.h"
#include "asterisk/calendar.h"
#include "asterisk/lock.h"
#include "asterisk/config.h"
#include "asterisk/astobj2.h"
Go to the source code of this file.
Data Structures | |
struct | icalendar_pvt |
Functions | |
static void | __reg_module (void) |
static void | __unreg_module (void) |
static int | auth_credentials (void *userdata, const char *realm, int attempts, char *username, char *secret) |
static icalcomponent * | fetch_icalendar (struct icalendar_pvt *pvt) |
static int | fetch_response_reader (void *data, const char *block, size_t len) |
static void * | ical_load_calendar (void *data) |
static void | icalendar_add_event (icalcomponent *comp, struct icaltime_span *span, void *data) |
static void | icalendar_destructor (void *obj) |
static void | icalendar_update_events (struct icalendar_pvt *pvt) |
static time_t | icalfloat_to_timet (icaltimetype time) |
static int | load_module (void) |
static int | unload_module (void) |
static void * | unref_icalendar (void *obj) |
Variables | |
static struct ast_module_info | __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Asterisk iCalendar .ics file integration" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "88eaa8f5c1bd988bedd71113385e0886" , .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEVSTATE_PLUGIN, } |
static struct ast_module_info * | ast_module_info = &__mod_info |
static struct ast_calendar_tech | ical_tech |
Definition in file res_calendar_icalendar.c.
static void __reg_module | ( | void | ) | [static] |
Definition at line 501 of file res_calendar_icalendar.c.
static void __unreg_module | ( | void | ) | [static] |
Definition at line 501 of file res_calendar_icalendar.c.
static int auth_credentials | ( | void * | userdata, | |
const char * | realm, | |||
int | attempts, | |||
char * | username, | |||
char * | secret | |||
) | [static] |
Definition at line 112 of file res_calendar_icalendar.c.
References ast_log(), LOG_WARNING, ast_calendar::name, icalendar_pvt::owner, icalendar_pvt::secret, and icalendar_pvt::user.
00113 { 00114 struct icalendar_pvt *pvt = userdata; 00115 00116 if (attempts > 1) { 00117 ast_log(LOG_WARNING, "Invalid username or password for iCalendar '%s'\n", pvt->owner->name); 00118 return -1; 00119 } 00120 00121 ne_strnzcpy(username, pvt->user, NE_ABUFSIZ); 00122 ne_strnzcpy(secret, pvt->secret, NE_ABUFSIZ); 00123 00124 return 0; 00125 }
static icalcomponent* fetch_icalendar | ( | struct icalendar_pvt * | pvt | ) | [static] |
Definition at line 127 of file res_calendar_icalendar.c.
References ast_free, ast_log(), ast_str_buffer(), ast_str_create(), ast_str_strlen(), ast_strlen_zero(), fetch_response_reader(), LOG_ERROR, LOG_WARNING, ast_calendar::name, icalendar_pvt::owner, icalendar_pvt::session, icalendar_pvt::uri, and icalendar_pvt::url.
Referenced by ical_load_calendar().
00128 { 00129 int ret; 00130 struct ast_str *response; 00131 ne_request *req; 00132 icalcomponent *comp = NULL; 00133 00134 if (!pvt) { 00135 ast_log(LOG_ERROR, "There is no private!\n"); 00136 } 00137 00138 if (!(response = ast_str_create(512))) { 00139 ast_log(LOG_ERROR, "Could not allocate memory for response.\n"); 00140 return NULL; 00141 } 00142 00143 req = ne_request_create(pvt->session, "GET", pvt->uri.path); 00144 ne_add_response_body_reader(req, ne_accept_2xx, fetch_response_reader, &response); 00145 00146 ret = ne_request_dispatch(req); 00147 ne_request_destroy(req); 00148 if (ret != NE_OK || !ast_str_strlen(response)) { 00149 ast_log(LOG_WARNING, "Unable to retrieve iCalendar '%s' from '%s': %s\n", pvt->owner->name, pvt->url, ne_get_error(pvt->session)); 00150 ast_free(response); 00151 return NULL; 00152 } 00153 00154 if (!ast_strlen_zero(ast_str_buffer(response))) { 00155 comp = icalparser_parse_string(ast_str_buffer(response)); 00156 } 00157 ast_free(response); 00158 00159 return comp; 00160 }
static int fetch_response_reader | ( | void * | data, | |
const char * | block, | |||
size_t | len | |||
) | [static] |
Definition at line 96 of file res_calendar_icalendar.c.
References ast_free, ast_malloc, and ast_str_append().
00097 { 00098 struct ast_str **response = data; 00099 unsigned char *tmp; 00100 00101 if (!(tmp = ast_malloc(len + 1))) { 00102 return -1; 00103 } 00104 memcpy(tmp, block, len); 00105 tmp[len] = '\0'; 00106 ast_str_append(response, 0, "%s", tmp); 00107 ast_free(tmp); 00108 00109 return 0; 00110 }
static void * ical_load_calendar | ( | void * | data | ) | [static] |
Definition at line 346 of file res_calendar_icalendar.c.
References ao2_alloc, ao2_trylock, ao2_unlock, ast_calendar_config_acquire(), ast_calendar_config_release(), ast_calendar_event_container_alloc(), ast_cond_timedwait, ast_debug, ast_log(), ast_mutex_init, ast_mutex_lock, ast_mutex_unlock, ast_string_field_init, ast_string_field_set, ast_strlen_zero(), ast_tvnow(), ast_variable_browse(), auth_credentials(), fetch_icalendar(), icalendar_destructor(), icalendar_update_events(), LOG_ERROR, LOG_WARNING, ast_variable::name, ast_calendar::name, ast_variable::next, refreshlock, secret, ast_calendar::tech_pvt, tv, ast_calendar::unloading, unref_icalendar(), url, and ast_variable::value.
00347 { 00348 struct icalendar_pvt *pvt; 00349 const struct ast_config *cfg; 00350 struct ast_variable *v; 00351 struct ast_calendar *cal = void_data; 00352 ast_mutex_t refreshlock; 00353 00354 if (!(cal && (cfg = ast_calendar_config_acquire()))) { 00355 ast_log(LOG_ERROR, "You must enable calendar support for res_icalendar to load\n"); 00356 return NULL; 00357 } 00358 if (ao2_trylock(cal)) { 00359 if (cal->unloading) { 00360 ast_log(LOG_WARNING, "Unloading module, load_calendar cancelled.\n"); 00361 } else { 00362 ast_log(LOG_WARNING, "Could not lock calendar, aborting!\n"); 00363 } 00364 ast_calendar_config_release(); 00365 return NULL; 00366 } 00367 00368 if (!(pvt = ao2_alloc(sizeof(*pvt), icalendar_destructor))) { 00369 ast_log(LOG_ERROR, "Could not allocate icalendar_pvt structure for calendar: %s\n", cal->name); 00370 ast_calendar_config_release(); 00371 return NULL; 00372 } 00373 00374 pvt->owner = cal; 00375 00376 if (!(pvt->events = ast_calendar_event_container_alloc())) { 00377 ast_log(LOG_ERROR, "Could not allocate space for fetching events for calendar: %s\n", cal->name); 00378 pvt = unref_icalendar(pvt); 00379 ao2_unlock(cal); 00380 ast_calendar_config_release(); 00381 return NULL; 00382 } 00383 00384 if (ast_string_field_init(pvt, 32)) { 00385 ast_log(LOG_ERROR, "Couldn't allocate string field space for calendar: %s\n", cal->name); 00386 pvt = unref_icalendar(pvt); 00387 ao2_unlock(cal); 00388 ast_calendar_config_release(); 00389 return NULL; 00390 } 00391 00392 for (v = ast_variable_browse(cfg, cal->name); v; v = v->next) { 00393 if (!strcasecmp(v->name, "url")) { 00394 ast_string_field_set(pvt, url, v->value); 00395 } else if (!strcasecmp(v->name, "user")) { 00396 ast_string_field_set(pvt, user, v->value); 00397 } else if (!strcasecmp(v->name, "secret")) { 00398 ast_string_field_set(pvt, secret, v->value); 00399 } 00400 } 00401 00402 ast_calendar_config_release(); 00403 00404 if (ast_strlen_zero(pvt->url)) { 00405 ast_log(LOG_WARNING, "No URL was specified for iCalendar '%s' - skipping.\n", cal->name); 00406 pvt = unref_icalendar(pvt); 00407 ao2_unlock(cal); 00408 return NULL; 00409 } 00410 00411 if (ne_uri_parse(pvt->url, &pvt->uri) || pvt->uri.host == NULL || pvt->uri.path == NULL) { 00412 ast_log(LOG_WARNING, "Could not parse url '%s' for iCalendar '%s' - skipping.\n", pvt->url, cal->name); 00413 pvt = unref_icalendar(pvt); 00414 ao2_unlock(cal); 00415 return NULL; 00416 } 00417 00418 if (pvt->uri.scheme == NULL) { 00419 pvt->uri.scheme = "http"; 00420 } 00421 00422 if (pvt->uri.port == 0) { 00423 pvt->uri.port = ne_uri_defaultport(pvt->uri.scheme); 00424 } 00425 00426 pvt->session = ne_session_create(pvt->uri.scheme, pvt->uri.host, pvt->uri.port); 00427 ne_redirect_register(pvt->session); 00428 ne_set_server_auth(pvt->session, auth_credentials, pvt); 00429 if (!strcasecmp(pvt->uri.scheme, "https")) { 00430 ne_ssl_trust_default_ca(pvt->session); 00431 } 00432 00433 cal->tech_pvt = pvt; 00434 00435 ast_mutex_init(&refreshlock); 00436 00437 /* Load it the first time */ 00438 if (!(pvt->data = fetch_icalendar(pvt))) { 00439 ast_log(LOG_WARNING, "Unable to parse iCalendar '%s'\n", cal->name); 00440 } 00441 00442 icalendar_update_events(pvt); 00443 00444 ao2_unlock(cal); 00445 00446 /* The only writing from another thread will be if unload is true */ 00447 for(;;) { 00448 struct timeval tv = ast_tvnow(); 00449 struct timespec ts = {0,}; 00450 00451 ts.tv_sec = tv.tv_sec + (60 * pvt->owner->refresh); 00452 00453 ast_mutex_lock(&refreshlock); 00454 while (!pvt->owner->unloading) { 00455 if (ast_cond_timedwait(&pvt->owner->unload, &refreshlock, &ts) == ETIMEDOUT) { 00456 break; 00457 } 00458 } 00459 ast_mutex_unlock(&refreshlock); 00460 00461 if (pvt->owner->unloading) { 00462 ast_debug(10, "Skipping refresh since we got a shutdown signal\n"); 00463 return NULL; 00464 } 00465 00466 ast_debug(10, "Refreshing after %d minute timeout\n", pvt->owner->refresh); 00467 00468 if (!(pvt->data = fetch_icalendar(pvt))) { 00469 ast_log(LOG_WARNING, "Unable to parse iCalendar '%s'\n", pvt->owner->name); 00470 continue; 00471 } 00472 00473 icalendar_update_events(pvt); 00474 } 00475 00476 return NULL; 00477 }
static void icalendar_add_event | ( | icalcomponent * | comp, | |
struct icaltime_span * | span, | |||
void * | data | |||
) | [static] |
Definition at line 188 of file res_calendar_icalendar.c.
References ast_calendar_event::alarm, ao2_link, AST_CALENDAR_BS_BUSY, AST_CALENDAR_BS_FREE, ast_calendar_event_alloc(), ast_calendar_unref_event(), ast_calloc, AST_LIST_INSERT_TAIL, ast_log(), ast_strdup, ast_string_field_set, ast_strlen_zero(), ast_calendar_event::attendees, ast_calendar_event::busy_state, ast_calendar_event::end, icalendar_pvt::events, icalfloat_to_timet(), LOG_ERROR, LOG_WARNING, icalendar_pvt::owner, ast_calendar_event::priority, ast_calendar_event::start, and ast_calendar_event::summary.
Referenced by icalendar_update_events().
00189 { 00190 struct icalendar_pvt *pvt = data; 00191 struct ast_calendar_event *event; 00192 icaltimezone *utc = icaltimezone_get_utc_timezone(); 00193 icaltimetype start, end, tmp; 00194 icalcomponent *valarm; 00195 icalproperty *prop; 00196 struct icaltriggertype trigger; 00197 00198 if (!(pvt && pvt->owner)) { 00199 ast_log(LOG_ERROR, "Require a private structure with an ownenr\n"); 00200 return; 00201 } 00202 00203 if (!(event = ast_calendar_event_alloc(pvt->owner))) { 00204 ast_log(LOG_ERROR, "Could not allocate an event!\n"); 00205 return; 00206 } 00207 00208 start = icalcomponent_get_dtstart(comp); 00209 end = icalcomponent_get_dtend(comp); 00210 00211 event->start = icaltime_get_tzid(start) ? span->start : icalfloat_to_timet(start); 00212 event->end = icaltime_get_tzid(end) ? span->end : icalfloat_to_timet(end); 00213 event->busy_state = span->is_busy ? AST_CALENDAR_BS_BUSY : AST_CALENDAR_BS_FREE; 00214 00215 if ((prop = icalcomponent_get_first_property(comp, ICAL_SUMMARY_PROPERTY))) { 00216 ast_string_field_set(event, summary, icalproperty_get_value_as_string(prop)); 00217 } 00218 00219 if ((prop = icalcomponent_get_first_property(comp, ICAL_DESCRIPTION_PROPERTY))) { 00220 ast_string_field_set(event, description, icalproperty_get_value_as_string(prop)); 00221 } 00222 00223 if ((prop = icalcomponent_get_first_property(comp, ICAL_ORGANIZER_PROPERTY))) { 00224 ast_string_field_set(event, organizer, icalproperty_get_value_as_string(prop)); 00225 } 00226 00227 if ((prop = icalcomponent_get_first_property(comp, ICAL_LOCATION_PROPERTY))) { 00228 ast_string_field_set(event, location, icalproperty_get_value_as_string(prop)); 00229 } 00230 00231 if ((prop = icalcomponent_get_first_property(comp, ICAL_CATEGORIES_PROPERTY))) { 00232 ast_string_field_set(event, categories, icalproperty_get_value_as_string(prop)); 00233 } 00234 00235 if ((prop = icalcomponent_get_first_property(comp, ICAL_PRIORITY_PROPERTY))) { 00236 event->priority = icalvalue_get_integer(icalproperty_get_value(prop)); 00237 } 00238 00239 if ((prop = icalcomponent_get_first_property(comp, ICAL_UID_PROPERTY))) { 00240 ast_string_field_set(event, uid, icalproperty_get_value_as_string(prop)); 00241 } else { 00242 ast_log(LOG_WARNING, "No UID found, but one is required. Generating, but updates may not be acurate\n"); 00243 if (!ast_strlen_zero(event->summary)) { 00244 ast_string_field_set(event, uid, event->summary); 00245 } else { 00246 char tmp[100]; 00247 snprintf(tmp, sizeof(tmp), "%lu", event->start); 00248 ast_string_field_set(event, uid, tmp); 00249 } 00250 } 00251 00252 /* Get the attendees */ 00253 for (prop = icalcomponent_get_first_property(comp, ICAL_ATTENDEE_PROPERTY); 00254 prop; prop = icalcomponent_get_next_property(comp, ICAL_ATTENDEE_PROPERTY)) { 00255 struct ast_calendar_attendee *attendee; 00256 const char *data; 00257 00258 if (!(attendee = ast_calloc(1, sizeof(*attendee)))) { 00259 event = ast_calendar_unref_event(event); 00260 return; 00261 } 00262 data = icalproperty_get_attendee(prop); 00263 if (!ast_strlen_zero(data)) { 00264 attendee->data = ast_strdup(data);; 00265 AST_LIST_INSERT_TAIL(&event->attendees, attendee, next); 00266 } 00267 } 00268 00269 00270 /* Only set values for alarm based on VALARM. Can be overriden in main/calendar.c by autoreminder 00271 * therefore, go ahead and add events even if their is no VALARM or it is malformed 00272 * Currently we are only getting the first VALARM and are handling repitition in main/calendar.c from calendar.conf */ 00273 if (!(valarm = icalcomponent_get_first_component(comp, ICAL_VALARM_COMPONENT))) { 00274 ao2_link(pvt->events, event); 00275 event = ast_calendar_unref_event(event); 00276 return; 00277 } 00278 00279 if (!(prop = icalcomponent_get_first_property(valarm, ICAL_TRIGGER_PROPERTY))) { 00280 ast_log(LOG_WARNING, "VALARM has no TRIGGER, skipping!\n"); 00281 ao2_link(pvt->events, event); 00282 event = ast_calendar_unref_event(event); 00283 return; 00284 } 00285 00286 trigger = icalproperty_get_trigger(prop); 00287 00288 if (icaltriggertype_is_null_trigger(trigger)) { 00289 ast_log(LOG_WARNING, "Bad TRIGGER for VALARM, skipping!\n"); 00290 ao2_link(pvt->events, event); 00291 event = ast_calendar_unref_event(event); 00292 return; 00293 } 00294 00295 if (!icaltime_is_null_time(trigger.time)) { /* This is an absolute time */ 00296 tmp = icaltime_convert_to_zone(trigger.time, utc); 00297 event->alarm = icaltime_as_timet_with_zone(tmp, utc); 00298 } else { /* Offset from either dtstart or dtend */ 00299 /* XXX Technically you can check RELATED to see if the event fires from the END of the event 00300 * But, I'm not sure I've ever seen anyone implement it in calendaring software, so I'm ignoring for now */ 00301 tmp = icaltime_add(start, trigger.duration); 00302 event->alarm = icaltime_as_timet_with_zone(tmp, utc); 00303 } 00304 00305 ao2_link(pvt->events, event); 00306 event = ast_calendar_unref_event(event); 00307 00308 return; 00309 }
static void icalendar_destructor | ( | void * | obj | ) | [static] |
Definition at line 70 of file res_calendar_icalendar.c.
References ao2_callback, ao2_ref, ast_debug, ast_string_field_free_memory, icalendar_pvt::data, icalendar_pvt::events, ast_calendar::name, OBJ_MULTIPLE, OBJ_NODATA, OBJ_UNLINK, icalendar_pvt::owner, and icalendar_pvt::session.
Referenced by ical_load_calendar().
00071 { 00072 struct icalendar_pvt *pvt = obj; 00073 00074 ast_debug(1, "Destroying pvt for iCalendar %s\n", pvt->owner->name); 00075 if (pvt->session) { 00076 ne_session_destroy(pvt->session); 00077 } 00078 if (pvt->data) { 00079 icalcomponent_free(pvt->data); 00080 } 00081 ast_string_field_free_memory(pvt); 00082 00083 ao2_callback(pvt->events, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL); 00084 00085 ao2_ref(pvt->events, -1); 00086 }
static void icalendar_update_events | ( | struct icalendar_pvt * | pvt | ) | [static] |
Definition at line 311 of file res_calendar_icalendar.c.
References ast_calendar_merge_events(), ast_log(), icalendar_pvt::data, icalendar_pvt::events, icalendar_add_event(), LOG_ERROR, icalendar_pvt::owner, and ast_calendar::timeframe.
Referenced by ical_load_calendar().
00312 { 00313 struct icaltimetype start_time, end_time; 00314 icalcomponent *iter; 00315 00316 if (!pvt) { 00317 ast_log(LOG_ERROR, "iCalendar is NULL\n"); 00318 return; 00319 } 00320 00321 if (!pvt->owner) { 00322 ast_log(LOG_ERROR, "iCalendar is an orphan!\n"); 00323 return; 00324 } 00325 00326 if (!pvt->data) { 00327 ast_log(LOG_ERROR, "The iCalendar has not been parsed!\n"); 00328 return; 00329 } 00330 00331 start_time = icaltime_current_time_with_zone(icaltimezone_get_utc_timezone()); 00332 end_time = icaltime_current_time_with_zone(icaltimezone_get_utc_timezone()); 00333 end_time.second += pvt->owner->timeframe * 60; 00334 icaltime_normalize(end_time); 00335 00336 for (iter = icalcomponent_get_first_component(pvt->data, ICAL_VEVENT_COMPONENT); 00337 iter; 00338 iter = icalcomponent_get_next_component(pvt->data, ICAL_VEVENT_COMPONENT)) 00339 { 00340 icalcomponent_foreach_recurrence(iter, start_time, end_time, icalendar_add_event, pvt); 00341 } 00342 00343 ast_calendar_merge_events(pvt->owner, pvt->events); 00344 }
static time_t icalfloat_to_timet | ( | icaltimetype | time | ) | [static] |
Definition at line 162 of file res_calendar_icalendar.c.
References ast_mktime(), ast_tm::tm_hour, ast_tm::tm_isdst, ast_tm::tm_mday, ast_tm::tm_min, ast_tm::tm_mon, ast_tm::tm_sec, and ast_tm::tm_year.
00163 { 00164 struct ast_tm tm = {0,}; 00165 struct timeval tv; 00166 00167 tm.tm_mday = time.day; 00168 tm.tm_mon = time.month - 1; 00169 tm.tm_year = time.year - 1900; 00170 tm.tm_hour = time.hour; 00171 tm.tm_min = time.minute; 00172 tm.tm_sec = time.second; 00173 tm.tm_isdst = -1; 00174 tv = ast_mktime(&tm, NULL); 00175 00176 return tv.tv_sec; 00177 }
static int load_module | ( | void | ) | [static] |
Definition at line 479 of file res_calendar_icalendar.c.
References ast_calendar_register(), AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_SUCCESS, and ical_tech.
00480 { 00481 ne_sock_init(); 00482 if (ast_calendar_register(&ical_tech)) { 00483 ne_sock_exit(); 00484 return AST_MODULE_LOAD_DECLINE; 00485 } 00486 00487 return AST_MODULE_LOAD_SUCCESS; 00488 }
static int unload_module | ( | void | ) | [static] |
Definition at line 490 of file res_calendar_icalendar.c.
References ast_calendar_unregister(), and ical_tech.
00491 { 00492 ast_calendar_unregister(&ical_tech); 00493 ne_sock_exit(); 00494 return 0; 00495 }
static void * unref_icalendar | ( | void * | obj | ) | [static] |
Definition at line 88 of file res_calendar_icalendar.c.
References ao2_ref.
Referenced by ical_load_calendar().
00089 { 00090 struct icalendar_pvt *pvt = obj; 00091 00092 ao2_ref(pvt, -1); 00093 return NULL; 00094 }
struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Asterisk iCalendar .ics file integration" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "88eaa8f5c1bd988bedd71113385e0886" , .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEVSTATE_PLUGIN, } [static] |
Definition at line 501 of file res_calendar_icalendar.c.
struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 501 of file res_calendar_icalendar.c.
struct ast_calendar_tech ical_tech [static] |
Definition at line 49 of file res_calendar_icalendar.c.
Referenced by load_module(), and unload_module().