From: Roman Romanchenko <rromanchenko@sipwise.com>
Date: Thu, 26 Mar 2020 10:06:46 +0100
Subject: TT#41650 Development of Kamailio module/function for parsing iCal
 structure

 - parsing ical from buffer and converting to tmrec using libical implementation.

Change-Id: Ifd823a576ed4eb1d95aa3c72f34226b6100c4c21
---
 src/modules/tmrec/Makefile    |  2 +-
 src/modules/tmrec/ical.c      | 45 ++++++++++++++++++++++++++++++++
 src/modules/tmrec/ical.h      | 17 ++++++++++++
 src/modules/tmrec/tmrec_mod.c | 60 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 123 insertions(+), 1 deletion(-)
 create mode 100644 src/modules/tmrec/ical.c
 create mode 100644 src/modules/tmrec/ical.h

diff --git a/src/modules/tmrec/Makefile b/src/modules/tmrec/Makefile
index 46de362..518136b 100644
--- a/src/modules/tmrec/Makefile
+++ b/src/modules/tmrec/Makefile
@@ -5,6 +5,6 @@ include ../../Makefile.defs
 auto_gen=
 NAME=tmrec.so
 DEFS +=
-LIBS +=
+LIBS += -L$(LOCALBASE)/lib -lical -licalss
 
 include ../../Makefile.modules
diff --git a/src/modules/tmrec/ical.c b/src/modules/tmrec/ical.c
new file mode 100644
index 0000000..91d9b60
--- /dev/null
+++ b/src/modules/tmrec/ical.c
@@ -0,0 +1,45 @@
+#include "ical.h"
+#include <libical/ical.h>
+#include <libical/icalss.h>
+#include <stdlib.h>
+
+#define PERIOD 24*3600*7 // one week
+
+void check_time_frame(icalcomponent *comp, struct icaltime_span *span, void *data)
+{
+      callback_data_t *d = (callback_data_t *)data;
+      if (span->start <= d->time && d->time <= span->end)
+      {
+          *(d->rc) = 1;
+      }
+}
+
+int match_ical_event(icalcomponent *c, time_t time)
+{
+      int rc = -1;
+
+      for(icalcomponent *event = icalcomponent_get_first_component(c, ICAL_VEVENT_COMPONENT); event != 0; event = icalcomponent_get_next_component(c, ICAL_VEVENT_COMPONENT))
+      {
+            struct icaltimetype start;
+            struct icaltimetype end;
+
+            icaltimezone *utc = icaltimezone_get_utc_timezone();
+            start = icaltime_from_timet_with_zone(time, 0, utc);
+            end = icaltime_from_timet_with_zone(time + PERIOD, 0, utc);
+            icaltime_normalize(end);
+
+            callback_data_t data = {&rc, time};
+            icalcomponent_foreach_recurrence(event, start, end, check_time_frame, &data);
+      }
+
+      icalcomponent_free(c);
+
+      return rc;
+}
+
+icalcomponent *create_from_buf(const char *buf)
+{
+      icalcomponent *c = icalparser_parse_string(buf);
+      return c;
+}
+
diff --git a/src/modules/tmrec/ical.h b/src/modules/tmrec/ical.h
new file mode 100644
index 0000000..5e44ba1
--- /dev/null
+++ b/src/modules/tmrec/ical.h
@@ -0,0 +1,17 @@
+#ifndef _ICAL_H_
+#define _ICAL_H_
+
+#include <libical/ical.h>
+#include <time.h>
+
+void check_time_frame(icalcomponent *comp, struct icaltime_span *span, void *data);
+int match_ical_event(icalcomponent *c, time_t t);
+icalcomponent *create_from_buf(const char *buf);
+
+typedef struct callback_data
+{
+    int *rc;
+    time_t time;
+} callback_data_t;
+
+#endif
diff --git a/src/modules/tmrec/tmrec_mod.c b/src/modules/tmrec/tmrec_mod.c
index cc97cc9..e574af1 100644
--- a/src/modules/tmrec/tmrec_mod.c
+++ b/src/modules/tmrec/tmrec_mod.c
@@ -41,6 +41,7 @@
 #include "../../core/kemi.h"
 #include "../../core/utils/tmrec.h"
 #include "period.h"
