| 
									
										
										
										
											2010-05-24 18:21:20 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Asterisk -- An open source telephony toolkit. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (C) 2008 - 2009, Digium, Inc. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Jan Kalab <pitlicek@gmail.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * See http://www.asterisk.org for more information about
 | 
					
						
							|  |  |  |  * the Asterisk project. Please do not directly contact | 
					
						
							|  |  |  |  * any of the maintainers of this project for assistance; | 
					
						
							|  |  |  |  * the project provides a web site, mailing lists and IRC | 
					
						
							|  |  |  |  * channels for your use. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software, distributed under the terms of | 
					
						
							|  |  |  |  * the GNU General Public License Version 2. See the LICENSE file | 
					
						
							|  |  |  |  * at the top of the source tree. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*! \file
 | 
					
						
							|  |  |  |  * \brief Resource for handling MS Exchange Web Service calendars | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*** MODULEINFO
 | 
					
						
							| 
									
										
										
										
											2010-05-26 05:33:11 +00:00
										 |  |  | 	<depend>neon29</depend> | 
					
						
							| 
									
										
										
										
											2010-05-24 18:21:20 +00:00
										 |  |  | ***/ | 
					
						
							|  |  |  | #include "asterisk.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ASTERISK_FILE_VERSION(__FILE__, "$Revision$") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-05-26 00:29:40 +00:00
										 |  |  | #include <ne_request.h>
 | 
					
						
							|  |  |  | #include <ne_session.h>
 | 
					
						
							|  |  |  | #include <ne_uri.h>
 | 
					
						
							|  |  |  | #include <ne_socket.h>
 | 
					
						
							|  |  |  | #include <ne_auth.h>
 | 
					
						
							|  |  |  | #include <ne_xml.h>
 | 
					
						
							|  |  |  | #include <ne_xmlreq.h>
 | 
					
						
							|  |  |  | #include <ne_utils.h>
 | 
					
						
							| 
									
										
										
										
											2010-05-24 18:21:20 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include "asterisk/module.h"
 | 
					
						
							|  |  |  | #include "asterisk/calendar.h"
 | 
					
						
							|  |  |  | #include "asterisk/lock.h"
 | 
					
						
							|  |  |  | #include "asterisk/config.h"
 | 
					
						
							|  |  |  | #include "asterisk/astobj2.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void *ewscal_load_calendar(void *data); | 
					
						
							|  |  |  | static void *unref_ewscal(void *obj); | 
					
						
							|  |  |  | static int ewscal_write_event(struct ast_calendar_event *event); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct ast_calendar_tech ewscal_tech = { | 
					
						
							|  |  |  | 	.type = "ews", | 
					
						
							|  |  |  | 	.description = "MS Exchange Web Service calendars", | 
					
						
							|  |  |  | 	.module = AST_MODULE, | 
					
						
							|  |  |  | 	.load_calendar = ewscal_load_calendar, | 
					
						
							|  |  |  | 	.unref_calendar = unref_ewscal, | 
					
						
							|  |  |  | 	.write_event = ewscal_write_event, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | enum xml_op { | 
					
						
							|  |  |  | 	XML_OP_FIND = 100, | 
					
						
							|  |  |  | 	XML_OP_GET, | 
					
						
							|  |  |  | 	XML_OP_CREATE, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct calendar_id { | 
					
						
							|  |  |  | 	struct ast_str *id; | 
					
						
							|  |  |  | 	AST_LIST_ENTRY(calendar_id) next; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct xml_context { | 
					
						
							|  |  |  | 	ne_xml_parser *parser; | 
					
						
							|  |  |  | 	struct ast_str *cdata; | 
					
						
							|  |  |  | 	struct ast_calendar_event *event; | 
					
						
							|  |  |  | 	enum xml_op op; | 
					
						
							|  |  |  | 	struct ewscal_pvt *pvt; | 
					
						
							|  |  |  | 	AST_LIST_HEAD_NOLOCK(ids, calendar_id) ids; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Important states of XML parsing */ | 
					
						
							|  |  |  | enum { | 
					
						
							|  |  |  | 	XML_EVENT_NAME = 10, | 
					
						
							|  |  |  | 	XML_EVENT_START, | 
					
						
							|  |  |  | 	XML_EVENT_END, | 
					
						
							|  |  |  | 	XML_EVENT_BUSY, | 
					
						
							|  |  |  | 	XML_EVENT_ORGANIZER, | 
					
						
							|  |  |  | 	XML_EVENT_LOCATION, | 
					
						
							|  |  |  | 	XML_EVENT_ATTENDEE_LIST, | 
					
						
							|  |  |  | 	XML_EVENT_ATTENDEE, | 
					
						
							|  |  |  | 	XML_EVENT_MAILBOX, | 
					
						
							|  |  |  | 	XML_EVENT_EMAIL_ADDRESS, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct ewscal_pvt { | 
					
						
							|  |  |  | 	AST_DECLARE_STRING_FIELDS( | 
					
						
							|  |  |  | 		AST_STRING_FIELD(url); | 
					
						
							|  |  |  | 		AST_STRING_FIELD(user); | 
					
						
							|  |  |  | 		AST_STRING_FIELD(secret); | 
					
						
							|  |  |  | 	); | 
					
						
							|  |  |  | 	struct ast_calendar *owner; | 
					
						
							|  |  |  | 	ne_uri uri; | 
					
						
							|  |  |  | 	ne_session *session; | 
					
						
							|  |  |  | 	struct ao2_container *events; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void ewscal_destructor(void *obj) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ewscal_pvt *pvt = obj; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_debug(1, "Destroying pvt for Exchange Web Service calendar %s\n", "pvt->owner->name"); | 
					
						
							|  |  |  | 	if (pvt->session) { | 
					
						
							|  |  |  | 		ne_session_destroy(pvt->session); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	ast_string_field_free_memory(pvt); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ao2_callback(pvt->events, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ao2_ref(pvt->events, -1); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void *unref_ewscal(void *obj) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ewscal_pvt *pvt = obj; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_debug(5, "EWS: unref_ewscal()\n"); | 
					
						
							|  |  |  | 	ao2_ref(pvt, -1); | 
					
						
							|  |  |  | 	return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int auth_credentials(void *userdata, const char *realm, int attempts, char *username, char *secret) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ewscal_pvt *pvt = userdata; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (attempts > 1) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Invalid username or password for Exchange Web Service calendar '%s'\n", pvt->owner->name); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ne_strnzcpy(username, pvt->user, NE_ABUFSIZ); | 
					
						
							|  |  |  | 	ne_strnzcpy(secret, pvt->secret, NE_ABUFSIZ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int ssl_verify(void *userdata, int failures, const ne_ssl_certificate *cert) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ewscal_pvt *pvt = userdata; | 
					
						
							|  |  |  | 	if (failures & NE_SSL_UNTRUSTED) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Untrusted SSL certificate for calendar %s!\n", pvt->owner->name); | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 1;	/* NE_SSL_NOTYETVALID, NE_SSL_EXPIRED, NE_SSL_IDMISMATCH */ | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static time_t mstime_to_time_t(char *mstime) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ast_tm tm; | 
					
						
							|  |  |  | 	struct timeval tv; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ast_strptime(mstime, "%FT%TZ", &tm)) { | 
					
						
							|  |  |  | 		tv = ast_mktime(&tm, "UTC"); | 
					
						
							|  |  |  | 		return tv.tv_sec; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int startelm(void *userdata, int parent, const char *nspace, const char *name, const char **atts) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct xml_context *ctx = userdata; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_debug(3, "EWS: XML: Start: %s\n", name); | 
					
						
							|  |  |  | 	if (ctx->op == XML_OP_CREATE) { | 
					
						
							|  |  |  | 		return NE_XML_DECLINE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Nodes needed for traversing until CalendarItem is found */ | 
					
						
							|  |  |  | 	if (!strcmp(name, "Envelope") || | 
					
						
							|  |  |  | 		!strcmp(name, "Body") || | 
					
						
							|  |  |  | 		!strcmp(name, "FindItemResponse") || | 
					
						
							|  |  |  | 		!strcmp(name, "GetItemResponse") || | 
					
						
							|  |  |  | 		!strcmp(name, "CreateItemResponse") || | 
					
						
							|  |  |  | 		!strcmp(name, "ResponseMessages") || | 
					
						
							|  |  |  | 		!strcmp(name, "FindItemResponseMessage") || !strcmp(name, "GetItemResponseMessage") || | 
					
						
							|  |  |  | 		!strcmp(name, "Items") | 
					
						
							|  |  |  | 	) { | 
					
						
							|  |  |  | 		return 1; | 
					
						
							|  |  |  | 	} else if (!strcmp(name, "RootFolder")) { | 
					
						
							|  |  |  | 		/* Get number of events */ | 
					
						
							|  |  |  | 		int items; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ast_debug(3, "EWS: XML: <RootFolder>\n"); | 
					
						
							|  |  |  | 		if (sscanf(ne_xml_get_attr(ctx->parser, atts, NULL, "TotalItemsInView"), "%d", &items) != 1) { | 
					
						
							|  |  |  | 			/* Couldn't read enything */ | 
					
						
							|  |  |  | 			ne_xml_set_error(ctx->parser, "Could't read number of events."); | 
					
						
							|  |  |  | 			return NE_XML_ABORT; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ast_debug(3, "EWS: %d calendar items to load\n", items); | 
					
						
							|  |  |  | 		if (items < 1) { | 
					
						
							|  |  |  | 			/* Stop processing XML if there are no events */ | 
					
						
							|  |  |  | 			return NE_XML_DECLINE; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return 1; | 
					
						
							|  |  |  | 	} else if (!strcmp(name, "CalendarItem")) { | 
					
						
							|  |  |  | 		/* Event start */ | 
					
						
							|  |  |  | 		ast_debug(3, "EWS: XML: <CalendarItem>\n"); | 
					
						
							|  |  |  | 		if (!(ctx->pvt && ctx->pvt->owner)) { | 
					
						
							|  |  |  | 			ast_log(LOG_ERROR, "Require a private structure with an owner\n"); | 
					
						
							|  |  |  | 			return NE_XML_ABORT; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ctx->event = ast_calendar_event_alloc(ctx->pvt->owner); | 
					
						
							|  |  |  | 		if (!ctx->event) { | 
					
						
							|  |  |  | 			ast_log(LOG_ERROR, "Could not allocate an event!\n"); | 
					
						
							|  |  |  | 			return NE_XML_ABORT; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ctx->cdata = ast_str_create(64); | 
					
						
							|  |  |  | 		if (!ctx->cdata) { | 
					
						
							|  |  |  | 			ast_log(LOG_ERROR, "Could not allocate CDATA!\n"); | 
					
						
							|  |  |  | 			return NE_XML_ABORT; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return 1; | 
					
						
							|  |  |  | 	} else if (!strcmp(name, "ItemId")) { | 
					
						
							|  |  |  | 		/* Event UID */ | 
					
						
							|  |  |  | 		if (ctx->op == XML_OP_FIND) { | 
					
						
							|  |  |  | 			struct calendar_id *id; | 
					
						
							|  |  |  | 			if (!(id = ast_calloc(1, sizeof(id)))) { | 
					
						
							|  |  |  | 				return NE_XML_ABORT; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if (!(id->id = ast_str_create(256))) { | 
					
						
							|  |  |  | 				ast_free(id); | 
					
						
							|  |  |  | 				return NE_XML_ABORT; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			ast_str_set(&id->id, 0, "%s", ne_xml_get_attr(ctx->parser, atts, NULL, "Id")); | 
					
						
							|  |  |  | 			AST_LIST_INSERT_TAIL(&ctx->ids, id, next); | 
					
						
							|  |  |  | 			ast_debug(3, "EWS_FIND: XML: UID: %s\n", ast_str_buffer(id->id)); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			ast_debug(3, "EWS_GET: XML: UID: %s\n", ne_xml_get_attr(ctx->parser, atts, NULL, "Id")); | 
					
						
							|  |  |  | 			ast_string_field_set(ctx->event, uid, ne_xml_get_attr(ctx->parser, atts, NULL, "Id")); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return XML_EVENT_NAME; | 
					
						
							|  |  |  | 	} else if (!strcmp(name, "Subject")) { | 
					
						
							|  |  |  | 		/* Event name */ | 
					
						
							|  |  |  | 		if (!ctx->cdata) { | 
					
						
							|  |  |  | 			return NE_XML_ABORT; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		ast_str_reset(ctx->cdata); | 
					
						
							|  |  |  | 		return XML_EVENT_NAME; | 
					
						
							|  |  |  | 	} else if (!strcmp(name, "Start")) { | 
					
						
							|  |  |  | 		/* Event start time */ | 
					
						
							|  |  |  | 		return XML_EVENT_START; | 
					
						
							|  |  |  | 	} else if (!strcmp(name, "End")) { | 
					
						
							|  |  |  | 		/* Event end time */ | 
					
						
							|  |  |  | 		return XML_EVENT_END; | 
					
						
							|  |  |  | 	} else if (!strcmp(name, "LegacyFreeBusyStatus")) { | 
					
						
							|  |  |  | 		/* Event busy state */ | 
					
						
							|  |  |  | 		return XML_EVENT_BUSY; | 
					
						
							|  |  |  | 	} else if (!strcmp(name, "Organizer") || | 
					
						
							|  |  |  | 			(parent == XML_EVENT_ORGANIZER && (!strcmp(name, "Mailbox") || | 
					
						
							|  |  |  | 			!strcmp(name, "Name")))) { | 
					
						
							|  |  |  | 		/* Event organizer */ | 
					
						
							|  |  |  | 		if (!ctx->cdata) { | 
					
						
							|  |  |  | 			return NE_XML_ABORT; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		ast_str_reset(ctx->cdata); | 
					
						
							|  |  |  | 		return XML_EVENT_ORGANIZER; | 
					
						
							|  |  |  | 	} else if (!strcmp(name, "Location")) { | 
					
						
							|  |  |  | 		/* Event location */ | 
					
						
							|  |  |  | 		if (!ctx->cdata) { | 
					
						
							|  |  |  | 			return NE_XML_ABORT; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		ast_str_reset(ctx->cdata); | 
					
						
							|  |  |  | 		return XML_EVENT_LOCATION; | 
					
						
							|  |  |  | 	} else if (!strcmp(name, "RequiredAttendees") || !strcmp(name, "OptionalAttendees")) { | 
					
						
							|  |  |  | 		return XML_EVENT_ATTENDEE_LIST; | 
					
						
							|  |  |  | 	} else if (!strcmp(name, "Attendee") && parent == XML_EVENT_ATTENDEE_LIST) { | 
					
						
							|  |  |  | 		return XML_EVENT_ATTENDEE; | 
					
						
							|  |  |  | 	} else if (!strcmp(name, "Mailbox") && parent == XML_EVENT_ATTENDEE) { | 
					
						
							|  |  |  | 		return XML_EVENT_MAILBOX; | 
					
						
							|  |  |  | 	} else if (!strcmp(name, "EmailAddress") && parent == XML_EVENT_MAILBOX) { | 
					
						
							|  |  |  | 		if (!ctx->cdata) { | 
					
						
							|  |  |  | 			return NE_XML_ABORT; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		ast_str_reset(ctx->cdata); | 
					
						
							|  |  |  | 		return XML_EVENT_EMAIL_ADDRESS; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return NE_XML_DECLINE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int cdata(void *userdata, int state, const char *cdata, size_t len) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct xml_context *ctx = userdata; | 
					
						
							|  |  |  | 	char data[len + 1]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* !!! DON'T USE AST_STRING_FIELD FUNCTIONS HERE, JUST COLLECT CTX->CDATA !!! */ | 
					
						
							|  |  |  | 	if (state < XML_EVENT_NAME || ctx->op == XML_OP_CREATE) { | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!ctx->event) { | 
					
						
							|  |  |  | 		ast_log(LOG_ERROR, "Parsing event data, but event object does not exist!\n"); | 
					
						
							|  |  |  | 		return 1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!ctx->cdata) { | 
					
						
							|  |  |  | 		ast_log(LOG_ERROR, "String for storing CDATA is unitialized!\n"); | 
					
						
							|  |  |  | 		return 1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_copy_string(data, cdata, len + 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (state) { | 
					
						
							|  |  |  | 	case XML_EVENT_START: | 
					
						
							|  |  |  | 		ctx->event->start = mstime_to_time_t(data); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case XML_EVENT_END: | 
					
						
							|  |  |  | 		ctx->event->end = mstime_to_time_t(data); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case XML_EVENT_BUSY: | 
					
						
							|  |  |  | 		if (!strcmp(data, "Busy") || !strcmp(data, "OOF")) { | 
					
						
							|  |  |  | 			ast_debug(3, "EWS: XML: Busy: yes\n"); | 
					
						
							|  |  |  | 			ctx->event->busy_state = AST_CALENDAR_BS_BUSY; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		else if (!strcmp(data, "Tentative")) { | 
					
						
							|  |  |  | 			ast_debug(3, "EWS: XML: Busy: tentative\n"); | 
					
						
							|  |  |  | 			ctx->event->busy_state = AST_CALENDAR_BS_BUSY_TENTATIVE; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		else { | 
					
						
							|  |  |  | 			ast_debug(3, "EWS: XML: Busy: no\n"); | 
					
						
							|  |  |  | 			ctx->event->busy_state = AST_CALENDAR_BS_FREE; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		ast_str_append(&ctx->cdata, 0, "%s", data); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_debug(5, "EWS: XML: CDATA: %s\n", ast_str_buffer(ctx->cdata)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int endelm(void *userdata, int state, const char *nspace, const char *name) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct xml_context *ctx = userdata; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_debug(5, "EWS: XML: End:   %s\n", name); | 
					
						
							|  |  |  | 	if (ctx->op == XML_OP_FIND || ctx->op == XML_OP_CREATE) { | 
					
						
							|  |  |  | 		return NE_XML_DECLINE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!strcmp(name, "Subject")) { | 
					
						
							|  |  |  | 		/* Event name end*/ | 
					
						
							|  |  |  | 		ast_string_field_set(ctx->event, summary, ast_str_buffer(ctx->cdata)); | 
					
						
							|  |  |  | 		ast_debug(3, "EWS: XML: Summary: %s\n", ctx->event->summary); | 
					
						
							|  |  |  | 		ast_str_reset(ctx->cdata); | 
					
						
							|  |  |  | 	} else if (!strcmp(name, "Organizer")) { | 
					
						
							|  |  |  | 		/* Event organizer end */ | 
					
						
							|  |  |  | 		ast_string_field_set(ctx->event, organizer, ast_str_buffer(ctx->cdata)); | 
					
						
							|  |  |  | 		ast_debug(3, "EWS: XML: Organizer: %s\n", ctx->event->organizer); | 
					
						
							|  |  |  | 		ast_str_reset(ctx->cdata); | 
					
						
							|  |  |  | 	} else if (!strcmp(name, "Location")) { | 
					
						
							|  |  |  | 		/* Event location end */ | 
					
						
							|  |  |  | 		ast_string_field_set(ctx->event, location, ast_str_buffer(ctx->cdata)); | 
					
						
							|  |  |  | 		ast_debug(3, "EWS: XML: Location: %s\n", ctx->event->location); | 
					
						
							|  |  |  | 		ast_str_reset(ctx->cdata); | 
					
						
							|  |  |  | 	} else if (state == XML_EVENT_EMAIL_ADDRESS) { | 
					
						
							|  |  |  | 		struct ast_calendar_attendee *attendee; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (!(attendee = ast_calloc(1, sizeof(*attendee)))) { | 
					
						
							|  |  |  | 			ctx->event = ast_calendar_unref_event(ctx->event); | 
					
						
							|  |  |  | 			return  1; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (ast_str_strlen(ctx->cdata)) { | 
					
						
							|  |  |  | 			attendee->data = ast_strdup(ast_str_buffer(ctx->cdata)); | 
					
						
							|  |  |  | 			AST_LIST_INSERT_TAIL(&ctx->event->attendees, attendee, next); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		ast_debug(3, "EWS: XML: attendee address '%s'\n", ast_str_buffer(ctx->cdata)); | 
					
						
							|  |  |  | 		ast_str_reset(ctx->cdata); | 
					
						
							|  |  |  | 	} else if (!strcmp(name, "CalendarItem")) { | 
					
						
							|  |  |  | 		/* Event end */ | 
					
						
							|  |  |  | 		ast_debug(3, "EWS: XML: </CalendarItem>\n"); | 
					
						
							|  |  |  | 		ast_free(ctx->cdata); | 
					
						
							|  |  |  | 		if (ctx->event) { | 
					
						
							|  |  |  | 			ao2_link(ctx->pvt->events, ctx->event); | 
					
						
							|  |  |  | 			ctx->event = ast_calendar_unref_event(ctx->event); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			ast_log(LOG_ERROR, "Event data ended in XML, but event object does not exist!\n"); | 
					
						
							|  |  |  | 			return 1; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else if (!strcmp(name, "Envelope")) { | 
					
						
							|  |  |  | 		/* Events end */ | 
					
						
							|  |  |  | 		ast_debug(3, "EWS: XML: All events has been parsed, merging…\n"); | 
					
						
							|  |  |  | 		ast_calendar_merge_events(ctx->pvt->owner, ctx->pvt->events); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const char *mstime(time_t t, char *buf, size_t buflen) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct timeval tv = { | 
					
						
							|  |  |  | 		.tv_sec = t, | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 	struct ast_tm tm; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_localtime(&tv, &tm, "utc"); | 
					
						
							|  |  |  | 	ast_strftime(buf, buflen, "%FT%TZ", &tm); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return S_OR(buf, ""); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const char *msstatus(enum ast_calendar_busy_state state) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	switch (state) { | 
					
						
							|  |  |  | 	case AST_CALENDAR_BS_BUSY_TENTATIVE: | 
					
						
							|  |  |  | 		return "Tentative"; | 
					
						
							|  |  |  | 	case AST_CALENDAR_BS_BUSY: | 
					
						
							|  |  |  | 		return "Busy"; | 
					
						
							|  |  |  | 	case AST_CALENDAR_BS_FREE: | 
					
						
							|  |  |  | 		return "Free"; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return ""; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const char *get_soap_action(enum xml_op op) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	switch (op) { | 
					
						
							|  |  |  | 	case XML_OP_FIND: | 
					
						
							|  |  |  | 		return "\"http://schemas.microsoft.com/exchange/services/2006/messages/FindItem\""; | 
					
						
							|  |  |  | 	case XML_OP_GET: | 
					
						
							|  |  |  | 		return "\"http://schemas.microsoft.com/exchange/services/2006/messages/GetItem\""; | 
					
						
							|  |  |  | 	case XML_OP_CREATE: | 
					
						
							|  |  |  | 		return "\"http://schemas.microsoft.com/exchange/services/2006/messages/CreateItem\""; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ""; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int send_ews_request_and_parse(struct ast_str *request, struct xml_context *ctx) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 	ne_request *req; | 
					
						
							|  |  |  | 	ne_xml_parser *parser; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_debug(3, "EWS: HTTP request...\n"); | 
					
						
							|  |  |  | 	if (!(ctx && ctx->pvt)) { | 
					
						
							|  |  |  | 		ast_log(LOG_ERROR, "There is no private!\n"); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!ast_str_strlen(request)) { | 
					
						
							|  |  |  | 		ast_log(LOG_ERROR, "No request to send!\n"); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_debug(3, "%s\n", ast_str_buffer(request)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Prepare HTTP POST request */ | 
					
						
							|  |  |  | 	req = ne_request_create(ctx->pvt->session, "POST", ctx->pvt->uri.path); | 
					
						
							|  |  |  | 	ne_set_request_flag(req, NE_REQFLAG_IDEMPOTENT, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Set headers--should be application/soap+xml, but MS… :/ */ | 
					
						
							|  |  |  | 	ne_add_request_header(req, "Content-Type", "text/xml; charset=utf-8"); | 
					
						
							|  |  |  | 	ne_add_request_header(req, "SOAPAction", get_soap_action(ctx->op)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Set body to SOAP request */ | 
					
						
							|  |  |  | 	ne_set_request_body_buffer(req, ast_str_buffer(request), ast_str_strlen(request)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Prepare XML parser */ | 
					
						
							|  |  |  | 	parser = ne_xml_create(); | 
					
						
							|  |  |  | 	ctx->parser = parser; | 
					
						
							|  |  |  | 	ne_xml_push_handler(parser, startelm, cdata, endelm, ctx);	/* Callbacks */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Dispatch request and parse response as XML */ | 
					
						
							|  |  |  | 	ret = ne_xml_dispatch_request(req, parser); | 
					
						
							|  |  |  | 	if (ret != NE_OK) { /* Error handling */ | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Unable to communicate with Exchange Web Service at '%s': %s\n", ctx->pvt->url, ne_get_error(ctx->pvt->session)); | 
					
						
							|  |  |  | 		ne_request_destroy(req); | 
					
						
							|  |  |  | 		ast_free(request); | 
					
						
							|  |  |  | 		ne_xml_destroy(parser); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Cleanup */ | 
					
						
							|  |  |  | 	ne_request_destroy(req); | 
					
						
							|  |  |  | 	ne_xml_destroy(parser); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int ewscal_write_event(struct ast_calendar_event *event) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ast_str *request; | 
					
						
							|  |  |  | 	struct ewscal_pvt *pvt = event->owner->tech_pvt; | 
					
						
							|  |  |  | 	char start[21], end[21]; | 
					
						
							|  |  |  | 	struct xml_context ctx = { | 
					
						
							|  |  |  | 		.op = XML_OP_CREATE, | 
					
						
							|  |  |  | 		.pvt = pvt, | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!pvt) { | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(request = ast_str_create(1024))) { | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_str_set(&request, 0, | 
					
						
							|  |  |  | 		"<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " | 
					
						
							|  |  |  | 			"xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" " | 
					
						
							|  |  |  | 			"xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" " | 
					
						
							|  |  |  | 			"xmlns:t=\"http://schemas.microsoft.com/exchange/services/2006/types\">" | 
					
						
							|  |  |  | 			"<soap:Body>" | 
					
						
							|  |  |  | 			"<CreateItem xmlns=\"http://schemas.microsoft.com/exchange/services/2006/messages\" " | 
					
						
							|  |  |  | 				"xmlns:t=\"http://schemas.microsoft.com/exchange/services/2006/types\" " | 
					
						
							|  |  |  | 				"SendMeetingInvitations=\"SendToNone\" >" | 
					
						
							|  |  |  | 				"<SavedItemFolderId>" | 
					
						
							|  |  |  | 					"<t:DistinguishedFolderId Id=\"calendar\"/>" | 
					
						
							|  |  |  | 				"</SavedItemFolderId>" | 
					
						
							|  |  |  | 				"<Items>" | 
					
						
							|  |  |  | 					"<t:CalendarItem xmlns=\"http://schemas.microsoft.com/exchange/services/2006/types\">" | 
					
						
							|  |  |  | 						"<Subject>%s</Subject>" | 
					
						
							|  |  |  | 						"<Body BodyType=\"Text\">%s</Body>" | 
					
						
							|  |  |  | 						"<ReminderIsSet>false</ReminderIsSet>" | 
					
						
							|  |  |  | 						"<Start>%s</Start>" | 
					
						
							|  |  |  | 						"<End>%s</End>" | 
					
						
							|  |  |  | 						"<IsAllDayEvent>false</IsAllDayEvent>" | 
					
						
							|  |  |  | 						"<LegacyFreeBusyStatus>%s</LegacyFreeBusyStatus>" | 
					
						
							|  |  |  | 						"<Location>%s</Location>" | 
					
						
							|  |  |  | 					"</t:CalendarItem>" | 
					
						
							|  |  |  | 				"</Items>" | 
					
						
							|  |  |  | 			"</CreateItem>" | 
					
						
							|  |  |  | 		"</soap:Body>" | 
					
						
							|  |  |  | 		"</soap:Envelope>", | 
					
						
							|  |  |  | 		event->summary, | 
					
						
							|  |  |  | 		event->description, | 
					
						
							|  |  |  | 		mstime(event->start, start, sizeof(start)), | 
					
						
							|  |  |  | 		mstime(event->end, end, sizeof(end)), | 
					
						
							|  |  |  | 		msstatus(event->busy_state), | 
					
						
							|  |  |  | 		event->location | 
					
						
							|  |  |  | 	); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = send_ews_request_and_parse(request, &ctx); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_free(request); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct calendar_id *get_ewscal_ids_for(struct ewscal_pvt *pvt) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	char start[21], end[21]; | 
					
						
							|  |  |  | 	struct ast_tm tm; | 
					
						
							|  |  |  | 	struct timeval tv; | 
					
						
							|  |  |  | 	struct ast_str *request; | 
					
						
							|  |  |  | 	struct xml_context ctx = { | 
					
						
							|  |  |  | 		.op = XML_OP_FIND, | 
					
						
							|  |  |  | 		.pvt = pvt, | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_debug(5, "EWS: get_ewscal_ids_for()\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!pvt) { | 
					
						
							|  |  |  | 		ast_log(LOG_ERROR, "There is no private!\n"); | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Prepare timeframe strings */ | 
					
						
							|  |  |  | 	tv = ast_tvnow(); | 
					
						
							|  |  |  | 	ast_localtime(&tv, &tm, "UTC"); | 
					
						
							|  |  |  | 	ast_strftime(start, sizeof(start), "%FT%TZ", &tm); | 
					
						
							|  |  |  | 	tv.tv_sec += 60 * pvt->owner->timeframe; | 
					
						
							|  |  |  | 	ast_localtime(&tv, &tm, "UTC"); | 
					
						
							|  |  |  | 	ast_strftime(end, sizeof(end), "%FT%TZ", &tm); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Prepare SOAP request */ | 
					
						
							|  |  |  | 	if (!(request = ast_str_create(512))) { | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_str_set(&request, 0, | 
					
						
							|  |  |  | 		"<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" " | 
					
						
							|  |  |  | 		"xmlns:ns1=\"http://schemas.microsoft.com/exchange/services/2006/types\" " | 
					
						
							|  |  |  | 		"xmlns:ns2=\"http://schemas.microsoft.com/exchange/services/2006/messages\">" | 
					
						
							|  |  |  | 			"<SOAP-ENV:Body>" | 
					
						
							|  |  |  | 				"<ns2:FindItem Traversal=\"Shallow\">" | 
					
						
							|  |  |  | 					"<ns2:ItemShape>" | 
					
						
							|  |  |  | 						"<ns1:BaseShape>IdOnly</ns1:BaseShape>" | 
					
						
							|  |  |  | 					"</ns2:ItemShape>" | 
					
						
							|  |  |  | 					"<ns2:CalendarView StartDate=\"%s\" EndDate=\"%s\"/>"	/* Timeframe */ | 
					
						
							|  |  |  | 					"<ns2:ParentFolderIds>" | 
					
						
							|  |  |  | 						"<ns1:DistinguishedFolderId Id=\"calendar\"/>" | 
					
						
							|  |  |  | 					"</ns2:ParentFolderIds>" | 
					
						
							|  |  |  | 				"</ns2:FindItem>" | 
					
						
							|  |  |  | 			"</SOAP-ENV:Body>" | 
					
						
							|  |  |  | 		"</SOAP-ENV:Envelope>", | 
					
						
							|  |  |  | 		start, end	/* Timeframe */ | 
					
						
							|  |  |  | 	); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	AST_LIST_HEAD_INIT_NOLOCK(&ctx.ids); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Dispatch request and parse response as XML */ | 
					
						
							|  |  |  | 	if (send_ews_request_and_parse(request, &ctx)) { | 
					
						
							|  |  |  | 		ast_free(request); | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Cleanup */ | 
					
						
							|  |  |  | 	ast_free(request); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return AST_LIST_FIRST(&ctx.ids); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int parse_ewscal_id(struct ewscal_pvt *pvt, const char *id) { | 
					
						
							|  |  |  | 	struct ast_str *request; | 
					
						
							|  |  |  | 	struct xml_context ctx = { | 
					
						
							|  |  |  | 		.pvt = pvt, | 
					
						
							|  |  |  | 		.op = XML_OP_GET, | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(request = ast_str_create(512))) { | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_str_set(&request, 0, | 
					
						
							|  |  |  | 		"<?xml version=\"1.0\" encoding=\"utf-8\"?>" | 
					
						
							|  |  |  | 		"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" " | 
					
						
							|  |  |  | 		"xmlns:t=\"http://schemas.microsoft.com/exchange/services/2006/types\">" | 
					
						
							|  |  |  | 		"<soap:Body>" | 
					
						
							|  |  |  | 			"<GetItem xmlns=\"http://schemas.microsoft.com/exchange/services/2006/messages\">" | 
					
						
							|  |  |  | 				"<ItemShape>" | 
					
						
							|  |  |  | 					"<t:BaseShape>AllProperties</t:BaseShape>" | 
					
						
							|  |  |  | 				"</ItemShape>" | 
					
						
							|  |  |  | 				"<ItemIds>" | 
					
						
							|  |  |  | 					"<t:ItemId Id=\"%s\"/>" | 
					
						
							|  |  |  | 				"</ItemIds>" | 
					
						
							|  |  |  | 			"</GetItem>" | 
					
						
							|  |  |  | 		"</soap:Body>" | 
					
						
							|  |  |  | 		"</soap:Envelope>", id | 
					
						
							|  |  |  | 	); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (send_ews_request_and_parse(request, &ctx)) { | 
					
						
							|  |  |  | 		ast_free(request); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_free(request); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int update_ewscal(struct ewscal_pvt *pvt) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct calendar_id *id_head; | 
					
						
							|  |  |  | 	struct calendar_id *iter; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(id_head = get_ewscal_ids_for(pvt))) { | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (iter = id_head; iter; iter = AST_LIST_NEXT(iter, next)) { | 
					
						
							|  |  |  | 		parse_ewscal_id(pvt, ast_str_buffer(iter->id)); | 
					
						
							|  |  |  | 		ast_free(iter->id); | 
					
						
							|  |  |  | 		ast_free(iter); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void *ewscal_load_calendar(void *void_data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ewscal_pvt *pvt; | 
					
						
							|  |  |  | 	const struct ast_config *cfg; | 
					
						
							|  |  |  | 	struct ast_variable *v; | 
					
						
							|  |  |  | 	struct ast_calendar *cal = void_data; | 
					
						
							|  |  |  | 	ast_mutex_t refreshlock; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_debug(5, "EWS: ewscal_load_calendar()\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(cal && (cfg = ast_calendar_config_acquire()))) { | 
					
						
							|  |  |  | 		ast_log(LOG_ERROR, "You must enable calendar support for res_ewscal to load\n"); | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ao2_trylock(cal)) { | 
					
						
							|  |  |  | 		if (cal->unloading) { | 
					
						
							|  |  |  | 			ast_log(LOG_WARNING, "Unloading module, load_calendar cancelled.\n"); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			ast_log(LOG_WARNING, "Could not lock calendar, aborting!\n"); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		ast_calendar_config_release(); | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(pvt = ao2_alloc(sizeof(*pvt), ewscal_destructor))) { | 
					
						
							|  |  |  | 		ast_log(LOG_ERROR, "Could not allocate ewscal_pvt structure for calendar: %s\n", cal->name); | 
					
						
							|  |  |  | 		ast_calendar_config_release(); | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pvt->owner = cal; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(pvt->events = ast_calendar_event_container_alloc())) { | 
					
						
							|  |  |  | 		ast_log(LOG_ERROR, "Could not allocate space for fetching events for calendar: %s\n", cal->name); | 
					
						
							|  |  |  | 		pvt = unref_ewscal(pvt); | 
					
						
							|  |  |  | 		ao2_unlock(cal); | 
					
						
							|  |  |  | 		ast_calendar_config_release(); | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ast_string_field_init(pvt, 32)) { | 
					
						
							|  |  |  | 		ast_log(LOG_ERROR, "Couldn't allocate string field space for calendar: %s\n", cal->name); | 
					
						
							|  |  |  | 		pvt = unref_ewscal(pvt); | 
					
						
							|  |  |  | 		ao2_unlock(cal); | 
					
						
							|  |  |  | 		ast_calendar_config_release(); | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (v = ast_variable_browse(cfg, cal->name); v; v = v->next) { | 
					
						
							|  |  |  | 		if (!strcasecmp(v->name, "url")) { | 
					
						
							|  |  |  | 			ast_string_field_set(pvt, url, v->value); | 
					
						
							|  |  |  | 		} else if (!strcasecmp(v->name, "user")) { | 
					
						
							|  |  |  | 			ast_string_field_set(pvt, user, v->value); | 
					
						
							|  |  |  | 		} else if (!strcasecmp(v->name, "secret")) { | 
					
						
							|  |  |  | 			ast_string_field_set(pvt, secret, v->value); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_calendar_config_release(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ast_strlen_zero(pvt->url)) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "No URL was specified for Exchange Web Service calendar '%s' - skipping.\n", cal->name); | 
					
						
							|  |  |  | 		pvt = unref_ewscal(pvt); | 
					
						
							|  |  |  | 		ao2_unlock(cal); | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ne_uri_parse(pvt->url, &pvt->uri) || pvt->uri.host == NULL || pvt->uri.path == NULL) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Could not parse url '%s' for Exchange Web Service calendar '%s' - skipping.\n", pvt->url, cal->name); | 
					
						
							|  |  |  | 		pvt = unref_ewscal(pvt); | 
					
						
							|  |  |  | 		ao2_unlock(cal); | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (pvt->uri.scheme == NULL) { | 
					
						
							|  |  |  | 		pvt->uri.scheme = "http"; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (pvt->uri.port == 0) { | 
					
						
							|  |  |  | 		pvt->uri.port = ne_uri_defaultport(pvt->uri.scheme); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_debug(3, "ne_uri.scheme	= %s\n", pvt->uri.scheme); | 
					
						
							|  |  |  | 	ast_debug(3, "ne_uri.host	= %s\n", pvt->uri.host); | 
					
						
							|  |  |  | 	ast_debug(3, "ne_uri.port	= %u\n", pvt->uri.port); | 
					
						
							|  |  |  | 	ast_debug(3, "ne_uri.path	= %s\n", pvt->uri.path); | 
					
						
							|  |  |  | 	ast_debug(3, "user		= %s\n", pvt->user); | 
					
						
							|  |  |  | 	ast_debug(3, "secret		= %s\n", pvt->secret); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pvt->session = ne_session_create(pvt->uri.scheme, pvt->uri.host, pvt->uri.port); | 
					
						
							|  |  |  | 	ne_set_server_auth(pvt->session, auth_credentials, pvt); | 
					
						
							|  |  |  | 	ne_set_useragent(pvt->session, "Asterisk"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!strcasecmp(pvt->uri.scheme, "https")) { | 
					
						
							|  |  |  | 		ne_ssl_trust_default_ca(pvt->session); | 
					
						
							|  |  |  | 		ne_ssl_set_verify(pvt->session, ssl_verify, pvt); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cal->tech_pvt = pvt; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_mutex_init(&refreshlock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Load it the first time */ | 
					
						
							|  |  |  | 	update_ewscal(pvt); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ao2_unlock(cal); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* The only writing from another thread will be if unload is true */ | 
					
						
							|  |  |  | 	for (;;) { | 
					
						
							|  |  |  | 		struct timeval tv = ast_tvnow(); | 
					
						
							|  |  |  | 		struct timespec ts = {0,}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ts.tv_sec = tv.tv_sec + (60 * pvt->owner->refresh); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ast_mutex_lock(&refreshlock); | 
					
						
							|  |  |  | 		while (!pvt->owner->unloading) { | 
					
						
							|  |  |  | 			if (ast_cond_timedwait(&pvt->owner->unload, &refreshlock, &ts) == ETIMEDOUT) { | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		ast_mutex_unlock(&refreshlock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (pvt->owner->unloading) { | 
					
						
							|  |  |  | 			ast_debug(10, "Skipping refresh since we got a shutdown signal\n"); | 
					
						
							|  |  |  | 			return NULL; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ast_debug(10, "Refreshing after %d minute timeout\n", pvt->owner->refresh); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		update_ewscal(pvt); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int load_module(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/* Actualy, 0.29.1 is required (because of NTLM authentication), but this
 | 
					
						
							|  |  |  | 	 * function does not support matching patch version. */ | 
					
						
							|  |  |  | 	if (ne_version_match(0, 29)) { | 
					
						
							|  |  |  | 		ast_log(LOG_ERROR, "Exchange Web Service calendar module require neon >= 0.29.1, but %s is installed.\n", ne_version_string()); | 
					
						
							|  |  |  | 		return AST_MODULE_LOAD_DECLINE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ast_calendar_register(&ewscal_tech) && (ne_sock_init() == 0)) { | 
					
						
							|  |  |  | 		return AST_MODULE_LOAD_DECLINE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return AST_MODULE_LOAD_SUCCESS; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int unload_module(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	ne_sock_exit(); | 
					
						
							|  |  |  | 	ast_calendar_unregister(&ewscal_tech); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-20 19:35:02 +00:00
										 |  |  | AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Asterisk MS Exchange Web Service Calendar Integration", | 
					
						
							| 
									
										
										
										
											2010-05-24 18:21:20 +00:00
										 |  |  | 	.load = load_module, | 
					
						
							|  |  |  | 	.unload = unload_module, | 
					
						
							| 
									
										
										
										
											2010-07-20 19:35:02 +00:00
										 |  |  | 	.load_pri = AST_MODPRI_DEVSTATE_PLUGIN, | 
					
						
							| 
									
										
										
										
											2010-05-24 18:21:20 +00:00
										 |  |  | ); |