aboutsummaryrefslogtreecommitdiffstats
path: root/protocols/msn/sb.c
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/msn/sb.c')
-rw-r--r--protocols/msn/sb.c712
1 files changed, 0 insertions, 712 deletions
diff --git a/protocols/msn/sb.c b/protocols/msn/sb.c
deleted file mode 100644
index 673d65c7..00000000
--- a/protocols/msn/sb.c
+++ /dev/null
@@ -1,712 +0,0 @@
-/********************************************************************\
- * BitlBee -- An IRC to other IM-networks gateway *
- * *
- * Copyright 2002-2012 Wilmer van der Gaast and others *
- \********************************************************************/
-
-/* MSN module - Switchboard server callbacks and utilities */
-
-/*
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- 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.
-
- You should have received a copy of the GNU General Public License with
- the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
- if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
- Fifth Floor, Boston, MA 02110-1301 USA
-*/
-
-#include <ctype.h>
-#include "nogaim.h"
-#include "msn.h"
-#include "md5.h"
-#include "soap.h"
-
-static gboolean msn_sb_callback(gpointer data, gint source, b_input_condition cond);
-static int msn_sb_command(struct msn_handler_data *handler, char **cmd, int num_parts);
-static int msn_sb_message(struct msn_handler_data *handler, char *msg, int msglen, char **cmd, int num_parts);
-
-int msn_sb_write(struct msn_switchboard *sb, const char *fmt, ...)
-{
- va_list params;
- char *out;
- size_t len;
- int st;
-
- va_start(params, fmt);
- out = g_strdup_vprintf(fmt, params);
- va_end(params);
-
- if (getenv("BITLBEE_DEBUG")) {
- fprintf(stderr, "->SB%d:%s\n", sb->fd, out);
- }
-
- len = strlen(out);
- st = write(sb->fd, out, len);
- g_free(out);
- if (st != len) {
- msn_sb_destroy(sb);
- return 0;
- }
-
- return 1;
-}
-
-int msn_sb_write_msg(struct im_connection *ic, struct msn_message *m)
-{
- struct msn_data *md = ic->proto_data;
- struct msn_switchboard *sb;
-
- /* FIXME: *CHECK* the reliability of using spare sb's! */
- if ((sb = msn_sb_spare(ic))) {
- debug("Trying to use a spare switchboard to message %s", m->who);
-
- sb->who = g_strdup(m->who);
- if (msn_sb_write(sb, "CAL %d %s\r\n", ++sb->trId, m->who)) {
- /* He/She should join the switchboard soon, let's queue the message. */
- sb->msgq = g_slist_append(sb->msgq, m);
- return(1);
- }
- }
-
- debug("Creating a new switchboard to message %s", m->who);
-
- /* If we reach this line, there was no spare switchboard, so let's make one. */
- if (!msn_ns_write(ic, -1, "XFR %d SB\r\n", ++md->trId)) {
- g_free(m->who);
- g_free(m->text);
- g_free(m);
-
- return(0);
- }
-
- /* And queue the message to md. We'll pick it up when the switchboard comes up. */
- md->msgq = g_slist_append(md->msgq, m);
-
- /* FIXME: If the switchboard creation fails, the message will not be sent. */
-
- return(1);
-}
-
-struct msn_switchboard *msn_sb_create(struct im_connection *ic, char *host, int port, char *key, int session)
-{
- struct msn_data *md = ic->proto_data;
- struct msn_switchboard *sb = g_new0(struct msn_switchboard, 1);
-
- sb->fd = proxy_connect(host, port, msn_sb_connected, sb);
- if (sb->fd < 0) {
- g_free(sb);
- return(NULL);
- }
-
- sb->ic = ic;
- sb->key = g_strdup(key);
- sb->session = session;
-
- msn_switchboards = g_slist_append(msn_switchboards, sb);
- md->switchboards = g_slist_append(md->switchboards, sb);
-
- return(sb);
-}
-
-struct msn_switchboard *msn_sb_by_handle(struct im_connection *ic, const char *handle)
-{
- struct msn_data *md = ic->proto_data;
- struct msn_switchboard *sb;
- GSList *l;
-
- for (l = md->switchboards; l; l = l->next) {
- sb = l->data;
- if (sb->who && strcmp(sb->who, handle) == 0) {
- return(sb);
- }
- }
-
- return(NULL);
-}
-
-struct msn_switchboard *msn_sb_by_chat(struct groupchat *c)
-{
- struct msn_data *md = c->ic->proto_data;
- struct msn_switchboard *sb;
- GSList *l;
-
- for (l = md->switchboards; l; l = l->next) {
- sb = l->data;
- if (sb->chat == c) {
- return(sb);
- }
- }
-
- return(NULL);
-}
-
-struct msn_switchboard *msn_sb_spare(struct im_connection *ic)
-{
- struct msn_data *md = ic->proto_data;
- struct msn_switchboard *sb;
- GSList *l;
-
- for (l = md->switchboards; l; l = l->next) {
- sb = l->data;
- if (!sb->who && !sb->chat) {
- return(sb);
- }
- }
-
- return(NULL);
-}
-
-int msn_sb_sendmessage(struct msn_switchboard *sb, char *text)
-{
- if (sb->ready) {
- char *buf;
- int i, j;
-
- /* Build the message. Convert LF to CR-LF for normal messages. */
- if (strcmp(text, TYPING_NOTIFICATION_MESSAGE) == 0) {
- i = strlen(MSN_TYPING_HEADERS) + strlen(sb->ic->acc->user);
- buf = g_new0(char, i);
- i = g_snprintf(buf, i, MSN_TYPING_HEADERS, sb->ic->acc->user);
- } else if (strcmp(text, NUDGE_MESSAGE) == 0) {
- buf = g_strdup(MSN_NUDGE_HEADERS);
- i = strlen(buf);
- } else if (strcmp(text, SB_KEEPALIVE_MESSAGE) == 0) {
- buf = g_strdup(MSN_SB_KEEPALIVE_HEADERS);
- i = strlen(buf);
- } else {
- buf = g_new0(char, sizeof(MSN_MESSAGE_HEADERS) + strlen(text) * 2 + 1);
- i = strlen(MSN_MESSAGE_HEADERS);
-
- strcpy(buf, MSN_MESSAGE_HEADERS);
- for (j = 0; text[j]; j++) {
- if (text[j] == '\n') {
- buf[i++] = '\r';
- }
-
- buf[i++] = text[j];
- }
- }
-
- /* Build the final packet (MSG command + the message). */
- if (msn_sb_write(sb, "MSG %d N %d\r\n%s", ++sb->trId, i, buf)) {
- g_free(buf);
- return 1;
- } else {
- g_free(buf);
- return 0;
- }
- } else if (sb->who) {
- struct msn_message *m = g_new0(struct msn_message, 1);
-
- m->who = g_strdup("");
- m->text = g_strdup(text);
- sb->msgq = g_slist_append(sb->msgq, m);
-
- return(1);
- } else {
- return(0);
- }
-}
-
-struct groupchat *msn_sb_to_chat(struct msn_switchboard *sb)
-{
- struct im_connection *ic = sb->ic;
- struct groupchat *c = NULL;
- char buf[1024];
-
- /* Create the groupchat structure. */
- g_snprintf(buf, sizeof(buf), "MSN groupchat session %d", sb->session);
- if (sb->who) {
- c = bee_chat_by_title(ic->bee, ic, sb->who);
- }
- if (c && !msn_sb_by_chat(c)) {
- sb->chat = c;
- } else {
- sb->chat = imcb_chat_new(ic, buf);
- }
-
- /* Populate the channel. */
- if (sb->who) {
- imcb_chat_add_buddy(sb->chat, sb->who);
- }
- imcb_chat_add_buddy(sb->chat, ic->acc->user);
-
- /* And make sure the switchboard doesn't look like a regular chat anymore. */
- if (sb->who) {
- g_free(sb->who);
- sb->who = NULL;
- }
-
- return sb->chat;
-}
-
-void msn_sb_destroy(struct msn_switchboard *sb)
-{
- struct im_connection *ic = sb->ic;
- struct msn_data *md = ic->proto_data;
-
- debug("Destroying switchboard: %s", sb->who ? sb->who : sb->key ? sb->key : "");
-
- msn_msgq_purge(ic, &sb->msgq);
- msn_sb_stop_keepalives(sb);
-
- if (sb->key) {
- g_free(sb->key);
- }
- if (sb->who) {
- g_free(sb->who);
- }
-
- if (sb->chat) {
- imcb_chat_free(sb->chat);
- }
-
- if (sb->handler) {
- if (sb->handler->rxq) {
- g_free(sb->handler->rxq);
- }
- if (sb->handler->cmd_text) {
- g_free(sb->handler->cmd_text);
- }
- g_free(sb->handler);
- }
-
- if (sb->inp) {
- b_event_remove(sb->inp);
- }
- closesocket(sb->fd);
-
- msn_switchboards = g_slist_remove(msn_switchboards, sb);
- md->switchboards = g_slist_remove(md->switchboards, sb);
- g_free(sb);
-}
-
-gboolean msn_sb_connected(gpointer data, gint source, b_input_condition cond)
-{
- struct msn_switchboard *sb = data;
- struct im_connection *ic;
- struct msn_data *md;
- char buf[1024];
-
- /* Are we still alive? */
- if (!g_slist_find(msn_switchboards, sb)) {
- return FALSE;
- }
-
- ic = sb->ic;
- md = ic->proto_data;
-
- if (source != sb->fd) {
- debug("Error %d while connecting to switchboard server", 1);
- msn_sb_destroy(sb);
- return FALSE;
- }
-
- /* Prepare the callback */
- sb->handler = g_new0(struct msn_handler_data, 1);
- sb->handler->fd = sb->fd;
- sb->handler->rxq = g_new0(char, 1);
- sb->handler->data = sb;
- sb->handler->exec_command = msn_sb_command;
- sb->handler->exec_message = msn_sb_message;
-
- if (sb->session == MSN_SB_NEW) {
- g_snprintf(buf, sizeof(buf), "USR %d %s;{%s} %s\r\n", ++sb->trId, ic->acc->user, md->uuid, sb->key);
- } else {
- g_snprintf(buf, sizeof(buf), "ANS %d %s;{%s} %s %d\r\n", ++sb->trId, ic->acc->user, md->uuid, sb->key,
- sb->session);
- }
-
- if (msn_sb_write(sb, "%s", buf)) {
- sb->inp = b_input_add(sb->fd, B_EV_IO_READ, msn_sb_callback, sb);
- } else {
- debug("Error %d while connecting to switchboard server", 2);
- }
-
- return FALSE;
-}
-
-static gboolean msn_sb_callback(gpointer data, gint source, b_input_condition cond)
-{
- struct msn_switchboard *sb = data;
- struct im_connection *ic = sb->ic;
- struct msn_data *md = ic->proto_data;
-
- if (msn_handler(sb->handler) != -1) {
- return TRUE;
- }
-
- if (sb->msgq != NULL) {
- time_t now = time(NULL);
-
- if (now - md->first_sb_failure > 600) {
- /* It's not really the first one, but the start of this "series".
- With this, the warning below will be shown only if this happens
- at least three times in ten minutes. This algorithm isn't
- perfect, but for this purpose it will do. */
- md->first_sb_failure = now;
- md->sb_failures = 0;
- }
-
- debug("Error: Switchboard died");
- if (++md->sb_failures >= 3) {
- imcb_log(ic, "Warning: Many switchboard failures on MSN connection. "
- "There might be problems delivering your messages.");
- }
-
- if (md->msgq == NULL) {
- md->msgq = sb->msgq;
- } else {
- GSList *l;
-
- for (l = md->msgq; l->next; l = l->next) {
- ;
- }
- l->next = sb->msgq;
- }
- sb->msgq = NULL;
-
- debug("Moved queued messages back to the main queue, "
- "creating a new switchboard to retry.");
- if (!msn_ns_write(ic, -1, "XFR %d SB\r\n", ++md->trId)) {
- return FALSE;
- }
- }
-
- msn_sb_destroy(sb);
- return FALSE;
-}
-
-static int msn_sb_command(struct msn_handler_data *handler, char **cmd, int num_parts)
-{
- struct msn_switchboard *sb = handler->data;
- struct im_connection *ic = sb->ic;
-
- if (!num_parts) {
- /* Hrrm... Empty command...? Ignore? */
- return(1);
- }
-
- if (strcmp(cmd[0], "XFR") == 0) {
- imcb_error(ic,
- "Received an XFR from a switchboard server, unable to comply! This is likely to be a bug, please report it!");
- imc_logout(ic, TRUE);
- return(0);
- } else if (strcmp(cmd[0], "USR") == 0) {
- if (num_parts < 5) {
- msn_sb_destroy(sb);
- return(0);
- }
-
- if (strcmp(cmd[2], "OK") != 0) {
- msn_sb_destroy(sb);
- return(0);
- }
-
- if (sb->who) {
- return msn_sb_write(sb, "CAL %d %s\r\n", ++sb->trId, sb->who);
- } else {
- debug("Just created a switchboard, but I don't know what to do with it.");
- }
- } else if (strcmp(cmd[0], "IRO") == 0) {
- int num, tot;
-
- if (num_parts < 6) {
- msn_sb_destroy(sb);
- return(0);
- }
-
- num = atoi(cmd[2]);
- tot = atoi(cmd[3]);
-
- if (tot <= 0) {
- msn_sb_destroy(sb);
- return(0);
- } else if (tot > 1) {
- char buf[1024];
-
- /* For as much as I understand this MPOP stuff now, a
- switchboard has two (or more) roster entries per
- participant. One "bare JID" and one JID;UUID. Ignore
- the latter. */
- if (!strchr(cmd[4], ';')) {
- /* HACK: Since even 1:1 chats now have >2 participants
- (ourselves included) it gets hard to tell them apart
- from rooms. Let's hope this is enough: */
- if (sb->chat == NULL && num != tot) {
- g_snprintf(buf, sizeof(buf), "MSN groupchat session %d", sb->session);
- sb->chat = imcb_chat_new(ic, buf);
-
- g_free(sb->who);
- sb->who = NULL;
- }
-
- if (sb->chat) {
- imcb_chat_add_buddy(sb->chat, cmd[4]);
- }
- }
-
- /* We have the full roster, start showing the channel to
- the user. */
- if (num == tot && sb->chat) {
- imcb_chat_add_buddy(sb->chat, ic->acc->user);
- }
- }
- } else if (strcmp(cmd[0], "ANS") == 0) {
- if (num_parts < 3) {
- msn_sb_destroy(sb);
- return(0);
- }
-
- if (strcmp(cmd[2], "OK") != 0) {
- debug("Switchboard server sent a negative ANS reply");
- msn_sb_destroy(sb);
- return(0);
- }
-
- sb->ready = 1;
-
- msn_sb_start_keepalives(sb, FALSE);
- } else if (strcmp(cmd[0], "CAL") == 0) {
- if (num_parts < 4 || !g_ascii_isdigit(cmd[3][0])) {
- msn_sb_destroy(sb);
- return(0);
- }
-
- sb->session = atoi(cmd[3]);
- } else if (strcmp(cmd[0], "JOI") == 0) {
- if (num_parts < 3) {
- msn_sb_destroy(sb);
- return(0);
- }
-
- /* See IRO above. Handle "bare JIDs" only. */
- if (strchr(cmd[1], ';')) {
- return 1;
- }
-
- if (sb->who && g_strcasecmp(cmd[1], sb->who) == 0) {
- /* The user we wanted to talk to is finally there, let's send the queued messages then. */
- struct msn_message *m;
- GSList *l;
- int st = 1;
-
- debug("%s arrived in the switchboard session, now sending queued message(s)", cmd[1]);
-
- /* Without this, sendmessage() will put everything back on the queue... */
- sb->ready = 1;
-
- while ((l = sb->msgq)) {
- m = l->data;
- if (st) {
- /* This hack is meant to convert a regular new chat into a groupchat */
- if (strcmp(m->text, GROUPCHAT_SWITCHBOARD_MESSAGE) == 0) {
- msn_sb_to_chat(sb);
- } else {
- st = msn_sb_sendmessage(sb, m->text);
- }
- }
- sb->msgq = g_slist_remove(sb->msgq, m);
- g_free(m->text);
- g_free(m->who);
- g_free(m);
- }
-
- msn_sb_start_keepalives(sb, FALSE);
-
- return(st);
- } else if (strcmp(cmd[1], ic->acc->user) == 0) {
- /* Well, gee thanks. Thanks for letting me know I've arrived.. */
- } else if (sb->who) {
- debug("Converting chat with %s to a groupchat because %s joined the session.", sb->who, cmd[1]);
-
- /* This SB is a one-to-one chat right now, but someone else is joining. */
- msn_sb_to_chat(sb);
-
- imcb_chat_add_buddy(sb->chat, cmd[1]);
- } else if (sb->chat) {
- imcb_chat_add_buddy(sb->chat, cmd[1]);
- sb->ready = 1;
- } else {
- /* PANIC! */
- }
- } else if (strcmp(cmd[0], "MSG") == 0) {
- if (num_parts < 4) {
- msn_sb_destroy(sb);
- return(0);
- }
-
- sb->handler->msglen = atoi(cmd[3]);
-
- if (sb->handler->msglen <= 0) {
- debug("Received a corrupted message on the switchboard, the switchboard will be closed");
- msn_sb_destroy(sb);
- return(0);
- }
- } else if (strcmp(cmd[0], "NAK") == 0) {
- if (sb->who) {
- imcb_log(ic, "The MSN servers could not deliver one of your messages to %s.", sb->who);
- } else {
- imcb_log(ic,
- "The MSN servers could not deliver one of your groupchat messages to all participants.");
- }
- } else if (strcmp(cmd[0], "BYE") == 0) {
- if (num_parts < 2) {
- msn_sb_destroy(sb);
- return(0);
- }
-
- /* if( cmd[2] && *cmd[2] == '1' ) -=> Chat is being cleaned up because of idleness */
-
- if (sb->who) {
- msn_sb_stop_keepalives(sb);
-
- /* This is a single-person chat, and the other person is leaving. */
- g_free(sb->who);
- sb->who = NULL;
- sb->ready = 0;
-
- debug("Person %s left the one-to-one switchboard connection. Keeping it around as a spare...",
- cmd[1]);
-
- /* We could clean up the switchboard now, but keeping it around
- as a spare for a next conversation sounds more sane to me.
- The server will clean it up when it's idle for too long. */
- } else if (sb->chat && !strchr(cmd[1], ';')) {
- imcb_chat_remove_buddy(sb->chat, cmd[1], "");
- } else {
- /* PANIC! */
- }
- } else if (g_ascii_isdigit(cmd[0][0])) {
- int num = atoi(cmd[0]);
- const struct msn_status_code *err = msn_status_by_number(num);
-
- /* If the person is offline, send an offline message instead,
- and don't report an error. */
- if (num == 217) {
- msn_ns_oim_send_queue(ic, &sb->msgq);
- } else {
- imcb_error(ic, "Error reported by switchboard server: %s", err->text);
- }
-
- if (err->flags & STATUS_SB_FATAL) {
- msn_sb_destroy(sb);
- return 0;
- } else if (err->flags & STATUS_FATAL) {
- imc_logout(ic, TRUE);
- return 0;
- } else if (err->flags & STATUS_SB_IM_SPARE) {
- if (sb->who) {
- /* Apparently some invitation failed. We might want to use this
- board later, so keep it as a spare. */
- g_free(sb->who);
- sb->who = NULL;
-
- /* Also clear the msgq, otherwise someone else might get them. */
- msn_msgq_purge(ic, &sb->msgq);
- }
-
- /* Do NOT return 0 here, we want to keep this sb. */
- }
- } else {
- /* debug( "Received unknown command from switchboard server: %s", cmd[0] ); */
- }
-
- return(1);
-}
-
-static int msn_sb_message(struct msn_handler_data *handler, char *msg, int msglen, char **cmd, int num_parts)
-{
- struct msn_switchboard *sb = handler->data;
- struct im_connection *ic = sb->ic;
- char *body;
-
- if (!num_parts) {
- return(1);
- }
-
- if ((body = strstr(msg, "\r\n\r\n"))) {
- body += 4;
- }
-
- if (strcmp(cmd[0], "MSG") == 0) {
- char *ct = get_rfc822_header(msg, "Content-Type:", msglen);
-
- if (!ct) {
- return(1);
- }
-
- if (g_strncasecmp(ct, "text/plain", 10) == 0) {
- g_free(ct);
-
- if (!body) {
- return(1);
- }
-
- if (sb->who) {
- imcb_buddy_msg(ic, cmd[1], body, 0, 0);
- } else if (sb->chat) {
- imcb_chat_msg(sb->chat, cmd[1], body, 0, 0);
- } else {
- /* PANIC! */
- }
- }
- else if (g_strncasecmp(ct, "application/x-msnmsgrp2p", 24) == 0) {
- /* Not currently implemented. Don't warn about it since
- this seems to be used for avatars now. */
- g_free(ct);
- } else if (g_strncasecmp(ct, "text/x-msmsgscontrol", 20) == 0) {
- char *who = get_rfc822_header(msg, "TypingUser:", msglen);
-
- if (who) {
- imcb_buddy_typing(ic, who, OPT_TYPING);
- g_free(who);
- }
-
- g_free(ct);
- } else {
- g_free(ct);
- }
- }
-
- return(1);
-}
-
-static gboolean msn_sb_keepalive(gpointer data, gint source, b_input_condition cond)
-{
- struct msn_switchboard *sb = data;
-
- return sb->ready && msn_sb_sendmessage(sb, SB_KEEPALIVE_MESSAGE);
-}
-
-void msn_sb_start_keepalives(struct msn_switchboard *sb, gboolean initial)
-{
- bee_user_t *bu;
-
- if (sb && sb->who && sb->keepalive == 0 &&
- (bu = bee_user_by_handle(sb->ic->bee, sb->ic, sb->who)) &&
- !(bu->flags & BEE_USER_ONLINE) &&
- set_getbool(&sb->ic->acc->set, "switchboard_keepalives")) {
- if (initial) {
- msn_sb_keepalive(sb, 0, 0);
- }
-
- sb->keepalive = b_timeout_add(20000, msn_sb_keepalive, sb);
- }
-}
-
-void msn_sb_stop_keepalives(struct msn_switchboard *sb)
-{
- if (sb && sb->keepalive > 0) {
- b_event_remove(sb->keepalive);
- sb->keepalive = 0;
- }
-}