/*-
* $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 <jonas@codefactory.se>
* Copyright (c) 2002 Daniel Lundin <daniel@codefactory.se>
* Copyright (c) 2002 CodeFactory AB. All rights reserved.
*/
#include <librr/rr.h>
#include <stdio.h>
#include <string.h>
#include <stdio.h>
#include <string.h>
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;
}
syntax highlighted by Code2HTML, v. 0.9.1