diff options
Diffstat (limited to 'libical/src/libicalcap/icalcap_rr.c')
-rw-r--r-- | libical/src/libicalcap/icalcap_rr.c | 545 |
1 files changed, 545 insertions, 0 deletions
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; +} |