avro-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From bru...@apache.org
Subject svn commit: r1063596 - in /avro/trunk: ./ lang/c/jansson/src/ lang/c/src/ lang/c/tests/
Date Wed, 26 Jan 2011 03:44:18 GMT
Author: brucem
Date: Wed Jan 26 03:44:18 2011
New Revision: 1063596

URL: http://svn.apache.org/viewvc?rev=1063596&view=rev
Log:
AVRO-741. The avro_give* functions now all take in a custom free function that
will be called once the datum no longer needs access to the underlying
buffer.  This gives complete flexibility over wrapping external buffers
into Avro datum instances.  The avro_wrap* functions have been removed,
since this is now just a special case — just use NULL for the free
function.  To get the previous behavior of avro_give* (where the buffer
is freed using the custom allocator), use avro_alloc_free as the free
function.

In addition to updating the give functions, the binary encoding class
now ensures that any bytes value is NUL terminated in memory.  This
fixes the bug in AVRO-741, where we were getting a segfault when trying
to parse a schema JSON directly from a bytes value.

Modified:
    avro/trunk/CHANGES.txt
    avro/trunk/lang/c/jansson/src/Makefile.am
    avro/trunk/lang/c/src/allocation.c
    avro/trunk/lang/c/src/avro.h
    avro/trunk/lang/c/src/datum.c
    avro/trunk/lang/c/src/datum.h
    avro/trunk/lang/c/src/datum_read.c
    avro/trunk/lang/c/src/encoding_binary.c
    avro/trunk/lang/c/src/schema.c
    avro/trunk/lang/c/tests/generate_interop_data.c
    avro/trunk/lang/c/tests/test_avro_data.c

Modified: avro/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/avro/trunk/CHANGES.txt?rev=1063596&r1=1063595&r2=1063596&view=diff
==============================================================================
--- avro/trunk/CHANGES.txt (original)
+++ avro/trunk/CHANGES.txt Wed Jan 26 03:44:18 2011
@@ -45,6 +45,8 @@ Avro 1.5.0 (unreleased)
     AVRO-696. Java: Make DataFileWriter.setMetaInternal(String,String)
     private. (Patrick Linehan via cutting)    
 
+    AVRO-741. C: Minor API change to handling of bytes data. (Douglas Creager via brucem)
+
   NEW FEATURES
 
     AVRO-684. Java: Add command-line "recodec" tool to change file

Modified: avro/trunk/lang/c/jansson/src/Makefile.am
URL: http://svn.apache.org/viewvc/avro/trunk/lang/c/jansson/src/Makefile.am?rev=1063596&r1=1063595&r2=1063596&view=diff
==============================================================================
--- avro/trunk/lang/c/jansson/src/Makefile.am (original)
+++ avro/trunk/lang/c/jansson/src/Makefile.am Wed Jan 26 03:44:18 2011
@@ -1,6 +1,6 @@
 include_HEADERS = jansson.h
 
-lib_LTLIBRARIES = libjansson.la
+noinst_LTLIBRARIES = libjansson.la
 libjansson_la_SOURCES = \
 	dump.c \
 	hashtable.c \

Modified: avro/trunk/lang/c/src/allocation.c
URL: http://svn.apache.org/viewvc/avro/trunk/lang/c/src/allocation.c?rev=1063596&r1=1063595&r2=1063596&view=diff
==============================================================================
--- avro/trunk/lang/c/src/allocation.c (original)
+++ avro/trunk/lang/c/src/allocation.c Wed Jan 26 03:44:18 2011
@@ -85,3 +85,10 @@ void avro_str_free(char *str)
 	//fprintf(stderr, "--- free %zu %p %s\n", *size, str, str);
 	avro_free(size, *size);
 }
+
+
+void
+avro_alloc_free(void *ptr, size_t sz)
+{
+	avro_free(ptr, sz);
+}

