mirror of
https://github.com/sailfishos/ofono
synced 2025-11-28 05:21:05 +08:00
Push forwarder plugin
This commit is contained in:
committed by
Martti Piirainen
parent
e25b5b13f9
commit
24e8514c55
@@ -549,6 +549,11 @@ builtin_sources += plugins/smart-messaging.c
|
||||
builtin_modules += push_notification
|
||||
builtin_sources += plugins/push-notification.c
|
||||
|
||||
builtin_modules += push_forwarder
|
||||
builtin_sources += plugins/push-forwarder.c
|
||||
builtin_cflags += @WSPCODEC_CFLAGS@
|
||||
builtin_libadd += @WSPCODEC_LIBS@
|
||||
|
||||
builtin_modules += sms_history
|
||||
builtin_sources += plugins/smshistory.c
|
||||
|
||||
|
||||
@@ -91,6 +91,11 @@ else
|
||||
fi
|
||||
AC_SUBST(DBUS_CONFDIR)
|
||||
|
||||
PKG_CHECK_MODULES(WSPCODEC, libwspcodec >= 2.0, dummy=yes,
|
||||
AC_MSG_ERROR(WSP decoder is required))
|
||||
AC_SUBST(WSPCODEC_CFLAGS)
|
||||
AC_SUBST(WSPCODEC_LIBS)
|
||||
|
||||
AC_ARG_WITH(dbusdatadir, AC_HELP_STRING([--with-dbusdatadir=PATH],
|
||||
[path to D-Bus data directory]), [path_dbusdata=${withval}],
|
||||
[path_dbusdata="`$PKG_CONFIG --variable=datadir dbus-1`"])
|
||||
|
||||
536
ofono/plugins/push-forwarder.c
Normal file
536
ofono/plugins/push-forwarder.c
Normal file
@@ -0,0 +1,536 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <glib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <wspcodec.h>
|
||||
|
||||
#define OFONO_API_SUBJECT_TO_CHANGE
|
||||
#include <ofono.h>
|
||||
#include <plugin.h>
|
||||
|
||||
/*
|
||||
* Push forwarder plugin is looking for configuration files in
|
||||
* /etc/ofono/push_forwarder.d directory. Confiration files are
|
||||
* glib key files that look like this:
|
||||
*
|
||||
* [Jolla MMS Handler]
|
||||
* ContentType = application/vnd.wap.mms-message
|
||||
* Interface = com.jolla.MmsEngine.
|
||||
* Service = com.jolla.MmsEngine
|
||||
* Method = HandlePush
|
||||
* Path = /
|
||||
*
|
||||
* Only files with .conf suffix are loaded. In addition to the keys
|
||||
* from the above example, SourcePort and DestinationPort port keys
|
||||
* are supported. All other keys are ignored. One file may describe
|
||||
* several push handlers. See pf_parse_config() function for details.
|
||||
*
|
||||
* When push fowarder receives a WAP push, it goes through the list
|
||||
* of registered handlers and invokes all of them that match content
|
||||
* type and/or port numbers. The rest is up to the D-Bus service
|
||||
* handling the call.
|
||||
*/
|
||||
|
||||
#define PF_CONFIG_DIR CONFIGDIR "/push_forwarder.d"
|
||||
|
||||
struct pf_modem {
|
||||
struct ofono_modem *modem;
|
||||
struct ofono_sms *sms;
|
||||
struct ofono_sim *sim;
|
||||
unsigned int sim_watch_id;
|
||||
unsigned int sms_watch_id;
|
||||
unsigned int push_watch_id;
|
||||
};
|
||||
|
||||
struct push_datagram_handler {
|
||||
char *name;
|
||||
char *content_type;
|
||||
char *interface;
|
||||
char *service;
|
||||
char *method;
|
||||
char *path;
|
||||
int dst_port;
|
||||
int src_port;
|
||||
};
|
||||
|
||||
static GSList *handlers;
|
||||
static GSList *modems;
|
||||
static unsigned int modem_watch_id;
|
||||
static int inotify_fd = -1;
|
||||
static int inotify_watch_id = -1;
|
||||
static guint inotify_watch_source_id;
|
||||
static GIOChannel *inotify_watch_channel;
|
||||
|
||||
static void pf_notify_handler(struct push_datagram_handler *h,
|
||||
const char *imsi, const char *from, const struct tm *remote,
|
||||
const struct tm *local, int dst, int src,
|
||||
const void *data, unsigned int len)
|
||||
{
|
||||
struct tm remote_tm = *remote;
|
||||
struct tm local_tm = *local;
|
||||
dbus_uint32_t remote_time_arg = mktime(&remote_tm);
|
||||
dbus_uint32_t local_time_arg = mktime(&local_tm);
|
||||
dbus_int32_t dst_arg = dst;
|
||||
dbus_int32_t src_arg = src;
|
||||
DBusMessageIter iter, array;
|
||||
DBusMessage *msg = dbus_message_new_method_call(h->service,
|
||||
h->path, h->interface, h->method);
|
||||
|
||||
dbus_message_append_args(msg,
|
||||
DBUS_TYPE_STRING, &imsi,
|
||||
DBUS_TYPE_STRING, &from,
|
||||
DBUS_TYPE_UINT32, &remote_time_arg,
|
||||
DBUS_TYPE_UINT32, &local_time_arg,
|
||||
DBUS_TYPE_INT32, &dst_arg,
|
||||
DBUS_TYPE_INT32, &src_arg,
|
||||
DBUS_TYPE_INVALID);
|
||||
dbus_message_iter_init_append(msg, &iter);
|
||||
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
|
||||
DBUS_TYPE_BYTE_AS_STRING, &array);
|
||||
dbus_message_iter_append_fixed_array(&array,
|
||||
DBUS_TYPE_BYTE, &data, len);
|
||||
dbus_message_iter_close_container(&iter, &array);
|
||||
dbus_message_set_no_reply(msg, TRUE);
|
||||
dbus_connection_send(ofono_dbus_get_connection(), msg, NULL);
|
||||
dbus_message_unref(msg);
|
||||
}
|
||||
|
||||
static gboolean pf_match_port(int port, int expected_port)
|
||||
{
|
||||
if (expected_port < 0)
|
||||
return TRUE;
|
||||
|
||||
if (expected_port == port)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean pf_match_handler(struct push_datagram_handler *h,
|
||||
const char *ct, int dst, int src)
|
||||
{
|
||||
if (pf_match_port(dst, h->dst_port) == FALSE)
|
||||
return FALSE;
|
||||
|
||||
if (pf_match_port(src, h->src_port) == FALSE)
|
||||
return FALSE;
|
||||
|
||||
if (h->content_type == NULL)
|
||||
return TRUE;
|
||||
|
||||
if (strcmp(h->content_type, ct) == 0)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void pf_handle_datagram(const char *from,
|
||||
const struct tm *remote, const struct tm *local, int dst,
|
||||
int src, const unsigned char *buffer, unsigned int len,
|
||||
void *userdata)
|
||||
{
|
||||
struct pf_modem *pm = userdata;
|
||||
guint remain;
|
||||
const guint8 *data;
|
||||
unsigned int hdrlen;
|
||||
unsigned int off;
|
||||
const void *ct;
|
||||
const char *imsi;
|
||||
GSList *link;
|
||||
|
||||
DBG("received push of size: %u", len);
|
||||
|
||||
if (pm->sim == NULL)
|
||||
return;
|
||||
|
||||
imsi = ofono_sim_get_imsi(pm->sim);
|
||||
if (len < 3)
|
||||
return;
|
||||
|
||||
if (buffer[1] != 6)
|
||||
return;
|
||||
|
||||
remain = len - 2;
|
||||
data = buffer + 2;
|
||||
|
||||
if (wsp_decode_uintvar(data, remain, &hdrlen, &off) == FALSE)
|
||||
return;
|
||||
|
||||
if ((off + hdrlen) > remain)
|
||||
return;
|
||||
|
||||
data += off;
|
||||
remain -= off;
|
||||
|
||||
DBG(" WAP header %u bytes", hdrlen);
|
||||
|
||||
if (wsp_decode_content_type(data, hdrlen, &ct, &off, NULL) == FALSE)
|
||||
return;
|
||||
|
||||
DBG(" content type %s", (char *)ct);
|
||||
DBG(" imsi %s", imsi);
|
||||
|
||||
link = handlers;
|
||||
|
||||
while (link) {
|
||||
struct push_datagram_handler *h = link->data;
|
||||
|
||||
if (pf_match_handler(h, ct, dst, src) != FALSE) {
|
||||
DBG("notifying %s", h->name);
|
||||
pf_notify_handler(h, imsi, from,
|
||||
remote, local, dst, src, buffer, len);
|
||||
}
|
||||
link = link->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void pf_sms_watch(struct ofono_atom *atom,
|
||||
enum ofono_atom_watch_condition cond, void *userdata)
|
||||
{
|
||||
struct pf_modem *pm = userdata;
|
||||
|
||||
if (cond == OFONO_ATOM_WATCH_CONDITION_REGISTERED) {
|
||||
DBG("registered");
|
||||
pm->sms = __ofono_atom_get_data(atom);
|
||||
pm->push_watch_id = __ofono_sms_datagram_watch_add(pm->sms,
|
||||
pf_handle_datagram, -1, -1, pm, NULL);
|
||||
} else if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) {
|
||||
DBG("unregistered");
|
||||
pm->sms = NULL;
|
||||
pm->push_watch_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void pf_sms_watch_done(void *userdata)
|
||||
{
|
||||
struct pf_modem *pm = userdata;
|
||||
|
||||
pm->sms_watch_id = 0;
|
||||
}
|
||||
|
||||
static void pf_sim_watch(struct ofono_atom *atom,
|
||||
enum ofono_atom_watch_condition cond, void *userdata)
|
||||
{
|
||||
struct pf_modem *pm = userdata;
|
||||
|
||||
if (cond == OFONO_ATOM_WATCH_CONDITION_REGISTERED) {
|
||||
DBG("registered");
|
||||
pm->sim = __ofono_atom_get_data(atom);
|
||||
} else if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) {
|
||||
DBG("unregistered");
|
||||
pm->sim = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void pf_sim_watch_done(void *userdata)
|
||||
{
|
||||
struct pf_modem *pm = userdata;
|
||||
|
||||
pm->sim_watch_id = 0;
|
||||
}
|
||||
|
||||
static void pf_free_modem(struct pf_modem *pm)
|
||||
{
|
||||
if (pm == NULL)
|
||||
return;
|
||||
|
||||
if (pm->push_watch_id != 0)
|
||||
__ofono_sms_datagram_watch_remove(pm->sms, pm->push_watch_id);
|
||||
|
||||
if (pm->sim_watch_id != 0)
|
||||
__ofono_modem_remove_atom_watch(pm->modem, pm->sim_watch_id);
|
||||
|
||||
if (pm->sms_watch_id != 0)
|
||||
__ofono_modem_remove_atom_watch(pm->modem, pm->sms_watch_id);
|
||||
|
||||
g_free(pm);
|
||||
}
|
||||
|
||||
static void pf_modem_watch(struct ofono_modem *modem,
|
||||
gboolean added, void *userdata)
|
||||
{
|
||||
DBG("modem: %p, added: %d", modem, added);
|
||||
if (added != FALSE) {
|
||||
struct pf_modem *pm;
|
||||
|
||||
pm = g_try_new0(struct pf_modem, 1);
|
||||
if (pm == NULL)
|
||||
return;
|
||||
|
||||
pm->modem = modem;
|
||||
pm->sim_watch_id = __ofono_modem_add_atom_watch(modem,
|
||||
OFONO_ATOM_TYPE_SMS, pf_sms_watch, pm,
|
||||
pf_sms_watch_done);
|
||||
pm->sms_watch_id = __ofono_modem_add_atom_watch(modem,
|
||||
OFONO_ATOM_TYPE_SIM, pf_sim_watch, pm,
|
||||
pf_sim_watch_done);
|
||||
modems = g_slist_append(modems, pm);
|
||||
} else {
|
||||
GSList *link = modems;
|
||||
|
||||
while (link) {
|
||||
struct pf_modem *pm = link->data;
|
||||
|
||||
if (pm->modem == modem) {
|
||||
modems = g_slist_delete_link(modems, link);
|
||||
pf_free_modem(pm);
|
||||
break;
|
||||
}
|
||||
link = link->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void pf_modem_init(struct ofono_modem *modem,
|
||||
void *userdata)
|
||||
{
|
||||
pf_modem_watch(modem, TRUE, NULL);
|
||||
}
|
||||
|
||||
static void pf_free_handler(void *data)
|
||||
{
|
||||
struct push_datagram_handler *h = data;
|
||||
|
||||
g_free(h->content_type);
|
||||
g_free(h->interface);
|
||||
g_free(h->service);
|
||||
g_free(h->method);
|
||||
g_free(h->path);
|
||||
g_free(h->name);
|
||||
g_free(h);
|
||||
}
|
||||
|
||||
static void pf_parse_handler(GKeyFile *conf, const char *g)
|
||||
{
|
||||
GError *err = NULL;
|
||||
struct push_datagram_handler *h;
|
||||
char *interface;
|
||||
char *service;
|
||||
char *method;
|
||||
char *path;
|
||||
|
||||
interface = g_key_file_get_string(conf, g, "Interface", NULL);
|
||||
if (interface == NULL)
|
||||
goto no_interface;
|
||||
|
||||
service = g_key_file_get_string(conf, g, "Service", NULL);
|
||||
if (service == NULL)
|
||||
goto no_service;
|
||||
|
||||
method = g_key_file_get_string(conf, g, "Method", NULL);
|
||||
if (method == NULL)
|
||||
goto no_method;
|
||||
|
||||
path = g_key_file_get_string(conf, g, "Path", NULL);
|
||||
if (path == NULL)
|
||||
goto no_path;
|
||||
|
||||
h = g_try_new0(struct push_datagram_handler, 1);
|
||||
if (h == NULL)
|
||||
goto no_memory;
|
||||
|
||||
h->name = g_strdup(g);
|
||||
h->interface = interface;
|
||||
h->service = service;
|
||||
h->method = method;
|
||||
h->path = path;
|
||||
h->content_type = g_key_file_get_string(conf, g, "ContentType", NULL);
|
||||
h->dst_port = g_key_file_get_integer(conf, g, "DestinationPort", &err);
|
||||
if (h->dst_port == 0 && err != NULL) {
|
||||
h->dst_port = -1;
|
||||
g_error_free(err);
|
||||
err = NULL;
|
||||
}
|
||||
h->src_port = g_key_file_get_integer(conf, g, "SourcePort", &err);
|
||||
if (h->src_port == 0 && err != NULL) {
|
||||
h->src_port = -1;
|
||||
g_error_free(err);
|
||||
err = NULL;
|
||||
}
|
||||
DBG("registered %s", h->name);
|
||||
if (h->content_type != NULL)
|
||||
DBG(" ContentType: %s", h->content_type);
|
||||
if (h->dst_port >= 0)
|
||||
DBG(" DestinationPort: %d", h->dst_port);
|
||||
if (h->src_port >= 0)
|
||||
DBG(" SourcePort: %d", h->src_port);
|
||||
DBG(" Interface: %s", interface);
|
||||
DBG(" Service: %s", service);
|
||||
DBG(" Method: %s", method);
|
||||
DBG(" Path: %s", path);
|
||||
handlers = g_slist_append(handlers, h);
|
||||
return;
|
||||
|
||||
no_memory:
|
||||
g_free(path);
|
||||
|
||||
no_path:
|
||||
g_free(method);
|
||||
|
||||
no_method:
|
||||
g_free(service);
|
||||
|
||||
no_service:
|
||||
g_free(interface);
|
||||
|
||||
no_interface:
|
||||
return;
|
||||
}
|
||||
|
||||
static void pf_parse_config(void)
|
||||
{
|
||||
GDir *dir;
|
||||
const gchar *file;
|
||||
|
||||
g_slist_free_full(handlers, pf_free_handler);
|
||||
handlers = NULL;
|
||||
|
||||
dir = g_dir_open(PF_CONFIG_DIR, 0, NULL);
|
||||
if (dir == NULL) {
|
||||
DBG(PF_CONFIG_DIR " not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
DBG("loading configuration from " PF_CONFIG_DIR);
|
||||
while ((file = g_dir_read_name(dir)) != NULL) {
|
||||
GError *err;
|
||||
GKeyFile *conf;
|
||||
char *path;
|
||||
|
||||
if (g_str_has_suffix(file, ".conf") == FALSE)
|
||||
continue;
|
||||
|
||||
err = NULL;
|
||||
conf = g_key_file_new();
|
||||
path = g_strconcat(PF_CONFIG_DIR "/", file, NULL);
|
||||
DBG("reading %s", file);
|
||||
|
||||
if (g_key_file_load_from_file(conf, path, 0, &err) != FALSE) {
|
||||
gsize i, n;
|
||||
char **names = g_key_file_get_groups(conf, &n);
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
pf_parse_handler(conf, names[i]);
|
||||
g_strfreev(names);
|
||||
} else {
|
||||
ofono_warn("%s", err->message);
|
||||
g_error_free(err);
|
||||
}
|
||||
|
||||
g_key_file_free(conf);
|
||||
g_free(path);
|
||||
}
|
||||
|
||||
g_dir_close(dir);
|
||||
}
|
||||
|
||||
static gboolean pf_inotify(GIOChannel *gio, GIOCondition c, gpointer data)
|
||||
{
|
||||
int avail;
|
||||
gsize len;
|
||||
void *buf;
|
||||
GError *error;
|
||||
|
||||
if (ioctl(inotify_fd, FIONREAD, &avail) < 0)
|
||||
return FALSE;
|
||||
|
||||
buf = g_try_malloc(avail);
|
||||
if (buf == NULL)
|
||||
return FALSE;
|
||||
|
||||
error = NULL;
|
||||
if (g_io_channel_read_chars(gio, buf, avail, &len, &error) !=
|
||||
G_IO_STATUS_NORMAL) {
|
||||
g_free(buf);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
pf_parse_config();
|
||||
g_free(buf);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int pf_plugin_init(void)
|
||||
{
|
||||
DBG("");
|
||||
pf_parse_config();
|
||||
modem_watch_id = __ofono_modemwatch_add(pf_modem_watch, NULL, NULL);
|
||||
__ofono_modem_foreach(pf_modem_init, NULL);
|
||||
inotify_fd = inotify_init();
|
||||
if (inotify_fd < 0)
|
||||
return 0;
|
||||
|
||||
inotify_watch_id = inotify_add_watch(inotify_fd,
|
||||
PF_CONFIG_DIR,
|
||||
IN_CLOSE_WRITE | IN_DELETE | IN_MOVE);
|
||||
if (inotify_watch_id < 0)
|
||||
goto no_inotify_watch_id;
|
||||
|
||||
inotify_watch_channel = g_io_channel_unix_new(inotify_fd);
|
||||
if (inotify_watch_channel == NULL)
|
||||
goto no_inotify_watch_channel;
|
||||
|
||||
g_io_channel_set_encoding(inotify_watch_channel, NULL, NULL);
|
||||
g_io_channel_set_buffered(inotify_watch_channel, FALSE);
|
||||
inotify_watch_source_id = g_io_add_watch(inotify_watch_channel,
|
||||
G_IO_IN, pf_inotify, NULL);
|
||||
if (inotify_watch_source_id != 0)
|
||||
return 0;
|
||||
|
||||
g_io_channel_unref(inotify_watch_channel);
|
||||
inotify_watch_channel = NULL;
|
||||
|
||||
no_inotify_watch_channel:
|
||||
inotify_rm_watch(inotify_fd, inotify_watch_id);
|
||||
inotify_watch_id = -1;
|
||||
|
||||
no_inotify_watch_id:
|
||||
close(inotify_fd);
|
||||
inotify_fd = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pf_plugin_exit(void)
|
||||
{
|
||||
DBG("");
|
||||
__ofono_modemwatch_remove(modem_watch_id);
|
||||
modem_watch_id = 0;
|
||||
g_slist_free_full(modems, (GDestroyNotify)pf_free_modem);
|
||||
modems = NULL;
|
||||
g_slist_free_full(handlers, pf_free_handler);
|
||||
handlers = NULL;
|
||||
if (inotify_watch_source_id == 0)
|
||||
return;
|
||||
|
||||
g_source_remove(inotify_watch_source_id);
|
||||
inotify_watch_source_id = 0;
|
||||
g_io_channel_unref(inotify_watch_channel);
|
||||
inotify_watch_channel = NULL;
|
||||
inotify_rm_watch(inotify_fd, inotify_watch_id);
|
||||
inotify_watch_id = -1;
|
||||
close(inotify_fd);
|
||||
inotify_fd = -1;
|
||||
}
|
||||
|
||||
OFONO_PLUGIN_DEFINE(push_forwarder, "Push Forwarder Plugin", VERSION,
|
||||
OFONO_PLUGIN_PRIORITY_DEFAULT, pf_plugin_init,
|
||||
pf_plugin_exit)
|
||||
@@ -18,6 +18,7 @@ BuildRequires: pkgconfig(dbus-1)
|
||||
BuildRequires: pkgconfig(libudev) >= 145
|
||||
BuildRequires: pkgconfig(bluez) >= 4.85
|
||||
BuildRequires: pkgconfig(mobile-broadband-provider-info)
|
||||
BuildRequires: pkgconfig(libwspcodec) >= 2.0
|
||||
BuildRequires: libtool
|
||||
BuildRequires: automake
|
||||
BuildRequires: autoconf
|
||||
|
||||
Reference in New Issue
Block a user