Asterisk - The Open Source Telephony Project  18.5.0
Data Structures | Functions | Variables
res_calendar_exchange.c File Reference

Resource for handling MS Exchange calendars. More...

#include "asterisk.h"
#include <libical/ical.h>
#include <ne_session.h>
#include <ne_uri.h>
#include <ne_request.h>
#include <ne_auth.h>
#include <ne_redirect.h>
#include <iksemel.h>
#include "asterisk/module.h"
#include "asterisk/channel.h"
#include "asterisk/calendar.h"
#include "asterisk/lock.h"
#include "asterisk/config.h"
#include "asterisk/astobj2.h"
#include "asterisk/uuid.h"
Include dependency graph for res_calendar_exchange.c:

Go to the source code of this file.

Data Structures

struct  exchangecal_pvt
 
struct  xmlstate
 

Functions

static void __reg_module (void)
 
static void __unreg_module (void)
 
struct ast_moduleAST_MODULE_SELF_SYM (void)
 
static int auth_credentials (void *userdata, const char *realm, int attempts, char *username, char *secret)
 
static struct ast_strbs_to_exchange_bs (struct ast_str *dst, enum ast_calendar_busy_state bs)
 
static struct ast_strepoch_to_exchange_time (struct ast_str *dst, time_t epoch)
 
static void exchangecal_destructor (void *obj)
 
static struct ast_strexchangecal_get_events_between (struct exchangecal_pvt *pvt, time_t start_time, time_t end_time)
 
static void * exchangecal_load_calendar (void *data)
 
static struct ast_strexchangecal_request (struct exchangecal_pvt *pvt, const char *method, struct ast_str *req_body, struct ast_str *subdir)
 
static int exchangecal_write_event (struct ast_calendar_event *event)
 
static int fetch_response_reader (void *data, const char *block, size_t len)
 
static struct ast_strgenerate_exchange_uuid (struct ast_str *uid)
 
static int is_valid_uuid (struct ast_str *uid)
 
static int load_module (void)
 
static enum ast_calendar_busy_state msbusy_to_bs (const char *msbusy)
 
static time_t mstime_to_time_t (char *mstime)
 
static int parse_cdata (void *data, char *value, size_t len)
 
static int parse_tag (void *data, char *name, char **atts, int type)
 
static int unload_module (void)
 
static void * unref_exchangecal (void *obj)
 
static int update_exchangecal (struct exchangecal_pvt *pvt)
 
static struct ast_strxml_encode_str (struct ast_str *dst, const char *src)
 

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Asterisk MS Exchange Calendar Integration" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "30ef0c93b36035ec78c9cfd712d36d9b" , .support_level = AST_MODULE_SUPPORT_EXTENDED, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEVSTATE_PLUGIN, .requires = "res_calendar", }
 
static const struct ast_module_infoast_module_info = &__mod_info
 
static struct ast_calendar_tech exchangecal_tech
 

Detailed Description

Resource for handling MS Exchange calendars.

Definition in file res_calendar_exchange.c.

Function Documentation

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 749 of file res_calendar_exchange.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 749 of file res_calendar_exchange.c.

◆ AST_MODULE_SELF_SYM()

struct ast_module* AST_MODULE_SELF_SYM ( void  )

Definition at line 749 of file res_calendar_exchange.c.

◆ auth_credentials()

static int auth_credentials ( void *  userdata,
const char *  realm,
int  attempts,
char *  username,
char *  secret 
)
static

Definition at line 357 of file res_calendar_exchange.c.

References ast_log, LOG_WARNING, ast_calendar::name, exchangecal_pvt::owner, exchangecal_pvt::secret, and exchangecal_pvt::user.

Referenced by exchangecal_load_calendar().

358 {
359  struct exchangecal_pvt *pvt = userdata;
360 
361  if (attempts > 1) {
362  ast_log(LOG_WARNING, "Invalid username or password for Exchange calendar '%s'\n", pvt->owner->name);
363  return -1;
364  }
365 
366  ne_strnzcpy(username, pvt->user, NE_ABUFSIZ);
367  ne_strnzcpy(secret, pvt->secret, NE_ABUFSIZ);
368 
369  return 0;
370 }
#define LOG_WARNING
Definition: logger.h:274
const ast_string_field secret
struct ast_calendar * owner
const ast_string_field user
#define ast_log
Definition: astobj2.c:42
const ast_string_field name
Definition: calendar.h:127

◆ bs_to_exchange_bs()

static struct ast_str* bs_to_exchange_bs ( struct ast_str dst,
enum ast_calendar_busy_state  bs 
)
static

Definition at line 323 of file res_calendar_exchange.c.

References AST_CALENDAR_BS_BUSY, AST_CALENDAR_BS_BUSY_TENTATIVE, and ast_str_set().

Referenced by exchangecal_write_event().

324 {
325  switch (bs) {
327  ast_str_set(&dst, 0, "%s", "BUSY");
328  break;
329 
331  ast_str_set(&dst, 0, "%s", "TENTATIVE");
332  break;
333 
334  default:
335  ast_str_set(&dst, 0, "%s", "FREE");
336  }
337 
338  return dst;
339 }
char * bs
Definition: eagi_proxy.c:73
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1065

◆ epoch_to_exchange_time()

static struct ast_str* epoch_to_exchange_time ( struct ast_str dst,
time_t  epoch 
)
static

Definition at line 302 of file res_calendar_exchange.c.

References ast_copy_string(), ast_str_append(), and tmp().

Referenced by exchangecal_write_event().

303 {
304  icaltimezone *utc = icaltimezone_get_utc_timezone();
305  icaltimetype tt = icaltime_from_timet_with_zone(epoch, 0, utc);
306  char tmp[30];
307  int i;
308 
309  ast_copy_string(tmp, icaltime_as_ical_string(tt), sizeof(tmp));
310  for (i = 0; tmp[i]; i++) {
311  ast_str_append(&dst, 0, "%c", tmp[i]);
312  if (i == 3 || i == 5)
313  ast_str_append(&dst, 0, "%c", '-');
314  if (i == 10 || i == 12)
315  ast_str_append(&dst, 0, "%c", ':');
316  if (i == 14)
317  ast_str_append(&dst, 0, "%s", ".000");
318  }
319 
320  return dst;
321 }
static int tmp()
Definition: bt_open.c:389
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1091
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401

◆ exchangecal_destructor()

static void exchangecal_destructor ( void *  obj)
static

Definition at line 219 of file res_calendar_exchange.c.

References ao2_callback, ao2_ref, ast_debug, ast_string_field_free_memory, exchangecal_pvt::events, ast_calendar::name, NULL, OBJ_MULTIPLE, OBJ_NODATA, OBJ_UNLINK, exchangecal_pvt::owner, exchangecal_pvt::session, and exchangecal_pvt::uri.

Referenced by exchangecal_load_calendar().

220 {
221  struct exchangecal_pvt *pvt = obj;
222 
223  ast_debug(1, "Destroying pvt for Exchange calendar %s\n", pvt->owner->name);
224  if (pvt->session) {
225  ne_session_destroy(pvt->session);
226  }
227  ne_uri_free(&pvt->uri);
229 
231 
232  ao2_ref(pvt->events, -1);
233 }
#define ao2_callback(c, flags, cb_fn, arg)
Definition: astobj2.h:1716
struct ast_calendar * owner
#define NULL
Definition: resample.c:96
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ao2_ref(o, delta)
Definition: astobj2.h:464
const ast_string_field name
Definition: calendar.h:127
struct ao2_container * events
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:368

◆ exchangecal_get_events_between()

static struct ast_str* exchangecal_get_events_between ( struct exchangecal_pvt pvt,
time_t  start_time,
time_t  end_time 
)
static

Definition at line 532 of file res_calendar_exchange.c.

References ast_debug, ast_free, ast_localtime(), ast_log, ast_str_append(), ast_str_buffer(), ast_str_create, ast_strftime(), end, exchangecal_request(), LOG_ERROR, NULL, and exchangecal_pvt::url.

Referenced by update_exchangecal().

533 {
534  struct ast_str *body, *response;
535  char start[80], end[80];
536  struct timeval tv = {0,};
537  struct ast_tm tm;
538 
539  tv.tv_sec = start_time;
540  ast_localtime(&tv, &tm, "UTC");
541  ast_strftime(start, sizeof(start), "%Y/%m/%d %T", &tm);
542 
543  tv.tv_sec = end_time;
544  ast_localtime(&tv, &tm, "UTC");
545  ast_strftime(end, sizeof(end), "%Y/%m/%d %T", &tm);
546 
547  if (!(body = ast_str_create(512))) {
548  ast_log(LOG_ERROR, "Could not allocate memory for body of request!\n");
549  return NULL;
550  }
551 
552  ast_str_append(&body, 0,
553  "<?xml version=\"1.0\"?>\n"
554  "<g:searchrequest xmlns:g=\"DAV:\">\n"
555  " <g:sql> SELECT \"urn:schemas:calendar:location\", \"urn:schemas:httpmail:subject\",\n"
556  " \"urn:schemas:calendar:dtstart\", \"urn:schemas:calendar:dtend\",\n"
557  " \"urn:schemas:calendar:busystatus\", \"urn:schemas:calendar:instancetype\",\n"
558  " \"urn:schemas:calendar:uid\", \"urn:schemas:httpmail:textdescription\",\n"
559  " \"urn:schemas:calendar:organizer\", \"urn:schemas:calendar:reminderoffset\"\n"
560  " FROM Scope('SHALLOW TRAVERSAL OF \"%s/Calendar\"')\n"
561  " WHERE NOT \"urn:schemas:calendar:instancetype\" = 1\n"
562  " AND \"DAV:contentclass\" = 'urn:content-classes:appointment'\n"
563  " AND NOT (\"urn:schemas:calendar:dtend\" &lt; '%s'\n"
564  " OR \"urn:schemas:calendar:dtstart\" &gt; '%s')\n"
565  " ORDER BY \"urn:schemas:calendar:dtstart\" ASC\n"
566  " </g:sql>\n"
567  "</g:searchrequest>\n", pvt->url, start, end);
568 
569  ast_debug(5, "Request:\n%s\n", ast_str_buffer(body));
570  response = exchangecal_request(pvt, "SEARCH", body, NULL);
571  ast_debug(5, "Response:\n%s\n", ast_str_buffer(response));
572  ast_free(body);
573 
574  return response;
575 }
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
struct ast_tm * ast_localtime(const struct timeval *timep, struct ast_tm *p_tm, const char *zone)
Timezone-independent version of localtime_r(3).
Definition: localtime.c:1739
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1091
#define NULL
Definition: resample.c:96
char * end
Definition: eagi_proxy.c:73
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
#define LOG_ERROR
Definition: logger.h:285
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
#define ast_free(a)
Definition: astmm.h:182
int ast_strftime(char *buf, size_t len, const char *format, const struct ast_tm *tm)
Special version of strftime(3) that handles fractions of a second. Takes the same arguments as strfti...
Definition: localtime.c:2524
static struct ast_str * exchangecal_request(struct exchangecal_pvt *pvt, const char *method, struct ast_str *req_body, struct ast_str *subdir)
const ast_string_field url
#define ast_str_create(init_len)
Create a malloc&#39;ed dynamic length string.
Definition: strings.h:620