Modified: avro/trunk/lang/c/src/avro.h
URL: http://svn.apache.org/viewvc/avro/trunk/lang/c/src/avro.h?rev=1063596&r1=1063595&r2=1063596&view=diff
==============================================================================
--- avro/trunk/lang/c/src/avro.h (original)
+++ avro/trunk/lang/c/src/avro.h Wed Jan 26 03:44:18 2011
@@ -211,14 +211,30 @@ void avro_writer_free(avro_writer_t writ
  * datum 
  */
 
+/**
+ * A function used to free a bytes, string, or fixed buffer once it is
+ * no longer needed by the datum that wraps it.
+ */
+
+typedef void
+(*avro_free_func_t)(void *ptr, size_t sz);
+
+/**
+ * An avro_free_func_t that frees the buffer using the custom allocator
+ * provided to avro_set_allocator.
+ */
+
+void
+avro_alloc_free(void *ptr, size_t sz);
+
 /* constructors */
 typedef struct avro_obj_t *avro_datum_t;
 avro_datum_t avro_string(const char *str);
-avro_datum_t avro_wrapstring(const char *str);
-avro_datum_t avro_givestring(const char *str);
+avro_datum_t avro_givestring(const char *str,
+			     avro_free_func_t free);
 avro_datum_t avro_bytes(const char *buf, int64_t len);
-avro_datum_t avro_wrapbytes(const char *buf, int64_t len);
-avro_datum_t avro_givebytes(const char *buf, int64_t len);
+avro_datum_t avro_givebytes(const char *buf, int64_t len,
+			    avro_free_func_t free);
 avro_datum_t avro_int32(int32_t i);
 avro_datum_t avro_int64(int64_t l);
 avro_datum_t avro_float(float f);
@@ -229,10 +245,9 @@ avro_datum_t avro_record(const char *nam
 avro_datum_t avro_enum(const char *name, int i);
 avro_datum_t avro_fixed(const char *name, const char *bytes,
 			const int64_t size);
-avro_datum_t avro_wrapfixed(const char *name, const char *bytes,
-			    const int64_t size);
 avro_datum_t avro_givefixed(const char *name, const char *bytes,
-			    const int64_t size);
+			    const int64_t size,
+			    avro_free_func_t free);
 avro_datum_t avro_map(void);
 avro_datum_t avro_array(void);
 avro_datum_t avro_union(int64_t discriminant, const avro_datum_t datum);
@@ -283,14 +298,13 @@ avro_datum_t avro_union_current_branch(a
 
 /* setters */
 int avro_string_set(avro_datum_t datum, const char *p);
-int avro_givestring_set(avro_datum_t datum, const char *p);
-int avro_wrapstring_set(avro_datum_t datum, const char *p);
+int avro_givestring_set(avro_datum_t datum, const char *p,
+			avro_free_func_t free);
 
 int avro_bytes_set(avro_datum_t datum, const char *bytes, const int64_t size);
 int avro_givebytes_set(avro_datum_t datum, const char *bytes,
-		       const int64_t size);
-int avro_wrapbytes_set(avro_datum_t datum, const char *bytes,
-		       const int64_t size);
+		       const int64_t size,
+		       avro_free_func_t free);
 
 int avro_int32_set(avro_datum_t datum, const int32_t i);
 int avro_int64_set(avro_datum_t datum, const int64_t l);
@@ -303,9 +317,8 @@ int avro_enum_set_name(avro_datum_t datu
 		       const char *symbol_name);
 int avro_fixed_set(avro_datum_t datum, const char *bytes, const int64_t size);
 int avro_givefixed_set(avro_datum_t datum, const char *bytes,
-		       const int64_t size);
-int avro_wrapfixed_set(avro_datum_t datum, const char *bytes,
-		       const int64_t size);
+		       const int64_t size,
+		       avro_free_func_t free);
 
 int avro_record_set(avro_datum_t record, const char *field_name,
 		    avro_datum_t value);

Modified: avro/trunk/lang/c/src/datum.c
URL: http://svn.apache.org/viewvc/avro/trunk/lang/c/src/datum.c?rev=1063596&r1=1063595&r2=1063596&view=diff
==============================================================================
--- avro/trunk/lang/c/src/datum.c (original)
+++ avro/trunk/lang/c/src/datum.c Wed Jan 26 03:44:18 2011
@@ -32,18 +32,10 @@ static void avro_datum_init(avro_datum_t
 	datum->refcount = 1;
 }
 
