Wed Apr 6 11:30:08 2011

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 = "8586c2a7d357cb591cc3a6607a8f62d1" , .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 499 of file res_calendar_icalendar.c.

static void __unreg_module ( void   )  [static]

Definition at line 499 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 110 of file res_calendar_icalendar.c.

References ast_log(), LOG_WARNING, ast_calendar::name, icalendar_pvt::owner, icalendar_pvt::secret, and icalendar_pvt::user.

00111 {
00112    struct icalendar_pvt *pvt = userdata;
00113 
00114    if (attempts > 1) {
00115       ast_log(LOG_WARNING, "Invalid username or password for iCalendar '%s'\n", pvt->owner->name);
00116       return -1;
00117    }
00118 
00119    ne_strnzcpy(username, pvt->user, NE_ABUFSIZ);
00120    ne_strnzcpy(secret, pvt->secret, NE_ABUFSIZ);
00121 
00122    return 0;
00123 }

static icalcomponent* fetch_icalendar ( struct icalendar_pvt pvt  )  [static]

Definition at line 125 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().

00126 {
00127    int ret;
00128    struct ast_str *response;
00129    ne_request *req;
00130    icalcomponent *comp = NULL;
00131 
00132    if (!pvt) {
00133       ast_log(LOG_ERROR, "There is no private!\n");
00134    }
00135 
00136    if (!(response = ast_str_create(512))) {
00137       ast_log(LOG_ERROR, "Could not allocate memory for response.\n");
00138       return NULL;
00139    }
00140 
00141    req = ne_request_create(pvt->session, "GET", pvt->uri.path);
00142    ne_add_response_body_reader(req, ne_accept_2xx, fetch_response_reader, &response);
00143 
00144    ret = ne_request_dispatch(req);
00145    ne_request_destroy(req);
00146    if (ret != NE_OK || !ast_str_strlen(response)) {
00147       ast_log(LOG_WARNING, "Unable to retrieve iCalendar '%s' from '%s': %s\n", pvt->owner->name, pvt->url, ne_get_error(pvt->session));
00148       ast_free(response);
00149       return NULL;
00150    }
00151 
00152    if (!ast_strlen_zero(ast_str_buffer(response))) {
00153       comp = icalparser_parse_string(ast_str_buffer(response));
00154    }
00155    ast_free(response);
00156 
00157    return comp;
00158 }

static int fetch_response_reader ( void *  data,
const char *  block,
size_t  len 
) [static]

Definition at line 94 of file res_calendar_icalendar.c.

References ast_free, ast_malloc, and ast_str_append().

00095 {
00096    struct ast_str **response = data;
00097    unsigned char *tmp;
00098 
00099    if (!(tmp = ast_malloc(len + 1))) {
00100       return -1;
00101    }
00102    memcpy(tmp, block, len);
00103    tmp[len] = '\0';
00104    ast_str_append(response, 0, "%s", tmp);
00105    ast_free(tmp);
00106 
00107    return 0;
00108 }

static void * ical_load_calendar ( void *  data  )  [static]

Definition at line 344 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.