◆ exchangecal_load_calendar()

static void * exchangecal_load_calendar ( void *  data)
static

Definition at line 600 of file res_calendar_exchange.c.

References ao2_alloc, ao2_trylock, ao2_unlock, ast_calendar_config_acquire(), ast_calendar_config_release(), ast_calendar_event_container_alloc(), ast_cond_timedwait, ast_debug, ast_log, ast_mutex_init, ast_mutex_lock, ast_mutex_unlock, ast_string_field_init, ast_string_field_set, ast_strlen_zero, ast_tvnow(), ast_variable_browse(), auth_credentials(), exchangecal_pvt::events, exchangecal_destructor(), LOG_ERROR, LOG_WARNING, ast_variable::name, ast_calendar::name, ast_variable::next, NULL, exchangecal_pvt::owner, ast_calendar::refresh, refreshlock, exchangecal_pvt::secret, exchangecal_pvt::session, ast_calendar::tech_pvt, ast_calendar::unload, ast_calendar::unloading, unref_exchangecal(), update_exchangecal(), exchangecal_pvt::uri, exchangecal_pvt::url, and ast_variable::value.

601 {
602  struct exchangecal_pvt *pvt;
603  const struct ast_config *cfg;
604  struct ast_variable *v;
605  struct ast_calendar *cal = void_data;
607 
608  if (!(cal && (cfg = ast_calendar_config_acquire()))) {
609  ast_log(LOG_ERROR, "You must enable calendar support for res_exchangecal to load\n");
610  return NULL;
611  }
612 
613  if (ao2_trylock(cal)) {
614  if (cal->unloading) {
615  ast_log(LOG_WARNING, "Unloading module, load_calendar cancelled.\n");
616  } else {
617  ast_log(LOG_WARNING, "Could not lock calendar, aborting!\n");
618  }
620  return NULL;
621  }
622 
623  if (!(pvt = ao2_alloc(sizeof(*pvt), exchangecal_destructor))) {
624  ast_log(LOG_ERROR, "Could not allocate exchangecal_pvt structure for calendar: %s\n", cal->name);
626  return NULL;
627  }
628 
629  pvt->owner = cal;
630 
631  if (!(pvt->events = ast_calendar_event_container_alloc())) {
632  ast_log(LOG_ERROR, "Could not allocate space for fetching events for calendar: %s\n", cal->name);
633  pvt = unref_exchangecal(pvt);
634  ao2_unlock(cal);
636  return NULL;
637  }
638 
639  if (ast_string_field_init(pvt, 32)) {
640  ast_log(LOG_ERROR, "Couldn't allocate string field space for calendar: %s\n", cal->name);
641  pvt = unref_exchangecal(pvt);
642  ao2_unlock(cal);
644  return NULL;
645  }
646 
647  for (v = ast_variable_browse(cfg, cal->name); v; v = v->next) {
648  if (!strcasecmp(v->name, "url")) {
649  ast_string_field_set(pvt, url, v->value);
650  } else if (!strcasecmp(v->name, "user")) {
651  ast_string_field_set(pvt, user, v->value);
652  } else if (!strcasecmp(v->name, "secret")) {
653  ast_string_field_set(pvt, secret, v->value);
654  }
655  }
656 
658 
659  if (ast_strlen_zero(pvt->url)) {
660  ast_log(LOG_WARNING, "No URL was specified for Exchange calendar '%s' - skipping.\n", cal->name);
661  pvt = unref_exchangecal(pvt);
662  ao2_unlock(cal);
663  return NULL;
664  }
665 
666  if (ne_uri_parse(pvt->url, &pvt->uri) || pvt->uri.host == NULL || pvt->uri.path == NULL) {
667  ast_log(LOG_WARNING, "Could not parse url '%s' for Exchange calendar '%s' - skipping.\n", pvt->url, cal->name);
668  pvt = unref_exchangecal(pvt);
669  ao2_unlock(cal);
670  return NULL;
671  }
672 
673  if (pvt->uri.scheme == NULL) {
674  pvt->uri.scheme = "http";
675  }
676 
677  if (pvt->uri.port == 0) {
678  pvt->uri.port = ne_uri_defaultport(pvt->uri.scheme);
679  }
680 
681  pvt->session = ne_session_create(pvt->uri.scheme, pvt->uri.host, pvt->uri.port);
682  ne_redirect_register(pvt->session);
683  ne_set_server_auth(pvt->session, auth_credentials, pvt);
684  if (!strcasecmp(pvt->uri.scheme, "https")) {
685  ne_ssl_trust_default_ca(pvt->session);
686  }
687 
688  cal->tech_pvt = pvt;
689 
690  ast_mutex_init(&refreshlock);
691 
692  /* Load it the first time */
693  update_exchangecal(pvt);
694 
695  ao2_unlock(cal);
696 
697  /* The only writing from another thread will be if unload is true */
698  for (;;) {
699  struct timeval tv = ast_tvnow();
700  struct timespec ts = {0,};
701 
702  ts.tv_sec = tv.tv_sec + (60 * pvt->owner->refresh);
703 
704  ast_mutex_lock(&refreshlock);
705  while (!pvt->owner->unloading) {
706  if (ast_cond_timedwait(&pvt->owner->unload, &refreshlock, &ts) == ETIMEDOUT) {
707  break;
708  }
709  }
710  ast_mutex_unlock(&refreshlock);
711 
712  if (pvt->owner->unloading) {
713  ast_debug(10, "Skipping refresh since we got a shutdown signal\n");
714  return NULL;
715  }
716 
717  ast_debug(10, "Refreshing after %d minute timeout\n", pvt->owner->refresh);
718 
719  update_exchangecal(pvt);
720  }
721 
722  return NULL;
723 }
struct ast_variable * next
ast_cond_t unload
Definition: calendar.h:135
unsigned int unloading
Definition: calendar.h:136
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
Definition: extconf.c:1216
static int update_exchangecal(struct exchangecal_pvt *pvt)
#define LOG_WARNING
Definition: logger.h:274
Structure for variables, used for configurations and for channel variables.
struct ast_calendar * owner
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:150
#define ast_mutex_lock(a)
Definition: lock.h:187
#define ao2_unlock(a)
Definition: astobj2.h:730
static void * unref_exchangecal(void *obj)
#define NULL
Definition: resample.c:96
#define ast_strlen_zero(foo)
Definition: strings.h:52
void * tech_pvt
Definition: calendar.h:119
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:353
static int auth_credentials(void *userdata, const char *realm, int attempts, char *username, char *secret)
struct ao2_container * ast_calendar_event_container_alloc(void)
Allocate an astobj2 container for ast_calendar_event objects.
Definition: res_calendar.c:689
const ast_string_field name
Definition: calendar.h:127
#define LOG_ERROR
Definition: logger.h:285
#define ao2_trylock(a)
Definition: astobj2.h:740
static ast_mutex_t refreshlock
Definition: res_calendar.c:225
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:411
const struct ast_config * ast_calendar_config_acquire(void)
Grab and lock pointer to the calendar config (read only)
Definition: res_calendar.c:258
struct ao2_container * events
structure to hold users read from users.conf
const ast_string_field url
#define ast_mutex_init(pmutex)
Definition: lock.h:184
Asterisk calendar structure.
Definition: calendar.h:117
void ast_calendar_config_release(void)
Release the calendar config.
Definition: res_calendar.c:270
static char url[512]
#define ast_cond_timedwait(cond, mutex, time)
Definition: lock.h:204
Structure for mutex and tracking information.
Definition: lock.h:135
#define ast_mutex_unlock(a)
Definition: lock.h:188
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:514
static void exchangecal_destructor(void *obj)

