/* See RFC 2045, Section 6.7 */
#include "gskmimeencodings.h"
#include "../gsksimplefilter.h"
#include "../gskmacros.h"
#include <string.h>
#include <ctype.h>
/* --- common --- */
static GObjectClass *parent_class = NULL;
/* --- typedefs --- */
typedef struct _GskMimeQuotedPrintableDecoder GskMimeQuotedPrintableDecoder;
typedef struct _GskMimeQuotedPrintableDecoderClass GskMimeQuotedPrintableDecoderClass;
/* --- type macros --- */
GType gsk_mime_quoted_printable_decoder_get_type(void) G_GNUC_CONST;
#define GSK_TYPE_MIME_QUOTED_PRINTABLE_DECODER (gsk_mime_quoted_printable_decoder_get_type ())
#define GSK_MIME_QUOTED_PRINTABLE_DECODER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSK_TYPE_MIME_QUOTED_PRINTABLE_DECODER, GskMimeQuotedPrintableDecoder))
#define GSK_MIME_QUOTED_PRINTABLE_DECODER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSK_TYPE_MIME_QUOTED_PRINTABLE_DECODER, GskMimeQuotedPrintableDecoderClass))
#define GSK_MIME_QUOTED_PRINTABLE_DECODER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSK_TYPE_MIME_QUOTED_PRINTABLE_DECODER, GskMimeQuotedPrintableDecoderClass))
#define GSK_IS_MIME_QUOTED_PRINTABLE_DECODER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSK_TYPE_MIME_QUOTED_PRINTABLE_DECODER))
#define GSK_IS_MIME_QUOTED_PRINTABLE_DECODER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSK_TYPE_MIME_QUOTED_PRINTABLE_DECODER))
/* --- structures --- */
struct _GskMimeQuotedPrintableDecoderClass
{
GskSimpleFilterClass simple_filter_class;
};
struct _GskMimeQuotedPrintableDecoder
{
GskSimpleFilter simple_filter;
};
/* --- GskSimpleFilter methods --- */
static gboolean
quoteprintable_char_to_hexval (char c, guint8 *val_out, GError **error)
{
if ('0' <= c && c <= '9')
*val_out = c - '0';
else if ('A' <= c && c <= 'F')
*val_out = c - 'A' + 10;
else
{
g_set_error (error, GSK_G_ERROR_DOMAIN, GSK_ERROR_BAD_FORMAT,
_("quoted-printable: error parsing hex value '%c'"), c);
return FALSE;
}
return TRUE;
}
static gboolean
gsk_mime_quoted_printable_decoder_process (GskSimpleFilter *filter,
GskBuffer *dst,
GskBuffer *src,
GError **error)
{
for (;;)
{
char buf[3];
guint p = gsk_buffer_peek (src, buf, 3);
guint ne = 0;
guint8 hexvalues[2];
while (ne < p)
{
if (buf[ne] == '=')
break;
ne++;
}
if (ne > 0)
{
gsk_buffer_append (dst, buf, ne);
gsk_buffer_discard (src, ne);
}
else if (p <= 1)
{
break;
}
else if (p == 2 && buf[1] == '\n')
{
gsk_buffer_discard (src, 2);
}
else
{
if (buf[1] == '\r' && buf[2] == '\n')
gsk_buffer_discard (src, 3);
else if (buf[1] == '\n')
gsk_buffer_discard (src, 2);
else
{
if (!quoteprintable_char_to_hexval (buf[1], &hexvalues[0], error)
|| !quoteprintable_char_to_hexval (buf[2], &hexvalues[1], error))
return FALSE;
gsk_buffer_append_char (dst, (hexvalues[0] << 4) | hexvalues[1]);
gsk_buffer_discard (src, 3);
}
}
}
return TRUE;
}
static gboolean
gsk_mime_quoted_printable_decoder_flush (GskSimpleFilter *filter,
GskBuffer *dst,
GskBuffer *src,
GError **error)
{
if (src->size > 0)
{
g_set_error (error, GSK_G_ERROR_DOMAIN, GSK_ERROR_BAD_FORMAT,
_("unprocessable junk in quoted-printable (%u bytes)"),
src->size);
return FALSE;
}
return TRUE;
}
/* --- functions --- */
static void
gsk_mime_quoted_printable_decoder_init (GskMimeQuotedPrintableDecoder *mime_quoted_printable_decoder)
{
}
static void
gsk_mime_quoted_printable_decoder_class_init (GskMimeQuotedPrintableDecoderClass *class)
{
GskSimpleFilterClass *simple_filter_class = GSK_SIMPLE_FILTER_CLASS (class);
parent_class = g_type_class_peek_parent (class);
simple_filter_class->process = gsk_mime_quoted_printable_decoder_process;
simple_filter_class->flush = gsk_mime_quoted_printable_decoder_flush;
}
GType gsk_mime_quoted_printable_decoder_get_type()
{
static GType mime_quoted_printable_decoder_type = 0;
if (!mime_quoted_printable_decoder_type)
{
static const GTypeInfo mime_quoted_printable_decoder_info =
{
sizeof(GskMimeQuotedPrintableDecoderClass),
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
(GClassInitFunc) gsk_mime_quoted_printable_decoder_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (GskMimeQuotedPrintableDecoder),
0, /* n_preallocs */
(GInstanceInitFunc) gsk_mime_quoted_printable_decoder_init,
NULL /* value_table */
};
mime_quoted_printable_decoder_type = g_type_register_static (GSK_TYPE_SIMPLE_FILTER,
"GskMimeQuotedPrintableDecoder",
&mime_quoted_printable_decoder_info, 0);
}
return mime_quoted_printable_decoder_type;
}
/**
* gsk_mime_quoted_printable_decoder_new:
*
* Allocate a new MIME encoder which
* takes quoted-printable encoded data in and gives
* raw binary data out.
* (See RFC 2045, Section 6.7).
*
* returns: the newly allocated decoder.
*/
GskStream *
gsk_mime_quoted_printable_decoder_new (void)
{
return g_object_new (GSK_TYPE_MIME_QUOTED_PRINTABLE_DECODER, NULL);
}
/* ================================= Encoder ================================ */
/* --- typedefs --- */
typedef struct _GskMimeQuotedPrintableEncoder GskMimeQuotedPrintableEncoder;
typedef struct _GskMimeQuotedPrintableEncoderClass GskMimeQuotedPrintableEncoderClass;
/* --- type macros --- */
GType gsk_mime_quoted_printable_encoder_get_type(void) G_GNUC_CONST;
#define GSK_TYPE_MIME_QUOTED_PRINTABLE_ENCODER (gsk_mime_quoted_printable_encoder_get_type ())
#define GSK_MIME_QUOTED_PRINTABLE_ENCODER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSK_TYPE_MIME_QUOTED_PRINTABLE_ENCODER, GskMimeQuotedPrintableEncoder))
#define GSK_MIME_QUOTED_PRINTABLE_ENCODER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSK_TYPE_MIME_QUOTED_PRINTABLE_ENCODER, GskMimeQuotedPrintableEncoderClass))
#define GSK_MIME_QUOTED_PRINTABLE_ENCODER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSK_TYPE_MIME_QUOTED_PRINTABLE_ENCODER, GskMimeQuotedPrintableEncoderClass))
#define GSK_IS_MIME_QUOTED_PRINTABLE_ENCODER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSK_TYPE_MIME_QUOTED_PRINTABLE_ENCODER))
#define GSK_IS_MIME_QUOTED_PRINTABLE_ENCODER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSK_TYPE_MIME_QUOTED_PRINTABLE_ENCODER))
/* --- structures --- */
struct _GskMimeQuotedPrintableEncoderClass
{
GskSimpleFilterClass simple_filter_class;
};
struct _GskMimeQuotedPrintableEncoder
{
GskSimpleFilter simple_filter;
guint n_chars_in_line;
};
/* --- prototypes --- */
#define IS_ENCODABLE_AS_ITSELF(c) \
((33 <= (c) && (c) <= 60) || (62 <= (c) && (c) <= 126))
/* --- GskSimpleFilter methods --- */
static gboolean
gsk_mime_quoted_printable_encoder_process (GskSimpleFilter *filter,
GskBuffer *dst,
GskBuffer *src,
GError **error)
{
GskMimeQuotedPrintableEncoder *encoder = GSK_MIME_QUOTED_PRINTABLE_ENCODER (filter);
guint n_chars_in_line = encoder->n_chars_in_line;
guint n_peeked;
char buf[256];
while ((n_peeked = gsk_buffer_peek (src, buf, sizeof(buf))) > 0)
{
char *at = buf;
guint rem = n_peeked;
while (rem)
{
if (n_chars_in_line > 68)
{
gsk_buffer_append (dst, "=\r\n", 3);
n_chars_in_line = 0;
}
if (IS_ENCODABLE_AS_ITSELF (*at))
{
gsk_buffer_append_char (dst, *at);
rem--;
at++;
n_chars_in_line++;
continue;
}
else if (*at == '\r')
{
if (rem >= 2)
{
if (at[1] == '\n')
{
gsk_buffer_append (dst, at, 2);
rem -= 2;
at += 2;
n_chars_in_line = 0;
continue;
}
}
else
break;
}
/* encode all other chars as hex */
{
char tmp[4];
g_snprintf (tmp, 4, "=%02X", (guint8) *at);
gsk_buffer_append (dst, tmp, 3);
rem--;
at++;
n_chars_in_line += 3;
}
}
gsk_buffer_discard (src, n_peeked - rem);
if (n_peeked < sizeof (buf))
break;
}
encoder->n_chars_in_line = n_chars_in_line;
return TRUE;
}
static gboolean
gsk_mime_quoted_printable_encoder_flush (GskSimpleFilter *filter,
GskBuffer *dst,
GskBuffer *src,
GError **error)
{
GskMimeQuotedPrintableEncoder *encoder = GSK_MIME_QUOTED_PRINTABLE_ENCODER (filter);
g_return_val_if_fail (src->size <= 1, FALSE);
if (src->size == 1)
{
gsk_buffer_printf (dst, "=%02X", gsk_buffer_read_char (src));
encoder->n_chars_in_line += 3;
}
if (encoder->n_chars_in_line > 0)
gsk_buffer_append (dst, "=\r\n", 3);
return TRUE;
}
/* --- functions --- */
static void
gsk_mime_quoted_printable_encoder_init (GskMimeQuotedPrintableEncoder *mime_quoted_printable_encoder)
{
}
static void
gsk_mime_quoted_printable_encoder_class_init (GskMimeQuotedPrintableEncoderClass *class)
{
GskSimpleFilterClass *simple_filter_class = GSK_SIMPLE_FILTER_CLASS (class);
parent_class = g_type_class_peek_parent (class);
simple_filter_class->process = gsk_mime_quoted_printable_encoder_process;
simple_filter_class->flush = gsk_mime_quoted_printable_encoder_flush;
}
GType gsk_mime_quoted_printable_encoder_get_type()
{
static GType mime_quoted_printable_encoder_type = 0;
if (!mime_quoted_printable_encoder_type)
{
static const GTypeInfo mime_quoted_printable_encoder_info =
{
sizeof(GskMimeQuotedPrintableEncoderClass),
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
(GClassInitFunc) gsk_mime_quoted_printable_encoder_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (GskMimeQuotedPrintableEncoder),
0, /* n_preallocs */
(GInstanceInitFunc) gsk_mime_quoted_printable_encoder_init,
NULL /* value_table */
};
mime_quoted_printable_encoder_type = g_type_register_static (GSK_TYPE_SIMPLE_FILTER,
"GskMimeQuotedPrintableEncoder",
&mime_quoted_printable_encoder_info, 0);
}
return mime_quoted_printable_encoder_type;
}
/**
* gsk_mime_quoted_printable_encoder_new:
*
* Allocate a new MIME encoder which
* takes raw binary data in and gives
* quoted-printable encoded data out.
* (See RFC 2045, Section 6.7).
*
* returns: the newly allocated encoder.
*/
GskStream *
gsk_mime_quoted_printable_encoder_new (void)
{
return g_object_new (GSK_TYPE_MIME_QUOTED_PRINTABLE_ENCODER, NULL);
}
syntax highlighted by Code2HTML, v. 0.9.1