#include "config.h" #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #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; }