◆ exchangecal_request()

static struct ast_str* exchangecal_request ( struct exchangecal_pvt pvt,
const char *  method,
struct ast_str req_body,
struct ast_str subdir 
)
static

Definition at line 372 of file res_calendar_exchange.c.

References ast_free, ast_log, ast_str_buffer(), ast_str_create, ast_str_strlen(), buf, fetch_response_reader(), LOG_ERROR, LOG_WARNING, ast_calendar::name, NULL, exchangecal_pvt::owner, exchangecal_pvt::session, exchangecal_pvt::uri, and exchangecal_pvt::url.

Referenced by exchangecal_get_events_between(), and exchangecal_write_event().

373 {
374  struct ast_str *response;
375  ne_request *req;
376  int ret;
377  char buf[1000];
378 
379  if (!pvt) {
380  ast_log(LOG_ERROR, "There is no private!\n");
381  return NULL;
382  }
383 
384  if (!(response = ast_str_create(512))) {
385  ast_log(LOG_ERROR, "Could not allocate memory for response.\n");
386  return NULL;
387  }
388 
389  snprintf(buf, sizeof(buf), "%s%s", pvt->uri.path, subdir ? ast_str_buffer(subdir) : "");
390 
391  req = ne_request_create(pvt->session, method, buf);
392  ne_add_response_body_reader(req, ne_accept_2xx, fetch_response_reader, &response);
393  ne_set_request_body_buffer(req, ast_str_buffer(req_body), ast_str_strlen(req_body));
394  ne_add_request_header(req, "Content-type", "text/xml");
395 
396  ret = ne_request_dispatch(req);
397  ne_request_destroy(req);
398 
399  if (ret != NE_OK || !ast_str_strlen(response)) {
400  ast_log(LOG_WARNING, "Unknown response to CalDAV calendar %s, request %s to %s: %s\n", pvt->owner->name, method, pvt->url, ne_get_error(pvt->session));
401  ast_free(response);
402  return NULL;
403  }
404 
405  return response;
406 }
static int fetch_response_reader(void *data, const char *block, size_t len)
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
#define LOG_WARNING
Definition: logger.h:274
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
struct ast_calendar * owner
#define NULL
Definition: resample.c:96
#define ast_log
Definition: astobj2.c:42
const char * method
Definition: res_pjsip.c:4335
const ast_string_field name
Definition: calendar.h:127
#define LOG_ERROR
Definition: logger.h:285
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
#define ast_free(a)
Definition: astmm.h:182
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:688
const ast_string_field url
#define ast_str_create(init_len)
Create a malloc&#39;ed dynamic length string.
Definition: strings.h:620

◆ exchangecal_write_event()

static int exchangecal_write_event ( struct ast_calendar_event event)
static

Definition at line 408 of file res_calendar_exchange.c.

References ast_free, ast_log, ast_str_append(), ast_str_buffer(), ast_str_create, ast_str_set(), ast_strlen_zero, AST_UUID_STR_LEN, ast_verb, bs_to_exchange_bs(), ast_calendar_event::busy_state, ast_calendar_event::description, end, ast_calendar_event::end, epoch_to_exchange_time(), exchangecal_request(), generate_exchange_uuid(), is_valid_uuid(), ast_calendar_event::location, LOG_ERROR, LOG_WARNING, NULL, ast_calendar_event::organizer, ast_calendar_event::owner, ast_calendar_event::start, ast_calendar_event::summary, ast_calendar::tech_pvt, ast_calendar_event::uid, and xml_encode_str().

