aboutsummaryrefslogtreecommitdiffstats
path: root/src/libmime/images.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libmime/images.c')
-rw-r--r--src/libmime/images.c255
1 files changed, 255 insertions, 0 deletions
diff --git a/src/libmime/images.c b/src/libmime/images.c
new file mode 100644
index 000000000..ff07bbd72
--- /dev/null
+++ b/src/libmime/images.c
@@ -0,0 +1,255 @@
+/* Copyright (c) 2010, Vsevolod Stakhov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "images.h"
+#include "main.h"
+#include "message.h"
+
+static const guint8 png_signature[] = {137, 80, 78, 71, 13, 10, 26, 10};
+static const guint8 jpg_sig1[] = {0xff, 0xd8};
+static const guint8 jpg_sig2[] = {'J', 'F', 'I', 'F'};
+static const guint8 gif_signature[] = {'G', 'I', 'F', '8'};
+static const guint8 bmp_signature[] = {'B', 'M'};
+
+static void process_image (struct rspamd_task *task, struct mime_part *part);
+
+
+void
+process_images (struct rspamd_task *task)
+{
+ GList *cur;
+ struct mime_part *part;
+
+ cur = task->parts;
+ while (cur) {
+ part = cur->data;
+ if (g_mime_content_type_is_type (part->type, "image", "*") && part->content->len > 0) {
+ process_image (task, part);
+ }
+ cur = g_list_next (cur);
+ }
+
+}
+
+static enum known_image_types
+detect_image_type (GByteArray *data)
+{
+ if (data->len > sizeof (png_signature) / sizeof (png_signature[0])) {
+ if (memcmp (data->data, png_signature, sizeof (png_signature)) == 0) {
+ return IMAGE_TYPE_PNG;
+ }
+ }
+ if (data->len > 10) {
+ if (memcmp (data->data, jpg_sig1, sizeof (jpg_sig1)) == 0) {
+ if (memcmp (data->data + 6, jpg_sig2, sizeof (jpg_sig2)) == 0) {
+ return IMAGE_TYPE_JPG;
+ }
+ }
+ }
+ if (data->len > sizeof (gif_signature) / sizeof (gif_signature[0])) {
+ if (memcmp (data->data, gif_signature, sizeof (gif_signature)) == 0) {
+ return IMAGE_TYPE_GIF;
+ }
+ }
+ if (data->len > sizeof (bmp_signature) / sizeof (bmp_signature[0])) {
+ if (memcmp (data->data, bmp_signature, sizeof (bmp_signature)) == 0) {
+ return IMAGE_TYPE_BMP;
+ }
+ }
+
+ return IMAGE_TYPE_UNKNOWN;
+}
+
+
+static struct rspamd_image *
+process_png_image (struct rspamd_task *task, GByteArray *data)
+{
+ struct rspamd_image *img;
+ guint32 t;
+ guint8 *p;
+
+ if (data->len < 24) {
+ msg_info ("bad png detected (maybe striped): <%s>", task->message_id);
+ return NULL;
+ }
+
+ /* In png we should find iHDR section and get data from it */
+ /* Skip signature and read header section */
+ p = data->data + 12;
+ if (memcmp (p, "IHDR", 4) != 0) {
+ msg_info ("png doesn't begins with IHDR section", task->message_id);
+ return NULL;
+ }
+
+ img = rspamd_mempool_alloc (task->task_pool, sizeof (struct rspamd_image));
+ img->type = IMAGE_TYPE_PNG;
+ img->data = data;
+
+ p += 4;
+ memcpy (&t, p, sizeof (guint32));
+ img->width = ntohl (t);
+ p += 4;
+ memcpy (&t, p, sizeof (guint32));
+ img->height = ntohl (t);
+
+ return img;
+}
+
+static struct rspamd_image *
+process_jpg_image (struct rspamd_task *task, GByteArray *data)
+{
+ guint8 *p;
+ guint16 t;
+ gsize remain;
+ struct rspamd_image *img;
+
+ img = rspamd_mempool_alloc (task->task_pool, sizeof (struct rspamd_image));
+ img->type = IMAGE_TYPE_JPG;
+ img->data = data;
+
+ p = data->data;
+ remain = data->len;
+ /* In jpeg we should find any data stream (ff c0 .. ff c3) and extract its height and width */
+ while (remain --) {
+ if (*p == 0xFF && remain > 8 && (*(p + 1) >= 0xC0 && *(p + 1) <= 0xC3)) {
+ memcpy (&t, p + 5, sizeof (guint16));
+ img->height = ntohs (t);
+ memcpy (&t, p + 7, sizeof (guint16));
+ img->width = ntohs (t);
+ return img;
+ }
+ p ++;
+ }
+
+ return NULL;
+}
+
+static struct rspamd_image *
+process_gif_image (struct rspamd_task *task, GByteArray *data)
+{
+ struct rspamd_image *img;
+ guint8 *p;
+ guint16 t;
+
+ if (data->len < 10) {
+ msg_info ("bad gif detected (maybe striped): <%s>", task->message_id);
+ return NULL;
+ }
+
+ img = rspamd_mempool_alloc (task->task_pool, sizeof (struct rspamd_image));
+ img->type = IMAGE_TYPE_GIF;
+ img->data = data;
+
+ p = data->data + 6;
+ memcpy (&t, p, sizeof (guint16));
+ img->width = GUINT16_FROM_LE (t);
+ memcpy (&t, p + 2, sizeof (guint16));
+ img->height = GUINT16_FROM_LE (t);
+
+ return img;
+}
+
+static struct rspamd_image *
+process_bmp_image (struct rspamd_task *task, GByteArray *data)
+{
+ struct rspamd_image *img;
+ gint32 t;
+ guint8 *p;
+
+
+
+ if (data->len < 28) {
+ msg_info ("bad bmp detected (maybe striped): <%s>", task->message_id);
+ return NULL;
+ }
+
+ img = rspamd_mempool_alloc (task->task_pool, sizeof (struct rspamd_image));
+ img->type = IMAGE_TYPE_BMP;
+ img->data = data;
+ p = data->data + 18;
+ memcpy (&t, p, sizeof (gint32));
+ img->width = abs (GINT32_FROM_LE (t));
+ memcpy (&t, p + 4, sizeof (gint32));
+ img->height = abs (GINT32_FROM_LE (t));
+
+ return img;
+}
+
+static void
+process_image (struct rspamd_task *task, struct mime_part *part)
+{
+ enum known_image_types type;
+ struct rspamd_image *img = NULL;
+ if ((type = detect_image_type (part->content)) != IMAGE_TYPE_UNKNOWN) {
+ switch (type) {
+ case IMAGE_TYPE_PNG:
+ img = process_png_image (task, part->content);
+ break;
+ case IMAGE_TYPE_JPG:
+ img = process_jpg_image (task, part->content);
+ break;
+ case IMAGE_TYPE_GIF:
+ img = process_gif_image (task, part->content);
+ break;
+ case IMAGE_TYPE_BMP:
+ img = process_bmp_image (task, part->content);
+ break;
+ default:
+ img = NULL;
+ break;
+ }
+ }
+
+ if (img != NULL) {
+ debug_task ("detected %s image of size %ud x %ud in message <%s>",
+ image_type_str (img->type),
+ img->width, img->height,
+ task->message_id);
+ img->filename = part->filename;
+ task->images = g_list_prepend (task->images, img);
+ }
+}
+
+const gchar *
+image_type_str (enum known_image_types type)
+{
+ switch (type) {
+ case IMAGE_TYPE_PNG:
+ return "PNG";
+ break;
+ case IMAGE_TYPE_JPG:
+ return "JPEG";
+ break;
+ case IMAGE_TYPE_GIF:
+ return "GIF";
+ break;
+ case IMAGE_TYPE_BMP:
+ return "BMP";
+ break;
+ default:
+ return "unknown";
+ }
+
+ return "unknown";
+}