Mercurial > pidgin.yaz
changeset 4542:86b0a0243be8
[gaim-migrate @ 4821]
Split up the MSN module a bit and added file receive support.
Caution: I lost some data in a large file that was sent to me. Don't rely
on this yet. I'll get it all fixed tomorrow.
committer: Tailor Script <tailor@pidgin.im>
author | Christian Hammond <chipx86@chipx86.com> |
---|---|
date | Thu, 06 Feb 2003 10:13:18 +0000 |
parents | 0626ec2f2feb |
children | 8be012f3a3d6 |
files | src/protocols/msn/Makefile.am src/protocols/msn/ft.c src/protocols/msn/msg.c src/protocols/msn/msg.h src/protocols/msn/msn.c src/protocols/msn/msn.h src/protocols/msn/switchboard.c src/protocols/msn/switchboard.h |
diffstat | 8 files changed, 1395 insertions(+), 930 deletions(-) [+] |
line wrap: on
line diff
--- a/src/protocols/msn/Makefile.am Thu Feb 06 10:12:33 2003 +0000 +++ b/src/protocols/msn/Makefile.am Thu Feb 06 10:13:18 2003 +0000 @@ -2,7 +2,14 @@ pkgdir = $(libdir)/gaim -MSNSOURCES = msn.c +MSNSOURCES = \ + ft.c \ + msg.c \ + msg.h \ + msn.c \ + msn.h \ + switchboard.c \ + switchboard.h AM_CFLAGS = $(st)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/msn/ft.c Thu Feb 06 10:13:18 2003 +0000 @@ -0,0 +1,465 @@ +/** + * @file msn.c The MSN protocol plugin + * + * gaim + * + * Copyright (C) 2003, Christian Hammond <chipx86@gnupdate.org> + * + * 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" + +G_MODULE_IMPORT GSList *connections; + +static struct gaim_xfer * +find_xfer_by_cookie(struct gaim_connection *gc, unsigned long cookie) +{ + GSList *g; + struct msn_data *md = (struct msn_data *)gc->proto_data; + struct gaim_xfer *xfer = NULL; + struct msn_xfer_data *xfer_data; + + for (g = md->file_transfers; g != NULL; g = g->next) { + xfer = (struct gaim_xfer *)g->data; + xfer_data = (struct msn_xfer_data *)xfer->data; + + if (xfer_data->cookie == cookie) + break; + + xfer = NULL; + } + + return xfer; +} + +static void +msn_xfer_init(struct gaim_xfer *xfer) +{ + struct gaim_account *account; + struct msn_xfer_data *xfer_data; + struct msn_switchboard *ms; + char header[MSN_BUF_LEN]; + char buf[MSN_BUF_LEN]; + + account = gaim_xfer_get_account(xfer); + + ms = msn_find_switch(account->gc, xfer->who); + + xfer_data = (struct msn_xfer_data *)xfer->data; + + if (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE) { + /* + * NOTE: We actually have to wait for the next Invitation message + * before the transfer starts. We handle that in + * msn_xfer_start(). + */ + + g_snprintf(header, sizeof(header), + "MIME-Version: 1.0\r\n" + "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n\r\n" + "Invitation-Command: ACCEPT\r\n" + "Invitation-Cookie: %lu\r\n" + "Launch-Application: FALSE\r\n" + "Request-Data: IP-Address:\r\n", + (unsigned long)xfer_data->cookie); + + g_snprintf(buf, sizeof(buf), "MSG %u N %d\r\n%s\r\n\r\n", + ++ms->trId, strlen(header) + strlen("\r\n\r\n"), + header); + + if (msn_write(ms->fd, buf, strlen(buf)) < 0) { + msn_kill_switch(ms); + gaim_xfer_destroy(xfer); + + return; + } + } +} + +static void +msn_xfer_start(struct gaim_xfer *xfer) +{ + struct msn_xfer_data *xfer_data; + + xfer_data = (struct msn_xfer_data *)xfer->data; + + xfer_data->transferring = TRUE; + + if (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE) { + char sendbuf[MSN_BUF_LEN]; + + /* Send the TFR string to request the start of a transfer. */ + g_snprintf(sendbuf, sizeof(sendbuf), "TFR\r\n"); + + if (msn_write(xfer->fd, sendbuf, strlen(sendbuf)) < 0) { + gaim_xfer_cancel(xfer); + } + } +} + +static void +msn_xfer_end(struct gaim_xfer *xfer) +{ + struct gaim_account *account; + struct msn_xfer_data *xfer_data; + struct msn_data *md; + + account = gaim_xfer_get_account(xfer); + xfer_data = (struct msn_xfer_data *)xfer->data; + md = (struct msn_data *)account->gc->proto_data; + + if (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE) { + char sendbuf[MSN_BUF_LEN]; + + g_snprintf(sendbuf, sizeof(sendbuf), "BYE 16777989\r\n"); + + msn_write(xfer->fd, sendbuf, strlen(sendbuf)); + + md->file_transfers = g_slist_remove(md->file_transfers, xfer); + + g_free(xfer_data); + xfer->data = NULL; + } +} + +static void +msn_xfer_cancel(struct gaim_xfer *xfer) +{ + struct gaim_account *account; + struct msn_xfer_data *xfer_data; + struct msn_data *md; + + account = gaim_xfer_get_account(xfer); + xfer_data = (struct msn_xfer_data *)xfer->data; + md = (struct msn_data *)account->gc->proto_data; + + if (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE) { + md->file_transfers = g_slist_remove(md->file_transfers, xfer); + + g_free(xfer_data); + xfer->data = NULL; + } +} + +static size_t +msn_xfer_read(char **buffer, struct gaim_xfer *xfer) +{ + unsigned char header[3]; + size_t len, size; + + if (read(xfer->fd, header, sizeof(header)) < 3) { + gaim_xfer_set_completed(xfer, TRUE); + return 0; + } + + if (header[0] != 0) { + debug_printf("MSNFTP: Invalid header[0]: %d. Aborting.\n", + header[0]); + return 0; + } + + size = header[1] | (header[2] << 8); + + *buffer = g_new0(char, size); + + for (len = 0; + len < size; + len += read(xfer->fd, *buffer + len, size - len)) + ; + + if (len == 0) + gaim_xfer_set_completed(xfer, TRUE); + + return len; +} + +static size_t +msn_xfer_write(const char *buffer, size_t size, struct gaim_xfer *xfer) +{ + return 0; +} + +static int +msn_process_msnftp(struct gaim_xfer *xfer, gint source, const char *buf) +{ + struct msn_xfer_data *xfer_data; + struct gaim_account *account; + char sendbuf[MSN_BUF_LEN]; + + xfer_data = (struct msn_xfer_data *)xfer->data; + account = gaim_xfer_get_account(xfer); + + if (!g_strncasecmp(buf, "VER MSNFTP", 10)) { + /* Send the USR string */ + g_snprintf(sendbuf, sizeof(sendbuf), "USR %s %lu\r\n", + account->gc->username, + (unsigned long)xfer_data->authcookie); + + if (msn_write(source, sendbuf, strlen(sendbuf)) < 0) { + gaim_xfer_cancel(xfer); /* ? */ + + return 0; + } + } + else if (!g_strncasecmp(buf, "FIL", 3)) { + gaim_input_remove(xfer_data->inpa); + xfer_data->inpa = 0; + + gaim_xfer_start(xfer, source, NULL, 0); + } +#if 0 + char *tmp = buf; + + /* + * This data is the size, but we already have + * the size, so who cares. + */ + GET_NEXT(tmp); + + /* Send the TFR string to request the start of a transfer. */ + g_snprintf(sendbuf, sizeof(sendbuf), "TFR\r\n"); + + + if (msn_write(source, sendbuf, strlen(sendbuf)) < 0) { + gaim_xfer_cancel(xfer); + + return 0; + } +#endif + + return 1; +} + +static void +msn_msnftp_cb(gpointer data, gint source, GaimInputCondition cond) +{ + struct gaim_xfer *xfer; + struct msn_xfer_data *xfer_data; + char buf[MSN_BUF_LEN]; + gboolean cont = TRUE; + size_t len; + + xfer = (struct gaim_xfer *)data; + xfer_data = (struct msn_xfer_data *)xfer->data; + + len = read(source, buf, sizeof(buf)); + + if (len <= 0) { + gaim_xfer_cancel(xfer); + return; + } + + xfer_data->rxqueue = g_realloc(xfer_data->rxqueue, + len + xfer_data->rxlen); + memcpy(xfer_data->rxqueue + xfer_data->rxlen, buf, len); + xfer_data->rxlen += len; + + while (cont) { + char *end = xfer_data->rxqueue; + char *cmd; + int cmdlen; + int i = 0; + + if (!xfer_data->rxlen) + return; + + for (i = 0; i < xfer_data->rxlen - 1; end++, i++) { + if (*end == '\r' && *(end + 1) == '\n') + break; + } + + if (i == xfer_data->rxlen - 1) + return; + + cmdlen = end - xfer_data->rxqueue + 2; + cmd = xfer_data->rxqueue; + + xfer_data->rxlen -= cmdlen; + + if (xfer_data->rxlen) + xfer_data->rxqueue = g_memdup(cmd + cmdlen, xfer_data->rxlen); + else { + xfer_data->rxqueue = NULL; + cmd = g_realloc(cmd, cmdlen + 1); + } + + cmd[cmdlen] = '\0'; + + g_strchomp(cmd); + + cont = msn_process_msnftp(xfer, source, cmd); + + g_free(cmd); + } +} + +static void +msn_msnftp_connect(gpointer data, gint source, GaimInputCondition cond) +{ + struct gaim_account *account; + struct gaim_xfer *xfer; + struct msn_xfer_data *xfer_data; + char buf[MSN_BUF_LEN]; + + xfer = (struct gaim_xfer *)data; + account = gaim_xfer_get_account(xfer); + xfer_data = (struct msn_xfer_data *)xfer->data; + + if (source == -1 || !g_slist_find(connections, account->gc)) { + debug_printf("MSNFTP: Error establishing connection\n"); + close(source); + + gaim_xfer_cancel(xfer); + + return; + } + + g_snprintf(buf, sizeof(buf), "VER MSNFTP\r\n"); + + if (msn_write(source, buf, strlen(buf)) < 0) { + gaim_xfer_cancel(xfer); + return; + } + + xfer_data->inpa = gaim_input_add(source, GAIM_INPUT_READ, + msn_msnftp_cb, xfer); +} + +void +msn_process_ft_msg(struct msn_switchboard *ms, char *msg) +{ + struct gaim_xfer *xfer; + struct msn_xfer_data *xfer_data; + struct msn_data *md = ms->gc->proto_data; + char *tmp = msg; + + if (strstr(msg, "Application-GUID: " MSN_FT_GUID) && + strstr(msg, "Invitation-Command: INVITE")) { + + /* + * First invitation message, requesting an ACCEPT or CANCEL from + * the recipient. Used in incoming file transfers. + */ + + char *filename; + char *cookie_s, *filesize_s; + + tmp = strstr(msg, "Invitation-Cookie"); + GET_NEXT(tmp); + cookie_s = tmp; + GET_NEXT(tmp); + GET_NEXT(tmp); + filename = tmp; + + /* Needed for filenames with spaces */ + tmp = strchr(tmp, '\r'); + *tmp = '\0'; + tmp += 2; + + GET_NEXT(tmp); + filesize_s = tmp; + GET_NEXT(tmp); + + /* Setup the MSN-specific file transfer data */ + xfer_data = g_new0(struct msn_xfer_data, 1); + xfer_data->cookie = atoi(cookie_s); + xfer_data->transferring = FALSE; + + /* Build the file transfer handle. */ + xfer = gaim_xfer_new(ms->gc->account, GAIM_XFER_RECEIVE, ms->msguser); + xfer->data = xfer_data; + + /* Set the info about the incoming file. */ + gaim_xfer_set_filename(xfer, filename); + gaim_xfer_set_size(xfer, atoi(filesize_s)); + + /* Setup our I/O op functions */ + gaim_xfer_set_init_fnc(xfer, msn_xfer_init); + gaim_xfer_set_start_fnc(xfer, msn_xfer_start); + gaim_xfer_set_end_fnc(xfer, msn_xfer_end); + gaim_xfer_set_cancel_fnc(xfer, msn_xfer_cancel); + gaim_xfer_set_read_fnc(xfer, msn_xfer_read); + gaim_xfer_set_write_fnc(xfer, msn_xfer_write); + + /* Keep track of this transfer for later. */ + md->file_transfers = g_slist_append(md->file_transfers, xfer); + + /* Now perform the request */ + gaim_xfer_request(xfer); + } + else if (strstr(msg, "Invitation-Command: ACCEPT")) { + + /* + * XXX I hope these checks don't return false positives, but they + * seem like they should work. The only issue is alternative + * protocols, *maybe*. + */ + + if (strstr(msg, "AuthCookie:")) { + + /* + * Second invitation request, sent after the recipient accepts + * the request. Used in incoming file transfers. + */ + char *cookie_s, *ip, *port_s, *authcookie_s; + char ip_s[16]; + + tmp = strstr(msg, "Invitation-Cookie"); + GET_NEXT(tmp); + cookie_s = tmp; + GET_NEXT(tmp); + GET_NEXT(tmp); + ip = tmp; + GET_NEXT(tmp); + GET_NEXT(tmp); + port_s = tmp; + GET_NEXT(tmp); + GET_NEXT(tmp); + authcookie_s = tmp; + GET_NEXT(tmp); + + xfer = find_xfer_by_cookie(ms->gc, atoi(cookie_s)); + + if (xfer == NULL) + { + debug_printf("MSNFTP : Cookie not found. " + "File transfer aborted.\n"); + return; + } + + xfer_data = (struct msn_xfer_data *)xfer->data; + xfer_data->authcookie = atol(authcookie_s); + + strncpy(ip_s, ip, sizeof(ip_s)); + + if (proxy_connect(ip_s, atoi(port_s), + msn_msnftp_connect, xfer) != 0) { + + gaim_xfer_cancel(xfer); + + return; + } + } + else + { + /* + * An accept message from the recipient. Used in outgoing + * file transfers. + */ + } + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/msn/msg.c Thu Feb 06 10:13:18 2003 +0000 @@ -0,0 +1,32 @@ +/** + * @file msg.c Message functions + * + * gaim + * + * Copyright (C) 2003, Christian Hammond <chipx86@gnupdate.org> + * + * 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" + +int +msn_write(int fd, void *data, int len) +{ + debug_printf("MSN C: %s", (char *)data); + + return write(fd, data, len); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/msn/msg.h Thu Feb 06 10:13:18 2003 +0000 @@ -0,0 +1,37 @@ +/** + * @file msg.h Message functions + * + * gaim + * + * Copyright (C) 2003, Christian Hammond <chipx86@gnupdate.org> + * + * 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_MSG_H_ +#define _MSN_MSG_H_ + +/** + * Writes a message to the server. + * + * @param fd The file descriptor. + * @param data The data to write. + * @param len The length of the data + * + * @return The number of bytes written. + */ +int msn_write(int fd, void *data, int len); + +#endif /* _MSN_MSG_H_ */
--- a/src/protocols/msn/msn.c Thu Feb 06 10:12:33 2003 +0000 +++ b/src/protocols/msn/msn.c Thu Feb 06 10:13:18 2003 +0000 @@ -1,146 +1,37 @@ -#include "config.h" - -#ifndef _WIN32 -#include <unistd.h> -#else -#include <winsock.h> -#include <io.h> -#endif - - -#include <sys/stat.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <stdio.h> -#include <ctype.h> -#ifndef _WIN32 -#include <netdb.h> -#endif -#include "gaim.h" -#include "prpl.h" -#include "proxy.h" -#include "md5.h" - -#ifdef _WIN32 -#include "win32dep.h" -#include "stdint.h" -#endif +/** + * @file msn.c The MSN protocol plugin + * + * gaim + * + * Parts Copyright (C) 2003, Christian Hammond <chipx86@gnupdate.org> + * + * 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 "pixmaps/protocols/msn/msn_online.xpm" #include "pixmaps/protocols/msn/msn_away.xpm" #include "pixmaps/protocols/msn/msn_occ.xpm" + static struct prpl *my_protocol = NULL; /* for win32 compatability */ G_MODULE_IMPORT GSList *connections; -#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" -#define USEROPT_MSNPORT 4 -#define MSN_PORT 1863 - -#define MSN_TYPING_RECV_TIMEOUT 6 -#define MSN_TYPING_SEND_TIMEOUT 4 - -struct msn_file_transfer { - enum { MFT_SENDFILE_IN, MFT_SENDFILE_OUT } type; - struct file_transfer *xfer; - struct gaim_connection *gc; - - int fd; - int inpa; - - char *filename; - - char *sn; - char ip[16]; - int port; - - uint32_t cookie; - uint32_t authcookie; - - int len; - - char *rxqueue; - int rxlen; - gboolean msg; - char *msguser; - int msglen; -}; - -struct msn_data { - int fd; - uint32_t 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_switchboard { - struct gaim_connection *gc; - struct gaim_conversation *chat; - int fd; - int inpa; - - char *rxqueue; - int rxlen; - gboolean msg; - char *msguser; - int msglen; - - char *sessid; - char *auth; - uint32_t trId; - int total; - char *user; - GSList *txqueue; -}; - -struct msn_buddy { - char *user; - char *friend; -}; - static void msn_login_callback(gpointer, gint, GaimInputCondition); static void msn_login_xfr_connect(gpointer, gint, GaimInputCondition); @@ -151,13 +42,6 @@ struct file_transfer *xfer); #endif -#define GET_NEXT(tmp) while (*(tmp) && *(tmp) != ' ') \ - (tmp)++; \ - *(tmp)++ = 0; \ - while (*(tmp) && *(tmp) == ' ') \ - (tmp)++; - - static char *msn_normalize(const char *s) { static char buf[BUF_LEN]; @@ -169,55 +53,8 @@ return buf; } -static int msn_write(int fd, void *data, int len) -{ - debug_printf("MSN C: %s", (char *)data); - return write(fd, data, len); -} - -static char *url_decode(const char *msg) -{ - static char buf[MSN_BUF_LEN]; - int i, j = 0; - - 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; - - 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 char *handle_errcode(char *buf, gboolean show) +char * +handle_errcode(char *buf, gboolean show) { int errcode; static char msg[MSN_BUF_LEN]; @@ -353,6 +190,49 @@ return msg; } + +char *url_decode(const char *msg) +{ + static char buf[MSN_BUF_LEN]; + int i, j = 0; + + 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; + + 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]; @@ -394,702 +274,6 @@ } } -static struct msn_switchboard *msn_find_switch(struct gaim_connection *gc, char *id) -{ - struct msn_data *md = gc->proto_data; - GSList *m = md->switches; - - while (m) { - struct msn_switchboard *ms = m->data; - m = m->next; - if ((ms->total <= 1) && !g_strcasecmp(ms->user, id)) - return ms; - } - - return NULL; -} - -static struct msn_switchboard *msn_find_switch_by_id(struct gaim_connection *gc, int id) -{ - struct msn_data *md = gc->proto_data; - GSList *m = md->switches; - - while (m) { - struct msn_switchboard *ms = m->data; - m = m->next; - if (ms->chat && gaim_chat_get_id(GAIM_CHAT(ms->chat)) == id) - return ms; - } - - return NULL; -} - -static struct msn_switchboard *msn_find_writable_switch(struct gaim_connection *gc) -{ - struct msn_data *md = gc->proto_data; - GSList *m = md->switches; - - while (m) { - struct msn_switchboard *ms = m->data; - m = m->next; - if (ms->txqueue) - return ms; - } - - return NULL; -} - -static void msn_kill_switch(struct msn_switchboard *ms) -{ - struct gaim_connection *gc = ms->gc; - struct msn_data *md = gc->proto_data; - - if (ms->inpa) - gaim_input_remove(ms->inpa); - close(ms->fd); - g_free(ms->rxqueue); - if (ms->msg) - g_free(ms->msguser); - if (ms->sessid) - g_free(ms->sessid); - g_free(ms->auth); - if (ms->user) - g_free(ms->user); - while (ms->txqueue) { - g_free(ms->txqueue->data); - ms->txqueue = g_slist_remove(ms->txqueue, ms->txqueue->data); - } - if (ms->chat) - serv_got_chat_left(gc, gaim_chat_get_id(GAIM_CHAT(ms->chat))); - - md->switches = g_slist_remove(md->switches, ms); - - g_free(ms); -} - -static int msn_process_switch(struct msn_switchboard *ms, char *buf) -{ - struct gaim_connection *gc = ms->gc; - char sendbuf[MSN_BUF_LEN]; - static int id = 0; - - if (!g_strncasecmp(buf, "ACK", 3)) { - } else if (!g_strncasecmp(buf, "ANS", 3)) { - if (ms->chat) - gaim_chat_add_user(GAIM_CHAT(ms->chat), gc->username, NULL); - } else if (!g_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 = find_buddy(gc->account, user)) != NULL) - username = get_buddy_alias(b); - else - username = user; - - g_snprintf(msgbuf, sizeof(msgbuf), - _("%s has closed the conversation window"), username); - - 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_strncasecmp(buf, "CAL", 3)) { - } else if (!g_strncasecmp(buf, "IRO", 3)) { - char *tot, *user, *tmp = buf; - - 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_strncasecmp(buf, "JOI", 3)) { - char *user, *tmp = buf; - GET_NEXT(tmp); - user = tmp; - GET_NEXT(tmp); - - 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); - g_free(ms->txqueue->data); - ms->txqueue = g_slist_remove(ms->txqueue, ms->txqueue->data); - if (msn_write(ms->fd, sendbuf, strlen(sendbuf)) < 0) { - msn_kill_switch(ms); - return 0; - } - debug_printf("\n"); - } - } else if (!g_strncasecmp(buf, "MSG", 3)) { - char *user, *tmp = buf; - int length; - - GET_NEXT(tmp); - user = tmp; - - GET_NEXT(tmp); - - GET_NEXT(tmp); - length = atoi(tmp); - - ms->msg = TRUE; - ms->msguser = g_strdup(user); - ms->msglen = length; - } else if (!g_strncasecmp(buf, "NAK", 3)) { - do_error_dialog(_("An MSN message may not have been received."), NULL, GAIM_ERROR); - } else if (!g_strncasecmp(buf, "NLN", 3)) { - } else if (!g_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_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); - - if (!ms) - return 0; - - g_snprintf(sendbuf, sizeof(sendbuf), "CAL %u %s\r\n", ++ms->trId, ms->user); - if (msn_write(ms->fd, sendbuf, strlen(sendbuf)) < 0) { - msn_kill_switch(ms); - return 0; - } - } else if (isdigit(*buf)) { - handle_errcode(buf, TRUE); - - if (atoi(buf) == 217) - msn_kill_switch(ms); - - } else { - debug_printf("Unhandled message!\n"); - } - - return 1; -} - -static char *msn_parse_format(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, "<FONT FACE=\""); - while (*cur && *cur != ';') { - ret = g_string_append_c(ret, *cur); - cur++; - } - 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), "<FONT COLOR=\"#%02hhx%02hhx%02hhx\">", 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 0 -static int msn_process_msnftp(struct msn_file_transfer *mft, char *buf) -{ - struct gaim_connection *gc = mft->gc; - char sendbuf[MSN_BUF_LEN]; - - if (!g_strncasecmp(buf, "VER MSNFTP", 10)) { - - /* Send the USR string. */ - g_snprintf(sendbuf, sizeof(sendbuf), "USR %s %lu\r\n", - gc->username, (unsigned long)mft->authcookie); - - if (msn_write(mft->fd, sendbuf, strlen(sendbuf)) < 0) { - /* TODO: Clean up */ - return 0; - } - } - else if (!g_strncasecmp(buf, "FIL", 3)) { - - char *tmp = buf; - - GET_NEXT(tmp); - - mft->len = atoi(tmp); - - /* Send the TFR string, to request a start of transfer. */ - g_snprintf(sendbuf, sizeof(sendbuf), "TFR\r\n"); - - gaim_input_remove(mft->inpa); - mft->inpa = 0; - - if (msn_write(mft->fd, sendbuf, strlen(sendbuf)) < 0) { - /* TODO: Clean up */ - return 0; - } - - if (transfer_in_do(mft->xfer, mft->fd, mft->filename, mft->len)) { - debug_printf("MSN: transfer_in_do failed\n"); - } - } - - return 1; -} - -static void msn_msnftp_callback(gpointer data, gint source, - GaimInputCondition cond) -{ - struct msn_file_transfer *mft = (struct msn_file_transfer *)data; - char buf[MSN_BUF_LEN]; - int cont = 1; - int len; - - mft->fd = source; - - len = read(mft->fd, buf, sizeof(buf)); - - if (len <= 0) { - /* TODO: Kill mft. */ - return; - } - - mft->rxqueue = g_realloc(mft->rxqueue, len + mft->rxlen); - memcpy(mft->rxqueue + mft->rxlen, buf, len); - mft->rxlen += len; - - while (cont) { - char *end = mft->rxqueue; - int cmdlen; - char *cmd; - int i = 0; - - if (!mft->rxlen) - return; - - while (i + 1 < mft->rxlen) { - if (*end == '\r' && end[1] == '\n') - break; - end++; i++; - } - if (i + 1 == mft->rxlen) - return; - - cmdlen = end - mft->rxqueue + 2; - cmd = mft->rxqueue; - mft->rxlen -= cmdlen; - if (mft->rxlen) { - mft->rxqueue = g_memdup(cmd + cmdlen, mft->rxlen); - } else { - mft->rxqueue = NULL; - cmd = g_realloc(cmd, cmdlen + 1); - } - cmd[cmdlen] = 0; - - g_strchomp(cmd); - cont = msn_process_msnftp(mft, cmd); - - g_free(cmd); - } -} - -static void msn_msnftp_connect(gpointer data, gint source, - GaimInputCondition cond) -{ - struct msn_file_transfer *mft = (struct msn_file_transfer *)data; - struct gaim_connection *gc = mft->gc; - char buf[MSN_BUF_LEN]; - - if (source == -1 || !g_slist_find(connections, gc)) { - debug_printf("Error establishing MSNFTP connection\n"); - close(source); - /* TODO: Clean up */ - return; - } - - if (mft->fd != source) - mft->fd = source; - - g_snprintf(buf, sizeof(buf), "VER MSNFTP\r\n"); - - if (msn_write(mft->fd, buf, strlen(buf)) < 0) { - /* TODO: Clean up */ - return; - } - - mft->inpa = gaim_input_add(mft->fd, GAIM_INPUT_READ, - msn_msnftp_callback, mft); -} - -static void msn_process_ft_msg(struct msn_switchboard *ms, char *msg) -{ - struct msn_file_transfer *mft; - struct msn_data *md = ms->gc->proto_data; - char *tmp = msg; - - if (strstr(msg, "Application-GUID: {5D3E02AB-6190-11d3-BBBB-00C04F795683}") && - strstr(msg, "Invitation-Command: INVITE")) { - - /* - * First invitation message, requesting an ACCEPT or CANCEL from - * the recipient. Used in incoming file transfers. - */ - - char *filename; - char *cookie_s, *filesize_s; - size_t filesize; - - tmp = strstr(msg, "Invitation-Cookie"); - GET_NEXT(tmp); - cookie_s = tmp; - GET_NEXT(tmp); - GET_NEXT(tmp); - filename = tmp; - - /* Needed for filenames with spaces */ - tmp = strchr(tmp, '\r'); - *tmp = '\0'; - tmp += 2; - - GET_NEXT(tmp); - filesize_s = tmp; - GET_NEXT(tmp); - - mft = g_new0(struct msn_file_transfer, 1); - mft->gc = ms->gc; - mft->type = MFT_SENDFILE_IN; - mft->sn = g_strdup(ms->msguser); - mft->cookie = atoi(cookie_s); - mft->filename = g_strdup(filename); - - filesize = atoi(filesize_s); - - md->file_transfers = g_slist_append(md->file_transfers, mft); - - mft->xfer = transfer_in_add(ms->gc, ms->msguser, - mft->filename, filesize, 1, NULL); - } - else if (strstr(msg, "Invitation-Command: ACCEPT")) { - - /* - * XXX I hope these checks don't return false positives, but they - * seem like they should work. The only issue is alternative - * protocols, *maybe*. - */ - - if (strstr(msg, "AuthCookie:")) { - - /* - * Second invitation request, sent after the recipient accepts - * the request. Used in incoming file transfers. - */ - - char *cookie_s, *ip, *port_s, *authcookie_s; - - tmp = strstr(msg, "Invitation-Cookie"); - GET_NEXT(tmp); - cookie_s = tmp; - GET_NEXT(tmp); - GET_NEXT(tmp); - ip = tmp; - GET_NEXT(tmp); - GET_NEXT(tmp); - port_s = tmp; - GET_NEXT(tmp); - GET_NEXT(tmp); - authcookie_s = tmp; - GET_NEXT(tmp); - - mft = find_mft_by_cookie(ms->gc, atoi(cookie_s)); - - if (!mft) - { - debug_printf("MSN: Cookie not found. File transfer aborted.\n"); - return; - } - - strncpy(mft->ip, ip, 16); - mft->port = atoi(port_s); - mft->authcookie = atol(authcookie_s); - - if (proxy_connect(mft->ip, mft->port, msn_msnftp_connect, mft) != 0) { - md->file_transfers = g_slist_remove(md->file_transfers, mft); - return; - } - } - else - { - /* - * An accept message from the recipient. Used in outgoing - * file transfers. - */ - } - } -} -#endif - -static void msn_process_switch_msg(struct msn_switchboard *ms, char *msg) -{ - char *content, *agent, *format; - char *message = NULL; - int flags = 0; - - agent = strstr(msg, "User-Agent: "); - if (agent) { - if (!g_strncasecmp(agent, "User-Agent: Gaim", strlen("User-Agent: Gaim"))) - flags |= IM_FLAG_GAIMUSER; - } - - format = strstr(msg, "X-MMS-IM-Format: "); - if (format) { - format = msn_parse_format(format); - } else { - format = NULL; - } - - content = strstr(msg, "Content-Type: "); - if (!content) - return; - if (!g_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; - } - - } else if (!g_strncasecmp(content, "Content-Type: text/x-msmsgsinvite;", - strlen("Content-Type: text/x-msmsgsinvite;"))) { - -#if 0 - /* - * 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); -#endif - - } else if (!g_strncasecmp(content, "Content-Type: text/plain", - strlen("Content-Type: text/plain"))) { - - - - char *skiphead; - skiphead = strstr(msg, "\r\n\r\n"); - if (!skiphead || !skiphead[4]) { - return; - } - skiphead += 4; - strip_linefeed(skiphead); - - if (format) { - message = g_strdup_printf("%s%s", format, skiphead); - } else { - message = g_strdup(skiphead); - } - - 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); - - g_free(message); - } -} - -static void msn_switchboard_callback(gpointer data, gint source, GaimInputCondition cond) -{ - 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; - } - - 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; - - debug_printf("MSN S: %s", cmd); - g_strchomp(cmd); - cont = msn_process_switch(ms, cmd); - - g_free(cmd); - } - } -} - -static void msn_rng_connect(gpointer data, gint source, GaimInputCondition cond) -{ - struct msn_switchboard *ms = data; - struct gaim_connection *gc = ms->gc; - struct msn_data *md; - char buf[MSN_BUF_LEN]; - - if (source == -1 || !g_slist_find(connections, gc)) { - close(source); - g_free(ms->sessid); - g_free(ms->auth); - g_free(ms); - return; - } - - md = gc->proto_data; - - if (ms->fd != source) - ms->fd = source; - - 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; - } - - md->switches = g_slist_append(md->switches, ms); - ms->inpa = gaim_input_add(ms->fd, GAIM_INPUT_READ, msn_switchboard_callback, ms); -} - -static void msn_ss_xfr_connect(gpointer data, gint source, GaimInputCondition cond) -{ - struct msn_switchboard *ms = data; - struct gaim_connection *gc = ms->gc; - char buf[MSN_BUF_LEN]; - - 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; - } - - if (ms->fd != source) - ms->fd = source; - - g_snprintf(buf, sizeof(buf), "USR %u %s %s\r\n", ++ms->trId, gc->username, ms->auth); - 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); -} - struct msn_add_permit { struct gaim_connection *gc; char *user; @@ -1584,16 +768,15 @@ } if (switchboard) { - struct msn_switchboard *ms = msn_find_writable_switch(gc); - if (!ms) - return 1; + struct msn_switchboard *ms; GET_NEXT(tmp); - if (proxy_connect(host, port, msn_ss_xfr_connect, ms) != 0) { - msn_kill_switch(ms); + ms = msn_switchboard_connect(gc, host, port); + + if (ms == NULL) return 1; - } + ms->auth = g_strdup(tmp); } else { close(md->fd); @@ -2064,44 +1247,6 @@ } #if 0 -/* XXX Don't blame me. I stole this from the oscar module! */ -static struct msn_file_transfer *find_mft_by_xfer(struct gaim_connection *gc, - struct file_transfer *xfer) -{ - GSList *g = ((struct msn_data *)gc->proto_data)->file_transfers; - struct msn_file_transfer *f = NULL; - - while (g) { - f = (struct msn_file_transfer *)g->data; - if (f->xfer == xfer) - break; - - g = g->next; - f = NULL; - } - - return f; -} - -/* XXX Don't blame me. I stole this from the oscar module! */ -static struct msn_file_transfer *find_mft_by_cookie(struct gaim_connection *gc, - unsigned long cookie) -{ - GSList *g = ((struct msn_data *)gc->proto_data)->file_transfers; - struct msn_file_transfer *f = NULL; - - while (g) { - f = (struct msn_file_transfer *)g->data; - if (f->cookie == cookie) - break; - - g = g->next; - f = NULL; - } - - return f; -} - static void msn_file_transfer_cancel(struct gaim_connection *gc, struct file_transfer *xfer) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/msn/msn.h Thu Feb 06 10:13:18 2003 +0000 @@ -0,0 +1,152 @@ +/** + * @file msn.h The MSN protocol plugin + * + * gaim + * + * Copyright (C) 2003, Christian Hammond <chipx86@gnupdate.org> + * + * 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_H_ +#define _MSN_H_ + +#include "config.h" + +#ifndef _WIN32 +#include <unistd.h> +#else +#include <winsock.h> +#include <io.h> +#endif + + +#include <sys/stat.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <stdio.h> +#include <ctype.h> +#ifndef _WIN32 +#include <netdb.h> +#endif +#include "gaim.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" +#define USEROPT_MSNPORT 4 +#define MSN_PORT 1863 + +#define MSN_TYPING_RECV_TIMEOUT 6 +#define MSN_TYPING_SEND_TIMEOUT 4 + +#define MSN_FT_GUID "{5D3E02AB-6190-11d3-BBBB-00C04F795683}" + +#define GET_NEXT(tmp) \ + while (*(tmp) && *(tmp) != ' ' && *(tmp) != '\r') \ + (tmp)++; \ + *(tmp)++ = 0; \ + while (*(tmp) && *(tmp) == ' ') \ + (tmp)++; + + +struct msn_xfer_data { + int inpa; + + uint32_t cookie; + uint32_t authcookie; + + gboolean transferring; + + char *rxqueue; + int rxlen; + gboolean msg; + char *msguser; + int msglen; +}; + +struct msn_data { + int fd; + uint32_t 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); + +#endif /* _MSN_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/msn/switchboard.c Thu Feb 06 10:13:18 2003 +0000 @@ -0,0 +1,529 @@ +/** + * @file switchboard.c MSN switchboard functions + * + * gaim + * + * Copyright (C) 2003, Christian Hammond <chipx86@gnupdate.org> + * + * 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" + +static char * +msn_parse_format(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, "<FONT FACE=\""); + while (*cur && *cur != ';') { + ret = g_string_append_c(ret, *cur); + cur++; + } + 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), "<FONT COLOR=\"#%02hhx%02hhx%02hhx\">", colors[0], colors[1], colors[2]); + ret = g_string_append(ret, tag); + } + } + + cur = url_decode(ret->str); + g_string_free(ret, TRUE); + return cur; +} + +static int +msn_process_switch(struct msn_switchboard *ms, char *buf) +{ + struct gaim_connection *gc = ms->gc; + char sendbuf[MSN_BUF_LEN]; + static int id = 0; + + if (!g_strncasecmp(buf, "ACK", 3)) { + } else if (!g_strncasecmp(buf, "ANS", 3)) { + if (ms->chat) + gaim_chat_add_user(GAIM_CHAT(ms->chat), gc->username, NULL); + } else if (!g_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 = find_buddy(gc->account, user)) != NULL) + username = get_buddy_alias(b); + else + username = user; + + g_snprintf(msgbuf, sizeof(msgbuf), + _("%s has closed the conversation window"), username); + + 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_strncasecmp(buf, "CAL", 3)) { + } else if (!g_strncasecmp(buf, "IRO", 3)) { + char *tot, *user, *tmp = buf; + + 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_strncasecmp(buf, "JOI", 3)) { + char *user, *tmp = buf; + GET_NEXT(tmp); + user = tmp; + GET_NEXT(tmp); + + 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); + + g_free(ms->txqueue->data); + ms->txqueue = g_slist_remove(ms->txqueue, ms->txqueue->data); + + if (msn_write(ms->fd, sendbuf, strlen(sendbuf)) < 0) { + msn_kill_switch(ms); + return 0; + } + + debug_printf("\n"); + } + } else if (!g_strncasecmp(buf, "MSG", 3)) { + char *user, *tmp = buf; + int length; + + GET_NEXT(tmp); + user = tmp; + + GET_NEXT(tmp); + + GET_NEXT(tmp); + length = atoi(tmp); + + ms->msg = TRUE; + ms->msguser = g_strdup(user); + ms->msglen = length; + } else if (!g_strncasecmp(buf, "NAK", 3)) { + do_error_dialog(_("An MSN message may not have been received."), NULL, GAIM_ERROR); + } else if (!g_strncasecmp(buf, "NLN", 3)) { + } else if (!g_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_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); + + if (!ms) + return 0; + + g_snprintf(sendbuf, sizeof(sendbuf), "CAL %u %s\r\n", + ++ms->trId, ms->user); + + if (msn_write(ms->fd, sendbuf, strlen(sendbuf)) < 0) { + msn_kill_switch(ms); + return 0; + } + } else if (isdigit(*buf)) { + handle_errcode(buf, TRUE); + + if (atoi(buf) == 217) + msn_kill_switch(ms); + + } else { + debug_printf("Unhandled message!\n"); + } + + return 1; +} + +static void +msn_process_switch_msg(struct msn_switchboard *ms, char *msg) +{ + char *content, *agent, *format; + char *message = NULL; + int flags = 0; + + agent = strstr(msg, "User-Agent: "); + if (agent) { + if (!g_strncasecmp(agent, "User-Agent: Gaim", + strlen("User-Agent: Gaim"))) + flags |= IM_FLAG_GAIMUSER; + } + + format = strstr(msg, "X-MMS-IM-Format: "); + if (format) { + format = msn_parse_format(format); + } else { + format = NULL; + } + + content = strstr(msg, "Content-Type: "); + if (!content) + return; + if (!g_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; + } + + } else if (!g_strncasecmp(content, "Content-Type: text/x-msmsgsinvite;", + strlen("Content-Type: text/x-msmsgsinvite;"))) { + + /* + * 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); + + } else if (!g_strncasecmp(content, "Content-Type: text/plain", + strlen("Content-Type: text/plain"))) { + + char *skiphead = strstr(msg, "\r\n\r\n"); + + if (!skiphead || !skiphead[4]) { + return; + } + + skiphead += 4; + strip_linefeed(skiphead); + + if (format) { + message = g_strdup_printf("%s%s", format, skiphead); + } else { + message = g_strdup(skiphead); + } + + 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); + + g_free(message); + } +} + +static void +msn_switchboard_callback(gpointer data, gint source, GaimInputCondition cond) +{ + 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; + } + + 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; + + debug_printf("MSN S: %s", cmd); + g_strchomp(cmd); + cont = msn_process_switch(ms, cmd); + + g_free(cmd); + } + } +} + +void +msn_rng_connect(gpointer data, gint source, GaimInputCondition cond) +{ + struct msn_switchboard *ms = data; + struct gaim_connection *gc = ms->gc; + struct msn_data *md; + char buf[MSN_BUF_LEN]; + + if (source == -1 || !g_slist_find(connections, gc)) { + close(source); + g_free(ms->sessid); + g_free(ms->auth); + g_free(ms); + return; + } + + md = gc->proto_data; + + if (ms->fd != source) + ms->fd = source; + + 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; + } + + md->switches = g_slist_append(md->switches, ms); + ms->inpa = gaim_input_add(ms->fd, GAIM_INPUT_READ, + msn_switchboard_callback, ms); +} + +static void +msn_ss_xfr_connect(gpointer data, gint source, GaimInputCondition cond) +{ + struct msn_switchboard *ms = data; + struct gaim_connection *gc = ms->gc; + char buf[MSN_BUF_LEN]; + + 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; + } + + if (ms->fd != source) + ms->fd = source; + + g_snprintf(buf, sizeof(buf), "USR %u %s %s\r\n", + ++ms->trId, gc->username, ms->auth); + + 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); +} + +struct msn_switchboard * +msn_find_switch(struct gaim_connection *gc, const char *username) +{ + struct msn_data *md = (struct msn_data *)gc->proto_data; + GSList *m = md->switches; + + for (m = md->switches; m != NULL; m = m->next) { + struct msn_switchboard *ms = (struct msn_switchboard *)m->data; + + if (ms->total <= 1 && !g_strcasecmp(ms->user, username)) + return ms; + } + + return NULL; +} + +struct msn_switchboard * +msn_find_switch_by_id(struct gaim_connection *gc, int chat_id) +{ + 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; + + if (ms->chat && gaim_chat_get_id(GAIM_CHAT(ms->chat)) == chat_id) + return ms; + } + + return NULL; +} + +struct msn_switchboard * +msn_find_writable_switch(struct gaim_connection *gc) +{ + 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; + + if (ms->txqueue != NULL) + return ms; + } + + return NULL; +} + +void +msn_kill_switch(struct msn_switchboard *ms) +{ + struct gaim_connection *gc = ms->gc; + struct msn_data *md = gc->proto_data; + + if (ms->inpa) + gaim_input_remove(ms->inpa); + + close(ms->fd); + g_free(ms->rxqueue); + + if (ms->msg) g_free(ms->msguser); + if (ms->user) g_free(ms->user); + if (ms->sessid) g_free(ms->sessid); + + g_free(ms->auth); + + while (ms->txqueue) { + g_free(ms->txqueue->data); + ms->txqueue = g_slist_remove(ms->txqueue, ms->txqueue->data); + } + + if (ms->chat) + serv_got_chat_left(gc, gaim_chat_get_id(GAIM_CHAT(ms->chat))); + + md->switches = g_slist_remove(md->switches, ms); + + g_free(ms); +} + +struct msn_switchboard * +msn_switchboard_connect(struct gaim_connection *gc, const char *host, int port) +{ + struct msn_switchboard *ms; + + if (host == NULL || port == 0) + return NULL; + + ms = msn_find_writable_switch(gc); + + if (ms == NULL) + return NULL; + + if (proxy_connect((char *)host, port, msn_ss_xfr_connect, ms) != 0) { + msn_kill_switch(ms); + + return NULL; + } + + return ms; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/msn/switchboard.h Thu Feb 06 10:13:18 2003 +0000 @@ -0,0 +1,98 @@ +/** + * @file switchboard.h MSN switchboard functions + * + * gaim + * + * Copyright (C) 2003, Christian Hammond <chipx86@gnupdate.org> + * + * 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_SWITCHBOARD_H_ +#define _MSN_SWITCHBOARD_H_ + +struct msn_switchboard { + struct gaim_connection *gc; + struct gaim_conversation *chat; + int fd; + int inpa; + + char *rxqueue; + int rxlen; + gboolean msg; + char *msguser; + int msglen; + + char *sessid; + char *auth; + uint32_t trId; + int total; + char *user; + GSList *txqueue; +}; + +/** + * Finds a switch with the given username. + * + * @param gc The gaim connection. + * @param username The username to search for. + * + * @return The switchboard, if found. + */ +struct msn_switchboard *msn_find_switch(struct gaim_connection *gc, + const char *username); + +/** + * Finds a switchboard with the given chat ID. + * + * @param gc The gaim connection. + * @param chat_id The chat ID to search for. + * + * @return The switchboard, if found. + */ +struct msn_switchboard *msn_find_switch_by_id(struct gaim_connection *gc, + int chat_id); + +/** + * Finds the first writable switchboard. + * + * @param gc The gaim connection. + * + * @return The first writable switchboard, if found. + */ +struct msn_switchboard *msn_find_writable_switch(struct gaim_connection *gc); + +/** + * Connects to a switchboard. + * + * @param gc The gaim connection. + * @param host The hostname. + * @param port The port. + * + * @return The new switchboard. + */ +struct msn_switchboard *msn_switchboard_connect(struct gaim_connection *gc, + const char *host, int port); + +/** + * Kills a switchboard. + * + * @param ms The switchboard to kill. + */ +void msn_kill_switch(struct msn_switchboard *ms); + +void msn_rng_connect(gpointer data, gint source, GaimInputCondition cond); + +#endif /* _MSN_SWITCHBOARD_H_ */