/*- * $Id: rr-message-start.c,v 1.22 2002/11/08 12:35:28 jonas Exp $ * * 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 * Copyright (c) 2002 Daniel Lundin * Copyright (c) 2002 CodeFactory AB. All rights reserved. */ #include #include #include #include #include static GObjectClass *parent_class = NULL; static void finalize (GObject *object); static gboolean process_frame (RRMessage *message, RRFrame *frame, GError **error); static RRFrame *get_frame (RRMessage *stat, gsize max_size); static void free_item (gpointer data, gpointer user_data) { RRStartItem *item = (RRStartItem *)data; g_free (item->piggyback); g_free (item); } static void finalize (GObject *object) { RRMessageStart *start = (RRMessageStart *)object; g_mutex_free (start->done_mutex); g_cond_free (start->done_cond); g_slist_foreach (start->in_list, free_item, 0); g_slist_foreach (start->out_list, (GFunc)g_object_unref, 0); g_slist_free (start->in_list); g_slist_free (start->out_list); g_free (start->server_name); if (start->client_init_error) g_error_free (start->client_init_error); parent_class->finalize (object); } static void rr_message_start_init (GObject *object) { RRMessageStart *start = (RRMessageStart *)object; RRMessage *msg = (RRMessage *)object; start->done_mutex = g_mutex_new (); start->done_cond = g_cond_new (); msg->type = RR_FRAME_TYPE_MSG; } static void rr_message_start_class_init (GObjectClass *klass) { RRMessageClass *msg_class = (RRMessageClass *)klass; klass->finalize = finalize; msg_class->get_frame = get_frame; msg_class->process_frame = process_frame; parent_class = g_type_class_peek_parent (klass); } GType rr_message_start_get_type (void) { static GType rr_type = 0; if (!rr_type) { static GTypeInfo type_info = { sizeof (RRMessageStartClass), NULL, NULL, (GClassInitFunc) rr_message_start_class_init, NULL, NULL, sizeof (RRMessageStart), 16, (GInstanceInitFunc) rr_message_start_init }; rr_type = g_type_register_static (RR_TYPE_MESSAGE, "RRMessageStart", &type_info, 0); } return rr_type; } RRMessageStart * rr_message_start_new (gint number, const gchar *server_name) { RRMessageStart *start; start = g_object_new (RR_TYPE_MESSAGE_START, NULL); start->number = number; if (server_name) start->server_name = g_strdup (server_name); return start; } GSList * rr_message_start_get_channel_list (RRMessageStart *start) { g_return_val_if_fail (RR_IS_MESSAGE_START (start), NULL); return start->in_list; } void rr_message_start_add_channel (RRMessageStart *start, RRConnection *conn, GType type, gpointer config_data) { RRChannel *channel; GError *error = NULL; gpointer global_config = NULL; channel = g_object_new (type, NULL); channel->id = start->number; channel->instance_config = config_data; if (conn->profreg) { global_config = rr_profile_registry_get_global_config (conn->profreg, type); } channel->global_config = global_config; rr_channel_set_connection (channel, conn); /* Give the channel a chance to generate a piggyback */ if (!rr_channel_client_init (channel, &error)) { if (error) { if (start->client_init_error) g_error_free (start->client_init_error); start->client_init_error = error; } rr_debug1 ("message_start::add_channel " "rr_channel_client_init failed: %s, %s", G_OBJECT_TYPE_NAME (G_OBJECT (channel)), error->message); g_object_unref (G_OBJECT (channel)); } else start->out_list = g_slist_append (start->out_list, channel); } static void append_channel (gpointer data, gpointer user_data) { RRChannel *channel = data; GString *str = (GString *)user_data; const gchar *uri, *piggyback; g_return_if_fail (RR_IS_CHANNEL (channel)); uri = rr_channel_get_uri (G_OBJECT_TYPE (G_OBJECT (channel))); piggyback = rr_channel_get_piggyback (channel); if (piggyback) { g_string_append_printf (str, " \r\n" " \r\n" " \r\n", uri, piggyback); } else g_string_append_printf (str, " \r\n", uri); } static gboolean process_frame (RRMessage *message, RRFrame *frame, GError **error) { RRMessageStart *start; RRConnection *conn; xmlDocPtr doc; xmlNodePtr node; gboolean ret; gchar *server_name; xmlChar *str; const gchar *data; gint len; g_return_val_if_fail (RR_IS_MESSAGE_START (message), FALSE); g_return_val_if_fail (RR_IS_FRAME (frame), FALSE); g_return_val_if_fail (RR_IS_CHANNEL (message->channel), FALSE); g_return_val_if_fail (RR_IS_CONNECTION (message->channel->connection), FALSE); conn = message->channel->connection; start = RR_MESSAGE_START (message); data = rr_frame_mime_get_body (frame); len = rr_frame_mime_get_body_size (frame); doc = xmlParseMemory (data, len); if (doc == NULL) { g_set_error (error, RR_ERROR, RR_BEEP_CODE_SYNTAX_ERROR, "Invalid start message."); return FALSE; } node = xmlDocGetRootElement (doc); if (strcmp (node->name, "start") != 0) goto err; if ((str = xmlGetProp (node, "number")) == NULL) goto err; start->number = atoi (str); xmlFree (str); if ((server_name = xmlGetProp (node, "serverName"))) { start->server_name = g_strdup (server_name); xmlFree (server_name); } node = node->children; while (node) { xmlChar *uri, *piggyback = NULL; GType type; RRStartItem *item; /* Ignore none element nodes */ while (node && node->type != XML_ELEMENT_NODE) node = node->next; if (node == NULL) break; if (strcmp (node->name, "profile") != 0) goto err; if ((uri = xmlGetProp (node, "uri")) == NULL) goto err; type = rr_profile_registry_lookup_by_uri (conn->profreg, uri); xmlFree (uri); if (type == G_TYPE_INVALID) { node = node->next; continue; } /* Extract the piggyback data if it exists */ if (node->children) { xmlNode *child, *cdata_node = NULL; child = node->children; while (child) { if (child->type == XML_CDATA_SECTION_NODE && child->content) { cdata_node = child; break; } child = child->next; } if (cdata_node && child->content) piggyback = cdata_node->content; else if (node->children->type == XML_TEXT_NODE && node->children->content) piggyback = node->children->content; } item = g_new (RRStartItem, 1); item->type = type; item->piggyback = g_strdup (piggyback); start->in_list = g_slist_append (start->in_list, item); node = node->next; } ret = TRUE; goto end; err: g_set_error (error, RR_ERROR, RR_BEEP_CODE_PARAM_ERROR, RR_GERROR_DEFAULT_MESSAGE); ret = FALSE; end: xmlFreeDoc (doc); return ret; } static RRFrame * get_frame (RRMessage *message, gsize max_size) { RRMessageStart *start = RR_MESSAGE_START(message); RRFrame *frame; GString *str = g_string_new (RR_BEEP_MIME_HEADER); g_assert (RR_IS_MESSAGE (start)); if (start->server_name) g_string_append_printf (str, "\r\n", start->number, start->server_name); else g_string_append_printf (str, "\r\n", start->number); g_slist_foreach (start->out_list, append_channel, str); g_string_append (str, "\r\n"); if (str->len > max_size) { g_string_free (str, TRUE); return NULL; } frame = rr_frame_new (RR_FRAME_TYPE_MSG, message->channel->id, FALSE, message->msgno, str->len, 0, str->str, TRUE); g_string_free (str, FALSE); return frame; } void rr_message_start_done (RRMessageStart *start, RRChannel *channel, GError *error) { g_return_if_fail (RR_IS_MESSAGE_START (start)); g_mutex_lock (start->done_mutex); start->done_channel = channel; if (error) start->done_error = g_error_copy (error); start->done = TRUE; g_cond_broadcast (start->done_cond); g_mutex_unlock (start->done_mutex); } RRChannel * rr_message_start_wait_for_reply (RRMessageStart *start, GError **error) { g_return_val_if_fail (RR_IS_MESSAGE_START (start), NULL); g_mutex_lock (start->done_mutex); while (!start->done) g_cond_wait (start->done_cond, start->done_mutex); g_mutex_unlock (start->done_mutex); if (start->done_error) { g_propagate_error (error, start->done_error); start->done_error = NULL; } return start->done_channel; } /** * rr_message_start_get_server_name: * @start: A #RRMessageStart. * * Returns the "serverName" attribute for the first successful "start" element * received by a BEEP peer is meaningful for the duration of the BEEP * session. * * Return value: the %serverName. **/ const gchar * rr_message_start_get_server_name (RRMessageStart *start) { g_return_val_if_fail (RR_IS_MESSAGE_START (start), NULL); return start->server_name; } gboolean rr_message_start_empty_request_p (RRMessageStart *start) { g_return_val_if_fail (RR_IS_MESSAGE_START (start), TRUE); return start->out_list == NULL; }