Commit 7fdf73ed authored by Slava Monich's avatar Slava Monich

Preliminary support for sending MMS messages

parent c4e37255
......@@ -24,3 +24,4 @@ mms-handler-dbus/test/mms_handler_dbus_client/build
mms-dump/build
mms-dump/mms_dump.ncb
mms-dump/mms_dump.opt
mms-send/build
......@@ -90,7 +90,7 @@ endif
DEBUG_CFLAGS = $(DEBUG_FLAGS) $(DEBUG_DEFS) $(CFLAGS)
RELEASE_CFLAGS = $(RELEASE_FLAGS) $(RELEASE_DEFS) $(CFLAGS)
LIBS = $(shell pkg-config --libs $(LIB_PKGS))
LIBS = $(shell pkg-config --libs $(LIB_PKGS)) -lmagic
DEBUG_LIBS = \
$(MMS_OFONO_DEBUG_LIB) \
$(MMS_HANDLER_DEBUG_LIB) \
......
......@@ -35,6 +35,8 @@ CONFIG(debug, debug|release) {
LIBS += $$MMS_LIB_DIR/build/release/libmms-lib.a
}
LIBS += -lmagic
MMS_ENGINE_DBUS_XML = $$DBUS_INTERFACE_DIR/org.nemomobile.MmsEngine.xml
MMS_ENGINE_DBUS_H = org.nemomobile.MmsEngine.h
org_nemomobile_mmsengine_h.input = MMS_ENGINE_DBUS_XML
......
......@@ -39,6 +39,7 @@ struct mms_engine {
gboolean stopped;
gboolean keep_running;
guint start_timeout_id;
gulong send_message_id;
gulong push_signal_id;
gulong push_notify_signal_id;
gulong receive_signal_id;
......@@ -129,6 +130,89 @@ mms_engine_handle_test(
}
#endif /* ENABLE_TEST */
/* org.nemomobile.MmsEngine.sendMessage */
static
gboolean
mms_engine_handle_send_message(
OrgNemomobileMmsEngine* proxy,
GDBusMethodInvocation* call,
int database_id,
const char* imsi_to,
const char* const* to,
const char* const* cc,
const char* const* bcc,
const char* subject,
guint flags,
GVariant* attachments,
MMSEngine* engine)
{
if (to && *to) {
unsigned int i;
char* to_list = g_strjoinv(",", (char**)to);
char* cc_list = NULL;
char* bcc_list = NULL;
char* id = NULL;
char* imsi;
MMSAttachmentInfo* parts;
GArray* info = g_array_sized_new(FALSE, FALSE, sizeof(*parts), 0);
GError* error = NULL;
/* Extract attachment info */
char* fn = NULL;
char* ct = NULL;
char* cid = NULL;
GVariantIter* iter = NULL;
g_variant_get(attachments, "a(sss)", &iter);
while (g_variant_iter_loop(iter, "(sss)", &fn, &ct, &cid)) {
MMSAttachmentInfo part;
part.file_name = g_strdup(fn);
part.content_type = g_strdup(ct);
part.content_id = g_strdup(cid);
g_array_append_vals(info, &part, 1);
}
/* Convert address lists into comma-separated strings
* expected by mms_dispatcher_send_message and mms_codec */
if (cc && *cc) cc_list = g_strjoinv(",", (char**)cc);
if (bcc && *bcc) bcc_list = g_strjoinv(",", (char**)bcc);
if (database_id > 0) id = g_strdup_printf("%u", database_id);
/* Queue the message */
parts = (void*)info->data;
imsi = mms_dispatcher_send_message(engine->dispatcher, id,
imsi_to, to_list, cc_list, bcc_list, subject, flags, parts,
info->len, &error);
if (imsi) {
if (mms_dispatcher_start(engine->dispatcher)) {
mms_engine_start_timeout_cancel(engine);
}
org_nemomobile_mms_engine_complete_send_message(proxy, call, imsi);
g_free(imsi);
} else {
g_dbus_method_invocation_return_error(call, G_DBUS_ERROR,
G_DBUS_ERROR_FAILED, "%s", MMS_ERRMSG(error));
g_error_free(error);
}
for (i=0; i<info->len; i++) {
g_free((void*)parts[i].file_name);
g_free((void*)parts[i].content_type);
g_free((void*)parts[i].content_id);
}
g_free(to_list);
g_free(cc_list);
g_free(bcc_list);
g_free(id);
g_array_unref(info);
g_variant_iter_free(iter);
} else {
g_dbus_method_invocation_return_error(call, G_DBUS_ERROR,
G_DBUS_ERROR_FAILED, "Missing recipient");
}
return TRUE;
}
/* org.nemomobile.MmsEngine.receiveMessage */
static
gboolean
......@@ -343,6 +427,9 @@ mms_engine_new(
mms->log_modules = log_modules;
mms->log_count = log_count;
mms->proxy = org_nemomobile_mms_engine_skeleton_new();
mms->send_message_id =
g_signal_connect(mms->proxy, "handle-send-message",
G_CALLBACK(mms_engine_handle_send_message), mms);
mms->push_signal_id =
g_signal_connect(mms->proxy, "handle-push",
G_CALLBACK(mms_engine_handle_push), mms);
......@@ -486,6 +573,7 @@ mms_engine_dispose(
#ifdef ENABLE_TEST
g_signal_handler_disconnect(e->proxy, e->test_signal_id);
#endif
g_signal_handler_disconnect(e->proxy, e->send_message_id);
g_signal_handler_disconnect(e->proxy, e->push_signal_id);
g_signal_handler_disconnect(e->proxy, e->push_notify_signal_id);
g_signal_handler_disconnect(e->proxy, e->receive_signal_id);
......
......@@ -131,7 +131,7 @@ mms_handler_dbus_message_received(
return ok;
}
/* Updates message state in the database */
/* Updates message receive state in the database */
static
gboolean
mms_handler_dbus_message_receive_state_changed(
......@@ -155,6 +155,53 @@ mms_handler_dbus_message_receive_state_changed(
return ok;
}
/* Updates message send state in the database */
static
gboolean
mms_handler_dbus_message_send_state_changed(
MMSHandler* handler,
const char* id,
MMS_SEND_STATE state)
{
gboolean ok = FALSE;
OrgNemomobileMmsHandler* proxy = mms_handler_dbus_connect(handler);
MMS_ASSERT(id && id[0]);
if (id && id[0] && proxy) {
GError* error = NULL;
if (org_nemomobile_mms_handler_call_message_send_state_changed_sync(
proxy, id, state, NULL, &error)) {
ok = TRUE;
} else {
MMS_ERR("%s", MMS_ERRMSG(error));
g_error_free(error);
}
}
return ok;
}
/* Message has been sent */
gboolean
mms_handler_dbus_message_sent(
MMSHandler* handler,
const char* id,
const char* msgid)
{
gboolean ok = FALSE;
OrgNemomobileMmsHandler* proxy = mms_handler_dbus_connect(handler);
MMS_ASSERT(id && id[0]);
if (id && id[0] && msgid && msgid[0] && proxy) {
GError* error = NULL;
if (org_nemomobile_mms_handler_call_message_sent_sync(proxy,
id, msgid, NULL, &error)) {
ok = TRUE;
} else {
MMS_ERR("%s", MMS_ERRMSG(error));
g_error_free(error);
}
}
return ok;
}
static
void
mms_handler_dbus_dispose(
......@@ -177,6 +224,9 @@ mms_handler_dbus_class_init(
klass->fn_message_received = mms_handler_dbus_message_received;
klass->fn_message_receive_state_changed =
mms_handler_dbus_message_receive_state_changed;
klass->fn_message_send_state_changed =
mms_handler_dbus_message_send_state_changed;
klass->fn_message_sent = mms_handler_dbus_message_sent;
G_OBJECT_CLASS(klass)->dispose = mms_handler_dbus_dispose;
}
......
......@@ -15,11 +15,12 @@ all: debug release
# Sources
#
SRC = 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_http.c mms_task_notification.c mms_task_notifyresp.c \
mms_task_publish.c mms_task_read.c mms_task_retrieve.c mms_util.c
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 \
mms_task_notifyresp.c mms_task_publish.c mms_task_read.c \
mms_task_retrieve.c mms_task_send.c mms_util.c
#
# Directories
......
......@@ -79,6 +79,23 @@ mms_dispatcher_send_read_report(
MMSReadStatus status,
GError** error);
char*
mms_dispatcher_send_message(
MMSDispatcher* dispatcher,
const char* id,
const char* imsi,
const char* to,
const char* cc,
const char* bcc,
const char* subject,
unsigned int flags,
const MMSAttachmentInfo* parts,
unsigned int nparts,
GError** error);
#define MMS_SEND_FLAG_REQUEST_DELIVERY_REPORT (0x01)
#define MMS_SEND_FLAG_REQUEST_READ_REPORT (0x02)
void
mms_dispatcher_cancel(
MMSDispatcher* dispatcher,
......
......@@ -28,6 +28,18 @@ typedef enum _mmm_receive_state {
MMS_RECEIVE_STATE_DECODING_ERROR
} MMS_RECEIVE_STATE;
/* Send state */
typedef enum _mmm_send_state {
MMS_SEND_STATE_INVALID = -1,
MMS_SEND_STATE_ENCODING,
MMS_SEND_STATE_TOO_BIG,
MMS_SEND_STATE_SENDING,
MMS_SEND_STATE_DEFERRED,
MMS_SEND_STATE_NO_SPACE,
MMS_SEND_STATE_SEND_ERROR,
MMS_SEND_STATE_REFUSED
} MMS_SEND_STATE;
/* Class */
typedef struct mms_handler_class {
GObjectClass parent;
......@@ -53,6 +65,18 @@ typedef struct mms_handler_class {
MMSHandler* handler, /* Handler instance */
MMSMessage* msg); /* Decoded message */
/* Sets the send state */
gboolean (*fn_message_send_state_changed)(
MMSHandler* handler, /* Handler instance */
const char* id, /* Handler record id */
MMS_SEND_STATE state); /* Receive state */
/* Message has been sent */
gboolean (*fn_message_sent)(
MMSHandler* handler, /* Handler instance */
const char* id, /* Handler record id */
const char* msgid); /* Message id assigned by operator */
} MMSHandlerClass;
GType mms_handler_get_type(void);
......@@ -86,6 +110,18 @@ mms_handler_message_received(
MMSHandler* handler, /* Handler instance */
MMSMessage* msg); /* Decoded message */
gboolean
mms_handler_message_send_state_changed(
MMSHandler* handler, /* Handler instance */
const char* id, /* Handler record id */
MMS_SEND_STATE state); /* Receive state */
gboolean
mms_handler_message_sent(
MMSHandler* handler, /* Handler instance */
const char* id, /* Handler record id */
const char* msgid); /* Message id assigned by operator */
#endif /* JOLLA_MMS_HANDLER_H */
/*
......
......@@ -21,9 +21,11 @@
log(mms_dispatcher_log)\
log(mms_handler_log)\
log(mms_message_log)\
log(mms_attachment_log)\
log(mms_task_log)\
log(mms_task_http_log)\
log(mms_task_decode_log)\
log(mms_task_encode_log)\
log(mms_task_notification_log)\
log(mms_task_retrieve_log)\
log(mms_task_publish_log)\
......
......@@ -49,6 +49,13 @@ typedef struct mms_config {
gboolean send_dr; /* Allow sending delivery reports */
} MMSConfig;
/* Attachment information */
typedef struct mms_attachment_info {
const char* file_name; /* Full path name */
const char* content_type; /* Content type */
const char* content_id; /* Content id */
} MMSAttachmentInfo;
/* Types */
typedef GObject MMSHandler;
typedef GObject MMSConnMan;
......@@ -57,9 +64,11 @@ typedef struct mms_dispatcher MMSDispatcher;
typedef struct mms_connection MMSConnection;
typedef struct mms_message MMSPdu;
typedef struct _mms_message MMSMessage;
typedef struct _mms_attachment MMSAttachment;
/* MMS content type */
#define MMS_CONTENT_TYPE "application/vnd.wap.mms-message"
#define SMIL_CONTENT_TYPE "application/smil"
/* MMS read status */
typedef enum mms_read_status {
......
......@@ -14,6 +14,7 @@ CONFIG(debug, debug|release) {
}
SOURCES += \
src/mms_attachment.c \
src/mms_codec.c \
src/mms_connection.c \
src/mms_connman.c \
......@@ -27,15 +28,18 @@ SOURCES += \
src/mms_task.c \
src/mms_task_ack.c \
src/mms_task_decode.c \
src/mms_task_encode.c \
src/mms_task_http.c \
src/mms_task_notification.c \
src/mms_task_notifyresp.c \
src/mms_task_publish.c \
src/mms_task_read.c \
src/mms_task_retrieve.c \
src/mms_task_send.c \
src/mms_util.c
HEADERS += \
src/mms_attachment.h \
src/mms_codec.h \
src/mms_error.h \
src/mms_file_util.h \
......
/*
* 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"
#include "mms_codec.h"
#include <magic.h>
/* Logging */
#define MMS_LOG_MODULE_NAME mms_attachment_log
#include "mms_lib_log.h"
#include "mms_error.h"
MMS_LOG_MODULE_DEFINE("mms-attachment");
#define MMS_ATTACHMENT_DEFAULT_TYPE "application/octet-stream"
G_DEFINE_TYPE(MMSAttachment, mms_attachment, G_TYPE_OBJECT);
#define MMS_ATTACHMENT(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj), MMS_TYPE_ATTACHMENT, MMSAttachment))
#define MMS_ATTACHMENT_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS((obj), MMS_TYPE_ATTACHMENT, MMSAttachmentClass))
#define REGION_TEXT "Text"
#define REGION_MEDIA "Media"
#define MEDIA_TEXT "text"
#define MEDIA_IMAGE "img"
#define MEDIA_VIDEO "video"
#define MEDIA_AUDIO "audio"
#define MEDIA_OTHER "ref"
static
void
mms_attachment_finalize(
GObject* object)
{
MMSAttachment* at = MMS_ATTACHMENT(object);
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);
rmdir(dir);
g_free(dir);
}
g_free(at->file_name);
g_free(at->content_type);
g_free(at->content_location);
g_free(at->content_id);
G_OBJECT_CLASS(mms_attachment_parent_class)->finalize(object);
}
static
void
mms_attachment_class_init(
MMSAttachmentClass* klass)
{
G_OBJECT_CLASS(klass)->finalize = mms_attachment_finalize;
}
static
void
mms_attachment_init(
MMSAttachment* attachment)
{
MMS_VERBOSE_("%p", attachment);
}
static
char*
mms_attachment_get_path(
const char* file,
GError** error)
{
char* path = g_malloc(PATH_MAX);
if (realpath(file, path)) {
if (g_file_test(path, G_FILE_TEST_IS_REGULAR)) {
char* fname = g_strdup(path);
g_free(path);
return fname;
} else {
MMS_ERROR(error, MMS_LIB_ERROR_IO, "%s not found", file);
}
} else {
MMS_ERROR(error, MMS_LIB_ERROR_IO, "%s: %s\n", file, strerror(errno));
}
return NULL;
}
gboolean
mms_attachment_write_smil(
FILE* f,
MMSAttachment** ats,
int n,
GError** error)
{
if (fputs(
"<!DOCTYPE smil PUBLIC \"-//W3C//DTD SMIL 1.0//EN\" "
"\"http://www.w3.org/TR/REC-smil/SMIL10.dtd\">\n"
"<smil>\n"
" <head>\n"
" <layout>\n"
" <root-layout height=\"160\" width=\"120\"/>\n"
" <region fit=\"scroll\" height=\"100%\" left=\"0\" "
"top=\"0\" width=\"100%\" id=\"" REGION_TEXT "\"/>\n"
" <region fit=\"meet\" height=\"100%\" left=\"0\" "
"top=\"0\" width=\"100%\" id=\"" REGION_MEDIA "\"/>\n"
" </layout>\n"
" </head>\n"
" <body>\n"
" <par dur=\"5000ms\">\n", f) >= 0) {
int i;
for (i=0; i<n; i++) {
const MMSAttachment* at = ats[i];
const char* elem;
const char* region;
MMS_ASSERT(!(at->flags & MMS_ATTACHMENT_SMIL));
if (g_str_has_prefix(at->content_type, "text/")) {
elem = "text";
region = REGION_TEXT;
} else {
region = REGION_MEDIA;
if (g_str_has_prefix(at->content_type, "image/")) {
elem = "img";
} else if (g_str_has_prefix(at->content_type, "video/")) {
elem = "video";
} else if (g_str_has_prefix(at->content_type, "audio/")) {
elem = "audio";
} else {
elem = "ref";
}
}
if (fprintf(f, " <%s src=\"%s\" region=\"%s\"/>\n", elem,
at->content_location, region) < 0) {
break;
}
}
if (i == n && fputs(" </par>\n </body>\n</smil>\n", f) >= 0) {
return TRUE;
}
}
MMS_ERROR(error, MMS_LIB_ERROR_IO, "Error writing SMIL: %s",
strerror(errno));
return FALSE;
}
MMSAttachment*
mms_attachment_new_smil(
const MMSConfig* config,
const char* path,
MMSAttachment** ats,
int n,
GError** error)
{
MMSAttachment* smil = NULL;
int fd = open(path, O_CREAT|O_RDWR|O_TRUNC, MMS_FILE_PERM);
if (fd >= 0) {
FILE* f = fdopen(fd, "w");
if (f) {
gboolean ok = mms_attachment_write_smil(f, ats, n, error);
fclose(f);
if (ok) {
MMSAttachmentInfo ai;
ai.file_name = path;
ai.content_type = SMIL_CONTENT_TYPE "; charset=utf-8";
ai.content_id = NULL;
smil = mms_attachment_new(config, &ai, error);
MMS_ASSERT(smil && (smil->flags & MMS_ATTACHMENT_SMIL));
}
} else {
MMS_ERROR(error, MMS_LIB_ERROR_IO,
"Failed to open file %s: %s", path, strerror(errno));
close(fd);
}
} else {
MMS_ERROR(error, MMS_LIB_ERROR_IO,
"Failed to create file %s: %s", path, strerror(errno));
}
return smil;
}
MMSAttachment*
mms_attachment_new(
const MMSConfig* config,
const MMSAttachmentInfo* info,
GError** error)
{
char* path = mms_attachment_get_path(info->file_name, error);
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;
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);
if (!strcmp(ct[0], SMIL_CONTENT_TYPE)) {
at->flags |= MMS_ATTACHMENT_SMIL;
}
g_strfreev(ct);
}
}
if (!at->content_type) {
/* Use magic to determine mime type */
const char* default_charset = "utf-8";
const char* content_type = NULL;
const char* charset = NULL;
const char* ct[4];
int n;
magic_t magic = magic_open(MAGIC_MIME_TYPE);
if (magic) {
if (magic_load(magic, NULL) == 0) {
content_type = magic_file(magic, path);
}
}
/* Magic detects SMIL as text/html */
if ((!content_type ||
g_str_has_prefix(content_type, "text/")) &&
mms_file_is_smil(path)) {
content_type = SMIL_CONTENT_TYPE;
}
if (!content_type) {
MMS_WARN("No mime type for %s", path);
content_type = MMS_ATTACHMENT_DEFAULT_TYPE;
}
if (!strcmp(content_type, SMIL_CONTENT_TYPE)) {
at->flags |= MMS_ATTACHMENT_SMIL;
charset = default_charset;
} else if (g_str_has_prefix(content_type, "text/")) {
charset = default_charset;
}
n = 0;
ct[n++] = content_type;
if (charset) {
ct[n++] = "charset";
ct[n++] = charset;
}
ct[n++] = NULL;
at->content_type = mms_unparse_http_content_type((char**)ct);
if (magic) magic_close(magic);
}
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);
}
return NULL;
}
MMSAttachment*
mms_attachment_ref(
MMSAttachment* at)
{
if (at) g_object_ref(MMS_ATTACHMENT(at));
return at;
}
void
mms_attachment_unref(
MMSAttachment* at)
{
if (at) g_object_unref(MMS_ATTACHMENT(at));
}
/*
* Local Variables:
* mode: C
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/
/*
* 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.
*
*/
#ifndef JOLLA_MMS_ATTACHMENT_H
#define JOLLA_MMS_ATTACHMENT_H
#include "mms_lib_types.h"
/* Attachment object */
struct _mms_attachment {
GObject parent; /* Parent object */
const MMSConfig* config; /* Immutable configuration */
char* file_name; /* Full path name */
char* content_type; /* Content type */
char* content_id; /* Content id */
char* content_location; /* Content location */
GMappedFile* map; /* Mapped attachment file */
unsigned int flags; /* Flags: */
#define MMS_ATTACHMENT_SMIL (0x01)
#define MMS_ATTACHMENT_DONT_DELETE_FILES (0x02)
};
typedef struct mms_attachment_class {
GObjectClass parent;
} MMSAttachmentClass;
GType mms_attachment_get_type(void);
#define MMS_TYPE_ATTACHMENT (mms_attachment_get_type())
#define MMS_ATTACHMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \
MMS_TYPE_ATTACHMENT, MMSAttachmentClass))
MMSAttachment*
mms_attachment_new(
const MMSConfig* config,
const MMSAttachmentInfo* info,
GError** error);