00345 {
00346    struct icalendar_pvt *pvt;
00347    const struct ast_config *cfg;
00348    struct ast_variable *v;
00349    struct ast_calendar *cal = void_data;
00350    ast_mutex_t refreshlock;
00351 
00352    if (!(cal && (cfg = ast_calendar_config_acquire()))) {
00353       ast_log(LOG_ERROR, "You must enable calendar support for res_icalendar to load\n");
00354       return NULL;
00355    }
00356    if (ao2_trylock(cal)) {
00357       if (cal->unloading) {
00358          ast_log(LOG_WARNING, "Unloading module, load_calendar cancelled.\n");
00359       } else {
00360          ast_log(LOG_WARNING, "Could not lock calendar, aborting!\n");
00361       }
00362       ast_calendar_config_release();
00363       return NULL;
00364    }
00365 
00366    if (!(pvt = ao2_alloc(sizeof(*pvt), icalendar_destructor))) {
00367       ast_log(LOG_ERROR, "Could not allocate icalendar_pvt structure for calendar: %s\n", cal->name);
00368       ast_calendar_config_release();
00369       return NULL;
00370    }
00371 
00372    pvt->owner = cal;
00373 
00374    if (!(pvt->events = ast_calendar_event_container_alloc())) {
00375       ast_log(LOG_ERROR, "Could not allocate space for fetching events for calendar: %s\n", cal->name);
00376       pvt = unref_icalendar(pvt);
00377       ao2_unlock(cal);
00378       ast_calendar_config_release();
00379       return NULL;
00380    }
00381 
00382    if (ast_string_field_init(pvt, 32)) {
00383       ast_log(LOG_ERROR, "Couldn't allocate string field space for calendar: %s\n", cal->name);
00384       pvt = unref_icalendar(pvt);
00385       ao2_unlock(cal);
00386       ast_calendar_config_release();
00387       return NULL;
00388    }
00389 
00390    for (v = ast_variable_browse(cfg, cal->name); v; v = v->next) {
00391       if (!strcasecmp(v->name, "url")) {
00392          ast_string_field_set(pvt, url, v->value);
00393       } else if (!strcasecmp(v->name, "user")) {
00394          ast_string_field_set(pvt, user, v->value);
00395       } else if (!strcasecmp(v->name, "secret")) {
00396          ast_string_field_set(pvt, secret, v->value);
00397       }
00398    }
00399 
00400    ast_calendar_config_release();
00401 
00402    if (ast_strlen_zero(pvt->url)) {
00403       ast_log(LOG_WARNING, "No URL was specified for iCalendar '%s' - skipping.\n", cal->name);
00404       pvt = unref_icalendar(pvt);
00405       ao2_unlock(cal);
00406       return NULL;
00407    }
00408 
00409    if (ne_uri_parse(pvt->url, &pvt->uri) || pvt->uri.host == NULL || pvt->uri.path == NULL) {
00410       ast_log(LOG_WARNING, "Could not parse url '%s' for iCalendar '%s' - skipping.\n", pvt->url, cal->name);
00411       pvt = unref_icalendar(pvt);
00412       ao2_unlock(cal);
00413       return NULL;
00414    }
00415 
00416    if (pvt->uri.scheme == NULL) {
00417       pvt->uri.scheme = "http";
00418    }
00419 
00420    if (pvt->uri.port == 0) {
00421       pvt->uri.port = ne_uri_defaultport(pvt->uri.scheme);
00422    }
00423 
00424    pvt->session = ne_session_create(pvt->uri.scheme, pvt->uri.host, pvt->uri.port);
00425    ne_redirect_register(pvt->session);
00426    ne_set_server_auth(pvt->session, auth_credentials, pvt);
00427    if (!strcasecmp(pvt->uri.scheme, "https")) {
00428       ne_ssl_trust_default_ca(pvt->session);
00429    }
00430 
00431    cal->tech_pvt = pvt;
00432 
00433    ast_mutex_init(&refreshlock);
00434 
00435    /* Load it the first time */
00436    if (!(pvt->data = fetch_icalendar(pvt))) {
00437       ast_log(LOG_WARNING, "Unable to parse iCalendar '%s'\n", cal->name);
00438    }
00439 
00440    icalendar_update_events(pvt);
00441 
00442    ao2_unlock(cal);
00443 
00444    /* The only writing from another thread will be if unload is true */
00445    for(;;) {
00446       struct timeval tv = ast_tvnow();
00447       struct timespec ts = {0,};
00448 
00449       ts.tv_sec = tv.tv_sec + (60 * pvt->owner->refresh);
00450 
00451       ast_mutex_lock(&refreshlock);
00452       while (!pvt->owner->unloading) {
00453          if (ast_cond_timedwait(&pvt->owner->unload, &refreshlock, &ts) == ETIMEDOUT) {
00454             break;
00455          }
00456       }
00457       ast_mutex_unlock(&refreshlock);
00458 
00459       if (pvt->owner->unloading) {
00460          ast_debug(10, "Skipping refresh since we got a shutdown signal\n");
00461          return NULL;
00462       }
00463 
00464       ast_debug(10, "Refreshing after %d minute timeout\n", pvt->owner->refresh);
00465 
00466       if (!(pvt->data = fetch_icalendar(pvt))) {
00467          ast_log(LOG_WARNING, "Unable to parse iCalendar '%s'\n", pvt->owner->name);
00468          continue;
00469       }
00470 
00471       icalendar_update_events(pvt);
00472    }
00473 
00474    return NULL;
00475 }

static void icalendar_add_event ( icalcomponent *  comp,
struct icaltime_span *  span,
void *  data 
) [static]

Definition at line 186 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().

