[svn-commits] twilson: branch twilson/calendaring r152642 - in /team/twilson/calendaring: i...

SVN commits to the Digium repositories svn-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, "&quot;"); break;
+			case '\'' : strcpy(buf, "&apos;"); break;
+			case '&' : strcpy(buf, "&amp;"); break;
+			case '<' : strcpy(buf, "&lt;"); break;
+			case '>' : strcpy(buf, "&gt;"); 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 svn-commits mailing list