+#include "ical.h"
 
 
 MODULE_VERSION
@@ -50,7 +51,9 @@ static int  child_init(int);
 static void mod_destroy(void);
 
 static int w_tmrec_match(struct sip_msg* msg, char* rec, char* t);
+static int w_tmrec_match_ical(struct sip_msg* msg, char* rec, char* t);
 static int fixup_tmrec_match(void** param, int param_no);
+static int fixup_ical(void** param, int param_no);
 static int w_is_leap_year(struct sip_msg* msg, char* t, char* p2);
 static int fixup_is_leap_year(void** param, int param_no);
 static int fixup_time_period_match(void** param, int param_no);
@@ -65,6 +68,10 @@ static cmd_export_t cmds[]={
 		0, ANY_ROUTE},
 	{"tmrec_match", (cmd_function)w_tmrec_match, 2, fixup_tmrec_match,
 		0, ANY_ROUTE},
+	{"tmrec_match_ical", (cmd_function)w_tmrec_match_ical, 1, fixup_ical,
+		0, ANY_ROUTE},
+	{"tmrec_match_ical", (cmd_function)w_tmrec_match_ical, 2, fixup_ical,
+		0, ANY_ROUTE},
 	{"is_leap_year", (cmd_function)w_is_leap_year, 0, fixup_is_leap_year,
 		0, ANY_ROUTE},
 	{"is_leap_year", (cmd_function)w_is_leap_year, 1, fixup_is_leap_year,
@@ -254,6 +261,45 @@ static int w_tmrec_match(struct sip_msg* msg, char* rec, char* t)
 	return ki_tmrec_match_timestamp(msg, &rv, ti);
 }
 
+static int w_tmrec_match_ical(struct sip_msg* msg, char* ical_rec, char* t)
+{
+	str rv;
+	time_t tv;
+	int ti;
+
+	if(msg==NULL)
+		return -2;
+
+	if(fixup_get_svalue(msg, (gparam_t*)ical_rec, &rv)!=0)
+	{
+		LM_ERR("w_tmrec_match_ical: invalid input");
+		return -1;
+	}
+
+	if(t!=NULL)
+	{
+		if(fixup_get_ivalue(msg, (gparam_t*)t, &ti)!=0)
+		{
+			LM_ERR("invalid time stamp parameter value\n");
+			return -4;
+		}
+		tv = (time_t)ti;
+	} else {
+		tv = time(NULL);
+	}
+
+	char time_string[20];
+	strftime(time_string, 20, "%Y-%m-%d %H:%M:%S", localtime(&tv));
+
+	icalcomponent *event = create_from_buf(rv.s);
+	LM_DBG("w_tmrec_match_ical: checking ical: %s, %s\n", time_string, rv.s);
+
+	int rc = match_ical_event(event, tv);
+
+	LM_DBG("match_ical_event return %d\n", rc);
+	return rc;
+}
+
 static int fixup_tmrec_match(void** param, int param_no)
 {
 	if(param_no==1)
@@ -268,6 +314,20 @@ static int fixup_tmrec_match(void** param, int param_no)
 	return 0;
 }
 
+static int fixup_ical(void** param, int param_no)
+{
+	if(param_no==1)
+	{
+		if(fixup_spve_null(param, 1)<0)
+			return -1;
+		return 0;
+	} else if(param_no==2) {
+		if(fixup_igp_null(param, 1)<0)
+			return -1;
+	}
+	return 0;
+}
+
 static int fixup_time_period_match(void** param, int param_no)
 {
 	if(param_no==1)