409 {
410  struct ast_str *body = NULL;
411  struct ast_str *response = NULL;
412  struct ast_str *subdir = NULL;
413  struct ast_str *uid = NULL;
414  struct ast_str *summary = NULL;
415  struct ast_str *description = NULL;
416  struct ast_str *organizer = NULL;
417  struct ast_str *location = NULL;
418  struct ast_str *start = NULL;
419  struct ast_str *end = NULL;
420  struct ast_str *busystate = NULL;
421  int ret = -1;
422 
423  if (!event) {
424  ast_log(LOG_WARNING, "No event passed!\n");
425  return -1;
426  }
427 
428  if (!(event->start && event->end)) {
429  ast_log(LOG_WARNING, "The event must contain a start and an end\n");
430  return -1;
431  }
432  if (!(body = ast_str_create(512)) ||
433  !(subdir = ast_str_create(32))) {
434  ast_log(LOG_ERROR, "Could not allocate memory for request!\n");
435  goto write_cleanup;
436  }
437 
438  if (!(uid = ast_str_create(AST_UUID_STR_LEN)) ||
439  !(summary = ast_str_create(32)) ||
440  !(description = ast_str_create(32)) ||
441  !(organizer = ast_str_create(32)) ||
442  !(location = ast_str_create(32)) ||
443  !(start = ast_str_create(32)) ||
444  !(end = ast_str_create(32)) ||
445  !(busystate = ast_str_create(32))) {
446  ast_log(LOG_ERROR, "Unable to allocate memory for request values\n");
447  goto write_cleanup;
448  }
449 
450  if (ast_strlen_zero(event->uid)) {
451  uid = generate_exchange_uuid(uid);
452  } else {
453  ast_str_set(&uid, AST_UUID_STR_LEN, "%s", event->uid);
454  }
455 
456  if (!is_valid_uuid(uid)) {
457  ast_log(LOG_WARNING, "An invalid uid was provided, you may leave this field blank to have one generated for you\n");
458  goto write_cleanup;
459  }
460 
461  summary = xml_encode_str(summary, event->summary);
462  description = xml_encode_str(description, event->description);
463  organizer = xml_encode_str(organizer, event->organizer);
464  location = xml_encode_str(location, event->location);
465  start = epoch_to_exchange_time(start, event->start);
466  end = epoch_to_exchange_time(end, event->end);
467  busystate = bs_to_exchange_bs(busystate, event->busy_state);
468 
469  ast_str_append(&body, 0,
470  "<?xml version=\"1.0\"?>\n"
471  "<a:propertyupdate\n"
472  " xmlns:a=\"DAV:\"\n"
473  " xmlns:e=\"http://schemas.microsoft.com/exchange/\"\n"
474  " xmlns:mapi=\"http://schemas.microsoft.com/mapi/\"\n"
475  " xmlns:mapit=\"http://schemas.microsoft.com/mapi/proptag/\"\n"
476  " xmlns:x=\"xml:\" xmlns:cal=\"urn:schemas:calendar:\"\n"
477  " xmlns:dt=\"uuid:%s/\"\n" /* uid */
478  " xmlns:header=\"urn:schemas:mailheader:\"\n"
479  " xmlns:mail=\"urn:schemas:httpmail:\"\n"
480  ">\n"
481  " <a:set>\n"
482  " <a:prop>\n"
483  " <a:contentclass>urn:content-classes:appointment</a:contentclass>\n"
484  " <e:outlookmessageclass>IPM.Appointment</e:outlookmessageclass>\n"
485  " <mail:subject>%s</mail:subject>\n" /* summary */
486  " <mail:description>%s</mail:description>\n" /* description */
487  " <header:to>%s</header:to>\n" /* organizer */
488  " <cal:location>%s</cal:location>\n" /* location */
489  " <cal:dtstart dt:dt=\"dateTime.tz\">%s</cal:dtstart>\n" /* start */
490  " <cal:dtend dt:dt=\"dateTime.tz\">%s</cal:dtend>\n" /* end */
491  " <cal:instancetype dt:dt=\"int\">0</cal:instancetype>\n"
492  " <cal:busystatus>%s</cal:busystatus>\n" /* busy_state (BUSY, FREE, BUSY_TENTATIVE) */
493  " <cal:meetingstatus>CONFIRMED</cal:meetingstatus>\n"
494  " <cal:alldayevent dt:dt=\"boolean\">0</cal:alldayevent>\n" /* XXX need to add event support for all day events */
495  " <cal:responserequested dt:dt=\"boolean\">0</cal:responserequested>\n"
496  " <mapi:finvited dt:dt=\"boolean\">1</mapi:finvited>\n"
497  " </a:prop>\n"
498  " </a:set>\n"
499  "</a:propertyupdate>\n",
500  ast_str_buffer(uid),
501  ast_str_buffer(summary),
502  ast_str_buffer(description),
503  ast_str_buffer(organizer),
504  ast_str_buffer(location),
505  ast_str_buffer(start),
506  ast_str_buffer(end),
507  ast_str_buffer(busystate));
508  ast_verb(0, "\n\n%s\n\n", ast_str_buffer(body));
509  ast_str_set(&subdir, 0, "/Calendar/%s.eml", ast_str_buffer(uid));
510 
511  if ((response = exchangecal_request(event->owner->tech_pvt, "PROPPATCH", body, subdir))) {
512  ret = 0;
513  }
514 
515 write_cleanup:
516  ast_free(uid);
517  ast_free(summary);
518  ast_free(description);
519  ast_free(organizer);
520  ast_free(location);
521  ast_free(start);
522  ast_free(end);
523  ast_free(busystate);
524  ast_free(body);
525  ast_free(response);
526  ast_free(subdir);
527 
528  return ret;
529 }
#define AST_UUID_STR_LEN
Definition: uuid.h:27
static struct ast_str * xml_encode_str(struct ast_str *dst, const char *src)
#define LOG_WARNING
Definition: logger.h:274
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1091
const ast_string_field uid
Definition: calendar.h:101
enum ast_calendar_busy_state busy_state
Definition: calendar.h:107
#define NULL
Definition: resample.c:96
char * end
Definition: eagi_proxy.c:73
const ast_string_field description
Definition: calendar.h:101
#define ast_verb(level,...)
Definition: logger.h:463
const ast_string_field organizer
Definition: calendar.h:101
#define ast_strlen_zero(foo)
Definition: strings.h:52
static struct ast_str * bs_to_exchange_bs(struct ast_str *dst, enum ast_calendar_busy_state bs)
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1065
void * tech_pvt
Definition: calendar.h:119
#define ast_log
Definition: astobj2.c:42
const ast_string_field location
Definition: calendar.h:101
#define LOG_ERROR
Definition: logger.h:285
static int is_valid_uuid(struct ast_str *uid)
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
static struct ast_str * generate_exchange_uuid(struct ast_str *uid)
#define ast_free(a)
Definition: astmm.h:182
struct ast_calendar * owner
Definition: calendar.h:103
static struct ast_str * exchangecal_request(struct exchangecal_pvt *pvt, const char *method, struct ast_str *req_body, struct ast_str *subdir)
const ast_string_field summary
Definition: calendar.h:101
#define ast_str_create(init_len)
Create a malloc&#39;ed dynamic length string.
Definition: strings.h:620
static struct ast_str * epoch_to_exchange_time(struct ast_str *dst, time_t epoch)

