From 6fc062de5597d8023661b76de65590e3167aa673 Mon Sep 17 00:00:00 2001 From: Slava Monich Date: Thu, 27 Feb 2014 12:19:43 +0200 Subject: [PATCH] Preliminary version of image resizing Images get resized if the message doesn't fit into the limit (300 kB by default). Requires ImageMagick. Works on images taken by camera. Dies and brings the whole system down on large images. We should be able to resize images (at least JPEGs) without loading the whole thing into the memory... --- mms-engine/Makefile | 2 +- mms-engine/main.c | 13 ++- mms-engine/mms-engine.pro | 2 +- mms-lib/Makefile | 18 +++- mms-lib/include/mms_lib_types.h | 1 + mms-lib/include/mms_lib_util.h | 7 +- mms-lib/mms-lib.pro | 4 + mms-lib/src/mms_attachment.c | 79 +++++++++----- mms-lib/src/mms_attachment.h | 16 ++- mms-lib/src/mms_attachment_image.c | 167 +++++++++++++++++++++++++++++ mms-lib/src/mms_file_util.h | 1 + mms-lib/src/mms_lib_util.c | 24 ++++- mms-lib/src/mms_task_encode.c | 123 +++++++++++++++++++-- rpm/mms-engine.spec | 2 + 14 files changed, 412 insertions(+), 47 deletions(-) create mode 100644 mms-lib/src/mms_attachment_image.c diff --git a/mms-engine/Makefile b/mms-engine/Makefile index 84d9a96..d7fef45 100644 --- a/mms-engine/Makefile +++ b/mms-engine/Makefile @@ -7,7 +7,7 @@ # Required packages PKGS = gio-unix-2.0 gio-2.0 glib-2.0 -LIB_PKGS = libwspcodec libsoup-2.4 $(PKGS) +LIB_PKGS = libwspcodec libsoup-2.4 ImageMagick $(PKGS) # # Default target diff --git a/mms-engine/main.c b/mms-engine/main.c index 190413c..3b1c7c8 100644 --- a/mms-engine/main.c +++ b/mms-engine/main.c @@ -149,6 +149,7 @@ mms_app_parse_options( gboolean ok; GError* error = NULL; gboolean session_bus = FALSE; + gint size_limit_kb = opt->config.size_limit/1024; char* root_dir_help = g_strdup_printf( "Root directory for MMS files [%s]", opt->config.root_dir); @@ -158,6 +159,9 @@ mms_app_parse_options( char* idle_secs_help = g_strdup_printf( "Inactivity timeout in seconds [%d]", opt->config.idle_secs); + char* size_limit_help = g_strdup_printf( + "Maximum size for outgoing messages [%d]", + size_limit_kb); char* description = mms_log_description(mms_app_log_modules, G_N_ELEMENTS(mms_app_log_modules)); @@ -171,6 +175,8 @@ mms_app_parse_options( &opt->config.retry_secs, retry_secs_help, "SEC" }, { "idle-secs", 'i', 0, G_OPTION_ARG_INT, &opt->config.idle_secs, idle_secs_help, "SEC" }, + { "size-limit", 's', 0, G_OPTION_ARG_INT, + &size_limit_kb, size_limit_help, "KB" }, { "keep-running", 'k', 0, G_OPTION_ARG_NONE, &opt->keep_running, "Keep running after everything is done", NULL }, { "keep-temp-files", 't', 0, G_OPTION_ARG_NONE, @@ -197,10 +203,12 @@ mms_app_parse_options( g_free(root_dir_help); g_free(retry_secs_help); g_free(idle_secs_help); + g_free(size_limit_help); g_free(description); - if (ok) { + if (ok && size_limit_kb >= 0) { MMS_INFO("Starting"); + opt->config.size_limit = size_limit_kb * 1024; if (opt->dir) opt->config.root_dir = opt->dir; if (session_bus) { MMS_DEBUG("Attaching to session bus"); @@ -221,7 +229,7 @@ int main(int argc, char* argv[]) { int result = 1; MMSAppOptions opt = {0}; - mms_lib_init(); + mms_lib_init(argv[0]); mms_log_default.name = MMS_APP_LOG_PREFIX; mms_lib_default_config(&opt.config); if (mms_app_parse_options(&opt, argc, argv)) { @@ -260,5 +268,6 @@ int main(int argc, char* argv[]) closelog(); } g_free(opt.dir); + mms_lib_deinit(); return result; } diff --git a/mms-engine/mms-engine.pro b/mms-engine/mms-engine.pro index a3c8121..c3f5f79 100644 --- a/mms-engine/mms-engine.pro +++ b/mms-engine/mms-engine.pro @@ -1,7 +1,7 @@ TEMPLATE = app CONFIG -= qt CONFIG += link_pkgconfig -PKGCONFIG += libsoup-2.4 gio-unix-2.0 gio-2.0 glib-2.0 dbus-1 libwspcodec +PKGCONFIG += gio-unix-2.0 gio-2.0 glib-2.0 libsoup-2.4 libwspcodec ImageMagick DBUS_INTERFACE_DIR = $$_PRO_FILE_PWD_ MMS_LIB_DIR = $$_PRO_FILE_PWD_/../mms-lib MMS_OFONO_DIR = $$_PRO_FILE_PWD_/../mms-ofono diff --git a/mms-lib/Makefile b/mms-lib/Makefile index 9abad76..d64adce 100644 --- a/mms-lib/Makefile +++ b/mms-lib/Makefile @@ -15,10 +15,10 @@ all: debug release # Sources # -SRC = mms_attachment.c mms_codec.c mms_connection.c mms_connman.c \ - mms_dispatcher.c mms_error.c mms_handler.c mms_lib_util.c mms_file_util.c \ - mms_log.c mms_message.c mms_task.c mms_task_ack.c mms_task_decode.c \ - mms_task_encode.c mms_task_http.c mms_task_notification.c \ +SRC = mms_attachment.c mms_attachment_image.c mms_codec.c mms_connection.c \ + mms_connman.c mms_dispatcher.c mms_error.c mms_handler.c mms_lib_util.c \ + mms_file_util.c mms_log.c mms_message.c mms_task.c mms_task_ack.c \ + mms_task_decode.c mms_task_encode.c mms_task_http.c mms_task_notification.c \ mms_task_notifyresp.c mms_task_publish.c mms_task_read.c \ mms_task_retrieve.c mms_task_send.c mms_util.c @@ -44,6 +44,13 @@ ifneq ($(GCOV),0) CFLAGS += --coverage endif +# +# ImageMagick support +# + +DEFINES += -DHAVE_IMAGEMAGICK +PKGS += ImageMagick + # # Tools and flags # @@ -57,7 +64,8 @@ DEBUG_DEFS = -DDEBUG RELEASE_DEFS = WARNINGS = -Wall INCLUDES = -I$(SRC_DIR) -I$(INCLUDE_DIR) -CFLAGS += $(WARNINGS) $(INCLUDES) $(shell pkg-config --cflags $(PKGS)) -MMD +CFLAGS += $(WARNINGS) $(DEFINES) $(INCLUDES) \ + $(shell pkg-config --cflags $(PKGS)) -MMD ifndef KEEP_SYMBOLS KEEP_SYMBOLS = 0 diff --git a/mms-lib/include/mms_lib_types.h b/mms-lib/include/mms_lib_types.h index 0cdea49..4daa029 100644 --- a/mms-lib/include/mms_lib_types.h +++ b/mms-lib/include/mms_lib_types.h @@ -49,6 +49,7 @@ typedef struct mms_config { const char* user_agent; /* User agent string */ int retry_secs; /* Retry timeout in seconds */ int idle_secs; /* Idle timeout */ + gsize size_limit; /* Maximum size of m-Send.req PDU */ gboolean keep_temp_files; /* Keep temporary files around */ gboolean attic_enabled; /* Keep unrecognized push message in attic */ gboolean send_dr; /* Allow sending delivery reports */ diff --git a/mms-lib/include/mms_lib_util.h b/mms-lib/include/mms_lib_util.h index 6aa9757..677daaa 100644 --- a/mms-lib/include/mms_lib_util.h +++ b/mms-lib/include/mms_lib_util.h @@ -32,7 +32,12 @@ typedef enum { /* One-time initialization */ void -mms_lib_init(void); +mms_lib_init( + const char* path); + +/* Final deinitialization */ +void +mms_lib_deinit(void); /* Reset configuration to default */ void diff --git a/mms-lib/mms-lib.pro b/mms-lib/mms-lib.pro index 94113fb..edc0a69 100644 --- a/mms-lib/mms-lib.pro +++ b/mms-lib/mms-lib.pro @@ -6,6 +6,9 @@ PKGCONFIG += glib-2.0 libsoup-2.4 libwspcodec INCLUDEPATH += include QMAKE_CFLAGS += -Wno-unused +DEFINES += HAVE_IMAGEMAGICK +PKGCONFIG += ImageMagick + CONFIG(debug, debug|release) { DEFINES += DEBUG DESTDIR = $$_PRO_FILE_PWD_/build/debug @@ -15,6 +18,7 @@ CONFIG(debug, debug|release) { SOURCES += \ src/mms_attachment.c \ + src/mms_attachment_image.c \ src/mms_codec.c \ src/mms_connection.c \ src/mms_connman.c \ diff --git a/mms-lib/src/mms_attachment.c b/mms-lib/src/mms_attachment.c index 286cc28..8eb9075 100644 --- a/mms-lib/src/mms_attachment.c +++ b/mms-lib/src/mms_attachment.c @@ -53,12 +53,12 @@ mms_attachment_finalize( g_mapped_file_unref(at->map); if (!at->config->keep_temp_files && !(at->flags & MMS_ATTACHMENT_DONT_DELETE_FILES)) { - char* dir = g_path_get_dirname(at->file_name); - remove(at->file_name); + char* dir = g_path_get_dirname(at->original_file); + remove(at->original_file); rmdir(dir); g_free(dir); } - g_free(at->file_name); + g_free(at->original_file); g_free(at->content_type); g_free(at->content_location); g_free(at->content_id); @@ -208,26 +208,25 @@ mms_attachment_new( if (path) { GMappedFile* map = g_mapped_file_new(path, FALSE, error); if (map) { - MMSAttachment* at = g_object_new(MMS_TYPE_ATTACHMENT, NULL); - at->config = config; - at->file_name = path; - at->map = map; + unsigned int flags = 0; + char* content_type = NULL; + MMSAttachment* at; if (info->content_type) { char** ct = mms_parse_http_content_type(info->content_type); if (ct) { - at->content_type = mms_unparse_http_content_type(ct); + content_type = mms_unparse_http_content_type(ct); if (!strcmp(ct[0], SMIL_CONTENT_TYPE)) { - at->flags |= MMS_ATTACHMENT_SMIL; + flags |= MMS_ATTACHMENT_SMIL; } g_strfreev(ct); } } - if (!at->content_type) { + if (!content_type) { /* Use magic to determine mime type */ const char* default_charset = "utf-8"; - const char* content_type = NULL; + const char* detected_type = NULL; const char* charset = NULL; const char* ct[4]; int n; @@ -236,50 +235,57 @@ mms_attachment_new( magic_t magic = magic_open(MAGIC_MIME_TYPE); if (magic) { if (magic_load(magic, NULL) == 0) { - content_type = magic_file(magic, path); + detected_type = magic_file(magic, path); } } /* Magic detects SMIL as text/html */ - if ((!content_type || - g_str_has_prefix(content_type, "text/")) && + if ((!detected_type || + g_str_has_prefix(detected_type, "text/")) && mms_file_is_smil(path)) { - content_type = SMIL_CONTENT_TYPE; + detected_type = SMIL_CONTENT_TYPE; } #endif - if (!content_type) { + if (!detected_type) { MMS_WARN("No mime type for %s", path); - content_type = MMS_ATTACHMENT_DEFAULT_TYPE; + detected_type = MMS_ATTACHMENT_DEFAULT_TYPE; } - if (!strcmp(content_type, SMIL_CONTENT_TYPE)) { - at->flags |= MMS_ATTACHMENT_SMIL; + if (!strcmp(detected_type, SMIL_CONTENT_TYPE)) { + flags |= MMS_ATTACHMENT_SMIL; charset = default_charset; - } else if (g_str_has_prefix(content_type, "text/")) { + } else if (g_str_has_prefix(detected_type, "text/")) { charset = default_charset; } n = 0; - ct[n++] = content_type; + ct[n++] = detected_type; if (charset) { ct[n++] = "charset"; ct[n++] = charset; } ct[n++] = NULL; - at->content_type = mms_unparse_http_content_type((char**)ct); + content_type = mms_unparse_http_content_type((char**)ct); #ifdef HAVE_MAGIC if (magic) magic_close(magic); #endif } + MMS_DEBUG("%s: %s", path, content_type); + at = g_object_new(g_str_has_prefix(content_type, "image/") ? + MMS_TYPE_ATTACHMENT_IMAGE : MMS_TYPE_ATTACHMENT, NULL); + + at->config = config; + at->map = map; + at->flags |= flags; + at->file_name = at->original_file = path; + at->content_type = content_type; at->content_location = g_path_get_basename(path); at->content_id = (info->content_id && info->content_id[0]) ? g_strdup(info->content_id) : g_strdup(at->content_location); - - MMS_DEBUG("%s: %s", path, at->content_type); return at; } g_free(path); @@ -302,6 +308,31 @@ mms_attachment_unref( if (at) g_object_unref(MMS_ATTACHMENT(at)); } +void +mms_attachment_reset( + MMSAttachment* at) +{ + if (at) { + MMSAttachmentClass* klass = MMS_ATTACHMENT_GET_CLASS(at); + if (klass->fn_reset) { + klass->fn_reset(at); + } + } +} + +gboolean +mms_attachment_resize( + MMSAttachment* at) +{ + if (at) { + MMSAttachmentClass* klass = MMS_ATTACHMENT_GET_CLASS(at); + if (klass->fn_resize) { + return klass->fn_resize(at); + } + } + return FALSE; +} + /* * Local Variables: * mode: C diff --git a/mms-lib/src/mms_attachment.h b/mms-lib/src/mms_attachment.h index ddbb0ef..d28340c 100644 --- a/mms-lib/src/mms_attachment.h +++ b/mms-lib/src/mms_attachment.h @@ -21,7 +21,8 @@ struct _mms_attachment { GObject parent; /* Parent object */ const MMSConfig* config; /* Immutable configuration */ - char* file_name; /* Full path name */ + char* original_file; /* Full path to the original file */ + const char* file_name; /* Actual file name */ char* content_type; /* Content type */ char* content_id; /* Content id */ char* content_location; /* Content location */ @@ -30,15 +31,20 @@ struct _mms_attachment { #define MMS_ATTACHMENT_SMIL (0x01) #define MMS_ATTACHMENT_DONT_DELETE_FILES (0x02) +#define MMS_ATTACHMENT_RESIZABLE (0x04) }; typedef struct mms_attachment_class { GObjectClass parent; + void (*fn_reset)(MMSAttachment* attachment); + gboolean (*fn_resize)(MMSAttachment* attachment); } MMSAttachmentClass; GType mms_attachment_get_type(void); +GType mms_attachment_image_get_type(void); #define MMS_TYPE_ATTACHMENT (mms_attachment_get_type()) +#define MMS_TYPE_ATTACHMENT_IMAGE (mms_attachment_image_get_type()) #define MMS_ATTACHMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \ MMS_TYPE_ATTACHMENT, MMSAttachmentClass)) @@ -64,6 +70,14 @@ void mms_attachment_unref( MMSAttachment* attachment); +void +mms_attachment_reset( + MMSAttachment* attachment); + +gboolean +mms_attachment_resize( + MMSAttachment* attachment); + #endif /* JOLLA_MMS_ATTACHMENT_H */ /* diff --git a/mms-lib/src/mms_attachment_image.c b/mms-lib/src/mms_attachment_image.c new file mode 100644 index 0000000..a0532e5 --- /dev/null +++ b/mms-lib/src/mms_attachment_image.c @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2013-2014 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 + * published by the Free Software Foundation. + * + * 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. + * + */ + +#include "mms_attachment.h" +#include "mms_file_util.h" + +#ifdef HAVE_IMAGEMAGICK +# include +#endif + +/* Logging */ +#define MMS_LOG_MODULE_NAME mms_attachment_log +#include "mms_lib_log.h" + +typedef MMSAttachmentClass MMSAttachmentImageClass; +typedef struct mma_attachment_image { + MMSAttachment attachment; + int resize_step; + char* resized; +} MMSAttachmentImage; + +G_DEFINE_TYPE(MMSAttachmentImage, mms_attachment_image, MMS_TYPE_ATTACHMENT); + +#define MMS_ATTACHMENT_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \ + MMS_TYPE_ATTACHMENT_IMAGE, MMSAttachmentImage)) +#define MMS_ATTACHMENT_IMAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \ + MMS_TYPE_ATTACHMENT_IMAGE, MMSAttachmentImageClass)) + +static +gboolean +mms_attachment_image_resize( + MMSAttachment* at) +{ + gboolean ok = FALSE; + +#ifdef HAVE_IMAGEMAGICK + MMSAttachmentImage* image = MMS_ATTACHMENT_IMAGE(at); + ExceptionInfo ex; + Image* src; + ImageInfo* info = CloneImageInfo(NULL); + GetExceptionInfo(&ex); + strncpy(info->filename, at->original_file, G_N_ELEMENTS(info->filename)); + info->filename[G_N_ELEMENTS(info->filename)-1] = 0; + + if (image->resized) { + remove(image->resized); + image->attachment.file_name = image->attachment.original_file; + } else { + char* dir = g_path_get_dirname(at->original_file); + char* subdir = g_strconcat(dir, "/" MMS_RESIZE_DIR, NULL); + g_mkdir_with_parents(subdir, MMS_DIR_PERM); + G_GNUC_BEGIN_IGNORE_DEPRECATIONS; + image->resized = g_strconcat(subdir, "/", + g_basename(at->original_file), NULL); + G_GNUC_END_IGNORE_DEPRECATIONS; + g_free(dir); + g_free(subdir); + } + + src = ReadImage(info, &ex); + if (src) { + if (src->magick_columns > 1 && src->magick_rows > 1) {; + const guint cols = src->magick_columns/(image->resize_step+2); + const guint rows = src->magick_rows/(image->resize_step+2); + Image* dest = ResizeImage(src, cols, rows, BoxFilter, 1.0, &ex); + if (dest) { + const char* fname = image->resized; + image->resize_step++; + strncpy(info->filename, fname, G_N_ELEMENTS(info->filename)); + strncpy(dest->filename, fname, G_N_ELEMENTS(dest->filename)); + info->filename[G_N_ELEMENTS(info->filename)-1] = 0; + dest->filename[G_N_ELEMENTS(dest->filename)-1] = 0; + if (WriteImage(info, dest)) { + GError* err = NULL; + GMappedFile* map = g_mapped_file_new(fname, FALSE, &err); + if (map) { + MMS_DEBUG("Resized %s (%ux%u)", fname, cols, rows); + image->attachment.file_name = fname; + if (at->map) g_mapped_file_unref(at->map); + at->map = map; + ok = TRUE; + } else { + MMS_ERR("%s", MMS_ERRMSG(err)); + g_error_free(err); + } + } else { + MMS_ERR("Failed to write %s", dest->filename); + } + DestroyImage(dest); + } + } + DestroyImage(src); + } else { + MMS_ERR("Failed to read %s", info->filename); + } + ClearMagickException(&ex); + DestroyExceptionInfo(&ex); + DestroyImageInfo(info); +#endif + + return ok; +} + +static +void +mms_attachment_image_reset( + MMSAttachment* at) +{ + MMSAttachmentImage* image = MMS_ATTACHMENT_IMAGE(at); + at->file_name = at->original_file; + if (image->resize_step > 0) { + if (at->map) g_mapped_file_unref(at->map); + at->map = g_mapped_file_new(at->original_file, FALSE, NULL); + } + image->resize_step = 0; +} + +static +void +mms_attachment_image_finalize( + GObject* object) +{ + MMSAttachmentImage* image = MMS_ATTACHMENT_IMAGE(object); + if (!image->attachment.config->keep_temp_files && + !(image->attachment.flags & MMS_ATTACHMENT_DONT_DELETE_FILES)) { + mms_remove_file_and_dir(image->resized); + } + g_free(image->resized); + G_OBJECT_CLASS(mms_attachment_image_parent_class)->finalize(object); +} + +static +void +mms_attachment_image_class_init( + MMSAttachmentImageClass* klass) +{ + klass->fn_reset = mms_attachment_image_reset; + klass->fn_resize = mms_attachment_image_resize; + G_OBJECT_CLASS(klass)->finalize = mms_attachment_image_finalize; +} + +static +void +mms_attachment_image_init( + MMSAttachmentImage* image) +{ + image->attachment.flags |= MMS_ATTACHMENT_RESIZABLE; +} + +/* + * Local Variables: + * mode: C + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/mms-lib/src/mms_file_util.h b/mms-lib/src/mms_file_util.h index 4a8c5f8..fd28feb 100644 --- a/mms-lib/src/mms_file_util.h +++ b/mms-lib/src/mms_file_util.h @@ -26,6 +26,7 @@ #define MMS_MESSAGE_DIR "msg" #define MMS_PARTS_DIR "parts" #define MMS_ENCODE_DIR "encode" +#define MMS_RESIZE_DIR "resize" #define MMS_NOTIFICATION_IND_FILE "m-notification.ind" #define MMS_NOTIFYRESP_IND_FILE "m-notifyresp.ind" diff --git a/mms-lib/src/mms_lib_util.c b/mms-lib/src/mms_lib_util.c index 102c07f..9dba245 100644 --- a/mms-lib/src/mms_lib_util.c +++ b/mms-lib/src/mms_lib_util.c @@ -18,10 +18,15 @@ #include "mms_task.h" #include "mms_log.h" +#ifdef HAVE_IMAGEMAGICK +# include +#endif + #define MMS_DEFAULT_ROOT_DIR "/tmp/mms" #define MMS_DEFAULT_USER_AGENT "Jolla MMS" #define MMS_DEFAULT_RETRY_SECS (15) #define MMS_DEFAULT_IDLE_SECS (20) +#define MMS_DEFAULT_SIZE_LIMIT (300*1024) /** * MMS error domain @@ -36,13 +41,29 @@ mms_lib_error_quark() * One-time initialization */ void -mms_lib_init(void) +mms_lib_init( + const char* path) { /* g_type_init has been deprecated since version 2.36 * the type system is initialised automagically since then */ G_GNUC_BEGIN_IGNORE_DEPRECATIONS; g_type_init(); G_GNUC_END_IGNORE_DEPRECATIONS; + +#ifdef HAVE_IMAGEMAGICK + MagickCoreGenesis(path, MagickFalse); +#endif +} + +/** + * Deinitialization + */ +void +mms_lib_deinit() +{ +#ifdef HAVE_IMAGEMAGICK + MagickCoreTerminus(); +#endif } /** @@ -56,6 +77,7 @@ mms_lib_default_config( config->user_agent = MMS_DEFAULT_USER_AGENT; config->retry_secs = MMS_DEFAULT_RETRY_SECS; config->idle_secs = MMS_DEFAULT_IDLE_SECS; + config->size_limit = MMS_DEFAULT_SIZE_LIMIT; config->keep_temp_files = FALSE; config->attic_enabled = FALSE; config->send_dr = TRUE; diff --git a/mms-lib/src/mms_task_encode.c b/mms-lib/src/mms_task_encode.c index 5c13996..c67405f 100644 --- a/mms-lib/src/mms_task_encode.c +++ b/mms-lib/src/mms_task_encode.c @@ -53,6 +53,7 @@ G_DEFINE_TYPE(MMSTaskEncode, mms_task_encode, MMS_TYPE_TASK); typedef enum mms_encode_state { MMS_ENCODE_STATE_NONE, MMS_ENCODE_STATE_RUNNING, + MMS_ENCODE_STATE_TOO_BIG, MMS_ENCODE_STATE_ERROR, MMS_ENCODE_STATE_DONE } MMS_ENCODE_STATE; @@ -73,18 +74,59 @@ mms_task_encode_job_done( MMSEncodeJob* job); static -void -mms_encode_job_run( +gboolean +mms_encode_job_resize( + MMSEncodeJob* job) +{ + /* Resize the largest resizable attachment */ + MMSTaskEncode* enc = job->enc; + MMSAttachment* resize_me = NULL; + unsigned int largest_size = 0; + int i; + MMS_DEBUG("Message is too big, need to resize"); + for (i=0; inparts; i++) { + MMSAttachment* part = enc->parts[i]; + if (part->flags & MMS_ATTACHMENT_RESIZABLE) { + const char* fname = part->file_name; + int fd = open(fname, O_RDONLY); + if (fd >= 0) { + struct stat st; + int err = fstat(fd, &st); + if (!err) { + if (largest_size < st.st_size) { + largest_size = st.st_size; + resize_me = part; + } + } else { + MMS_ERR("Can't stat %s: %s", fname, strerror(errno)); + } + close(fd); + } else { + MMS_ERR("Can't open %s: %s", fname, strerror(errno)); + } + } + } + if (resize_me) { + MMS_DEBUG("Resizing %s", resize_me->original_file); + return mms_attachment_resize(resize_me); + } else { + MMS_DEBUG("There is nothing to resize"); + return FALSE; + } +} + +static +gsize +mms_encode_job_encode( MMSEncodeJob* job) { MMSTaskEncode* enc = job->enc; - MMS_ENCODE_STATE final_state = MMS_ENCODE_STATE_ERROR; + gsize pdu_size = 0; char* dir; int fd; - job->state = MMS_ENCODE_STATE_RUNNING; - - MMS_ASSERT(!job->path); + g_free(job->path); + job->path = NULL; dir = mms_task_dir(&enc->task); fd = mms_create_file(dir, MMS_SEND_REQ_FILE, &job->path, NULL); if (fd >= 0) { @@ -128,21 +170,68 @@ mms_encode_job_run( ok = mms_message_encode(mms, fd); mms_message_free(mms); g_free(start); - close(fd); if (ok) { - MMS_DEBUG("Created %s", job->path); - final_state = MMS_ENCODE_STATE_DONE; + struct stat st; + int err = fstat(fd, &st); + if (!err) { + pdu_size = st.st_size; + MMS_DEBUG("Created %s (%d bytes)", job->path, (int)pdu_size); + } else { + MMS_ERR("Can't stat %s: %s", job->path, strerror(errno)); + ok = FALSE; + } } else { MMS_ERR("Failed to encode message"); + } + + close(fd); + if (!ok) { unlink(job->path); g_free(job->path); job->path = NULL; } } g_free(dir); + return pdu_size; +} - job->state = final_state; +static +void +mms_encode_job_run( + MMSEncodeJob* job) +{ + int i; + gsize size; + const MMSConfig* config = job->enc->task.config; + MMSTaskEncode* enc = job->enc; + + job->state = MMS_ENCODE_STATE_RUNNING; + + /* Reset the resizing state */ + for (i=0; inparts; i++) { + mms_attachment_reset(enc->parts[i]); + } + + /* Keep resizing attachments until we squeeze them into the limit */ + size = mms_encode_job_encode(job); + while (config->size_limit && size > config->size_limit && + !g_cancellable_is_cancelled(job->cancellable) && + mms_encode_job_resize(job)) { + gsize last_size = size; + size = mms_encode_job_encode(job); + if (!size || size >= last_size) break; + } + + if (size > 0 && (!config->size_limit || size <= config->size_limit)) { + job->state = MMS_ENCODE_STATE_DONE; + } else { + unlink(job->path); + g_free(job->path); + job->path = NULL; + job->state = (size > 0) ? MMS_ENCODE_STATE_TOO_BIG : + MMS_ENCODE_STATE_ERROR; + } } static @@ -228,7 +317,8 @@ mms_task_encode_job_done( task->config, task->handler, task->id, task->imsi)); } else { mms_handler_message_send_state_changed(task->handler, task->id, - MMS_SEND_STATE_SEND_ERROR); + (job->state == MMS_ENCODE_STATE_TOO_BIG) ? + MMS_SEND_STATE_TOO_BIG : MMS_SEND_STATE_SEND_ERROR); } mms_task_set_state(&enc->task, MMS_TASK_STATE_DONE); mms_encode_job_unref(job); @@ -267,6 +357,16 @@ mms_task_encode_run( } } +static +void +mms_task_encode_cancel( + MMSTask* task) +{ + MMSTaskEncode* enc = MMS_TASK_ENCODE(task); + if (enc->active_job) g_cancellable_cancel(enc->active_job->cancellable); + MMS_TASK_CLASS(mms_task_encode_parent_class)->fn_cancel(task); +} + static void mms_task_encode_finalize( @@ -433,6 +533,7 @@ mms_task_encode_class_init( MMSTaskEncodeClass* klass) { klass->fn_run = mms_task_encode_run; + klass->fn_cancel = mms_task_encode_cancel; G_OBJECT_CLASS(klass)->finalize = mms_task_encode_finalize; } diff --git a/rpm/mms-engine.spec b/rpm/mms-engine.spec index 0476f7b..31e778d 100644 --- a/rpm/mms-engine.spec +++ b/rpm/mms-engine.spec @@ -8,9 +8,11 @@ URL: https://github.com/nemomobile/mms-engine Source0: %{name}-%{version}.tar.bz2 Requires: dbus Requires: ofono +Requires: ImageMagick BuildRequires: python BuildRequires: file-devel +BuildRequires: pkgconfig(ImageMagick) BuildRequires: pkgconfig(glib-2.0) >= 2.32 BuildRequires: pkgconfig(libsoup-2.4) >= 2.38 BuildRequires: pkgconfig(libwspcodec) >= 2.1