Commit 557912a3 authored by Slava Monich's avatar Slava Monich

[mms-lib] Handle content transfer encoding

parent 3e93d0f7
......@@ -17,7 +17,7 @@ include ../mms-lib/Config.mak
#
PKGS = gio-unix-2.0 gio-2.0
LIB_PKGS = libwspcodec libsoup-2.4 dconf $(RESIZE_PKG) $(PKGS)
LIB_PKGS = libwspcodec gmime-2.6 libsoup-2.4 dconf $(RESIZE_PKG) $(PKGS)
#
# Default target
......
TEMPLATE = app
CONFIG += link_pkgconfig
PKGCONFIG += gio-unix-2.0 gio-2.0 glib-2.0 libsoup-2.4 dconf libwspcodec ImageMagick
PKGCONFIG += gmime-2.6 gio-unix-2.0 gio-2.0 glib-2.0 libsoup-2.4 dconf libwspcodec ImageMagick
DBUS_INTERFACE_DIR = $$_PRO_FILE_PWD_
MMS_LIB_DIR = $$_PRO_FILE_PWD_/../mms-lib
MMS_OFONO_DIR = $$_PRO_FILE_PWD_/../mms-ofono
......
......@@ -12,7 +12,7 @@ include Config.mak
# Required packages
#
PKGS = glib-2.0 libsoup-2.4 libwspcodec
PKGS = gmime-2.6 glib-2.0 libsoup-2.4 libwspcodec
#
# Default target
......
/*
* Copyright (C) 2013-2014 Jolla Ltd.
* Copyright (C) 2013-2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
......@@ -48,6 +49,7 @@ typedef struct _mms_message_part {
char* content_type; /* Content-Type */
char* content_id; /* Content-ID */
char* file; /* File name */
char* orig; /* File prior to decoding */
} MMSMessagePart;
MMSMessage*
......
TEMPLATE = lib
CONFIG += staticlib
CONFIG += link_pkgconfig
PKGCONFIG += glib-2.0 libsoup-2.4 libwspcodec
PKGCONFIG += gmime-2.6 glib-2.0 libsoup-2.4 libwspcodec
INCLUDEPATH += include
QMAKE_CFLAGS += -Wno-unused
......
......@@ -3,7 +3,7 @@
* Multimedia Messaging Service
*
* Copyright (C) 2010-2011 Intel Corporation. All rights reserved.
* Copyright (C) 2013-2014 Jolla Ltd.
* Copyright (C) 2013-2015 Jolla Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
......@@ -501,6 +501,7 @@ static gboolean extract_text(struct wsp_header_iter *iter, void *user)
if (text == NULL)
return FALSE;
g_free(*out);
*out = g_strdup(text);
return TRUE;
......@@ -1261,24 +1262,29 @@ static gboolean attachment_parse_headers(struct wsp_header_iter *iter,
{
while (wsp_header_iter_next(iter)) {
const unsigned char *hdr = wsp_header_iter_get_hdr(iter);
unsigned char h;
/* Skip application headers */
if (wsp_header_iter_get_hdr_type(iter) !=
WSP_HEADER_TYPE_WELL_KNOWN)
continue;
h = hdr[0] & 0x7f;
switch (h) {
case MMS_PART_HEADER_CONTENT_ID:
if (!extract_quoted_string(iter, &part->content_id))
return FALSE;
break;
case MMS_PART_HEADER_CONTENT_LOCATION:
if (!extract_text(iter, &part->content_location))
return FALSE;
break;
if (wsp_header_iter_get_hdr_type(iter) ==
WSP_HEADER_TYPE_WELL_KNOWN) {
switch (hdr[0] & 0x7f) {
case MMS_PART_HEADER_CONTENT_ID:
if (!extract_quoted_string(iter,
&part->content_id))
return FALSE;
break;
case MMS_PART_HEADER_CONTENT_LOCATION:
if (!extract_text(iter,
&part->content_location))
return FALSE;
break;
}
} else {
/* Application (textual) header */
if (g_ascii_strcasecmp((char*)hdr,
"content-transfer-encoding") == 0) {
if (!extract_text(iter,
&part->transfer_encoding))
return FALSE;
}
}
}
......@@ -1292,6 +1298,7 @@ static void free_attachment(gpointer data, gpointer user_data)
g_free(attach->content_type);
g_free(attach->content_id);
g_free(attach->content_location);
g_free(attach->transfer_encoding);
g_free(attach);
}
......
......@@ -3,6 +3,7 @@
* Multimedia Messaging Service
*
* Copyright (C) 2010-2011 Intel Corporation. All rights reserved.
* Copyright (C) 2013-2015 Jolla Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
......@@ -185,6 +186,7 @@ struct mms_attachment {
char *content_type;
char *content_id;
char *content_location;
char *transfer_encoding;
};
struct mms_message {
......
/*
* Copyright (C) 2013-2014 Jolla Ltd.
* Copyright (C) 2013-2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
......@@ -222,6 +223,123 @@ mms_file_copy(
return ok;
}
/*
* MIME decoding
*/
typedef struct mms_mime_decode {
int out;
const char* dest;
size_t outbytes;
size_t outbuflen;
void* outbuf;
GMimeEncoding state;
} MMSMimeDecode;
static
gboolean
mms_file_decode_init(
MMSMimeDecode* dec,
const char* dest,
GMimeContentEncoding enc,
GError** error)
{
memset(dec, 0, sizeof(*dec));
dec->out = open(dest, O_CREAT|O_WRONLY|O_TRUNC|O_BINARY, MMS_FILE_PERM);
if (dec->out >= 0) {
dec->dest = dest;
g_mime_encoding_init_decode(&dec->state, enc);
return TRUE;
} else {
MMS_ERROR(error, MMS_LIB_ERROR_IO, "Failed to create file %s: %s",
dest, strerror(errno));
return FALSE;
}
}
static
void
mms_file_decode_deinit(
MMSMimeDecode* dec)
{
g_free(dec->outbuf);
close(dec->out);
}
gboolean
mms_file_decode_step(
MMSMimeDecode* dec,
const void* inbuf,
size_t inlen,
size_t (*step)(GMimeEncoding*, const char*, size_t, char*),
GError** error)
{
ssize_t nbytes;
size_t need = g_mime_encoding_outlen(&dec->state, inlen);
if (need > dec->outbuflen) {
g_free(dec->outbuf);
dec->outbuf = g_malloc(need);
dec->outbuflen = need;
}
nbytes = step(&dec->state, inbuf, inlen, dec->outbuf);
if (write(dec->out, dec->outbuf, nbytes) == nbytes) {
dec->outbytes += nbytes;
return TRUE;
} else {
MMS_ERROR(error, MMS_LIB_ERROR_IO, "Failed to write %s: %s",
dec->dest, strerror(errno));
return FALSE;
}
}
gboolean
mms_file_decode(
const char* src,
const char* dest,
GMimeContentEncoding enc,
GError** error)
{
gboolean ok = FALSE;
int in = open(src, O_RDONLY | O_BINARY);
if (in >= 0) {
MMSMimeDecode dec;
if (mms_file_decode_init(&dec, dest, enc, error)) {
int nbytes;
size_t inbytes = 0;
const size_t buflen = 1024;
void* inbuf = g_malloc(buflen);
ok = TRUE;
while ((nbytes = read(in, inbuf, buflen)) > 0) {
inbytes += nbytes;
if (!mms_file_decode_step(&dec, inbuf, nbytes,
g_mime_encoding_step, error)) {
ok = FALSE;
break;
}
}
if (nbytes < 0) {
ok = FALSE;
MMS_ERROR(error, MMS_LIB_ERROR_IO,
"Failed to read %s: %s", src, strerror(errno));
} else if (ok) {
if (mms_file_decode_step(&dec, NULL, 0,
g_mime_encoding_flush, error)) {
MMS_DEBUG("Decoded %s (%d bytes) -> %s (%d bytes)",
src, (int)inbytes, dest, (int)dec.outbytes);
} else {
ok = FALSE;
}
}
mms_file_decode_deinit(&dec);
g_free(inbuf);
}
close(in);
} else {
MMS_ERROR(error, MMS_LIB_ERROR_IO,
"Failed to open file %s: %s", src, strerror(errno));
}
return ok;
}
/*
* Local Variables:
* mode: C
......
/*
* Copyright (C) 2013-2014 Jolla Ltd.
* Copyright (C) 2013-2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
......@@ -17,6 +18,8 @@
#include "mms_lib_types.h"
#include <gmime/gmime-encodings.h>
/* Permissions for MMS files and directories */
#define MMS_DIR_PERM (0755)
#define MMS_FILE_PERM (0644)
......@@ -75,6 +78,13 @@ mms_file_copy(
const char* dest,
GError** error);
gboolean
mms_file_decode(
const char* src,
const char* dest,
GMimeContentEncoding enc,
GError** error);
#define mms_message_dir(config,id) \
(g_strconcat((config)->root_dir, "/" MMS_MESSAGE_DIR "/" , id, NULL))
#define mms_task_dir(task) \
......
/*
* Copyright (C) 2013-2014 Jolla Ltd.
* Copyright (C) 2013-2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
......@@ -33,6 +34,10 @@ mms_message_part_free(
if (!(msg->flags & MMS_MESSAGE_FLAG_KEEP_FILES)) remove(part->file);
g_free(part->file);
}
if (part->orig) {
if (!(msg->flags & MMS_MESSAGE_FLAG_KEEP_FILES)) remove(part->orig);
g_free(part->orig);
}
g_free(part);
}
......
/*
* Copyright (C) 2013-2014 Jolla Ltd.
* Copyright (C) 2013-2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
......@@ -54,7 +55,7 @@ mms_task_decode_array_contains_string(
}
static
char*
const char*
mms_task_decode_add_file_name(
GPtrArray* names,
const char* proposed)
......@@ -100,6 +101,42 @@ mms_task_decode_make_content_id(
return id;
}
static
void
mms_task_decode_part(
MMSMessagePart* part,
GMimeContentEncoding enc,
const char* dir,
GPtrArray* part_files)
{
char* default_name;
const char* orig_file;
const char* part_file;
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
part_file = g_basename(part->file);
G_GNUC_END_IGNORE_DEPRECATIONS;
default_name = g_strconcat(part_file, ".orig", NULL);
orig_file = mms_task_decode_add_file_name(part_files, default_name);
g_free(default_name);
part->orig = g_strconcat(dir, "/", orig_file, NULL);
if (rename(part->file, part->orig) == 0) {
GError* error = NULL;
if (!mms_file_decode(part->orig, part->file, enc, &error)) {
unlink(part->file);
rename(part->orig, part->file);
g_free(part->orig);
part->orig = NULL;
MMS_ERR("%s", MMS_ERRMSG(error));
g_error_free(error);
}
} else {
MMS_ERR("Failed to rename %s to %s: %s", part->file, part->orig,
strerror(errno));
}
}
static
MMSMessage*
mms_task_decode_retrieve_conf(
......@@ -169,7 +206,7 @@ mms_task_decode_retrieve_conf(
const char* name = attach->content_location ?
attach->content_location : attach->content_id;
char* path = NULL;
char* file;
const char* file;
if (name && name[0]) {
file = mms_task_decode_add_file_name(part_files, name);
} else {
......@@ -188,6 +225,16 @@ mms_task_decode_retrieve_conf(
part->content_type = g_strdup(attach->content_type);
part->content_id = mms_task_decode_make_content_id(part_ids, id);
part->file = path;
if (attach->transfer_encoding) {
GMimeContentEncoding enc =
g_mime_content_encoding_from_string(
attach->transfer_encoding);
if (enc > GMIME_CONTENT_ENCODING_BINARY) {
/* The part actually needs some decoding */
MMS_DEBUG("Decoding %s", attach->transfer_encoding);
mms_task_decode_part(part,enc,msg->parts_dir,part_files);
}
}
msg->parts = g_slist_append(msg->parts, part);
if (tmp && tmp != part->content_id) g_free(tmp);
}
......
......@@ -25,7 +25,7 @@ include ../../Config.mak
# Required packages
#
PKGS += glib-2.0 libsoup-2.4
PKGS += gmime-2.6 glib-2.0 libsoup-2.4
LIB_PKGS += $(PKGS) libwspcodec $(RESIZE_PKG)
#
......
Tämmönen 😈 tää lähtee huomenna.
\ No newline at end of file
VMOkbW3Dtm5lbiDwn5iIIHTDpMOkIGzDpGh0ZWUgaHVvbWVubmEuT
\ No newline at end of file
<smil>
<head>
<layout>
<root-layout height="768" width="480"/>
<region fit="scroll" height="100%" left="0" top="0" width="100%" id="Text"/>
<region fit="meet" height="100%" left="0" top="0" width="100%" id="Media"/>
</layout>
</head>
<body>
<par dur="5000ms">
<img src="1.jpg" region="Media"/>
</par>
<par dur="5000ms">
<text src="2.txt" region="Text"/>
</par>
</body>
</smil>
<!DOCTYPE smil PUBLIC "-//W3C//DTD SMIL 1.0//EN" "http://www.w3.org/TR/REC-smil/SMIL10.dtd">
<smil>
<head>
<layout>
<root-layout height="160" width="120"/>
<region fit="scroll" height="100%" left="0" top="0" width="100%" id="Text"/>
<region fit="meet" height="100%" left="0" top="0" width="100%" id="Media"/>
</layout>
</head>
<body>
<par dur="5000ms">
<img src="1" region="Media"/>
</par>
</body>
</smil>
Tämmönen 😈 tää lähtee huomenna.
\ No newline at end of file
T=C3=A4mm=C3=B6nen =F0=9F=98=88 t=C3=A4=C3=A4 l=C3=A4htee huomenna.
\ No newline at end of file
<smil>
<head>
<layout>
<root-layout height="768" width="480"/>
<region fit="scroll" height="100%" left="0" top="0" width="100%" id="Text"/>
<region fit="meet" height="100%" left="0" top="0" width="100%" id="Media"/>
</layout>
</head>
<body>
<par dur="5000ms">
<img src="1.jpg" region="Media"/>
</par>
<par dur="5000ms">
<text src="2.txt" region="Text"/>
</par>
</body>
</smil>
<smil><head><layout><root-layout width="360" height="615"/><region id="Image" width="347" height="260" top="341" left="7" fit="meet"/><region id="Text" width="326" height="320" top="14" left="7" fit="scroll"/></layout></head><body><par dur="5000ms"><img region="Image" src="2014_03_.jpg"/><text region="Text" src="Test_mms.txt"/></par></body></smil>
/*
* Copyright (C) 2013-2014 Jolla Ltd.
* Copyright (C) 2013-2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
......@@ -97,6 +98,12 @@ static const TestPartDesc retrieve_success3_parts [] = {
{ "text/plain;charset=utf-8", "<2>", "text_001.txt" }
};
static const TestPartDesc retrieve_transfer_encoding_parts [] = {
{ "application/smil;charset=utf-8", "<smil>", "smil" },
{ "image/jpeg", "<1.jpg>", "1.jpg" },
{ "text/plain;charset=utf-8", "<2.txt>", "2.txt" }
};
static const TestPartDesc retrieve_invalid_subject [] = {
{ "application/smil;charset=us-ascii", "<start>", "smil.smi" },
{ "image/jpeg", "<1>", "1" }
......@@ -142,6 +149,30 @@ static const TestDesc retrieve_tests[] = {
MMS_MESSAGE_TYPE_ACKNOWLEDGE_IND,
TEST_PARTS(retrieve_success3_parts),
0
},{
"QuotedPrintable", /* quoted-printable transfer encoding for text */
NULL,
"m-notification.ind",
"m-retrieve.conf",
SOUP_STATUS_OK,
MMS_CONTENT_TYPE,
NULL,
MMS_RECEIVE_STATE_DECODING,
MMS_MESSAGE_TYPE_ACKNOWLEDGE_IND,
TEST_PARTS(retrieve_transfer_encoding_parts),
0
},{
"Base64", /* Base64 transfer encoding for text */
NULL,
"m-notification.ind",
"m-retrieve.conf",
SOUP_STATUS_OK,
MMS_CONTENT_TYPE,
NULL,
MMS_RECEIVE_STATE_DECODING,
MMS_MESSAGE_TYPE_ACKNOWLEDGE_IND,
TEST_PARTS(retrieve_transfer_encoding_parts),
0
},{
"InvalidSubject",
NULL,
......@@ -313,6 +344,36 @@ static const TestDesc retrieve_tests[] = {
}
};
static
gboolean
test_files_equal(
const char* path1,
const char* path2)
{
gboolean equal = FALSE;
if (path1 && path2) {
GError* error = NULL;
GMappedFile* f1 = g_mapped_file_new(path1, FALSE, &error);
if (f1) {
GMappedFile* f2 = g_mapped_file_new(path2, FALSE, &error);
if (f2) {
const gsize size = g_mapped_file_get_length(f1);
if (g_mapped_file_get_length(f2) == size) {
const void* data1 = g_mapped_file_get_contents(f1);
const void* data2 = g_mapped_file_get_contents(f2);
equal = !memcmp(data1, data2, size);
}
g_mapped_file_unref(f2);
}
g_mapped_file_unref(f1);
}
}
if (!equal) {
MMS_ERR("%s is not identical to %s", path1, path2);
}
return equal;
}
static
int
test_validate_parts(
......@@ -323,16 +384,28 @@ test_validate_parts(
if (!desc->nparts && (!msg || !g_slist_length(msg->parts))) {
return RET_OK;
} else {
const char* dir = desc->dir ? desc->dir : desc->name;
const unsigned int nparts = g_slist_length(msg->parts);
if (desc->nparts == nparts) {
const TestPartDesc* expect = test->desc->parts;
GSList* list = msg->parts;
while (list) {
const MMSMessagePart* part = list->data;
char* sample = g_strconcat(DATA_DIR, dir, "/parts/",
expect->file_name, NULL);
const gboolean match = test_files_equal(part->file, sample);
const char* fname;
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
fname = g_basename(part->file);
G_GNUC_END_IGNORE_DEPRECATIONS;
g_free(sample);
if (!match) {
/* Message message is printed by test_files_equal */
return RET_ERR;
}
if (strcmp(expect->content_type, part->content_type)) {
MMS_ERR("Content type mismatch: expected %s, got %s",
expect->content_type, part->content_type);
......@@ -352,7 +425,7 @@ test_validate_parts(
expect->content_id, part->content_id);
return RET_ERR;
}
list = list->next;
list = list->next;
expect++;
}
return RET_OK;
......@@ -408,33 +481,17 @@ test_finish(
}
}
if (test->ret == RET_OK && desc->attic_file) {
gboolean ok = FALSE;
const char* dir = desc->dir ? desc->dir : desc->name;
char* f1 = g_strconcat(DATA_DIR, dir, "/", desc->ni_file, NULL);
char* f2 = g_strconcat(test->config->root_dir, "/" MMS_ATTIC_DIR "/",
desc->attic_file, NULL);
GMappedFile* m1 = g_mapped_file_new(f1, FALSE, NULL);
if (m1) {
GMappedFile* m2 = g_mapped_file_new(f2, FALSE, NULL);
if (m2) {
const gsize len = g_mapped_file_get_length(m1);
if (len == g_mapped_file_get_length(m2)) {
const void* ptr1 = g_mapped_file_get_contents(m1);
const void* ptr2 = g_mapped_file_get_contents(m2);
ok = !memcmp(ptr1, ptr2, len);
}
g_mapped_file_unref(m2);
}
g_mapped_file_unref(m1);
}
if (ok) {
if (test_files_equal(f1, f2)) {
char* dir = g_path_get_dirname(f2);
remove(f2);
rmdir(dir);
g_free(dir);
} else {
test->ret = RET_ERR;
MMS_ERR("%s is not identical to %s", f2, f1);
}
g_free(f1);
g_free(f2);
......@@ -691,6 +748,7 @@ int main(int argc, char* argv[])
MMSConfig config;
const char* test_name = (argc == 2) ? argv[1] : NULL;
char* tmpd = g_mkdtemp(g_strdup("/tmp/test_retrieve_XXXXXX"));
char* msgdir = g_strconcat(tmpd, "/msg", NULL);
MMS_VERBOSE("Temporary directory %s", tmpd);
mms_lib_default_config(&config);
......@@ -721,7 +779,9 @@ int main(int argc, char* argv[])
}
}
remove(tmpd);
rmdir(msgdir);
rmdir(tmpd);
g_free(msgdir);
g_free(tmpd);
} else {
fprintf(stderr, "%s\n", MMS_ERRMSG(error));
......
......@@ -16,6 +16,7 @@ BuildRequires: libjpeg-turbo-devel
BuildRequires: pkgconfig(dconf)
BuildRequires: pkgconfig(libpng)
BuildRequires: pkgconfig(libexif)
BuildRequires: pkgconfig(gmime-2.6)
BuildRequires: pkgconfig(glib-2.0) >= 2.32
BuildRequires: pkgconfig(libsoup-2.4) >= 2.38
BuildRequires: pkgconfig(libwspcodec) >= 2.2
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment