aboutsummaryrefslogtreecommitdiffstats
path: root/camel
diff options
context:
space:
mode:
authorEttore Perazzoli <ettore@src.gnome.org>1999-11-17 22:39:25 +0800
committerEttore Perazzoli <ettore@src.gnome.org>1999-11-17 22:39:25 +0800
commitca7044930f42a698fd88f914c2512a20e2eeaae9 (patch)
tree522962a77005d741097713e87de1d50353b17768 /camel
parent16de3313b700cb56ab9e829d1e9b7e7d2c81241b (diff)
downloadgsoc2013-evolution-ca7044930f42a698fd88f914c2512a20e2eeaae9.tar
gsoc2013-evolution-ca7044930f42a698fd88f914c2512a20e2eeaae9.tar.gz
gsoc2013-evolution-ca7044930f42a698fd88f914c2512a20e2eeaae9.tar.bz2
gsoc2013-evolution-ca7044930f42a698fd88f914c2512a20e2eeaae9.tar.lz
gsoc2013-evolution-ca7044930f42a698fd88f914c2512a20e2eeaae9.tar.xz
gsoc2013-evolution-ca7044930f42a698fd88f914c2512a20e2eeaae9.tar.zst
gsoc2013-evolution-ca7044930f42a698fd88f914c2512a20e2eeaae9.zip
Added streaming capability to CamelDataWrapper. This makes it possible, for
example, to build multipart messages out of files that are on disk without loading them in memory. svn path=/trunk/; revision=1394
Diffstat (limited to 'camel')
-rw-r--r--camel/Makefile.am5
-rw-r--r--camel/camel-data-wrapper.c13
-rw-r--r--camel/camel-data-wrapper.h2
-rw-r--r--camel/camel-mime-message.c5
-rw-r--r--camel/camel-mime-part.c102
-rw-r--r--camel/camel-mime-part.h26
-rw-r--r--camel/camel-simple-data-wrapper-stream.c276
-rw-r--r--camel/camel-simple-data-wrapper-stream.h74
-rw-r--r--camel/camel-simple-data-wrapper.c34
-rw-r--r--camel/camel-simple-data-wrapper.h2
-rw-r--r--camel/camel-stream-data-wrapper.c209
-rw-r--r--camel/camel-stream-data-wrapper.h75
-rw-r--r--camel/gmime-base64.c21
-rw-r--r--camel/gmime-base64.h16
14 files changed, 805 insertions, 55 deletions
diff --git a/camel/Makefile.am b/camel/Makefile.am
index 0f5a4227b2..63303f4384 100644
--- a/camel/Makefile.am
+++ b/camel/Makefile.am
@@ -36,6 +36,8 @@ libcamel_la_SOURCES = \
camel-data-wrapper.c \
camel-exception.c \
camel-simple-data-wrapper.c \
+ camel-simple-data-wrapper-stream.c \
+ camel-stream-data-wrapper.c \
camel-folder.c \
camel-folder-summary.c \
camel-medium.c \
@@ -56,6 +58,7 @@ libcamel_la_SOURCES = \
camel-stream-fs.c \
camel-stream-mem.c \
data-wrapper-repository.c \
+ gmime-base64.c \
gmime-content-field.c \
gmime-utils.c \
gstring-util.c \
@@ -71,6 +74,8 @@ libcamelinclude_HEADERS = \
camel-data-wrapper.h \
camel-exception.h \
camel-simple-data-wrapper.h \
+ camel-simple-data-wrapper-stream.h \
+ camel-stream-data-wrapper.h \
camel-folder.h \
camel-folder-summary.h \
camel-mime-body-part.h \
diff --git a/camel/camel-data-wrapper.c b/camel/camel-data-wrapper.c
index 35d34051de..5c5ac86eef 100644
--- a/camel/camel-data-wrapper.c
+++ b/camel/camel-data-wrapper.c
@@ -41,6 +41,7 @@ static void _set_mime_type (CamelDataWrapper *data_wrapper, gchar *mime_type);
static gchar *_get_mime_type (CamelDataWrapper *data_wrapper);
static GMimeContentField *_get_mime_type_field (CamelDataWrapper *data_wrapper);
static void _set_mime_type_field (CamelDataWrapper *data_wrapper, GMimeContentField *mime_type);
+static CamelStream *_get_stream (CamelDataWrapper *data_wrapper);
static void _finalize (GtkObject *object);
static void
@@ -57,6 +58,7 @@ camel_data_wrapper_class_init (CamelDataWrapperClass *camel_data_wrapper_class)
camel_data_wrapper_class->get_mime_type = _get_mime_type;
camel_data_wrapper_class->get_mime_type_field = _get_mime_type_field;
camel_data_wrapper_class->set_mime_type_field = _set_mime_type_field;
+ camel_data_wrapper_class->get_stream = _get_stream;
/* virtual method overload */
gtk_object_class->finalize = _finalize;
@@ -236,4 +238,15 @@ camel_data_wrapper_set_mime_type_field (CamelDataWrapper *data_wrapper, GMimeCon
CDW_CLASS(data_wrapper)->set_mime_type_field (data_wrapper, mime_type);
}
+static CamelStream *
+_get_stream (CamelDataWrapper *data_wrapper)
+{
+ /* This needs to be implemented in subclasses. */
+ return NULL;
+}
+CamelStream *
+camel_data_wrapper_get_stream (CamelDataWrapper *data_wrapper)
+{
+ CDW_CLASS(data_wrapper)->get_stream (data_wrapper);
+}
diff --git a/camel/camel-data-wrapper.h b/camel/camel-data-wrapper.h
index 2f56ed2000..e5a97f0f78 100644
--- a/camel/camel-data-wrapper.h
+++ b/camel/camel-data-wrapper.h
@@ -66,6 +66,7 @@ typedef struct {
gchar * (*get_mime_type) (CamelDataWrapper *data_wrapper);
GMimeContentField * (*get_mime_type_field) (CamelDataWrapper *data_wrapper);
void (*set_mime_type_field) (CamelDataWrapper *data_wrapper, GMimeContentField *mime_type_field);
+ CamelStream * (*get_stream) (CamelDataWrapper *data_wrapper);
} CamelDataWrapperClass;
@@ -82,6 +83,7 @@ void camel_data_wrapper_set_mime_type (CamelDataWrapper *data_wrapper, gchar *mi
gchar *camel_data_wrapper_get_mime_type (CamelDataWrapper *data_wrapper);
GMimeContentField *camel_data_wrapper_get_mime_type_field (CamelDataWrapper *data_wrapper);
void camel_data_wrapper_set_mime_type_field (CamelDataWrapper *data_wrapper, GMimeContentField *mime_type);
+CamelStream *camel_data_wrapper_get_stream (CamelDataWrapper *data_wrapper);
#ifdef __cplusplus
}
diff --git a/camel/camel-mime-message.c b/camel/camel-mime-message.c
index 582f9265fe..4742dea638 100644
--- a/camel/camel-mime-message.c
+++ b/camel/camel-mime-message.c
@@ -599,6 +599,11 @@ _write_to_stream (CamelDataWrapper *data_wrapper, CamelStream *stream)
WHPT (stream, "Date", mm->received_date);
CAMEL_LOG_FULL_DEBUG ( "CamelMimeMessage:: Writing \"Subject\"\n");
WHPT (stream, "Subject", mm->subject);
+
+ /* FIXME correct to do it here? */
+ CAMEL_LOG_FULL_DEBUG ( "CamelMimeMessage:: Writing \"Mime-Version\"\n");
+ WHPT (stream, "Mime-Version:", "1.0");
+
CAMEL_DATA_WRAPPER_CLASS (parent_class)->write_to_stream (data_wrapper, stream);
}
diff --git a/camel/camel-mime-part.c b/camel/camel-mime-part.c
index 180a843c02..fbf7c89591 100644
--- a/camel/camel-mime-part.c
+++ b/camel/camel-mime-part.c
@@ -60,7 +60,7 @@ static void _finalize (GtkObject *object);
/* from CamelDataWrapper */
static void _write_to_stream (CamelDataWrapper *data_wrapper, CamelStream *stream);
-void _construct_from_stream (CamelDataWrapper *data_wrapper, CamelStream *stream);
+static void _construct_from_stream (CamelDataWrapper *data_wrapper, CamelStream *stream);
/* from CamelMedia */
static void _add_header (CamelMedium *medium, gchar *header_name, gchar *header_value);
@@ -79,8 +79,8 @@ static void _set_content_id (CamelMimePart *mime_part, gchar *content_id);
static const gchar *_get_content_id (CamelMimePart *mime_part);
static void _set_content_MD5 (CamelMimePart *mime_part, gchar *content_MD5);
static const gchar *_get_content_MD5 (CamelMimePart *mime_part);
-static void _set_encoding (CamelMimePart *mime_part, gchar *encoding);
-static const gchar *_get_encoding (CamelMimePart *mime_part);
+static void _set_encoding (CamelMimePart *mime_part, CamelMimePartEncodingType encoding);
+static CamelMimePartEncodingType _get_encoding (CamelMimePart *mime_part);
static void _set_content_languages (CamelMimePart *mime_part, GList *content_languages);
static const GList *_get_content_languages (CamelMimePart *mime_part);
static void _set_header_lines (CamelMimePart *mime_part, GList *header_lines);
@@ -162,7 +162,7 @@ camel_mime_part_init (gpointer object, gpointer klass)
camel_mime_part->content_id = NULL;
camel_mime_part->content_MD5 = NULL;
camel_mime_part->content_languages = NULL;
- camel_mime_part->encoding = NULL;
+ camel_mime_part->encoding = CAMEL_MIME_PART_ENCODING_DEFAULT;
camel_mime_part->filename = NULL;
camel_mime_part->header_lines = NULL;
@@ -214,7 +214,6 @@ _finalize (GtkObject *object)
g_free (mime_part->content_id);
g_free (mime_part->content_MD5);
string_list_free (mime_part->content_languages);
- g_free (mime_part->encoding);
g_free (mime_part->filename);
if (mime_part->header_lines) string_list_free (mime_part->header_lines);
@@ -416,14 +415,14 @@ camel_mime_part_get_content_MD5 (CamelMimePart *mime_part)
static void
-_set_encoding (CamelMimePart *mime_part, gchar *encoding)
+_set_encoding (CamelMimePart *mime_part, CamelMimePartEncodingType encoding)
{
- g_free(mime_part->encoding);
mime_part->encoding = encoding;
}
void
-camel_mime_part_set_encoding (CamelMimePart *mime_part, gchar *encoding)
+camel_mime_part_set_encoding (CamelMimePart *mime_part,
+ CamelMimePartEncodingType encoding)
{
CMP_CLASS(mime_part)->set_encoding (mime_part, encoding);
}
@@ -433,13 +432,13 @@ camel_mime_part_set_encoding (CamelMimePart *mime_part, gchar *encoding)
-static const gchar *
+static CamelMimePartEncodingType
_get_encoding (CamelMimePart *mime_part)
{
return mime_part->encoding;
}
-const gchar *
+const CamelMimePartEncodingType
camel_mime_part_get_encoding (CamelMimePart *mime_part)
{
return CMP_CLASS(mime_part)->get_encoding (mime_part);
@@ -620,6 +619,7 @@ static void
_write_content_to_stream (CamelMimePart *mime_part, CamelStream *stream)
{
CamelMedium *medium = CAMEL_MEDIUM (mime_part);
+ CamelStream *wrapper_stream;
guint buffer_size;
gchar *buffer;
gchar *encoded_buffer;
@@ -629,23 +629,27 @@ _write_content_to_stream (CamelMimePart *mime_part, CamelStream *stream)
CAMEL_LOG_FULL_DEBUG ( "CamelMimePart::_write_content_to_stream, content=%p\n", content);
if (!content) return;
- /* buffer_size = camel_data_wrapper_size (content); */
- /* buffer = g_malloc (buffer_size); */
-
- camel_data_wrapper_write_to_stream (content, stream);
-
-#if 0
- //if (mime_part->encoding) {
- // encoded_buffer_size = gmime_encoded_size(buffer, buffer_size, encoding);
- // encoded_buffer = g_malloc (encoded_buffer_size);
- // gmime_encode_buffer (buffer, encoded_buffer, encoding);
- // camel_stream_write (stream, encoded_buffer, encoded_buffer_size);
- // g_free (encoded_buffer);
- //} else
- //fwrite (buffer, buffer_size, 1, file);
- //camel_stream_write (stream, buffer, buffer_size);
- //g_free (buffer);
-#endif
+ switch (mime_part->encoding) {
+ case CAMEL_MIME_PART_ENCODING_DEFAULT:
+ case CAMEL_MIME_PART_ENCODING_7BIT:
+ case CAMEL_MIME_PART_ENCODING_8BIT:
+ camel_data_wrapper_write_to_stream (content, stream);
+ break;
+ case CAMEL_MIME_PART_ENCODING_BASE64:
+ wrapper_stream = camel_data_wrapper_get_stream (content);
+ if (wrapper_stream == NULL) {
+ /* FIXME in this case, we should probably copy stuff
+ in-memory and make sure things work anyway. */
+ g_warning ("Class `%s' does not implement `get_stream'",
+ gtk_type_name (GTK_OBJECT (content)->klass->type));
+ }
+ gmime_encode_base64 (wrapper_stream, stream);
+ break;
+ default:
+ g_warning ("Encoding type `%s' not supported.",
+ camel_mime_part_encoding_to_string
+ (mime_part->encoding));
+ }
CAMEL_LOG_FULL_DEBUG ( "Leaving CamelMimePart::_write_content_to_stream\n");
}
@@ -665,7 +669,8 @@ _write_to_stream (CamelDataWrapper *data_wrapper, CamelStream *stream)
CAMEL_LOG_FULL_DEBUG ( "CamelMimePart::write_to_stream writing content-disposition\n");
gmime_content_field_write_to_stream(mp->disposition, stream);
CAMEL_LOG_FULL_DEBUG ( "CamelMimePart::write_to_stream writing content-transfer-encoding\n");
- WHPT (stream, "Content-Transfer-Encoding", mp->encoding);
+ WHPT (stream, "Content-Transfer-Encoding",
+ camel_mime_part_encoding_to_string (mp->encoding));
CAMEL_LOG_FULL_DEBUG ( "CamelMimePart::write_to_stream writing content-description\n");
WHPT (stream, "Content-Description", mp->description);
CAMEL_LOG_FULL_DEBUG ( "CamelMimePart::write_to_stream writing content-MD5\n");
@@ -733,7 +738,9 @@ _parse_header_pair (CamelMimePart *mime_part, gchar *header_name, gchar *header_
"CamelMimePart::parse_header_pair found HEADER_ENCODING: %s\n",
header_value);
- camel_mime_part_set_encoding (mime_part, header_value);
+ camel_mime_part_set_encoding
+ (mime_part,
+ camel_mime_part_encoding_from_string (header_value));
header_handled = TRUE;
break;
@@ -767,7 +774,7 @@ _parse_header_pair (CamelMimePart *mime_part, gchar *header_name, gchar *header_
}
-void
+static void
_construct_from_stream (CamelDataWrapper *data_wrapper, CamelStream *stream)
{
@@ -782,6 +789,41 @@ _construct_from_stream (CamelDataWrapper *data_wrapper, CamelStream *stream)
}
+const gchar *
+camel_mime_part_encoding_to_string (CamelMimePartEncodingType encoding)
+{
+ switch (encoding) {
+ case CAMEL_MIME_PART_ENCODING_DEFAULT:
+ case CAMEL_MIME_PART_ENCODING_7BIT:
+ return "7bit";
+ case CAMEL_MIME_PART_ENCODING_8BIT:
+ return "8bit";
+ case CAMEL_MIME_PART_ENCODING_BASE64:
+ return "base64";
+ case CAMEL_MIME_PART_ENCODING_QUOTEDPRINTABLE:
+ return "quoted-printable";
+ }
+}
+
+
+/* FIXME I am not sure this is the correct way to do this. */
+CamelMimePartEncodingType
+camel_mime_part_encoding_from_string (const gchar *string)
+{
+ if (strncmp (string, "7bit") == 0)
+ return CAMEL_MIME_PART_ENCODING_7BIT;
+ else if (strncmp (string, "8bit") == 0)
+ return CAMEL_MIME_PART_ENCODING_8BIT;
+ else if (strncmp (string, "base64") == 0)
+ return CAMEL_MIME_PART_ENCODING_BASE64;
+ else if (strncmp (string, "quoted-printable") == 0)
+ return CAMEL_MIME_PART_ENCODING_QUOTEDPRINTABLE;
+ else
+ /* FIXME? Spit a warning? */
+ return CAMEL_MIME_PART_ENCODING_DEFAULT;
+}
+
+
/******************************/
/** Misc utility functions **/
diff --git a/camel/camel-mime-part.h b/camel/camel-mime-part.h
index 37766568f5..375d4aa0e3 100644
--- a/camel/camel-mime-part.h
+++ b/camel/camel-mime-part.h
@@ -45,6 +45,17 @@ extern "C" {
#define IS_CAMEL_MIME_PART(o) (GTK_CHECK_TYPE((o), CAMEL_MIME_PART_TYPE))
+enum _CamelMimePartEncodingType {
+ CAMEL_MIME_PART_ENCODING_DEFAULT,
+ CAMEL_MIME_PART_ENCODING_7BIT,
+ CAMEL_MIME_PART_ENCODING_8BIT,
+ CAMEL_MIME_PART_ENCODING_BASE64,
+ CAMEL_MIME_PART_ENCODING_QUOTEDPRINTABLE,
+ CAMEL_MIME_PART_NUM_ENCODINGS
+};
+typedef enum _CamelMimePartEncodingType CamelMimePartEncodingType;
+
+
/* Do not change these values directly, you
would regret it one day */
typedef struct
@@ -57,7 +68,7 @@ typedef struct
gchar *content_id;
gchar *content_MD5;
GList *content_languages;
- gchar *encoding;
+ CamelMimePartEncodingType encoding;
gchar *filename;
GList *header_lines;
@@ -82,8 +93,8 @@ typedef struct {
const gchar * (*get_content_id) (CamelMimePart *mime_part);
void (*set_content_MD5) (CamelMimePart *mime_part, gchar *content_MD5);
const gchar * (*get_content_MD5) (CamelMimePart *mime_part);
- void (*set_encoding) (CamelMimePart *mime_part, gchar *encoding);
- const gchar * (*get_encoding) (CamelMimePart *mime_part);
+ void (*set_encoding) (CamelMimePart *mime_part, CamelMimePartEncodingType type);
+ const CamelMimePartEncodingType (*get_encoding) (CamelMimePart *mime_part);
void (*set_content_languages) (CamelMimePart *mime_part, GList *content_languages);
const GList * (*get_content_languages) (CamelMimePart *mime_part);
void (*set_header_lines) (CamelMimePart *mime_part, GList *header_lines);
@@ -116,8 +127,8 @@ const gchar *camel_mime_part_get_filename (CamelMimePart *mime_part);
const gchar *camel_mime_part_get_content_id (CamelMimePart *mime_part);
const gchar *camel_mime_part_get_content_MD5 (CamelMimePart *mime_part);
void camel_mime_part_set_encoding (CamelMimePart *mime_part,
- gchar *encoding);
-const gchar *camel_mime_part_get_encoding (CamelMimePart *mime_part);
+ CamelMimePartEncodingType type);
+CamelMimePartEncodingType camel_mime_part_get_encoding (CamelMimePart *mime_part);
void camel_mime_part_set_content_languages (CamelMimePart *mime_part,
GList *content_languages);
const GList *camel_mime_part_get_content_languages (CamelMimePart *mime_part);
@@ -127,12 +138,13 @@ const GList *camel_mime_part_get_header_lines (CamelMimePart *mime_part);
GMimeContentField *camel_mime_part_get_content_type (CamelMimePart *mime_part);
+const gchar *camel_mime_part_encoding_to_string (CamelMimePartEncodingType encoding);
+CamelMimePartEncodingType camel_mime_part_encoding_from_string (const gchar *string);
+
/* utility functions */
void camel_mime_part_set_text (CamelMimePart *camel_mime_part, const gchar *text);
-
-
#ifdef __cplusplus
}
#endif /* __cplusplus */
diff --git a/camel/camel-simple-data-wrapper-stream.c b/camel/camel-simple-data-wrapper-stream.c
new file mode 100644
index 0000000000..8c45c7530d
--- /dev/null
+++ b/camel/camel-simple-data-wrapper-stream.c
@@ -0,0 +1,276 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* camel-simple-data-wrapper-stream.c
+ *
+ * Copyright 1999 International GNOME Support (http://www.gnome-support.com)
+ *
+ * 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, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Ettore Perazzoli
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "camel-simple-data-wrapper-stream.h"
+
+
+static CamelStreamClass *parent_class = NULL;
+
+
+/* CamelStream methods. */
+
+static gint
+read (CamelStream *stream,
+ gchar *buffer,
+ gint n)
+{
+ CamelSimpleDataWrapperStream *wrapper_stream;
+ CamelSimpleDataWrapper *wrapper;
+ GByteArray *array;
+ gint len;
+
+ wrapper_stream = CAMEL_SIMPLE_DATA_WRAPPER_STREAM (stream);
+ wrapper = wrapper_stream->wrapper;
+ g_return_val_if_fail (wrapper != NULL, -1);
+ array = wrapper->byte_array;
+
+ len = MIN (n, array->len - wrapper_stream->current_position);
+ if (len > 0) {
+ memcpy (buffer, array->data, len);
+ wrapper_stream->current_position += len;
+ return len;
+ } else {
+ return 0;
+ }
+}
+
+static gint
+write (CamelStream *stream,
+ const gchar *buffer,
+ gint n)
+{
+ CamelSimpleDataWrapperStream *wrapper_stream;
+ CamelSimpleDataWrapper *wrapper;
+ GByteArray *array;
+ gint len;
+ const gchar *buffer_next;
+ gint left;
+
+ wrapper_stream = CAMEL_SIMPLE_DATA_WRAPPER_STREAM (stream);
+ wrapper = wrapper_stream->wrapper;
+ g_return_val_if_fail (wrapper != NULL, -1);
+ array = wrapper->byte_array;
+
+ len = MIN (n, array->len - wrapper_stream->current_position);
+ if (len > 0) {
+ memcpy (array->data, buffer, len);
+ buffer_next = buffer + len;
+ left = n - len;
+ } else {
+ /* If we are past the end of the array, fill with zeros. */
+ if (wrapper_stream->current_position > array->len) {
+ gint saved_length;
+
+ saved_length = array->len;
+ g_byte_array_set_size
+ (array, wrapper_stream->current_position);
+ memset (array->data + saved_length,
+ 0,
+ (wrapper_stream->current_position
+ - saved_length));
+ }
+
+ buffer_next = buffer;
+ left = n;
+ }
+
+ if (n > 0)
+ g_byte_array_append (array, buffer_next, left);
+
+ wrapper_stream->current_position += n;
+ return n;
+}
+
+static void
+flush (CamelStream *stream)
+{
+ /* No op, as we don't do any buffering. */
+}
+
+static gint
+available (CamelStream *stream)
+{
+ CamelSimpleDataWrapperStream *wrapper_stream;
+ CamelSimpleDataWrapper *wrapper;
+ GByteArray *array;
+ gint available;
+
+ wrapper_stream = CAMEL_SIMPLE_DATA_WRAPPER_STREAM (stream);
+ wrapper = wrapper_stream->wrapper;
+ g_return_val_if_fail (wrapper != NULL, -1);
+ array = wrapper->byte_array;
+
+ available = array->len - wrapper_stream->current_position;
+ return MAX (available, 0);
+}
+
+static gboolean
+eos (CamelStream *stream)
+{
+ if (available (stream) > 0)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+static void
+close (CamelStream *stream)
+{
+ /* Nothing to do, we have no associated file descriptor. */
+}
+
+static gint
+seek (CamelStream *stream,
+ gint offset,
+ CamelStreamSeekPolicy policy)
+{
+ CamelSimpleDataWrapperStream *wrapper_stream;
+ gint new_position;
+
+ wrapper_stream = CAMEL_SIMPLE_DATA_WRAPPER_STREAM (stream);
+
+ switch (policy) {
+ case CAMEL_STREAM_SET:
+ new_position = offset;
+ break;
+ case CAMEL_STREAM_CUR:
+ new_position = wrapper_stream->current_position + offset;
+ break;
+ case CAMEL_STREAM_END:
+ new_position = wrapper_stream->wrapper->byte_array->len;
+ break;
+ default:
+ g_warning ("Unknown CamelStreamSeekPolicy %d.", policy);
+ return -1;
+ }
+
+ wrapper_stream->current_position = new_position;
+ return new_position;
+}
+
+
+/* This handles destruction of the associated CamelDataWrapper. */
+/* Hm, this should never happen though, because we gtk_object_ref() the
+ wrapper. */
+static void
+wrapper_destroy_cb (GtkObject *object,
+ gpointer data)
+{
+ CamelSimpleDataWrapperStream *stream;
+
+ g_warning ("CamelSimpleDataWrapperStream: associated CamelSimpleDataWrapper was destroyed.");
+ stream = CAMEL_SIMPLE_DATA_WRAPPER_STREAM (object);
+ stream->wrapper = NULL;
+}
+
+
+/* GtkObject methods. */
+
+static void
+destroy (GtkObject *object)
+{
+ CamelSimpleDataWrapperStream *stream;
+
+ stream = CAMEL_SIMPLE_DATA_WRAPPER_STREAM (object);
+
+ gtk_object_unref (GTK_OBJECT (stream->wrapper));
+
+ if (GTK_OBJECT_CLASS (parent_class)->destroy != NULL)
+ (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+
+static void
+class_init (CamelSimpleDataWrapperStreamClass *klass)
+{
+ GtkObjectClass *object_class;
+
+ object_class = (GtkObjectClass*) klass;
+
+ parent_class = gtk_type_class (camel_stream_get_type ());
+}
+
+static void
+init (CamelSimpleDataWrapperStream *simple_data_wrapper_stream)
+{
+ simple_data_wrapper_stream->current_position = 0;
+}
+
+
+GtkType
+camel_simple_data_wrapper_stream_get_type (void)
+{
+ static GtkType type = 0;
+
+ if (type == 0) {
+ static const GtkTypeInfo info = {
+ "CamelSimpleDataWrapperStream",
+ sizeof (CamelSimpleDataWrapperStream),
+ sizeof (CamelSimpleDataWrapperStreamClass),
+ (GtkClassInitFunc) class_init,
+ (GtkObjectInitFunc) init,
+ /* reserved_1 */ NULL,
+ /* reserved_2 */ NULL,
+ (GtkClassInitFunc) NULL,
+ };
+
+ type = gtk_type_unique (camel_stream_get_type (), &info);
+ }
+
+ return type;
+}
+
+void
+camel_simple_data_wrapper_stream_construct (CamelSimpleDataWrapperStream *stream,
+ CamelSimpleDataWrapper *wrapper)
+{
+ g_return_if_fail (stream != NULL);
+ g_return_if_fail (IS_CAMEL_SIMPLE_DATA_WRAPPER_STREAM (stream));
+ g_return_if_fail (wrapper != NULL);
+ g_return_if_fail (IS_CAMEL_SIMPLE_DATA_WRAPPER (wrapper));
+
+ gtk_object_ref (GTK_OBJECT (wrapper));
+ stream->wrapper = wrapper;
+ gtk_signal_connect (GTK_OBJECT (wrapper), "destroy",
+ wrapper_destroy_cb, stream);
+}
+
+CamelStream *
+camel_simple_data_wrapper_stream_new (CamelSimpleDataWrapper *wrapper)
+{
+ CamelStream *stream;
+
+ g_return_val_if_fail (wrapper != NULL, NULL);
+ g_return_val_if_fail (IS_CAMEL_SIMPLE_DATA_WRAPPER (wrapper), NULL);
+
+ stream = gtk_type_new (camel_simple_data_wrapper_stream_get_type ());
+
+ camel_simple_data_wrapper_stream_construct
+ (CAMEL_SIMPLE_DATA_WRAPPER_STREAM (stream), wrapper);
+
+ return stream;
+}
diff --git a/camel/camel-simple-data-wrapper-stream.h b/camel/camel-simple-data-wrapper-stream.h
new file mode 100644
index 0000000000..ee24fd71d3
--- /dev/null
+++ b/camel/camel-simple-data-wrapper-stream.h
@@ -0,0 +1,74 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* camel-simple-data-wrapper-stream.h
+ *
+ * Copyright 1999 International GNOME Support (http://www.gnome-support.com)
+ *
+ * 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, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Ettore Perazzoli
+ */
+
+#ifndef __CAMEL_SIMPLE_DATA_WRAPPER_STREAM_H__
+#define __CAMEL_SIMPLE_DATA_WRAPPER_STREAM_H__
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+
+#include "camel-simple-data-wrapper.h"
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+#define CAMEL_TYPE_SIMPLE_DATA_WRAPPER_STREAM \
+ (camel_simple_data_wrapper_stream_get_type ())
+#define CAMEL_SIMPLE_DATA_WRAPPER_STREAM(obj) \
+ (GTK_CHECK_CAST ((obj), CAMEL_TYPE_SIMPLE_DATA_WRAPPER_STREAM, CamelSimpleDataWrapperStream))
+#define CAMEL_SIMPLE_DATA_WRAPPER_STREAM_CLASS(klass) \
+ (GTK_CHECK_CLASS_CAST ((klass), CAMEL_TYPE_SIMPLE_DATA_WRAPPER_STREAM, CamelSimpleDataWrapperStreamClass))
+#define IS_CAMEL_SIMPLE_DATA_WRAPPER_STREAM(obj) \
+ (GTK_CHECK_TYPE ((obj), CAMEL_TYPE_SIMPLE_DATA_WRAPPER_STREAM))
+#define IS_CAMEL_SIMPLE_DATA_WRAPPER_STREAM_CLASS(klass) \
+ (GTK_CHECK_CLASS_TYPE ((obj), CAMEL_TYPE_SIMPLE_DATA_WRAPPER_STREAM))
+
+
+typedef struct _CamelSimpleDataWrapperStream CamelSimpleDataWrapperStream;
+typedef struct _CamelSimpleDataWrapperStreamClass CamelSimpleDataWrapperStreamClass;
+
+struct _CamelSimpleDataWrapperStream {
+ CamelStream parent;
+
+ CamelSimpleDataWrapper *wrapper;
+ gint current_position;
+};
+
+struct _CamelSimpleDataWrapperStreamClass {
+ CamelStreamClass parent_class;
+};
+
+
+GtkType camel_simple_data_wrapper_stream_get_type (void);
+CamelStream *camel_simple_data_wrapper_stream_new (CamelSimpleDataWrapper *wrapper);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __CAMEL_SIMPLE_DATA_WRAPPER_STREAM_H__ */
diff --git a/camel/camel-simple-data-wrapper.c b/camel/camel-simple-data-wrapper.c
index 073561a1a1..fb1c682814 100644
--- a/camel/camel-simple-data-wrapper.c
+++ b/camel/camel-simple-data-wrapper.c
@@ -25,7 +25,9 @@
* USA
*/
#include <config.h>
+
#include "camel-simple-data-wrapper.h"
+#include "camel-simple-data-wrapper-stream.h"
#include "camel-log.h"
static CamelDataWrapperClass *parent_class=NULL;
@@ -36,6 +38,7 @@ static CamelDataWrapperClass *parent_class=NULL;
static void _construct_from_stream (CamelDataWrapper *data_wrapper, CamelStream *stream);
static void _write_to_stream (CamelDataWrapper *data_wrapper, CamelStream *stream);
static void _finalize (GtkObject *object);
+static CamelStream *_get_stream (CamelDataWrapper *data_wrapper);
static void
camel_simple_data_wrapper_class_init (CamelSimpleDataWrapperClass *camel_simple_data_wrapper_class)
@@ -50,13 +53,17 @@ camel_simple_data_wrapper_class_init (CamelSimpleDataWrapperClass *camel_simple_
camel_data_wrapper_class->write_to_stream = _write_to_stream;
camel_data_wrapper_class->construct_from_stream = _construct_from_stream;
+ camel_data_wrapper_class->get_stream = _get_stream;
+
gtk_object_class->finalize = _finalize;
}
-
-
-
+static void
+camel_simple_data_wrapper_init (CamelSimpleDataWrapper *wrapper)
+{
+ wrapper->stream = NULL;
+}
GtkType
camel_simple_data_wrapper_get_type (void)
@@ -70,7 +77,7 @@ camel_simple_data_wrapper_get_type (void)
sizeof (CamelSimpleDataWrapper),
sizeof (CamelSimpleDataWrapperClass),
(GtkClassInitFunc) camel_simple_data_wrapper_class_init,
- (GtkObjectInitFunc) NULL,
+ (GtkObjectInitFunc) camel_simple_data_wrapper_init,
/* reserved_1 */ NULL,
/* reserved_2 */ NULL,
(GtkClassInitFunc) NULL,
@@ -134,8 +141,6 @@ _write_to_stream (CamelDataWrapper *data_wrapper, CamelStream *stream)
}
-
-
#define _CMSDW_TMP_BUF_SIZE 100
static void
_construct_from_stream (CamelDataWrapper *data_wrapper, CamelStream *stream)
@@ -206,3 +211,20 @@ camel_simple_data_wrapper_set_text (CamelSimpleDataWrapper *simple_data_wrapper,
CAMEL_LOG_FULL_DEBUG ("CamelSimpleDataWrapper:: Entering set_text\n");
}
+
+
+static CamelStream *
+_get_stream (CamelDataWrapper *data_wrapper)
+{
+ CamelSimpleDataWrapper *simple_data_wrapper;
+
+ simple_data_wrapper = CAMEL_SIMPLE_DATA_WRAPPER (data_wrapper);
+ if (simple_data_wrapper->stream == NULL) {
+ CamelStream *s;
+
+ s = camel_simple_data_wrapper_stream_new (simple_data_wrapper);
+ simple_data_wrapper->stream = s;
+ }
+
+ return simple_data_wrapper->stream;
+}
diff --git a/camel/camel-simple-data-wrapper.h b/camel/camel-simple-data-wrapper.h
index b999722cc1..b3be850f9d 100644
--- a/camel/camel-simple-data-wrapper.h
+++ b/camel/camel-simple-data-wrapper.h
@@ -53,7 +53,7 @@ typedef struct
CamelDataWrapper parent_object;
GByteArray *byte_array;
-
+ CamelStream *stream;
} CamelSimpleDataWrapper;
diff --git a/camel/camel-stream-data-wrapper.c b/camel/camel-stream-data-wrapper.c
new file mode 100644
index 0000000000..564571cf09
--- /dev/null
+++ b/camel/camel-stream-data-wrapper.c
@@ -0,0 +1,209 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* camel-stream-data-wrapper.c
+ *
+ * Copyright 1999 International GNOME Support (http://www.gnome-support.com)
+ *
+ * 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, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Ettore Perazzoli
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+
+#include "camel-stream-data-wrapper.h"
+
+
+static CamelDataWrapperClass *parent_class = NULL;
+
+
+/* CamelDataWrapper methods. */
+
+static void
+write_to_stream (CamelDataWrapper *data_wrapper,
+ CamelStream *output_stream)
+{
+#define BUFFER_SIZE 4096
+ gchar buffer[BUFFER_SIZE];
+ CamelStreamDataWrapper *stream_data_wrapper;
+ CamelStream *input_stream;
+
+ stream_data_wrapper = CAMEL_STREAM_DATA_WRAPPER (data_wrapper);
+ input_stream = stream_data_wrapper->stream;
+
+ while (TRUE) {
+ gchar *p;
+ gint read, written;
+
+ read = camel_stream_read (input_stream, buffer, BUFFER_SIZE);
+ if (read == 0)
+ break;
+
+ p = buffer;
+ while (read > 0) {
+ written = camel_stream_write (output_stream, p, read);
+
+ /* FIXME no way to report an error?! */
+ if (written == -1)
+ break;
+
+ p += written;
+ read -= written;
+ }
+ }
+#undef BUFFER_SIZE
+}
+
+static CamelStream *
+get_stream (CamelDataWrapper *data_wrapper)
+{
+ CamelStreamDataWrapper *stream_data_wrapper;
+
+ stream_data_wrapper = CAMEL_STREAM_DATA_WRAPPER (data_wrapper);
+ return stream_data_wrapper->stream;
+}
+
+
+/* GtkObject methods. */
+
+static void
+destroy (GtkObject *object)
+{
+ CamelStreamDataWrapper *stream_data_wrapper;
+ GtkObject *stream_object;
+
+ stream_data_wrapper = CAMEL_STREAM_DATA_WRAPPER (object);
+
+ stream_object = GTK_OBJECT (object);
+ stream_data_wrapper->stream = NULL;
+
+ gtk_object_unref (stream_object);
+}
+
+
+/* This handles destruction of the associated CamelDataWrapper outside
+ CamelStreamDataWrapper, for debuggin purposes (this should never happen). */
+static void
+stream_destroy_cb (GtkObject *object,
+ gpointer data)
+{
+ CamelStreamDataWrapper *wrapper;
+
+ wrapper = CAMEL_STREAM_DATA_WRAPPER (data);
+
+ /* Hack: when we destroy the stream ourselves, we set the `stream'
+ member to NULL first, so that we can recognize when this is done out
+ of our control. */
+ if (wrapper->stream != NULL) {
+ g_warning ("CamelSimpleDataWrapperStream: associated CamelSimpleDataWrapper was destroyed.");
+ wrapper->stream = NULL;
+ }
+}
+
+
+static void
+class_init (CamelStreamDataWrapperClass *class)
+{
+ GtkObjectClass *object_class;
+ CamelDataWrapperClass *data_wrapper_class;
+
+ object_class = GTK_OBJECT_CLASS (class);
+ object_class->destroy = destroy;
+
+ data_wrapper_class = CAMEL_DATA_WRAPPER_CLASS (class);
+ data_wrapper_class->write_to_stream = write_to_stream;
+ data_wrapper_class->get_stream = get_stream;
+
+ parent_class = gtk_type_class (camel_data_wrapper_get_type ());
+}
+
+static void
+init (CamelStreamDataWrapper *wrapper)
+{
+ wrapper->stream = NULL;
+}
+
+
+GtkType
+camel_stream_data_wrapper_get_type (void)
+{
+ static GtkType type = 0;
+
+ if (type == 0) {
+ static const GtkTypeInfo info = {
+ "CamelStreamDataWrapper",
+ sizeof (CamelStreamDataWrapper),
+ sizeof (CamelStreamDataWrapperClass),
+ (GtkClassInitFunc) class_init,
+ (GtkObjectInitFunc) init,
+ /* reserved_1 */ NULL,
+ /* reserved_2 */ NULL,
+ (GtkClassInitFunc) NULL,
+ };
+
+ type = gtk_type_unique (camel_data_wrapper_get_type (), &info);
+ }
+
+ return type;
+}
+
+/**
+ * camel_stream_data_wrapper_construct:
+ * @wrapper: A CamelStreamDataWrapper object
+ * @stream: A Camel stream object
+ *
+ * Construct @wrapper associating @stream to it. Notice that, after this call,
+ * @stream is conceptually owned by @wrapper and will be destroyed when
+ * @wrapper is destroyed.
+ **/
+void
+camel_stream_data_wrapper_construct (CamelStreamDataWrapper *wrapper,
+ CamelStream *stream)
+{
+ g_return_if_fail (wrapper != NULL);
+ g_return_if_fail (IS_CAMEL_STREAM_DATA_WRAPPER (wrapper));
+ g_return_if_fail (stream != NULL);
+ g_return_if_fail (IS_CAMEL_STREAM (stream));
+
+ wrapper->stream = stream;
+ gtk_signal_connect (GTK_OBJECT (stream), "destroy",
+ GTK_SIGNAL_FUNC (stream_destroy_cb), wrapper);
+}
+
+/**
+ * camel_stream_data_wrapper_new:
+ * @stream: A Camel stream object
+ *
+ * Create a new stream data wrapper object for @stream. Notice that, after
+ * this call, @stream is conceptually owned by the new wrapper and will be
+ * destroyed when the wrapper is destroyed.
+ *
+ * Return value: A pointer to the new CamelStreamDataWrapper object.
+ **/
+CamelDataWrapper *
+camel_stream_data_wrapper_new (CamelStream *stream)
+{
+ CamelDataWrapper *wrapper;
+
+ wrapper = gtk_type_new (camel_stream_data_wrapper_get_type ());
+ camel_stream_data_wrapper_construct
+ (CAMEL_STREAM_DATA_WRAPPER (wrapper), stream);
+
+ return wrapper;
+}
diff --git a/camel/camel-stream-data-wrapper.h b/camel/camel-stream-data-wrapper.h
new file mode 100644
index 0000000000..3f972a99f9
--- /dev/null
+++ b/camel/camel-stream-data-wrapper.h
@@ -0,0 +1,75 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* camel-stream-data-wrapper.h
+ *
+ * Copyright 1999 International GNOME Support (http://www.gnome-support.com) .
+ *
+ * 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, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Ettore Perazzoli
+ */
+
+#ifndef __CAMEL_STREAM_DATA_WRAPPER_H__
+#define __CAMEL_STREAM_DATA_WRAPPER_H__
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+#include "camel-data-wrapper.h"
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+
+#define CAMEL_TYPE_STREAM_DATA_WRAPPER \
+ (camel_stream_data_wrapper_get_type ())
+#define CAMEL_STREAM_DATA_WRAPPER(obj) \
+ (GTK_CHECK_CAST ((obj), CAMEL_TYPE_STREAM_DATA_WRAPPER, CamelStreamDataWrapper))
+#define CAMEL_STREAM_DATA_WRAPPER_CLASS(klass) \
+ (GTK_CHECK_CLASS_CAST ((klass), CAMEL_TYPE_STREAM_DATA_WRAPPER, CamelStreamDataWrapperClass))
+#define IS_CAMEL_STREAM_DATA_WRAPPER(obj) \
+ (GTK_CHECK_TYPE ((obj), CAMEL_TYPE_STREAM_DATA_WRAPPER))
+#define IS_CAMEL_STREAM_DATA_WRAPPER_CLASS(klass) \
+ (GTK_CHECK_CLASS_TYPE ((obj), CAMEL_TYPE_STREAM_DATA_WRAPPER))
+
+
+typedef struct _CamelStreamDataWrapper CamelStreamDataWrapper;
+typedef struct _CamelStreamDataWrapperClass CamelStreamDataWrapperClass;
+
+struct _CamelStreamDataWrapper {
+ CamelDataWrapper parent;
+
+ CamelStream *stream;
+};
+
+struct _CamelStreamDataWrapperClass {
+ CamelDataWrapperClass parent_class;
+};
+
+
+GtkType camel_stream_data_wrapper_get_type (void);
+CamelDataWrapper *camel_stream_data_wrapper_new (CamelStream *stream);
+void camel_stream_data_wrapper_construct (CamelStreamDataWrapper *wrapper,
+ CamelStream *stream);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __CAMEL_STREAM_DATA_WRAPPER_H__ */
diff --git a/camel/gmime-base64.c b/camel/gmime-base64.c
index a9e596d2dc..db784f7143 100644
--- a/camel/gmime-base64.c
+++ b/camel/gmime-base64.c
@@ -30,15 +30,12 @@ gmime_encode_base64 (CamelStream *input, CamelStream *output)
char buffer [BSIZE];
char obuf [80]; /* Output is limited to 76 characters, rfc2045 */
int n, i, j, state;
- long bytes;
-
- j = 0;
+ int keep = 0;
+
state = 0;
- bytes = 0;
- while ((n = camel_stream_read (input, &buffer, sizeof (buffer))) > 0){
- int keep = 0;
-
- for (i = 0; i < n; i++, state++, bytes++){
+ j = 0;
+ while ((n = camel_stream_read (input, buffer, sizeof (buffer))) > 0){
+ for (i = 0; i < n; i++, state++){
char c = buffer [i];
switch (state % 3){
@@ -48,21 +45,23 @@ gmime_encode_base64 (CamelStream *input, CamelStream *output)
break;
case 1:
obuf [j++] = base64_alphabet [keep | (c >> 4)];
- keep = (c & 0xf) << 4;
+ keep = (c & 0xf) << 2;
break;
case 2:
obuf [j++] = base64_alphabet [keep | (c >> 6)];
obuf [j++] = base64_alphabet [c & 0x3f];
break;
}
-
- if ((bytes % 72) == 0){
+
+ if (j == 72){
obuf [j++] = '\r';
obuf [j++] = '\n';
camel_stream_write (output, obuf, j);
+ j = 0;
}
}
}
+
switch (state % 3){
case 0:
/* full ouput, nothing left to do */
diff --git a/camel/gmime-base64.h b/camel/gmime-base64.h
new file mode 100644
index 0000000000..58ddd58722
--- /dev/null
+++ b/camel/gmime-base64.h
@@ -0,0 +1,16 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Base64 handlers
+ *
+ * Author:
+ * Miguel de Icaza (miguel@kernel.org)
+ */
+
+#ifndef _GMIME_BASE64_H
+#define _GMIME_BASE64_H
+
+#include "camel-stream.h"
+
+void gmime_encode_base64 (CamelStream *input, CamelStream *output);
+
+#endif