00187 {
00188    struct icalendar_pvt *pvt = data;
00189    struct ast_calendar_event *event;
00190    icaltimezone *utc = icaltimezone_get_utc_timezone();
00191    icaltimetype start, end, tmp;
00192    icalcomponent *valarm;
00193    icalproperty *prop;
00194    struct icaltriggertype trigger;
00195 
00196    if (!(pvt && pvt->owner)) {
00197       ast_log(LOG_ERROR, "Require a private structure with an ownenr\n");
00198       return;
00199    }
00200 
00201    if (!(event = ast_calendar_event_alloc(pvt->owner))) {
00202       ast_log(LOG_ERROR, "Could not allocate an event!\n");
00203       return;
00204    }
00205 
00206    start = icalcomponent_get_dtstart(comp);
00207    end = icalcomponent_get_dtend(comp);
00208 
00209    event->start = icaltime_get_tzid(start) ? span->start : icalfloat_to_timet(start);
00210    event->end = icaltime_get_tzid(end) ? span->end : icalfloat_to_timet(end);
00211    event->busy_state = span->is_busy ? AST_CALENDAR_BS_BUSY : AST_CALENDAR_BS_FREE;
00212 
00213    if ((prop = icalcomponent_get_first_property(comp, ICAL_SUMMARY_PROPERTY))) {
00214       ast_string_field_set(event, summary, icalproperty_get_value_as_string(prop));
00215    }
00216 
00217    if ((prop = icalcomponent_get_first_property(comp, ICAL_DESCRIPTION_PROPERTY))) {
00218       ast_string_field_set(event, description, icalproperty_get_value_as_string(prop));
00219    }
00220 
00221    if ((prop = icalcomponent_get_first_property(comp, ICAL_ORGANIZER_PROPERTY))) {
00222       ast_string_field_set(event, organizer, icalproperty_get_value_as_string(prop));
00223    }
00224 
00225    if ((prop = icalcomponent_get_first_property(comp, ICAL_LOCATION_PROPERTY))) {
00226       ast_string_field_set(event, location, icalproperty_get_value_as_string(prop));
00227    }
00228 
00229    if ((prop = icalcomponent_get_first_property(comp, ICAL_CATEGORIES_PROPERTY))) {
00230       ast_string_field_set(event, categories, icalproperty_get_value_as_string(prop));
00231    }
00232 
00233    if ((prop = icalcomponent_get_first_property(comp, ICAL_PRIORITY_PROPERTY))) {
00234       event->priority = icalvalue_get_integer(icalproperty_get_value(prop));
00235    }
00236 
00237    if ((prop = icalcomponent_get_first_property(comp, ICAL_UID_PROPERTY))) {
00238       ast_string_field_set(event, uid, icalproperty_get_value_as_string(prop));
00239    } else {
00240       ast_log(LOG_WARNING, "No UID found, but one is required. Generating, but updates may not be acurate\n");
00241       if (!ast_strlen_zero(event->summary)) {
00242          ast_string_field_set(event, uid, event->summary);
00243       } else {
00244          char tmp[100];
00245          snprintf(tmp, sizeof(tmp), "%lu", event->start);
00246          ast_string_field_set(event, uid, tmp);
00247       }
00248    }
00249 
00250    /* Get the attendees */
00251    for (prop = icalcomponent_get_first_property(comp, ICAL_ATTENDEE_PROPERTY);
00252          prop; prop = icalcomponent_get_next_property(comp, ICAL_ATTENDEE_PROPERTY)) {
00253       struct ast_calendar_attendee *attendee;
00254       const char *data;
00255 
00256       if (!(attendee = ast_calloc(1, sizeof(*attendee)))) {
00257          event = ast_calendar_unref_event(event);
00258          return;
00259       }
00260       data = icalproperty_get_attendee(prop);
00261       if (!ast_strlen_zero(data)) {
00262          attendee->data = ast_strdup(data);;
00263          AST_LIST_INSERT_TAIL(&event->attendees, attendee, next);
00264       }
00265    }
00266 
00267 
00268    /* Only set values for alarm based on VALARM.  Can be overriden in main/calendar.c by autoreminder
00269     * therefore, go ahead and add events even if their is no VALARM or it is malformed
00270     * Currently we are only getting the first VALARM and are handling repitition in main/calendar.c from calendar.conf */
00271    if (!(valarm = icalcomponent_get_first_component(comp, ICAL_VALARM_COMPONENT))) {
00272       ao2_link(pvt->events, event);
00273       event = ast_calendar_unref_event(event);
00274       return;
00275    }
00276 
00277    if (!(prop = icalcomponent_get_first_property(valarm, ICAL_TRIGGER_PROPERTY))) {
00278       ast_log(LOG_WARNING, "VALARM has no TRIGGER, skipping!\n");
00279       ao2_link(pvt->events, event);
00280       event = ast_calendar_unref_event(event);
00281       return;
00282    }
00283 
00284    trigger = icalproperty_get_trigger(prop);
00285 
00286    if (icaltriggertype_is_null_trigger(trigger)) {
00287       ast_log(LOG_WARNING, "Bad TRIGGER for VALARM, skipping!\n");
00288       ao2_link(pvt->events, event);
00289       event = ast_calendar_unref_event(event);
00290       return;
00291    }
00292 
00293    if (!icaltime_is_null_time(trigger.time)) { /* This is an absolute time */
00294       tmp = icaltime_convert_to_zone(trigger.time, utc);
00295       event->alarm = icaltime_as_timet_with_zone(tmp, utc);
00296    } else { /* Offset from either dtstart or dtend */
00297       /* XXX Technically you can check RELATED to see if the event fires from the END of the event
00298        * But, I'm not sure I've ever seen anyone implement it in calendaring software, so I'm ignoring for now */
00299       tmp = icaltime_add(start, trigger.duration);
00300       event->alarm = icaltime_as_timet_with_zone(tmp, utc);
00301    }
00302 
00303    ao2_link(pvt->events, event);
00304    event = ast_calendar_unref_event(event);
00305 
00306    return;
00307 }