◆ fetch_response_reader()

static int fetch_response_reader ( void *  data,
const char *  block,
size_t  len 
)
static

Definition at line 341 of file res_calendar_exchange.c.

References ast_free, ast_malloc, ast_str_append(), len(), and tmp().

Referenced by exchangecal_request().

342 {
343  struct ast_str **response = data;
344  unsigned char *tmp;
345 
346  if (!(tmp = ast_malloc(len + 1))) {
347  return -1;
348  }
349  memcpy(tmp, block, len);
350  tmp[len] = '\0';
351  ast_str_append(response, 0, "%s", tmp);
352  ast_free(tmp);
353 
354  return 0;
355 }
static int tmp()
Definition: bt_open.c:389
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1091
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:193
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
#define ast_free(a)
Definition: astmm.h:182

◆ generate_exchange_uuid()

static struct ast_str* generate_exchange_uuid ( struct ast_str uid)
static

Definition at line 244 of file res_calendar_exchange.c.

References ast_str_set(), ast_uuid_generate_str(), and AST_UUID_STR_LEN.

Referenced by exchangecal_write_event().

245 {
246  char buffer[AST_UUID_STR_LEN];
247 
248  ast_uuid_generate_str(buffer, sizeof(buffer));
249  ast_str_set(&uid, 0, "%s", buffer);
250  return uid;
251 }
#define AST_UUID_STR_LEN
Definition: uuid.h:27
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1065
char * ast_uuid_generate_str(char *buf, size_t size)
Generate a UUID string.
Definition: uuid.c:143

◆ is_valid_uuid()

static int is_valid_uuid ( struct ast_str uid)
static

Definition at line 253 of file res_calendar_exchange.c.

References ast_free, ast_str_buffer(), and ast_str_to_uuid().

Referenced by exchangecal_write_event().

254 {
255  struct ast_uuid *uuid = ast_str_to_uuid(ast_str_buffer(uid));
256 
257  if (uuid) {
258  ast_free(uuid);
259  return 1;
260  }
261 
262  return 0;
263 }
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
Definition: uuid.c:39
struct ast_uuid * ast_str_to_uuid(char *str)
Convert a string to a UUID.
Definition: uuid.c:151
#define ast_free(a)
Definition: astmm.h:182

◆ load_module()

static int load_module ( void  )
static

Definition at line 725 of file res_calendar_exchange.c.

References ast_calendar_register(), AST_MODULE_LOAD_DECLINE, and AST_MODULE_LOAD_SUCCESS.

Referenced by unload_module().

726 {
727  ne_sock_init();
729  ne_sock_exit();
731  }
732 
734 }
int ast_calendar_register(struct ast_calendar_tech *tech)
Register a new calendar technology.
Definition: res_calendar.c:549
static struct ast_calendar_tech exchangecal_tech
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78

◆ msbusy_to_bs()

static enum ast_calendar_busy_state msbusy_to_bs ( const char *  msbusy)
static

Definition at line 160 of file res_calendar_exchange.c.

References AST_CALENDAR_BS_BUSY, AST_CALENDAR_BS_BUSY_TENTATIVE, and AST_CALENDAR_BS_FREE.

Referenced by parse_cdata().

161 {
162  if (!strcasecmp(msbusy, "FREE")) {
163  return AST_CALENDAR_BS_FREE;
164  } else if (!strcasecmp(msbusy, "TENTATIVE")) {
166  } else {
167  return AST_CALENDAR_BS_BUSY;
168  }
169 }

◆ mstime_to_time_t()

static time_t mstime_to_time_t ( char *  mstime)
static

Definition at line 140 of file res_calendar_exchange.c.

Referenced by parse_cdata().

141 {
142  char *read, *write;
143  icaltimetype tt;
144  for (read = write = mstime; *read; read++) {
145  if (*read == '.') {
146  *write++ = 'Z';
147  *write = '\0';
148  break;
149  }
150  if (*read == '-' || *read == ':')
151  continue;
152  *write = *read;
153  write++;
154  }
155 
156  tt = icaltime_from_string(mstime);
157  return icaltime_as_timet(tt);
158 }
static const char * mstime(time_t t, char *buf, size_t buflen)

