[asterisk-commits] twilson: branch twilson/calendaring r152642 - in /team/twilson/calendaring: i...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Wed Oct 29 03:15:45 CDT 2008
Author: twilson
Date: Wed Oct 29 03:15:44 2008
New Revision: 152642
URL: http://svn.digium.com/view/asterisk?view=rev&rev=152642
Log:
First draft of write support. Generic function and res_exchangecal support completed.
Modified:
team/twilson/calendaring/include/asterisk/calendar.h
team/twilson/calendaring/main/calendar.c
team/twilson/calendaring/res/res_exchangecal.c
Modified: team/twilson/calendaring/include/asterisk/calendar.h
URL: http://svn.digium.com/view/asterisk/team/twilson/calendaring/include/asterisk/calendar.h?view=diff&rev=152642&r1=152641&r2=152642
==============================================================================
--- team/twilson/calendaring/include/asterisk/calendar.h (original)
+++ team/twilson/calendaring/include/asterisk/calendar.h Wed Oct 29 03:15:44 2008
@@ -35,12 +35,12 @@
* function to query whether or not a calendar is busy at the present time,
* a devicestate provider, and notification of calendar events through execution
* of dialplan apps or dialplan logic at a specific context and extension. The
- * variables available to the dialplan for notification are:
+ * information available through the CALENDAR_EVENT() dialplan function are:
*
- * EVENT_SUMMARY, EVENT_DESCRIPTION, EVENT_ORGANIZER, EVENT_LOCATION
- * EVENT_CALENDAR, EVENT_UID, EVENT_START, EVENT_END, and EVENT_BUSYSTATE
+ * SUMMARY, DESCRIPTION, ORGANIZER, LOCATION
+ * CALENDAR, UID, START, END, and BUSYSTATE
*
- * EVENT_BUSYSTATE can have the values 0 (free), 1 (tentatively busy), or 2 (busy)
+ * BUSYSTATE can have the values 0 (free), 1 (tentatively busy), or 2 (busy)
*
* Usage
* All calendaring configuration data is located in calendar.conf and is only read
@@ -60,6 +60,7 @@
*/
struct ast_calendar;
+struct ast_calendar_event;
/*! \brief The structure to pass to the load_calendar callback */
struct ast_calendar_load_data {
@@ -74,7 +75,8 @@
const char *module;
int (* is_busy)(struct ast_calendar *calendar); /*!< Override default busy determination */
void *(* load_calendar)(void *data); /*!< Create private structure, add calendar events, etc. */
- void *(* unref_calendar)(void *obj); /*!< Function to be called to free the private structure */
+ void *(* unref_calendar)(void *obj); /*!< Function to be called to free the private structure */
+ int (* write_event)(struct ast_calendar_event *event); /*!< Function for writing an event to the calendar */
};
enum ast_calendar_busy_state {
Modified: team/twilson/calendaring/main/calendar.c
URL: http://svn.digium.com/view/asterisk/team/twilson/calendaring/main/calendar.c?view=diff&rev=152642&r1=152641&r2=152642
==============================================================================
--- team/twilson/calendaring/main/calendar.c (original)
+++ team/twilson/calendaring/main/calendar.c Wed Oct 29 03:15:44 2008
@@ -971,6 +971,117 @@
.read = calendar_query_result_exec,
};
+
+static int calendar_write_exec(struct ast_channel *chan, const char *cmd, char *data, const char *value)
+{
+ int i, j, ret = -1;
+ char *val_dup = NULL;
+ struct ast_calendar *cal = NULL;
+ struct ast_calendar_event *event = NULL;
+ AST_DECLARE_APP_ARGS(fields,
+ AST_APP_ARG(field)[10];
+ );
+ AST_DECLARE_APP_ARGS(values,
+ AST_APP_ARG(value)[10];
+ );
+
+ if (!(val_dup = ast_strdup(value))) {
+ ast_log(LOG_ERROR, "Could not allocate memory for values\n");
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(fields, data);
+ AST_STANDARD_APP_ARGS(values, val_dup);
+
+ /* XXX Eventually we will support unnamed calendars, so if we don't find one, we parse
+ * for a calendar type and create it */
+ if (!(cal = find_calendar(fields.field[0]))) {
+ ast_log(LOG_WARNING, "Couldn't find calendar '%s'\n", fields.field[0]);
+ goto write_cleanup;
+ }
+
+ if (!(cal->tech->write_event)) {
+ ast_log(LOG_WARNING, "Calendar '%s' has no write function!\n", cal->name);
+ goto write_cleanup;
+ }
+
+
+ if (!(event = ast_calendar_event_alloc(cal))) {
+ ast_log(LOG_ERROR, "Could not allocate memory for event\n");
+ goto write_cleanup;
+ }
+
+ if (ast_string_field_init(event, 32)) {
+ ast_log(LOG_ERROR, "Could not allocate memory for event stringfields\n");
+ goto write_cleanup;
+ }
+
+ if (ast_strlen_zero(fields.field[0])) {
+ ast_log(LOG_WARNING, "CALENDAR_WRITE requires a calendar name!\n");
+ goto write_cleanup;
+ }
+
+ if (fields.argc - 1 != values.argc) {
+ ast_log(LOG_WARNING, "CALENDAR_WRITE should have the same number of fields (%d) and values (%d)!\n", fields.argc - 1, values.argc);
+ goto write_cleanup;
+ }
+
+ event->owner = cal;
+
+ for (i = 1, j = 0; i < fields.argc; i++, j++) {
+ if (!strcasecmp(fields.field[i], "summary")) {
+ ast_string_field_set(event, summary, values.value[j]);
+ } else if (!strcasecmp(fields.field[i], "description")) {
+ ast_string_field_set(event, description, values.value[j]);
+ } else if (!strcasecmp(fields.field[i], "organizer")) {
+ ast_string_field_set(event, organizer, values.value[j]);
+ } else if (!strcasecmp(fields.field[i], "location")) {
+ ast_string_field_set(event, location, values.value[j]);
+ } else if (!strcasecmp(fields.field[i], "uid")) {
+ ast_string_field_set(event, uid, values.value[j]);
+ } else if (!strcasecmp(fields.field[i], "start")) {
+ event->start = atoi(values.value[j]);
+ } else if (!strcasecmp(fields.field[i], "end")) {
+ event->end = atoi(values.value[j]);
+ } else if (!strcasecmp(fields.field[i], "busystate")) {
+ event->busy_state = atoi(values.value[j]);
+ } else {
+ ast_log(LOG_WARNING, "Unknown calendar event field '%s'\n", fields.field[i]);
+ }
+ }
+
+ if((ret = cal->tech->write_event(event))) {
+ ast_log(LOG_WARNING, "Writing event to calendar '%s' failed!\n", cal->name);
+ }
+
+write_cleanup:
+ if (cal) cal = unref_calendar(cal);
+ if (event) event = ast_calendar_unref_event(event);
+ if (val_dup) ast_free(val_dup);
+
+ return ret;
+}
+
+static struct ast_custom_function calendar_write_function = {
+ .name = "CALENDAR_WRITE",
+ .synopsis = "Write an event to a calendar",
+ .syntax = "CALENDAR_WRITE(<calendar>,<field1>[,<field2>][,...<fieldn>])=<value1>[,<value2>][,...<fieldn>]",
+ .desc = "Options:\n"
+ " <calendar> : The name of the calendar, or a calendar URI\n"
+ " <field> : Specific information about a particular event\n"
+ " summary = A summary of the event\n"
+ " description = The full event description\n"
+ " organizer = The event organizer\n"
+ " location = The event location\n"
+ " uid = The unique identifier for the event\n"
+ " start = The start time of the event (in seconds since epoch)\n"
+ " end = The end time of the event (in seconds since epoch)\n"
+ " busystate = The busy status of the event 0=FREE, 1=TENTATIVE, 2=BUSY\n"
+ "\n"
+ "The field and value arguments can easily be set/passed using the HASHKEYS() and HASH() functions\n",
+ .write = calendar_write_exec,
+};
+
/*! \brief CLI command to list available calendars */
static char *handle_show_calendars(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
@@ -1117,6 +1228,7 @@
__ast_custom_function_register(&calendar_event_function, NULL);
__ast_custom_function_register(&calendar_query_function, NULL);
__ast_custom_function_register(&calendar_query_result_function, NULL);
+ __ast_custom_function_register(&calendar_write_function, NULL);
ast_cli_register_multiple(calendar_cli, ARRAY_LEN(calendar_cli));
ast_mutex_init(&refreshlock);
Modified: team/twilson/calendaring/res/res_exchangecal.c
URL: http://svn.digium.com/view/asterisk/team/twilson/calendaring/res/res_exchangecal.c?view=diff&rev=152642&r1=152641&r2=152642
==============================================================================
--- team/twilson/calendaring/res/res_exchangecal.c (original)
+++ team/twilson/calendaring/res/res_exchangecal.c Wed Oct 29 03:15:44 2008
@@ -42,6 +42,7 @@
static void *exchangecal_load_calendar(void *data);
static void *unref_exchangecal(void *obj);
+static int exchangecal_write_event(struct ast_calendar_event *event);
static const struct ast_calendar_tech exchangecal_tech = {
.type = "exchange",
@@ -49,6 +50,7 @@
.module = AST_MODULE,
.load_calendar = exchangecal_load_calendar,
.unref_calendar = unref_exchangecal,
+ .write_event = exchangecal_write_event,
};
struct exchangecal_pvt {
@@ -127,7 +129,7 @@
{
char *read, *write;
icaltimetype tt;
- for(read = write = mstime;*read;read++) {
+ for(read = write = mstime; *read; read++) {
if (*read == '.') {
*write++ = 'Z';
*write = '\0';
@@ -217,6 +219,90 @@
return NULL;
}
+/* It is very important to use the return value of this function as a realloc could occur */
+static struct ast_str *generate_exchange_uuid(struct ast_str *uid)
+{
+ unsigned short val[8];
+ int x;
+
+ for (x = 0; x < 8; x++) {
+ val[x] = ast_random();
+ }
+ ast_str_set(&uid, 0, "%04x%04x-%04x-%04x-%04x-%04x%04x%04x", val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7]);
+
+ return uid;
+}
+
+static int is_valid_uuid(struct ast_str *uid)
+{
+ int i;
+
+ if (uid->used != 36) {
+ return 0;
+ }
+
+ for (i = 0; i < uid->used; i++) {
+ if (i == 8 || i == 13 || i == 18 || i == 23) {
+ if (uid->str[i] != '-') {
+ return 0;
+ }
+ } else if (!((uid->str[i] > 47 && uid->str[i] < 58) || (uid->str[i] > 96 && uid->str[i] < 103))) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static struct ast_str *xml_encode_str(struct ast_str *dst, const char *src)
+{
+ char *tmp;
+ char buf[7];
+
+ for (tmp = (char *)src; *tmp; tmp++) {
+ switch (*tmp) {
+ case '\"' : strcpy(buf, """); break;
+ case '\'' : strcpy(buf, "'"); break;
+ case '&' : strcpy(buf, "&"); break;
+ case '<' : strcpy(buf, "<"); break;
+ case '>' : strcpy(buf, ">"); break;
+ default : sprintf(buf, "%c", *tmp);
+ }
+
+ ast_str_append(&dst, 0, "%s", buf);
+ }
+
+ return dst;
+}
+
+static struct ast_str *epoch_to_exchange_time(struct ast_str *dst, time_t epoch)
+{
+ icaltimezone *utc = icaltimezone_get_utc_timezone();
+ icaltimetype tt = icaltime_from_timet_with_zone(epoch, 0, utc);
+ char tmp[30];
+ int i;
+
+ strncpy(tmp, icaltime_as_ical_string(tt), sizeof(tmp));
+ for (i = 0; tmp[i]; i++) {
+ ast_str_append(&dst, 0, "%c", tmp[i]);
+ if (i == 3 || i == 5)
+ ast_str_append(&dst, 0, "%c", '-');
+ if (i == 10 || i == 12)
+ ast_str_append(&dst, 0, "%c", ':');
+ if (i == 14)
+ ast_str_append(&dst, 0, "%s", ".000");
+ }
+
+ return dst;
+}
+
+static struct ast_str *bs_to_exchange_bs(struct ast_str *dst, enum ast_calendar_busy_state bs)
+{
+ ast_str_set(&dst, 0, "%s", "BUSY");
+
+ return dst;
+}
+
static int fetch_response_reader(void *data, const char *block, size_t len)
{
struct ast_str **response = data;
@@ -246,11 +332,12 @@
return 0;
}
-static struct ast_str *exchangecal_request(struct exchangecal_pvt *pvt, const char *method, struct ast_str *req_body)
+static struct ast_str *exchangecal_request(struct exchangecal_pvt *pvt, const char *method, struct ast_str *req_body, struct ast_str *subdir)
{
struct ast_str *response;
ne_request *req;
int ret;
+ char buf[1000];
if (!pvt) {
ast_log(LOG_ERROR, "There is no private!\n");
@@ -262,7 +349,9 @@
return NULL;
}
- req = ne_request_create(pvt->session, method, pvt->uri.path);
+ snprintf(buf, sizeof(buf), "%s%s", pvt->uri.path, subdir ? subdir->str : "");
+
+ req = ne_request_create(pvt->session, method, buf);
ne_add_response_body_reader(req, ne_accept_2xx, fetch_response_reader, &response);
ne_set_request_body_buffer(req, req_body->str, req_body->used);
ne_add_request_header(req, "Content-type", "text/xml");
@@ -288,6 +377,116 @@
return response;
}
+
+static int exchangecal_write_event(struct ast_calendar_event *event)
+{
+ struct ast_str *body = NULL, *response = NULL, *subdir = NULL;
+ struct ast_str *uid = NULL, *summary = NULL, *description = NULL, *organizer = NULL,
+ *location = NULL, *start = NULL, *end = NULL, *busystate = NULL;
+ int ret = -1;
+
+ if (!event) {
+ ast_log(LOG_WARNING, "No event passed!\n");
+ return -1;
+ }
+
+ if (!(event->start && event->end)) {
+ ast_log(LOG_WARNING, "The event must contain a start and an end\n");
+ return -1;
+ }
+ if (!(body = ast_str_create(512)) ||
+ !(subdir = ast_str_create(32)) ||
+ !(response = ast_str_create(512))) {
+ ast_log(LOG_ERROR, "Could not allocate memory for request and response!\n");
+ goto write_cleanup;
+ }
+
+ if (!(uid = ast_str_create(32)) ||
+ !(summary = ast_str_create(32)) ||
+ !(description = ast_str_create(32)) ||
+ !(organizer = ast_str_create(32)) ||
+ !(location = ast_str_create(32)) ||
+ !(start = ast_str_create(32)) ||
+ !(end = ast_str_create(32)) ||
+ !(busystate = ast_str_create(32))) {
+ ast_log(LOG_ERROR, "Unable to allocate memory for request values\n");
+ goto write_cleanup;
+ }
+
+ if (ast_strlen_zero(event->uid)) {
+ uid = generate_exchange_uuid(uid);
+ } else {
+ ast_str_set(&uid, 36, "%s", event->uid);
+ }
+
+ if (!is_valid_uuid(uid)) {
+ ast_log(LOG_WARNING, "An invalid uid was provided, you may leave this field blank to have one generated for you\n");
+ goto write_cleanup;
+ }
+
+ summary = xml_encode_str(summary, event->summary);
+ description = xml_encode_str(description, event->description);
+ organizer = xml_encode_str(organizer, event->organizer);
+ location = xml_encode_str(location, event->location);
+ start = epoch_to_exchange_time(start, event->start);
+ end = epoch_to_exchange_time(end, event->end);
+ busystate = bs_to_exchange_bs(busystate, event->busy_state);
+
+ ast_str_append(&body, 0,
+ "<?xml version=\"1.0\"?>\n"
+ "<a:propertyupdate\n"
+ " xmlns:a=\"DAV:\"\n"
+ " xmlns:e=\"http://schemas.microsoft.com/exchange/\"\n"
+ " xmlns:mapi=\"http://schemas.microsoft.com/mapi/\"\n"
+ " xmlns:mapit=\"http://schemas.microsoft.com/mapi/proptag/\"\n"
+ " xmlns:x=\"xml:\" xmlns:cal=\"urn:schemas:calendar:\"\n"
+ " xmlns:dt=\"uuid:%s/\"\n" /* uid */
+ " xmlns:header=\"urn:schemas:mailheader:\"\n"
+ " xmlns:mail=\"urn:schemas:httpmail:\"\n"
+ ">\n"
+ " <a:set>\n"
+ " <a:prop>\n"
+ " <a:contentclass>urn:content-classes:appointment</a:contentclass>\n"
+ " <e:outlookmessageclass>IPM.Appointment</e:outlookmessageclass>\n"
+ " <mail:subject>%s</mail:subject>\n" /* summary */
+ " <mail:description>%s</mail:description>\n" /* description */
+ " <header:to>%s</header:to>\n" /* organizer */
+ " <cal:location>%s</cal:location>\n" /* location */
+ " <cal:dtstart dt:dt=\"dateTime.tz\">%s</cal:dtstart>\n" /* start */
+ " <cal:dtend dt:dt=\"dateTime.tz\">%s</cal:dtend>\n" /* end */
+ " <cal:instancetype dt:dt=\"int\">0</cal:instancetype>\n"
+ " <cal:busystatus>%s</cal:busystatus>\n" /* busy_state (BUSY, FREE, BUSY_TENTATIVE) */
+ " <cal:meetingstatus>CONFIRMED</cal:meetingstatus>\n"
+ " <cal:alldayevent dt:dt=\"boolean\">0</cal:alldayevent>\n" /* XXX need to add event support for all day events */
+ " <cal:responserequested dt:dt=\"boolean\">0</cal:responserequested>\n"
+ " <mapi:finvited dt:dt=\"boolean\">1</mapi:finvited>\n"
+ " </a:prop>\n"
+ " </a:set>\n"
+ "</a:propertyupdate>\n",
+ uid->str, summary->str, description->str, organizer->str, location->str, start->str, end->str, busystate->str);
+ ast_verb(0, "\n\n%s\n\n", body->str);
+ ast_str_set(&subdir, 0, "/Calendar/%s.eml", uid->str);
+
+ response = exchangecal_request(event->owner->tech_pvt, "PROPPATCH", body, subdir);
+ ast_verb(1, "%s", response->str);
+
+ ret = 0;
+write_cleanup:
+ if (uid) ast_free(uid);
+ if (summary) ast_free(summary);
+ if (description) ast_free(description);
+ if (organizer) ast_free(organizer);
+ if (location) ast_free(location);
+ if (start) ast_free(start);
+ if (end) ast_free(end);
+ if (busystate) ast_free(busystate);
+ if (body) ast_free(body);
+ if (response) ast_free(response);
+ if (subdir) ast_free(subdir);
+
+ return ret;
+}
+
static struct ast_str *exchangecal_get_events_between(struct exchangecal_pvt *pvt, time_t start_time, time_t end_time)
{
@@ -327,7 +526,7 @@
"</g:searchrequest>\n", pvt->url, start, end);
ast_debug(5, "Request:\n%s\n", body->str);
- response = exchangecal_request(pvt, "SEARCH", body);
+ response = exchangecal_request(pvt, "SEARCH", body, NULL);
ast_debug(5, "Response:\n%s\n", response->str);
ast_free(body);
@@ -381,7 +580,7 @@
pvt->owner = cal;
if (!(pvt->events = ast_calendar_event_container_alloc())) {
- ast_log(LOG_NOTICE, "Could not allocate space for fetching events for calendar: %s\n", cal->name);
+ ast_log(LOG_ERROR, "Could not allocate space for fetching events for calendar: %s\n", cal->name);
pvt = unref_exchangecal(pvt);
return NULL;
}
More information about the asterisk-commits
mailing list