/*- * $Id: rr-frame.c,v 1.24 2002/12/12 14:59:52 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 gint build (RRFrame *frame, gchar *buffer); static gint parse (RRFrame *frame, const gchar *buffer, const gchar *next_line, gint len, GError **error); static char *type_string[] = { "UNKNOWN", "MSG", "RPY", "ERR", "ANS", "NUL"}; static void finalize (GObject *object) { RRFrame *frame = (RRFrame *)object; if (frame->payload && frame->should_free) g_free (frame->payload); /* If the frame holds a reference of a message * release it now */ if (frame->msg) g_object_unref (frame->msg); if (frame->part) rr_mime_part_free (frame->part); parent_class->finalize (object); } static void rr_frame_init (GObject *object) { } static void rr_frame_class_init (GObjectClass *klass) { RRFrameClass *frame_class = (RRFrameClass *)klass; frame_class->build = build; frame_class->parse = parse; klass->finalize = finalize; parent_class = g_type_class_peek_parent (klass); } GType rr_frame_get_type (void) { static GType rr_type = 0; if (!rr_type) { static GTypeInfo type_info = { sizeof (RRFrameClass), NULL, NULL, (GClassInitFunc) rr_frame_class_init, NULL, NULL, sizeof (RRFrame), 16, (GInstanceInitFunc) rr_frame_init }; rr_type = g_type_register_static (G_TYPE_OBJECT, "RRFrame", &type_info, 0); } return rr_type; } gint rr_frame_build (RRFrame *frame, gchar *buffer) { return RR_FRAME_GET_CLASS (frame)->build (frame, buffer); } gint rr_frame_parse (RRFrame *frame, const gchar *buffer, const gchar *body, gint len, GError **error) { return RR_FRAME_GET_CLASS (frame)->parse (frame, buffer, body, len, error); } void rr_frame_reference_message (RRFrame *frame, RRMessage *msg) { g_return_if_fail (RR_IS_FRAME (frame)); g_return_if_fail (RR_IS_MESSAGE (msg)); if (frame->msg) g_object_unref (G_OBJECT (frame->msg)); if (msg) frame->msg = g_object_ref (G_OBJECT (msg)); else frame->msg = NULL; } RRFrame * rr_frame_new (RRFrameType type, gint32 channel_id, gboolean more, gint32 msgno, gint32 size, gint32 ansno, gchar *payload, gboolean should_free) { RRFrame *frame; frame = g_object_new (RR_TYPE_FRAME, NULL); frame->channel_id = channel_id; frame->type = type; frame->more = more; frame->payload = payload; frame->msgno = msgno; frame->size = size; frame->ansno = ansno; frame->should_free = should_free; return frame; } static gint build (RRFrame *frame, gchar *buffer) { gint len; switch (frame->type) { case RR_FRAME_TYPE_ANS: sprintf (buffer, "%s %d %d %c %d %u %d\r\n", type_string[frame->type], frame->channel_id, frame->msgno, frame->more ? '*' : '.', frame->seqno, frame->size, frame->ansno); break; case RR_FRAME_TYPE_MSG: case RR_FRAME_TYPE_RPY: case RR_FRAME_TYPE_ERR: case RR_FRAME_TYPE_NUL: sprintf (buffer, "%s %d %d %c %u %d\r\n", type_string[frame->type], frame->channel_id, frame->msgno, frame->more ? '*' : '.', frame->seqno, frame->size); break; default: g_assert_not_reached (); } len = strlen (buffer); memcpy (buffer + len, frame->payload, frame->size); len += frame->size; strcpy (buffer + len, "END\r\n"); len += 5; return len; } static RRFrameType get_frame_type (const gchar *buffer) { char str[4]; g_return_val_if_fail (buffer != NULL, RR_FRAME_TYPE_UNKNOWN); if (sscanf (buffer, "%3s", str) != 1) { g_print ("frame_divider, parse error1\n"); return RR_FRAME_TYPE_UNKNOWN; } if (!strcmp ("MSG", str)) return RR_FRAME_TYPE_MSG; if (!strcmp ("RPY", str)) return RR_FRAME_TYPE_RPY; if (!strcmp ("ERR", str)) return RR_FRAME_TYPE_ERR; if (!strcmp ("ANS", str)) return RR_FRAME_TYPE_ANS; if (!strcmp ("NUL", str)) return RR_FRAME_TYPE_NUL; return RR_FRAME_TYPE_UNKNOWN; } static gint parse (RRFrame *frame, const gchar *buffer, const gchar *body, gint len, GError **error) { gint header_len; gchar more; g_return_val_if_fail (RR_IS_FRAME (frame), 0); header_len = body - buffer; frame->type = get_frame_type (buffer); if (sscanf (buffer, "%*s %d %d %c %u %d %d", &frame->channel_id, &frame->msgno, &more, &frame->seqno, &frame->size, &frame->ansno) < 5) { g_set_error (error, RR_BEEP_ERROR, RR_BEEP_CODE_SYNTAX_ERROR, "frame header parse error"); return -1; } if (frame->size < 0) { g_set_error (error, RR_BEEP_ERROR, RR_BEEP_CODE_SYNTAX_ERROR, "frame header parse error"); return -1; } frame->more = more == '*' ? TRUE : FALSE; /* Do we need more data? */ if (len - header_len - 5 < frame->size) { /* 5 = strlen ("END\r\n") */ return 0; } if (strncmp (body + frame->size, "END", 3) != 0) { g_set_error (error, RR_BEEP_ERROR, RR_BEEP_CODE_SYNTAX_ERROR, "frame syntax error"); return -1; } frame->payload = g_new (guint8, frame->size + 1); frame->should_free = TRUE; memcpy (frame->payload, body, frame->size); frame->payload[frame->size] = 0; return header_len + frame->size + 5; } RRFrame * rr_frame_aggregate (GSList **frame_list, RRFrame *frame) { /* Return immediately if message consists only of one frame */ if ((NULL == *frame_list) && !frame->more) { return g_object_ref (G_OBJECT (frame)); } *frame_list = g_slist_append (*frame_list, g_object_ref (G_OBJECT (frame))); if (!frame->more) { /* If this is the last frame, create one new frame * with all the payload */ RRFrame *new_frame; GSList *list; gchar *payload, *ptr; gint size = 0; /* Calculate the total size */ list = *frame_list; while (list) { size += RR_FRAME (list->data)->size; list = list->next; } payload = ptr = g_new (guint8, size + 1); /* Aggregate the frames */ list = *frame_list; while (list) { RRFrame *frame = RR_FRAME (list->data); memcpy (ptr, frame->payload, frame->size); ptr += frame->size; list = list->next; } payload[size] = 0; /* Allocate _one_ large frame that will contain all the data */ new_frame = rr_frame_new (frame->type, frame->channel_id, FALSE, frame->msgno, size, 0, payload, TRUE); rr_frame_aggregator_free (frame_list); return new_frame; } return NULL; } void rr_frame_aggregator_free (GSList **list) { /* Free the other frames */ g_slist_foreach (*list, (GFunc)g_object_unref, NULL); g_slist_free (*list); *list = NULL; } /** * rr_frame_parse_mime: * @frame: A #RRFrame. * * This function attempts to parse the frame payload as a mime message. * * Note: The RRMimePart object returned by this function will be freed * by RoadRunner when the #RRFrame object is destroyed. * * Return value: A pointer to a #RRMimePart or %NULL if parse failed. **/ RRMimePart * rr_frame_parse_mime (RRFrame *frame) { g_return_val_if_fail (RR_IS_FRAME (frame), NULL); g_return_val_if_fail (frame->payload != NULL, NULL); if (frame->part == NULL) { frame->part = rr_mime_parse (frame->payload, frame->size); } return frame->part; } /** * rr_frame_mime_get_body: * @frame: A #RRFrame * * Returns a pointer to the body of the mime body. If the frame * contains a multipart mime message a pointer to the body of the * first non-multipart part. * * Return value: A pointer to the mime body or %NULL. **/ const gchar * rr_frame_mime_get_body (RRFrame *frame) { RRMimePart *part, *first; g_return_val_if_fail (RR_IS_FRAME (frame), NULL); part = rr_frame_parse_mime (frame); if (part) { first = rr_mime_part_get_first (part); if (first) { return rr_mime_part_get_body (first); } } return NULL; } /** * rr_frame_mime_get_body_size: * @frame: * * Returns the length of the body of the mime body. If the frame * contains a multipart mime message the length of the body of the * first non-multipart part. * * Return value: The length of the mime body returned by rr_frame_mime_get_body. **/ gint32 rr_frame_mime_get_body_size (RRFrame *frame) { RRMimePart *part, *first; g_return_val_if_fail (RR_IS_FRAME (frame), 0); part = rr_frame_parse_mime (frame); if (part) { first = rr_mime_part_get_first (part); if (first) { return rr_mime_part_get_body_len (first); } } return 0; }