Tue Aug 20 16:35:13 2013

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 = "ac1f6a56484a8820659555499174e588" , .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 509 of file res_calendar_icalendar.c.

static void __unreg_module ( void   )  [static]

Definition at line 509 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, icalendar_pvt::owner, icalendar_pvt::secret, and icalendar_pvt::user.

Referenced by ical_load_calendar().

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, 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       return NULL;
00137    }
00138 
00139    if (!(response = ast_str_create(512))) {
00140       ast_log(LOG_ERROR, "Could not allocate memory for response.\n");
00141       return NULL;
00142    }
00143 
00144    req = ne_request_create(pvt->session, "GET", pvt->uri.path);
00145    ne_add_response_body_reader(req, ne_accept_2xx, fetch_response_reader, &response);
00146 
00147    ret = ne_request_dispatch(req);
00148    ne_request_destroy(req);
00149    if (ret != NE_OK || !ast_str_strlen(response)) {
00150       ast_log(LOG_WARNING, "Unable to retrieve iCalendar '%s' from '%s': %s\n", pvt->owner->name, pvt->url, ne_get_error(pvt->session));
00151       ast_free(response);
00152       return NULL;
00153    }
00154 
00155    if (!ast_strlen_zero(ast_str_buffer(response))) {
00156       comp = icalparser_parse_string(ast_str_buffer(response));
00157    }
00158    ast_free(response);
00159 
00160    return comp;
00161 }

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

Referenced by fetch_icalendar().

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 349 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(), icalendar_pvt::data, icalendar_pvt::events, fetch_icalendar(), icalendar_destructor(), icalendar_update_events(), LOG_ERROR, LOG_WARNING, ast_variable::name, ast_variable::next, icalendar_pvt::owner, ast_calendar::refresh, refreshlock, secret, icalendar_pvt::session, ast_calendar::tech_pvt, ast_calendar::unload, ast_calendar::unloading, unref_icalendar(), icalendar_pvt::uri, icalendar_pvt::url, url, and ast_variable::value.

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

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

Definition at line 189 of file res_calendar_icalendar.c.

References ao2_link, AST_CALENDAR_BS_BUSY, AST_CALENDAR_BS_FREE, ast_calendar_event_alloc(), ast_calendar_unref_event(), ast_calloc, ast_free, AST_LIST_INSERT_TAIL, ast_log(), ast_strdup, ast_string_field_set, ast_strlen_zero(), ast_calendar_attendee::data, ast_calendar_event::end, icalendar_pvt::events, icalfloat_to_timet(), LOG_ERROR, LOG_WARNING, icalendar_pvt::owner, and ast_calendar_event::start.

Referenced by icalendar_update_events().

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

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

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

static time_t icalfloat_to_timet ( icaltimetype  time  )  [static]

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

Referenced by icalendar_add_event().

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

static int load_module ( void   )  [static]

Definition at line 487 of file res_calendar_icalendar.c.

References ast_calendar_register(), AST_MODULE_LOAD_DECLINE, and AST_MODULE_LOAD_SUCCESS.

00488 {
00489    ne_sock_init();
00490    if (ast_calendar_register(&ical_tech)) {
00491       ne_sock_exit();
00492       return AST_MODULE_LOAD_DECLINE;
00493    }
00494 
00495    return AST_MODULE_LOAD_SUCCESS;
00496 }

static int unload_module ( void   )  [static]

Definition at line 498 of file res_calendar_icalendar.c.

References ast_calendar_unregister().

00499 {
00500    ast_calendar_unregister(&ical_tech);
00501    ne_sock_exit();
00502    return 0;
00503 }

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

Definition at line 509 of file res_calendar_icalendar.c.

Definition at line 509 of file res_calendar_icalendar.c.

struct ast_calendar_tech ical_tech [static]

Definition at line 49 of file res_calendar_icalendar.c.


Generated on 20 Aug 2013 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1