-/* need this since avro_free is a macro */
-static void
-avro_free_wrapper(void *ptr, size_t sz)
-{
-	avro_free(ptr, sz);
-}
-
 static void
 avro_str_free_wrapper(void *ptr, size_t sz)
 {
-	// don't need this, since the size is stored in the string
-	// buffer
+	// don't need sz, since the size is stored in the string buffer
 	AVRO_UNUSED(sz);
 	avro_str_free(ptr);
 }
@@ -73,15 +65,11 @@ avro_datum_t avro_string(const char *str
 	return avro_string_private(p, 0, avro_str_free_wrapper);
 }
 
-avro_datum_t avro_givestring(const char *str)
+avro_datum_t avro_givestring(const char *str,
+			     avro_free_func_t free)
 {
 	int64_t  sz = strlen(str)+1;
-	return avro_string_private((char *)str, sz, avro_free_wrapper);
-}
-
-avro_datum_t avro_wrapstring(const char *str)
-{
-	return avro_string_private((char *)str, 0, NULL);
+	return avro_string_private((char *)str, sz, free);
 }
 
 int avro_string_get(avro_datum_t datum, char **p)
@@ -126,15 +114,11 @@ int avro_string_set(avro_datum_t datum, 
 	return rval;
 }
 
-int avro_givestring_set(avro_datum_t datum, const char *p)
+int avro_givestring_set(avro_datum_t datum, const char *p,
+			avro_free_func_t free)
 {
 	int64_t  size = strlen(p)+1;
-	return avro_string_set_private(datum, p, size, avro_free_wrapper);
-}
-
-int avro_wrapstring_set(avro_datum_t datum, const char *p)
-{
-	return avro_string_set_private(datum, p, 0, NULL);
+	return avro_string_set_private(datum, p, size, free);
 }
 
 static avro_datum_t avro_bytes_private(char *bytes, int64_t size,
@@ -161,21 +145,17 @@ avro_datum_t avro_bytes(const char *byte
 	}
 	memcpy(bytes_copy, bytes, size);
 	avro_datum_t  result =
-		avro_bytes_private(bytes_copy, size, avro_free_wrapper);
+		avro_bytes_private(bytes_copy, size, avro_alloc_free);
 	if (result == NULL) {
 		avro_free(bytes_copy, size);
 	}
 	return result;
 }
 
-avro_datum_t avro_givebytes(const char *bytes, int64_t size)
-{
-	return avro_bytes_private((char *)bytes, size, avro_free_wrapper);
-}
-
-avro_datum_t avro_wrapbytes(const char *bytes, int64_t size)
+avro_datum_t avro_givebytes(const char *bytes, int64_t size,
+			    avro_free_func_t free)
 {
-	return avro_bytes_private((char *)bytes, size, NULL);
+	return avro_bytes_private((char *)bytes, size, free);
 }
 
 static int avro_bytes_set_private(avro_datum_t datum, const char *bytes,
@@ -207,7 +187,7 @@ int avro_bytes_set(avro_datum_t datum, c
 		return ENOMEM;
 	}
 	memcpy(bytes_copy, bytes, size);
-	rval = avro_bytes_set_private(datum, bytes_copy, size, avro_free_wrapper);
+	rval = avro_bytes_set_private(datum, bytes_copy, size, avro_alloc_free);
 	if (rval) {
 		avro_free(bytes_copy, size);
 	}
@@ -215,15 +195,9 @@ int avro_bytes_set(avro_datum_t datum, c
 }
 
 int avro_givebytes_set(avro_datum_t datum, const char *bytes,
-		       const int64_t size)
-{
-	return avro_bytes_set_private(datum, bytes, size, avro_free_wrapper);
-}
-
-int avro_wrapbytes_set(avro_datum_t datum, const char *bytes,
-		       const int64_t size)
+		       const int64_t size, avro_free_func_t free)
 {
-	return avro_bytes_set_private(datum, bytes, size, NULL);
+	return avro_bytes_set_private(datum, bytes, size, free);
 }
 
 int avro_bytes_get(avro_datum_t datum, char **bytes, int64_t * size)
@@ -644,19 +618,13 @@ avro_datum_t avro_fixed(const char *name
 		return NULL;
 	}
 	memcpy(bytes_copy, bytes, size);
