/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/* GMime
* Copyright (C) 2000-2007 Jeffrey Stedfast
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <ctype.h>
#include <string.h>
#include "gmime-common.h"
#include "gmime-object.h"
#include "gmime-stream-mem.h"
#include "gmime-utils.h"
struct _type_bucket {
char *type;
GType object_type;
GHashTable *subtype_hash;
};
struct _subtype_bucket {
char *subtype;
GType object_type;
};
static void g_mime_object_class_init (GMimeObjectClass *klass);
static void g_mime_object_init (GMimeObject *object, GMimeObjectClass *klass);
static void g_mime_object_finalize (GObject *object);
static void init (GMimeObject *object);
static void add_header (GMimeObject *object, const char *header, const char *value);
static void set_header (GMimeObject *object, const char *header, const char *value);
static const char *get_header (GMimeObject *object, const char *header);
static void remove_header (GMimeObject *object, const char *header);
static void set_content_type (GMimeObject *object, GMimeContentType *content_type);
static char *get_headers (GMimeObject *object);
static ssize_t write_to_stream (GMimeObject *object, GMimeStream *stream);
static ssize_t write_content_type (GMimeStream *stream, const char *name, const char *value);
static void type_registry_init (void);
static GHashTable *type_hash = NULL;
static GObjectClass *parent_class = NULL;
GType
g_mime_object_get_type (void)
{
static GType type = 0;
if (!type) {
static const GTypeInfo info = {
sizeof (GMimeObjectClass),
NULL, /* base_class_init */
NULL, /* base_class_finalize */
(GClassInitFunc) g_mime_object_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (GMimeObject),
0, /* n_preallocs */
(GInstanceInitFunc) g_mime_object_init,
};
type = g_type_register_static (G_TYPE_OBJECT, "GMimeObject", &info, 0);
}
return type;
}
static void
g_mime_object_class_init (GMimeObjectClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
parent_class = g_type_class_ref (G_TYPE_OBJECT);
object_class->finalize = g_mime_object_finalize;
klass->init = init;
klass->add_header = add_header;
klass->set_header = set_header;
klass->get_header = get_header;
klass->remove_header = remove_header;
klass->set_content_type = set_content_type;
klass->get_headers = get_headers;
klass->write_to_stream = write_to_stream;
type_registry_init ();
}
static void
g_mime_object_init (GMimeObject *object, GMimeObjectClass *klass)
{
object->content_type = NULL;
object->headers = g_mime_header_new ();
object->content_id = NULL;
g_mime_header_register_writer (object->headers, "Content-Type", write_content_type);
}
static void
g_mime_object_finalize (GObject *object)
{
GMimeObject *mime = (GMimeObject *) object;
if (mime->content_type)
g_mime_content_type_destroy (mime->content_type);
if (mime->headers)
g_mime_header_destroy (mime->headers);
g_free (mime->content_id);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static ssize_t
write_content_type (GMimeStream *stream, const char *name, const char *value)
{
GMimeContentType *content_type;
ssize_t nwritten;
GString *out;
char *val;
out = g_string_new ("");
g_string_printf (out, "%s: ", name);
content_type = g_mime_content_type_new_from_string (value);
val = g_mime_content_type_to_string (content_type);
g_string_append (out, val);
g_free (val);
g_mime_param_write_to_string (content_type->params, TRUE, out);
g_mime_content_type_destroy (content_type);
nwritten = g_mime_stream_write (stream, out->str, out->len);
g_string_free (out, TRUE);
return nwritten;
}
/**
* g_mime_object_ref:
* @object: mime object
*
* Ref's a MIME object.
*
* WARNING: This method is deprecated. Use g_object_ref() instead.
**/
void
g_mime_object_ref (GMimeObject *object)
{
g_return_if_fail (GMIME_IS_OBJECT (object));
g_object_ref (object);
}
/**
* g_mime_object_unref:
* @object: mime object
*
* Unref's a MIME object.
*
* WARNING: This method is deprecated. Use g_object_unref() instead.
**/
void
g_mime_object_unref (GMimeObject *object)
{
g_return_if_fail (GMIME_IS_OBJECT (object));
g_object_unref (object);
}
/**
* g_mime_object_register_type:
* @type: mime type
* @subtype: mime subtype
* @object_type: object type
*
* Registers the object type @object_type for use with the
* g_mime_object_new_type() convenience function.
*
* Note: You may use the wildcard "*" to match any type and/or
* subtype.
**/
void
g_mime_object_register_type (const char *type, const char *subtype, GType object_type)
{
struct _type_bucket *bucket;
struct _subtype_bucket *sub;
g_return_if_fail (object_type != 0);
g_return_if_fail (subtype != NULL);
g_return_if_fail (type != NULL);
type_registry_init ();
if (!(bucket = g_hash_table_lookup (type_hash, type))) {
bucket = g_new (struct _type_bucket, 1);
bucket->type = g_strdup (type);
bucket->object_type = *type == '*' ? object_type : 0;
bucket->subtype_hash = g_hash_table_new (g_mime_strcase_hash, g_mime_strcase_equal);
g_hash_table_insert (type_hash, bucket->type, bucket);
}
sub = g_new (struct _subtype_bucket, 1);
sub->subtype = g_strdup (subtype);
sub->object_type = object_type;
g_hash_table_insert (bucket->subtype_hash, sub->subtype, sub);
}
static void
init (GMimeObject *object)
{
/* no-op */
}
/**
* g_mime_object_new_type:
* @type: mime type
* @subtype: mime subtype
*
* Performs a lookup of registered #GMimeObject subclasses, registered
* using g_mime_object_register_type(), to find an appropriate class
* capable of handling MIME parts of type @type/@subtype. If no class
* has been registered to handle that type, it looks for a registered
* class that can handle @type. If that also fails, then it will use
* the generic part class, #GMimePart.
*
* Returns an appropriate #GMimeObject registered to handle mime-types
* of @type/@subtype.
**/
GMimeObject *
g_mime_object_new_type (const char *type, const char *subtype)
{
struct _type_bucket *bucket;
struct _subtype_bucket *sub;
GMimeObject *object;
GType obj_type;
g_return_val_if_fail (type != NULL, NULL);
type_registry_init ();
if ((bucket = g_hash_table_lookup (type_hash, type))) {
if (!(sub = g_hash_table_lookup (bucket->subtype_hash, subtype)))
sub = g_hash_table_lookup (bucket->subtype_hash, "*");
obj_type = sub ? sub->object_type : 0;
} else {
bucket = g_hash_table_lookup (type_hash, "*");
obj_type = bucket ? bucket->object_type : 0;
}
if (!obj_type) {
/* use the default mime object */
if ((bucket = g_hash_table_lookup (type_hash, "*"))) {
sub = g_hash_table_lookup (bucket->subtype_hash, "*");
obj_type = sub ? sub->object_type : 0;
}
if (!obj_type)
return NULL;
}
object = g_object_new (obj_type, NULL);
GMIME_OBJECT_GET_CLASS (object)->init (object);
return object;
}
static void
sync_content_type (GMimeObject *object)
{
GMimeContentType *content_type;
GMimeParam *params;
GString *string;
char *type, *p;
content_type = object->content_type;
string = g_string_new ("Content-Type: ");
type = g_mime_content_type_to_string (content_type);
g_string_append (string, type);
g_free (type);
if ((params = content_type->params))
g_mime_param_write_to_string (params, FALSE, string);
p = string->str;
g_string_free (string, FALSE);
type = p + strlen ("Content-Type: ");
g_mime_header_set (object->headers, "Content-Type", type);
g_free (p);
}
static void
set_content_type (GMimeObject *object, GMimeContentType *content_type)
{
if (object->content_type)
g_mime_content_type_destroy (object->content_type);
object->content_type = content_type;
sync_content_type (object);
}
/**
* g_mime_object_set_content_type:
* @object: MIME object
* @mime_type: MIME type
*
* Sets the content-type for the specified MIME object.
**/
void
g_mime_object_set_content_type (GMimeObject *object, GMimeContentType *mime_type)
{
g_return_if_fail (GMIME_IS_OBJECT (object));
g_return_if_fail (mime_type != NULL);
GMIME_OBJECT_GET_CLASS (object)->set_content_type (object, mime_type);
}
/**
* g_mime_object_get_content_type:
* @object: MIME object
*
* Gets the Content-Type object for the given MIME object or %NULL on
* fail.
*
* Returns the content-type object for the specified MIME object.
**/
const GMimeContentType *
g_mime_object_get_content_type (GMimeObject *object)
{
g_return_val_if_fail (GMIME_IS_OBJECT (object), NULL);
return object->content_type;
}
/**
* g_mime_object_set_content_type_parameter:
* @object: MIME object
* @name: param name
* @value: param value
*
* Sets the content-type param @name to the value @value.
**/
void
g_mime_object_set_content_type_parameter (GMimeObject *object, const char *name, const char *value)
{
g_return_if_fail (GMIME_IS_OBJECT (object));
g_return_if_fail (name != NULL);
g_mime_content_type_set_parameter (object->content_type, name, value);
sync_content_type (object);
}
/**
* g_mime_object_get_content_type_parameter:
* @object: MIME object
* @name: param name
*
* Gets the value of the content-type param @name set on the MIME part
* @object.
*
* Returns the value of the requested content-type param or %NULL on
* if the param doesn't exist.
**/
const char *
g_mime_object_get_content_type_parameter (GMimeObject *object, const char *name)
{
g_return_val_if_fail (GMIME_IS_OBJECT (object), NULL);
g_return_val_if_fail (name != NULL, NULL);
return g_mime_content_type_get_parameter (object->content_type, name);
}
/**
* g_mime_object_set_content_id:
* @object: MIME object
* @content_id: content-id (addr-spec portion)
*
* Sets the Content-Id of the MIME object.
**/
void
g_mime_object_set_content_id (GMimeObject *object, const char *content_id)
{
char *msgid;
g_return_if_fail (GMIME_IS_OBJECT (object));
g_free (object->content_id);
object->content_id = g_strdup (content_id);
msgid = g_strdup_printf ("<%s>", content_id);
g_mime_object_set_header (object, "Content-Id", msgid);
g_free (msgid);
}
/**
* g_mime_object_get_content_id:
* @object: MIME object
*
* Gets the Content-Id of the MIME object or NULL if one is not set.
*
* Returns a const pointer to the Content-Id header.
**/
const char *
g_mime_object_get_content_id (GMimeObject *object)
{
g_return_val_if_fail (GMIME_IS_OBJECT (object), NULL);
return object->content_id;
}
enum {
HEADER_CONTENT_TYPE,
HEADER_CONTENT_ID,
HEADER_UNKNOWN,
};
static char *headers[] = {
"Content-Type",
"Content-Id",
NULL
};
static gboolean
process_header (GMimeObject *object, const char *header, const char *value)
{
GMimeContentType *content_type;
int i;
for (i = 0; headers[i]; i++) {
if (!g_ascii_strcasecmp (headers[i], header))
break;
}
switch (i) {
case HEADER_CONTENT_TYPE:
content_type = g_mime_content_type_new_from_string (value);
g_mime_object_set_content_type (object, content_type);
break;
case HEADER_CONTENT_ID:
g_free (object->content_id);
object->content_id = g_mime_utils_decode_message_id (value);
break;
default:
return FALSE;
}
g_mime_header_set (object->headers, header, value);
return TRUE;
}
static void
add_header (GMimeObject *object, const char *header, const char *value)
{
if (!process_header (object, header, value))
g_mime_header_add (object->headers, header, value);
}
/**
* g_mime_object_add_header:
* @object: mime object
* @header: header name
* @value: header value
*
* Adds an arbitrary header to the MIME object.
**/
void
g_mime_object_add_header (GMimeObject *object, const char *header, const char *value)
{
g_return_if_fail (GMIME_IS_OBJECT (object));
g_return_if_fail (header != NULL);
g_return_if_fail (value != NULL);
GMIME_OBJECT_GET_CLASS (object)->add_header (object, header, value);
}
static void
set_header (GMimeObject *object, const char *header, const char *value)
{
if (!process_header (object, header, value))
g_mime_header_set (object->headers, header, value);
}
/**
* g_mime_object_set_header:
* @object: mime object
* @header: header name
* @value: header value
*
* Sets an arbitrary header on the MIME object.
**/
void
g_mime_object_set_header (GMimeObject *object, const char *header, const char *value)
{
g_return_if_fail (GMIME_IS_OBJECT (object));
g_return_if_fail (header != NULL);
g_return_if_fail (value != NULL);
GMIME_OBJECT_GET_CLASS (object)->set_header (object, header, value);
}
static const char *
get_header (GMimeObject *object, const char *header)
{
return g_mime_header_get (object->headers, header);
}
/**
* g_mime_object_get_header:
* @object: mime object
* @header: header name
*
* Gets the value of the requested header if it exists or %NULL
* otherwise.
*
* Returns the value of the header @header if it exists or %NULL
* otherwise.
**/
const char *
g_mime_object_get_header (GMimeObject *object, const char *header)
{
g_return_val_if_fail (GMIME_IS_OBJECT (object), NULL);
g_return_val_if_fail (header != NULL, NULL);
return GMIME_OBJECT_GET_CLASS (object)->get_header (object, header);
}
static void
remove_header (GMimeObject *object, const char *header)
{
g_mime_header_remove (object->headers, header);
}
/**
* g_mime_object_remove_header:
* @object: mime object
* @header: header name
*
* Removed the specified header if it exists.
**/
void
g_mime_object_remove_header (GMimeObject *object, const char *header)
{
g_return_if_fail (GMIME_IS_OBJECT (object));
g_return_if_fail (header != NULL);
GMIME_OBJECT_GET_CLASS (object)->remove_header (object, header);
}
static char *
get_headers (GMimeObject *object)
{
return g_mime_header_to_string (object->headers);
}
/**
* g_mime_object_get_headers:
* @object: mime object
*
* Allocates a string buffer containing all of the MIME object's raw
* headers.
*
* Returns an allocated string containing all of the raw MIME headers.
**/
char *
g_mime_object_get_headers (GMimeObject *object)
{
g_return_val_if_fail (GMIME_IS_OBJECT (object), NULL);
return GMIME_OBJECT_GET_CLASS (object)->get_headers (object);
}
static ssize_t
write_to_stream (GMimeObject *object, GMimeStream *stream)
{
return -1;
}
/**
* g_mime_object_write_to_stream:
* @object: mime object
* @stream: stream
*
* Write the contents of the MIME object to @stream.
*
* Returns -1 on fail.
**/
ssize_t
g_mime_object_write_to_stream (GMimeObject *object, GMimeStream *stream)
{
g_return_val_if_fail (GMIME_IS_OBJECT (object), -1);
g_return_val_if_fail (GMIME_IS_STREAM (stream), -1);
return GMIME_OBJECT_GET_CLASS (object)->write_to_stream (object, stream);
}
/**
* g_mime_object_to_string:
* @object: mime object
*
* Allocates a string buffer containing the contents of @object.
*
* Returns an allocated string containing the contents of the mime
* object.
**/
char *
g_mime_object_to_string (GMimeObject *object)
{
GMimeStream *stream;
GByteArray *array;
char *str;
g_return_val_if_fail (GMIME_IS_OBJECT (object), NULL);
array = g_byte_array_new ();
stream = g_mime_stream_mem_new ();
g_mime_stream_mem_set_byte_array (GMIME_STREAM_MEM (stream), array);
g_mime_object_write_to_stream (object, stream);
g_object_unref (stream);
g_byte_array_append (array, (unsigned char *) "", 1);
str = (char *) array->data;
g_byte_array_free (array, FALSE);
return str;
}
static void
subtype_bucket_foreach (gpointer key, gpointer value, gpointer user_data)
{
struct _subtype_bucket *bucket = value;
g_free (bucket->subtype);
g_free (bucket);
}
static void
type_bucket_foreach (gpointer key, gpointer value, gpointer user_data)
{
struct _type_bucket *bucket = value;
g_free (bucket->type);
if (bucket->subtype_hash) {
g_hash_table_foreach (bucket->subtype_hash, subtype_bucket_foreach, NULL);
g_hash_table_destroy (bucket->subtype_hash);
}
g_free (bucket);
}
static void
type_registry_shutdown (void)
{
g_hash_table_foreach (type_hash, type_bucket_foreach, NULL);
g_hash_table_destroy (type_hash);
}
static void
type_registry_init (void)
{
if (type_hash)
return;
type_hash = g_hash_table_new (g_mime_strcase_hash, g_mime_strcase_equal);
g_atexit (type_registry_shutdown);
}
syntax highlighted by Code2HTML, v. 0.9.1