aboutsummaryrefslogtreecommitdiffstats
path: root/libical/src/libicalcap
diff options
context:
space:
mode:
Diffstat (limited to 'libical/src/libicalcap')
-rw-r--r--libical/src/libicalcap/Makefile.am77
-rw-r--r--libical/src/libicalcap/client.c187
-rw-r--r--libical/src/libicalcap/icalcap.c44
-rw-r--r--libical/src/libicalcap/icalcap.h47
-rw-r--r--libical/src/libicalcap/icalcap_impl.h34
-rw-r--r--libical/src/libicalcap/icalcap_message.c225
-rw-r--r--libical/src/libicalcap/icalcap_message_impl.h33
-rw-r--r--libical/src/libicalcap/icalcap_rr.c545
-rw-r--r--libical/src/libicalcap/icalcap_server.c52
-rw-r--r--libical/src/libicalcap/icalcap_server.h19
-rw-r--r--libical/src/libicalcap/icalcap_server_impl.h31
-rw-r--r--libical/src/libicalcap/icalcap_session.c62
-rw-r--r--libical/src/libicalcap/icalcap_session.h15
-rw-r--r--libical/src/libicalcap/icalcap_session_impl.h36
-rw-r--r--libical/src/libicalcap/icalcap_utils.c134
15 files changed, 1541 insertions, 0 deletions
diff --git a/libical/src/libicalcap/Makefile.am b/libical/src/libicalcap/Makefile.am
new file mode 100644
index 0000000000..b03a6717e4
--- /dev/null
+++ b/libical/src/libicalcap/Makefile.am
@@ -0,0 +1,77 @@
+#======================================================================
+# FILE: Makefile.am
+# CREATOR: acampi
+#
+# $Id: Makefile.am,v 1.1 2003/09/11 22:04:28 hansp Exp $
+#
+#
+# (C) COPYRIGHT 2003, Andrea Campi, mailto:a.campi@inet.it
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either:
+#
+# The LGPL as published by the Free Software Foundation, version
+# 2.1, available at: http://www.fsf.org/copyleft/lesser.html
+#
+# Or:
+#
+# The Mozilla Public License Version 1.0. You may obtain a copy of
+# the License at http://www.mozilla.org/MPL/
+#
+#
+#
+#======================================================================
+
+
+lib_LTLIBRARIES = libicalcap.la
+
+noinst_LTLIBRARIES = libicalcap-static.la
+libicalcap_static_la_SOURCES = $(libicalcap_la_SOURCES)
+libicalcap_static_la_LDFLAGS = --all-static
+
+INCLUDES = \
+ -I$(top_builddir) \
+ -I$(top_srcdir)/src \
+ -I$(top_builddir)/src \
+ -I$(top_srcdir)/src/libical \
+ -I$(top_builddir)/libical \
+ -I$(srcdir) \
+ -DWITH_RR $(RRCAP_CFLAGS)
+
+libicalcap_la_LDFLAGS = $(RRCAP_LIBS) -version-info 0:0:0
+
+
+libicalcap_la_SOURCES = \
+ icalcap.c \
+ icalcap.h \
+ icalcap_impl.h \
+ icalcap_message.c \
+ icalcap_message_impl.h \
+ icalcap_rr.c \
+ icalcap_server.c \
+ icalcap_server.h \
+ icalcap_server_impl.h \
+ icalcap_session.c \
+ icalcap_session.h \
+ icalcap_session_impl.h \
+ icalcap_utils.c
+
+libicalcapincludedir = $(includedir)
+
+libicalcapinclude_HEADERS = \
+ icalcap.h \
+ icalcap_impl.h \
+ icalcap_message_impl.h \
+ icalcap_server.h \
+ icalcap_server_impl.h \
+ icalcap_session.h \
+ icalcap_session_impl.h
+
+
+noinst_PROGRAMS = client
+
+LDADD = ../libicalss/.libs/libicalss.a ../libicalvcal/.libs/libicalvcal.a $(cxx_libs) ../libical/.libs/libical.a libicalcap.la $(RRCAP_LIBS)
+
+
+client_SOURCES = client.c
+
diff --git a/libical/src/libicalcap/client.c b/libical/src/libicalcap/client.c
new file mode 100644
index 0000000000..399568ac79
--- /dev/null
+++ b/libical/src/libicalcap/client.c
@@ -0,0 +1,187 @@
+/*-
+ * $Id$
+ *
+ * See the file LICENSE for redistribution information.
+ * If you have not received a copy of the license, please contact CodeFactory
+ * by email at info@codefactory.se, or on the web at http://www.codefactory.se/
+ * You may also write to: CodeFactory AB, SE-903 47, Umeå, Sweden.
+ *
+ * Copyright (c) 2002 Jonas Borgström <jonas@codefactory.se>
+ * Copyright (c) 2002 Daniel Lundin <daniel@codefactory.se>
+ * Copyright (c) 2002 CodeFactory AB. All rights reserved.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <librr/rr.h>
+
+#include "icalcap.h"
+#include "icalcap_session.h"
+
+#define CLIENT_CAPABILITY \
+"Content-Type: text/html\n\n\
+BEGIN:VCALENDAR\n\
+VERSION:2.0\n\
+PRODIR:rrcap-client\n\
+CMD:REPLY\n\
+BEGIN:VREPLY\n\
+CAP-VERSION:1.0\n\
+PRODID:rrcap-client\n\
+QUERY-LEVEL:CAL-QL-NONE\n\
+CAR-LEVEL:CAR-FULL-NONE\n\
+DATE-MAX:99991231T235959Z\n\
+DATE-MIN:00000101T000000Z\n\
+MAX-COMPONENT-SIZE:0\n\
+COMPONENTS:VCALENDAR,VTODO,VJOURNAL,VEVENT,VCAR,\n\
+ VALARM,VFREEBUSY,VTIMEZONE,STANDARD,DAYLIGHT,VREPLY\n\
+ITIP-VERSION:2447\n\
+RECUR-ACCEPTED:TRUE\n\
+RECUR-EXPAND:TRUE\n\
+RECUR-LIMIT:0\n\
+STORES-EXPANDED:FALSE\n\
+END:VREPLY\n\
+END:VCALENDAR\n"
+
+
+icalcomponent *
+icalcap_send_cmd(const icalcap *cap, const gchar *cmd, const gchar *id, int timeout) {
+
+ icalcap_message*capmsg;
+ icalcomponent *comp, *ret = NULL;
+ icalproperty *prop;
+
+ prop = icalproperty_vanew_cmd(
+ icalproperty_string_to_enum(cmd),
+ icalparameter_new_id(id),
+ 0);
+
+ if (timeout > 0) {
+ char buf[16];
+
+ snprintf(buf, 16, "%d", timeout);
+ icalproperty_add_parameter(prop,
+ icalparameter_new_latency(buf));
+
+ icalproperty_add_parameter(prop,
+ icalparameter_new_actionparam(ICAL_ACTIONPARAM_ABORT));
+ }
+
+ comp = icalcomponent_vanew(
+ ICAL_VCALENDAR_COMPONENT,
+ icalproperty_new_version("2.0"),
+ icalproperty_new_prodid("-//I.Net spa//NONSGML//EN"),
+ prop,
+ 0);
+
+ capmsg = icalcap_message_new(cap, comp);
+ ret = icalcap_message_sync_send(capmsg, timeout);
+ icalcap_message_free(capmsg);
+ icalcomponent_free(comp);
+
+ return ret;
+}
+
+icalcomponent *
+icalcap_new_reply_component(const char *id, const icalcomponent *comp) {
+
+ if (comp == NULL)
+ return NULL;
+
+ return icalcomponent_vanew(
+ ICAL_VCALENDAR_COMPONENT,
+ icalproperty_new_version("2.0"),
+ icalproperty_new_prodid("-//I.Net spa//NONSGML//EN"),
+ icalproperty_vanew_cmd(
+ ICAL_CMD_REPLY,
+/* icalparameter_new_id(id), */
+ 0),
+ comp,
+ 0);
+}
+
+
+static int
+msg_handler(const icalcap_message *capmsg) {
+
+ icalcomponent *reply;
+
+ g_message("Got: %s", icalcomponent_as_ical_string(capmsg->comp));
+
+ /* FIXME Check it's a GET-CAPABILITY */
+
+ reply = icalcap_new_reply_component(NULL, capmsg->comp);
+ if (reply == NULL) {
+ return FALSE;
+ }
+
+ icalcomponent_add_property(reply,
+ icalproperty_new_prodid("client"));
+
+ icalcomponent_free(reply);
+ return TRUE;
+}
+
+int
+main (gint argc, gchar **argv)
+{
+ icalcap_session*conn;
+ icalcap *cap;
+ icalcomponent *comp;
+
+ int i, n;
+ int verbose = 0;
+
+ if ((conn = icalcap_session_new()) == NULL) {
+ fprintf(stderr, "Init failed\n");
+ exit(0);
+ }
+
+ if (!icalcap_session_connect(conn, "gundam.inet.it", 0)) {
+ fprintf(stderr, "Connect failed\n");
+ exit(0);
+ }
+
+ if (!icalcap_session_login(conn, "user@example.com", "user@example.com", "password")) {
+ fprintf(stderr, "Login failed\n");
+ exit(0);
+ }
+
+ if ((cap = icalcap_session_start(conn, msg_handler)) == NULL) {
+ fprintf(stderr, "Start failed\n");
+ exit(0);
+ }
+
+ if (argc > 1 && *argv[1] == '1')
+ n = 100;
+ else
+ n = 1;
+
+ for (i=0; i<n; i++) {
+ g_print("Test 1: %d\n", i);
+
+ if ((comp = icalcap_send_cmd(cap, "GET-CAPABILITY", "zero",15))
+ == NULL) {
+ fprintf(stderr, "Send failed\n");
+ exit(0);
+ }
+
+ if (verbose)
+ g_print("Got %s\n", icalcomponent_as_ical_string(comp));
+
+ if (comp)
+ icalcomponent_free(comp);
+ }
+
+ if (!icalcap_stop(cap)) {
+ fprintf(stderr, "Stop failed\n");
+ exit(0);
+ }
+
+ if (!icalcap_session_disconnect(conn)) {
+ fprintf(stderr, "Disconnect failed\n");
+ exit(0);
+ }
+
+ return 0;
+}
diff --git a/libical/src/libicalcap/icalcap.c b/libical/src/libicalcap/icalcap.c
new file mode 100644
index 0000000000..8d404f0ad4
--- /dev/null
+++ b/libical/src/libicalcap/icalcap.c
@@ -0,0 +1,44 @@
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "icalcap.h"
+#include "icalcap_impl.h"
+#include "icalcap_session_impl.h" /* FIXME */
+
+
+void
+icalcap_free(icalcap *cap) {
+
+#ifdef WITH_RR
+ icalcap_free_rr(cap);
+#endif
+}
+
+int
+icalcap_stop(icalcap *cap) {
+
+#ifdef WITH_RR
+ return icalcap_stop_rr(cap);
+#else
+ return 0;
+#endif
+}
+
+const char *
+icalcap_get_username(const icalcap *cap) {
+
+#ifdef WITH_RR
+ if (cap == NULL || cap->username == NULL)
+ return NULL;
+
+ return cap->username;
+#else
+ return NULL;
+#endif
+}
diff --git a/libical/src/libicalcap/icalcap.h b/libical/src/libicalcap/icalcap.h
new file mode 100644
index 0000000000..5e61bf9c8e
--- /dev/null
+++ b/libical/src/libicalcap/icalcap.h
@@ -0,0 +1,47 @@
+#ifndef __ICALCAP_H__
+#define __ICALCAP_H__
+
+#include <ical.h>
+
+/*
+ * Opaque objects
+ */
+typedef struct _icalcap icalcap;
+typedef struct _icalerror icalerror;
+typedef struct _icalcap_message icalcap_message;
+
+/*
+ * Callback
+ */
+typedef int (*icalcap_msg_handler)(const icalcap_message *msg);
+
+/*
+ * icalcap member functions
+ */
+void icalcap_free(icalcap *cap);
+int icalcap_stop(icalcap *cap);
+const char *icalcap_get_username(const icalcap *cap);
+
+/*
+ * icalcap_message member functions
+ */
+
+struct _icalcap_message {
+ icalcap *cap;
+ int type;
+
+ icalcomponent *comp;
+};
+
+icalcap_message*icalcap_message_new(const icalcap *cap, const icalcomponent *comp);
+icalcap_message*icalcap_message_new_reply(const icalcap_message *capmsg, const icalcomponent *comp);
+void icalcap_message_free(icalcap_message *capmsg);
+
+int icalcap_message_reply_error(const icalcap_message *orig, enum icalrequeststatus status,
+ const char *msg, const char *debug);
+int icalcap_message_reply_component(const icalcap_message *orig, icalcomponent *comp);
+
+int icalcap_message_send(icalcap_message *capmsg);
+icalcomponent *icalcap_message_sync_send(icalcap_message *capmsg, int timeout);
+
+#endif
diff --git a/libical/src/libicalcap/icalcap_impl.h b/libical/src/libicalcap/icalcap_impl.h
new file mode 100644
index 0000000000..19f8c1db06
--- /dev/null
+++ b/libical/src/libicalcap/icalcap_impl.h
@@ -0,0 +1,34 @@
+#ifndef __ICALCAP_IMPL_H__
+#define __ICALCAP_IMPL_H__
+
+#include "icalcap_session.h"
+
+#ifdef WITH_RR
+
+#include <librr/rr.h>
+#include <librrsasl/rr-sasl.h>
+#include <librrtls/rr-tls.h>
+#include <librrcap/rr-cap.h>
+
+struct _icalcap {
+ RRCAP *chan;
+ const char *username,
+ *authname;
+};
+
+struct _icalerror {
+ GError *err;
+};
+
+
+void icalcap_free_rr(icalcap *cap);
+
+int icalcap_stop_rr(icalcap *cap);
+
+icalcomponent *icalcap_send_component_rr(const icalcap *cap, const icalcomponent *comp, int timeout);
+
+#else
+#error "No implementation of icalcap found!"
+#endif
+
+#endif
diff --git a/libical/src/libicalcap/icalcap_message.c b/libical/src/libicalcap/icalcap_message.c
new file mode 100644
index 0000000000..32abbf0a73
--- /dev/null
+++ b/libical/src/libicalcap/icalcap_message.c
@@ -0,0 +1,225 @@
+#include "config.h"
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "ical.h"
+#include "icalcap.h"
+#include "icalcap_message_impl.h"
+
+
+/**
+ * constructor
+ *
+ * Create a new icalcap_message from a component. This represents a command.
+ */
+icalcap_message *
+icalcap_message_new(const icalcap *cap, const icalcomponent *comp) {
+
+ icalcap_message *ret = NULL;
+
+#ifdef WITH_RR
+ ret = icalcap_message_new_rr(cap, comp);
+#else
+ ret = NULL;
+#endif
+ return ret;
+}
+
+/**
+ * constructor
+ *
+ * Create a new icalcap_message from a component. This represents a reply.
+ */
+icalcap_message *
+icalcap_message_new_reply(const icalcap_message *capmsg, const icalcomponent *comp) {
+
+#ifdef WITH_RR
+ return icalcap_message_new_reply_rr(capmsg, comp);
+#else
+ return NULL;
+#endif
+}
+
+/**
+ * destructor
+ *
+ * Delete icalcap_message
+ */
+void
+icalcap_message_free(icalcap_message *capmsg) {
+
+#ifdef WITH_RR
+ icalcap_message_free_rr(capmsg);
+#else
+#endif
+}
+
+/**
+ * Send the icalcap_message and wait for an answer.
+ * The icalcap_message will be invalid after this call, and only valid call will be
+ * icalcap_message_free().
+ */
+icalcomponent *
+icalcap_message_sync_send(icalcap_message *capmsg, int timeout) {
+
+ g_return_val_if_fail(capmsg, NULL);
+
+#ifdef WITH_RR
+ return icalcap_message_sync_send_rr(capmsg, timeout);
+#else
+ return NULL;
+#endif
+}
+
+/**
+ * Send the icalcap_message.
+ * The icalcap_message will be invalid after this call, and only valid call will be
+ * icalcap_message_free().
+ */
+int
+icalcap_message_send(icalcap_message *capmsg) {
+
+ int rc = FALSE;
+ char *str;
+
+ g_return_val_if_fail(capmsg, FALSE);
+
+#ifdef WITH_RR
+ rc = icalcap_message_send_reply_rr(capmsg);
+#else
+ rc = FALSE;
+#endif
+
+ return rc;
+}
+
+/**
+ * Convenience method to send a component in reply to the given icalcap_message.
+ * The icalcomponent is not modified in any way.
+ *
+ * FIXME should be const icalcomponent *
+ */
+int
+icalcap_message_reply_component(const icalcap_message *orig, icalcomponent *in) {
+
+ icalcap_message*reply;
+ int rc;
+
+ reply = icalcap_message_new_reply(orig, in);
+ if (reply == NULL)
+ return FALSE;
+
+ rc = icalcap_message_send(reply);
+ icalcap_message_free(reply);
+
+ return rc;
+}
+
+/* Only used by icalcap_message_reply_error */
+static icalcomponent *new_reply_component(const icalcap_message *capmsg, const icalcomponent *comp);
+
+/**
+ * Convenience method to send an error in reply to the given icalcap_message.
+ */
+int
+icalcap_message_reply_error(const icalcap_message *orig, enum icalrequeststatus status,
+ const char *msg, const char *debug) {
+
+ struct icalreqstattype stat;
+ icalcomponent *comp, *vreply;
+ int rc;
+
+ /* Prepare the REQUEST-STATUS */
+ stat = icalreqstattype_from_string(
+ icalenum_reqstat_code(status));
+
+ if (msg != NULL) {
+ /* FIXME we used to do
+ stat.desc = strdup(msg);
+ but this created a memory leak. Maybe the destructor for reqstat? */
+ stat.desc = msg;
+ } else {
+ stat.desc = icalenum_reqstat_desc(status);
+ }
+
+ if (debug != NULL)
+ stat.debug = debug;
+
+ /* Prepare a VREPLY component */
+ vreply = icalcomponent_vanew(
+ ICAL_VREPLY_COMPONENT,
+ icalproperty_new_requeststatus(stat),
+ 0);
+ if (vreply == NULL) {
+ /* FIXME */
+ return FALSE;
+ }
+
+ comp = new_reply_component(orig, vreply);
+
+ rc = icalcap_message_reply_component(orig, comp);
+
+ icalcomponent_free(comp);
+ icalcomponent_free(vreply);
+
+ return rc;
+}
+
+
+/* internal use */
+
+/* only used by new_reply_component */
+static const char *
+get_id(icalcomponent *comp) {
+
+ icalproperty *prop;
+ icalparameter *param;
+
+ prop = icalcomponent_get_first_property(comp, ICAL_CMD_PROPERTY);
+ if (prop == NULL)
+ return NULL;
+
+ param = icalproperty_get_first_parameter(prop, ICAL_ID_PARAMETER);
+ if (param == NULL)
+ return NULL;
+
+ return icalparameter_get_id(param);
+}
+
+/* only used by icalcap_message_reply_error */
+static icalcomponent *
+new_reply_component(const icalcap_message *capmsg, const icalcomponent *comp) {
+
+ icalcomponent *clone, *cal, *root;
+ icalproperty *cmd;
+ const char *id;
+
+ cmd = icalproperty_new_cmd(ICAL_CMD_REPLY);
+
+ if (capmsg->comp != NULL) {
+ id = get_id(capmsg->comp);
+
+ if (id != NULL)
+ icalproperty_add_parameter(cmd, icalparameter_new_id(id));
+ }
+
+ cal = icalcomponent_vanew(
+ ICAL_VCALENDAR_COMPONENT,
+ icalproperty_new_version("2.0"),
+ icalproperty_new_prodid("-//I.Net spa//NONSGML//EN"),
+ cmd,
+ 0);
+
+ if (comp != NULL) {
+ clone = icalcomponent_new_clone(comp);
+ icalcomponent_add_component(cal, clone);
+ }
+
+ root = icalcomponent_vanew(
+ ICAL_XROOT_COMPONENT,
+ cal,
+ 0);
+ return root;
+}
diff --git a/libical/src/libicalcap/icalcap_message_impl.h b/libical/src/libicalcap/icalcap_message_impl.h
new file mode 100644
index 0000000000..ae234d2efa
--- /dev/null
+++ b/libical/src/libicalcap/icalcap_message_impl.h
@@ -0,0 +1,33 @@
+#ifndef __ICALCAP_MESSAGE_IMPL_H__
+#define __ICALCAP_MESSAGE_IMPL_H__
+
+#ifdef WITH_RR
+
+#include <ical.h>
+#include <librr/rr.h>
+
+#define ICALCAP_MESSAGE_CMD 1
+#define ICALCAP_MESSAGE_REPLY 1
+
+struct _icalcap_message_rr {
+ const icalcap *cap;
+ int type;
+
+ icalcomponent *comp;
+
+ RRFrame *frame;
+ RRMessage *msg;
+};
+
+icalcap_message*icalcap_message_new_rr(const icalcap *cap, const icalcomponent *comp);
+icalcap_message*icalcap_message_new_reply_rr(const icalcap_message *capmsg, const icalcomponent *comp);
+void icalcap_message_free_rr(icalcap_message *capmsg);
+
+int icalcap_message_send_reply_rr(icalcap_message *capmsg);
+icalcomponent *icalcap_message_sync_send_rr(icalcap_message *capmsg, int timeout);
+
+#else
+#error "No implementation of icalcap found!"
+#endif
+
+#endif
diff --git a/libical/src/libicalcap/icalcap_rr.c b/libical/src/libicalcap/icalcap_rr.c
new file mode 100644
index 0000000000..7e464ee155
--- /dev/null
+++ b/libical/src/libicalcap/icalcap_rr.c
@@ -0,0 +1,545 @@
+#include "config.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "icalcap.h"
+#include "icalcap_session.h"
+#include "icalcap_server.h"
+
+#include "icalcap_impl.h"
+#include "icalcap_message_impl.h"
+#include "icalcap_session_impl.h"
+#include "icalcap_server_impl.h"
+
+icalcomponent *icalcap_component_new_from_string(const char *data);
+
+static int default_msg_handler(RRCAP *cap, RRFrame *frame, GError **error);
+static void client_final_handler(RRCAP *cap);
+
+/**
+ * Implementation of functions in icalcap
+ */
+void
+icalcap_free_rr(icalcap *cap) {
+
+ g_return_if_fail(cap);
+
+ if (cap->username)
+ free(cap->username);
+
+ if (cap->authname)
+ free(cap->authname);
+
+ g_free(cap);
+}
+
+/**
+ * Implementation of functions in icalcap_session
+ */
+icalcap_session *
+icalcap_session_new_rr(void) {
+
+ icalcap_session *sess;
+ GError *error = NULL;
+
+ /* Initialize roadrunner */
+ if (!rr_init(0, NULL, &error))
+ return 0;
+
+ if ((sess = g_new0(icalcap_session, 1)) == NULL) {
+ /* FIXME return an error */
+ return NULL;
+ }
+
+ sess->cfg = rr_cap_config_new();
+
+ /* Tell roadrunner which profiles we want to support */
+ sess->profreg = rr_profile_registry_new();
+ rr_profile_registry_add_profile(sess->profreg, RR_TYPE_CAP, NULL);
+ rr_profile_registry_add_profile(sess->profreg, RR_TYPE_TLS, NULL);
+ rr_profile_registry_add_profile(sess->profreg, RR_TYPE_SASL_DIGEST_MD5, NULL);
+
+ return sess;
+}
+
+int
+icalcap_session_connect_rr(icalcap_session *sess, const char *hostname, const int port) {
+
+ GError *error = NULL;
+
+ if (sess == NULL) {
+ /* FIXME return the error */
+ return FALSE;
+ }
+
+ /* Create a connection object */
+ sess->connection = rr_tcp_connection_new(sess->profreg, hostname, port, &error);
+ if (sess->connection == NULL) {
+ /* FIXME return the error */
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+int
+icalcap_session_login_rr(icalcap_session *sess, const char *username, const char *authname,
+ const char *password) {
+
+ /* assert cap != NULL */
+ GError *error = NULL;
+
+ rr_sasl_set_username(sess->connection, username);
+ rr_sasl_set_authname(sess->connection, authname);
+ rr_sasl_set_password(sess->connection, password);
+
+ /* FIXME */
+ if (!rr_sasl_login(sess->connection, RR_TYPE_SASL_DIGEST_MD5,
+ "foo.example.com", NULL, &error)) {
+ /* FIXME return the error */
+ return 0;
+ }
+
+ return 1;
+}
+
+icalcap *
+icalcap_session_start_rr(const icalcap_session *sess, icalcap_msg_handler handler) {
+
+ /* assert sess != NULL */
+ icalcap *cap;
+ RRCAP *channel;
+ GError *error = NULL;
+
+ if ((cap = g_new0(icalcap, 1)) == NULL) {
+ /* FIXME return an error */
+ return NULL;
+ }
+
+ if (handler != NULL) {
+ rr_cap_config_set_msg_handler(sess->cfg, default_msg_handler, (void *)handler);
+ /* FIXME rr_cap_config_set_final_handler(cfg, client_final_handler); */
+ }
+
+ if ((channel = rr_cap_client_start(sess->connection, sess->cfg, &error)) == NULL) {
+ /* FIXME return the error */
+ goto FAILED;
+ }
+
+ cap->chan = channel;
+ channel->hl = cap;
+
+ return cap;
+
+FAILED:
+ g_free(cap);
+ return NULL;
+}
+
+int
+icalcap_stop_rr(icalcap *cap) {
+
+ /* assert cap != NULL */
+ GError *error = NULL;
+
+ if (!rr_cap_close(cap->chan, &error)) {
+ /* FIXME return the error */
+ return 0;
+ }
+
+ cap->chan = NULL;
+ g_free(cap);
+
+ return 1;
+}
+
+int
+icalcap_session_disconnect_rr(icalcap_session *sess) {
+
+ /* assert cap != NULL */
+ GError *error = NULL;
+
+ if (!rr_connection_disconnect(sess->connection, &error)) {
+ /* FIXME return the error */
+ return 0;
+ }
+
+ sess->connection = NULL;
+ g_free(sess);
+
+ if (!rr_exit(&error)) {
+ /* FIXME return the error */
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * Implementation of functions in icalcap_server
+ */
+
+/**
+ * If the user properly authenticated (via SASL), initialize the channel
+ * credentials. Otherwise, refuse to open the channel.
+ */
+static int
+server_init_handler(RRCAP *chan, const gchar *piggyback, GError **error) {
+
+ icalcap *cap;
+ RRConnection *connection;
+
+ icalcap_auth_handler func;
+ const gchar *username, *authname;
+ int rc;
+
+ g_return_val_if_fail(chan, FALSE);
+ g_return_val_if_fail(chan->cfg, FALSE);
+ g_return_val_if_fail(chan->cfg->server_init_data, FALSE);
+
+ connection = rr_channel_get_connection(RR_CHANNEL(chan));
+ if ((username = rr_sasl_get_username(connection)) == NULL)
+ return FALSE;
+ if ((authname = rr_sasl_get_authname(connection)) == NULL)
+ return FALSE;
+
+ if ((cap = g_new0(icalcap, 1)) == NULL) {
+ return FALSE;
+ }
+
+ cap->chan = chan;
+ chan->hl = cap;
+
+ cap->username = strdup(username);
+ cap->authname = strdup(authname);
+
+ func = (icalcap_auth_handler)chan->cfg->server_init_data;
+ return func(cap, piggyback);
+}
+
+static void
+server_confirmation_handler(RRCAP *chan) {
+
+ icalcap_chanup_handler func;
+
+ g_return_if_fail(chan);
+ g_return_if_fail(chan->cfg);
+ g_return_if_fail(chan->cfg->server_confirmation_data);
+
+ func = (icalcap_chanup_handler)chan->cfg->server_confirmation_data;
+
+ func(chan->hl);
+}
+
+static gboolean
+server_frame_handler(RRCAP *cap, RRFrame *frame, GError **error)
+{
+ if (frame->type == RR_FRAME_TYPE_MSG)
+ /* FIXME */
+ return default_msg_handler(cap, frame, error);
+ else
+ return FALSE;
+}
+
+static void
+server_final_handler(RRCAP *cap)
+{
+ g_return_if_fail(cap);
+ g_return_if_fail(RR_CAP(cap));
+
+ if (cap->hl != NULL) {
+ icalcap_free(cap->hl);
+ cap->hl = NULL;
+ }
+}
+
+/*
+ * FIXME Do we want to pass argc and argv in?
+ */
+icalcap_server *
+icalcap_server_new_rr(icalcap_auth_handler auth_cb, icalcap_chanup_handler chanup_cb,
+ icalcap_msg_handler msg_cb) {
+
+ icalcap_server *serv;
+ GError *error = NULL;
+
+ /* Initialize roadrunner */
+ if (!rr_init(0, NULL, &error))
+ return 0;
+
+ if ((serv = g_new0(icalcap_server, 1)) == NULL) {
+ /* FIXME return an error */
+ return NULL;
+ }
+ serv->handler = msg_cb;
+
+ /* This is somewhat hackish */
+ serv->cfg = rr_cap_config_new();
+ rr_cap_config_set_msg_handler(serv->cfg, NULL, (void *)msg_cb);
+ rr_cap_config_set_frame_handler(serv->cfg, server_frame_handler);
+ rr_cap_config_set_final_handler(serv->cfg, server_final_handler);
+ rr_cap_config_set_server_init_handler(serv->cfg, server_init_handler, (void *)auth_cb);
+ rr_cap_config_set_server_confirmation_handler(serv->cfg,
+ server_confirmation_handler, (void *)chanup_cb);
+
+ /* Tell roadrunner which profiles we want to support */
+ serv->profreg = rr_profile_registry_new();
+ rr_profile_registry_add_profile(serv->profreg, RR_TYPE_CAP, serv->cfg);
+ rr_profile_registry_add_profile(serv->profreg, RR_TYPE_TLS, NULL);
+ rr_profile_registry_add_profile(serv->profreg, RR_TYPE_SASL_DIGEST_MD5, NULL);
+
+ return serv;
+}
+
+int
+icalcap_server_listen_rr(icalcap_server *serv, const char *hostname, const int port) {
+
+ GError *error = NULL;
+ g_return_val_if_fail(serv, FALSE);
+
+ /* Create a listener object */
+ serv->listener = rr_tcp_listener_new(serv->profreg, hostname, port, &error);
+ if (serv->listener == NULL) {
+ /* FIXME return the error */
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+int
+icalcap_server_run_rr(const icalcap_server *serv) {
+
+ /* assert cap != NULL */
+ GError *error = NULL;
+
+ if (!rr_wait_until_done(&error)) {
+ /* FIXME return the error */
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+int
+icalcap_server_shutdown_rr(icalcap_server *serv) {
+
+ /* assert cap != NULL */
+ GError *error = NULL;
+
+ if (!rr_listener_shutdown(serv->listener, &error)) {
+ /* FIXME return the error */
+ return 0;
+ }
+
+ serv->listener = NULL;
+ g_free(serv);
+
+ return 1;
+}
+
+/*
+ * icalcap_message.c
+ */
+
+/*
+ * Internal constructor
+ */
+static struct _icalcap_message_rr *
+_icalcap_message_new_from_component_rr(const icalcap *cap, int type, icalcomponent *comp) {
+
+ struct _icalcap_message_rr *ret;
+
+ if ((ret = g_new0(struct _icalcap_message_rr, 1)) == NULL) {
+ /* FIXME return an error */
+ return NULL;
+ }
+
+ ret->cap = cap;
+ ret->type = type;
+
+ ret->comp = comp;
+
+ return ret;
+}
+
+static icalcap_message *
+_icalcap_message_new_from_frame_rr(const icalcap *cap, int type, RRFrame *frame) {
+
+ struct _icalcap_message_rr *ret;
+
+ if ((ret = g_new0(struct _icalcap_message_rr, 1)) == NULL) {
+ /* FIXME return an error */
+ return NULL;
+ }
+
+ ret->cap = cap;
+ ret->type = type;
+ ret->frame = frame;
+
+ ret->comp = icalcap_component_new_from_string(ret->frame->payload);
+
+ return (icalcap_message *)ret;
+}
+
+icalcap_message *
+icalcap_message_new_rr(const icalcap *cap, const icalcomponent *comp) {
+
+ struct _icalcap_message_rr *ret;
+ gchar *str;
+
+ if (comp == NULL) {
+ /* FIXME return an error */
+ return NULL;
+ }
+
+ ret = _icalcap_message_new_from_component_rr(cap, ICALCAP_MESSAGE_CMD, NULL);
+
+ str = g_strdup_printf("%s\n\n%s",
+ "Content-Type: text/calendar",
+ icalcomponent_as_ical_string(comp));
+
+ ret->msg = rr_message_static_new(RR_FRAME_TYPE_MSG, str, strlen(str), TRUE);
+
+ return (icalcap_message *)ret;
+}
+
+/*
+ * This method and its implementation are critical. It has the responsibility for
+ * serializing the component. The tricky part is that we have a options for encoding XROOT.
+ */
+icalcap_message *
+icalcap_message_new_reply_rr(const icalcap_message *orig, const icalcomponent *comp) {
+
+ struct _icalcap_message_rr *in, *ret;
+ icalcomponent *cc;
+ GString *str;
+
+ if ((in = (struct _icalcap_message_rr *)orig) == NULL) {
+ /* FIXME return an error */
+ return NULL;
+ }
+
+ ret = _icalcap_message_new_from_component_rr(in->cap, ICALCAP_MESSAGE_REPLY, NULL);
+
+ /* FIXME */
+ if (icalcomponent_isa(comp) != ICAL_XROOT_COMPONENT)
+ return NULL;
+
+ /* FIXME don't hardcode */
+ str = g_string_new("Content-Type: text/calendar\n\n");
+
+ for (cc = icalcomponent_get_first_component(comp,
+ ICAL_VCALENDAR_COMPONENT);
+ cc != NULL;
+ cc = icalcomponent_get_next_component(comp,
+ ICAL_VCALENDAR_COMPONENT)) {
+ g_string_append(str, icalcomponent_as_ical_string(cc));
+ }
+
+ ret->msg = rr_message_static_new(RR_FRAME_TYPE_RPY, str->str, strlen(str->str), TRUE);
+ /* FIXME this should now be ok but if change the API we need to change */
+ ret->msg->msgno = in->frame->msgno;
+
+ return (icalcap_message *)ret;
+}
+
+void
+icalcap_message_free_rr(icalcap_message *in) {
+
+ struct _icalcap_message_rr *capmsg = (struct _icalcap_message_rr *)in;
+ g_return_if_fail(capmsg);
+
+ if (capmsg->comp != NULL) {
+ icalcomponent_free(capmsg->comp);
+ capmsg->comp = NULL;
+ }
+
+ if (capmsg->msg != NULL) {
+ g_free(capmsg->msg);
+ }
+
+ g_free(capmsg);
+}
+
+int
+icalcap_message_send_reply_rr(icalcap_message *in) {
+
+ struct _icalcap_message_rr *capmsg = (struct _icalcap_message_rr *)in;
+ GError *error = NULL;
+ int rc;
+
+ if ((rc = rr_channel_send_message(RR_CHANNEL(capmsg->cap->chan), capmsg->msg, &error)) == 0)
+ g_message("error = %s", error->message);
+ /* FIXME handle error */
+
+ capmsg->msg = NULL;
+
+ return rc;
+}
+
+icalcomponent *
+icalcap_message_sync_send_rr(icalcap_message *in, int timeout) {
+
+ struct _icalcap_message_rr *capmsg = (struct _icalcap_message_rr *)in;
+ icalcomponent *comp, *ret;
+
+ gchar *str2;
+ GError *error = NULL;
+ int rc;
+
+ /* FIXME */
+ rc = rr_cap_cmd(capmsg->cap->chan, capmsg->msg, 3 * timeout, &str2, &error);
+ capmsg->msg = NULL;
+ if (rc == 0) {
+ g_message("error = %s", error->message);
+ /* FIXME handle error */
+ return 0;
+ }
+
+ comp = icalcap_component_new_from_string(str2);
+ g_free(str2);
+ if (ret == NULL)
+ return NULL;
+
+ return comp;
+}
+
+/*
+ *
+ */
+
+/*
+ * FIXME We assume we can safely create an icalcap_message derived object
+ * without calls to the base object
+ */
+int
+default_msg_handler(RRCAP *cap, RRFrame *frame, GError **error) {
+
+ icalcap_msg_handler func;
+ icalcap_message *msg;
+ int ret;
+
+ g_return_val_if_fail(cap, FALSE);
+ g_return_val_if_fail(cap->cfg, FALSE);
+ g_return_val_if_fail(cap->cfg->msg_handler_data, FALSE);
+
+ func = (icalcap_msg_handler)cap->cfg->msg_handler_data;
+
+ msg = _icalcap_message_new_from_frame_rr(cap->hl, ICALCAP_MESSAGE_CMD, frame);
+ if (msg == NULL) {
+ /* FIXME */
+ g_message("error");
+ return FALSE;
+ }
+
+ ret = func(msg);
+ icalcap_message_free(msg);
+
+ return ret;
+}
diff --git a/libical/src/libicalcap/icalcap_server.c b/libical/src/libicalcap/icalcap_server.c
new file mode 100644
index 0000000000..77b8484554
--- /dev/null
+++ b/libical/src/libicalcap/icalcap_server.c
@@ -0,0 +1,52 @@
+#include "config.h"
+
+#include "icalcap.h"
+#include "icalcap_server.h"
+#include "icalcap_server_impl.h"
+
+
+icalcap_server *
+icalcap_server_new(icalcap_auth_handler auth_cb, icalcap_chanup_handler chanup_cb,
+ icalcap_msg_handler msg_cb) {
+
+#ifdef WITH_RR
+ return icalcap_server_new_rr(auth_cb, chanup_cb, msg_cb);
+#else
+ return NULL;
+#endif
+}
+
+int
+icalcap_server_listen(icalcap_server *cap, const char *hostname, const int _port) {
+
+ int port = _port;
+
+ if (port <= 0)
+ port = 1026;
+
+#ifdef WITH_RR
+ return icalcap_server_listen_rr(cap, hostname, port);
+#else
+ return 0;
+#endif
+}
+
+int
+icalcap_server_run(const icalcap_server *cap) {
+
+#ifdef WITH_RR
+ return icalcap_server_run_rr(cap);
+#else
+ return 0;
+#endif
+}
+
+int
+icalcap_server_shutdown(icalcap_server *cap) {
+
+#ifdef WITH_RR
+ return icalcap_server_shutdown_rr(cap);
+#else
+ return 0;
+#endif
+}
diff --git a/libical/src/libicalcap/icalcap_server.h b/libical/src/libicalcap/icalcap_server.h
new file mode 100644
index 0000000000..53c73d3bab
--- /dev/null
+++ b/libical/src/libicalcap/icalcap_server.h
@@ -0,0 +1,19 @@
+#ifndef __ICALCAP_SERVER_H__
+#define __ICALCAP_SERVER_H__
+
+#include "icalcap.h"
+
+typedef struct _icalcap_server icalcap_server;
+
+typedef int (*icalcap_auth_handler)(const icalcap *cap, const char *piggyback);
+typedef void (*icalcap_chanup_handler)(const icalcap *cap);
+
+icalcap_server*icalcap_server_new(icalcap_auth_handler auth_cb,
+ icalcap_chanup_handler chanup_cb,
+ icalcap_msg_handler msg_cb);
+
+int icalcap_server_listen(icalcap_server *cap, const char *hostname, const int port);
+int icalcap_server_run(const icalcap_server *cap);
+int icalcap_server_shutdown(icalcap_server *cap);
+
+#endif
diff --git a/libical/src/libicalcap/icalcap_server_impl.h b/libical/src/libicalcap/icalcap_server_impl.h
new file mode 100644
index 0000000000..c5d527d266
--- /dev/null
+++ b/libical/src/libicalcap/icalcap_server_impl.h
@@ -0,0 +1,31 @@
+#ifndef __ICALCAP_SERVER_IMPL_H__
+#define __ICALCAP_SERVER_IMPL_H__
+
+#ifdef WITH_RR
+
+#include <librr/rr.h>
+#include <librrsasl/rr-sasl.h>
+#include <librrtls/rr-tls.h>
+#include <librrcap/rr-cap.h>
+
+struct _icalcap_server {
+ RRProfileRegistry *profreg;
+ RRListener *listener;
+ RRCAPConfig *cfg;
+ icalcap_msg_handler handler;
+};
+
+icalcap_server *icalcap_server_new_rr(icalcap_auth_handler auth_cb,
+ icalcap_chanup_handler chanup_cb,
+ icalcap_msg_handler msg_cb);
+int icalcap_server_listen_rr(icalcap_server *cap,
+ const char *hostname,
+ const int port);
+int icalcap_server_run_rr(const icalcap_server *cap);
+int icalcap_server_shutdown_rr(icalcap_server *cap);
+
+#else
+#error "No implementation of icalcap found!"
+#endif
+
+#endif
diff --git a/libical/src/libicalcap/icalcap_session.c b/libical/src/libicalcap/icalcap_session.c
new file mode 100644
index 0000000000..2f8fb2a701
--- /dev/null
+++ b/libical/src/libicalcap/icalcap_session.c
@@ -0,0 +1,62 @@
+#include "config.h"
+
+#include "icalcap.h"
+#include "icalcap_session.h"
+#include "icalcap_session_impl.h"
+
+
+icalcap_session *
+icalcap_session_new(void) {
+
+#ifdef WITH_RR
+ return icalcap_session_new_rr();
+#else
+ return NULL;
+#endif
+}
+
+int
+icalcap_session_connect(icalcap_session *sess, const char *hostname, const int _port) {
+
+ int port = _port;
+
+ if (port <= 0)
+ port = 1026;
+
+#ifdef WITH_RR
+ return icalcap_session_connect_rr(sess, hostname, port);
+#else
+ return 0;
+#endif
+}
+
+int
+icalcap_session_login(icalcap_session *sess, const char *username, const char *authname,
+ const char *password) {
+
+#ifdef WITH_RR
+ return icalcap_session_login_rr(sess, username, authname, password);
+#else
+ return 0;
+#endif
+}
+
+icalcap *
+icalcap_session_start(const icalcap_session *sess, icalcap_msg_handler handler) {
+
+#ifdef WITH_RR
+ return icalcap_session_start_rr(sess, handler);
+#else
+ return 0;
+#endif
+}
+
+int
+icalcap_session_disconnect(icalcap_session *sess) {
+
+#ifdef WITH_RR
+ return icalcap_session_disconnect_rr(sess);
+#else
+ return 0;
+#endif
+}
diff --git a/libical/src/libicalcap/icalcap_session.h b/libical/src/libicalcap/icalcap_session.h
new file mode 100644
index 0000000000..c12d434b8c
--- /dev/null
+++ b/libical/src/libicalcap/icalcap_session.h
@@ -0,0 +1,15 @@
+#ifndef __ICALCAP_SESSION_H__
+#define __ICALCAP_SESSION_H__
+
+#include "icalcap.h"
+
+typedef struct _icalcap_session icalcap_session;
+
+icalcap_session*icalcap_session_new(void);
+int icalcap_session_connect(icalcap_session *cap, const char *hostname, const int port);
+int icalcap_session_login(icalcap_session *cap, const char *username, const char *authname,
+ const char *password);
+icalcap*icalcap_session_start(const icalcap_session *cap, icalcap_msg_handler handler);
+int icalcap_session_disconnect(icalcap_session *cap);
+
+#endif
diff --git a/libical/src/libicalcap/icalcap_session_impl.h b/libical/src/libicalcap/icalcap_session_impl.h
new file mode 100644
index 0000000000..26bb7d107d
--- /dev/null
+++ b/libical/src/libicalcap/icalcap_session_impl.h
@@ -0,0 +1,36 @@
+#ifndef __ICALCAP_SESSION_IMPL_H__
+#define __ICALCAP_SESSION_IMPL_H__
+
+#ifdef WITH_RR
+
+#include <librr/rr.h>
+#include <librrsasl/rr-sasl.h>
+#include <librrtls/rr-tls.h>
+#include <librrcap/rr-cap.h>
+
+struct _icalcap_session {
+ RRProfileRegistry *profreg;
+ RRConnection *connection;
+ RRCAPConfig *cfg;
+ icalcap_msg_handler handler;
+
+ char *username;
+};
+
+icalcap_session*icalcap_session_new_rr(void);
+int icalcap_session_connect_rr(icalcap_session *cap,
+ const char *hostname,
+ const int port);
+int icalcap_session_login_rr(icalcap_session *cap,
+ const char *username,
+ const char *authname,
+ const char *password);
+icalcap *icalcap_session_start_rr(const icalcap_session *cap,
+ icalcap_msg_handler handler);
+int icalcap_session_disconnect_rr(icalcap_session *cap);
+
+#else
+#error "No implementation of icalcap found!"
+#endif
+
+#endif
diff --git a/libical/src/libicalcap/icalcap_utils.c b/libical/src/libicalcap/icalcap_utils.c
new file mode 100644
index 0000000000..b676f6272d
--- /dev/null
+++ b/libical/src/libicalcap/icalcap_utils.c
@@ -0,0 +1,134 @@
+/*-
+ * $Id$
+ *
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002 Andrea Campi <a.campi@inet.it>
+ */
+
+#include "config.h"
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "icalcap.h"
+
+#define CONTENT_TYPE "Content-Type: text/calendar"
+
+
+icalcomponent *
+icalcap_component_new_from_string(const char *data) {
+
+ icalcomponent *ret = NULL;
+ char *mtype;
+
+ /* FIXME split the check */
+ if (strncmp(data, CONTENT_TYPE, strlen(CONTENT_TYPE))) {
+ return NULL;
+ }
+
+ mtype = (char *)data+strlen(CONTENT_TYPE);
+
+ ret = icalcomponent_new_from_string(mtype);
+ if (ret == NULL) {
+ return NULL;
+ }
+
+#ifdef DEBUG
+ g_message("icalcap_component_new_from_string icalcomponent_new_from_string = %p", ret);
+#endif
+
+ /* FIXME
+ * Validate here: should check at least the version
+ */
+ if (icalcomponent_isa(ret) != ICAL_VCALENDAR_COMPONENT &&
+ icalcomponent_isa(ret) != ICAL_XROOT_COMPONENT) {
+ icalcomponent_free(ret);
+
+ return NULL;
+ }
+
+ return ret;
+}
+
+#if 0
+RRCAPCmdArgs *
+msg_parse(RRCAP *cap, icalcomponent *comp) {
+
+ icalproperty *prop;
+ icalparameter *param;
+ icalvalue *value;
+ RRCAPCmdArgs *ret = g_new0(RRCAPCmdArgs, 1);
+
+ ret->comp = comp;
+
+ /* Find the command */
+ if ((prop = icalcomponent_get_first_property(comp, ICAL_CMD_PROPERTY)) == NULL) {
+ rr_cap_send_error(cap, NULL, ICAL_9_0_UNRECOGNIZED_COMMAND,
+ "No CMD sent", NULL);
+ goto FAILED;
+ }
+ if ((value = icalproperty_get_value(prop)) == NULL) {
+ rr_cap_send_error(cap, NULL, ICAL_9_0_UNRECOGNIZED_COMMAND,
+ "CMD has no value", icalproperty_as_ical_string(prop));
+ goto FAILED;
+ }
+ ret->cmd = icalvalue_get_cmd(value);
+
+ /* Look for params */
+
+ /* ID */
+ if ((param = icalproperty_get_first_parameter(prop,
+ ICAL_ID_PARAMETER)) != NULL) {
+ if ((ret->id = icalparameter_get_id(param)) == NULL) {
+ rr_cap_send_error(cap, NULL,
+ ICAL_9_0_UNRECOGNIZED_COMMAND,
+ "ID param is garbled",
+ icalproperty_as_ical_string(prop));
+ goto FAILED;
+ }
+ }
+
+ /* LATENCY */
+ if ((param = icalproperty_get_first_parameter(prop,
+ ICAL_LATENCY_PARAMETER)) != NULL) {
+ const char *tmp;
+ if ((tmp = icalparameter_get_latency(param)) == NULL) {
+ rr_cap_send_error(cap, NULL,
+ ICAL_9_0_UNRECOGNIZED_COMMAND,
+ "LATENCY is garbled",
+ icalproperty_as_ical_string(prop));
+ goto FAILED;
+ }
+
+ ret->latency = atoi(tmp);
+ }
+
+ /* ACTION */
+ if ((param = icalproperty_get_first_parameter(prop,
+ ICAL_ACTIONPARAM_PARAMETER)) != NULL) {
+ if ((ret->action = icalparameter_get_actionparam(param))
+ == NULL) {
+ rr_cap_send_error(cap, NULL,
+ ICAL_9_0_UNRECOGNIZED_COMMAND,
+ "ACTION is garbled",
+ icalproperty_as_ical_string(prop));
+ goto FAILED;
+ }
+ }
+
+ if ((ret->latency >= 0) ^ (ret->action != ICAL_ACTIONPARAM_NONE)) {
+ rr_cap_send_error(cap, NULL, ICAL_9_0_UNRECOGNIZED_COMMAND,
+ "LATENCY and ACTION must be both present",
+ icalproperty_as_ical_string(prop));
+ goto FAILED;
+ }
+
+ return ret;
+
+FAILED:
+ g_free(ret);
+ return NULL;
+}
+#endif