-	return avro_fixed_private(name, bytes_copy, size, avro_free_wrapper);
-}
-
-avro_datum_t avro_wrapfixed(const char *name, const char *bytes,
-			    const int64_t size)
-{
-	return avro_fixed_private(name, bytes, size, NULL);
+	return avro_fixed_private(name, bytes_copy, size, avro_alloc_free);
 }
 
 avro_datum_t avro_givefixed(const char *name, const char *bytes,
-			    const int64_t size)
+			    const int64_t size, avro_free_func_t free)
 {
-	return avro_fixed_private(name, bytes, size, avro_free_wrapper);
+	return avro_fixed_private(name, bytes, size, free);
 }
 
 static int avro_fixed_set_private(avro_datum_t datum, const char *bytes,
@@ -690,7 +658,7 @@ int avro_fixed_set(avro_datum_t datum, c
 		return ENOMEM;
 	}
 	memcpy(bytes_copy, bytes, size);
-	rval = avro_fixed_set_private(datum, bytes_copy, size, avro_free_wrapper);
+	rval = avro_fixed_set_private(datum, bytes_copy, size, avro_alloc_free);
 	if (rval) {
 		avro_free(bytes_copy, size);
 	}
@@ -698,15 +666,9 @@ int avro_fixed_set(avro_datum_t datum, c
 }
 
 int avro_givefixed_set(avro_datum_t datum, const char *bytes,
-		       const int64_t size)
-{
-	return avro_fixed_set_private(datum, bytes, size, avro_free_wrapper);
-}
-
-int avro_wrapfixed_set(avro_datum_t datum, const char *bytes,
-		       const int64_t size)
+		       const int64_t size, avro_free_func_t free)
 {
-	return avro_fixed_set_private(datum, bytes, size, NULL);
+	return avro_fixed_set_private(datum, bytes, size, free);
 }
 
 int avro_fixed_get(avro_datum_t datum, char **bytes, int64_t * size)

Modified: avro/trunk/lang/c/src/datum.h
URL: http://svn.apache.org/viewvc/avro/trunk/lang/c/src/datum.h?rev=1063596&r1=1063595&r2=1063596&view=diff
==============================================================================
--- avro/trunk/lang/c/src/datum.h (original)
+++ avro/trunk/lang/c/src/datum.h Wed Jan 26 03:44:18 2011
@@ -21,9 +21,6 @@
 #include "avro_private.h"
 #include "st.h"
 
-typedef void
-(*avro_free_func_t)(void *ptr, size_t sz);
-
 struct avro_string_datum_t {
 	struct avro_obj_t obj;
 	char *s;

Modified: avro/trunk/lang/c/src/datum_read.c
URL: http://svn.apache.org/viewvc/avro/trunk/lang/c/src/datum_read.c?rev=1063596&r1=1063595&r2=1063596&view=diff
==============================================================================
--- avro/trunk/lang/c/src/datum_read.c (original)
+++ avro/trunk/lang/c/src/datum_read.c Wed Jan 26 03:44:18 2011
@@ -291,6 +291,14 @@ read_record(avro_reader_t reader, const 
 	return 0;
 }
 
+static void
+free_bytes(void *ptr, size_t sz)
+{
+	// The binary encoder class allocates bytes values with an extra
+	// byte, so that they're NUL terminated.
+	avro_free(ptr, sz+1);
+}
+
 int
 avro_read_data(avro_reader_t reader, avro_schema_t writers_schema,
 	       avro_schema_t readers_schema, avro_datum_t * datum)
@@ -332,7 +340,7 @@ avro_read_data(avro_reader_t reader, avr
 			char *s;
 			rval = enc->read_string(reader, &s, &len);
 			if (!rval) {
-				*datum = avro_givestring(s);
+				*datum = avro_givestring(s, avro_alloc_free);
 			}
 		}
 		break;
@@ -383,7 +391,7 @@ avro_read_data(avro_reader_t reader, avr
 			int64_t len;
 			rval = enc->read_bytes(reader, &bytes, &len);
 			if (!rval) {
-				*datum = avro_givebytes(bytes, len);
+				*datum = avro_givebytes(bytes, len, free_bytes);
 			}
 		}
 		break;
@@ -401,7 +409,8 @@ avro_read_data(avro_reader_t reader, avr
 			}
 			rval = avro_read(reader, bytes, size);
 			if (!rval) {
-				*datum = avro_givefixed(name, bytes, size);
+				*datum = avro_givefixed(name, bytes, size,
+							avro_alloc_free);
 			}
 		}
 		break;