◆ parse_cdata()

static int parse_cdata ( void *  data,
char *  value,
size_t  len 
)
static

Definition at line 171 of file res_calendar_exchange.c.

References ast_calloc, ast_free, ast_skip_blanks(), ast_string_field_build, ast_calendar_event::description, xmlstate::in_prop, xmlstate::in_propstat, xmlstate::in_response, ast_calendar_event::location, msbusy_to_bs(), mstime_to_time_t(), ast_calendar_event::organizer, xmlstate::ptr, str, ast_calendar_event::summary, xmlstate::tag, and ast_calendar_event::uid.

Referenced by update_exchangecal().

172 {
173  char *str;
174  struct xmlstate *state = data;
175  struct ast_calendar_event *event = state->ptr;
176 
177 
178  str = ast_skip_blanks(value);
179 
180  if (str == value + len)
181  return IKS_OK;
182 
183  if (!(str = ast_calloc(1, len + 1))) {
184  return IKS_NOMEM;
185  }
186  memcpy(str, value, len);
187  if (!(state->in_response && state->in_propstat && state->in_prop)) {
188  ast_free(str);
189  return IKS_OK;
190  }
191  /* We use ast_string_field_build here because libiksemel is parsing CDATA with &lt; as
192  * new elements which is a bit odd and shouldn't happen */
193  if (!strcasecmp(state->tag, "subject")) {
194  ast_string_field_build(event, summary, "%s%s", event->summary, str);
195  } else if (!strcasecmp(state->tag, "location")) {
196  ast_string_field_build(event, location, "%s%s", event->location, str);
197  } else if (!strcasecmp(state->tag, "uid")) {
198  ast_string_field_build(event, uid, "%s%s", event->location, str);
199  } else if (!strcasecmp(state->tag, "organizer")) {
200  ast_string_field_build(event, organizer, "%s%s", event->organizer, str);
201  } else if (!strcasecmp(state->tag, "textdescription")) {
202  ast_string_field_build(event, description, "%s%s", event->description, str);
203  } else if (!strcasecmp(state->tag, "dtstart")) {
204  event->start = mstime_to_time_t(str);
205  } else if (!strcasecmp(state->tag, "dtend")) {
206  event->end = mstime_to_time_t(str);
207  } else if (!strcasecmp(state->tag, "busystatus")) {
208  event->busy_state = msbusy_to_bs(str);
209  } else if (!strcasecmp(state->tag, "reminderoffset")) {
210  /*XXX Currently we rely on event->start being set first which means we rely on the response order
211  * which technically should be fine since the query returns in the order we ask for, but ... */
212  event->alarm = event->start - atoi(str);
213  }
214 
215  ast_free(str);
216  return IKS_OK;
217 }
static time_t mstime_to_time_t(char *mstime)
Definition: astman.c:222
const ast_string_field uid
Definition: calendar.h:101
const char * str
Definition: app_jack.c:147
int value
Definition: syslog.c:37
const ast_string_field description
Definition: calendar.h:101
const ast_string_field organizer
Definition: calendar.h:101
const ast_string_field location
Definition: calendar.h:101
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
char * ast_skip_blanks(const char *str)
Gets a pointer to the first non-whitespace character in a string.
Definition: strings.h:157
#define ast_free(a)
Definition: astmm.h:182
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
#define ast_string_field_build(x, field, fmt, args...)
Set a field to a complex (built) value.
Definition: stringfields.h:550
const ast_string_field summary
Definition: calendar.h:101
static enum ast_calendar_busy_state msbusy_to_bs(const char *msbusy)

◆ parse_tag()

static int parse_tag ( void *  data,
char *  name,
char **  atts,
int  type 
)
static

Definition at line 83 of file res_calendar_exchange.c.

References ao2_link, ast_calendar_event_alloc(), ast_calendar_unref_event(), ast_copy_string(), ast_log, ast_strlen_zero, exchangecal_pvt::events, caldav_pvt::events, xmlstate::in_prop, xmlstate::in_propstat, xmlstate::in_response, LOG_ERROR, caldav_pvt::owner, xmlstate::ptr, xmlstate::pvt, xmlstate::tag, and tmp().

Referenced by update_exchangecal().

84 {
85  struct xmlstate *state = data;
86  char *tmp;
87 
88  if ((tmp = strchr(name, ':'))) {
89  tmp++;
90  } else {
91  return IKS_HOOK;
92  }
93 
94  ast_copy_string(state->tag, tmp, sizeof(state->tag));
95 
96  switch (type) {
97  case IKS_OPEN:
98  if (!strcasecmp(state->tag, "response")) {
99  struct ast_calendar_event *event;
100 
101  state->in_response = 1;
102  if (!(event = ast_calendar_event_alloc(state->pvt->owner))) {
103  return IKS_NOMEM;
104  }
105  state->ptr = event;
106  } else if (!strcasecmp(state->tag, "propstat")) {
107  state->in_propstat = 1;
108  } else if (!strcasecmp(state->tag, "prop")) {
109  state->in_prop = 1;
110  }
111  break;
112 
113  case IKS_CLOSE:
114  if (!strcasecmp(state->tag, "response")) {
115  struct ao2_container *events = state->pvt->events;
116  struct ast_calendar_event *event = state->ptr;
117 
118  state->in_response = 0;
119  if (ast_strlen_zero(event->uid)) {
120  ast_log(LOG_ERROR, "This event has no UID, something has gone wrong\n");
121  event = ast_calendar_unref_event(event);
122  return IKS_HOOK;
123  }
124  ao2_link(events, event);
125  event = ast_calendar_unref_event(event);
126  } else if (!strcasecmp(state->tag, "propstat")) {
127  state->in_propstat = 0;
128  } else if (!strcasecmp(state->tag, "prop")) {
129  state->in_prop = 0;
130  }
131  break;
132 
133  default:
134  return IKS_OK;
135  }
136 
137  return IKS_OK;
138 }
static const char type[]
Definition: chan_ooh323.c:109
static int tmp()
Definition: bt_open.c:389
Definition: astman.c:222
static const struct adsi_event events[]
Definition: app_adsiprog.c:85
struct caldav_pvt * pvt
const ast_string_field uid
Definition: calendar.h:101
#define ast_strlen_zero(foo)
Definition: strings.h:52
#define ast_log
Definition: astobj2.c:42
struct ast_calendar_event * ast_calendar_event_alloc(struct ast_calendar *cal)
Allocate an astobj2 ast_calendar_event object.
Definition: res_calendar.c:667
#define LOG_ERROR
Definition: logger.h:285
struct ast_calendar * owner
static const char name[]
Definition: cdr_mysql.c:74
struct ast_calendar_event * ast_calendar_unref_event(struct ast_calendar_event *event)
Unreference an ast_calendar_event.
Definition: res_calendar.c:321
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
Generic container type.
struct ao2_container * events
#define ao2_link(container, obj)
Definition: astobj2.h:1549

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 736 of file res_calendar_exchange.c.