static void icalendar_destructor ( void *  obj  )  [static]

Definition at line 68 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().

00069 {
00070    struct icalendar_pvt *pvt = obj;
00071 
00072    ast_debug(1, "Destroying pvt for iCalendar %s\n", pvt->owner->name);
00073    if (pvt->session) {
00074       ne_session_destroy(pvt->session);
00075    }
00076    if (pvt->data) {
00077       icalcomponent_free(pvt->data);
00078    }
00079    ast_string_field_free_memory(pvt);
00080 
00081    ao2_callback(pvt->events, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL);
00082 
00083    ao2_ref(pvt->events, -1);
00084 }

static void icalendar_update_events ( struct icalendar_pvt pvt  )  [static]

Definition at line 309 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().

00310 {
00311    struct icaltimetype start_time, end_time;
00312    icalcomponent *iter;
00313 
00314    if (!pvt) {
00315       ast_log(LOG_ERROR, "iCalendar is NULL\n");
00316       return;
00317    }
00318 
00319    if (!pvt->owner) {
00320       ast_log(LOG_ERROR, "iCalendar is an orphan!\n");
00321       return;
00322    }
00323 
00324    if (!pvt->data) {
00325       ast_log(LOG_ERROR, "The iCalendar has not been parsed!\n");
00326       return;
00327    }
00328 
00329    start_time = icaltime_current_time_with_zone(icaltimezone_get_utc_timezone());
00330    end_time = icaltime_current_time_with_zone(icaltimezone_get_utc_timezone());
00331    end_time.second += pvt->owner->timeframe * 60;
00332    icaltime_normalize(end_time);
00333 
00334    for (iter = icalcomponent_get_first_component(pvt->data, ICAL_VEVENT_COMPONENT);
00335         iter;
00336        iter = icalcomponent_get_next_component(pvt->data, ICAL_VEVENT_COMPONENT))
00337    {
00338       icalcomponent_foreach_recurrence(iter, start_time, end_time, icalendar_add_event, pvt);
00339    }
00340 
00341    ast_calendar_merge_events(pvt->owner, pvt->events);
00342 }

static time_t icalfloat_to_timet ( icaltimetype  time  )  [static]

Definition at line 160 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.

00161 {
00162    struct ast_tm tm = {0,};
00163    struct timeval tv;
00164 
00165    tm.tm_mday = time.day;
00166    tm.tm_mon = time.month - 1;
00167    tm.tm_year = time.year - 1900;
00168    tm.tm_hour = time.hour;
00169    tm.tm_min = time.minute;
00170    tm.tm_sec = time.second;
00171    tm.tm_isdst = -1;
00172    tv = ast_mktime(&tm, NULL);
00173 
00174    return tv.tv_sec;
00175 }

static int load_module ( void   )  [static]

Definition at line 477 of file res_calendar_icalendar.c.

References ast_calendar_register(), AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_SUCCESS, and ical_tech.

00478 {
00479    ne_sock_init();
00480    if (ast_calendar_register(&ical_tech)) {
00481       ne_sock_exit();
00482       return AST_MODULE_LOAD_DECLINE;
00483    }
00484 
00485    return AST_MODULE_LOAD_SUCCESS;
00486 }

static int unload_module ( void   )  [static]

Definition at line 488 of file res_calendar_icalendar.c.

References ast_calendar_unregister(), and ical_tech.

00489 {
00490    ast_calendar_unregister(&ical_tech);
00491    ne_sock_exit();
00492    return 0;
00493 }

static void * unref_icalendar ( void *  obj  )  [static]

Definition at line 86 of file res_calendar_icalendar.c.

References ao2_ref.

Referenced by ical_load_calendar().

00087 {
00088    struct icalendar_pvt *pvt = obj;
00089 
00090    ao2_ref(pvt, -1);
00091    return NULL;
00092 }


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 = "8586c2a7d357cb591cc3a6607a8f62d1" , .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEVSTATE_PLUGIN, } [static]

Definition at line 499 of file res_calendar_icalendar.c.

struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 499 of file res_calendar_icalendar.c.

struct ast_calendar_tech ical_tech [static]

Definition at line 47 of file res_calendar_icalendar.c.

Referenced by load_module(), and unload_module().


Generated on Wed Apr 6 11:30:08 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7