Modified: avro/trunk/lang/c/src/encoding_binary.c
URL: http://svn.apache.org/viewvc/avro/trunk/lang/c/src/encoding_binary.c?rev=1063596&r1=1063595&r2=1063596&view=diff
==============================================================================
--- avro/trunk/lang/c/src/encoding_binary.c (original)
+++ avro/trunk/lang/c/src/encoding_binary.c Wed Jan 26 03:44:18 2011
@@ -127,11 +127,12 @@ static int read_bytes(avro_reader_t read
 	if (rval) {
 		return rval;
 	}
-	*bytes = avro_malloc(*len);
+	*bytes = avro_malloc(*len + 1);
 	if (!*bytes) {
 		return ENOMEM;
 	}
 	AVRO_READ(reader, *bytes, *len);
+	(*bytes)[*len] = '\0';
 	return 0;
 }
 

Modified: avro/trunk/lang/c/src/schema.c
URL: http://svn.apache.org/viewvc/avro/trunk/lang/c/src/schema.c?rev=1063596&r1=1063595&r2=1063596&view=diff
==============================================================================
--- avro/trunk/lang/c/src/schema.c (original)
+++ avro/trunk/lang/c/src/schema.c Wed Jan 26 03:44:18 2011
@@ -1262,10 +1262,10 @@ avro_datum_t avro_datum_from_schema(cons
 
 	switch (avro_typeof(schema)) {
 		case AVRO_STRING:
-			return avro_wrapstring("");
+			return avro_givestring("", NULL);
 
 		case AVRO_BYTES:
-			return avro_wrapbytes("", 0);
+			return avro_givebytes("", 0, NULL);
 
 		case AVRO_INT32:
 			return avro_int32(0);
@@ -1322,7 +1322,8 @@ avro_datum_t avro_datum_from_schema(cons
 			{
 				const struct avro_fixed_schema_t *fixed_schema =
 				    avro_schema_to_fixed(schema);
-				return avro_wrapfixed(fixed_schema->name, "", 0);
+				return avro_givefixed(fixed_schema->name,
+						      "", 0, NULL);
 			}
 
 		case AVRO_MAP:

Modified: avro/trunk/lang/c/tests/generate_interop_data.c
URL: http://svn.apache.org/viewvc/avro/trunk/lang/c/tests/generate_interop_data.c?rev=1063596&r1=1063595&r2=1063596&view=diff
==============================================================================
--- avro/trunk/lang/c/tests/generate_interop_data.c (original)
+++ avro/trunk/lang/c/tests/generate_interop_data.c Wed Jan 26 03:44:18 2011
@@ -60,7 +60,7 @@ int main(int argc, char *argv[])
 	avro_record_set(interop, "intField", avro_int32(42));
 	avro_record_set(interop, "longField", avro_int64(4242));
 	avro_record_set(interop, "stringField",
-			avro_wrapstring("Follow your bliss."));
+			avro_givestring("Follow your bliss.", NULL));
 	avro_record_set(interop, "boolField", avro_boolean(1));
 	avro_record_set(interop, "floatField", avro_float(3.14159265));
 	avro_record_set(interop, "doubleField", avro_double(2.71828183));
@@ -82,7 +82,7 @@ int main(int argc, char *argv[])
 
 	node_datum = avro_record("Node", NULL);
 	avro_record_set(node_datum, "label",
-			avro_wrapstring("If you label me, you negate me."));
+			avro_givestring("If you label me, you negate me.", NULL));
 	avro_record_set(node_datum, "children", avro_array());
 	avro_record_set(interop, "recordField", node_datum);
 

Modified: avro/trunk/lang/c/tests/test_avro_data.c
URL: http://svn.apache.org/viewvc/avro/trunk/lang/c/tests/test_avro_data.c?rev=1063596&r1=1063595&r2=1063596&view=diff
==============================================================================
--- avro/trunk/lang/c/tests/test_avro_data.c (original)
+++ avro/trunk/lang/c/tests/test_avro_data.c Wed Jan 26 03:44:18 2011
@@ -152,12 +152,12 @@ static int test_string(void)
 	};
 	avro_schema_t writer_schema = avro_schema_string();
 	for (i = 0; i < sizeof(strings) / sizeof(strings[0]); i++) {
-		avro_datum_t datum = avro_wrapstring(strings[i]);
+		avro_datum_t datum = avro_givestring(strings[i], NULL);
 		write_read_check(writer_schema, NULL, datum, "string");
 		avro_datum_decref(datum);
 	}
 
