diff --git a/librocketchat.c b/librocketchat.c
index 7d428f9..30dd2b1 100644
--- a/librocketchat.c
+++ b/librocketchat.c
@@ -248,7 +248,10 @@ purple_message_destroy(PurpleMessage *message)
typedef struct {
PurpleAccount *account;
PurpleConnection *pc;
-
+ PurpleSslConnection *websocket;
+ int inpa;
+ int fd;
+
GHashTable *cookie_table;
gchar *session_token;
gchar *channel;
@@ -261,8 +264,9 @@ typedef struct {
gchar *username;
gchar *server;
gchar *path;
-
- PurpleSslConnection *websocket;
+ gboolean tls;
+ gchar *http_str;
+
gboolean websocket_header_received;
gboolean sync_complete;
guchar packet_code;
@@ -682,6 +686,29 @@ rc_cookies_to_string(RocketChatAccount *ya)
return g_string_free(str, FALSE);
}
+
+size_t rc_sock_read(RocketChatAccount *ya, gpointer sock, void *buf, size_t len)
+{
+ if (ya->tls) {
+ return purple_ssl_read(sock, buf, len);
+ }
+
+ g_return_val_if_fail(ya->fd > 0, -1);
+ return read(ya->fd, buf, len);
+}
+
+// modeled after libpurple/jabber.c:js_do_send()
+static int rc_sock_write(RocketChatAccount *ya, void *data, int len) {
+ int ret;
+
+ if (ya->websocket)
+ ret = purple_ssl_write(ya->websocket, data, len);
+ else
+ ret = write(ya->fd, data, len);
+
+ return ret;
+}
+
static void
rc_response_callback(PurpleHttpConnection *http_conn,
#if PURPLE_VERSION_CHECK(3, 0, 0)
@@ -988,7 +1015,8 @@ rc_login_response(RocketChatAccount *ya, JsonNode *node, gpointer user_data, Jso
//a["{\"msg\":\"result\",\"id\":\"5\",\"error\":{\"error\":403,\"reason\":\"User has no password set\",\"message\":\"User has no password set [403]\",\"errorType\":\"Meteor.Error\"}}"]
// Download all user presence (requires the session_token)
- gchar *url = g_strconcat("https://", ya->server, ya->path, "/api/v1/users.presence", NULL);
+ gchar *url = g_strconcat(ya->http_str, ya->server, ya->path,
+ "/api/v1/users.presence", NULL);
rc_fetch_url(ya, url, NULL, rc_got_users_presence, NULL);
g_free(url);
}
@@ -1426,7 +1454,11 @@ rc_process_room_message(RocketChatAccount *ya, JsonObject *message_obj, JsonObje
const gchar *title_link = json_object_get_string_member(attachment, "title_link");
if (title != NULL && title_link != NULL) {
- gchar *temp_message = g_strdup_printf("%s %s", (message ? message : ""), ya->server, ya->path, title_link, title);
+ gchar *temp_message = g_strdup_printf("%s %s"
+ , (message ? message : ""),
+ ya->http_str,
+ ya->server, ya->path,
+ title_link, title);
g_free(message);
message = temp_message;
}
@@ -2093,6 +2125,7 @@ rc_login(PurpleAccount *account)
const gchar *username = purple_account_get_username(account);
gchar *url;
PurpleConnectionFlags pc_flags;
+ const char *connection_security;
pc_flags = purple_connection_get_flags(pc);
pc_flags |= PURPLE_CONNECTION_FLAG_HTML;
@@ -2137,9 +2170,19 @@ rc_login(PurpleAccount *account)
ya->server = g_strdup(userparts[1]);
ya->path = g_strdup(purple_account_get_string(account, "server_path", ""));
g_strfreev(userparts);
-
-
- ya->session_token = g_strdup(purple_account_get_string(account, "personal_access_token", NULL));
+
+ connection_security = purple_account_get_string(account, "connection_security", "tls");
+ purple_debug_info("rocketchat", "connection-security: %s\n", connection_security);
+
+ if (!purple_strequal(connection_security, "tls")) {
+ ya->tls = FALSE;
+ ya->http_str = "http://";
+ } else {
+ ya->tls = TRUE;
+ ya->http_str = "https://";
+ }
+
+ ya->session_token = g_strdup(purple_account_get_string(account, "personal_access_token", NULL));
if (ya->session_token && *ya->session_token) {
const gchar *user_id = purple_account_get_string(account, "personal_access_token_user_id", NULL);
@@ -2164,7 +2207,7 @@ rc_login(PurpleAccount *account)
//Build the initial hash tables from the current buddy list
rc_build_groups_from_blist(ya);
- url = g_strconcat("https://", ya->server, ya->path, "/api/me", NULL);
+ url = g_strconcat(ya->http_str, ya->server, ya->path, "/api/me", NULL);
rc_fetch_url(ya, url, NULL, rc_login_me_cb, NULL);
g_free(url);
}
@@ -2203,7 +2246,13 @@ rc_close(PurpleConnection *pc)
g_return_if_fail(ya != NULL);
// account = purple_connection_get_account(pc);
- if (ya->websocket != NULL) purple_ssl_close(ya->websocket);
+ if (ya->websocket != NULL)
+ purple_ssl_close(ya->websocket);
+ else if (ya->fd > 0) {
+ if (pc->inpa)
+ purple_input_remove(pc->inpa);
+ close(ya->fd);
+ }
g_hash_table_remove_all(ya->one_to_ones);
g_hash_table_unref(ya->one_to_ones);
@@ -2384,10 +2433,10 @@ rc_socket_write_data(RocketChatAccount *ya, guchar *data, gsize data_len, guchar
memmove(full_data + (1 + len_size), &mkey, 4);
memmove(full_data + (1 + len_size + 4), data, data_len);
-
- purple_ssl_write(ya->websocket, full_data, 1 + data_len + len_size + 4);
-
- g_free(full_data);
+
+ rc_sock_write(ya, full_data, 1 + data_len + len_size + 4);
+
+ g_free(full_data);
g_free(data);
}
@@ -2440,9 +2489,10 @@ rc_socket_write_json(RocketChatAccount *rca, JsonObject *data)
}
static void
-rc_socket_got_data(gpointer userdata, PurpleSslConnection *conn, PurpleInputCondition cond)
+rc_socket_got_data(gpointer data, PurpleSslConnection *gsc)
{
- RocketChatAccount *ya = userdata;
+ PurpleConnection *pc = data;
+ RocketChatAccount *ya = purple_connection_get_protocol_data(pc);
guchar length_code = -1;
gint ping_frame_len = -1;
int read_len = 0;
@@ -2453,7 +2503,7 @@ rc_socket_got_data(gpointer userdata, PurpleSslConnection *conn, PurpleInputCond
gint nlbr_count = 0;
gchar nextchar;
- while(nlbr_count < 4 && (read_len = purple_ssl_read(conn, &nextchar, 1)) == 1) {
+ while(nlbr_count < 4 && (read_len = rc_sock_read(ya, gsc, &nextchar, 1)) == 1) {
if (nextchar == '\r' || nextchar == '\n') {
nlbr_count++;
} else {
@@ -2473,7 +2523,7 @@ rc_socket_got_data(gpointer userdata, PurpleSslConnection *conn, PurpleInputCond
}
}
- while(ya->frame || (read_len = purple_ssl_read(conn, &ya->packet_code, 1)) == 1) {
+ while(ya->frame || (read_len = rc_sock_read(ya, gsc, &ya->packet_code, 1)) == 1) {
if (!ya->frame) {
if (ya->packet_code != 129) {
@@ -2485,20 +2535,20 @@ rc_socket_got_data(gpointer userdata, PurpleSslConnection *conn, PurpleInputCond
if (ya->packet_code == 137) {
// Ping
length_code = 0;
- purple_ssl_read(conn, &length_code, 1);
+ rc_sock_read(ya, gsc, &length_code, 1);
if (length_code <= 125) {
ping_frame_len = length_code;
} else if (length_code == 126) {
guchar len_buf[2];
- purple_ssl_read(conn, len_buf, 2);
+ rc_sock_read(ya, gsc, len_buf, 2);
ping_frame_len = (len_buf[0] << 8) + len_buf[1];
} else if (length_code == 127) {
- purple_ssl_read(conn, &ping_frame_len, 8);
+ rc_sock_read(ya, gsc, &ping_frame_len, 8);
ping_frame_len = GUINT64_FROM_BE(ping_frame_len);
}
if (ping_frame_len) {
guchar *pong_data = g_new0(guchar, ping_frame_len);
- purple_ssl_read(conn, pong_data, ping_frame_len);
+ rc_sock_read(ya, gsc, pong_data, ping_frame_len);
rc_socket_write_data(ya, pong_data, ping_frame_len, 138);
g_free(pong_data);
@@ -2515,25 +2565,25 @@ rc_socket_got_data(gpointer userdata, PurpleSslConnection *conn, PurpleInputCond
}
length_code = 0;
- purple_ssl_read(conn, &length_code, 1);
+ rc_sock_read(ya, gsc, &length_code, 1);
if (length_code <= 125) {
ya->frame_len = length_code;
} else if (length_code == 126) {
guchar len_buf[2];
- purple_ssl_read(conn, len_buf, 2);
+ rc_sock_read(ya, gsc, len_buf, 2);
ya->frame_len = (len_buf[0] << 8) + len_buf[1];
} else if (length_code == 127) {
- purple_ssl_read(conn, &ya->frame_len, 8);
+ rc_sock_read(ya, gsc, &ya->frame_len, 8);
ya->frame_len = GUINT64_FROM_BE(ya->frame_len);
}
//purple_debug_info("rocketchat", "frame_len: %" G_GUINT64_FORMAT "\n", ya->frame_len);
-
+
ya->frame = g_new0(gchar, ya->frame_len + 1);
ya->frame_len_progress = 0;
}
do {
- read_len = purple_ssl_read(conn, ya->frame + ya->frame_len_progress, ya->frame_len - ya->frame_len_progress);
+ read_len = rc_sock_read(ya, gsc, ya->frame + ya->frame_len_progress, ya->frame_len - ya->frame_len_progress);
if (read_len > 0) {
ya->frame_len_progress += read_len;
}
@@ -2545,11 +2595,17 @@ rc_socket_got_data(gpointer userdata, PurpleSslConnection *conn, PurpleInputCond
g_free(ya->frame); ya->frame = NULL;
ya->packet_code = 0;
ya->frame_len = 0;
-
- if (G_UNLIKELY(ya->websocket == NULL || success == FALSE)) {
- return;
- }
- } else {
+
+ if (ya->tls) {
+ if (G_UNLIKELY(ya->websocket == NULL || success == FALSE)) {
+ return;
+ }
+ } else {
+ if (G_UNLIKELY(ya->pc == NULL || success == FALSE )) {
+ return;
+ }
+ }
+ } else {
return;
}
}
@@ -2582,17 +2638,29 @@ rc_socket_got_data(gpointer userdata, PurpleSslConnection *conn, PurpleInputCond
}
static void
-rc_socket_connected(gpointer userdata, PurpleSslConnection *conn, PurpleInputCondition cond)
+rc_socket_got_data_ssl(gpointer data, PurpleSslConnection *gsc,
+ PurpleInputCondition cond)
+
+{
+ rc_socket_got_data(data, gsc);
+}
+
+static void
+rc_socket_got_data_proxy(gpointer data, gint source,
+ PurpleInputCondition condition)
+{
+ rc_socket_got_data(data, NULL);
+}
+
+static void
+rc_socket_upgrade(RocketChatAccount *ya)
{
- RocketChatAccount *ya = userdata;
gchar *websocket_header;
gchar *cookies;
const gchar *websocket_key = "15XF+ptKDhYVERXoGcdHTA=="; //TODO don't be lazy
GString *url = g_string_new(NULL);
-
- purple_ssl_input_add(ya->websocket, rc_socket_got_data, ya);
-
- g_string_append_printf(url, "%s/sockjs/%d/pidgin%d/websocket", ya->path, g_random_int_range(100, 999), g_random_int_range(1, 100));
+
+ g_string_append_printf(url, "%s/sockjs/%d/pidgin%d/websocket", ya->path, g_random_int_range(100, 999), g_random_int_range(1, 100));
cookies = rc_cookies_to_string(ya);
websocket_header = g_strdup_printf("GET %s HTTP/1.1\r\n"
@@ -2608,14 +2676,52 @@ rc_socket_connected(gpointer userdata, PurpleSslConnection *conn, PurpleInputCon
//"Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits\r\n"
"\r\n", url->str, ya->server,
websocket_key, cookies);
-
- purple_ssl_write(ya->websocket, websocket_header, strlen(websocket_header));
-
- g_free(websocket_header);
+ rc_sock_write(ya, websocket_header, strlen(websocket_header));
+
+ g_free(websocket_header);
g_string_free(url, TRUE);
g_free(cookies);
}
+static void
+rc_socket_connected_ssl(gpointer data, PurpleSslConnection *conn,
+ PurpleInputCondition cond) {
+
+ PurpleConnection *gc = data;
+ RocketChatAccount *ya = purple_connection_get_protocol_data(gc);
+
+ /* In case the connection was closed earlier */
+ if(!PURPLE_CONNECTION_IS_VALID(gc)) {
+ purple_ssl_close(conn);
+ g_return_if_reached();
+ }
+
+ purple_ssl_input_add(ya->websocket, rc_socket_got_data_ssl, gc);
+
+ rc_socket_upgrade(ya);
+}
+
+static void
+rc_socket_connected_proxy(gpointer data, gint source, const gchar *error)
+{
+ PurpleConnection *gc = data;
+ RocketChatAccount *ya = purple_connection_get_protocol_data(gc);
+
+ if (source < 0) {
+ purple_connection_error_reason(ya->pc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Unable to connect"));
+ return;
+ }
+
+ ya->fd = source;
+
+ gc->inpa = purple_input_add(ya->fd, PURPLE_INPUT_READ,
+ rc_socket_got_data_proxy, gc);
+
+ rc_socket_upgrade(ya);
+}
+
static void
rc_socket_failed(PurpleSslConnection *conn, PurpleSslErrorType errortype, gpointer userdata)
{
@@ -2623,7 +2729,7 @@ rc_socket_failed(PurpleSslConnection *conn, PurpleSslErrorType errortype, gpoint
ya->websocket = NULL;
ya->websocket_header_received = FALSE;
-
+
if (errortype == PURPLE_SSL_CERTIFICATE_INVALID) {
purple_connection_ssl_error(ya->pc, errortype);
return;
@@ -2635,28 +2741,44 @@ rc_socket_failed(PurpleSslConnection *conn, PurpleSslErrorType errortype, gpoint
static void
rc_start_socket(RocketChatAccount *ya)
{
+ PurpleConnection *gc = ya->pc;
gchar **server_split;
gint port = 443;
-
+
//Reset all the old stuff
if (ya->websocket != NULL) {
- purple_ssl_close(ya->websocket);
- }
-
+ purple_ssl_close(ya->websocket);
+ }
ya->websocket = NULL;
+ ya->fd = -1;
ya->websocket_header_received = FALSE;
g_free(ya->frame); ya->frame = NULL;
ya->packet_code = 0;
ya->frame_len = 0;
ya->frames_since_reconnect = 0;
- server_split = g_strsplit(ya->server, ":", 2);
+ server_split = g_strsplit(ya->server, ":", 2);
if (server_split[1] != NULL) {
port = atoi(server_split[1]);
}
- ya->websocket = purple_ssl_connect(ya->account, server_split[0], port, rc_socket_connected, rc_socket_failed, ya);
-
- g_strfreev(server_split);
+
+ if (ya->tls) {
+ ya->fd = -1;
+ ya->websocket = purple_ssl_connect(ya->account, server_split[0], port,
+ rc_socket_connected_ssl,
+ rc_socket_failed,
+ gc);
+ } else {
+ if (purple_proxy_connect(ya->pc, ya->account,
+ server_split[0], port,
+ rc_socket_connected_proxy, ya->pc) == NULL) {
+ purple_connection_error_reason(ya->pc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Unable to connect"));
+ }
+ }
+
+ g_strfreev(server_split);
}
@@ -3579,7 +3701,8 @@ rc_add_buddy(PurpleConnection *pc, PurpleBuddy *buddy, PurpleGroup *group
//avatar at https://{server}/avatar/{username}.jpg?_dc=0
- avatar_url = g_strdup_printf("https://%s%s/avatar/%s.jpg?_dc=0", ya->server, ya->path, purple_url_encode(buddy_name));
+ avatar_url = g_strdup_printf("%s%s%s/avatar/%s.jpg?_dc=0", ya->http_str, ya->server,
+ ya->path, purple_url_encode(buddy_name));
rc_fetch_url(ya, avatar_url, NULL, rc_got_avatar, buddy);
g_free(avatar_url);
@@ -3630,6 +3753,7 @@ static GList *
rc_add_account_options(GList *account_options)
{
PurpleAccountOption *option;
+ GList *encryption_values = NULL;
option = purple_account_option_bool_new(N_("Auto-add buddies to the buddy list"), "auto-add-buddy", FALSE);
account_options = g_list_append(account_options, option);
@@ -3645,6 +3769,25 @@ rc_add_account_options(GList *account_options)
option = purple_account_option_string_new(N_("Server Path"), "server_path", "");
account_options = g_list_append(account_options, option);
+
+#define ADD_VALUE(list, desc, v) { \
+ PurpleKeyValuePair *kvp = g_new0(PurpleKeyValuePair, 1); \
+ kvp->key = g_strdup((desc)); \
+ kvp->value = g_strdup((v)); \
+ list = g_list_prepend(list, kvp); \
+}
+
+
+ ADD_VALUE(encryption_values, _("No encryption"), "no_tls");
+ ADD_VALUE(encryption_values, _("Require encryption"), "tls");
+ encryption_values = g_list_reverse(encryption_values);
+
+#undef ADD_VALUE
+
+ option = purple_account_option_list_new(_("Connection security"),
+ "connection_security", encryption_values);
+ account_options = g_list_append(account_options, option);
+
return account_options;
}