33#include <libical/ical.h>
35#if ICAL_MAJOR_VERSION >= 4
36#define ICAL_SPAN_CONST const
38#define ICAL_SPAN_CONST
40#include <ne_session.h>
42#include <ne_request.h>
44#include <ne_redirect.h>
45#include <libxml/xmlreader.h>
60 .description =
"CalDAV calendars",
85 ne_session_destroy(pvt->
session);
87 ne_uri_free(&pvt->
uri);
105 struct ast_str **response = data;
111 memcpy(tmp, block,
len);
119static int auth_credentials(
void *userdata,
const char *realm,
int attempts,
char *username,
char *secret)
128 ne_strnzcpy(username, pvt->
user, NE_ABUFSIZ);
136 if (st->code < 200 || st->code > 299) {
137 if (st->code == 401) {
138 ast_debug(1,
"Got a 401 from the server but we expect this to happen when authenticating, %d: %s\n", st->code, st->reason_phrase);
140 ast_debug(1,
"Unexpected response from server, %d: %s\n", st->code, st->reason_phrase);
169 ne_add_request_header(req,
"Content-type",
ast_strlen_zero(content_type) ?
"text/xml" : content_type);
170 ne_add_request_header(req,
"Depth",
"1");
172 ret = ne_request_dispatch(req);
173 ne_request_destroy(req);
188 icalcomponent *calendar, *icalevent;
189 icaltimezone *utc = icaltimezone_get_utc_timezone();
210 unsigned short val[8];
212 for (x = 0; x < 8; x++) {
216 (
unsigned)
val[0], (
unsigned)
val[1], (
unsigned)
val[2],
217 (
unsigned)
val[3], (
unsigned)
val[4], (
unsigned)
val[5],
218 (
unsigned)
val[6], (
unsigned)
val[7]);
221 calendar = icalcomponent_new(ICAL_VCALENDAR_COMPONENT);
222 icalcomponent_add_property(calendar, icalproperty_new_version(
"2.0"));
223 icalcomponent_add_property(calendar, icalproperty_new_prodid(
"-//Digium, Inc.//res_caldav//EN"));
225 icalevent = icalcomponent_new(ICAL_VEVENT_COMPONENT);
226 icalcomponent_add_property(icalevent, icalproperty_new_dtstamp(icaltime_current_time_with_zone(utc)));
227 icalcomponent_add_property(icalevent, icalproperty_new_uid(
event->uid));
228 icalcomponent_add_property(icalevent, icalproperty_new_dtstart(icaltime_from_timet_with_zone(
event->start, 0, utc)));
229 icalcomponent_add_property(icalevent, icalproperty_new_dtend(icaltime_from_timet_with_zone(
event->end, 0, utc)));
231 icalcomponent_add_property(icalevent, icalproperty_new_organizer(
event->organizer));
234 icalcomponent_add_property(icalevent, icalproperty_new_summary(
event->summary));
237 icalcomponent_add_property(icalevent, icalproperty_new_description(
event->description));
240 icalcomponent_add_property(icalevent, icalproperty_new_location(
event->location));
243 icalcomponent_add_property(icalevent, icalproperty_new_categories(
event->categories));
245 if (
event->priority > 0) {
246 icalcomponent_add_property(icalevent, icalproperty_new_priority(
event->priority));
249 switch (
event->busy_state) {
251 icalcomponent_add_property(icalevent, icalproperty_new_status(ICAL_STATUS_CONFIRMED));
255 icalcomponent_add_property(icalevent, icalproperty_new_status(ICAL_STATUS_TENTATIVE));
259 icalcomponent_add_property(icalevent, icalproperty_new_status(ICAL_STATUS_NONE));
262 icalcomponent_add_component(calendar, icalevent);
264 ast_str_append(&body, 0,
"%s", icalcomponent_as_ical_string(calendar));
265 ast_str_set(&subdir, 0,
"%s%s.ics", pvt->
url[strlen(pvt->
url) - 1] ==
'/' ?
"" :
"/",
event->uid);
267 if ((response =
caldav_request(pvt,
"PUT", body, subdir,
"text/calendar"))) {
287 struct ast_str *body, *response;
288 icaltimezone *utc = icaltimezone_get_utc_timezone();
289 icaltimetype start,
end;
290 const char *start_str, *end_str;
297 start = icaltime_from_timet_with_zone(start_time, 0, utc);
298 end = icaltime_from_timet_with_zone(end_time, 0, utc);
299 start_str = icaltime_as_ical_string(start);
300 end_str = icaltime_as_ical_string(
end);
307 "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
308 "<C:calendar-query xmlns:D=\"DAV:\" xmlns:C=\"urn:ietf:params:xml:ns:caldav\">\n"
310 " <C:calendar-data>\n"
311 " <C:expand start=\"%s\" end=\"%s\"/>\n"
312 " </C:calendar-data>\n"
315 " <C:comp-filter name=\"VCALENDAR\">\n"
316 " <C:comp-filter name=\"VEVENT\">\n"
317 " <C:time-range start=\"%s\" end=\"%s\"/>\n"
318 " </C:comp-filter>\n"
319 " </C:comp-filter>\n"
321 "</C:calendar-query>\n", start_str, end_str, start_str, end_str);
339 tm.
tm_mon = time.month - 1;
366 struct icaltriggertype trigger;
368 if (!(pvt && pvt->
owner)) {
378 start = icalcomponent_get_dtstart(comp);
379 end = icalcomponent_get_dtend(comp);
385 if ((prop = icalcomponent_get_first_property(comp, ICAL_SUMMARY_PROPERTY))) {
389 if ((prop = icalcomponent_get_first_property(comp, ICAL_DESCRIPTION_PROPERTY))) {
393 if ((prop = icalcomponent_get_first_property(comp, ICAL_ORGANIZER_PROPERTY))) {
397 if ((prop = icalcomponent_get_first_property(comp, ICAL_LOCATION_PROPERTY))) {
401 if ((prop = icalcomponent_get_first_property(comp, ICAL_CATEGORIES_PROPERTY))) {
405 if ((prop = icalcomponent_get_first_property(comp, ICAL_PRIORITY_PROPERTY))) {
406 event->priority = icalvalue_get_integer(icalproperty_get_value(prop));
409 if ((prop = icalcomponent_get_first_property(comp, ICAL_UID_PROPERTY))) {
412 ast_log(
LOG_WARNING,
"No UID found, but one is required. Generating, but updates may not be accurate\n");
423 for (prop = icalcomponent_get_first_property(comp, ICAL_ATTENDEE_PROPERTY);
424 prop; prop = icalcomponent_get_next_property(comp, ICAL_ATTENDEE_PROPERTY)) {
428 if (!(attendee =
ast_calloc(1,
sizeof(*attendee)))) {
432 data = icalproperty_get_attendee(prop);
445 if (!(valarm = icalcomponent_get_first_component(comp, ICAL_VALARM_COMPONENT))) {
451 if (!(prop = icalcomponent_get_first_property(valarm, ICAL_TRIGGER_PROPERTY))) {
458 trigger = icalproperty_get_trigger(prop);
460 if (icaltriggertype_is_null_trigger(trigger)) {
467 if (!icaltime_is_null_time(trigger.time)) {
468 tmp = icaltime_convert_to_zone(trigger.time, utc);
469 event->alarm = icaltime_as_timet_with_zone(tmp, utc);
473#if ICAL_MAJOR_VERSION >= 4
475 int sign = trigger.duration.is_neg ? -1 : 1;
476 icaltime_adjust(&tmp,
477 sign * (trigger.duration.days + trigger.duration.weeks * 7),
478 sign * trigger.duration.hours,
479 sign * trigger.duration.minutes,
480 sign * trigger.duration.seconds);
482 tmp = icaltime_add(start, trigger.duration);
484 event->alarm = icaltime_as_timet_with_zone(tmp, icaltime_get_timezone(start));
506 const xmlChar *localname = xmlTextReaderConstLocalName(reader);
507 const xmlChar *uri = xmlTextReaderConstNamespaceUri(reader);
513 state->in_caldata = 1;
519 struct icaltimetype start,
end;
520 icaltimezone *utc = icaltimezone_get_utc_timezone();
523 const xmlChar *localname = xmlTextReaderConstLocalName(reader);
524 const xmlChar *uri = xmlTextReaderConstNamespaceUri(reader);
530 state->in_caldata = 0;
536 start = icaltime_from_timet_with_zone(
state->start, 0, utc);
537 end = icaltime_from_timet_with_zone(
state->end, 0, utc);
540 for (iter = icalcomponent_get_first_component(comp, ICAL_VEVENT_COMPONENT);
542 iter = icalcomponent_get_next_component(comp, ICAL_VEVENT_COMPONENT))
547 icalcomponent_free(comp);
554 if (!
state->in_caldata) {
558 text = xmlTextReaderValue(reader);
566 xmlParserSeverities
severity, xmlTextReaderLocatorPtr locator)
569 case XML_PARSER_SEVERITY_VALIDITY_WARNING:
570 case XML_PARSER_SEVERITY_WARNING:
572 xmlTextReaderLocatorLineNumber(locator),
575 case XML_PARSER_SEVERITY_VALIDITY_ERROR:
576 case XML_PARSER_SEVERITY_ERROR:
579 xmlTextReaderLocatorLineNumber(locator),
590 xmlTextReaderPtr reader;
667 ast_log(
LOG_ERROR,
"You must enable calendar support for res_caldav to load\n");
690 ast_log(
LOG_ERROR,
"Could not allocate space for fetching events for calendar: %s\n", cal->
name);
706 if (!strcasecmp(v->
name,
"url")) {
708 }
else if (!strcasecmp(v->
name,
"user")) {
710 }
else if (!strcasecmp(v->
name,
"secret")) {
732 pvt->
uri.scheme =
"http";
735 if (pvt->
uri.port == 0) {
736 pvt->
uri.port = ne_uri_defaultport(pvt->
uri.scheme);
739 pvt->
session = ne_session_create(pvt->
uri.scheme, pvt->
uri.host, pvt->
uri.port);
740 ne_redirect_register(pvt->
session);
742 if (!strcasecmp(pvt->
uri.scheme,
"https")) {
743 ne_ssl_trust_default_ca(pvt->
session);
759 struct timespec ts = {0,};
772 ast_debug(10,
"Skipping refresh since we got a shutdown signal\n");
807 .
requires =
"res_calendar",
Asterisk main include file. File version handling, generic pbx functions.
#define ast_strdup(str)
A wrapper for strdup()
#define ast_calloc(num, len)
A wrapper for calloc()
#define ast_malloc(len)
A wrapper for malloc()
#define ao2_link(container, obj)
Add an object to a container.
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container,...
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
#define ao2_alloc(data_size, destructor_fn)
A general API for managing calendar events with Asterisk.
void ast_calendar_unregister(struct ast_calendar_tech *tech)
Unregister a new calendar technology.
void ast_calendar_merge_events(struct ast_calendar *cal, struct ao2_container *new_events)
Add an event to the list of events for a calendar.
void ast_calendar_config_release(void)
Release the calendar config.
@ AST_CALENDAR_BS_BUSY_TENTATIVE
struct ao2_container * ast_calendar_event_container_alloc(void)
Allocate an astobj2 container for ast_calendar_event objects.
int ast_calendar_register(struct ast_calendar_tech *tech)
Register a new calendar technology.
struct ast_calendar_event * ast_calendar_event_alloc(struct ast_calendar *cal)
Allocate an astobj2 ast_calendar_event object.
struct ast_calendar_event * ast_calendar_unref_event(struct ast_calendar_event *event)
Unreference an ast_calendar_event.
const struct ast_config * ast_calendar_config_acquire(void)
Grab and lock pointer to the calendar config (read only)
General Asterisk PBX channel definitions.
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
Configuration File Parser.
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
#define ast_debug(level,...)
Log a DEBUG message.
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
struct timeval ast_mktime(struct ast_tm *const tmp, const char *zone)
Timezone-independent version of mktime(3).
Asterisk locking-related definitions:
#define ast_cond_timedwait(cond, mutex, time)
#define ast_mutex_init(pmutex)
#define ast_mutex_unlock(a)
#define ast_mutex_lock(a)
enum ast_security_event_severity severity
Asterisk module definitions.
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
@ AST_MODPRI_DEVSTATE_PLUGIN
@ AST_MODULE_SUPPORT_EXTENDED
#define ASTERISK_GPL_KEY
The text the key() function should return.
@ AST_MODULE_LOAD_SUCCESS
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
static ast_mutex_t refreshlock
static struct ast_calendar_tech caldav_tech
static void handle_start_element(xmlTextReaderPtr reader, struct xmlstate *state)
static void * unref_caldav(void *obj)
static int update_caldav(struct caldav_pvt *pvt)
static struct ast_str * caldav_get_events_between(struct caldav_pvt *pvt, time_t start_time, time_t end_time)
static int debug_response_handler(void *userdata, ne_request *req, const ne_status *st)
static void * caldav_load_calendar(void *data)
static void caldav_add_event(icalcomponent *comp, ICAL_SPAN_CONST struct icaltime_span *span, void *data)
static int verify_cert(void *userdata, int failures, const ne_ssl_certificate *cert)
static void caldav_destructor(void *obj)
static void handle_characters(xmlTextReaderPtr reader, struct xmlstate *state)
static const xmlChar * caldav_node_nsuri
static int auth_credentials(void *userdata, const char *realm, int attempts, char *username, char *secret)
static int fetch_response_reader(void *data, const char *block, size_t len)
static time_t icalfloat_to_timet(icaltimetype time)
static int caldav_write_event(struct ast_calendar_event *event)
static int load_module(void)
static void handle_end_element(xmlTextReaderPtr reader, struct xmlstate *state)
static void parse_error_handler(void *arg, const char *msg, xmlParserSeverities severity, xmlTextReaderLocatorPtr locator)
static int unload_module(void)
static struct ast_str * caldav_request(struct caldav_pvt *pvt, const char *method, struct ast_str *req_body, struct ast_str *subdir, const char *content_type)
static const xmlChar * caldav_node_localname
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
#define AST_STRING_FIELD(name)
Declare a string field.
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
#define ast_string_field_build(x, field, fmt, args...)
Set a field to a complex (built) value.
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
size_t attribute_pure ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
static force_inline int attribute_pure ast_strlen_zero(const char *s)
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
char *attribute_pure ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
struct ast_calendar_attendee * next
Individual calendaring technology data.
Asterisk calendar structure.
const ast_string_field name
Structure for mutex and tracking information.
Support for dynamic strings.
Structure for variables, used for configurations and for channel variables.
struct ast_variable * next
const ast_string_field url
struct ast_calendar * owner
const ast_string_field user
struct ao2_container * events
const ast_string_field secret
structure to hold users read from phoneprov_users.conf
struct association categories[]
int ast_time_t_to_string(time_t time, char *buf, size_t length)
Converts to a string representation of a time_t as decimal seconds since the epoch....
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
long int ast_random(void)