-	avro_datum_t  datum = avro_wrapstring(strings[0]);
+	avro_datum_t  datum = avro_givestring(strings[0], NULL);
 	test_json(datum, writer_schema,
 		  "\"Four score and seven years ago\"");
 	avro_datum_decref(datum);
@@ -180,16 +180,16 @@ static int test_bytes(void)
 	avro_datum_t datum;
 	avro_datum_t expected_datum;
 
-	datum = avro_wrapbytes(bytes, sizeof(bytes));
+	datum = avro_givebytes(bytes, sizeof(bytes), NULL);
 	write_read_check(writer_schema, NULL, datum, "bytes");
 	test_json(datum, writer_schema,
 		  "\"\\u00de\\u00ad\\u00be\\u00ef\"");
 	avro_datum_decref(datum);
 	avro_schema_decref(writer_schema);
 
-	datum = avro_wrapbytes(NULL, 0);
-    avro_wrapbytes_set(datum, bytes, sizeof(bytes));
-	expected_datum = avro_wrapbytes(bytes, sizeof(bytes));
+	datum = avro_givebytes(NULL, 0, NULL);
+	avro_givebytes_set(datum, bytes, sizeof(bytes), NULL);
+	expected_datum = avro_givebytes(bytes, sizeof(bytes), NULL);
 	if (!avro_datum_equal(datum, expected_datum)) {
 		fprintf(stderr,
 		        "Expected equal bytes instances.\n");
@@ -315,7 +315,7 @@ static int test_record(void)
 	avro_schema_record_field_append(schema, "name", avro_schema_string());
 	avro_schema_record_field_append(schema, "age", avro_schema_int());
 
-	name_datum = avro_wrapstring("Joseph Campbell");
+	name_datum = avro_givestring("Joseph Campbell", NULL);
 	age_datum = avro_int32(83);
 
 	avro_record_set(datum, "name", name_datum);
@@ -465,7 +465,7 @@ static int test_union(void)
 	avro_schema_union_append(schema, avro_schema_int());
 	avro_schema_union_append(schema, avro_schema_null());
 
-	datum = avro_wrapstring("Follow your bliss.");
+	datum = avro_givestring("Follow your bliss.", NULL);
 	union_datum = avro_union(0, datum);
 
 	if (avro_union_discriminant(union_datum) != 0) {
@@ -480,7 +480,7 @@ static int test_union(void)
 
 	union_datum1 = avro_datum_from_schema(schema);
 	avro_union_set_discriminant(union_datum1, schema, 0, &datum1);
-	avro_wrapstring_set(datum1, "Follow your bliss.");
+	avro_givestring_set(datum1, "Follow your bliss.", NULL);
 
 	if (!avro_datum_equal(datum, datum1)) {
 		fprintf(stderr, "Union values should be equal\n");
@@ -509,14 +509,14 @@ static int test_fixed(void)
 	avro_datum_t datum;
 	avro_datum_t expected_datum;
 
-	datum = avro_wrapfixed("msg", bytes, sizeof(bytes));
+	datum = avro_givefixed("msg", bytes, sizeof(bytes), NULL);
 	write_read_check(schema, NULL, datum, "fixed");
 	test_json(datum, schema, "\"\\r\\n\\r\\n\\u000b\\n\\u000b\\n\"");
 	avro_datum_decref(datum);
 
-	datum = avro_wrapfixed("msg", NULL, 0);
-    avro_wrapfixed_set(datum, bytes, sizeof(bytes));
-	expected_datum = avro_wrapfixed("msg", bytes, sizeof(bytes));
+	datum = avro_givefixed("msg", NULL, 0, NULL);
+	avro_givefixed_set(datum, bytes, sizeof(bytes), NULL);
+	expected_datum = avro_givefixed("msg", bytes, sizeof(bytes), NULL);
 	if (!avro_datum_equal(datum, expected_datum)) {
 		fprintf(stderr,
 		        "Expected equal fixed instances.\n");



Mime
View raw message