References ast_calendar_unregister(), AST_MODFLAG_LOAD_ORDER, AST_MODPRI_DEVSTATE_PLUGIN, AST_MODULE_INFO(), AST_MODULE_SUPPORT_EXTENDED, ASTERISK_GPL_KEY, and load_module().

737 {
739  ne_sock_exit();
740  return 0;
741 }
static struct ast_calendar_tech exchangecal_tech
void ast_calendar_unregister(struct ast_calendar_tech *tech)
Unregister a new calendar technology.
Definition: res_calendar.c:587

◆ unref_exchangecal()

static void * unref_exchangecal ( void *  obj)
static

Definition at line 235 of file res_calendar_exchange.c.

References ao2_ref, and NULL.

Referenced by exchangecal_load_calendar().

236 {
237  struct exchangecal_pvt *pvt = obj;
238 
239  ao2_ref(pvt, -1);
240  return NULL;
241 }
#define NULL
Definition: resample.c:96
#define ao2_ref(o, delta)
Definition: astobj2.h:464

◆ update_exchangecal()

static int update_exchangecal ( struct exchangecal_pvt pvt)
static

Definition at line 577 of file res_calendar_exchange.c.

References ast_calendar_merge_events(), ast_free, ast_str_buffer(), ast_str_strlen(), ast_tvnow(), end, exchangecal_pvt::events, exchangecal_get_events_between(), exchangecal_pvt::owner, parse_cdata(), parse_tag(), xmlstate::pvt, and ast_calendar::timeframe.

Referenced by exchangecal_load_calendar().

578 {
579  struct xmlstate state;
580  struct timeval now = ast_tvnow();
581  time_t start, end;
582  struct ast_str *response;
583  iksparser *p;
584 
585  state.pvt = pvt;
586  start = now.tv_sec;
587  end = now.tv_sec + 60 * pvt->owner->timeframe;
588  if (!(response = exchangecal_get_events_between(pvt, start, end))) {
589  return -1;
590  }
591 
592  p = iks_sax_new(&state, parse_tag, parse_cdata);
593  iks_parse(p, ast_str_buffer(response), ast_str_strlen(response), 1);
595  ast_free(response);
596 
597  return 0;
598 }
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
struct ast_calendar * owner
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:150
int timeframe
Definition: calendar.h:133
char * end
Definition: eagi_proxy.c:73
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.
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
#define ast_free(a)
Definition: astmm.h:182
static int parse_tag(void *data, char *name, char **atts, int type)
static struct ast_str * exchangecal_get_events_between(struct exchangecal_pvt *pvt, time_t start_time, time_t end_time)
struct ao2_container * events
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:688
static int parse_cdata(void *data, char *value, size_t len)

◆ xml_encode_str()

static struct ast_str* xml_encode_str ( struct ast_str dst,
const char *  src 
)
static

Definition at line 265 of file res_calendar_exchange.c.

References ast_str_append(), buf, and tmp().

Referenced by exchangecal_write_event().

266 {
267  const char *tmp;
268  char buf[7];
269 
270  for (tmp = src; *tmp; tmp++) {
271  switch (*tmp) {
272  case '\"':
273  strcpy(buf, "&quot;");
274  break;
275 
276  case '\'':
277  strcpy(buf, "&apos;");
278  break;
279 
280  case '&':
281  strcpy(buf, "&amp;");
282  break;
283 
284  case '<':
285  strcpy(buf, "&lt;");
286  break;
287 
288  case '>':
289  strcpy(buf, "&gt;");
290  break;
291 
292  default:
293  sprintf(buf, "%c", *tmp);
294  }
295 
296  ast_str_append(&dst, 0, "%s", buf);
297  }
298 
299  return dst;
300 }
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
static int tmp()
Definition: bt_open.c:389
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1091

Variable Documentation

◆ __mod_info

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Asterisk MS Exchange Calendar Integration" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "30ef0c93b36035ec78c9cfd712d36d9b" , .support_level = AST_MODULE_SUPPORT_EXTENDED, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEVSTATE_PLUGIN, .requires = "res_calendar", }
static

Definition at line 749 of file res_calendar_exchange.c.

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 749 of file res_calendar_exchange.c.

◆ exchangecal_tech

struct ast_calendar_tech exchangecal_tech
static

Definition at line 53 of file res_calendar_exchange.c.