# HG changeset patch # User Christian Hammond # Date 1052186816 0 # Node ID e2e53316a21d379b5579d619e16eb91727eb4031 # Parent 6aa785e55d0fe708ded64144eacb2ac85c3ae07c [gaim-migrate @ 5681] Announcing the new MSN prpl! It probably has some bugs, and for the time being, there is no file transfer. That's good though, because the current MSN file transfer is a little broken. I've had many corrupted files. I'll commit new file transfer code when it's written. I want this heavily tested before 0.63! If you use MSN, please talk to people on it. Let me know of any oddities, crashes, bugs, whatever. I'll fix things as I find them. committer: Tailor Script diff -r 6aa785e55d0f -r e2e53316a21d src/protocols/msn/Makefile.am --- a/src/protocols/msn/Makefile.am Tue May 06 00:34:54 2003 +0000 +++ b/src/protocols/msn/Makefile.am Tue May 06 02:06:56 2003 +0000 @@ -3,13 +3,29 @@ pkgdir = $(libdir)/gaim MSNSOURCES = \ - ft.c \ + away.c \ + away.h \ + dispatch.c \ + dispatch.h \ + error.c \ + error.h \ + md5.h \ msg.c \ msg.h \ msn.c \ msn.h \ + notification.c \ + notification.h \ + servconn.c \ + servconn.h \ + session.c \ + session.h \ switchboard.c \ - switchboard.h + switchboard.h \ + user.c \ + user.h \ + utils.c \ + utils.h AM_CFLAGS = $(st) diff -r 6aa785e55d0f -r e2e53316a21d src/protocols/msn/away.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/msn/away.c Tue May 06 02:06:56 2003 +0000 @@ -0,0 +1,45 @@ +/** + * @file away.c Away functions + * + * gaim + * + * Copyright (C) 2003 Christian Hammond + * + * 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 + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "msn.h" +#include "away.h" + +static const char *away_text[] = +{ + N_("Available"), + N_("Available"), + N_("Busy"), + N_("Idle"), + N_("Be Right Back"), + N_("Away From Computer"), + N_("On The Phone"), + N_("Out To Lunch"), + N_("Available"), + N_("Available") +}; + +const char * +msn_away_get_text(MsnAwayType type) +{ + g_return_val_if_fail(type >= 0 && type <= MSN_HIDDEN, NULL); + + return away_text[type]; +} diff -r 6aa785e55d0f -r e2e53316a21d src/protocols/msn/away.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/msn/away.h Tue May 06 02:06:56 2003 +0000 @@ -0,0 +1,51 @@ +/** + * @file away.h Away functions + * + * gaim + * + * Copyright (C) 2003 Christian Hammond + * + * 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 + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _MSN_AWAY_H_ +#define _MSN_AWAY_H_ + +/** + * Away types. + */ +typedef enum +{ + MSN_ONLINE = 1, + MSN_BUSY = 2, + MSN_IDLE = 3, + MSN_BRB = 4, + MSN_AWAY = 5, + MSN_PHONE = 6, + MSN_LUNCH = 7, + MSN_OFFLINE = 8, + MSN_HIDDEN = 9 + +} MsnAwayType; + +/** + * Returns the string representation of an away type. + * + * @param type The away type. + * + * @return The string representation of the away type. + */ +const char *msn_away_get_text(MsnAwayType type); + +#endif /* _MSN_AWAY_H_ */ diff -r 6aa785e55d0f -r e2e53316a21d src/protocols/msn/dispatch.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/msn/dispatch.c Tue May 06 02:06:56 2003 +0000 @@ -0,0 +1,228 @@ +/** + * @file dispatch.c Dispatch server functions + * + * gaim + * + * Copyright (C) 2003 Christian Hammond + * + * 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 + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "msn.h" +#include "dispatch.h" +#include "notification.h" +#include "error.h" + +static GHashTable *dispatch_commands = NULL; + +static gboolean +__ver_cmd(MsnServConn *servconn, const char *command, const char **params, + size_t param_count) +{ + struct gaim_connection *gc = servconn->session->account->gc; + size_t i; + gboolean msnp5_found = FALSE; + + for (i = 1; i < param_count; i++) { + if (!strcmp(params[i], "MSNP5")) { + msnp5_found = TRUE; + break; + } + } + + if (!msnp5_found) { + hide_login_progress(gc, _("Protocol not supported")); + signoff(gc); + + return FALSE; + } + + if (!msn_servconn_send_command(servconn, "INF", NULL)) { + hide_login_progress(gc, _("Unable to request INF\n")); + signoff(gc); + + return FALSE; + } + + return TRUE; +} + +static gboolean +__inf_cmd(MsnServConn *servconn, const char *command, const char **params, + size_t param_count) +{ + struct gaim_connection *gc = servconn->session->account->gc; + char outparams[MSN_BUF_LEN]; + + if (strcmp(params[1], "MD5")) { + hide_login_progress(gc, _("Unable to login using MD5")); + signoff(gc); + + return FALSE; + } + + g_snprintf(outparams, sizeof(outparams), "MD5 I %s", gc->username); + + if (!msn_servconn_send_command(servconn, "USR", outparams)) { + hide_login_progress(gc, _("Unable to send USR\n")); + signoff(gc); + + return FALSE; + } + + set_login_progress(gc, 3, _("Requesting to send password")); + + return TRUE; +} + +static gboolean +__xfr_cmd(MsnServConn *servconn, const char *command, const char **params, + size_t param_count) +{ + MsnSession *session = servconn->session; + struct gaim_connection *gc = servconn->session->account->gc; + char *host; + int port; + char *c; + + if (param_count < 2 || strcmp(params[1], "NS")) { + hide_login_progress(gc, _("Got invalid XFR\n")); + signoff(gc); + + return FALSE; + } + + host = g_strdup(params[2]); + + if ((c = strchr(host, ':')) != NULL) { + *c = '\0'; + + port = atoi(c + 1); + } + else + port = 1863; + + session->passport_info.sl = time(NULL); + + /* Disconnect from here. */ + msn_servconn_destroy(servconn); + session->dispatch_conn = NULL; + + /* Now connect to the switchboard. */ + session->notification_conn = msn_notification_new(session, host, port); + + g_free(host); + + if (!msn_servconn_connect(session->notification_conn)) { + hide_login_progress(gc, _("Unable to transfer")); + signoff(gc); + } + + return FALSE; +} + +static gboolean +__unknown_cmd(MsnServConn *servconn, const char *command, const char **params, + size_t param_count) +{ + struct gaim_connection *gc = servconn->session->account->gc; + + if (isdigit(*command)) { + char buf[4]; + + strncpy(buf, command, 4); + buf[4] = '\0'; + + hide_login_progress(gc, (char *)msn_error_get_text(atoi(buf))); + } + else + hide_login_progress(gc, _("Unable to parse message.")); + + signoff(gc); + + return FALSE; +} + +static gboolean +__connect_cb(gpointer data, gint source, GaimInputCondition cond) +{ + MsnServConn *dispatch = data; + MsnSession *session = dispatch->session; + struct gaim_connection *gc = session->account->gc; + + if (source == -1) { + hide_login_progress(session->account->gc, _("Unable to connect")); + signoff(session->account->gc); + return FALSE; + } + + set_login_progress(gc, 1, _("Connecting")); + + if (dispatch->fd != source) + dispatch->fd = source; + + if (!msn_servconn_send_command(dispatch, "VER", + "MSNP7 MSNP6 MSNP5 MSNP4 CVR0")) { + hide_login_progress(gc, _("Unable to write to server")); + signoff(gc); + return FALSE; + } + + set_login_progress(session->account->gc, 2, _("Syncing with server")); + + return TRUE; +} + +static void +__failed_read_cb(gpointer data, gint source, GaimInputCondition cond) +{ + MsnServConn *dispatch = data; + struct gaim_connection *gc; + + gc = dispatch->session->account->gc; + + hide_login_progress(gc, _("Error reading from server")); + signoff(gc); +} + +MsnServConn * +msn_dispatch_new(MsnSession *session, const char *server, int port) +{ + MsnServConn *dispatch; + + dispatch = msn_servconn_new(session); + + msn_servconn_set_server(dispatch, server, port); + msn_servconn_set_connect_cb(dispatch, __connect_cb); + msn_servconn_set_failed_read_cb(dispatch, __failed_read_cb); + + if (dispatch_commands == NULL) { + /* Register the command callbacks. */ + msn_servconn_register_command(dispatch, "VER", __ver_cmd); + msn_servconn_register_command(dispatch, "INF", __inf_cmd); + msn_servconn_register_command(dispatch, "XFR", __xfr_cmd); + msn_servconn_register_command(dispatch, "_unknown_", __unknown_cmd); + + /* Save this for future use. */ + dispatch_commands = dispatch->commands; + } + else { + g_hash_table_destroy(dispatch->commands); + + dispatch->commands = dispatch_commands; + } + + return dispatch; +} + diff -r 6aa785e55d0f -r e2e53316a21d src/protocols/msn/dispatch.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/msn/dispatch.h Tue May 06 02:06:56 2003 +0000 @@ -0,0 +1,31 @@ +/** + * @file dispatch.h Dispatch server functions + * + * gaim + * + * Copyright (C) 2003 Christian Hammond + * + * 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 + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _MSN_DISPATCH_H_ +#define _MSN_DISPATCH_H_ + +#include "session.h" +#include "servconn.h" + +MsnServConn *msn_dispatch_new(MsnSession *session, const char *server, + int port); + +#endif /* _MSN_DISPATCH_H_ */ diff -r 6aa785e55d0f -r e2e53316a21d src/protocols/msn/error.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/msn/error.c Tue May 06 02:06:56 2003 +0000 @@ -0,0 +1,180 @@ +/** + * @file error.c Error functions + * + * gaim + * + * Copyright (C) 2003 Christian Hammond + * + * 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 + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "msn.h" + +const char * +msn_error_get_text(unsigned int type) +{ + static char msg[MSN_BUF_LEN]; + + switch (type) { + case 200: + g_snprintf(msg, sizeof(msg), + _("Syntax Error (probably a Gaim bug)")); + break; + case 201: + g_snprintf(msg, sizeof(msg), + _("Invalid Parameter (probably a Gaim bug)")); + break; + case 205: + g_snprintf(msg, sizeof(msg), _("Invalid User")); + break; + case 206: + g_snprintf(msg, sizeof(msg), + _("Fully Qualified Domain Name missing")); + break; + case 207: + g_snprintf(msg, sizeof(msg), _("Already Login")); + break; + case 208: + g_snprintf(msg, sizeof(msg), _("Invalid Username")); + break; + case 209: + g_snprintf(msg, sizeof(msg), _("Invalid Friendly Name")); + break; + case 210: + g_snprintf(msg, sizeof(msg), _("List Full")); + break; + case 215: + g_snprintf(msg, sizeof(msg), _("Already there")); + break; + case 216: + g_snprintf(msg, sizeof(msg), _("Not on list")); + break; + case 217: + g_snprintf(msg, sizeof(msg), _("User is offline")); + break; + case 218: + g_snprintf(msg, sizeof(msg), _("Already in the mode")); + break; + case 219: + g_snprintf(msg, sizeof(msg), _("Already in opposite list")); + break; + case 231: + g_snprintf(msg, sizeof(msg), + _("Tried to add a contact to a group " + "that doesn't exist")); + break; + case 280: + g_snprintf(msg, sizeof(msg), _("Switchboard failed")); + break; + case 281: + g_snprintf(msg, sizeof(msg), _("Notify Transfer failed")); + break; + + case 300: + g_snprintf(msg, sizeof(msg), _("Required fields missing")); + break; + case 302: + g_snprintf(msg, sizeof(msg), _("Not logged in")); + break; + + case 500: + g_snprintf(msg, sizeof(msg), _("Internal server error")); + break; + case 501: + g_snprintf(msg, sizeof(msg), _("Database server error")); + break; + case 510: + g_snprintf(msg, sizeof(msg), _("File operation error")); + break; + case 520: + g_snprintf(msg, sizeof(msg), _("Memory allocation error")); + break; + case 540: + g_snprintf(msg, sizeof(msg), _("Wrong CHL value sent to server")); + break; + + case 600: + g_snprintf(msg, sizeof(msg), _("Server busy")); + break; + case 601: + g_snprintf(msg, sizeof(msg), _("Server unavailable")); + break; + case 602: + g_snprintf(msg, sizeof(msg), _("Peer Notification server down")); + break; + case 603: + g_snprintf(msg, sizeof(msg), _("Database connect error")); + break; + case 604: + g_snprintf(msg, sizeof(msg), + _("Server is going down (abandon ship)")); + break; + + case 707: + g_snprintf(msg, sizeof(msg), _("Error creating connection")); + break; + case 710: + g_snprintf(msg, sizeof(msg), + _("CVR parameters are either unknown or not allowed")); + break; + case 711: + g_snprintf(msg, sizeof(msg), _("Unable to write")); + break; + case 712: + g_snprintf(msg, sizeof(msg), _("Session overload")); + break; + case 713: + g_snprintf(msg, sizeof(msg), _("User is too active")); + break; + case 714: + g_snprintf(msg, sizeof(msg), _("Too many sessions")); + break; + case 715: + g_snprintf(msg, sizeof(msg), _("Not expected")); + break; + case 717: + g_snprintf(msg, sizeof(msg), _("Bad friend file")); + break; + + case 911: + g_snprintf(msg, sizeof(msg), _("Authentication failed")); + break; + case 913: + g_snprintf(msg, sizeof(msg), _("Not allowed when offline")); + break; + case 920: + g_snprintf(msg, sizeof(msg), _("Not accepting new users")); + break; + case 924: + g_snprintf(msg, sizeof(msg), + _("Passport account not yet verified")); + break; + + default: + g_snprintf(msg, sizeof(msg), _("Unknown Error Code %d"), type); + break; + } + + return msg; +} + +void +msn_error_handle(unsigned int type) +{ + const char *text; + + text = msn_error_get_text(type); + + do_error_dialog(text, NULL, GAIM_ERROR); +} diff -r 6aa785e55d0f -r e2e53316a21d src/protocols/msn/error.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/msn/error.h Tue May 06 02:06:56 2003 +0000 @@ -0,0 +1,41 @@ +/** + * @file error.h Error functions + * + * gaim + * + * Copyright (C) 2003 Christian Hammond + * + * 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 + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _MSN_ERROR_H_ +#define _MSN_ERROR_H_ + +/** + * Returns the string representation of an error type. + * + * @param type The error type. + * + * @return The string representation of the error type. + */ +const char *msn_error_get_text(unsigned int type); + +/** + * Handles an error. + * + * @param type The error type. + */ +void msn_error_handle(unsigned int type); + +#endif /* _MSN_ERROR_H_ */ diff -r 6aa785e55d0f -r e2e53316a21d src/protocols/msn/msg.c --- a/src/protocols/msn/msg.c Tue May 06 00:34:54 2003 +0000 +++ b/src/protocols/msn/msg.c Tue May 06 02:06:56 2003 +0000 @@ -3,7 +3,7 @@ * * gaim * - * Copyright (C) 2003, Christian Hammond + * Copyright (C) 2003 Christian Hammond * * 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 @@ -18,15 +18,502 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ #include "msn.h" +#include "msg.h" -int -msn_write(int fd, void *data, int len) +#define GET_NEXT(tmp) \ + while (*(tmp) && *(tmp) != ' ' && *(tmp) != '\r') \ + (tmp)++; \ + *(tmp)++ = '\0'; \ + if (*(tmp) == '\n') *(tmp)++; \ + while (*(tmp) && *(tmp) == ' ') \ + (tmp)++ + +#define GET_NEXT_LINE(tmp) \ + while (*(tmp) && *(tmp) != '\r') \ + (tmp)++; \ + *(tmp)++ = '\0'; \ + if (*(tmp) == '\n') *(tmp)++ + +/* + * "MIME-Version: 1.0\r\n" == 19 + * "Content-Type: " == 14 + * "\r\n" == 2 + * "\r\n" before body == 2 + * ---- + * 37 + * MATH PAYS OFF!! + */ +#define MSN_MESSAGE_BASE_SIZE 37 + +MsnMessage * +msn_message_new(void) +{ + MsnMessage *msg; + + msg = g_new0(MsnMessage, 1); + + msg->attr_table = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, g_free); + msg->size = MSN_MESSAGE_BASE_SIZE; + + msn_message_set_attr(msg, "User-Agent", "Gaim/" VERSION); + msn_message_set_content_type(msg, "text/plain"); + msn_message_set_charset(msg, "UTF-8"); + msn_message_set_flag(msg, 'N'); + + return msg; +} + +MsnMessage * +msn_message_new_from_str(MsnSession *session, const char *str) { - gaim_debug(GAIM_DEBUG_MISC, "msn", "C: %s", (char *)data); + MsnMessage *msg; + char *tmp, *field1, *field2, *c; + + g_return_val_if_fail(str != NULL, NULL); + g_return_val_if_fail(!g_ascii_strncasecmp(str, "MSG", 3), NULL); + + msg = msn_message_new(); + + tmp = g_strdup(str); + + GET_NEXT(tmp); /* Skip MSG */ + field1 = tmp; + + GET_NEXT(tmp); /* Skip the passport or TID */ + field2 = tmp; + + GET_NEXT(tmp); /* Skip the username or flag */ + msg->size = atoi(tmp); + + if (msg->size != strlen(strchr(str, '\n') + 1)) { + gaim_debug(GAIM_DEBUG_ERROR, "msn", + "Message size (%d) and string length (%d) " + "do not match!\n", msg->size, strlen(str)); + } + + /* + * We're going to make sure this is incoming by checking field1. + * If it has any non-numbers in it, it's incoming. Otherwise, outgoing. + */ + msg->incoming = FALSE; + + for (c = field1; *c != '\0'; c++) { + if (*c < '0' || *c > '9') { + msg->incoming = TRUE; + break; + } + } + + if (msg->incoming) { + msg->sender = msn_users_find_with_passport(session->users, field1); + + gaim_debug(GAIM_DEBUG_MISC, "msn", "incoming message: %s, %s\n", + field1, field2); + if (msg->sender == NULL) + msg->sender = msn_user_new(session, field1, field2); + else + msn_user_ref(msg->sender); + } + else { + msg->tid = atoi(field1); + msg->flag = *field2; + } + + /* Back to the parsination. */ + while (*tmp != '\r') { + char *key, *value; + + key = tmp; + + GET_NEXT(tmp); /* Key */ + + value = tmp; + + GET_NEXT_LINE(tmp); /* Value */ + + if ((c = strchr(key, ':')) != NULL) + *c = '\0'; + + if (!g_ascii_strcasecmp(key, "Content-Type")) { + char *charset; + + if ((c = strchr(value, ';')) != NULL) { + if ((charset = strchr(c, '=')) != NULL) { + charset++; + msn_message_set_charset(msg, charset); + } + + *c = '\0'; + } + + msn_message_set_content_type(msg, value); + } + else + msn_message_set_attr(msg, key, value); + } + + /* "\r\n" */ + tmp += 2; + + /* Now we *should* be at the body. */ + msn_message_set_body(msg, tmp); + + /* Done! */ + + return msg; +} - return write(fd, data, len); +void +msn_message_destroy(MsnMessage *msg) +{ + g_return_if_fail(msg != NULL); + + if (msg->sender != NULL) + msn_user_unref(msg->sender); + + if (msg->receiver != NULL) + msn_user_unref(msg->receiver); + + if (msg->body != NULL) + g_free(msg->body); + + if (msg->content_type != NULL) + g_free(msg->content_type); + + if (msg->charset != NULL) + g_free(msg->charset); + + g_hash_table_destroy(msg->attr_table); + g_list_free(msg->attr_list); + + g_free(msg); +} + +char * +msn_message_build_string(const MsnMessage *msg) +{ + GList *l; + char *str; + char buf[MSN_BUF_LEN]; + int len; + + /* + * Okay, how we do things here is just bad. I don't like writing to + * a static buffer and then copying to the string. Unfortunately, + * just trying to append to the string is causing issues.. Such as + * the string you're appending to being erased. Ugh. So, this is + * good enough for now. + * + * -- ChipX86 + */ + g_return_val_if_fail(msg != NULL, NULL); + + if (msn_message_is_incoming(msg)) { + MsnUser *sender = msn_message_get_sender(msg); + + g_snprintf(buf, sizeof(buf), "MSG %s %s %d\r\n", + msn_user_get_passport(sender), msn_user_get_name(sender), + msg->size); + } + else { + g_snprintf(buf, sizeof(buf), "MSG %d %c %d\r\n", + msn_message_get_transaction_id(msg), + msn_message_get_flag(msg), msg->size); + } + + len = strlen(buf) + msg->size + 1; + + str = g_new0(char, len); + + g_strlcpy(str, buf, len); + + /* Standard header. */ + if (msg->charset == NULL) { + g_snprintf(buf, sizeof(buf), + "MIME-Version: 1.0\r\n" + "Content-Type: %s\r\n", + msg->content_type); + } + else { + g_snprintf(buf, sizeof(buf), + "MIME-Version: 1.0\r\n" + "Content-Type: %s; charset=%s\r\n", + msg->content_type, msg->charset); + } + + g_strlcat(str, buf, len); + + for (l = msg->attr_list; l != NULL; l = l->next) { + const char *key = (char *)l->data; + const char *value; + + value = msn_message_get_attr(msg, key); + + g_snprintf(buf, sizeof(buf), "%s: %s\r\n", key, value); + + g_strlcat(str, buf, len); + } + + g_snprintf(buf, sizeof(buf), "\r\n%s", msn_message_get_body(msg)); + + g_strlcat(str, buf, len); + + return str; } +gboolean +msn_message_is_outgoing(const MsnMessage *msg) +{ + g_return_val_if_fail(msg != NULL, FALSE); + + return !msg->incoming; +} + +gboolean +msn_message_is_incoming(const MsnMessage *msg) +{ + g_return_val_if_fail(msg != NULL, FALSE); + + return msg->incoming; +} + +void +msn_message_set_sender(MsnMessage *msg, MsnUser *user) +{ + g_return_if_fail(msg != NULL); + g_return_if_fail(user != NULL); + + msg->sender = user; + + msn_user_ref(msg->sender); +} + +MsnUser * +msn_message_get_sender(const MsnMessage *msg) +{ + g_return_val_if_fail(msg != NULL, NULL); + + return msg->sender; +} + +void +msn_message_set_receiver(MsnMessage *msg, MsnUser *user) +{ + g_return_if_fail(msg != NULL); + g_return_if_fail(user != NULL); + + msg->receiver = user; + + msn_user_ref(msg->receiver); +} + +MsnUser * +msn_message_get_receiver(const MsnMessage *msg) +{ + g_return_val_if_fail(msg != NULL, NULL); + + return msg->receiver; +} + +void +msn_message_set_transaction_id(MsnMessage *msg, unsigned int tid) +{ + g_return_if_fail(msg != NULL); + g_return_if_fail(tid > 0); + + msg->tid = tid; +} + +unsigned int +msn_message_get_transaction_id(const MsnMessage *msg) +{ + g_return_val_if_fail(msg != NULL, 0); + + return msg->tid; +} + +void +msn_message_set_flag(MsnMessage *msg, char flag) +{ + g_return_if_fail(msg != NULL); + g_return_if_fail(flag != 0); + + msg->flag = flag; +} + +char +msn_message_get_flag(const MsnMessage *msg) +{ + g_return_val_if_fail(msg != NULL, 0); + + return msg->flag; +} + +void +msn_message_set_body(MsnMessage *msg, const char *body) +{ + g_return_if_fail(msg != NULL); + g_return_if_fail(body != NULL); + + if (msg->body != NULL) { + msg->size -= strlen(msg->body); + g_free(msg->body); + } + + msg->body = g_strdup(body); + + msg->size += strlen(body); +} + +const char * +msn_message_get_body(const MsnMessage *msg) +{ + g_return_val_if_fail(msg != NULL, NULL); + + return msg->body; +} + +void +msn_message_set_content_type(MsnMessage *msg, const char *type) +{ + g_return_if_fail(msg != NULL); + g_return_if_fail(type != NULL); + + if (msg->content_type != NULL) { + msg->size -= strlen(msg->content_type); + g_free(msg->content_type); + } + + msg->content_type = g_strdup(type); + + msg->size += strlen(type); +} + +const char * +msn_message_get_content_type(const MsnMessage *msg) +{ + g_return_val_if_fail(msg != NULL, NULL); + + return msg->content_type; +} + +void +msn_message_set_charset(MsnMessage *msg, const char *charset) +{ + g_return_if_fail(msg != NULL); + + if (msg->charset != NULL) { + msg->size -= strlen(msg->charset) + strlen("; charset="); + g_free(msg->charset); + } + + if (charset != NULL) { + msg->charset = g_strdup(charset); + + msg->size += strlen(charset) + strlen("; charset="); + } + else + msg->charset = NULL; +} + +const char * +msn_message_get_charset(const MsnMessage *msg) +{ + g_return_val_if_fail(msg != NULL, NULL); + + return msg->charset; +} + +void +msn_message_set_attr(MsnMessage *msg, const char *attr, const char *value) +{ + const char *temp; + char *new_attr; + + g_return_if_fail(msg != NULL); + g_return_if_fail(attr != NULL); + + temp = msn_message_get_attr(msg, attr); + + if (value == NULL) { + if (temp != NULL) { + GList *l; + + for (l = msg->attr_list; l != NULL; l = l->next) { + if (!g_ascii_strcasecmp(l->data, attr)) { + msg->attr_list = g_list_remove(msg->attr_list, l->data); + + break; + } + } + + g_hash_table_remove(msg->attr_table, attr); + + msg->size -= strlen(temp) + strlen(attr) + 4; + } + + return; + } + + new_attr = g_strdup(attr); + + g_hash_table_insert(msg->attr_table, new_attr, g_strdup(value)); + + if (temp == NULL) { + msg->attr_list = g_list_append(msg->attr_list, new_attr); + msg->size += strlen(attr) + 4; + } + else + msg->size -= strlen(temp); + + msg->size += strlen(value); +} + +const char * +msn_message_get_attr(const MsnMessage *msg, const char *attr) +{ + g_return_val_if_fail(msg != NULL, NULL); + g_return_val_if_fail(attr != NULL, NULL); + + return g_hash_table_lookup(msg->attr_table, attr); +} + +GHashTable * +msn_message_get_hashtable_from_body(const MsnMessage *msg) +{ + GHashTable *table; + char *body, *s, *c; + + g_return_val_if_fail(msg != NULL, NULL); + g_return_val_if_fail(msn_message_get_body(msg) != NULL, NULL); + + s = body = g_strdup(msn_message_get_body(msg)); + + table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + + while (*s != '\r') { + char *key, *value; + + key = s; + + GET_NEXT(s); + + value = s; + + GET_NEXT_LINE(s); + + if ((c = strchr(key, ':')) != NULL) { + *c = '\0'; + + g_hash_table_insert(table, g_strdup(key), g_strdup(value)); + } + } + + g_free(body); + + return table; +} + diff -r 6aa785e55d0f -r e2e53316a21d src/protocols/msn/msg.h --- a/src/protocols/msn/msg.h Tue May 06 00:34:54 2003 +0000 +++ b/src/protocols/msn/msg.h Tue May 06 02:06:56 2003 +0000 @@ -3,7 +3,7 @@ * * gaim * - * Copyright (C) 2003, Christian Hammond + * Copyright (C) 2003 Christian Hammond * * 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 @@ -18,20 +18,233 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ #ifndef _MSN_MSG_H_ #define _MSN_MSG_H_ +typedef struct _MsnMessage MsnMessage; + +#include "session.h" +#include "user.h" + /** - * Writes a message to the server. + * A message. + */ +struct _MsnMessage +{ + MsnUser *sender; + MsnUser *receiver; + + unsigned int tid; + char flag; + + gboolean incoming; + + size_t size; + + char *content_type; + char *charset; + char *body; + + GHashTable *attr_table; + GList *attr_list; +}; + +/** + * Creates a new, empty message. + * + * @return A new message. + */ +MsnMessage *msn_message_new(void); + +/** + * Creates a new message based off a string. + * + * @param session The MSN session. + * @param str The string. + * + * @return The new message. + */ +MsnMessage *msn_message_new_from_str(MsnSession *session, const char *str); + +/** + * Destroys a message. + */ +void msn_message_destroy(MsnMessage *msg); + +/** + * Converts a message to a string. + * + * @param msg The message. + * + * @return The string representation of a message. + */ +char *msn_message_build_string(const MsnMessage *msg); + +/** + * Returns TRUE if the message is outgoing. + * + * @param msg The message. + * + * @return @c TRUE if the message is outgoing, or @c FALSE otherwise. + */ +gboolean msn_message_is_outgoing(const MsnMessage *msg); + +/** + * Returns TRUE if the message is incoming. + * + * @param msg The message. + * + * @return @c TRUE if the message is incoming, or @c FALSE otherwise. + */ +gboolean msn_message_is_incoming(const MsnMessage *msg); + +/** + * Sets the message's sender. + * + * @param msg The message. + * @param user The sender. + */ +void msn_message_set_sender(MsnMessage *msg, MsnUser *user); + +/** + * Returns the message's sender. + * + * @param msg The message. + * + * @return The sender. + */ +MsnUser *msn_message_get_sender(const MsnMessage *msg); + +/** + * Sets the message's receiver. + * + * @param msg The message. + * @param user The receiver. + */ +void msn_message_set_receiver(MsnMessage *msg, MsnUser *user); + +/** + * Returns the message's receiver. + * + * @param msg The message. * - * @param fd The file descriptor. - * @param data The data to write. - * @param len The length of the data + * @return The receiver. + */ +MsnUser *msn_message_get_receiver(const MsnMessage *msg); + +/** + * Sets the message transaction ID. + * + * @param msg The message. + * @param tid The transaction ID. + */ +void msn_message_set_transaction_id(MsnMessage *msg, unsigned int tid); + +/** + * Returns the message transaction ID. + * + * @param msg The message. + * + * @return The transaction ID. + */ +unsigned int msn_message_get_transaction_id(const MsnMessage *msg); + +/** + * Sets the flag for an outgoing message. + * + * @param msg The message. + * @param flag The flag. + */ +void msn_message_set_flag(MsnMessage *msg, char flag); + +/** + * Returns the flag for an outgoing message. + * + * @param msg The message. + * + * @return The flag. + */ +char msn_message_get_flag(const MsnMessage *msg); + +/** + * Sets the body of a message. + * + * @param msg The message. + * @param body The body of the message. + */ +void msn_message_set_body(MsnMessage *msg, const char *body); + +/** + * Returns the body of the message. + * + * @param msg The message. + * + * @return The body of the message. + */ +const char *msn_message_get_body(const MsnMessage *msg); + +/** + * Sets the content type in a message. * - * @return The number of bytes written. + * @param msg The message. + * @param type The content-type. + */ +void msn_message_set_content_type(MsnMessage *msg, const char *type); + +/** + * Returns the content type in a message. + * + * @param msg The message. + * + * @return The content-type. + */ +const char *msn_message_get_content_type(const MsnMessage *msg); + +/** + * Sets the charset in a message. + * + * @param msg The message. + * @param charset The charset. + */ +void msn_message_set_charset(MsnMessage *msg, const char *charset); + +/** + * Returns the charset in a message. + * + * @param msg The message. + * + * @return The charset. */ -int msn_write(int fd, void *data, int len); +const char *msn_message_get_charset(const MsnMessage *msg); + +/** + * Sets an attribute in a message. + * + * @param msg The message. + * @param attr The attribute name. + * @param value The attribute value. + */ +void msn_message_set_attr(MsnMessage *msg, const char *attr, + const char *value); + +/** + * Returns an attribute from a message. + * + * @param msg The message. + * @param attr The attribute. + * + * @return The value, or @c NULL if not found. + */ +const char *msn_message_get_attr(const MsnMessage *msg, const char *attr); + +/** + * Parses the body and returns it in the form of a hashtable. + * + * @param msg The message. + * + * @return The resulting hashtable. + */ +GHashTable *msn_message_get_hashtable_from_body(const MsnMessage *msg); #endif /* _MSN_MSG_H_ */ diff -r 6aa785e55d0f -r e2e53316a21d src/protocols/msn/msn.c --- a/src/protocols/msn/msn.c Tue May 06 00:34:54 2003 +0000 +++ b/src/protocols/msn/msn.c Tue May 06 02:06:56 2003 +0000 @@ -3,7 +3,7 @@ * * gaim * - * Parts Copyright (C) 2003, Christian Hammond + * Copyright (C) 2003 Christian Hammond * * 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 @@ -18,13 +18,12 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ #include "msn.h" - -#ifdef _WIN32 -#include "win32dep.h" -#endif +#include "away.h" +#include "msg.h" +#include "session.h" +#include "utils.h" #define BUDDY_ALIAS_MAXLEN 388 @@ -33,1320 +32,92 @@ /* for win32 compatability */ G_MODULE_IMPORT GSList *connections; -static void msn_login_callback(gpointer, gint, GaimInputCondition); -static void msn_login_xfr_connect(gpointer, gint, GaimInputCondition); - -static char *msn_normalize(const char *s) -{ - static char buf[BUF_LEN]; - - g_return_val_if_fail(s != NULL, NULL); - - g_snprintf(buf, sizeof(buf), "%s%s", s, strchr(s, '@') ? "" : "@hotmail.com"); - - return buf; -} - -char * -handle_errcode(char *buf, gboolean show) -{ - int errcode; - static char msg[MSN_BUF_LEN]; - - buf[4] = 0; - errcode = atoi(buf); - - switch (errcode) { - case 200: - g_snprintf(msg, sizeof(msg), _("Syntax Error (probably a Gaim bug)")); - break; - case 201: - g_snprintf(msg, sizeof(msg), _("Invalid Parameter (probably a Gaim bug)")); - break; - case 205: - g_snprintf(msg, sizeof(msg), _("Invalid User")); - break; - case 206: - g_snprintf(msg, sizeof(msg), _("Fully Qualified Domain Name missing")); - break; - case 207: - g_snprintf(msg, sizeof(msg), _("Already Login")); - break; - case 208: - g_snprintf(msg, sizeof(msg), _("Invalid Username")); - break; - case 209: - g_snprintf(msg, sizeof(msg), _("Invalid Friendly Name")); - break; - case 210: - g_snprintf(msg, sizeof(msg), _("List Full")); - break; - case 215: - g_snprintf(msg, sizeof(msg), _("Already there")); - break; - case 216: - g_snprintf(msg, sizeof(msg), _("Not on list")); - break; - case 217: - g_snprintf(msg, sizeof(msg), _("User is offline")); - break; - case 218: - g_snprintf(msg, sizeof(msg), _("Already in the mode")); - break; - case 219: - g_snprintf(msg, sizeof(msg), _("Already in opposite list")); - break; - case 280: - g_snprintf(msg, sizeof(msg), _("Switchboard failed")); - break; - case 281: - g_snprintf(msg, sizeof(msg), _("Notify Transfer failed")); - break; - - case 300: - g_snprintf(msg, sizeof(msg), _("Required fields missing")); - break; - case 302: - g_snprintf(msg, sizeof(msg), _("Not logged in")); - break; - - case 500: - g_snprintf(msg, sizeof(msg), _("Internal server error")); - break; - case 501: - g_snprintf(msg, sizeof(msg), _("Database server error")); - break; - case 510: - g_snprintf(msg, sizeof(msg), _("File operation error")); - break; - case 520: - g_snprintf(msg, sizeof(msg), _("Memory allocation error")); - break; - - case 600: - g_snprintf(msg, sizeof(msg), _("Server busy")); - break; - case 601: - g_snprintf(msg, sizeof(msg), _("Server unavailable")); - break; - case 602: - g_snprintf(msg, sizeof(msg), _("Peer Notification server down")); - break; - case 603: - g_snprintf(msg, sizeof(msg), _("Database connect error")); - break; - case 604: - g_snprintf(msg, sizeof(msg), _("Server is going down (abandon ship)")); - break; - - case 707: - g_snprintf(msg, sizeof(msg), _("Error creating connection")); - break; - case 711: - g_snprintf(msg, sizeof(msg), _("Unable to write")); - break; - case 712: - g_snprintf(msg, sizeof(msg), _("Session overload")); - break; - case 713: - g_snprintf(msg, sizeof(msg), _("User is too active")); - break; - case 714: - g_snprintf(msg, sizeof(msg), _("Too many sessions")); - break; - case 715: - g_snprintf(msg, sizeof(msg), _("Not expected")); - break; - case 717: - g_snprintf(msg, sizeof(msg), _("Bad friend file")); - break; - - case 911: - g_snprintf(msg, sizeof(msg), _("Authentication failed")); - break; - case 913: - g_snprintf(msg, sizeof(msg), _("Not allowed when offline")); - break; - case 920: - g_snprintf(msg, sizeof(msg), _("Not accepting new users")); - break; - case 924: - g_snprintf(msg, sizeof(msg), _("User unverified")); - break; - default: - g_snprintf(msg, sizeof(msg), _("Unknown Error Code")); - break; - } - - if (show) - do_error_dialog(msg, NULL, GAIM_ERROR); - - return msg; -} - - -char *url_decode(const char *msg) -{ - static char buf[MSN_BUF_LEN]; - int i, j = 0; - char *bum; - - bzero(buf, sizeof(buf)); - for (i = 0; i < strlen(msg); i++) { - char hex[3]; - if (msg[i] != '%') { - buf[j++] = msg[i]; - continue; - } - strncpy(hex, msg + ++i, 2); hex[2] = 0; - /* i is pointing to the start of the number */ - i++; /* now it's at the end and at the start of the for loop - will be at the next character */ - buf[j++] = strtol(hex, NULL, 16); - } - buf[j] = 0; - - if(!g_utf8_validate(buf, -1, (const char **)&bum)) - *bum = '\0'; - - return buf; -} - -static char *url_encode(const char *msg) -{ - static char buf[MSN_BUF_LEN]; - int i, j = 0; - - bzero(buf, sizeof(buf)); - for (i = 0; i < strlen(msg); i++) { - if (isalnum(msg[i])) - buf[j++] = msg[i]; - else { - sprintf(buf + j, "%%%02x", (unsigned char)msg[i]); - j += 3; - } - } - buf[j] = 0; - - return buf; -} - -static void handle_hotmail(struct gaim_connection *gc, char *data) -{ - char login_url[2048]; - char buf[MSN_BUF_LEN]; - struct msn_data *md = gc->proto_data; - - if (strchr(gc->username, '@') != strstr(gc->username, "@hotmail.com")) - /* We can only get Hotmail notification from hotmail users */ - return; - - if (!md->passport) { - g_snprintf(buf, sizeof(buf), "URL %u INBOX\r\n", ++md->trId); - - if (msn_write(md->fd, buf, strlen(buf)) < 0) { - return; - } - } else { - g_snprintf(login_url, sizeof(login_url), "%s", md->passport); - - if (strstr(data, "Content-Type: text/x-msmsgsinitialemailnotification;")) { - char *x = strstr(data, "Inbox-Unread:"); - if (!x) return; - x += strlen("Inbox-Unread: "); - connection_has_mail(gc, atoi(x), NULL, NULL, login_url); - } else if (strstr(data, "Content-Type: text/x-msmsgsemailnotification;")) { - char *from = strstr(data, "From:"); - char *subject = strstr(data, "Subject:"); - char *x; - if (!from || !subject) { - connection_has_mail(gc, 1, NULL, NULL, login_url); - return; - } - from += strlen("From: "); - x = strstr(from, "\r\n"); *x = 0; - subject += strlen("Subject: "); - x = strstr(subject, "\r\n"); *x = 0; - connection_has_mail(gc, -1, from, subject, login_url); - } - } -} - -struct msn_add_permit { - struct gaim_connection *gc; - char *user; - char *friend; -}; - -static void msn_accept_add(struct msn_add_permit *map) -{ - if(g_slist_find(connections, map->gc)) { - struct msn_data *md = map->gc->proto_data; - char buf[MSN_BUF_LEN]; - - g_snprintf(buf, sizeof(buf), "ADD %u AL %s %s\r\n", ++md->trId, map->user, url_encode(map->friend)); - - if (msn_write(md->fd, buf, strlen(buf)) < 0) { - hide_login_progress(map->gc, _("Write error")); - signoff(map->gc); - return; - } - gaim_privacy_permit_add(map->gc->account, map->user); - build_allow_list(); /* er. right. we'll need to have a thing for this in CUI too */ - show_got_added(map->gc, NULL, map->user, map->friend, NULL); - } - - g_free(map->user); - g_free(map->friend); - g_free(map); -} - -static void msn_cancel_add(struct msn_add_permit *map) -{ - if(g_slist_find(connections, map->gc)) { - struct msn_data *md = map->gc->proto_data; - char buf[MSN_BUF_LEN]; - - g_snprintf(buf, sizeof(buf), "ADD %u BL %s %s\r\n", ++md->trId, map->user, url_encode(map->friend)); - if (msn_write(md->fd, buf, strlen(buf)) < 0) { - hide_login_progress(map->gc, _("Write error")); - signoff(map->gc); - return; - } - gaim_privacy_deny_add(map->gc->account, map->user); - build_block_list(); - } - - g_free(map->user); - g_free(map->friend); - g_free(map); -} - -static int msn_process_main(struct gaim_connection *gc, char *buf) -{ - struct msn_data *md = gc->proto_data; - char sendbuf[MSN_BUF_LEN]; - - if (!g_ascii_strncasecmp(buf, "ADD", 3)) { - char *list, *user, *friend, *tmp = buf; - struct msn_add_permit *ap; - GSList *perm = gc->account->permit; - char msg[MSN_BUF_LEN]; - - GET_NEXT(tmp); - GET_NEXT(tmp); - list = tmp; - - GET_NEXT(tmp); - GET_NEXT(tmp); - user = tmp; - - GET_NEXT(tmp); - friend = url_decode(tmp); - - if (g_ascii_strcasecmp(list, "RL")) - return 1; - - while (perm) { - if (!gaim_utf8_strcasecmp(perm->data, user)) - return 1; - perm = perm->next; - } - - ap = g_new0(struct msn_add_permit, 1); - ap->user = g_strdup(user); - ap->friend = g_strdup(friend); - ap->gc = gc; - - g_snprintf(msg, sizeof(msg), _("The user %s (%s) wants to add %s to his or her buddy list."), - ap->user, ap->friend, ap->gc->username); - - // do_ask_dialog(msg, NULL, ap, _("Authorize"), msn_accept_add, _("Deny"), msn_cancel_add, my_protocol->handle, FALSE); - } else if (!g_ascii_strncasecmp(buf, "BLP", 3)) { - char *type, *tmp = buf; - - GET_NEXT(tmp); - GET_NEXT(tmp); - GET_NEXT(tmp); - type = tmp; - - if (!g_ascii_strcasecmp(type, "AL")) { - /* If the current setting is AL, messages - * from users who are not in BL will be delivered - * - * In other words, deny some */ - gc->account->permdeny = DENY_SOME; - } else { - /* If the current - * setting is BL, only messages from people who are in the AL will be - * delivered. - * - * In other words, permit some */ - gc->account->permdeny = PERMIT_SOME; - } - } else if (!g_ascii_strncasecmp(buf, "BPR", 3)) { - } else if (!g_ascii_strncasecmp(buf, "CHG", 3)) { - } else if (!g_ascii_strncasecmp(buf, "CHL", 3)) { - char *hash = buf; - char buf2[MSN_BUF_LEN]; - md5_state_t st; - md5_byte_t di[16]; - int i; - - GET_NEXT(hash); - GET_NEXT(hash); - - md5_init(&st); - md5_append(&st, (const md5_byte_t *)hash, strlen(hash)); - md5_append(&st, (const md5_byte_t *)"Q1P7W2E4J9R8U3S5", strlen("Q1P7W2E4J9R8U3S5")); - md5_finish(&st, di); - - g_snprintf(sendbuf, sizeof(sendbuf), "QRY %u msmsgs@msnmsgr.com 32\r\n", ++md->trId); - for (i = 0; i < 16; i++) { - g_snprintf(buf2, sizeof(buf2), "%02x", di[i]); - strcat(sendbuf, buf2); - } - - if (msn_write(md->fd, sendbuf, strlen(sendbuf)) < 0) { - hide_login_progress(gc, _("Unable to write to server")); - signoff(gc); - } - - } else if (!g_ascii_strncasecmp(buf, "FLN", 3)) { - char *usr = buf; - - GET_NEXT(usr); - serv_got_update(gc, usr, 0, 0, 0, 0, 0); - } else if (!g_ascii_strncasecmp(buf, "GTC", 3)) { - } else if (!g_ascii_strncasecmp(buf, "INF", 3)) { - } else if (!g_ascii_strncasecmp(buf, "ILN", 3)) { - char *state, *user, *friend, *tmp = buf; - int status = 0; - - GET_NEXT(tmp); - - GET_NEXT(tmp); - state = tmp; - - GET_NEXT(tmp); - user = tmp; - - GET_NEXT(tmp); - friend = url_decode(tmp); - - serv_got_alias(gc, user, friend); +static char *msn_normalize(const char *str); - if (!g_ascii_strcasecmp(state, "BSY")) { - status |= UC_UNAVAILABLE | (MSN_BUSY << 1); - } else if (!g_ascii_strcasecmp(state, "IDL")) { - status |= UC_UNAVAILABLE | (MSN_IDLE << 1); - } else if (!g_ascii_strcasecmp(state, "BRB")) { - status |= UC_UNAVAILABLE | (MSN_BRB << 1); - } else if (!g_ascii_strcasecmp(state, "AWY")) { - status |= UC_UNAVAILABLE | (MSN_AWAY << 1); - } else if (!g_ascii_strcasecmp(state, "PHN")) { - status |= UC_UNAVAILABLE | (MSN_PHONE << 1); - } else if (!g_ascii_strcasecmp(state, "LUN")) { - status |= UC_UNAVAILABLE | (MSN_LUNCH << 1); - } - - serv_got_update(gc, user, 1, 0, 0, 0, status); - } else if (!g_ascii_strncasecmp(buf, "LST", 3)) { - char *which, *who, *friend, *tmp = buf; - struct msn_add_permit *ap; /* for any as yet undealt with buddies who've added you to their buddy list when you were off-line. How dare they! */ - GSList *perm = gc->account->permit; /* current permit list */ - GSList *denyl = gc->account->deny; - char msg[MSN_BUF_LEN]; - int new = 1; - int pos, tot; - - GET_NEXT(tmp); - GET_NEXT(tmp); - which = tmp; - - GET_NEXT(tmp); - GET_NEXT(tmp); - pos = strtol(tmp, NULL, 10); - GET_NEXT(tmp); - tot = strtol(tmp, NULL, 10); - GET_NEXT(tmp); - who = tmp; - - GET_NEXT(tmp); - friend = url_decode(tmp); - - if (!g_ascii_strcasecmp(which, "FL") && pos) { - struct msn_buddy *b = g_new0(struct msn_buddy, 1); - b->user = g_strdup(who); - b->friend = g_strdup(friend); - md->fl = g_slist_append(md->fl, b); - } else if (!g_ascii_strcasecmp(which, "AL") && pos) { - if (g_slist_find_custom(gc->account->deny, who, - (GCompareFunc)strcmp)) { - gaim_debug(GAIM_DEBUG_INFO, "msn", - "moving from deny to permit: %s", who); - gaim_privacy_deny_remove(gc->account, who); - } - gaim_privacy_permit_add(gc->account, who); - } else if (!g_ascii_strcasecmp(which, "BL") && pos) { - gaim_privacy_deny_add(gc->account, who); - } else if (!g_ascii_strcasecmp(which, "RL")) { - if (pos) { - while(perm) { - if(!g_ascii_strcasecmp(perm->data, who)) - new = 0; - perm = perm->next; - } - while(denyl) { - if(!g_ascii_strcasecmp(denyl->data, who)) - new = 0; - denyl = denyl->next; - } - if(new) { - gaim_debug(GAIM_DEBUG_INFO, "msn", - "Unresolved MSN RL entry\n"); - ap = g_new0(struct msn_add_permit, 1); - ap->user = g_strdup(who); - ap->friend = g_strdup(friend); - ap->gc = gc; - - g_snprintf(msg, sizeof(msg), _("The user %s (%s) wants to add you to their buddy list"),ap->user, ap->friend); - do_ask_dialog(msg, NULL, ap, _("Authorize"), msn_accept_add, _("Deny"), msn_cancel_add, my_protocol->handle, FALSE); - } - } - - if (pos != tot) - return 1; /* this isn't the last one in the RL, so return. */ - - g_snprintf(sendbuf, sizeof(sendbuf), "CHG %u NLN\r\n", ++md->trId); - if (msn_write(md->fd, sendbuf, strlen(sendbuf)) < 0) { - hide_login_progress(gc, _("Unable to write")); - signoff(gc); - return 0; - } - - account_online(gc); - serv_finish_login(gc); - - md->permit = g_slist_copy(gc->account->permit); - md->deny = g_slist_copy(gc->account->deny); - - while (md->fl) { - struct msn_buddy *mb = md->fl->data; - struct buddy *b = gaim_find_buddy(gc->account, mb->user); - md->fl = g_slist_remove(md->fl, mb); - if(!b) { - struct group *g; - printf("I'm adding %s now..\n", mb->user); - if (!(g = gaim_find_group(_("Buddies")))) { - printf("How could I not exitst!?!\n"); - g = gaim_group_new(_("Buddies")); - gaim_blist_add_group(g, NULL); - } - b = gaim_buddy_new(gc->account, mb->user, NULL); - gaim_blist_add_buddy(b,g,NULL); - } - serv_got_alias(gc, mb->user, mb->friend); - g_free(mb->user); - g_free(mb->friend); - g_free(mb); - } - } - } else if (!g_ascii_strncasecmp(buf, "MSG", 3)) { - char *user, *tmp = buf; - int length; - - GET_NEXT(tmp); - user = tmp; - - GET_NEXT(tmp); - - GET_NEXT(tmp); - length = atoi(tmp); - - md->msg = TRUE; - md->msguser = g_strdup(user); - md->msglen = length; - } else if (!g_ascii_strncasecmp(buf, "NLN", 3)) { - char *state, *user, *friend, *tmp = buf; - int status = 0; - - GET_NEXT(tmp); - state = tmp; - - GET_NEXT(tmp); - user = tmp; - - GET_NEXT(tmp); - friend = url_decode(tmp); - - serv_got_alias(gc, user, friend); - - if (!g_ascii_strcasecmp(state, "BSY")) { - status |= UC_UNAVAILABLE | (MSN_BUSY << 1); - } else if (!g_ascii_strcasecmp(state, "IDL")) { - status |= UC_UNAVAILABLE | (MSN_IDLE << 1); - } else if (!g_ascii_strcasecmp(state, "BRB")) { - status |= UC_UNAVAILABLE | (MSN_BRB << 1); - } else if (!g_ascii_strcasecmp(state, "AWY")) { - status |= UC_UNAVAILABLE | (MSN_AWAY << 1); - } else if (!g_ascii_strcasecmp(state, "PHN")) { - status |= UC_UNAVAILABLE | (MSN_PHONE << 1); - } else if (!g_ascii_strcasecmp(state, "LUN")) { - status |= UC_UNAVAILABLE | (MSN_LUNCH << 1); - } - - serv_got_update(gc, user, 1, 0, 0, 0, status); - } else if (!g_ascii_strncasecmp(buf, "OUT", 3)) { - char *tmp = buf; - - GET_NEXT(tmp); - if (!g_ascii_strncasecmp(tmp, "OTH", 3)) { - hide_login_progress(gc, _("You have been disconnected. You have " - "signed on from another location.")); - signoff(gc); - return 0; - } - } else if (!g_ascii_strncasecmp(buf, "PRP", 3)) { - } else if (!g_ascii_strncasecmp(buf, "QNG", 3)) { - } else if (!g_ascii_strncasecmp(buf, "QRY", 3)) { - } else if (!g_ascii_strncasecmp(buf, "REA", 3)) { - char *friend, *tmp = buf; - - GET_NEXT(tmp); - GET_NEXT(tmp); - GET_NEXT(tmp); - GET_NEXT(tmp); - - friend = url_decode(tmp); - - g_snprintf(gc->displayname, sizeof(gc->displayname), "%s", friend); - } else if (!g_ascii_strncasecmp(buf, "REM", 3)) { - } else if (!g_ascii_strncasecmp(buf, "RNG", 3)) { - struct msn_switchboard *ms; - char *sessid, *ssaddr, *auth, *user; - int port, i = 0; - char *tmp = buf; - - GET_NEXT(tmp); - sessid = tmp; - - GET_NEXT(tmp); - ssaddr = tmp; - - GET_NEXT(tmp); - - GET_NEXT(tmp); - auth = tmp; - - GET_NEXT(tmp); - user = tmp; - GET_NEXT(tmp); +static void +msn_act_id(gpointer data, char *entry) +{ + struct gaim_connection *gc = data; + MsnSession *session = gc->proto_data; + char outparams[MSN_BUF_LEN]; + char *alias; - while (ssaddr[i] && ssaddr[i] != ':') i++; - if (ssaddr[i] == ':') { - char *x = &ssaddr[i + 1]; - ssaddr[i] = 0; - port = atoi(x); - } else - port = 1863; - - ms = g_new0(struct msn_switchboard, 1); - if (proxy_connect(gc->account, ssaddr, port, msn_rng_connect, ms) != 0) { - g_free(ms); - return 1; - } - ms->user = g_strdup(user); - ms->sessid = g_strdup(sessid); - ms->auth = g_strdup(auth); - ms->gc = gc; - } else if (!g_ascii_strncasecmp(buf, "URL", 3)) { - char *tmp = buf; - FILE *fd; - md5_state_t st; - md5_byte_t di[16]; - int i; - char buf2[64]; - char sendbuf[64]; - char hippy[2048]; - char *rru; - char *passport; - - GET_NEXT(tmp); - GET_NEXT(tmp); - rru = tmp; - GET_NEXT(tmp); - passport = tmp; - - g_snprintf(hippy, sizeof(hippy), "%s%lu%s", md->mspauth, time(NULL) - md->sl, gc->password); - - md5_init(&st); - md5_append(&st, (const md5_byte_t *)hippy, strlen(hippy)); - md5_finish(&st, di); - - bzero(sendbuf, sizeof(sendbuf)); - for (i = 0; i < 16; i++) { - g_snprintf(buf2, sizeof(buf2), "%02x", di[i]); - strcat(sendbuf, buf2); - } - - if (md->passport) { - unlink(md->passport); - g_free(md->passport); - } + if (entry == NULL || *entry == '\0') + alias = g_strdup(""); + else + alias = g_strdup(entry); - if( (fd = gaim_mkstemp(&(md->passport))) == NULL ) { - gaim_debug(GAIM_DEBUG_ERROR, "msn", - "Error opening temp file: %s\n", strerror(errno)); - } - else { - fputs("\n" - "\n" - "\n" - "\n\n", fd); - - fprintf(fd, "\n"); - fprintf(fd, "
\n\n", passport); - fprintf(fd, "\n"); - fprintf(fd, "\n", gc->username); - fprintf(fd, "\n", gc->username); - fprintf(fd, "\n", md->sid); - fprintf(fd, "\n", md->kv); - fprintf(fd, "\n"); - fprintf(fd, "\n", time(NULL) - md->sl); - fprintf(fd, "\n", rru); - fprintf(fd, "\n", md->mspauth); - fprintf(fd, "\n", sendbuf); // Digest me - fprintf(fd, "\n"); - fprintf(fd, "\n"); - fprintf(fd, "
\n"); - fprintf(fd, "\n"); - if (fclose(fd)) { - gaim_debug(GAIM_DEBUG_ERROR, "msn", - "Error closing temp file: %s\n", strerror(errno)); - unlink(md->passport); - g_free(md->passport); - } - else { - /* - * Renaming file with .html extension, so that - * win32 open_url will work. - */ - char *tmp; - if ((tmp = g_strdup_printf("%s.html", md->passport)) != NULL) { - if (rename(md->passport, tmp) == 0) { - g_free(md->passport); - md->passport = tmp; - } else - g_free(tmp); - } - } - } - } else if (!g_ascii_strncasecmp(buf, "SYN", 3)) { - } else if (!g_ascii_strncasecmp(buf, "USR", 3)) { - } else if (!g_ascii_strncasecmp(buf, "XFR", 3)) { - char *host = strstr(buf, "SB"); - int port; - int i = 0; - gboolean switchboard = TRUE; - char *tmp; - - if (!host) { - host = strstr(buf, "NS"); - if (!host) { - hide_login_progress(gc, _("Got invalid XFR\n")); - signoff(gc); - return 0; - } - switchboard = FALSE; - } - - GET_NEXT(host); - while (host[i] && host[i] != ':') i++; - if (host[i] == ':') { - tmp = &host[i + 1]; - host[i] = 0; - while (isdigit(*tmp)) tmp++; - *tmp++ = 0; - port = atoi(&host[i + 1]); - } else { - port = 1863; - tmp = host; - GET_NEXT(tmp); - } - - if (switchboard) { - struct msn_switchboard *ms; - - GET_NEXT(tmp); - - ms = msn_switchboard_connect(gc, host, port); - - if (ms == NULL) - return 1; - - ms->auth = g_strdup(tmp); - } else { - close(md->fd); - gaim_input_remove(md->inpa); - md->inpa = 0; - if (proxy_connect(gc->account, host, port, msn_login_xfr_connect, gc) != 0) { - hide_login_progress(gc, _("Error transferring")); - signoff(gc); - return 0; - } - } - } else if (isdigit(*buf)) { - handle_errcode(buf, TRUE); - } else { - gaim_debug(GAIM_DEBUG_WARNING, "msn", "Unhandled message!\n"); - } - - return 1; -} - -static void msn_process_main_msg(struct gaim_connection *gc, char *msg) -{ - struct msn_data *md = gc->proto_data; - char *skiphead; - char *content; - - content = strstr(msg, "Content-Type: "); - - if ((content) && (!g_ascii_strncasecmp(content, "Content-Type: text/x-msmsgsprofile", - strlen("Content-Type: text/x-msmsgsprofile")))) { - - char *kv,*sid,*mspauth; - - kv = strstr(msg, "kv: "); - sid = strstr(msg, "sid: "); - mspauth = strstr(msg, "MSPAuth: "); - - if (kv) { - char *tmp; - - kv += strlen("kv: "); - tmp = strstr(kv, "\r\n"); *tmp = 0; - md->kv = g_strdup(kv); - } - - if (sid) { - char *tmp; - - sid += strlen("sid: "); - tmp = strstr(sid, "\r\n"); *tmp = 0; - md->sid = g_strdup(sid); - } - - if (mspauth) { - char *tmp; - - mspauth += strlen("MSPAuth: "); - tmp = strstr(mspauth, "\r\n"); *tmp = 0; - md->mspauth = g_strdup(mspauth); - } - - } - - - - if (!g_ascii_strcasecmp(md->msguser, "hotmail")) { - handle_hotmail(gc, msg); + if (strlen(alias) >= BUDDY_ALIAS_MAXLEN) { + do_error_dialog(_("Your new MSN friendly name is too long."), + NULL, GAIM_ERROR); return; } - - skiphead = strstr(msg, "\r\n\r\n"); - if (!skiphead || !skiphead[4]) - return; - skiphead += 4; - strip_linefeed(skiphead); - - serv_got_im(gc, md->msguser, skiphead, 0, time(NULL), -1); -} - -static void msn_callback(gpointer data, gint source, GaimInputCondition cond) -{ - struct gaim_connection *gc = data; - struct msn_data *md = gc->proto_data; - char buf[MSN_BUF_LEN]; - int cont = 1; - int len; - - len = read(md->fd, buf, sizeof(buf)); - if (len <= 0) { - hide_login_progress_error(gc, _("Error reading from server")); - signoff(gc); - return; - } - - md->rxqueue = g_realloc(md->rxqueue, len + md->rxlen); - memcpy(md->rxqueue + md->rxlen, buf, len); - md->rxlen += len; - - while (cont) { - if (!md->rxlen) - return; - - if (md->msg) { - char *msg; - if (md->msglen > md->rxlen) - return; - msg = md->rxqueue; - md->rxlen -= md->msglen; - if (md->rxlen) { - md->rxqueue = g_memdup(msg + md->msglen, md->rxlen); - } else { - md->rxqueue = NULL; - msg = g_realloc(msg, md->msglen + 1); - } - msg[md->msglen] = 0; - md->msglen = 0; - md->msg = FALSE; - - msn_process_main_msg(gc, msg); - - g_free(md->msguser); - g_free(msg); - } else { - char *end = md->rxqueue; - int cmdlen; - char *cmd; - int i = 0; - - while (i + 1 < md->rxlen) { - if (*end == '\r' && end[1] == '\n') - break; - end++; i++; - } - if (i + 1 == md->rxlen) - return; - - cmdlen = end - md->rxqueue + 2; - cmd = md->rxqueue; - md->rxlen -= cmdlen; - if (md->rxlen) { - md->rxqueue = g_memdup(cmd + cmdlen, md->rxlen); - } else { - md->rxqueue = NULL; - cmd = g_realloc(cmd, cmdlen + 1); - } - cmd[cmdlen] = 0; - - gaim_debug(GAIM_DEBUG_MISC, "msn", "S: %s", cmd); - g_strchomp(cmd); - cont = msn_process_main(gc, cmd); - - g_free(cmd); - } - } -} + g_snprintf(outparams, sizeof(outparams), "%s %s", + gc->username, msn_url_encode(alias)); -static void msn_login_xfr_connect(gpointer data, gint source, GaimInputCondition cond) -{ - struct gaim_connection *gc = data; - struct msn_data *md; - char buf[MSN_BUF_LEN]; - - if (!g_slist_find(connections, gc)) { - close(source); - return; - } - - md = gc->proto_data; - - if (md->fd != source) - md->fd = source; - - if (md->fd == -1) { - hide_login_progress(gc, _("Unable to connect to Notification Server")); - signoff(gc); - return; - } - - g_snprintf(buf, sizeof(buf), "VER %u MSNP5\r\n", ++md->trId); - if (msn_write(md->fd, buf, strlen(buf)) < 0) { - hide_login_progress(gc, _("Unable to talk to Notification Server")); - signoff(gc); - return; - } - - md->inpa = gaim_input_add(md->fd, GAIM_INPUT_READ, msn_login_callback, gc); -} - -static int msn_process_login(struct gaim_connection *gc, char *buf) -{ - struct msn_data *md = gc->proto_data; - char sendbuf[MSN_BUF_LEN]; - - if (!g_ascii_strncasecmp(buf, "VER", 3)) { - /* we got VER, check to see that MSNP5 is in the list, then send INF */ - if (!strstr(buf, "MSNP5")) { - hide_login_progress(gc, _("Protocol not supported")); - signoff(gc); - return 0; - } - - g_snprintf(sendbuf, sizeof(sendbuf), "INF %u\r\n", ++md->trId); - if (msn_write(md->fd, sendbuf, strlen(sendbuf)) < 0) { - hide_login_progress(gc, _("Unable to request INF\n")); - signoff(gc); - return 0; - } - } else if (!g_ascii_strncasecmp(buf, "INF", 3)) { - /* check to make sure we can use md5 */ - if (!strstr(buf, "MD5")) { - hide_login_progress(gc, _("Unable to login using MD5")); - signoff(gc); - return 0; - } - - g_snprintf(sendbuf, sizeof(sendbuf), "USR %u MD5 I %s\r\n", ++md->trId, gc->username); - if (msn_write(md->fd, sendbuf, strlen(sendbuf)) < 0) { - hide_login_progress(gc, _("Unable to send USR\n")); - signoff(gc); - return 0; - } - - set_login_progress(gc, 3, _("Requesting to send password")); - } else if (!g_ascii_strncasecmp(buf, "USR", 3)) { - char *resp, *friend, *tmp = buf; - - GET_NEXT(tmp); - GET_NEXT(tmp); - resp = tmp; - GET_NEXT(tmp); - GET_NEXT(tmp); - friend = url_decode(tmp); - GET_NEXT(tmp); - - /* so here, we're either getting the challenge or the OK */ - if (!g_ascii_strcasecmp(resp, "OK")) { - g_snprintf(gc->displayname, sizeof(gc->displayname), "%s", friend); - - g_snprintf(sendbuf, sizeof(sendbuf), "SYN %u 0\r\n", ++md->trId); - if (msn_write(md->fd, sendbuf, strlen(sendbuf)) < 0) { - hide_login_progress(gc, _("Unable to write")); - signoff(gc); - return 0; - } + g_free(alias); - gaim_input_remove(md->inpa); - md->inpa = gaim_input_add(md->fd, GAIM_INPUT_READ, msn_callback, gc); - return 0; - } else if (!g_ascii_strcasecmp(resp, "MD5")) { - char buf2[MSN_BUF_LEN]; - md5_state_t st; - md5_byte_t di[16]; - int i; - - g_snprintf(buf2, sizeof(buf2), "%s%s", friend, gc->password); - - md5_init(&st); - md5_append(&st, (const md5_byte_t *)buf2, strlen(buf2)); - md5_finish(&st, di); - - g_snprintf(sendbuf, sizeof(sendbuf), "USR %u MD5 S ", ++md->trId); - for (i = 0; i < 16; i++) { - g_snprintf(buf2, sizeof(buf2), "%02x", di[i]); - strcat(sendbuf, buf2); - } - strcat(sendbuf, "\r\n"); - - if (msn_write(md->fd, sendbuf, strlen(sendbuf)) < 0) { - hide_login_progress(gc, _("Unable to send password")); - signoff(gc); - return 0; - } - - set_login_progress(gc, 4, _("Password sent")); - } - } else if (!g_ascii_strncasecmp(buf, "XFR", 3)) { - char *host = strstr(buf, "NS"); - int port; - int i = 0; - - if (!host) { - hide_login_progress(gc, _("Got invalid XFR\n")); - signoff(gc); - return 0; - } - - GET_NEXT(host); - while (host[i] && host[i] != ':') i++; - if (host[i] == ':') { - char *x = &host[i + 1]; - host[i] = 0; - port = atoi(x); - } else - port = 1863; - - close(md->fd); - gaim_input_remove(md->inpa); - md->inpa = 0; - md->fd = 0; - md->sl = time(NULL); - if (proxy_connect(gc->account, host, port, msn_login_xfr_connect, gc) != 0) { - hide_login_progress(gc, _("Unable to transfer")); - signoff(gc); - } - return 0; - } else { - if (isdigit(*buf)) - hide_login_progress(gc, handle_errcode(buf, FALSE)); - else - hide_login_progress(gc, _("Unable to parse message")); - signoff(gc); - return 0; - } - - return 1; -} - -static void msn_login_callback(gpointer data, gint source, GaimInputCondition cond) -{ - struct gaim_connection *gc = data; - struct msn_data *md = gc->proto_data; - char buf[MSN_BUF_LEN]; - int cont = 1; - int len; - - len = read(md->fd, buf, sizeof(buf)); - if (len <= 0) { - hide_login_progress(gc, _("Error reading from server")); - signoff(gc); - return; - } + if (!msn_servconn_send_command(session->notification_conn, + "REA", outparams)) { - md->rxqueue = g_realloc(md->rxqueue, len + md->rxlen); - memcpy(md->rxqueue + md->rxlen, buf, len); - md->rxlen += len; - - while (cont) { - char *end = md->rxqueue; - int cmdlen; - char *cmd; - int i = 0; - - if (!md->rxlen) - return; - - while (i + 1 < md->rxlen) { - if (*end == '\r' && end[1] == '\n') - break; - end++; i++; - } - if (i + 1 == md->rxlen) - return; - - cmdlen = end - md->rxqueue + 2; - cmd = md->rxqueue; - md->rxlen -= cmdlen; - if (md->rxlen) { - md->rxqueue = g_memdup(cmd + cmdlen, md->rxlen); - } else { - md->rxqueue = NULL; - cmd = g_realloc(cmd, cmdlen + 1); - } - cmd[cmdlen] = 0; - - gaim_debug(GAIM_DEBUG_MISC, "msn", "S: %s", cmd); - g_strchomp(cmd); - cont = msn_process_login(gc, cmd); - - g_free(cmd); - } -} - -static void msn_login_connect(gpointer data, gint source, GaimInputCondition cond) -{ - struct gaim_connection *gc = data; - struct msn_data *md; - char buf[1024]; - - if (!g_slist_find(connections, gc)) { - close(source); - return; - } - - md = gc->proto_data; - - if (md->fd != source) - md->fd = source; - - if (md->fd == -1) { - hide_login_progress(gc, _("Unable to connect")); - signoff(gc); - return; - } - - g_snprintf(buf, sizeof(buf), "VER %u MSNP5\r\n", ++md->trId); - if (msn_write(md->fd, buf, strlen(buf)) < 0) { - hide_login_progress(gc, _("Unable to write to server")); - signoff(gc); - return; - } - - md->inpa = gaim_input_add(md->fd, GAIM_INPUT_READ, msn_login_callback, gc); - set_login_progress(gc, 2,_("Synching with server")); -} - -static void msn_login(struct gaim_account *account) -{ - struct gaim_connection *gc = new_gaim_conn(account); - gc->proto_data = g_new0(struct msn_data, 1); - - set_login_progress(gc, 1, _("Connecting")); - - g_snprintf(gc->username, sizeof(gc->username), "%s", msn_normalize(gc->username)); - - if (proxy_connect(account, account->proto_opt[USEROPT_MSNSERVER][0] ? - account->proto_opt[USEROPT_MSNSERVER] : MSN_SERVER, - account->proto_opt[USEROPT_MSNPORT][0] ? - atoi(account->proto_opt[USEROPT_MSNPORT]) : MSN_PORT, - msn_login_connect, gc) != 0) { - hide_login_progress(gc, _("Unable to connect")); + hide_login_progress(gc, _("Write error")); signoff(gc); } } -static void msn_close(struct gaim_connection *gc) +static void +msn_show_set_friendly_name(struct gaim_connection *gc) { - struct msn_data *md = gc->proto_data; - close(md->fd); - if (md->inpa) - gaim_input_remove(md->inpa); - g_free(md->rxqueue); - if (md->msg) - g_free(md->msguser); - if (md->passport) { - unlink(md->passport); - g_free(md->passport); - } - while (md->switches) - msn_kill_switch(md->switches->data); - while (md->fl) { - struct msn_buddy *tmp = md->fl->data; - md->fl = g_slist_remove(md->fl, tmp); - g_free(tmp->user); - g_free(tmp->friend); - g_free(tmp); - } - g_slist_free(md->permit); - g_slist_free(md->deny); - g_free(md); + do_prompt_dialog(_("Set Friendly Name:"), gc->displayname, + gc, msn_act_id, NULL); } -static int msn_send_typing(struct gaim_connection *gc, char *who, int typing) { - struct msn_switchboard *ms = msn_find_switch(gc, who); - char header[MSN_BUF_LEN] = "MIME-Version: 1.0\r\n" - "Content-Type: text/x-msmsgscontrol\r\n" - "TypingUser: "; - char buf [MSN_BUF_LEN]; - if (!ms || !typing) - return 0; - g_snprintf(buf, sizeof(buf), "MSG %u N %d\r\n%s%s\r\n\r\n\r\n", - ++ms->trId, - strlen(header) + strlen("\r\n\r\n\r\n") + strlen(gc->username), - header, gc->username); - if (msn_write(ms->fd, buf, strlen(buf)) < 0) - msn_kill_switch(ms); - return MSN_TYPING_SEND_TIMEOUT; + +/************************************************************************** + * Protocol Plugin ops + **************************************************************************/ + +static const char * +msn_list_icon(struct gaim_account *a, struct buddy *b) +{ + return "msn"; } -static int msn_send_im(struct gaim_connection *gc, const char *who, const char *message, int len, int flags) +static void +msn_list_emblems(struct buddy *b, char **se, char **sw, + char **nw, char **ne) { - struct msn_data *md = gc->proto_data; - struct msn_switchboard *ms = msn_find_switch(gc, who); - char buf[MSN_BUF_LEN]; - - if (ms) { - char *send; - - if (ms->txqueue) { - gaim_debug(GAIM_DEBUG_INFO, "msn", "appending to queue\n"); - ms->txqueue = g_slist_append(ms->txqueue, g_strdup(message)); - return 1; - } - - send = add_cr(message); - g_snprintf(buf, sizeof(buf), "MSG %u N %d\r\n%s%s", ++ms->trId, - strlen(MIME_HEADER) + strlen(send), - MIME_HEADER, send); - g_free(send); - if (msn_write(ms->fd, buf, strlen(buf)) < 0) - msn_kill_switch(ms); - } else if (strcmp(who, gc->username)) { - g_snprintf(buf, MSN_BUF_LEN, "XFR %u SB\r\n", ++md->trId); - if (msn_write(md->fd, buf, strlen(buf)) < 0) { - hide_login_progress(gc, _("Write error")); - signoff(gc); - return 1; - } - - ms = g_new0(struct msn_switchboard, 1); - md->switches = g_slist_append(md->switches, ms); - ms->user = g_strdup(who); - ms->txqueue = g_slist_append(ms->txqueue, g_strdup(message)); - ms->gc = gc; - ms->fd = -1; - } else - /* in msn you can't send messages to yourself, so we'll fake like we received it ;) */ - serv_got_im(gc, who, message, flags | IM_FLAG_GAIMUSER, time(NULL), -1); - return 1; + if (b->present == GAIM_BUDDY_OFFLINE) + *se = "offline"; + else if ((b->uc >> 1) == 2 || (b->uc >> 1) == 6) + *se = "occupied"; + else if (b->uc) + *se = "away"; } -static int msn_chat_send(struct gaim_connection *gc, int id, char *message) +static char * +msn_status_text(struct buddy *b) { - struct msn_switchboard *ms = msn_find_switch_by_id(gc, id); - char buf[MSN_BUF_LEN]; - char *send; - - if (!ms) - return -EINVAL; + if (b->uc & UC_UNAVAILABLE) + return g_strdup(msn_away_get_text(b->uc >> 1)); - send = add_cr(message); - g_snprintf(buf, sizeof(buf), "MSG %u N %d\r\n%s%s", ++ms->trId, - strlen(MIME_HEADER) + strlen(send), - MIME_HEADER, send); - g_free(send); - if (msn_write(ms->fd, buf, strlen(buf)) < 0) { - msn_kill_switch(ms); - return 0; - } - serv_got_chat_in(gc, id, gc->username, 0, message, time(NULL)); - return 0; + return NULL; } -static void msn_chat_invite(struct gaim_connection *gc, int id, const char *msg, const char *who) +static char * +msn_tooltip_text(struct buddy *b) { - struct msn_switchboard *ms = msn_find_switch_by_id(gc, id); - char buf[MSN_BUF_LEN]; + if (GAIM_BUDDY_IS_ONLINE(b)) { + return g_strdup_printf(_("Status: %s"), + msn_away_get_text(b->uc >> 1)); + } - if (!ms) - return; - - g_snprintf(buf, sizeof(buf), "CAL %u %s\r\n", ++ms->trId, who); - if (msn_write(ms->fd, buf, strlen(buf)) < 0) - msn_kill_switch(ms); + return NULL; } -static void msn_chat_leave(struct gaim_connection *gc, int id) -{ - struct msn_switchboard *ms = msn_find_switch_by_id(gc, id); - char buf[MSN_BUF_LEN]; - - if (!ms) - return; - - g_snprintf(buf, sizeof(buf), "OUT\r\n"); - if (msn_write(ms->fd, buf, strlen(buf)) < 0) - msn_kill_switch(ms); -} - -static GList *msn_away_states(struct gaim_connection *gc) +static GList * +msn_away_states(struct gaim_connection *gc) { GList *m = NULL; @@ -1361,21 +132,185 @@ return m; } -static void msn_set_away(struct gaim_connection *gc, char *state, char *msg) +static GList * +msn_actions(struct gaim_connection *gc) +{ + GList *m = NULL; + struct proto_actions_menu *pam; + + pam = g_new0(struct proto_actions_menu, 1); + pam->label = _("Set Friendly Name"); + pam->callback = msn_show_set_friendly_name; + pam->gc = gc; + m = g_list_append(m, pam); + + return m; +} + +static GList * +msn_buddy_menu(struct gaim_connection *gc, const char *who) +{ + GList *m = NULL; + + return m; +} + +static void +msn_login(struct gaim_account *account) +{ + struct gaim_connection *gc; + MsnSession *session; + const char *server; + int port; + + server = (*account->proto_opt[USEROPT_MSNSERVER] + ? account->proto_opt[USEROPT_MSNSERVER] + : MSN_SERVER); + port = (*account->proto_opt[USEROPT_MSNPORT] + ? atoi(account->proto_opt[USEROPT_MSNPORT]) + : MSN_PORT); + + + gc = new_gaim_conn(account); + + session = msn_session_new(account, server, port); + session->prpl = my_protocol; + + gc->proto_data = session; + + set_login_progress(gc, 1, _("Connecting")); + + g_snprintf(gc->username, sizeof(gc->username), "%s", + msn_normalize(gc->username)); + + if (!msn_session_connect(session)) { + hide_login_progress(gc, _("Unable to connect")); + signoff(gc); + } +} + +static void +msn_close(struct gaim_connection *gc) +{ + MsnSession *session = gc->proto_data; + + msn_session_destroy(session); + + gc->proto_data = NULL; +} + +static int +msn_send_im(struct gaim_connection *gc, const char *who, const char *message, + int len, int flags) { - struct msn_data *md = gc->proto_data; - char buf[MSN_BUF_LEN]; + MsnSession *session = gc->proto_data; + MsnSwitchBoard *swboard; + + swboard = msn_session_find_switch_with_passport(session, who); + + if (g_ascii_strcasecmp(who, gc->username)) { + MsnMessage *msg; + MsnUser *user; + + user = msn_user_new(session, who, NULL); + + msg = msn_message_new(); + msn_message_set_receiver(msg, user); + msn_message_set_attr(msg, "X-MMS-IM-Format", + "FN=Arial; EF=; CO=0; PF=0"); + msn_message_set_body(msg, message); + + if (swboard != NULL) { + if (!msn_switchboard_send_msg(swboard, msg)) + msn_switchboard_destroy(swboard); + } + else { + if ((swboard = msn_session_open_switchboard(session)) == NULL) { + msn_message_destroy(msg); + + hide_login_progress(gc, _("Write error")); + signoff(gc); + + return 1; + } + + msn_switchboard_set_user(swboard, user); + msn_switchboard_send_msg(swboard, msg); + } + + msn_user_destroy(user); + msn_message_destroy(msg); + } + else { + /* + * In MSN, you can't send messages to yourself, so + * we'll fake like we received it ;) + */ + serv_got_typing_stopped(gc, (char *)who); + serv_got_im(gc, who, message, flags | IM_FLAG_GAIMUSER, + time(NULL), -1); + } + + return 1; +} + +static int +msn_send_typing(struct gaim_connection *gc, char *who, int typing) +{ + MsnSession *session = gc->proto_data; + MsnSwitchBoard *swboard; + MsnMessage *msg; + MsnUser *user; + + if (!typing) + return 0; + + if (!g_ascii_strcasecmp(who, gc->username)) { + /* We'll just fake it, since we're sending to ourself. */ + serv_got_typing(gc, who, MSN_TYPING_RECV_TIMEOUT, TYPING); + + return MSN_TYPING_SEND_TIMEOUT; + } + + swboard = msn_session_find_switch_with_passport(session, who); + + if (swboard == NULL) + return 0; + + user = msn_user_new(session, who, NULL); + + msg = msn_message_new(); + msn_message_set_content_type(msg, "text/x-msmsgscontrol"); + msn_message_set_receiver(msg, user); + msn_message_set_charset(msg, NULL); + msn_message_set_attr(msg, "TypingUser", gc->username); + msn_message_set_attr(msg, "User-Agent", NULL); + msn_message_set_body(msg, "\r\n"); + + if (!msn_switchboard_send_msg(swboard, msg)) + msn_switchboard_destroy(swboard); + + msn_user_destroy(user); + + return MSN_TYPING_SEND_TIMEOUT; +} + +static void +msn_set_away(struct gaim_connection *gc, char *state, char *msg) +{ + MsnSession *session = gc->proto_data; const char *away; - if (gc->away) { + if (gc->away != NULL) { g_free(gc->away); gc->away = NULL; } - if (msg) { + if (msg != NULL) { gc->away = g_strdup(""); away = "AWY"; - } else if (state) { + } + else if (state) { gc->away = g_strdup(""); if (!strcmp(state, _("Away From Computer"))) @@ -1395,415 +330,460 @@ gc->away = NULL; away = "NLN"; } - } else if (gc->is_idle) + } + else if (gc->is_idle) away = "IDL"; else away = "NLN"; - g_snprintf(buf, sizeof(buf), "CHG %u %s\r\n", ++md->trId, away); - if (msn_write(md->fd, buf, strlen(buf)) < 0) { + if (!msn_servconn_send_command(session->notification_conn, "CHG", away)) { + hide_login_progress(gc, _("Write error")); + signoff(gc); + } +} + +static void +msn_set_idle(struct gaim_connection *gc, int idle) +{ + MsnSession *session = gc->proto_data; + + if (gc->away != NULL) + return; + + if (!msn_servconn_send_command(session->notification_conn, "CHG", + (idle ? "IDL" : "NLN"))) { + hide_login_progress(gc, _("Write error")); signoff(gc); + } +} + +static void +msn_add_buddy(struct gaim_connection *gc, const char *name) +{ + MsnSession *session = gc->proto_data; + char *who; + char outparams[MSN_BUF_LEN]; + GSList *l; + + who = msn_normalize(name); + + if (strchr(who, ' ')) { + /* This is a broken blist entry. */ return; } + + for (l = session->lists.forward; l != NULL; l = l->next) { + MsnUser *user = l->data; + + if (!gaim_utf8_strcasecmp(who, msn_user_get_passport(user))) + break; + } + + if (l != NULL) + return; + + g_snprintf(outparams, sizeof(outparams), + "FL %s %s", who, who); + + if (!msn_servconn_send_command(session->notification_conn, + "ADD", outparams)) { + hide_login_progress(gc, _("Write error")); + signoff(gc); + } +} + +static void +msn_rem_buddy(struct gaim_connection *gc, char *who, char *group) +{ + MsnSession *session = gc->proto_data; + char outparams[MSN_BUF_LEN]; + + g_snprintf(outparams, sizeof(outparams), "FL %s", who); + + if (!msn_servconn_send_command(session->notification_conn, + "REM", outparams)) { + + hide_login_progress(gc, _("Write error")); + signoff(gc); + } } -static void msn_set_idle(struct gaim_connection *gc, int idle) +static void +msn_add_permit(struct gaim_connection *gc, const char *who) { - struct msn_data *md = gc->proto_data; - char buf[64]; + MsnSession *session = gc->proto_data; + char buf[MSN_BUF_LEN]; + + if (!strchr(who, '@')) { + g_snprintf(buf, sizeof(buf), + _("An MSN screenname must be in the form \"user@server.com\". " + "Perhaps you meant %s@hotmail.com. No changes were made " + "to your allow list."), who); + + do_error_dialog(_("Invalid MSN screenname"), buf, GAIM_ERROR); + gaim_privacy_permit_remove(gc->account, who); + + return; + } + + if (g_slist_find_custom(gc->account->deny, who, (GCompareFunc)strcmp)) { + gaim_debug(GAIM_DEBUG_INFO, "msn", "Moving %s from BL to AL\n", who); + gaim_privacy_deny_remove(gc->account, who); + + g_snprintf(buf, sizeof(buf), "BL %s", who); + + if (!msn_servconn_send_command(session->notification_conn, + "REM", buf)) { + + hide_login_progress(gc, _("Write error")); + signoff(gc); + return; + } + } + + g_snprintf(buf, sizeof(buf), "AL %s %s", who, who); - if (gc->away) + if (!msn_servconn_send_command(session->notification_conn, "ADD", buf)) { + hide_login_progress(gc, _("Write error")); + signoff(gc); + } +} + +static void +msn_add_deny(struct gaim_connection *gc, const char *who) +{ + MsnSession *session = gc->proto_data; + char buf[MSN_BUF_LEN]; + + if (!strchr(who, '@')) { + g_snprintf(buf, sizeof(buf), + _("An MSN screenname must be in the form \"user@server.com\". " + "Perhaps you meant %s@hotmail.com. No changes were made " + "to your block list."), who); + + do_error_dialog(_("Invalid MSN screenname"), buf, GAIM_ERROR); + + gaim_privacy_deny_remove(gc->account, who); + return; - g_snprintf(buf, sizeof(buf), - idle ? "CHG %d IDL\r\n" : "CHG %u NLN\r\n", ++md->trId); - if (msn_write(md->fd, buf, strlen(buf)) < 0) { + } + + if (g_slist_find_custom(gc->account->permit, who, (GCompareFunc)strcmp)) { + gaim_debug(GAIM_DEBUG_INFO, "msn", "Moving %s from AL to BL\n", who); + gaim_privacy_permit_remove(gc->account, who); + + g_snprintf(buf, sizeof(buf), "AL %s", who); + + if (!msn_servconn_send_command(session->notification_conn, + "REM", buf)) { + + hide_login_progress(gc, _("Write error")); + signoff(gc); + return; + } + } + + g_snprintf(buf, sizeof(buf), "BL %s %s", who, who); + + if (!msn_servconn_send_command(session->notification_conn, "ADD", buf)) { hide_login_progress(gc, _("Write error")); signoff(gc); return; } } -static const char *msn_list_icon(struct gaim_account *a, struct buddy *b) -{ - return "msn"; -} - -static void msn_list_emblems(struct buddy *b, char **se, char **sw, char **nw, char **ne) -{ - if (b->present == GAIM_BUDDY_OFFLINE) - *se = "offline"; - else if (b->uc >> 1 == 2 || b->uc >> 1 == 6) - *se = "occupied"; - else if (b->uc) - *se = "away"; -} - -static char *msn_get_away_text(int s) +static void +msn_rem_permit(struct gaim_connection *gc, const char *who) { - switch (s) { - case MSN_BUSY : - return _("Busy"); - case MSN_BRB : - return _("Be Right Back"); - case MSN_AWAY : - return _("Away From Computer"); - case MSN_PHONE : - return _("On The Phone"); - case MSN_LUNCH : - return _("Out To Lunch"); - case MSN_IDLE : - return _("Idle"); - default: - return _("Available"); - } -} + MsnSession *session = gc->proto_data; + char buf[MSN_BUF_LEN]; -static char *msn_status_text(struct buddy *b) { - if (b->uc & UC_UNAVAILABLE) - return g_strdup(msn_get_away_text(b->uc >> 1)); - return NULL; -} + g_snprintf(buf, sizeof(buf), "AL %s", who); -static char *msn_tooltip_text(struct buddy *b) { - if (GAIM_BUDDY_IS_ONLINE(b)) - return g_strdup_printf(_("Status: %s"), msn_get_away_text(b->uc >> 1)); - - return NULL; -} - -static GList *msn_buddy_menu(struct gaim_connection *gc, const char *who) -{ - GList *m = NULL; - - return m; -} + if (!msn_servconn_send_command(session->notification_conn, "REM", buf)) { + hide_login_progress(gc, _("Write error")); + signoff(gc); + return; + } -static void msn_add_buddy(struct gaim_connection *gc, const char *name) -{ - struct msn_data *md = gc->proto_data; - char *who = msn_normalize(name); - char buf[MSN_BUF_LEN]; - GSList *l = md->fl; - - if (who[0] == '@') - /* how did this happen? */ - return; + gaim_privacy_deny_add(gc->account, who); - if (strchr(who, ' ')) - /* This is a broken blist entry. */ - return; + g_snprintf(buf, sizeof(buf), "BL %s %s", who, who); - while (l) { - struct msn_buddy *b = l->data; - if (!gaim_utf8_strcasecmp(who, b->user)) - break; - l = l->next; - } - if (l) - return; - - g_snprintf(buf, sizeof(buf), "ADD %u FL %s %s\r\n", ++md->trId, who, who); - if (msn_write(md->fd, buf, strlen(buf)) < 0) { + if (!msn_servconn_send_command(session->notification_conn, "ADD", buf)) { hide_login_progress(gc, _("Write error")); signoff(gc); return; } } -static void msn_rem_buddy(struct gaim_connection *gc, char *who, char *group) +static void +msn_rem_deny(struct gaim_connection *gc, const char *who) { - struct msn_data *md = gc->proto_data; + MsnSession *session = gc->proto_data; char buf[MSN_BUF_LEN]; - g_snprintf(buf, sizeof(buf), "REM %u FL %s\r\n", ++md->trId, who); - if (msn_write(md->fd, buf, strlen(buf)) < 0) { - hide_login_progress(gc, _("Write error")); - signoff(gc); - return; - } -} + g_snprintf(buf, sizeof(buf), "BL %s", who); -static void msn_act_id(gpointer data, char *entry) -{ - struct gaim_connection *gc = data; - struct msn_data *md = gc->proto_data; - char buf[MSN_BUF_LEN]; - char *alias; - - if (!entry || *entry == '\0') - alias = g_strdup(""); - else - alias = g_strdup(entry); - - if (strlen(alias) >= BUDDY_ALIAS_MAXLEN) { - do_error_dialog(_("New MSN friendly name too long."), NULL, GAIM_ERROR); - return; - } - - g_snprintf(buf, sizeof(buf), "REA %u %s %s\r\n", ++md->trId, gc->username, url_encode(alias)); - g_free(alias); - if (msn_write(md->fd, buf, strlen(buf)) < 0) { + if (!msn_servconn_send_command(session->notification_conn, "REM", buf)) { hide_login_progress(gc, _("Write error")); signoff(gc); return; } -} -static void msn_show_set_friendly_name(struct gaim_connection *gc) -{ - do_prompt_dialog(_("Set Friendly Name:"), gc->displayname, gc, msn_act_id, NULL); -} - -static GList *msn_actions(struct gaim_connection *gc) -{ - GList *m = NULL; - struct proto_actions_menu *pam; - - pam = g_new0(struct proto_actions_menu, 1); - pam->label = _("Set Friendly Name"); - pam->callback = msn_show_set_friendly_name; - pam->gc = gc; - m = g_list_append(m, pam); - - return m; -} + gaim_privacy_permit_add(gc->account, who); -static void msn_convo_closed(struct gaim_connection *gc, char *who) -{ - struct msn_switchboard *ms = msn_find_switch(gc, who); - - if (ms) { - char sendbuf[256]; - - g_snprintf(sendbuf, sizeof(sendbuf), "BYE %s\r\n", gc->username); - - msn_write(ms->fd, sendbuf, strlen(sendbuf)); + g_snprintf(buf, sizeof(buf), "AL %s %s", who, who); - msn_kill_switch(ms); - } -} - -static void msn_keepalive(struct gaim_connection *gc) -{ - struct msn_data *md = gc->proto_data; - char buf[MSN_BUF_LEN]; - - g_snprintf(buf, sizeof(buf), "PNG\r\n"); - if (msn_write(md->fd, buf, strlen(buf)) < 0) { + if (!msn_servconn_send_command(session->notification_conn, "ADD", buf)) { hide_login_progress(gc, _("Write error")); signoff(gc); return; } } -static void msn_set_permit_deny(struct gaim_connection *gc) +static void +msn_set_permit_deny(struct gaim_connection *gc) { - struct msn_data *md = gc->proto_data; + MsnSession *session = gc->proto_data; char buf[MSN_BUF_LEN]; GSList *s, *t = NULL; - if (gc->account->permdeny == PERMIT_ALL || gc->account->permdeny == DENY_SOME) - g_snprintf(buf, sizeof(buf), "BLP %u AL\r\n", ++md->trId); + if (gc->account->permdeny == PERMIT_ALL || + gc->account->permdeny == DENY_SOME) { + + strcpy(buf, "AL"); + } else - g_snprintf(buf, sizeof(buf), "BLP %u BL\r\n", ++md->trId); + strcpy(buf, "BL"); - if (msn_write(md->fd, buf, strlen(buf)) < 0) { + if (!msn_servconn_send_command(session->notification_conn, "BLP", buf)) { hide_login_progress(gc, _("Write error")); signoff(gc); return; } - /* this is safe because we'll always come here after we've gotten the list off the server, - * and data is never removed. So if the lengths are equal we don't know about anyone locally - * and so there's no sense in going through them all. */ - if (g_slist_length(gc->account->permit) == g_slist_length(md->permit)) { - g_slist_free(md->permit); - md->permit = NULL; + /* + * This is safe because we'll always come here after we've gotten + * the list off the server, and data is never removed. So if the + * lengths are equal we don't know about anyone locally and so + * there's no sense in going through them all. + */ + if (g_slist_length(gc->account->permit) == + g_slist_length(session->lists.allow)) { + + g_slist_free(session->lists.allow); + session->lists.allow = NULL; } - if (g_slist_length(gc->account->deny) == g_slist_length(md->deny)) { - g_slist_free(md->deny); - md->deny = NULL; + + if (g_slist_length(gc->account->deny) == + g_slist_length(session->lists.block)) { + + g_slist_free(session->lists.block); + session->lists.block = NULL; } - if (!md->permit && !md->deny) + + if (session->lists.allow == NULL && session->lists.block == NULL) return; - if (md->permit) { - s = g_slist_nth(gc->account->permit, g_slist_length(md->permit)); - while (s) { + if (session->lists.allow != NULL) { + + for (s = g_slist_nth(gc->account->permit, + g_slist_length(session->lists.allow)); + s != NULL; + s = s->next) { + char *who = s->data; - s = s->next; + if (!strchr(who, '@')) { t = g_slist_append(t, who); continue; } - if (g_slist_find(md->deny, who)) { + + if (g_slist_find(session->lists.block, who)) { t = g_slist_append(t, who); continue; } - g_snprintf(buf, sizeof(buf), "ADD %u AL %s %s\r\n", ++md->trId, who, who); - if (msn_write(md->fd, buf, strlen(buf)) < 0) { + + g_snprintf(buf, sizeof(buf), "AL %s %s", who, who); + + if (!msn_servconn_send_command(session->notification_conn, + "ADD", buf)) { hide_login_progress(gc, _("Write error")); signoff(gc); return; } } - while (t) { + + for (; t != NULL; t = t->next) gaim_privacy_permit_remove(gc->account, t->data); - t = t->next; - } - if (t) + + if (t != NULL) g_slist_free(t); - t = NULL; - g_slist_free(md->permit); - md->permit = NULL; + + t = NULL; + g_slist_free(session->lists.allow); + session->lists.allow = NULL; } - - if (md->deny) { - s = g_slist_nth(gc->account->deny, g_slist_length(md->deny)); - while (s) { + + if (session->lists.block) { + for (s = g_slist_nth(gc->account->deny, + g_slist_length(session->lists.block)); + s != NULL; + s = s->next) { + char *who = s->data; - s = s->next; + if (!strchr(who, '@')) { t = g_slist_append(t, who); continue; } - if (g_slist_find(md->deny, who)) { + + if (g_slist_find(session->lists.block, who)) { t = g_slist_append(t, who); continue; } - g_snprintf(buf, sizeof(buf), "ADD %u BL %s %s\r\n", ++md->trId, who, who); - if (msn_write(md->fd, buf, strlen(buf)) < 0) { + + g_snprintf(buf, sizeof(buf), "BL %s %s", who, who); + + if (!msn_servconn_send_command(session->notification_conn, + "ADD", buf)) { hide_login_progress(gc, _("Write error")); signoff(gc); return; } } - while (t) { + + for (; t != NULL; t = t->next) gaim_privacy_deny_remove(gc->account, t->data); - t = t->next; - } - if (t) + + if (t != NULL) g_slist_free(t); - g_slist_free(md->deny); - md->deny = NULL; + + g_slist_free(session->lists.block); + session->lists.block = NULL; } } -static void msn_add_permit(struct gaim_connection *gc, const char *who) +static void +msn_chat_invite(struct gaim_connection *gc, int id, const char *msg, + const char *who) +{ + MsnSession *session = gc->proto_data; + MsnSwitchBoard *swboard = msn_session_find_switch_with_id(session, id); + + if (swboard == NULL) + return; + + if (!msn_switchboard_send_command(swboard, "CAL", who)) + msn_switchboard_destroy(swboard); +} + +static void +msn_chat_leave(struct gaim_connection *gc, int id) +{ + MsnSession *session = gc->proto_data; + MsnSwitchBoard *swboard = msn_session_find_switch_with_id(session, id); + char buf[6]; + + if (swboard == NULL) + return; + + strcpy(buf, "OUT\r\n"); + + if (!msn_servconn_write(swboard->servconn, buf, strlen(buf))) + msn_switchboard_destroy(swboard); +} + +static int +msn_chat_send(struct gaim_connection *gc, int id, char *message) { - struct msn_data *md = gc->proto_data; + MsnSession *session = gc->proto_data; + MsnSwitchBoard *swboard = msn_session_find_switch_with_id(session, id); + MsnMessage *msg; + char *send; + + if (swboard == NULL) + return -EINVAL; + + send = add_cr(message); + + msg = msn_message_new(); + msn_message_set_attr(msg, "X-MMS-IM-Format", "FN=Arial; EF=; CO=0; PF=0"); + msn_message_set_body(msg, send); + + g_free(send); + + if (!msn_switchboard_send_msg(swboard, msg)) { + msn_switchboard_destroy(swboard); + + msn_message_destroy(msg); + + return 0; + } + + msn_message_destroy(msg); + + serv_got_chat_in(gc, id, gc->username, 0, message, time(NULL)); + + return 0; +} + +static void +msn_keepalive(struct gaim_connection *gc) +{ + MsnSession *session = gc->proto_data; char buf[MSN_BUF_LEN]; - if (!strchr(who, '@')) { - g_snprintf(buf, sizeof(buf), - _("An MSN screenname must be in the form \"user@server.com\". " - "Perhaps you meant %s@hotmail.com. No changes were made to your " - "allow list."), who); - do_error_dialog(_("Invalid MSN screenname"), buf, GAIM_ERROR); - gaim_privacy_permit_remove(gc->account, who); - return; - } + g_snprintf(buf, sizeof(buf), "PNG\r\n"); - if (g_slist_find_custom(gc->account->deny, who, (GCompareFunc)strcmp)) { - gaim_debug(GAIM_DEBUG_INFO, "msn", "Moving %s from BL to AL\n", who); - gaim_privacy_deny_remove(gc->account, who); - g_snprintf(buf, sizeof(buf), "REM %u BL %s\r\n", ++md->trId, who); - if (msn_write(md->fd, buf, strlen(buf)) < 0) { - hide_login_progress(gc, _("Write error")); - signoff(gc); - return; - } - } - g_snprintf(buf, sizeof(buf), "ADD %u AL %s %s\r\n", ++md->trId, who, who); - if (msn_write(md->fd, buf, strlen(buf)) < 0) { + if (msn_servconn_write(session->notification_conn, + buf, strlen(buf)) < 0) { + hide_login_progress(gc, _("Write error")); signoff(gc); return; } } -static void msn_rem_permit(struct gaim_connection *gc, const char *who) +static void +msn_buddy_free(struct buddy *b) { - struct msn_data *md = gc->proto_data; - char buf[MSN_BUF_LEN]; + if (b->proto_data != NULL) + g_free(b->proto_data); +} - g_snprintf(buf, sizeof(buf), "REM %u AL %s\r\n", ++md->trId, who); - if (msn_write(md->fd, buf, strlen(buf)) < 0) { - hide_login_progress(gc, _("Write error")); - signoff(gc); - return; - } +static void +msn_convo_closed(struct gaim_connection *gc, char *who) +{ + MsnSession *session = gc->proto_data; + MsnSwitchBoard *swboard; + + swboard = msn_session_find_switch_with_passport(session, who); - gaim_privacy_deny_add(gc->account, who); - g_snprintf(buf, sizeof(buf), "ADD %u BL %s %s\r\n", ++md->trId, who, who); - if (msn_write(md->fd, buf, strlen(buf)) < 0) { - hide_login_progress(gc, _("Write error")); - signoff(gc); - return; + if (swboard != NULL) { + char sendbuf[256]; + + g_snprintf(sendbuf, sizeof(sendbuf), "BYE %s\r\n", gc->username); + + msn_servconn_write(swboard->servconn, sendbuf, strlen(sendbuf)); + + msn_switchboard_destroy(swboard); } } -static void msn_add_deny(struct gaim_connection *gc, const char *who) +static char * +msn_normalize(const char *str) { - struct msn_data *md = gc->proto_data; - char buf[MSN_BUF_LEN]; - - if (!strchr(who, '@')) { - g_snprintf(buf, sizeof(buf), - _("An MSN screenname must be in the form \"user@server.com\". " - "Perhaps you meant %s@hotmail.com. No changes were made to your " - "block list."), who); - do_error_dialog(_("Invalid MSN screenname"), buf, GAIM_ERROR); - gaim_privacy_deny_remove(gc->account, who); - return; - } - - if (g_slist_find_custom(gc->account->permit, who, (GCompareFunc)strcmp)) { - gaim_debug(GAIM_DEBUG_INFO, "msn", "Moving %s from AL to BL\n", who); - gaim_privacy_permit_remove(gc->account, who); - g_snprintf(buf, sizeof(buf), "REM %u AL %s\r\n", ++md->trId, who); - if (msn_write(md->fd, buf, strlen(buf)) < 0) { - hide_login_progress(gc, _("Write error")); - signoff(gc); - return; - } - } - + static char buf[BUF_LEN]; - g_snprintf(buf, sizeof(buf), "ADD %u BL %s %s\r\n", ++md->trId, who, who); - if (msn_write(md->fd, buf, strlen(buf)) < 0) { - hide_login_progress(gc, _("Write error")); - signoff(gc); - return; - } -} - -static void msn_rem_deny(struct gaim_connection *gc, const char *who) -{ - struct msn_data *md = gc->proto_data; - char buf[MSN_BUF_LEN]; + g_return_val_if_fail(str != NULL, NULL); - g_snprintf(buf, sizeof(buf), "REM %u BL %s\r\n", ++md->trId, who); - if (msn_write(md->fd, buf, strlen(buf)) < 0) { - hide_login_progress(gc, _("Write error")); - signoff(gc); - return; - } + g_snprintf(buf, sizeof(buf), "%s%s", str, + (strchr(str, '@') ? "" : "@hotmail.com")); - gaim_privacy_permit_add(gc->account, who); - g_snprintf(buf, sizeof(buf), "ADD %u AL %s %s\r\n", ++md->trId, who, who); - if (msn_write(md->fd, buf, strlen(buf)) < 0) { - hide_login_progress(gc, _("Write error")); - signoff(gc); - return; - } -} - -static void msn_buddy_free(struct buddy *b) -{ - if (b->proto_data) - g_free(b->proto_data); + return buf; } static GaimPluginProtocolInfo prpl_info = diff -r 6aa785e55d0f -r e2e53316a21d src/protocols/msn/msn.h --- a/src/protocols/msn/msn.h Tue May 06 00:34:54 2003 +0000 +++ b/src/protocols/msn/msn.h Tue May 06 02:06:56 2003 +0000 @@ -3,7 +3,7 @@ * * gaim * - * Copyright (C) 2003, Christian Hammond + * Copyright (C) 2003 Christian Hammond * * 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 @@ -18,7 +18,6 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ #ifndef _MSN_H_ #define _MSN_H_ @@ -42,39 +41,19 @@ #ifndef _WIN32 #include #endif -#include "gaim.h" + +#include "blist.h" +#include "debug.h" +#include "md5.h" +#include "proxy.h" #include "prpl.h" -#include "proxy.h" -#include "md5.h" #ifdef _WIN32 #include "win32dep.h" #include "stdint.h" #endif -#include "msg.h" -#include "switchboard.h" - #define MSN_BUF_LEN 8192 -#define MIME_HEADER "MIME-Version: 1.0\r\n" \ - "Content-Type: text/plain; charset=UTF-8\r\n" \ - "User-Agent: Gaim/" VERSION "\r\n" \ - "X-MMS-IM-Format: FN=Arial; EF=; CO=0; PF=0\r\n\r\n" - -#define HOTMAIL_URL "http://www.hotmail.com/cgi-bin/folders" -#define PASSPORT_URL "http://lc1.law13.hotmail.passport.com/cgi-bin/dologin?login=" - -#define MSN_ONLINE 1 -#define MSN_BUSY 2 -#define MSN_IDLE 3 -#define MSN_BRB 4 -#define MSN_AWAY 5 -#define MSN_PHONE 6 -#define MSN_LUNCH 7 -#define MSN_OFFLINE 8 -#define MSN_HIDDEN 9 - -#define USEROPT_HOTMAIL 0 #define USEROPT_MSNSERVER 3 #define MSN_SERVER "messenger.hotmail.com" @@ -84,70 +63,12 @@ #define MSN_TYPING_RECV_TIMEOUT 6 #define MSN_TYPING_SEND_TIMEOUT 4 -#define MSN_FT_GUID "{5D3E02AB-6190-11d3-BBBB-00C04F795683}" +#define HOTMAIL_URL "http://www.hotmail.com/cgi-bin/folders" +#define PASSPORT_URL "http://lc1.law13.hotmail.passport.com/cgi-bin/dologin?login=" -#define GET_NEXT(tmp) \ - while (*(tmp) && *(tmp) != ' ' && *(tmp) != '\r') \ - (tmp)++; \ - *(tmp)++ = 0; \ - while (*(tmp) && *(tmp) == ' ') \ - (tmp)++; +#define USEROPT_HOTMAIL 0 -struct msn_xfer_data { - int inpa; - - guint32 cookie; - guint32 authcookie; - - gboolean transferring; - gboolean do_cancel; - - char *rxqueue; - int rxlen; - gboolean msg; - char *msguser; - int msglen; -}; - -struct msn_data { - int fd; - guint32 trId; - int inpa; - - char *rxqueue; - int rxlen; - gboolean msg; - char *msguser; - int msglen; - - GSList *switches; - GSList *fl; - GSList *permit; - GSList *deny; - GSList *file_transfers; - - char *kv; - char *sid; - char *mspauth; - unsigned long sl; - char *passport; -}; - -struct msn_buddy { - char *user; - char *friend; -}; - -/** - * Processes a file transfer message. - * - * @param ms The switchboard. - * @param msg The message. - */ -void msn_process_ft_msg(struct msn_switchboard *ms, char *msg); - -char *handle_errcode(char *buf, gboolean show); -char *url_decode(const char *msg); +#define MSN_FT_GUID "{5D3E02AB-6190-11d3-BBBB-00C04F795683}" #endif /* _MSN_H_ */ diff -r 6aa785e55d0f -r e2e53316a21d src/protocols/msn/notification.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/msn/notification.c Tue May 06 02:06:56 2003 +0000 @@ -0,0 +1,1061 @@ +/** + * @file notification.c Notification server functions + * + * gaim + * + * Copyright (C) 2003 Christian Hammond + * + * 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 + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "msn.h" +#include "notification.h" +#include "away.h" +#include "error.h" +#include "utils.h" + +typedef struct +{ + struct gaim_connection *gc; + MsnUser *user; + +} MsnPermitAdd; + +static GHashTable *notification_commands = NULL; +static GHashTable *notification_msg_types = NULL; + +/************************************************************************** + * Callbacks + **************************************************************************/ +static void +msn_accept_add_cb(MsnPermitAdd *pa) +{ + if (g_slist_find(connections, pa->gc) != NULL) { + MsnSession *session = pa->gc->proto_data; + char outparams[MSN_BUF_LEN]; + + g_snprintf(outparams, sizeof(outparams), "AL %s %s", + msn_user_get_passport(pa->user), + msn_url_encode(msn_user_get_name(pa->user))); + + if (msn_servconn_send_command(session->notification_conn, + "ADD", outparams) <= 0) { + hide_login_progress(pa->gc, _("Write error")); + signoff(pa->gc); + return; + } + + gaim_privacy_permit_add(pa->gc->account, + msn_user_get_passport(pa->user)); + build_allow_list(); + + show_got_added(pa->gc, NULL, msn_user_get_passport(pa->user), + msn_user_get_name(pa->user), NULL); + } + + msn_user_destroy(pa->user); + g_free(pa); +} + +static void +msn_cancel_add_cb(MsnPermitAdd *pa) +{ + if (g_slist_find(connections, pa->gc) != NULL) { + MsnSession *session = pa->gc->proto_data; + char outparams[MSN_BUF_LEN]; + + g_snprintf(outparams, sizeof(outparams), "BL %s %s", + msn_user_get_passport(pa->user), + msn_url_encode(msn_user_get_name(pa->user))); + + if (msn_servconn_send_command(session->notification_conn, + "ADD", outparams) <= 0) { + hide_login_progress(pa->gc, _("Write error")); + signoff(pa->gc); + return; + } + + gaim_privacy_deny_add(pa->gc->account, + msn_user_get_passport(pa->user)); + build_block_list(); + } + + msn_user_destroy(pa->user); + g_free(pa); +} + +/************************************************************************** + * Catch-all commands + **************************************************************************/ +static gboolean +__blank_cmd(MsnServConn *servconn, const char *command, const char **params, + size_t param_count) +{ + return TRUE; +} + +static gboolean +__unknown_cmd(MsnServConn *servconn, const char *command, const char **params, + size_t param_count) +{ + struct gaim_connection *gc = servconn->session->account->gc; + + if (isdigit(*command)) { + hide_login_progress(gc, (char *)msn_error_get_text(atoi(command))); + } + else + hide_login_progress(gc, _("Unable to parse message.")); + + signoff(gc); + + return FALSE; +} + + +/************************************************************************** + * Login + **************************************************************************/ +static gboolean +__ver_cmd(MsnServConn *servconn, const char *command, const char **params, + size_t param_count) +{ + struct gaim_connection *gc = servconn->session->account->gc; + size_t i; + gboolean msnp5_found = FALSE; + + for (i = 1; i < param_count; i++) { + if (!strcmp(params[i], "MSNP5")) { + msnp5_found = TRUE; + break; + } + } + + if (!msnp5_found) { + hide_login_progress(gc, _("Protocol not supported")); + signoff(gc); + + return FALSE; + } + + if (!msn_servconn_send_command(servconn, "INF", NULL)) { + hide_login_progress(gc, _("Unable to request INF")); + signoff(gc); + + return FALSE; + } + + return TRUE; +} + +static gboolean +__inf_cmd(MsnServConn *servconn, const char *command, const char **params, + size_t param_count) +{ + struct gaim_connection *gc = servconn->session->account->gc; + char outparams[MSN_BUF_LEN]; + + if (strcmp(params[1], "MD5")) { + hide_login_progress(gc, _("Unable to login using MD5")); + signoff(gc); + + return FALSE; + } + + g_snprintf(outparams, sizeof(outparams), "MD5 I %s", gc->username); + + if (!msn_servconn_send_command(servconn, "USR", outparams)) { + hide_login_progress(gc, _("Unable to send USR")); + signoff(gc); + + return FALSE; + } + + set_login_progress(gc, 3, _("Requesting to send password")); + + return TRUE; +} + +static gboolean +__usr_cmd(MsnServConn *servconn, const char *command, const char **params, + size_t param_count) +{ + struct gaim_connection *gc = servconn->session->account->gc; + char outparams[MSN_BUF_LEN]; + + /* We're either getting the challenge or the OK. Let's find out. */ + if (!g_ascii_strcasecmp(params[1], "OK")) { + /* OK */ + + if (!msn_servconn_send_command(servconn, "SYN", "0")) { + hide_login_progress(gc, _("Unable to write")); + signoff(gc); + + return FALSE; + } + } + else { + /* Challenge */ + const char *challenge = params[3]; + char buf[MSN_BUF_LEN]; + md5_state_t st; + md5_byte_t di[16]; + int i; + + g_snprintf(buf, sizeof(buf), "%s%s", challenge, gc->password); + + md5_init(&st); + md5_append(&st, (const md5_byte_t *)buf, strlen(buf)); + md5_finish(&st, di); + + g_snprintf(outparams, sizeof(outparams), "MD5 S "); + + for (i = 0; i < 16; i++) { + g_snprintf(buf, sizeof(buf), "%02x", di[i]); + strcat(outparams, buf); + } + + if (!msn_servconn_send_command(servconn, "USR", outparams)) { + hide_login_progress(gc, _("Unable to send password")); + signoff(gc); + + return FALSE; + } + + set_login_progress(gc, 4, _("Password sent")); + } + + return TRUE; +} + +/************************************************************************** + * Log out + **************************************************************************/ +static gboolean +__out_cmd(MsnServConn *servconn, const char *command, const char **params, + size_t param_count) +{ + struct gaim_connection *gc = servconn->session->account->gc; + + if (!g_ascii_strcasecmp(params[0], "OTH")) { + hide_login_progress(gc, + _("You have been disconnected. You have " + "signed on from another location.")); + signoff(gc); + } + else if (!g_ascii_strcasecmp(params[0], "SSD")) { + hide_login_progress(gc, + _("You have been disconnected. The MSN servers " + "are going down temporarily.")); + signoff(gc); + } + + return FALSE; +} + +/************************************************************************** + * Messages + **************************************************************************/ +static gboolean +__msg_cmd(MsnServConn *servconn, const char *command, const char **params, + size_t param_count) +{ + gaim_debug(GAIM_DEBUG_INFO, "msn", "Found message. Parsing.\n"); + + servconn->parsing_msg = TRUE; + servconn->msg_passport = g_strdup(params[0]); + servconn->msg_friendly = g_strdup(params[1]); + servconn->msg_len = atoi(params[2]); + + return TRUE; +} + +/************************************************************************** + * Challenges + **************************************************************************/ +static gboolean +__chl_cmd(MsnServConn *servconn, const char *command, const char **params, + size_t param_count) +{ + struct gaim_connection *gc = servconn->session->account->gc; + char buf[MSN_BUF_LEN]; + char buf2[3]; + md5_state_t st; + md5_byte_t di[16]; + int i; + + md5_init(&st); + md5_append(&st, (const md5_byte_t *)params[1], strlen(params[1])); + md5_append(&st, (const md5_byte_t *)"Q1P7W2E4J9R8U3S5", + strlen("Q1P7W2E4J9R8U3S5")); + md5_finish(&st, di); + + g_snprintf(buf, sizeof(buf), + "QRY %u msmsgs@msnmsgr.com 32\r\n", + servconn->session->trId++); + + for (i = 0; i < 16; i++) { + g_snprintf(buf2, sizeof(buf2), "%02x", di[i]); + strcat(buf, buf2); + } + + if (msn_servconn_write(servconn, buf, strlen(buf)) <= 0) { + hide_login_progress(gc, _("Unable to write to server")); + signoff(gc); + } + + return TRUE; +} + +/************************************************************************** + * Buddy Lists + **************************************************************************/ +static gboolean +__add_cmd(MsnServConn *servconn, const char *command, const char **params, + size_t param_count) +{ + MsnSession *session = servconn->session; + struct gaim_connection *gc = session->account->gc; + MsnPermitAdd *pa; + GSList *sl; + const char *list, *passport; + char *friend; + char msg[MSN_BUF_LEN]; + + list = params[1]; + passport = params[3]; + + friend = msn_url_decode(params[4]); + + if (g_ascii_strcasecmp(list, "RL")) + return TRUE; + + for (sl = gc->account->permit; sl != NULL; sl = sl->next) { + if (!gaim_utf8_strcasecmp(sl->data, passport)) + return TRUE; + } + + pa = g_new0(MsnPermitAdd, 1); + pa->user = msn_user_new(session, passport, friend); + pa->gc = gc; + + g_snprintf(msg, sizeof(msg), + _("The user %s (%s) wants to add %s to his or her buddy list."), + passport, friend, gc->username); + + do_ask_dialog(msg, NULL, pa, + _("Authorize"), msn_accept_add_cb, + _("Deny"), msn_cancel_add_cb, + session->prpl->handle, FALSE); + + return TRUE; +} + +static gboolean +__blp_cmd(MsnServConn *servconn, const char *command, const char **params, + size_t param_count) +{ + struct gaim_connection *gc = servconn->session->account->gc; + + if (!g_ascii_strcasecmp(params[2], "AL")) { + /* + * If the current setting is AL, messages from users who + * are not in BL will be delivered. + * + * In other words, deny some. + */ + gc->account->permdeny = DENY_SOME; + } + else { + /* If the current setting is BL, only messages from people + * who are in the AL will be delivered. + * + * In other words, permit some. + */ + gc->account->permdeny = PERMIT_SOME; + } + + return TRUE; +} + +static gboolean +__fln_cmd(MsnServConn *servconn, const char *command, const char **params, + size_t param_count) +{ + struct gaim_connection *gc = servconn->session->account->gc; + + serv_got_update(gc, (char *)params[0], 0, 0, 0, 0, 0); + + return TRUE; +} + +static gboolean +__iln_cmd(MsnServConn *servconn, const char *command, const char **params, + size_t param_count) +{ + struct gaim_connection *gc = servconn->session->account->gc; + int status = 0; + const char *state, *passport, *friend; + + state = params[1]; + passport = params[2]; + friend = msn_url_decode(params[3]); + + serv_got_alias(gc, (char *)passport, (char *)friend); + + if (!g_ascii_strcasecmp(state, "BSY")) + status |= UC_UNAVAILABLE | (MSN_BUSY << 1); + else if (!g_ascii_strcasecmp(state, "IDL")) + status |= UC_UNAVAILABLE | (MSN_IDLE << 1); + else if (!g_ascii_strcasecmp(state, "BRB")) + status |= UC_UNAVAILABLE | (MSN_BRB << 1); + else if (!g_ascii_strcasecmp(state, "AWY")) + status |= UC_UNAVAILABLE | (MSN_AWAY << 1); + else if (!g_ascii_strcasecmp(state, "PHN")) + status |= UC_UNAVAILABLE | (MSN_PHONE << 1); + else if (!g_ascii_strcasecmp(state, "LUN")) + status |= UC_UNAVAILABLE | (MSN_LUNCH << 1); + + serv_got_update(gc, (char *)passport, 1, 0, 0, 0, status); + + return TRUE; +} + +static gboolean +__lsg_cmd(MsnServConn *servconn, const char *command, const char **params, + size_t param_count) +{ + MsnSession *session = servconn->session; + struct group *g; + const char *name; + int group_num, num_groups, group_id; + + group_num = atoi(params[2]); + num_groups = atoi(params[3]); + group_id = atoi(params[4]); + name = msn_url_decode(params[5]); + + if (group_num == 1) { + session->groups = g_hash_table_new_full(g_int_hash, g_int_equal, + NULL, g_free); + } + + g_hash_table_insert(session->groups, GINT_TO_POINTER(group_id), + g_strdup(name)); + + if ((g = gaim_find_group(name)) == NULL) { + g = gaim_group_new(name); + gaim_blist_add_group(g, NULL); + } + + return TRUE; +} + +static gboolean +__lst_cmd(MsnServConn *servconn, const char *command, const char **params, + size_t param_count) +{ + MsnSession *session = servconn->session; + struct gaim_connection *gc = session->account->gc; + int user_num; + int num_users; + const char *type; + const char *passport; + const char *friend; + + user_num = atoi(params[3]); + num_users = atoi(params[4]); + + if (user_num == 0 && num_users == 0) + return TRUE; /* There are no users on this list. */ + + type = params[1]; + passport = params[5]; + friend = msn_url_decode(params[6]); + + if (!g_ascii_strcasecmp(type, "FL") && user_num != 0) { + /* These are users on our contact list. */ + MsnUser *user; + + user = msn_user_new(session, passport, friend); + + if (param_count == 8) + msn_user_set_group_id(user, atoi(params[7])); + + session->lists.forward = g_slist_append(session->lists.forward, user); + } + else if (!g_ascii_strcasecmp(type, "AL") && user_num != 0) { + /* These are users who are allowed to see our status. */ + if (g_slist_find_custom(gc->account->deny, passport, + (GCompareFunc)strcmp)) { + + gaim_debug(GAIM_DEBUG_INFO, "msn", + "Moving user from deny list to permit: %s (%s)\n", + passport, friend); + + gaim_privacy_deny_remove(gc->account, passport); + } + + gaim_privacy_permit_add(gc->account, passport); + } + else if (!g_ascii_strcasecmp(type, "BL") && user_num != 0) { + /* These are users who are not allowed to see our status. */ + gaim_privacy_deny_add(gc->account, passport); + } + else if (!g_ascii_strcasecmp(type, "RL")) { + /* These are users who have us on their contact list. */ + if (user_num > 0) { + gboolean new_entry = TRUE; + + if (g_slist_find_custom(gc->account->permit, passport, + (GCompareFunc)g_ascii_strcasecmp)) { + new_entry = FALSE; + } + + if (g_slist_find_custom(gc->account->deny, passport, + (GCompareFunc)g_ascii_strcasecmp)) { + new_entry = FALSE; + } + + if (new_entry) { + MsnPermitAdd *pa; + char msg[MSN_BUF_LEN]; + + gaim_debug(GAIM_DEBUG_WARNING, "msn", + "Unresolved MSN RL entry: %s\n", passport); + + pa = g_new0(MsnPermitAdd, 1); + pa->user = msn_user_new(session, passport, friend); + pa->gc = gc; + + g_snprintf(msg, sizeof(msg), + _("The user %s (%s) wants to add you to their " + "buddy list."), + msn_user_get_passport(pa->user), + msn_user_get_name(pa->user)); + + do_ask_dialog(msg, NULL, pa, + _("Authorize"), msn_accept_add_cb, + _("Deny"), msn_cancel_add_cb, + session->prpl->handle, FALSE); + } + } + + if (user_num != num_users) + return TRUE; /* This isn't the last one in the RL. */ + + if (!msn_servconn_send_command(servconn, "CHG", "NLN")) { + hide_login_progress(gc, _("Unable to write")); + signoff(gc); + + return FALSE; + } + + account_online(gc); + serv_finish_login(gc); + + session->lists.allow = g_slist_copy(gc->account->permit); + session->lists.block = g_slist_copy(gc->account->deny); + + while (session->lists.forward != NULL) { + MsnUser *user = session->lists.forward->data; + struct buddy *b; + + b = gaim_find_buddy(gc->account, msn_user_get_passport(user)); + + session->lists.forward = g_slist_remove(session->lists.forward, + user); + + if (b == NULL) { + struct group *g = NULL; + const char *group_name = NULL; + int group_id; + + group_id = msn_user_get_group_id(user); + + if (group_id > -1) { + group_name = g_hash_table_lookup(session->groups, + GINT_TO_POINTER(group_id)); + } + + if (group_name == NULL) { + gaim_debug(GAIM_DEBUG_WARNING, "msn", + "Group ID %d for user %s was not defined.\n", + group_id, passport); + } + else if ((g = gaim_find_group(group_name)) == NULL) { + gaim_debug(GAIM_DEBUG_ERROR, "msn", + "Group '%s' appears in server-side " + "buddy list, but not here!", + group_name); + } + + if (g == NULL) { + if ((g = gaim_find_group(_("Buddies"))) == NULL) { + g = gaim_group_new(_("Buddies")); + gaim_blist_add_group(g, NULL); + } + } + + b = gaim_buddy_new(gc->account, + msn_user_get_passport(user), NULL); + + gaim_blist_add_buddy(b, g, NULL); + } + + serv_got_alias(gc, (char *)msn_user_get_passport(user), + (char *)msn_user_get_name(user)); + + msn_user_destroy(user); + } + } + + return TRUE; +} + +static gboolean +__nln_cmd(MsnServConn *servconn, const char *command, const char **params, + size_t param_count) +{ + MsnSession *session = servconn->session; + struct gaim_connection *gc = session->account->gc; + const char *state; + const char *passport; + const char *friend; + int status = 0; + + state = params[0]; + passport = params[1]; + friend = msn_url_decode(params[2]); + + serv_got_alias(gc, (char *)passport, (char *)friend); + + if (!g_ascii_strcasecmp(state, "BSY")) + status |= UC_UNAVAILABLE | (MSN_BUSY << 1); + else if (!g_ascii_strcasecmp(state, "IDL")) + status |= UC_UNAVAILABLE | (MSN_IDLE << 1); + else if (!g_ascii_strcasecmp(state, "BRB")) + status |= UC_UNAVAILABLE | (MSN_BRB << 1); + else if (!g_ascii_strcasecmp(state, "AWY")) + status |= UC_UNAVAILABLE | (MSN_AWAY << 1); + else if (!g_ascii_strcasecmp(state, "PHN")) + status |= UC_UNAVAILABLE | (MSN_PHONE << 1); + else if (!g_ascii_strcasecmp(state, "LUN")) + status |= UC_UNAVAILABLE | (MSN_LUNCH << 1); + + serv_got_update(gc, (char *)passport, 1, 0, 0, 0, status); + + return TRUE; +} + +static gboolean +__rea_cmd(MsnServConn *servconn, const char *command, const char **params, + size_t param_count) +{ + MsnSession *session = servconn->session; + struct gaim_connection *gc = session->account->gc; + char *friend; + + friend = msn_url_decode(params[2]); + + g_snprintf(gc->displayname, sizeof(gc->displayname), "%s", friend); + + return TRUE; +} + +/************************************************************************** + * Misc commands + **************************************************************************/ +static gboolean +__url_cmd(MsnServConn *servconn, const char *command, const char **params, + size_t param_count) +{ + MsnSession *session = servconn->session; + struct gaim_connection *gc = session->account->gc; + const char *rru; + const char *url; + md5_state_t st; + md5_byte_t di[16]; + FILE *fd; + char buf[2048]; + char buf2[3]; + char sendbuf[64]; + int i; + + rru = params[1]; + url = params[2]; + + g_snprintf(buf, sizeof(buf), "%s%lu%s", + session->passport_info.mspauth, + time(NULL) - session->passport_info.sl, gc->password); + + md5_init(&st); + md5_append(&st, (const md5_byte_t *)buf, strlen(buf)); + md5_finish(&st, di); + + memset(sendbuf, 0, sizeof(sendbuf)); + + for (i = 0; i < 16; i++) { + g_snprintf(buf2, sizeof(buf2), "%02x", di[i]); + strcat(sendbuf, buf2); + } + + if (session->passport_info.file != NULL) { + unlink(session->passport_info.file); + g_free(session->passport_info.file); + } + + if ((fd = gaim_mkstemp(&session->passport_info.file)) == NULL) { + gaim_debug(GAIM_DEBUG_ERROR, "msn", + "Error opening temp passport file: %s\n", + strerror(errno)); + } + else { + fputs("\n" + "\n" + "\n" + "\n\n", + fd); + + fprintf(fd, "\n"); + fprintf(fd, "
\n\n", + url); + fprintf(fd, "\n"); + fprintf(fd, "\n", + gc->username); + fprintf(fd, "\n", + gc->username); + fprintf(fd, "\n", + session->passport_info.sid); + fprintf(fd, "\n", + session->passport_info.kv); + fprintf(fd, "\n"); + fprintf(fd, "\n", + time(NULL) - session->passport_info.sl); + fprintf(fd, "\n", + rru); + fprintf(fd, "\n", + session->passport_info.mspauth); + fprintf(fd, "\n", + sendbuf); /* TODO Digest me (huh? -- ChipX86) */ + fprintf(fd, "\n"); + fprintf(fd, "\n"); + fprintf(fd, "
\n"); + fprintf(fd, "\n"); + + if (fclose(fd)) { + gaim_debug(GAIM_DEBUG_ERROR, "msn", + "Error closing temp passport file: %s\n", + strerror(errno)); + + unlink(session->passport_info.file); + g_free(session->passport_info.file); + } + else { + /* + * Renaming file with .html extension, so that the + * win32 open_url will work. + */ + char *tmp; + + if ((tmp = g_strdup_printf("%s.html", + session->passport_info.file)) != NULL) { + + if (rename(session->passport_info.file, tmp) == 0) { + g_free(session->passport_info.file); + session->passport_info.file = tmp; + } + else + g_free(tmp); + } + } + } + + return TRUE; +} +/************************************************************************** + * Switchboards + **************************************************************************/ +static gboolean +__rng_cmd(MsnServConn *servconn, const char *command, const char **params, + size_t param_count) +{ + MsnSession *session = servconn->session; + MsnSwitchBoard *swboard; + MsnUser *user; + const char *session_id; + char *host, *c; + int port; + + session_id = params[0]; + + host = g_strdup(params[1]); + + if ((c = strchr(host, ':')) != NULL) { + *c = '\0'; + port = atoi(c + 1); + } + else + port = 1863; + + swboard = msn_switchboard_new(session); + + user = msn_user_new(session, params[4], NULL); + + msn_switchboard_set_invited(swboard, TRUE); + msn_switchboard_set_session_id(swboard, params[0]); + msn_switchboard_set_auth_key(swboard, params[3]); + msn_switchboard_set_user(swboard, user); + + if (!msn_switchboard_connect(swboard, host, port)) { + gaim_debug(GAIM_DEBUG_ERROR, "msn", + "Unable to connect to switchboard on %s, port %d\n", + host, port); + + g_free(host); + + return FALSE; + } + + g_free(host); + + return TRUE; +} + +static gboolean +__xfr_cmd(MsnServConn *servconn, const char *command, const char **params, + size_t param_count) +{ + MsnSession *session = servconn->session; + MsnSwitchBoard *swboard; + struct gaim_connection *gc = session->account->gc; + char *host; + char *c; + int port; + + if (strcmp(params[1], "SB")) { + hide_login_progress(gc, _("Got invalid XFR")); + signoff(gc); + + return FALSE; + } + + host = g_strdup(params[2]); + + if ((c = strchr(host, ':')) != NULL) { + *c = '\0'; + port = atoi(c + 1); + } + else + port = 1863; + + swboard = msn_session_find_unused_switch(session); + + if (swboard == NULL) { + gaim_debug(GAIM_DEBUG_ERROR, "msn", + "Received an XFR SB request when there's no unused " + "switchboards!\n"); + } + + msn_switchboard_set_auth_key(swboard, params[4]); + + if (!msn_switchboard_connect(swboard, host, port)) { + gaim_debug(GAIM_DEBUG_ERROR, "msn", + "Unable to connect to switchboard on %s, port %d\n", + host, port); + + g_free(host); + + return FALSE; + } + + g_free(host); + + return TRUE; +} + +/************************************************************************** + * Message Types + **************************************************************************/ +static gboolean +__profile_msg(MsnServConn *servconn, const MsnMessage *msg) +{ + MsnSession *session = servconn->session; + const char *value; + + if ((value = msn_message_get_attr(msg, "kv")) != NULL) + session->passport_info.kv = g_strdup(value); + + if ((value = msn_message_get_attr(msg, "sid")) != NULL) + session->passport_info.sid = g_strdup(value); + + if ((value = msn_message_get_attr(msg, "MSPAuth")) != NULL) + session->passport_info.mspauth = g_strdup(value); + + return TRUE; +} + +static gboolean +__initial_email_msg(MsnServConn *servconn, const MsnMessage *msg) +{ + MsnSession *session = servconn->session; + struct gaim_connection *gc = session->account->gc; + GHashTable *table; + const char *unread; + + table = msn_message_get_hashtable_from_body(msg); + + unread = g_hash_table_lookup(table, "Inbox-Unread"); + + if (unread != NULL) + connection_has_mail(gc, atoi(unread), NULL, NULL, + session->passport_info.file); + + g_hash_table_destroy(table); + + return TRUE; +} + +static gboolean +__email_msg(MsnServConn *servconn, const MsnMessage *msg) +{ + MsnSession *session = servconn->session; + struct gaim_connection *gc = session->account->gc; + GHashTable *table; + const char *from, *subject; + + table = msn_message_get_hashtable_from_body(msg); + + from = g_hash_table_lookup(table, "From"); + subject = g_hash_table_lookup(table, "Subject"); + + if (from == NULL || subject == NULL) { + connection_has_mail(gc, 1, NULL, NULL, session->passport_info.file); + } + else { + connection_has_mail(gc, -1, from, subject, + session->passport_info.file); + } + + g_hash_table_destroy(table); + + return TRUE; +} + +static gboolean +__connect_cb(gpointer data, gint source, GaimInputCondition cond) +{ + MsnServConn *notification = data; + MsnSession *session = notification->session; + struct gaim_connection *gc = session->account->gc; + + if (source == -1) { + hide_login_progress(session->account->gc, _("Unable to connect")); + signoff(session->account->gc); + return FALSE; + } + + if (notification->fd != source) + notification->fd = source; + + if (!msn_servconn_send_command(notification, "VER", + "MSNP6 MSNP5 MSNP4 CVR0")) { + hide_login_progress(gc, _("Unable to write to server")); + signoff(gc); + return FALSE; + } + + set_login_progress(session->account->gc, 2, _("Syncing with server")); + + return TRUE; +} + +static void +__failed_read_cb(gpointer data, gint source, GaimInputCondition cond) +{ + MsnServConn *notification = data; + struct gaim_connection *gc; + + gc = notification->session->account->gc; + + hide_login_progress(gc, _("Error reading from server")); + signoff(gc); +} + +MsnServConn * +msn_notification_new(MsnSession *session, const char *server, int port) +{ + MsnServConn *notification; + + notification = msn_servconn_new(session); + + msn_servconn_set_server(notification, server, port); + msn_servconn_set_connect_cb(notification, __connect_cb); + msn_servconn_set_failed_read_cb(notification, __failed_read_cb); + + if (notification_commands == NULL) { + /* Register the command callbacks. */ + msn_servconn_register_command(notification, "ADD", __add_cmd); + msn_servconn_register_command(notification, "BLP", __blp_cmd); + msn_servconn_register_command(notification, "BPR", __blank_cmd); + msn_servconn_register_command(notification, "CHG", __blank_cmd); + msn_servconn_register_command(notification, "CHL", __chl_cmd); + msn_servconn_register_command(notification, "FLN", __fln_cmd); + msn_servconn_register_command(notification, "GTC", __blank_cmd); + msn_servconn_register_command(notification, "ILN", __iln_cmd); + msn_servconn_register_command(notification, "INF", __inf_cmd); + msn_servconn_register_command(notification, "LSG", __lsg_cmd); + msn_servconn_register_command(notification, "LST", __lst_cmd); + msn_servconn_register_command(notification, "MSG", __msg_cmd); + msn_servconn_register_command(notification, "NLN", __nln_cmd); + msn_servconn_register_command(notification, "OUT", __out_cmd); + msn_servconn_register_command(notification, "PRP", __blank_cmd); + msn_servconn_register_command(notification, "QNG", __blank_cmd); + msn_servconn_register_command(notification, "QRY", __blank_cmd); + msn_servconn_register_command(notification, "REA", __rea_cmd); + msn_servconn_register_command(notification, "REM", __blank_cmd); + msn_servconn_register_command(notification, "RNG", __rng_cmd); + msn_servconn_register_command(notification, "SYN", __blank_cmd); + msn_servconn_register_command(notification, "URL", __url_cmd); + msn_servconn_register_command(notification, "USR", __usr_cmd); + msn_servconn_register_command(notification, "VER", __ver_cmd); + msn_servconn_register_command(notification, "XFR", __xfr_cmd); + msn_servconn_register_command(notification, "_unknown_", __unknown_cmd); + + /* Register the message type callbacks. */ + msn_servconn_register_msg_type(notification, "text/x-msmsgsprofile", + __profile_msg); + msn_servconn_register_msg_type(notification, + "text/x-msmsgsinitialemailnotification", + __initial_email_msg); + msn_servconn_register_msg_type(notification, + "text/x-msmsgsemailnotification", + __email_msg); + + /* Save these for future use. */ + notification_commands = notification->commands; + notification_msg_types = notification->msg_types; + } + else { + g_hash_table_destroy(notification->commands); + g_hash_table_destroy(notification->msg_types); + + notification->commands = notification_commands; + notification->msg_types = notification_msg_types; + } + + return notification; +} diff -r 6aa785e55d0f -r e2e53316a21d src/protocols/msn/notification.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/msn/notification.h Tue May 06 02:06:56 2003 +0000 @@ -0,0 +1,31 @@ +/** + * @file notification.h Notification server functions + * + * gaim + * + * Copyright (C) 2003 Christian Hammond + * + * 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 + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _MSN_NOTIFICATION_H_ +#define _MSN_NOTIFICATION_H_ + +#include "session.h" +#include "servconn.h" + +MsnServConn *msn_notification_new(MsnSession *session, const char *server, + int port); + +#endif /* _MSN_NOTIFICATION_H_ */ diff -r 6aa785e55d0f -r e2e53316a21d src/protocols/msn/servconn.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/msn/servconn.c Tue May 06 02:06:56 2003 +0000 @@ -0,0 +1,394 @@ +/** + * @file servconn.c Server connection functions + * + * gaim + * + * Copyright (C) 2003 Christian Hammond + * + * 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 + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "msn.h" +#include "servconn.h" + +static gboolean +__process_single_line(MsnServConn *servconn, char *str) +{ + MsnServConnCommandCb cb; + gboolean result; + size_t param_count = 0; + char *command, *param_start; + char **params = NULL; + + command = str; + + /** + * See how many spaces we have in this. + */ + param_start = strchr(command, ' '); + + if (param_start != NULL) { + params = g_strsplit(param_start + 1, " ", 0); + + for (param_count = 0; params[param_count] != NULL; param_count++) + ; + + *param_start = '\0'; + } + + cb = g_hash_table_lookup(servconn->commands, command); + + if (cb == NULL) { + cb = g_hash_table_lookup(servconn->commands, "_unknown_"); + + if (cb == NULL) { + gaim_debug(GAIM_DEBUG_WARNING, "msn", + "Unhandled command '%s'\n", str); + + if (params != NULL) + g_strfreev(params); + + return TRUE; + } + } + + result = cb(servconn, command, (const char **)params, param_count); + + if (params != NULL) + g_strfreev(params); + + return result; +} + +static gboolean +__process_multi_line(MsnServConn *servconn, char *buffer) +{ + MsnServConnMsgCb cb; + MsnMessage *msg; + char msg_str[MSN_BUF_LEN]; + + g_snprintf(msg_str, sizeof(msg_str), + "MSG %s %s %d\r\n%s", + servconn->msg_passport, servconn->msg_friendly, + servconn->msg_len, buffer); + + msg = msn_message_new_from_str(servconn->session, msg_str); + + cb = g_hash_table_lookup(servconn->msg_types, + msn_message_get_content_type(msg)); + + if (cb == NULL) { + gaim_debug(GAIM_DEBUG_WARNING, "msn", + "Unhandled content-type '%s': %s\n", + msn_message_get_content_type(msg), + msn_message_get_body(msg)); + + msn_message_destroy(msg); + + return FALSE; + } + + cb(servconn, msg); + + msn_message_destroy(msg); + + return TRUE; +} + +static void +__connect_cb(gpointer data, gint source, GaimInputCondition cond) +{ + MsnServConn *servconn = data; + + if (servconn->connect_cb(data, source, cond)) + servconn->inpa = gaim_input_add(servconn->fd, GAIM_INPUT_READ, + servconn->login_cb, data); +} + +MsnServConn * +msn_servconn_new(MsnSession *session) +{ + MsnServConn *servconn; + + g_return_val_if_fail(session != NULL, NULL); + + servconn = g_new0(MsnServConn, 1); + + servconn->login_cb = msn_servconn_parse_data; + servconn->session = session; + + servconn->commands = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, NULL); + + servconn->msg_types = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, NULL); + + return servconn; +} + +gboolean +msn_servconn_connect(MsnServConn *servconn) +{ + int i; + + g_return_val_if_fail(servconn != NULL, FALSE); + g_return_val_if_fail(servconn->server != NULL, FALSE); + g_return_val_if_fail(!servconn->connected, TRUE); + + i = proxy_connect(servconn->session->account, servconn->server, + servconn->port, __connect_cb, servconn); + + if (i == 0) + servconn->connected = TRUE; + + return servconn->connected; +} + +void +msn_servconn_disconnect(MsnServConn *servconn) +{ + g_return_if_fail(servconn != NULL); + g_return_if_fail(servconn->connected); + + close(servconn->fd); + + if (servconn->inpa) + gaim_input_remove(servconn->inpa); + + g_free(servconn->rxqueue); + + while (servconn->txqueue) { + g_free(servconn->txqueue->data); + + servconn->txqueue = g_slist_remove(servconn->txqueue, + servconn->txqueue->data); + } + + servconn->connected = FALSE; +} + +void +msn_servconn_destroy(MsnServConn *servconn) +{ + g_return_if_fail(servconn != NULL); + + if (servconn->connected) + msn_servconn_disconnect(servconn); + + if (servconn->server != NULL) + g_free(servconn->server); + + g_free(servconn); +} + +void +msn_servconn_set_server(MsnServConn *servconn, const char *server, int port) +{ + g_return_if_fail(servconn != NULL); + g_return_if_fail(server != NULL); + g_return_if_fail(port > 0); + + if (servconn->server != NULL) + g_free(servconn->server); + + servconn->server = g_strdup(server); + servconn->port = port; +} + +const char * +msn_servconn_get_server(const MsnServConn *servconn) +{ + g_return_val_if_fail(servconn != NULL, NULL); + + return servconn->server; +} + +int +msn_servconn_get_port(const MsnServConn *servconn) +{ + g_return_val_if_fail(servconn != NULL, 0); + + return servconn->port; +} + +void +msn_servconn_set_connect_cb(MsnServConn *servconn, + gboolean (*connect_cb)(gpointer, gint, + GaimInputCondition)) +{ + g_return_if_fail(servconn != NULL); + + servconn->connect_cb = connect_cb; +} + +void +msn_servconn_set_failed_read_cb(MsnServConn *servconn, + void (*failed_read_cb)(gpointer, gint, + GaimInputCondition)) +{ + g_return_if_fail(servconn != NULL); + + servconn->failed_read_cb = failed_read_cb; +} + +size_t +msn_servconn_write(MsnServConn *servconn, const char *buf, size_t size) +{ + g_return_val_if_fail(servconn != NULL, 0); + + gaim_debug(GAIM_DEBUG_MISC, "msn", "C: %s%s", buf, + (*(buf + size - 1) == '\n' ? "" : "\n")); + + return write(servconn->fd, buf, size); +} + +gboolean +msn_servconn_send_command(MsnServConn *servconn, const char *command, + const char *params) +{ + char buf[MSN_BUF_LEN]; + + g_return_val_if_fail(servconn != NULL, FALSE); + g_return_val_if_fail(command != NULL, FALSE); + + if (params == NULL) + g_snprintf(buf, sizeof(buf), "%s %u\r\n", command, + servconn->session->trId++); + else + g_snprintf(buf, sizeof(buf), "%s %u %s\r\n", + command, servconn->session->trId++, params); + + return (msn_servconn_write(servconn, buf, strlen(buf)) > 0); +} + +void +msn_servconn_register_command(MsnServConn *servconn, const char *command, + MsnServConnCommandCb cb) +{ + char *command_up; + + g_return_if_fail(servconn != NULL); + g_return_if_fail(command != NULL); + g_return_if_fail(cb != NULL); + + command_up = g_ascii_strup(command, -1); + + g_hash_table_insert(servconn->commands, command_up, cb); +} + +void +msn_servconn_register_msg_type(MsnServConn *servconn, + const char *content_type, + MsnServConnMsgCb cb) +{ + g_return_if_fail(servconn != NULL); + g_return_if_fail(content_type != NULL); + g_return_if_fail(cb != NULL); + + g_hash_table_insert(servconn->msg_types, g_strdup(content_type), cb); +} + +void +msn_servconn_parse_data(gpointer data, gint source, GaimInputCondition cond) +{ + MsnServConn *servconn = (MsnServConn *)data; + char buf[MSN_BUF_LEN]; + gboolean cont = TRUE; + int len; + + len = read(servconn->fd, buf, sizeof(buf)); + + if (len <= 0) { + if (servconn->failed_read_cb != NULL) + servconn->failed_read_cb(data, source, cond); + + return; + } + + servconn->rxqueue = g_realloc(servconn->rxqueue, len + servconn->rxlen); + memcpy(servconn->rxqueue + servconn->rxlen, buf, len); + servconn->rxlen += len; + + while (cont) { + if (servconn->parsing_msg) { + char *msg; + + if (servconn->rxlen == 0) + break; + + if (servconn->msg_len > servconn->rxlen) + break; + + msg = servconn->rxqueue; + servconn->rxlen -= servconn->msg_len; + + if (servconn->rxlen) { + servconn->rxqueue = g_memdup(msg + servconn->msg_len, + servconn->rxlen); + } + else { + servconn->rxqueue = NULL; + msg = g_realloc(msg, servconn->msg_len + 1); + } + + msg[servconn->msg_len] = '\0'; + servconn->parsing_msg = FALSE; + + __process_multi_line(servconn, msg); + + servconn->msg_len = 0; + g_free(servconn->msg_passport); + g_free(servconn->msg_friendly); + g_free(msg); + } + else { + char *end = servconn->rxqueue; + char *cmd; + int cmdlen, i; + + if (!servconn->rxlen) + return; + + for (i = 0; i < servconn->rxlen - 1; end++, i++) { + if (*end == '\r' && end[1] == '\n') + break; + } + + if (i == servconn->rxlen - 1) + return; + + cmdlen = end - servconn->rxqueue + 2; + cmd = servconn->rxqueue; + servconn->rxlen -= cmdlen; + + if (servconn->rxlen) + servconn->rxqueue = g_memdup(cmd + cmdlen, servconn->rxlen); + else { + servconn->rxqueue = NULL; + cmd = g_realloc(cmd, cmdlen + 1); + } + + cmd[cmdlen] = '\0'; + + gaim_debug(GAIM_DEBUG_MISC, "msn", "S: %s", cmd); + + g_strchomp(cmd); + + cont = __process_single_line(servconn, cmd); + + g_free(cmd); + } + } +} + diff -r 6aa785e55d0f -r e2e53316a21d src/protocols/msn/servconn.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/msn/servconn.h Tue May 06 02:06:56 2003 +0000 @@ -0,0 +1,106 @@ +/** + * @file servconn.h Server connection functions + * + * gaim + * + * Copyright (C) 2003 Christian Hammond + * + * 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 + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _MSN_SERVCONN_H_ +#define _MSN_SERVCONN_H_ + +typedef struct _MsnServConn MsnServConn; + +#include "msg.h" + +typedef gboolean (*MsnServConnCommandCb)(MsnServConn *servconn, + const char *cmd, const char **params, + size_t param_count); + +typedef gboolean (*MsnServConnMsgCb)(MsnServConn *servconn, + const MsnMessage *msg); + +#include "session.h" + +struct _MsnServConn +{ + MsnSession *session; + + gboolean connected; + + char *server; + int port; + + int fd; + int inpa; + + char *rxqueue; + int rxlen; + + GSList *txqueue; + + gboolean parsing_msg; + char *msg_passport; + char *msg_friendly; + int msg_len; + + GHashTable *commands; + GHashTable *msg_types; + + gboolean (*connect_cb)(gpointer, gint, GaimInputCondition); + void (*failed_read_cb)(gpointer, gint, GaimInputCondition); + void (*login_cb)(gpointer, gint, GaimInputCondition); + + void *data; +}; + +MsnServConn *msn_servconn_new(MsnSession *session); + +void msn_servconn_destroy(MsnServConn *servconn); + +gboolean msn_servconn_connect(MsnServConn *servconn); +void msn_servconn_disconnect(MsnServConn *servconn); + +void msn_servconn_set_server(MsnServConn *servconn, const char *server, + int port); + +const char *msn_servconn_get_server(const MsnServConn *servconn); +int msn_servconn_get_port(const MsnServConn *servconn); + +void msn_servconn_set_connect_cb(MsnServConn *servconn, + gboolean (*connect_cb)(gpointer, gint, GaimInputCondition)); + +void msn_servconn_set_failed_read_cb(MsnServConn *servconn, + void (*failed_read_cb)(gpointer, gint, GaimInputCondition)); + +size_t msn_servconn_write(MsnServConn *servconn, const char *buf, + size_t size); + +gboolean msn_servconn_send_command(MsnServConn *servconn, const char *command, + const char *params); + +void msn_servconn_register_command(MsnServConn *servconn, + const char *command, + MsnServConnCommandCb cb); + +void msn_servconn_register_msg_type(MsnServConn *servconn, + const char *content_type, + MsnServConnMsgCb cb); + +void msn_servconn_parse_data(gpointer data, gint source, + GaimInputCondition cond); + +#endif /* _MSN_SERVCONN_H_ */ diff -r 6aa785e55d0f -r e2e53316a21d src/protocols/msn/session.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/msn/session.c Tue May 06 02:06:56 2003 +0000 @@ -0,0 +1,185 @@ +/** + * @file session.c MSN session functions + * + * gaim + * + * Copyright (C) 2003 Christian Hammond + * + * 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 + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "msn.h" +#include "session.h" +#include "dispatch.h" + +MsnSession * +msn_session_new(struct gaim_account *account, const char *server, int port) +{ + MsnSession *session; + + g_return_val_if_fail(account != NULL, NULL); + + session = g_new0(MsnSession, 1); + + session->account = account; + session->dispatch_server = g_strdup(server); + session->dispatch_port = port; + + session->users = msn_users_new(); + + return session; +} + +void +msn_session_destroy(MsnSession *session) +{ + g_return_if_fail(session != NULL); + + if (session->connected) + msn_session_disconnect(session); + + if (session->dispatch_server != NULL) + g_free(session->dispatch_server); + + while (session->switches != NULL) + msn_switchboard_destroy(session->switches->data); + + while (session->lists.forward) + msn_user_destroy(session->lists.forward->data); + + g_slist_free(session->lists.allow); + g_slist_free(session->lists.block); + + msn_users_destroy(session->users); + + g_free(session); +} + +gboolean +msn_session_connect(MsnSession *session) +{ + g_return_val_if_fail(session != NULL, FALSE); + g_return_val_if_fail(!session->connected, TRUE); + + session->connected = TRUE; + + session->dispatch_conn = msn_dispatch_new(session, + session->dispatch_server, + session->dispatch_port); + + if (msn_servconn_connect(session->dispatch_conn)) + return TRUE; + + return FALSE; +} + +void +msn_session_disconnect(MsnSession *session) +{ + g_return_if_fail(session != NULL); + g_return_if_fail(session->connected); + + if (session->dispatch_conn != NULL) { + msn_servconn_destroy(session->dispatch_conn); + session->dispatch_conn = NULL; + } + + while (session->switches != NULL) { + MsnSwitchBoard *board = (MsnSwitchBoard *)session->switches->data; + + msn_switchboard_destroy(board); + } + + if (session->notification_conn != NULL) { + msn_servconn_destroy(session->notification_conn); + session->notification_conn = NULL; + } +} + +MsnSwitchBoard * +msn_session_open_switchboard(MsnSession *session) +{ + MsnSwitchBoard *swboard; + + g_return_val_if_fail(session != NULL, NULL); + + if (msn_servconn_send_command(session->notification_conn, + "XFR", "SB") < 0) { + return NULL; + } + + swboard = msn_switchboard_new(session); + + return swboard; +} + +MsnSwitchBoard * +msn_session_find_switch_with_passport(const MsnSession *session, + const char *passport) +{ + GList *l; + MsnSwitchBoard *swboard; + + g_return_val_if_fail(session != NULL, NULL); + g_return_val_if_fail(passport != NULL, NULL); + + for (l = session->switches; l != NULL; l = l->next) { + swboard = (MsnSwitchBoard *)l->data; + + if (!g_ascii_strcasecmp(passport, + msn_user_get_passport(swboard->user))) { + return swboard; + } + } + + return NULL; +} + +MsnSwitchBoard * +msn_session_find_switch_with_id(const MsnSession *session, int chat_id) +{ + GList *l; + MsnSwitchBoard *swboard; + + g_return_val_if_fail(session != NULL, NULL); + g_return_val_if_fail(chat_id > 0, NULL); + + for (l = session->switches; l != NULL; l = l->next) { + swboard = (MsnSwitchBoard *)l->data; + + if (swboard->chat_id == chat_id) + return swboard; + } + + return NULL; +} + +MsnSwitchBoard * +msn_session_find_unused_switch(const MsnSession *session) +{ + GList *l; + MsnSwitchBoard *swboard; + + g_return_val_if_fail(session != NULL, NULL); + + for (l = session->switches; l != NULL; l = l->next) { + swboard = (MsnSwitchBoard *)l->data; + + if (!swboard->in_use) + return swboard; + } + + return NULL; +} + diff -r 6aa785e55d0f -r e2e53316a21d src/protocols/msn/session.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/msn/session.h Tue May 06 02:06:56 2003 +0000 @@ -0,0 +1,148 @@ +/** + * @file session.h MSN session functions + * + * gaim + * + * Copyright (C) 2003 Christian Hammond + * + * 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 + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _MSN_SESSION_H_ +#define _MSN_SESSION_H_ + +typedef struct _MsnSession MsnSession; + +#include "servconn.h" +#include "switchboard.h" +#include "user.h" + +struct _MsnSession +{ + struct gaim_account *account; + + char *dispatch_server; + int dispatch_port; + + gboolean connected; + + MsnServConn *dispatch_conn; + MsnServConn *notification_conn; + + unsigned int trId; + + MsnUsers *users; + + GList *switches; + GHashTable *groups; + + struct + { + GSList *forward; + GSList *reverse; + GSList *allow; + GSList *block; + + } lists; + + struct + { + char *kv; + char *sid; + char *mspauth; + unsigned long sl; + char *file; + + } passport_info; + + /* You have no idea how much I hate this. */ + GaimPlugin *prpl; +}; + +/** + * Creates an MSN session. + * + * @param account The account. + * @param server The dispatch server. + * @param port The dispatch port. + * + * @return The new MSN session. + */ +MsnSession *msn_session_new(struct gaim_account *account, + const char *server, int port); + +/** + * Destroys an MSN session. + * + * @param session The MSN session to destroy. + */ +void msn_session_destroy(MsnSession *session); + +/** + * Connects to and initiates an MSN session. + * + * @param session The MSN session. + * + * @return @c TRUE on success, @c FALSE on failure. + */ +gboolean msn_session_connect(MsnSession *session); + +/** + * Disconnects from an MSN session. + * + * @param session The MSN session. + */ +void msn_session_disconnect(MsnSession *session); + +/** + * Opens a new switchboard connection. + * + * @param session The MSN session. + * + * @return The new switchboard connection. + */ +MsnSwitchBoard *msn_session_open_switchboard(MsnSession *session); + +/** + * Finds a switch with the given passport. + * + * @param session The MSN session. + * @param passport The passport to search for. + * + * @return The switchboard, if found. + */ +MsnSwitchBoard *msn_session_find_switch_with_passport( + const MsnSession *session, const char *passport); + +/** + * Finds a switchboard with the given chat ID. + * + * @param session The MSN session. + * @param chat_id The chat ID to search for. + * + * @return The switchboard, if found. + */ +MsnSwitchBoard *msn_session_find_switch_with_id(const MsnSession *session, + int chat_id); + +/** + * Finds the first unused switchboard. + * + * @param session The MSN session. + * + * @return The first unused, writable switchboard, if found. + */ +MsnSwitchBoard *msn_session_find_unused_switch(const MsnSession *session); + +#endif /* _MSN_SESSION_H_ */ diff -r 6aa785e55d0f -r e2e53316a21d src/protocols/msn/switchboard.c --- a/src/protocols/msn/switchboard.c Tue May 06 00:34:54 2003 +0000 +++ b/src/protocols/msn/switchboard.c Tue May 06 02:06:56 2003 +0000 @@ -17,518 +17,535 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "msn.h" +#include "switchboard.h" +#include "utils.h" -#ifdef _WIN32 -#include "win32dep.h" -#endif +static GHashTable *switchboard_commands = NULL; +static GHashTable *switchboard_msg_types = NULL; + -G_MODULE_IMPORT GSList *connections; +/************************************************************************** + * Catch-all commands + **************************************************************************/ +static gboolean +__blank_cmd(MsnServConn *servconn, const char *command, const char **params, + size_t param_count) +{ + return TRUE; +} -static char * -msn_parse_format(char *mime) +static gboolean +__unknown_cmd(MsnServConn *servconn, const char *command, const char **params, + size_t param_count) { - char *cur; - GString *ret = g_string_new(NULL); - guint colorbuf; - char *colors = (char *)(&colorbuf); - + gaim_debug(GAIM_DEBUG_ERROR, "msg", + "Handled switchboard message: %s\n", command); + + return FALSE; +} + +/************************************************************************** + * Switchboard Commands + **************************************************************************/ +static gboolean +__ans_cmd(MsnServConn *servconn, const char *command, const char **params, + size_t param_count) +{ + struct gaim_connection *gc = servconn->session->account->gc; + MsnSwitchBoard *swboard = servconn->data; + + if (swboard->chat != NULL) + gaim_chat_add_user(GAIM_CHAT(swboard->chat), gc->username, NULL); + + return TRUE; +} - cur = strstr(mime, "FN="); - if (cur && (*(cur = cur + 3) != ';')) { - ret = g_string_append(ret, ""); +static gboolean +__bye_cmd(MsnServConn *servconn, const char *command, const char **params, + size_t param_count) +{ + struct gaim_connection *gc = servconn->session->account->gc; + MsnSwitchBoard *swboard = servconn->data; + const char *user = params[0]; + + if (swboard->chat != NULL) + gaim_chat_remove_user(GAIM_CHAT(swboard->chat), user, NULL); + else { + const char *username; + struct gaim_conversation *conv; + struct buddy *b; + char buf[MSN_BUF_LEN]; + + if ((b = gaim_find_buddy(gc->account, user)) != NULL) + username = gaim_get_buddy_alias(b); + else + username = user; + + g_snprintf(buf, sizeof(buf), + _("%s has closed the conversation window."), username); + + if ((conv = gaim_find_conversation(user)) != NULL) + gaim_conversation_write(conv, NULL, buf, -1, WFLAG_SYSTEM, + time(NULL)); + + msn_switchboard_destroy(swboard); + + return FALSE; } - - cur = strstr(mime, "EF="); - if (cur && (*(cur = cur + 3) != ';')) { - while (*cur && *cur != ';') { - ret = g_string_append_c(ret, '<'); - ret = g_string_append_c(ret, *cur); - ret = g_string_append_c(ret, '>'); - cur++; - } + + return TRUE; +} + +static gboolean +__iro_cmd(MsnServConn *servconn, const char *command, const char **params, + size_t param_count) +{ + struct gaim_connection *gc = servconn->session->account->gc; + MsnSwitchBoard *swboard = servconn->data; + + swboard->total_users = atoi(params[2]); + + if (swboard->total_users > 1) { + if (swboard->chat == NULL) + swboard->chat = serv_got_joined_chat(gc, ++swboard->chat_id, + "MSN Chat"); + + gaim_chat_add_user(GAIM_CHAT(swboard->chat), params[3], NULL); } - - cur = strstr(mime, "CO="); - if (cur && (*(cur = cur + 3) != ';')) { - if (sscanf (cur, "%x;", &colorbuf) == 1) { - char tag[64]; - g_snprintf(tag, sizeof(tag), "", colors[0], colors[1], colors[2]); - ret = g_string_append(ret, tag); - } - } - - cur = url_decode(ret->str); - g_string_free(ret, TRUE); - return cur; + + if (swboard->chat != NULL) + gaim_chat_add_user(GAIM_CHAT(swboard->chat), gc->username, NULL); + + return TRUE; } -static int -msn_process_switch(struct msn_switchboard *ms, char *buf) +static gboolean +__joi_cmd(MsnServConn *servconn, const char *command, const char **params, + size_t param_count) { - struct gaim_connection *gc = ms->gc; - char sendbuf[MSN_BUF_LEN]; - static int id = 0; - - if (!g_ascii_strncasecmp(buf, "ACK", 3)) { - } else if (!g_ascii_strncasecmp(buf, "ANS", 3)) { - if (ms->chat) - gaim_chat_add_user(GAIM_CHAT(ms->chat), gc->username, NULL); - } else if (!g_ascii_strncasecmp(buf, "BYE", 3)) { - char *user, *tmp = buf; - GET_NEXT(tmp); - user = tmp; - - if (ms->chat) { - gaim_chat_remove_user(GAIM_CHAT(ms->chat), user, NULL); - } else { - char msgbuf[256]; - const char *username; - struct gaim_conversation *cnv; - struct buddy *b; - - if ((b = gaim_find_buddy(gc->account, user)) != NULL) - username = gaim_get_buddy_alias(b); - else - username = user; - - g_snprintf(msgbuf, sizeof(msgbuf), - _("%s has closed the conversation window"), username); + struct gaim_connection *gc = servconn->session->account->gc; + MsnSwitchBoard *swboard = servconn->data; + const char *passport; - if ((cnv = gaim_find_conversation(user))) - gaim_conversation_write(cnv, NULL, msgbuf, -1, - WFLAG_SYSTEM, time(NULL)); - - msn_kill_switch(ms); - return 0; - } - } else if (!g_ascii_strncasecmp(buf, "CAL", 3)) { - } else if (!g_ascii_strncasecmp(buf, "IRO", 3)) { - char *tot, *user, *tmp = buf; + passport = params[0]; - GET_NEXT(tmp); - GET_NEXT(tmp); - GET_NEXT(tmp); - tot = tmp; - GET_NEXT(tmp); - ms->total = atoi(tot); - user = tmp; - GET_NEXT(tmp); - - if (ms->total > 1) { - if (!ms->chat) - ms->chat = serv_got_joined_chat(gc, ++id, "MSN Chat"); - - gaim_chat_add_user(GAIM_CHAT(ms->chat), user, NULL); - } - } else if (!g_ascii_strncasecmp(buf, "JOI", 3)) { - char *user, *tmp = buf; - GET_NEXT(tmp); - user = tmp; - GET_NEXT(tmp); + if (swboard->total_users == 1) { + swboard->chat = serv_got_joined_chat(gc, ++swboard->chat_id, + "MSN Chat"); + gaim_chat_add_user(GAIM_CHAT(swboard->chat), + msn_user_get_passport(swboard->user), NULL); + gaim_chat_add_user(GAIM_CHAT(swboard->chat), gc->username, NULL); - if (ms->total == 1) { - ms->chat = serv_got_joined_chat(gc, ++id, "MSN Chat"); - gaim_chat_add_user(GAIM_CHAT(ms->chat), ms->user, NULL); - gaim_chat_add_user(GAIM_CHAT(ms->chat), gc->username, NULL); - g_free(ms->user); - ms->user = NULL; - } - if (ms->chat) - gaim_chat_add_user(GAIM_CHAT(ms->chat), user, NULL); - ms->total++; - while (ms->txqueue) { - char *send = add_cr(ms->txqueue->data); - g_snprintf(sendbuf, sizeof(sendbuf), - "MSG %u N %d\r\n%s%s", ++ms->trId, - strlen(MIME_HEADER) + strlen(send), - MIME_HEADER, send); + msn_user_unref(swboard->user); + } - g_free(ms->txqueue->data); - ms->txqueue = g_slist_remove(ms->txqueue, ms->txqueue->data); + if (swboard->chat != NULL) + gaim_chat_add_user(GAIM_CHAT(swboard->chat), passport, NULL); - if (msn_write(ms->fd, sendbuf, strlen(sendbuf)) < 0) { - msn_kill_switch(ms); - return 0; - } - } - } else if (!g_ascii_strncasecmp(buf, "MSG", 3)) { - char *user, *tmp = buf; - int length; - - GET_NEXT(tmp); - user = tmp; - - GET_NEXT(tmp); + swboard->total_users++; - GET_NEXT(tmp); - length = atoi(tmp); + while (servconn->txqueue) { + char *buf = servconn->txqueue->data; - ms->msg = TRUE; - ms->msguser = g_strdup(user); - ms->msglen = length; - } else if (!g_ascii_strncasecmp(buf, "NAK", 3)) { - do_error_dialog(_("An MSN message may not have been received."), NULL, GAIM_ERROR); - } else if (!g_ascii_strncasecmp(buf, "NLN", 3)) { - } else if (!g_ascii_strncasecmp(buf, "OUT", 3)) { - if (ms->chat) - serv_got_chat_left(gc, gaim_chat_get_id(GAIM_CHAT(ms->chat))); - msn_kill_switch(ms); - return 0; - } else if (!g_ascii_strncasecmp(buf, "USR", 3)) { - /* good, we got USR, now we need to find out who we want to talk to */ - struct msn_switchboard *ms = msn_find_writable_switch(gc); + servconn->txqueue = g_slist_remove(servconn->txqueue, buf); - if (!ms) - return 0; - - g_snprintf(sendbuf, sizeof(sendbuf), "CAL %u %s\r\n", - ++ms->trId, ms->user); + if (msn_servconn_write(swboard->servconn, buf, strlen(buf)) < 0) { + msn_switchboard_destroy(swboard); - if (msn_write(ms->fd, sendbuf, strlen(sendbuf)) < 0) { - msn_kill_switch(ms); - return 0; + return FALSE; } - } else if (isdigit(*buf)) { - handle_errcode(buf, TRUE); - - if (atoi(buf) == 217) - msn_kill_switch(ms); - - } else { - gaim_debug(GAIM_DEBUG_WARNING, "msn", "Unhandled message!\n"); } - return 1; + return TRUE; +} + +static gboolean +__msg_cmd(MsnServConn *servconn, const char *command, const char **params, + size_t param_count) +{ + gaim_debug(GAIM_DEBUG_INFO, "msn", "Found message. Parsing.\n"); + + servconn->parsing_msg = TRUE; + servconn->msg_passport = g_strdup(params[0]); + servconn->msg_friendly = g_strdup(params[1]); + servconn->msg_len = atoi(params[2]); + + return TRUE; +} + +static gboolean +__nak_cmd(MsnServConn *servconn, const char *command, const char **params, + size_t param_count) +{ + /* + * TODO: Investigate this, as it seems to occur frequently with + * the old prpl. + */ + do_error_dialog(_("An MSN message may not have been received."), + NULL, GAIM_ERROR); + + return TRUE; } -static void -msn_process_switch_msg(struct msn_switchboard *ms, char *msg) +static gboolean +__out_cmd(MsnServConn *servconn, const char *command, const char **params, + size_t param_count) +{ + struct gaim_connection *gc = servconn->session->account->gc; + MsnSwitchBoard *swboard = servconn->data; + + if (swboard->chat != NULL) + serv_got_chat_left(gc, gaim_chat_get_id(GAIM_CHAT(swboard->chat))); + + msn_switchboard_destroy(swboard); + + return FALSE; +} + +static gboolean +__usr_cmd(MsnServConn *servconn, const char *command, const char **params, + size_t param_count) { - char *content, *agent, *format; - char *message = NULL; + MsnSwitchBoard *swboard = servconn->data; + + if (!msn_switchboard_send_command(swboard, "CAL", + msn_user_get_passport(swboard->user))) { + msn_switchboard_destroy(swboard); + + return FALSE; + } + + return TRUE; +} + +/************************************************************************** + * Message Types + **************************************************************************/ +static gboolean +__plain_msg(MsnServConn *servconn, const MsnMessage *msg) +{ + struct gaim_connection *gc = servconn->session->account->gc; + MsnSwitchBoard *swboard = servconn->data; + char *body; + const char *value; + char *format; int flags = 0; - agent = strstr(msg, "User-Agent: "); - if (agent) { - if (!g_ascii_strncasecmp(agent, "User-Agent: Gaim", - strlen("User-Agent: Gaim"))) + body = g_strdup(msn_message_get_body(msg)); + + if ((value = msn_message_get_attr(msg, "User-Agent")) != NULL) { + if (!g_ascii_strncasecmp(value, "Gaim", 4)) flags |= IM_FLAG_GAIMUSER; } - format = strstr(msg, "X-MMS-IM-Format: "); - if (format) { - format = msn_parse_format(format); - } else { - format = NULL; + if ((value = msn_message_get_attr(msg, "X-MMS-IM-Format")) != NULL) { + format = msn_parse_format(value); + + body = g_strdup_printf("%s%s", format, body); + + g_free(format); + } + + if (swboard->chat != NULL) + serv_got_chat_in(gc, gaim_chat_get_id(GAIM_CHAT(swboard->chat)), + servconn->msg_passport, flags, body, time(NULL)); + else + serv_got_im(gc, servconn->msg_passport, body, flags, time(NULL), -1); + + g_free(body); + + return TRUE; +} + +static gboolean +__control_msg(MsnServConn *servconn, const MsnMessage *msg) +{ + struct gaim_connection *gc = servconn->session->account->gc; + MsnSwitchBoard *swboard = servconn->data; + const char *value; + + if (swboard->chat == NULL && + (value = msn_message_get_attr(msg, "TypingUser")) != NULL) { + + serv_got_typing(gc, servconn->msg_passport, MSN_TYPING_RECV_TIMEOUT, + TYPING); } - content = strstr(msg, "Content-Type: "); - if (!content) - return; - if (!g_ascii_strncasecmp(content, "Content-Type: text/x-msmsgscontrol\r\n", - strlen( "Content-Type: text/x-msmsgscontrol\r\n"))) { - if (strstr(content,"TypingUser: ") && !ms->chat) { - serv_got_typing(ms->gc, ms->msguser, - MSN_TYPING_RECV_TIMEOUT, TYPING); - return; - } + return TRUE; +} - } else if (!g_ascii_strncasecmp(content, "Content-Type: text/x-msmsgsinvite;", - strlen("Content-Type: text/x-msmsgsinvite;"))) { +/************************************************************************** + * Connect stuff + **************************************************************************/ +static gboolean +__connect_cb(gpointer data, gint source, GaimInputCondition cond) +{ + MsnServConn *servconn = data; + MsnSwitchBoard *swboard = servconn->data; + char outparams[MSN_BUF_LEN]; - /* - * NOTE: Other things, such as voice communication, would go in - * here too (since they send the same Content-Type). However, - * this is the best check for file transfer messages, so I'm - * calling msn_process_ft_invite_msg(). If anybody adds support - * for anything else that sends a text/x-msmsgsinvite, perhaps - * this should be changed. For now, it stays. - */ - msn_process_ft_msg(ms, content); + if (servconn->fd != source) + servconn->fd = source; + + swboard->in_use = TRUE; + + gaim_debug(GAIM_DEBUG_INFO, "msn", "Connecting to switchboard...\n"); - } else if (!g_ascii_strncasecmp(content, "Content-Type: text/plain", - strlen("Content-Type: text/plain"))) { + if (msn_switchboard_is_invited(swboard)) { + g_snprintf(outparams, sizeof(outparams), "%s %s %s", + servconn->session->account->gc->username, + swboard->auth_key, swboard->session_id); - char *skiphead = strstr(msg, "\r\n\r\n"); - - if (!skiphead || !skiphead[4]) { - return; - } + if (!msn_switchboard_send_command(swboard, "ANS", outparams)) { + msn_switchboard_destroy(swboard); - skiphead += 4; - strip_linefeed(skiphead); - - if (format) { - message = g_strdup_printf("%s%s", format, skiphead); - } else { - message = g_strdup(skiphead); + return FALSE; } - - if (ms->chat) - serv_got_chat_in(ms->gc, gaim_chat_get_id(GAIM_CHAT(ms->chat)), - ms->msguser, flags, message, time(NULL)); - else - serv_got_im(ms->gc, ms->msguser, message, flags, time(NULL), -1); + } + else { + g_snprintf(outparams, sizeof(outparams), "%s %s", + servconn->session->account->gc->username, swboard->auth_key); - g_free(message); + if (!msn_switchboard_send_command(swboard, "USR", outparams)) { + msn_switchboard_destroy(swboard); + + return FALSE; + } } + + return TRUE; } static void -msn_switchboard_callback(gpointer data, gint source, GaimInputCondition cond) +__failed_read_cb(gpointer data, gint source, GaimInputCondition cond) +{ + MsnServConn *servconn = data; + + msn_switchboard_destroy(servconn->data); +} + +MsnSwitchBoard * +msn_switchboard_new(MsnSession *session) { - struct msn_switchboard *ms = data; - char buf[MSN_BUF_LEN]; - int cont = 1; - int len; - - ms->fd = source; - len = read(ms->fd, buf, sizeof(buf)); - if (len <= 0) { - msn_kill_switch(ms); - return; + MsnSwitchBoard *swboard; + MsnServConn *servconn; + + g_return_val_if_fail(session != NULL, NULL); + + swboard = g_new0(MsnSwitchBoard, 1); + + swboard->servconn = servconn = msn_servconn_new(session); + msn_servconn_set_connect_cb(servconn, __connect_cb); + msn_servconn_set_failed_read_cb(servconn, __failed_read_cb); + + servconn->data = swboard; + + session->switches = g_list_append(session->switches, swboard); + + if (switchboard_commands == NULL) { + /* Register the command callbacks. */ + msn_servconn_register_command(servconn, "ACK", __blank_cmd); + msn_servconn_register_command(servconn, "ANS", __ans_cmd); + msn_servconn_register_command(servconn, "BYE", __bye_cmd); + msn_servconn_register_command(servconn, "CAL", __blank_cmd); + msn_servconn_register_command(servconn, "IRO", __iro_cmd); + msn_servconn_register_command(servconn, "JOI", __joi_cmd); + msn_servconn_register_command(servconn, "MSG", __msg_cmd); + msn_servconn_register_command(servconn, "NAK", __nak_cmd); + msn_servconn_register_command(servconn, "NLN", __blank_cmd); + msn_servconn_register_command(servconn, "OUT", __out_cmd); + msn_servconn_register_command(servconn, "USR", __usr_cmd); + msn_servconn_register_command(servconn, "_unknown_", __unknown_cmd); + + /* Register the message type callbacks. */ + msn_servconn_register_msg_type(servconn, "text/plain", __plain_msg); + msn_servconn_register_msg_type(servconn, "text/x-msmsgscontrol", + __control_msg); + + /* Save these for future use. */ + switchboard_commands = servconn->commands; + switchboard_msg_types = servconn->msg_types; + } + else { + g_hash_table_destroy(servconn->commands); + g_hash_table_destroy(servconn->msg_types); + + servconn->commands = switchboard_commands; + servconn->msg_types = switchboard_msg_types; } - ms->rxqueue = g_realloc(ms->rxqueue, len + ms->rxlen); - memcpy(ms->rxqueue + ms->rxlen, buf, len); - ms->rxlen += len; - - while (cont) { - if (!ms->rxlen) - return; - - if (ms->msg) { - char *msg; - if (ms->msglen > ms->rxlen) - return; - msg = ms->rxqueue; - ms->rxlen -= ms->msglen; - if (ms->rxlen) { - ms->rxqueue = g_memdup(msg + ms->msglen, ms->rxlen); - } else { - ms->rxqueue = NULL; - msg = g_realloc(msg, ms->msglen + 1); - } - msg[ms->msglen] = 0; - ms->msglen = 0; - ms->msg = FALSE; - - msn_process_switch_msg(ms, msg); - - g_free(ms->msguser); - g_free(msg); - } else { - char *end = ms->rxqueue; - int cmdlen; - char *cmd; - int i = 0; - - while (i + 1 < ms->rxlen) { - if (*end == '\r' && end[1] == '\n') - break; - end++; i++; - } - if (i + 1 == ms->rxlen) - return; - - cmdlen = end - ms->rxqueue + 2; - cmd = ms->rxqueue; - ms->rxlen -= cmdlen; - if (ms->rxlen) { - ms->rxqueue = g_memdup(cmd + cmdlen, ms->rxlen); - } else { - ms->rxqueue = NULL; - cmd = g_realloc(cmd, cmdlen + 1); - } - cmd[cmdlen] = 0; - - gaim_debug(GAIM_DEBUG_MISC, "msn", "S: %s", cmd); - g_strchomp(cmd); - cont = msn_process_switch(ms, cmd); - - g_free(cmd); - } - } + return swboard; } void -msn_rng_connect(gpointer data, gint source, GaimInputCondition cond) +msn_switchboard_destroy(MsnSwitchBoard *swboard) { - struct msn_switchboard *ms = data; - struct gaim_connection *gc = ms->gc; - struct msn_data *md; - char buf[MSN_BUF_LEN]; + MsnSession *session; + + g_return_if_fail(swboard != NULL); + + session = swboard->servconn->session; - if (source == -1 || !g_slist_find(connections, gc)) { - close(source); - g_free(ms->sessid); - g_free(ms->auth); - g_free(ms); - return; - } + if (swboard->servconn->connected) + msn_switchboard_disconnect(swboard); + + if (swboard->user != NULL) + msn_user_unref(swboard->user); + + if (swboard->auth_key != NULL) + g_free(swboard->auth_key); - md = gc->proto_data; + if (swboard->session_id != NULL) + g_free(swboard->session_id); - if (ms->fd != source) - ms->fd = source; + session->switches = g_list_remove(session->switches, swboard); + + msn_servconn_destroy(swboard->servconn); - g_snprintf(buf, sizeof(buf), "ANS %u %s %s %s\r\n", ++ms->trId, gc->username, ms->auth, ms->sessid); - if (msn_write(ms->fd, buf, strlen(buf)) < 0) { - close(ms->fd); - g_free(ms->sessid); - g_free(ms->auth); - g_free(ms); - return; - } + g_free(swboard); +} - md->switches = g_slist_append(md->switches, ms); - ms->inpa = gaim_input_add(ms->fd, GAIM_INPUT_READ, - msn_switchboard_callback, ms); +void +msn_switchboard_set_user(MsnSwitchBoard *swboard, MsnUser *user) +{ + g_return_if_fail(swboard != NULL); + + swboard->user = user; + + msn_user_ref(user); } -static void -msn_ss_xfr_connect(gpointer data, gint source, GaimInputCondition cond) +MsnUser * +msn_switchboard_get_user(const MsnSwitchBoard *swboard) { - struct msn_switchboard *ms = data; - struct gaim_connection *gc = ms->gc; - char buf[MSN_BUF_LEN]; + g_return_val_if_fail(swboard != NULL, NULL); - if (source == -1 || !g_slist_find(connections, gc)) { - close(source); - if (g_slist_find(connections, gc)) { - msn_kill_switch(ms); - do_error_dialog(_("Gaim was unable to send an MSN message"), - _("Gaim encountered an error communicating with the " - "MSN switchboard server. Please try again later."), - GAIM_ERROR); - } + return swboard->user; +} - return; - } - - if (ms->fd != source) - ms->fd = source; - - g_snprintf(buf, sizeof(buf), "USR %u %s %s\r\n", - ++ms->trId, gc->username, ms->auth); +void +msn_switchboard_set_auth_key(MsnSwitchBoard *swboard, const char *key) +{ + g_return_if_fail(swboard != NULL); + g_return_if_fail(key != NULL); - if (msn_write(ms->fd, buf, strlen(buf)) < 0) { - g_free(ms->auth); - g_free(ms); - return; - } - - ms->inpa = gaim_input_add(ms->fd, GAIM_INPUT_READ, - msn_switchboard_callback, ms); + swboard->auth_key = g_strdup(key); } -struct msn_switchboard * -msn_find_switch(struct gaim_connection *gc, const char *username) +const char * +msn_switchboard_get_auth_key(const MsnSwitchBoard *swboard) { - struct msn_data *md = (struct msn_data *)gc->proto_data; - GSList *m = md->switches; + g_return_val_if_fail(swboard != NULL, NULL); - for (m = md->switches; m != NULL; m = m->next) { - struct msn_switchboard *ms = (struct msn_switchboard *)m->data; - - if (ms->total <= 1 && !gaim_utf8_strcasecmp(ms->user, username)) - return ms; - } - - return NULL; + return swboard->auth_key; } -struct msn_switchboard * -msn_find_switch_by_id(struct gaim_connection *gc, int chat_id) +void +msn_switchboard_set_session_id(MsnSwitchBoard *swboard, const char *id) { - struct msn_data *md = (struct msn_data *)gc->proto_data; - GSList *m; + g_return_if_fail(swboard != NULL); + g_return_if_fail(id != NULL); - for (m = md->switches; m != NULL; m = m->next) { - struct msn_switchboard *ms = (struct msn_switchboard *)m->data; + if (swboard->session_id != NULL) + g_free(swboard->session_id); - if (ms->chat && gaim_chat_get_id(GAIM_CHAT(ms->chat)) == chat_id) - return ms; - } - - return NULL; + swboard->session_id = g_strdup(id); } -struct msn_switchboard * -msn_find_writable_switch(struct gaim_connection *gc) +const char * +msn_switchboard_get_session_id(const MsnSwitchBoard *swboard) { - struct msn_data *md = (struct msn_data *)gc->proto_data; - GSList *m; - - for (m = md->switches; m != NULL; m = m->next) { - struct msn_switchboard *ms = (struct msn_switchboard *)m->data; + g_return_val_if_fail(swboard != NULL, NULL); - if (ms->txqueue != NULL) - return ms; - } - - return NULL; + return swboard->session_id; } void -msn_kill_switch(struct msn_switchboard *ms) +msn_switchboard_set_invited(MsnSwitchBoard *swboard, gboolean invited) +{ + g_return_if_fail(swboard != NULL); + + swboard->invited = invited; +} + +gboolean +msn_switchboard_is_invited(const MsnSwitchBoard *swboard) { - struct gaim_connection *gc = ms->gc; - struct msn_data *md = gc->proto_data; + g_return_val_if_fail(swboard != NULL, FALSE); + + return swboard->invited; +} - if (ms->inpa) - gaim_input_remove(ms->inpa); +gboolean +msn_switchboard_connect(MsnSwitchBoard *swboard, const char *server, int port) +{ + g_return_val_if_fail(swboard != NULL, FALSE); + + msn_servconn_set_server(swboard->servconn, server, port); + + if (msn_servconn_connect(swboard->servconn)) + swboard->in_use = TRUE; + + return swboard->in_use; +} - close(ms->fd); - g_free(ms->rxqueue); +void +msn_switchboard_disconnect(MsnSwitchBoard *swboard) +{ + g_return_if_fail(swboard != NULL); + g_return_if_fail(swboard->servconn->connected); + + msn_servconn_disconnect(swboard->servconn); + + swboard->in_use = FALSE; +} - if (ms->msg) g_free(ms->msguser); - if (ms->user) g_free(ms->user); - if (ms->sessid) g_free(ms->sessid); +gboolean +msn_switchboard_send_msg(MsnSwitchBoard *swboard, MsnMessage *msg) +{ + char *buf; + int ret; + + g_return_val_if_fail(swboard != NULL, FALSE); + g_return_val_if_fail(msg != NULL, FALSE); - g_free(ms->auth); + msn_message_set_transaction_id(msg, ++swboard->trId); + buf = msn_message_build_string(msg); - while (ms->txqueue) { - g_free(ms->txqueue->data); - ms->txqueue = g_slist_remove(ms->txqueue, ms->txqueue->data); + if (swboard->servconn->txqueue != NULL || !swboard->in_use) { + gaim_debug(GAIM_DEBUG_INFO, "msn", "Appending message to queue.\n"); + + swboard->servconn->txqueue = + g_slist_append(swboard->servconn->txqueue, buf); + + return TRUE; } - if (ms->chat) - serv_got_chat_left(gc, gaim_chat_get_id(GAIM_CHAT(ms->chat))); + ret = msn_servconn_write(swboard->servconn, buf, strlen(buf)); - md->switches = g_slist_remove(md->switches, ms); + g_free(buf); - g_free(ms); + return (ret > 0); } -struct msn_switchboard * -msn_switchboard_connect(struct gaim_connection *gc, const char *host, int port) +gboolean +msn_switchboard_send_command(MsnSwitchBoard *swboard, const char *command, + const char *params) { - struct msn_switchboard *ms; + char buf[MSN_BUF_LEN]; - if (host == NULL || port == 0) - return NULL; - - ms = msn_find_writable_switch(gc); + g_return_val_if_fail(swboard != NULL, FALSE); + g_return_val_if_fail(command != NULL, FALSE); - if (ms == NULL) - return NULL; + if (params == NULL) + g_snprintf(buf, sizeof(buf), "%s %u\r\n", command, + ++swboard->trId); + else + g_snprintf(buf, sizeof(buf), "%s %u %s\r\n", + command, ++swboard->trId, params); - if (proxy_connect(gc->account, (char *)host, port, msn_ss_xfr_connect, - ms) != 0) { - msn_kill_switch(ms); - - return NULL; - } - - return ms; + return (msn_servconn_write(swboard->servconn, buf, strlen(buf)) > 0); } diff -r 6aa785e55d0f -r e2e53316a21d src/protocols/msn/switchboard.h --- a/src/protocols/msn/switchboard.h Tue May 06 00:34:54 2003 +0000 +++ b/src/protocols/msn/switchboard.h Tue May 06 02:06:56 2003 +0000 @@ -3,7 +3,7 @@ * * gaim * - * Copyright (C) 2003, Christian Hammond + * Copyright (C) 2003 Christian Hammond * * 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 @@ -18,81 +18,164 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ #ifndef _MSN_SWITCHBOARD_H_ #define _MSN_SWITCHBOARD_H_ -struct msn_switchboard { - struct gaim_connection *gc; +typedef struct _MsnSwitchBoard MsnSwitchBoard; + +#include "servconn.h" +#include "msg.h" +#include "user.h" + +struct _MsnSwitchBoard +{ + MsnServConn *servconn; + MsnUser *user; + + char *auth_key; + char *session_id; + + gboolean invited; + struct gaim_conversation *chat; - int fd; - int inpa; + + gboolean in_use; - char *rxqueue; - int rxlen; + int total_users; + gboolean msg; - char *msguser; int msglen; - char *sessid; - char *auth; - guint32 trId; - int total; - char *user; - GSList *txqueue; + int chat_id; + int trId; }; /** - * Finds a switch with the given username. + * Creates a new switchboard. + * + * @param session The MSN session. * - * @param gc The gaim connection. - * @param username The username to search for. + * @return The new switchboard. + */ +MsnSwitchBoard *msn_switchboard_new(MsnSession *session); + +/** + * Destroys a switchboard. + * + * @param swboard The switchboard to destroy. + */ +void msn_switchboard_destroy(MsnSwitchBoard *swboard); + +/** + * Sets the user the switchboard is supposed to connect to. * - * @return The switchboard, if found. + * @param swboard The switchboard. + * @param user The user. */ -struct msn_switchboard *msn_find_switch(struct gaim_connection *gc, - const char *username); +void msn_switchboard_set_user(MsnSwitchBoard *swboard, MsnUser *user); + +/** + * Returns the user the switchboard is supposed to connect to. + * + * @param swboard The switchboard. + * + * @return The user. + */ +MsnUser *msn_switchboard_get_user(const MsnSwitchBoard *swboard); + +/** + * Sets the auth key the switchboard must use when connecting. + * + * @param swboard The switchboard. + * @param key The auth key. + */ +void msn_switchboard_set_auth_key(MsnSwitchBoard *swboard, const char *key); /** - * Finds a switchboard with the given chat ID. + * Returns the auth key the switchboard must use when connecting. * - * @param gc The gaim connection. - * @param chat_id The chat ID to search for. + * @param swboard The switchboard. * - * @return The switchboard, if found. + * @return The auth key. */ -struct msn_switchboard *msn_find_switch_by_id(struct gaim_connection *gc, - int chat_id); +const char *msn_switchboard_get_auth_key(const MsnSwitchBoard *swboard); + +/** + * Sets the session ID the switchboard must use when connecting. + * + * @param swboard The switchboard. + * @param id The session ID. + */ +void msn_switchboard_set_session_id(MsnSwitchBoard *swboard, const char *id); /** - * Finds the first writable switchboard. + * Returns the session ID the switchboard must use when connecting. + * + * @param swboard The switchboard. * - * @param gc The gaim connection. + * @return The session ID. + */ +const char *msn_switchboard_get_session_id(const MsnSwitchBoard *swboard); + +/** + * Sets whether or not the user was invited to this switchboard. * - * @return The first writable switchboard, if found. + * @param swboard The switchboard. + * @param invite @c TRUE if invited, @c FALSE otherwise. */ -struct msn_switchboard *msn_find_writable_switch(struct gaim_connection *gc); +void msn_switchboard_set_invited(MsnSwitchBoard *swboard, gboolean invited); + +/** + * Returns whether or not the user was invited to this switchboard. + * + * @param swboard The switchboard. + * + * @return @c TRUE if invited, @c FALSE otherwise. + */ +gboolean msn_switchboard_is_invited(const MsnSwitchBoard *swboard); /** * Connects to a switchboard. * - * @param gc The gaim connection. - * @param host The hostname. - * @param port The port. + * @param swboard The switchboard. + * @param server The server. + * @param port The port. * - * @return The new switchboard. + * @return @c TRUE if able to connect, or @c FALSE otherwise. */ -struct msn_switchboard *msn_switchboard_connect(struct gaim_connection *gc, - const char *host, int port); +gboolean msn_switchboard_connect(MsnSwitchBoard *swboard, + const char *server, int port); + +/** + * Disconnects from a switchboard. + * + * @param swboard The switchboard. + */ +void msn_switchboard_disconnect(MsnSwitchBoard *swboard); /** - * Kills a switchboard. + * Sends a message to a switchboard. * - * @param ms The switchboard to kill. + * @param swboard The switchboard. + * @param msg The message to send. + * + * @return @c TRUE if successful, or @c FALSE otherwise. */ -void msn_kill_switch(struct msn_switchboard *ms); +gboolean msn_switchboard_send_msg(MsnSwitchBoard *swboard, + MsnMessage *msg); -void msn_rng_connect(gpointer data, gint source, GaimInputCondition cond); +/** + * Sends a command to the switchboard. + * + * @param swboard The switchboard. + * @param command The command. + * @param params The parameters. + * + * @return @c TRUE if successful, or @c FALSE otherwise. + */ +gboolean msn_switchboard_send_command(MsnSwitchBoard *swboard, + const char *command, + const char *params); #endif /* _MSN_SWITCHBOARD_H_ */ diff -r 6aa785e55d0f -r e2e53316a21d src/protocols/msn/user.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/msn/user.c Tue May 06 02:06:56 2003 +0000 @@ -0,0 +1,227 @@ +/** + * @file user.c User functions + * + * gaim + * + * Copyright (C) 2003 Christian Hammond + * + * 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 + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "msn.h" +#include "user.h" + +MsnUser * +msn_user_new(MsnSession *session, const char *passport, const char *name) +{ + MsnUser *user; + + user = msn_users_find_with_passport(session->users, passport); + + if (user != NULL) { + if (name != NULL) + msn_user_set_name(user, name); + + msn_user_ref(user); + + return user; + } + + user = g_new0(MsnUser, 1); + + user->session = session; + + if (name != NULL) + msn_user_set_name(user, name); + + msn_user_set_passport(user, passport); + msn_user_set_group_id(user, -1); + + msn_users_add(session->users, user); + + msn_user_ref(user); + + return user; +} + +void +msn_user_destroy(MsnUser *user) +{ + g_return_if_fail(user != NULL); + + if (user->ref_count > 0) { + msn_user_unref(user); + + return; + } + + if (user->session != NULL && user->session->users != NULL) + msn_users_remove(user->session->users, user); + + if (user->passport != NULL) + g_free(user->passport); + + if (user->name != NULL) + g_free(user->name); + + g_free(user); +} + +MsnUser * +msn_user_ref(MsnUser *user) +{ + g_return_val_if_fail(user != NULL, NULL); + + user->ref_count++; + + return user; +} + +MsnUser * +msn_user_unref(MsnUser *user) +{ + g_return_val_if_fail(user != NULL, NULL); + + if (user->ref_count <= 0) + return NULL; + + user->ref_count--; + + if (user->ref_count == 0) { + msn_user_destroy(user); + + return NULL; + } + + return user; +} + +void +msn_user_set_passport(MsnUser *user, const char *passport) +{ + g_return_if_fail(user != NULL); + + if (user->passport != NULL) + g_free(user->passport); + + user->passport = g_strdup(passport); +} + +void +msn_user_set_name(MsnUser *user, const char *name) +{ + g_return_if_fail(user != NULL); + + if (user->name != NULL) + g_free(user->name); + + user->name = g_strdup(name); +} + +void +msn_user_set_group_id(MsnUser *user, int id) +{ + g_return_if_fail(user != NULL); + + user->group_id = id; +} + +const char * +msn_user_get_passport(const MsnUser *user) +{ + g_return_val_if_fail(user != NULL, NULL); + + return user->passport; +} + +const char * +msn_user_get_name(const MsnUser *user) +{ + g_return_val_if_fail(user != NULL, NULL); + + return user->name; +} + +int +msn_user_get_group_id(const MsnUser *user) +{ + g_return_val_if_fail(user != NULL, -1); + + return user->group_id; +} + +MsnUsers * +msn_users_new(void) +{ + MsnUsers *users = g_new0(MsnUsers, 1); + + return users; +} + +void +msn_users_destroy(MsnUsers *users) +{ + g_return_if_fail(users != NULL); + + while (users->users != NULL) + msn_user_destroy(users->users->data); + + /* See if we've leaked anybody. */ + while (users->users != NULL) { + gaim_debug(GAIM_DEBUG_WARNING, "msn", + "Leaking user %s\n", + msn_user_get_passport(users->users->data)); + } + + g_free(users); +} + +void +msn_users_add(MsnUsers *users, MsnUser *user) +{ + g_return_if_fail(users != NULL); + g_return_if_fail(user != NULL); + + users->users = g_list_append(users->users, user); +} + +void +msn_users_remove(MsnUsers *users, MsnUser *user) +{ + g_return_if_fail(users != NULL); + g_return_if_fail(user != NULL); + + users->users = g_list_remove(users->users, user); +} + +MsnUser * +msn_users_find_with_passport(MsnUsers *users, const char *passport) +{ + GList *l; + + g_return_val_if_fail(users != NULL, NULL); + g_return_val_if_fail(passport != NULL, NULL); + + for (l = users->users; l != NULL; l = l->next) { + MsnUser *user = (MsnUser *)l->data; + + if (user->passport != NULL && + !g_ascii_strcasecmp(passport, user->passport)) { + + return user; + } + } + + return NULL; +} diff -r 6aa785e55d0f -r e2e53316a21d src/protocols/msn/user.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/msn/user.h Tue May 06 02:06:56 2003 +0000 @@ -0,0 +1,184 @@ +/** + * @file user.h User functions + * + * gaim + * + * Copyright (C) 2003 Christian Hammond + * + * 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 + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _MSN_USER_H_ +#define _MSN_USER_H_ + +typedef struct _MsnUser MsnUser; +typedef struct _MsnUsers MsnUsers; + +#include "session.h" + +/** + * A user. + */ +struct _MsnUser +{ + MsnSession *session; /**< The MSN session. */ + + char *passport; /**< The passport account. */ + char *name; /**< The friendly name. */ + + int group_id; /**< The group ID. */ + + size_t ref_count; /**< The reference count. */ +}; + +/** + * A collection of users. + */ +struct _MsnUsers +{ + GList *users; /** The list of users. */ +}; + +/** + * Creates a new user structure. + * + * @param session The MSN session. + * @param passport The initial passport. + * @param name The initial friendly name. + * + * @return A new user structure. + */ +MsnUser *msn_user_new(MsnSession *session, const char *passport, + const char *name); + +/** + * Destroys a user structure. + * + * @param user The user to destroy. + */ +void msn_user_destroy(MsnUser *user); + +/** + * Increments the reference count on a user. + * + * @param user The user. + * + * @return @a user + */ +MsnUser *msn_user_ref(MsnUser *user); + +/** + * Decrements the reference count on a user. + * + * This will destroy the structure if the count hits 0. + * + * @param user The user. + * + * @return @a user, or @c NULL if the new count is 0. + */ +MsnUser *msn_user_unref(MsnUser *user); + +/** + * Sets the passport account for a user. + * + * @param user The user. + * @param passport The passport account. + */ +void msn_user_set_passport(MsnUser *user, const char *passport); + +/** + * Sets the friendly name for a user. + * + * @param user The user. + * @param name The friendly name. + */ +void msn_user_set_name(MsnUser *user, const char *name); + +/** + * Sets the group ID for a user. + * + * @param user The user. + * @param id The group ID. + */ +void msn_user_set_group_id(MsnUser *user, int id); + +/** + * Returns the passport account for a user. + * + * @param user The user. + * + * @return The passport account. + */ +const char *msn_user_get_passport(const MsnUser *user); + +/** + * Returns the friendly name for a user. + * + * @param user The user. + * + * @return The friendly name. + */ +const char *msn_user_get_name(const MsnUser *user); + +/** + * Returns the group ID for a user. + * + * @param user The user. + * + * @return The group ID. + */ +int msn_user_get_group_id(const MsnUser *user); + +/** + * Creates a new MsnUsers structure. + * + * @return A new MsnUsers structure. + */ +MsnUsers *msn_users_new(void); + +/** + * Destroys a users list. + * + * @param users The users list. + */ +void msn_users_destroy(MsnUsers *users); + +/** + * Adds a user to a users list. + * + * @param users The users list. + * @param user The user. + */ +void msn_users_add(MsnUsers *users, MsnUser *user); + +/** + * Removes a user from a users list. + * + * @param users The users list. + * @param user The user. + */ +void msn_users_remove(MsnUsers *users, MsnUser *user); + + +/** + * Finds a user with the specified passport. + * + * @param users A list of users. + * @param passport The passport. + * + * @return The user if found, or @c NULL otherwise. + */ +MsnUser *msn_users_find_with_passport(MsnUsers *users, const char *passport); + +#endif /* _MSN_USER_H_ */ diff -r 6aa785e55d0f -r e2e53316a21d src/protocols/msn/utils.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/msn/utils.c Tue May 06 02:06:56 2003 +0000 @@ -0,0 +1,132 @@ +/** + * @file utils.h Utility functions + * + * gaim + * + * Copyright (C) 2003 Christian Hammond + * + * 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 + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "msn.h" + +char * +msn_url_decode(const char *str) +{ + static char buf[MSN_BUF_LEN]; + int i, j = 0; + char *bum; + + g_return_val_if_fail(str != NULL, NULL); + + for (i = 0; i < strlen(str); i++) { + char hex[3]; + + if (str[i] != '%') + buf[j++] = str[i]; + else { + strncpy(hex, str + ++i, 2); + hex[2] = '\0'; + + /* i is pointing to the start of the number */ + i++; + + /* + * Now it's at the end and at the start of the for loop + * will be at the next character. + */ + buf[j++] = strtol(hex, NULL, 16); + } + } + + buf[j] = '\0'; + + if (!g_utf8_validate(buf, -1, (const char **)&bum)) + *bum = '\0'; + + return buf; +} + +char * +msn_url_encode(const char *str) +{ + static char buf[MSN_BUF_LEN]; + int i, j = 0; + + g_return_val_if_fail(str != NULL, NULL); + + for (i = 0; i < strlen(str); i++) { + if (isalnum(str[i])) + buf[j++] = str[i]; + else { + sprintf(buf + j, "%%%02x", (unsigned char)str[i]); + j += 3; + } + } + + buf[j] = '\0'; + + return buf; +} + +char * +msn_parse_format(const char *mime) +{ + char *cur; + GString *ret = g_string_new(NULL); + guint colorbuf; + char *colors = (char *)(&colorbuf); + + cur = strstr(mime, "FN="); + + if (cur && (*(cur = cur + 3) != ';')) { + ret = g_string_append(ret, ""); + } + + cur = strstr(mime, "EF="); + + if (cur && (*(cur = cur + 3) != ';')) { + while (*cur && *cur != ';') { + ret = g_string_append_c(ret, '<'); + ret = g_string_append_c(ret, *cur); + ret = g_string_append_c(ret, '>'); + cur++; + } + } + + cur = strstr(mime, "CO="); + + if (cur && (*(cur = cur + 3) != ';')) { + if (sscanf (cur, "%x;", &colorbuf) == 1) { + char tag[64]; + g_snprintf(tag, sizeof(tag), + "", + colors[0], colors[1], colors[2]); + + ret = g_string_append(ret, tag); + } + } + + cur = msn_url_decode(ret->str); + g_string_free(ret, TRUE); + + return cur; +} diff -r 6aa785e55d0f -r e2e53316a21d src/protocols/msn/utils.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/msn/utils.h Tue May 06 02:06:56 2003 +0000 @@ -0,0 +1,56 @@ +/** + * @file utils.h Utility functions + * + * gaim + * + * Copyright (C) 2003 Christian Hammond + * + * 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 + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _MSN_UTILS_H_ +#define _MSN_UTILS_H_ + +/** + * Decodes a URL into a plain string. + * + * This will change hex codes and such to their ascii equivalents. + * + * @param str The string to translate. + * + * @return The resulting string. + */ +char *msn_url_decode(const char *str); + +/** + * Encodes a URL into an escaped string. + * + * This will change non-alphanumeric characters to hex codes. + * + * @param str The string to translate. + * + * @return The resulting string. + */ +char *msn_url_encode(const char *str); + +/** + * Parses the MSN message formatting into a format compatible with Gaim. + * + * @param mime The mime header with the formatting. + * + * @return The new message. + */ +char *msn_parse_format(const char *mime); + +#endif /* _MSN_UTILS_H_ */