Commit 5fa005c3 authored by Alyssa Rosenzweig's avatar Alyssa Rosenzweig 💜

Migrate push notifications to proxy (fixes major regression)

parent c331633d
......@@ -2,8 +2,8 @@ all: sapphire-backend sapphire-proxy
echo "Bing"
# Deps: libpurple-dev, libglib2.0-dev, libjson-glib-dev
sapphire-backend: core.c websocket.c event-loop.c push.c
gcc `pkg-config --cflags glib-2.0` `pkg-config --cflags gio-unix-2.0` `pkg-config --cflags json-glib-1.0` core.c websocket.c event-loop.c -I/usr/include/libpurple `pkg-config --libs gobject-2.0` `pkg-config --libs json-glib-1.0` -lpurple -I/usr/include/libsoup-2.4 -lsoup-2.4 -Wall -Wextra -Werror -Wno-unused-parameter -o sapphire-backend -g push.c -g
sapphire-backend: core.c websocket.c event-loop.c
gcc `pkg-config --cflags glib-2.0` `pkg-config --cflags gio-unix-2.0` `pkg-config --cflags json-glib-1.0` core.c websocket.c event-loop.c -I/usr/include/libpurple `pkg-config --libs gobject-2.0` `pkg-config --libs json-glib-1.0` -lpurple -I/usr/include/libsoup-2.4 -lsoup-2.4 -Wall -Wextra -Werror -Wno-unused-parameter -o sapphire-backend -g
# Deps: libglib2.0-dev, libsoup2.4-dev, libjson-glib-dev
sapphire-proxy: proxy.c
......
......@@ -37,7 +37,6 @@
#include "core.h"
#include "websocket.h"
#include "push.h"
#include "event-loop.h"
#include "json_compat.h"
......@@ -161,6 +160,7 @@ sapphire_send_icon(const gchar *name, const gchar *ext, gconstpointer data, size
g_hash_table_insert(sent_icons, g_strdup(name), g_strdup(hash));
g_free(str);
g_free(str_prefixed);
g_free(base64);
json_object_unref(obj);
......@@ -281,6 +281,9 @@ sapphire_decode_account(JsonObject *data)
return g_hash_table_lookup(id_to_account, account_id);
}
void
sapphire_push_connected(void);
void
sapphire_process_message(Connection *conn, JsonObject *data)
{
......@@ -1086,6 +1089,58 @@ sapphire_buddy_typing_changed(PurpleAccount *account, const char *name, gpointer
json_object_unref(data);
}
/* Push notification infrastrcture -- has a push notification been sent since
* the last login? We default to TRUE to avoid sporadic push notifications
* before the first connection*/
gboolean sapphire_already_pushed = TRUE;
/* We got a connection, so we're clear for another push */
void
sapphire_push_connected(void)
{
sapphire_already_pushed = FALSE;
}
/* Send a push notification with the given content. This wrapper function
* determines whether the notification should actually be pushed, handling
* state appropriately. Actual medium-specific sending is delegated. */
extern gboolean sapphire_any_connected_clients;
void
sapphire_push_notification(const gchar *content)
{
/* Push notifications can only be sent if:
*
* 0. Push notifications are enabled (TODO)
* 1. There are no connected clients
* 2. There has not been another push notification sent
*
* Check these criteria
*/
if (sapphire_any_connected_clients) return;
if (sapphire_already_pushed) return;
/* We've passed the criteria, so send the notification via the proxy */
JsonObject *resp = json_object_new();
json_object_set_string_member(resp, "op", "push");
json_object_set_string_member(resp, "content", content);
gchar *str = json_object_to_string(resp);
gchar *str_prefixed = g_strdup_printf(">%s", str);
sapphire_send_raw_packet(str_prefixed);
json_object_unref(resp);
g_free(str_prefixed);
g_free(str);
/* Record that we pushed something */
sapphire_already_pushed = TRUE;
}
static void
sapphire_received_message(PurpleAccount *account, const char *who, const char *message, PurpleConversation *conv,
PurpleMessageFlags flags, gpointer null)
......
......@@ -46,6 +46,9 @@ typedef struct InstanceConnection {
/* The connections */
GList *authenticated_connections;
/* Who we are */
struct SapphireAccount *account;
} InstanceConnection;
static void
......@@ -64,6 +67,18 @@ sapphire_send_instance(struct InstanceConnection *proxy, const gchar *frame)
g_output_stream_write(ostream, &end, 1, NULL, &gerror);
}
/* Represents a particular account */
typedef struct SapphireAccount {
gchar *password_hash;
/* Email if it is set, or NULL/empty string it not */
gchar *push_notification_email;
/* Instance connection associated with this account */
InstanceConnection *instance;
} SapphireAccount;
/* Represents a connection to a given client. Minimal state should be kept
* here, since connections are device-specific, not for the client as a whole.
* Essentially, just enough for the websocket metadata and a little potpourrie */
......@@ -135,16 +150,6 @@ json_parse_to_root(gchar *frame)
/* Serialize accounts to/from disk. TODO: Serializing back */
typedef struct {
gchar *password_hash;
/* Email if it is set, or NULL/empty string it not */
gchar *push_notification_email;
/* Instance connection associated with this account */
InstanceConnection *instance;
} SapphireAccount;
GHashTable *username_to_account;
static void
......@@ -225,6 +230,48 @@ sapphire_connect_instance(const gchar *name)
/* Associate the instance object with the account */
SapphireAccount *acct = (SapphireAccount *) g_hash_table_lookup(username_to_account, name);
acct->instance = instance;
instance->account = (struct SapphireAccount *) acct;
}
/* Push notification routine (medium-specific). At the moment, push
* notifications are email-based, allowing for SMS beeps via SMS-email bridge,
* etc */
static
void sapphire_push_notification_raw(InstanceConnection *conn, const gchar *content)
{
/* Send push notification as the body of an email via mailx. First,
* write the content to a file */
const char *temp_file = "/dev/shm/push-notification.txt";
FILE *fp = fopen(temp_file, "w");
fputs(content, fp);
fclose(fp);
/* Find the target mail address */
const char *address = conn->account->push_notification_email;
if (!address) {
printf("No address for push\n");
return;
}
/* Strip whitespace so it's zero length if blank */
gchar *stripped = g_strstrip(g_strdup(address));
if (stripped[0] == '\0') {
printf("Can't do push notifications to empty address, thonk\n");
return;
}
/* Now, invoke mailx via system. XXX: SECURITY: Sanitize the address */
gchar *command = g_strdup_printf("mailx -- %s < %s", stripped, temp_file);
printf("Invoking %s\n", command);
system(command);
g_free(command);
g_free(stripped);
printf("Pushing %s\n", content);
}
/* Functions for icon proxying, TODO segregate by user */
......@@ -312,7 +359,7 @@ sapphire_restore_rate_limit(gpointer user_data)
}
static void
sapphire_got_internal(gchar *frame)
sapphire_got_internal(InstanceConnection *conn, gchar *frame)
{
JsonNode *root = json_parse_to_root(frame);
......@@ -335,6 +382,9 @@ sapphire_got_internal(gchar *frame)
icon->data = g_base64_decode(base64, &icon->size);
g_hash_table_insert(username_to_icon, g_strdup(name), icon);
} else if (g_strcmp0(op, "push") == 0) {
const gchar *content = json_object_get_string_member(obj, "content");
sapphire_push_notification_raw(conn, content);
} else {
printf("Unknown op %s\n", op);
return;
......@@ -375,7 +425,7 @@ sapphire_got_line(GObject *source_object,
/* Check the first character. If it's >, this is an internal packet. Otherwise, pass it along */
if (data[0] == '>') {
sapphire_got_internal(data + 1);
sapphire_got_internal(conn, data + 1);
} else {
sapphire_send_raw_packet(conn, data);
}
......
......@@ -39,12 +39,10 @@ sapphire_push_connected(void)
* determines whether the notification should actually be pushed, handling
* state appropriately. Actual medium-specific sending is delegated. */
static void sapphire_push_notification_raw(const gchar *content);
extern gboolean sapphire_any_connected_clients;
void
sapphire_push_notification(const gchar *content)
sapphire_push_notification(Connection *conn, const gchar *content)
{
/* Push notifications can only be sent if:
*
......@@ -58,45 +56,19 @@ sapphire_push_notification(const gchar *content)
if (sapphire_any_connected_clients) return;
if (sapphire_already_pushed) return;
/* We've passed the criteria, so send the notification */
/* We've passed the criteria, so send the notification via the proxy */
JsonObject *resp = json_object_new();
json_object_set_string_member(resp, "op", "push");
json_object_set_string_member(resp, "content", content);
sapphire_push_notification_raw(content);
gchar *str = json_object_to_string(resp);
gchar *str_prefixed = g_strdup_printf(">%s", str);
sapphire_send_raw_packet(str_prefixed);
json_object_unref(resp);
g_free(str_prefixed);
g_free(str);
/* Record that we pushed something */
sapphire_already_pushed = TRUE;
}
/* Actually send the push notification (medium-specific). At the moment, push
* notifications are email-based, allowing for SMS beeps via SMS-email bridge,
* etc */
static
void sapphire_push_notification_raw(const gchar *content)
{
/* Send push notification as the body of an email via mailx. First,
* write the content to a file */
const char *temp_file = "/dev/shm/push-notification.txt";
FILE *fp = fopen(temp_file, "w");
fputs(content, fp);
fclose(fp);
/* Find the target mail address */
const char *address = purple_prefs_get_string(SAPPHIRE_PUSH_EMAIL_PREF);
/* Strip whitespace so it's zero length if blank */
gchar *stripped = g_strstrip(g_strdup(address));
if (stripped[0] == '\0') {
printf("Can't do push notifications to empty address, thonk\n");
return;
}
/* Now, invoke mailx via system. XXX: SECURITY: Sanitize the address */
gchar *command = g_strdup_printf("mailx -- %s < %s", stripped, temp_file);
printf("Invoking %s\n", command);
system(command);
g_free(command);
g_free(stripped);
printf("Pushing %s\n", content);
}
......@@ -24,10 +24,10 @@
#include <glib.h>
#define SAPPHIRE_PUSH_EMAIL_PREF "/purple/sapphire/push-email"
struct Connection;
void
sapphire_push_notification(const gchar *content);
sapphire_push_notification(Connection *conn, const gchar *content);
void
sapphire_push_connected(void);
......
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