Sat Mar 10 01:55:40 2012

Asterisk developer's documentation


res_calendar_icalendar.c File Reference

Resource for handling iCalendar calendars. More...

#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_infoast_module_info = &__mod_info
static struct ast_calendar_tech ical_tech


Detailed Description

Resource for handling iCalendar calendars.

Definition in file res_calendar_icalendar.c.


Function Documentation

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 }


Variable Documentation

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().


Generated on Sat Mar 10 01:55:40 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7