diff -Naur gaim-0.68/configure.ac sscp_gaim/configure.ac --- gaim-0.68/configure.ac 2003-09-01 19:00:37.000000000 -0700 +++ sscp_gaim/configure.ac 2003-09-08 12:06:21.000000000 -0700 @@ -474,6 +474,7 @@ src/protocols/msn/Makefile src/protocols/napster/Makefile src/protocols/oscar/Makefile + src/protocols/sscp/Makefile src/protocols/toc/Makefile src/protocols/yahoo/Makefile src/protocols/zephyr/Makefile diff -Naur gaim-0.68/pixmaps/status/default/Makefile.am sscp_gaim/pixmaps/status/default/Makefile.am --- gaim-0.68/pixmaps/status/default/Makefile.am 2003-07-28 15:53:13.000000000 -0700 +++ sscp_gaim/pixmaps/status/default/Makefile.am 2003-09-08 12:06:21.000000000 -0700 @@ -24,6 +24,7 @@ notauthorized.png \ occupied.png \ offline.png \ + sscp.png \ trepia.png \ wireless.png \ yahoo.png diff -Naur gaim-0.68/src/protocols/Makefile.am sscp_gaim/src/protocols/Makefile.am --- gaim-0.68/src/protocols/Makefile.am 2003-07-16 07:28:26.000000000 -0700 +++ sscp_gaim/src/protocols/Makefile.am 2003-09-08 12:06:21.000000000 -0700 @@ -1,4 +1,4 @@ -DIST_SUBDIRS = gg irc jabber msn napster oscar toc yahoo zephyr +DIST_SUBDIRS = gg irc jabber msn napster oscar sscp toc yahoo zephyr if PRPLS diff -Naur gaim-0.68/src/protocols/sscp/Makefile.am sscp_gaim/src/protocols/sscp/Makefile.am --- gaim-0.68/src/protocols/sscp/Makefile.am 1969-12-31 16:00:00.000000000 -0800 +++ sscp_gaim/src/protocols/sscp/Makefile.am 2003-09-08 12:06:21.000000000 -0700 @@ -0,0 +1,16 @@ +pkgdir = $(libdir)/gaim + +SSCPSOURCES = sscp.c sol.c sscp.h + +AM_CFLAGS = $(st) + +libsscp_la_LDFLAGS = -module -avoid-version + +st = +pkg_LTLIBRARIES = libsscp.la +libsscp_la_SOURCES = $(SSCPSOURCES) + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src \ + $(GLIB_CFLAGS) \ + $(DEBUG_CFLAGS) diff -Naur gaim-0.68/src/protocols/sscp/sol.c sscp_gaim/src/protocols/sscp/sol.c --- gaim-0.68/src/protocols/sscp/sol.c 1969-12-31 16:00:00.000000000 -0800 +++ sscp_gaim/src/protocols/sscp/sol.c 2003-09-08 12:06:21.000000000 -0700 @@ -0,0 +1,336 @@ + +#undef DEBUG +#undef DEBUG_KEYSTREAM +#undef DEBUG_MOVEJOKER +#undef DEBUG_TRIPLECUT + + +#ifdef DEBUG +#include +#endif +#include + +#define DECKSIZE 54 + +static char abc[256]; +static int deck[10*DECKSIZE]; + +static int llength(int *s) +{ + int n; + + n = 0; + while (*s++) + n++; + return(n); +} + +static int lsearch(int *array, int item) +{ + int i; + + for (i = 0; array[i]; i++) + if (array[i] == item) + return(i); + return(-1); +} + +static void lappend(int *l, int item) +{ + while (*l) + l++; + *l++ = item; + *l++ = 0; +} + +static int lindex(int *s, int st) +{ + if (st < 0 || st >= llength(s)) + return(0); + return(s[st]); +} + +static void lrange_append(int *d, int *s, int st, int en) +{ + int i; + + if (st >= llength(s) || en < 0) + return; + if (st < 0) + st = 0; + if (en >= llength(s)) + en = llength(s)-1; + while (*d) + d++; + for (i = st; i <= en; i++) + *d++ = s[i]; + *d = 0; +} + +static void lcopy(int *d, int *s) +{ + while (*s) + *d++ = *s++; + *d = 0; +} + +static int findit(int c) +{ + return(lsearch(deck, c)); +} + + +#ifdef DEBUG +static void show_deck(char *msg, int *d) +{ + int n; + + printf("%s: len=%d\n", msg, llength(d)); + n = 0; + while (*d) { + printf("%d ", *d++); + } + putchar('\n'); + fflush(stdout); +} +#endif + +void sol_init() +{ + int c; + char *p; + + p = abc; + for (c = 'A'; c <= 'Z'; c++) + *p++ = c; + for (c = 'a'; c <= 'z'; c++) + *p++ = c; + for (c = '0'; c <= '9'; c++) + *p++ = c; + *p = 0; +} + +static int jokerA() +{ + return(llength(deck)-1); +} + +static int jokerB() +{ + return(llength(deck)); +} + +static void initDeck() +{ + int i; + + for (i = 0; i < DECKSIZE; i++) + deck[i] = i+1; + deck[DECKSIZE] = 0; +} + +static void moveJoker(int joker) +{ + int pos; + int nwDeck[10*DECKSIZE]; + + pos = findit(joker); + if (pos == llength(deck) - 1) { +#ifdef DEBUG_MOVEJOKER + printf("moveJoker(%d): pos (%d) == DECKSIZE - 1\n", joker, pos); +#endif + nwDeck[0] = deck[0]; + nwDeck[1] = 0; + lappend(nwDeck, joker); + lrange_append(nwDeck, deck, 1, llength(deck)-2); + } + else { + nwDeck[0] = 0; + lrange_append(nwDeck, deck, 0, pos-1); +#ifdef DEBUG_MOVEJOKER + show_deck("lrange_append 0, pos-1", nwDeck); +#endif + lappend(nwDeck, lindex(deck, pos+1)); +#ifdef DEBUG_MOVEJOKER + show_deck("lindex(deck, pos+1)", nwDeck); +#endif + lappend(nwDeck, joker); +#ifdef DEBUG_MOVEJOKER + show_deck("lappend(nwDeck, joker)", nwDeck); +#endif + lrange_append(nwDeck, deck, pos+2, llength(deck)-1); +#ifdef DEBUG_MOVEJOKER + show_deck("lrange_append pos+2, llength(deck)-1", nwDeck); +#endif + } + lcopy(deck, nwDeck); +} + +static void tripleCut() +{ + int posA, posB, i; + int nwDeck[10*DECKSIZE]; + + posA = findit(jokerA()); + posB = findit(jokerB()); +#ifdef DEBUG_TRIPLECUT + printf("tripleCut: findit(%d)=%d findit(%d)=%d\n", jokerA(), posA, jokerB(), posB); +#endif + if (posA > posB) { + i = posA; + posA = posB; + posB = i; + } +#ifdef DEBUG_TRIPLECUT + printf("tripleCut: posA=%d posB=%d\n", posA, posB); +#endif + nwDeck[0] = 0; + lrange_append(nwDeck, deck, posB+1, llength(deck)-1); + lrange_append(nwDeck, deck, posA, posB); + lrange_append(nwDeck, deck, 0, posA-1); + lcopy(deck,nwDeck); +} + + +static void countCut(int val) +{ + int cutPos; + int nwDeck[10*DECKSIZE]; + + if (val < 0) + cutPos = deck[llength(deck)-1] - 1; + else + cutPos = val; + if (cutPos == jokerB()) + cutPos = jokerA(); + nwDeck[0] = 0; + lrange_append(nwDeck, deck, cutPos + 1, llength(deck) - 2); + lrange_append(nwDeck, deck, 0, cutPos); + lappend(nwDeck, lindex(deck,llength(deck)-1)); + lcopy(deck, nwDeck); +} + +static int keyStream() +{ + int pos; + int card; + +#ifdef DEBUG_KEYSTREAM + show_deck("keyStream before:", deck); +#endif + // step 1 + moveJoker(jokerA()); +#ifdef DEBUG_KEYSTREAM + show_deck("keyStream JOKERA:", deck); +#endif + // step 2 + moveJoker(jokerB()); +#ifdef DEBUG_KEYSTREAM + show_deck("keyStream JOKERB:", deck); +#endif + moveJoker(jokerB()); +#ifdef DEBUG_KEYSTREAM + show_deck("keyStream JOKERB:", deck); +#endif + // step 3 + tripleCut(); +#ifdef DEBUG_KEYSTREAM + show_deck("keyStream tripleCut:", deck); +#endif + // step 4 + countCut(-1); +#ifdef DEBUG_KEYSTREAM + show_deck("keyStream countCut:", deck); +#endif + // step 5 + // "Find the output card. To do this, look at the top card. Convert it + // into a number from 1 to 53 in the same manner as step 4. Count down + // that many cards. (Count the top card as numer one.) Write the card + // after the one you counted to on a piece of paper; don't remove it + // from the deck. (If you hit a joker, don't write anything down and + // start over again with step 1)." + pos = deck[0]; + if (pos == jokerB()) + pos = jokerA(); + card = deck[pos]; + if (card == jokerA() || card == jokerB()) { +#ifdef DEBUG + printf("keyStream card=%d recurse\n", card); +#endif + card = keyStream(); + } + return(card); +} + + +static int c2n (int m) +{ + int pos; + + for (pos = 0; pos < strlen(abc); pos++) + if (abc[pos] == m) + return(pos); + return(0); +} + +static void shuffleDeck(char *passPhrase) +{ + char *p; + int n; + + for (p = passPhrase, n = 0; *p; p++, n++) { + (void)keyStream(); + countCut(c2n(*p)); +#ifdef DEBUG + printf("(***) shuffleDeck: char %d '%c'\n", n, *p); + show_deck("shuffleDeck", deck); +#endif + + } +} + +static int n2c(int c) +{ + return(abc[c]); +} + +void solitaire_encrypt(char *outbuf, char *msg, char *passPhrase) +{ + char *s, *p; + int m, k; + + initDeck(); +#ifdef DEBUG + show_deck("solitaire_encrypt initDeck", deck); +#endif + shuffleDeck(passPhrase); +#ifdef DEBUG + show_deck("solitaire_encrypt shuffleDeck done", deck); +#endif + p = outbuf; + for (s = msg; *s; s++) { + m = c2n(*s); + k = keyStream(); + *p++ = n2c((m + k) % strlen(abc)); + } + *p = 0; +} + +void solitaire_decrypt(char *outbuf, char *msg, char *passPhrase) +{ + char *s, *p; + int o, k; + + initDeck(); +#ifdef DEBUG + show_deck("solitaire_decrypt initDeck", deck); +#endif + shuffleDeck(passPhrase); + p = outbuf; + for (s = msg; *s; s++) { + o = c2n(*s); + k = keyStream(); + *p++ = n2c((o + strlen(abc) - k) % strlen(abc)); + } + *p = 0; +} diff -Naur gaim-0.68/src/protocols/sscp/sscp.c sscp_gaim/src/protocols/sscp/sscp.c --- gaim-0.68/src/protocols/sscp/sscp.c 1969-12-31 16:00:00.000000000 -0800 +++ sscp_gaim/src/protocols/sscp/sscp.c 2003-09-08 12:06:53.000000000 -0700 @@ -0,0 +1,1552 @@ +/* + * gaim - SSCP Confmgr Protocol Plugin + * + * David Beckemeyer + * + * 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 "internal.h" + +#include "account.h" +#include "accountopt.h" +#include "conversation.h" +#include "debug.h" +#include "multi.h" +#include "notify.h" +#include "prpl.h" +#include "proxy.h" +#include "util.h" + +/* XXX */ +#include "gaim.h" + +#include "sscp.h" + +typedef unsigned char u; + +extern void sol_init(); +extern void solitaire_encrypt(char *outbuf, char *msg, char *passPhrase); + +static long base64_encode (char *to, char *from, unsigned int len); +static char *base64_enc(const char *data, int len); +static void a(u * x, u * y, int o); +static void s(u * x); +static void r(u * x); +static void M(u * x, u * y); +static void h(char *x, u * y); +static char *p(u * x); +static char *sscp_dh(char *pubgen, char *key); +static void sscp_hdr_init(struct sscp_hdr *msg); +static void sscp_hdr_free(struct sscp_hdr *msg); +static char *strduptrim(char *s); +static void sscp_add_buddy(GaimConnection *gc, const char *name); +static void sscp_add_buddies(GaimConnection *gc, GList *buddies); +static void sscp_remove_buddy(GaimConnection *gc, const char *name, const char *group); +static void sscp_chat_leave(GaimConnection *gc, int id); +static int uidcmp(struct sscp_notify_rec *r1, char *who); +static void sscp_get_info(GaimConnection *gc, const char *who); +static void sscp_buddy_online(GaimConnection *gc, struct sscp_data *ndata, struct sscp_notify_rec *nrec); +static void sscp_buddy_offline(GaimConnection *gc, struct sscp_data *ndata, struct sscp_notify_rec *nrec); +static int notifycmp(struct sscp_notify_rec *r1, struct sscp_notify_rec *r2); +static void sscp_update_notify(GaimConnection *gc, struct sscp_data *ndata, GList *m); +static void sscp_process_notify(GaimConnection *gc, struct sscp_data *ndata); +static void sscp_process_pmsg(GaimConnection *gc, struct sscp_data *ndata); +static void sscp_process_msg(GaimConnection *gc, struct sscp_data *ndata); +static void sscp_process_oob(GaimConnection *gc, struct sscp_data *ndata, int rc); +static void sscp_process_info(GaimConnection *gc, struct sscp_data *ndata); +static int sscp_process_subresponse(GaimConnection *gc, struct sscp_data *ndata); +static void sscp_update_query(GaimConnection *gc, struct sscp_data *ndata, GList *m); +static void sscp_process_query(GaimConnection *gc, struct sscp_data *ndata); +static void sscp_clear_participants(struct sscp_data *ndata); +static int sscp_process_response(GaimConnection *gc, struct sscp_data *ndata); +static void sscp_process_line(struct sscp_data *ndata, gchar *inbuf); +static void sscp_parser(GaimConnection *gc, struct sscp_data *ndata, int(*process_func)(GaimConnection *, struct sscp_data *)); +static void sscp_message_callback(gpointer data, gint source, GaimInputCondition condition); +static void sscp_pmessage_connect(gpointer data, gint source, GaimInputCondition condition); +static void sscp_message_connect(gpointer data, gint source, GaimInputCondition condition); +static char *sscp_client_ip(struct sscp_data *ndata, char *who); +static void sscp_join_chat(GaimConnection *gc, GHashTable *data); +static void sscp_keepalive(GaimConnection *gc) ; +static int sscp_chat_send(GaimConnection *gc, int id, const char *message); +static int sscp_send_im(GaimConnection *gc, const char *who, const char *message, int len, GaimImFlags flags); +static void sscp_callback(gpointer data, gint source, GaimInputCondition condition); +static void sscp_login_connect(gpointer data, gint source, GaimInputCondition cond); +static void sscp_login(GaimAccount *account); +static void sscp_close(GaimConnection *gc); +static const char* sscp_list_icon(GaimAccount *a, struct buddy *b); +static void sscp_list_emblems(struct buddy *b, char **se, char **sw, char **nw, char **ne); +static GList *sscp_buddy_menu(GaimConnection *gc, const char *who); +static GList *sscp_chat_info(GaimConnection *gc); +static void init_plugin(GaimPlugin *plugin); + +//GSList *sscp_connections = NULL; + +static int last_id = 0; + +/* Usage: dh base exponent modulus */ +static u m[1024], g[1024], e[1024], b[1024]; +static int n, v, d, z, S; + +static char b64string[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +static long base64_encode (char *to, char *from, unsigned int len) +{ + char *fromp = from; + char *top = to; + unsigned char cbyte; + unsigned char obyte; + char end[3]; + + for (; len >= 3; len -= 3) { + cbyte = *fromp++; + *top++ = b64string[(int)(cbyte >> 2)]; + obyte = (cbyte << 4) & 0x30; /* 0011 0000 */ + + cbyte = *fromp++; + obyte |= (cbyte >> 4); /* 0000 1111 */ + *top++ = b64string[(int)obyte]; + obyte = (cbyte << 2) & 0x3C; /* 0011 1100 */ + + cbyte = *fromp++; + obyte |= (cbyte >> 6); /* 0000 0011 */ + *top++ = b64string[(int)obyte]; + *top++ = b64string[(int)(cbyte & 0x3F)];/* 0011 1111 */ + } + + if (len) { + end[0] = *fromp++; + if (--len) end[1] = *fromp++; else end[1] = 0; + end[2] = 0; + + cbyte = end[0]; + *top++ = b64string[(int)(cbyte >> 2)]; + obyte = (cbyte << 4) & 0x30; /* 0011 0000 */ + + cbyte = end[1]; + obyte |= (cbyte >> 4); + *top++ = b64string[(int)obyte]; + obyte = (cbyte << 2) & 0x3C; /* 0011 1100 */ + + if (len) *top++ = b64string[(int)obyte]; + else *top++ = 0; + *top++ = 0; + } + *top = 0; + return top - to; +} + +static char *base64_enc(const char *data, int len) +{ + char *out; + + out = g_malloc(len*4); + (void)base64_encode (out, data, len); + return(out); +} + +static void a(u * x, u * y, int o) +{ + d = 0; + for (v = S; v--;) { + d += x[v] + y[v] * o; + x[v] = d; + d = d >> 8; + } +} +static void s(u * x) +{ + for (v = 0; (v < S - 1) && (x[v] == m[v]);) + v++; + if (x[v] >= m[v]) + a(x, m, -1); +} +static void r(u * x) +{ + d = 0; + for (v = 0; v < S;) { + d |= x[v]; + x[v++] = d / 2; + d = (d & 1) << 8; + } +} +static void M(u * x, u * y) +{ + u X[1024], Y[1024]; + memmove(X, x, S); + memmove(Y, y, S); + memset(x, 0, S); + for (z = S * 8; z--;) { + if (X[S - 1] & 1) { + a(x, Y, 1); + s(x); + } + r(X); + a(Y, Y, 1); + s(Y); + } +} +static void h(char *x, u * y) +{ + u xx; + + memset(y, 0, S); + for (n = 0; x[n] > 0; n++) { + for (z = 4; z--;) + a(y, y, 1); + xx = x[n] | 32; + y[S - 1] |= xx - 48 - (xx > 96) * 39; + } +} +static char *p(u * x) +{ + char *buf; + char *g; + + buf = g_malloc(S*2+2); + for (n = 0; !x[n];) + n++; + g = buf; + for (; n < S; n++) { + *g++ = 48 + x[n] / 16 + (x[n] > 159) * 7; + *g++ = 48 + (x[n] & 15) + 7 * ((x[n] & 15) > 9); + } + *g = 0; + return(buf); +} + +static char *sscp_dh(char *pubgen, char *key) +{ + char *t; + + n = v = d = z = 0; + memset(m, 0, sizeof(m)); + memset(g, 0, sizeof(g)); + memset(e, 0, sizeof(e)); + memset(b, 0, sizeof(b)); + S = 51; + + t = g_strdup("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDAF"); + h(pubgen, g); + h(key, e); + h(t, m); + b[S - 1] = 1; + for (n = S * 8; n--;) { + if (e[S - 1] & 1) + M(b, g); + M(g, g); + r(e); + } + g_free(t); + return(p(b)); +} + +static void sscp_hdr_init(struct sscp_hdr *msg) +{ + msg->Status = 0; + msg->ConferenceID = 0; + msg->From = 0; + msg->To = 0; + msg->Subject = 0; + msg->Authenticate = 0; + msg->Cseq = 0; + msg->ContentLength = 0; +} + +static void sscp_hdr_free(struct sscp_hdr *msg) +{ + if (msg->Status) { + g_free(msg->Status); + msg->Status = 0; + } + if (msg->ConferenceID) { + g_free(msg->ConferenceID); + msg->ConferenceID = 0; + } + if (msg->From) { + g_free(msg->From); + msg->From = 0; + } + if (msg->To) { + g_free(msg->To); + msg->To = 0; + } + if (msg->Subject) { + g_free(msg->Subject); + msg->Subject = 0; + } + if (msg->Authenticate) { + g_free(msg->Authenticate); + msg->Authenticate = 0; + } + if (msg->Cseq) { + g_free(msg->Cseq); + msg->Cseq = 0; + } +} + + +static char *strduptrim(char *s) +{ + char *p, *r; + + while (*s && *s <= ' ') + s++; + p = g_strdup(s); + if (p) { + r = p; + while (*r && *r >= ' ') + r++; + *r = 0; + } + return(p); +} + + +static void sscp_add_buddy(GaimConnection *gc, const char *name) +{ +} + +static void sscp_add_buddies(GaimConnection *gc, GList *buddies) +{ +} + +static void sscp_remove_buddy(GaimConnection *gc, const char *name, const char *group) +{ +} + + +static void sscp_chat_leave(GaimConnection *gc, int id) +{ + GaimConversation *c = gaim_find_chat(gc, id); + struct sscp_data *ndata = gc->proto_data; + char *tmpbuf; + + if (!c) + return; + + ndata->con_state = CON_STATE_LEAVE; + tmpbuf = g_strdup_printf("LEAVE SSCP/1.0\r\nFrom: %s\r\n" + "Allow: 124,126\r\n" + "Authorization: %s\r\n" + "User-Agent: " USER_AGENT "\r\n\r\n", + gaim_account_get_username(gc->account), + ndata->AuthInfo); + write(ndata->fd, tmpbuf, strlen(tmpbuf)); + g_free(tmpbuf); + serv_got_chat_left(gc, id); + sscp_clear_participants(ndata); + g_free(ndata->confid); + ndata->confid = 0; +} + + +static int uidcmp(struct sscp_notify_rec *r1, char *who) +{ + return(strcmp(r1->uid, who)); +} + +static void sscp_get_info(GaimConnection *gc, const char *who) +{ + struct sscp_data *ndata = gc->proto_data; + GList *mm; + struct sscp_notify_rec *nrec; + GString *info; + char *str; + + mm = g_list_find_custom(ndata->members, who, (GCompareFunc)(uidcmp)); + if (mm) { + nrec = (struct sscp_notify_rec *)mm->data; + info = g_string_new(""); + g_string_append_printf(info, "User ID: %s
\n", nrec->uid); + g_string_append_printf(info, "Full name: %s
\n", nrec->fname); + g_string_append_printf(info, "IP: %s
\n", nrec->ip); + g_string_append_printf(info, "Client Info: %s
\n", nrec->hname); + str = g_string_free(info, FALSE); + g_show_info_text(gc, nrec->uid, 2, str, NULL); + g_free(str); + } +} + + +static void sscp_buddy_online(GaimConnection *gc, struct sscp_data *ndata, struct sscp_notify_rec *nrec) +{ + struct buddy *b; + + b = gaim_find_buddy(gc->account, nrec->uid); + if (b == NULL) { + struct group *g; + + g = gaim_find_group(_("Confmgr Users")); + if (g == NULL) { + g = gaim_group_new(_("Confmgr Users")); + gaim_blist_add_group(g, NULL); + } + b = gaim_buddy_new(gc->account, nrec->uid, nrec->fname); + gaim_blist_add_buddy(b, g, NULL); + } + serv_got_update(gc, nrec->uid, 1, 0, 0, 0, 0); +} + + +static void sscp_buddy_offline(GaimConnection *gc, struct sscp_data *ndata, struct sscp_notify_rec *nrec) +{ + struct buddy *b; + + serv_got_update(gc, nrec->uid, 0, 0, 0, 0, 0); + b = gaim_find_buddy(gc->account, nrec->uid); + if (b) { + gaim_blist_remove_buddy(b); + } +} + +static int notifycmp(struct sscp_notify_rec *r1, struct sscp_notify_rec *r2) +{ + return(strcmp(r1->uid, r2->uid)); +} + + +static void sscp_update_notify(GaimConnection *gc, struct sscp_data *ndata, GList *m) +{ + GList *mm; + struct sscp_notify_rec *nrec; + + switch (ndata->con_state) { + case CON_STATE_INFO: + case CON_STATE_JOIN: + case CON_STATE_QUERY: + case CON_STATE_LEAVE: + /* find new users */ + for (mm = m; mm; mm = g_list_next(mm)) { + if (!g_list_find_custom(ndata->members, mm->data, (GCompareFunc)(notifycmp))) { + nrec = (struct sscp_notify_rec *)mm->data; + sscp_buddy_online(gc, ndata, nrec); + } + } + /* find the users that aren't there anymore */ + for (mm = ndata->members; mm; mm = g_list_next(mm)) { + if (!g_list_find_custom(m, mm->data, (GCompareFunc)(notifycmp))) { + nrec = (struct sscp_notify_rec *)mm->data; + sscp_buddy_offline(gc, ndata, nrec); + } + } + } + /* free old list */ + for (mm = ndata->members; mm; mm = g_list_next(mm)) { + nrec = (struct sscp_notify_rec *)mm->data; + g_free(nrec->uid); + g_free(nrec->fname); + g_free(nrec->ip); + g_free(nrec->hname); + g_free(nrec); + } + if (ndata->members) g_list_free(ndata->members); + /* replace with new list */ + ndata->members = m; +} + +static void sscp_process_notify(GaimConnection *gc, struct sscp_data *ndata) +{ + GList *m = NULL; + char *cur, *end, *ustr; + char *uid, *fname, *ip, *hname; + struct sscp_notify_rec *nrec; + + for (cur = ndata->inbuf; cur < ndata->inbuf + ndata->msg.ContentLength && (end = strstr(cur, "\r\n")); cur = end + 2) { + *end = '\0'; + if (*cur) { + gaim_debug(GAIM_DEBUG_MISC, "sscp", "sscp_update_notify: %s\n", cur); + nrec = g_new0(struct sscp_notify_rec, 1); + ustr = strtok(cur, ":"); + ip = strtok(NULL, ":"); + hname = strtok(NULL, ":"); + uid = strtok(ustr, "("); + fname = strtok(NULL, ")"); + if (!uid) { + gaim_debug(GAIM_DEBUG_MISC, "sscp", "sscp_update_notify: uid=NULL\n"); + continue; + } + if (!fname) { + gaim_debug(GAIM_DEBUG_MISC, "sscp", "sscp_update_notify: fname=NULL\n"); + continue; + } + if (!ip) { + gaim_debug(GAIM_DEBUG_MISC, "sscp", "sscp_update_notify: ip=NULL\n"); + continue; + } + if (!hname) { + gaim_debug(GAIM_DEBUG_MISC, "sscp", "sscp_update_notify: hname=NULL\n"); + continue; + } + nrec->uid = g_strdup(uid); + nrec->fname = g_strdup(fname); + nrec->ip = g_strdup(ip); + nrec->hname = g_strdup(hname); + m = g_list_append(m, nrec); + } + } + sscp_update_notify(gc, ndata, m); +} + +static void sscp_process_pmsg(GaimConnection *gc, struct sscp_data *ndata) +{ + char *uid, *p, *msg, *end; + + if (!ndata->msg.From) { + gaim_debug(GAIM_DEBUG_MISC, "sscp", "sscp_process_pmsg: no From address\n"); + return; + } + uid = g_strdup(ndata->msg.From); + for (p = uid; *p == ' '; p++) + ; + while (*p > ' ') + p++; + *p = 0; + msg = g_malloc(ndata->msg.ContentLength+1); + memcpy(msg, ndata->inbuf, ndata->msg.ContentLength); + msg[ndata->msg.ContentLength] = 0; + for (p = msg, end = 0; *p; p++) { + if (*p < ' ') + end = p; + } + if (end) + *end = 0; + gaim_debug(GAIM_DEBUG_MISC, "sscp", "sscp_process_pmsg: uid='%s' msg='%s'\n", uid, msg); + serv_got_im(gc, uid, msg, 0, time(NULL), -1); + g_free(uid); + g_free(msg); +} + +static void sscp_process_msg(GaimConnection *gc, struct sscp_data *ndata) +{ + GaimConversation *c = gaim_find_conversation(ndata->confid); + char *uid, *p, *msg, *end; + + if (!s) { + gaim_debug(GAIM_DEBUG_MISC, "sscp", "sscp_process_msg: no conversation"); + return; + } + if (!ndata->msg.From) { + gaim_debug(GAIM_DEBUG_MISC, "sscp", "sscp_process_pmsg: no From address\n"); + return; + } + uid = g_strdup(ndata->msg.From); + for (p = uid; *p == ' '; p++) + ; + while (*p > ' ') + p++; + *p = 0; + msg = g_malloc(ndata->msg.ContentLength+1); + memcpy(msg, ndata->inbuf, ndata->msg.ContentLength); + msg[ndata->msg.ContentLength] = 0; + for (p = msg, end = 0; *p; p++) { + if (*p < ' ') + end = p; + } + if (end) + *end = 0; + gaim_debug(GAIM_DEBUG_MISC, "sscp", "sscp_process_msg: uid='%s' msg='%s'\n", uid, msg); + serv_got_chat_in(gc, gaim_chat_get_id(GAIM_CHAT(c)), uid, 0, msg, time(NULL)); + g_free(uid); + g_free(msg); +} + +static void sscp_process_invite(GaimConnection *gc, struct sscp_data *ndata) +{ + GHashTable *components = g_hash_table_new_full(g_str_hash, g_str_equal,g_free, g_free); + + if (!ndata->msg.From || !ndata->msg.ConferenceID) { + gaim_debug(GAIM_DEBUG_MISC, "sscp", "sscp_process_invite: missing required data\n"); + return; + } + g_hash_table_replace(components, g_strdup("conf"), g_strdup(ndata->msg.ConferenceID)); + serv_got_chat_invite(gc, ndata->msg.ConferenceID, ndata->msg.From, NULL, components); +} + +static void sscp_process_oob(GaimConnection *gc, struct sscp_data *ndata, int rc) +{ + gaim_debug(GAIM_DEBUG_MISC, "sscp", "con-state: %d Out-of-band: %s\n", ndata->con_state, ndata->msg.Status); + if (rc == 126) + sscp_process_notify(gc, ndata); + else if (rc == 124) + sscp_process_pmsg(gc, ndata); + else if (rc == 120) + sscp_process_msg(gc, ndata); + else if (rc == 122) + sscp_process_invite(gc, ndata); +} + +static void sscp_process_info(GaimConnection *gc, struct sscp_data *ndata) +{ + if (ndata->msg.ContentLength) { + gaim_debug(GAIM_DEBUG_MISC, "sscp", "INFO: (len=%d) %s\n", ndata->msg.ContentLength, ndata->inbuf); + } else { + gaim_debug(GAIM_DEBUG_MISC, "sscp", "INFO: Empty!"); + } +} + + +static int sscp_process_subresponse(GaimConnection *gc, struct sscp_data *ndata) +{ + int rc; + struct sscp_req *sr = (struct sscp_req *)ndata->data; + + if (ndata->sscp_state == SSCP_STATE_RESPLINE) { + gaim_debug(GAIM_DEBUG_MISC, "sscp", "Ignoring data between packets: %s\n", ndata); + sscp_hdr_init(&ndata->msg); + return 0; + } + if (ndata->msg.Status) gaim_debug(GAIM_DEBUG_MISC, "sscp", "sscp_process_subresponse: %d Status: %s\n", ndata->fd, ndata->msg.Status); + if (ndata->msg.ConferenceID) gaim_debug(GAIM_DEBUG_MISC, "sscp", "sscp_process_subresponse: %d ConferenceID: %s\n", ndata->fd, ndata->msg.ConferenceID); + if (ndata->msg.From) gaim_debug(GAIM_DEBUG_MISC, "sscp", "sscp_process_subresponse: %d From: %s\n", ndata->fd, ndata->msg.From); + if (ndata->msg.To) gaim_debug(GAIM_DEBUG_MISC, "sscp", "sscp_process_subresponse: %d To: %s\n", ndata->fd, ndata->msg.To); + if (ndata->msg.Subject) gaim_debug(GAIM_DEBUG_MISC, "sscp", "sscp_process_subresponse: %d Subject: %s\n", ndata->fd, ndata->msg.Subject); + if (ndata->msg.Authenticate) gaim_debug(GAIM_DEBUG_MISC, "sscp", "sscp_process_subresponse: %d Authenticate: %s\n", ndata->fd, ndata->msg.Authenticate); + if (ndata->msg.Cseq) gaim_debug(GAIM_DEBUG_MISC, "sscp", "sscp_process_subresponse: %d Cseq: %s\n", ndata->fd, ndata->msg.Cseq); + gaim_debug(GAIM_DEBUG_MISC, "sscp", "sscp_process_subresponse: %d ContentLength: %d\n", ndata->fd, ndata->msg.ContentLength); + + if (!ndata->msg.Status) { + gaim_debug(GAIM_DEBUG_MISC, "sscp", "sscp_process_subresponse: %d No Status line\n", ndata->fd); + gaim_connection_error(gc, "Error: No Status line"); + sscp_hdr_free(&ndata->msg); + sscp_hdr_init(&ndata->msg); + ndata->sscp_state = SSCP_STATE_RESPLINE; + return 0; + } + rc = atoi(ndata->msg.Status+8); + gaim_debug(GAIM_DEBUG_MISC, "sscp", "sscp_process_subresponse: %d con_state %d, Status code: %d\n", ndata->fd, ndata->con_state, rc); + if (rc < 200) { + sscp_hdr_free(&ndata->msg); + sscp_hdr_init(&ndata->msg); + ndata->sscp_state = SSCP_STATE_RESPLINE; + return 0; + } + if (rc != 200) { + gaim_notify_error(gc, NULL, _("Error sending message"), ndata->msg.Status); + } + sscp_hdr_free(&ndata->msg); + gaim_input_remove(sr->inpa); + close(ndata->fd); + g_free(sr->who); + g_free(sr->message); + g_free(sr); + g_free(ndata->inbuf); + g_free(ndata->AuthInfo); + g_free(ndata); + return 1; +} + +static void sscp_update_query(GaimConnection *gc, struct sscp_data *ndata, GList *m) +{ + GList *mm; + struct sscp_notify_rec *nrec; + GaimConversation *c; + + switch (ndata->con_state) { + case CON_STATE_QUERY: + /* find new users */ + for (mm = m; mm; mm = g_list_next(mm)) { + if (!g_list_find_custom(ndata->participants, mm->data, (GCompareFunc)(notifycmp))) { + nrec = (struct sscp_notify_rec *)mm->data; + c = gaim_find_conversation(ndata->confid); + gaim_chat_add_user(GAIM_CHAT(c), nrec->uid, NULL); + } + } + /* find the users that aren't there anymore */ + for (mm = ndata->participants; mm; mm = g_list_next(mm)) { + if (!g_list_find_custom(m, mm->data, (GCompareFunc)(notifycmp))) { + nrec = (struct sscp_notify_rec *)mm->data; + c = gaim_find_conversation(ndata->confid); + gaim_chat_remove_user(GAIM_CHAT(c), nrec->uid, NULL); + } + } + } + /* free old list */ + for (mm = ndata->participants; mm; mm = g_list_next(mm)) { + nrec = (struct sscp_notify_rec *)mm->data; + g_free(nrec->uid); + g_free(nrec->fname); + g_free(nrec->ip); + g_free(nrec->hname); + g_free(nrec); + } + if (ndata->participants) g_list_free(ndata->participants); + + /* replace with new list */ + ndata->participants = m; +} + +static void sscp_process_query(GaimConnection *gc, struct sscp_data *ndata) +{ + GList *m = NULL; + char *cur, *end, *ustr; + char *uid, *fname, *ip, *hname; + struct sscp_notify_rec *nrec; + + for (cur = ndata->inbuf; cur < ndata->inbuf + ndata->msg.ContentLength && (end = strstr(cur, "\r\n")); cur = end + 2) { + *end = '\0'; + if (*cur) { + gaim_debug(GAIM_DEBUG_MISC, "sscp", "sscp_process_query: %s\n", cur); + nrec = g_new0(struct sscp_notify_rec, 1); + ustr = strtok(cur, ":"); + ip = strtok(NULL, ":"); + hname = strtok(NULL, ":"); + uid = strtok(ustr, "("); + fname = strtok(NULL, ")"); + if (!uid) { + gaim_debug(GAIM_DEBUG_MISC, "sscp", "sscp_process_query: uid=NULL\n"); + continue; + } + if (!fname) { + gaim_debug(GAIM_DEBUG_MISC, "sscp", "sscp_process_query: fname=NULL\n"); + continue; + } + if (!ip) { + gaim_debug(GAIM_DEBUG_MISC, "sscp", "sscp_process_query: ip=NULL\n"); + continue; + } + if (!hname) { + gaim_debug(GAIM_DEBUG_MISC, "sscp", "sscp_process_query: hname=NULL\n"); + continue; + } + nrec->uid = g_strdup(uid); + nrec->fname = g_strdup(fname); + nrec->ip = g_strdup(ip); + nrec->hname = g_strdup(hname); + m = g_list_append(m, nrec); + } + } + sscp_update_query(gc, ndata, m); +} + +static void sscp_clear_participants(struct sscp_data *ndata) +{ + GList *mm; + struct sscp_notify_rec *nrec; + + for (mm = ndata->participants; mm; mm = g_list_next(mm)) { + nrec = (struct sscp_notify_rec *)mm->data; + g_free(nrec->uid); + g_free(nrec->fname); + g_free(nrec->ip); + g_free(nrec->hname); + g_free(nrec); + } + if (ndata->participants) g_list_free(ndata->participants); + ndata->participants = 0; +} + +static int sscp_process_response(GaimConnection *gc, struct sscp_data *ndata) +{ + int rc; + char *ts, *pubx, *x, *tmpbuf, *cred, *pw, *y; + GList *m; + GaimConversation *c; + + if (ndata->sscp_state == SSCP_STATE_RESPLINE) { + gaim_debug(GAIM_DEBUG_MISC, "sscp", "Ignoring data between packets: %s\n", ndata); + sscp_hdr_init(&ndata->msg); + return 0; + } + if (ndata->msg.Status) gaim_debug(GAIM_DEBUG_MISC, "sscp", "Status: %s\n", ndata->msg.Status); + if (ndata->msg.ConferenceID) gaim_debug(GAIM_DEBUG_MISC, "sscp", "ConferenceID: %s\n", ndata->msg.ConferenceID); + if (ndata->msg.From) gaim_debug(GAIM_DEBUG_MISC, "sscp", "From: %s\n",ndata->msg.From); + if (ndata->msg.To) gaim_debug(GAIM_DEBUG_MISC, "sscp", "To: %s\n", ndata->msg.To); + if (ndata->msg.Subject) gaim_debug(GAIM_DEBUG_MISC, "sscp", "Subject: %s\n", ndata->msg.Subject); + if (ndata->msg.Authenticate) gaim_debug(GAIM_DEBUG_MISC, "sscp", "Authenticate: %s\n", ndata->msg.Authenticate); + if (ndata->msg.Cseq) gaim_debug(GAIM_DEBUG_MISC, "sscp", "Cseq: %s\n", ndata->msg.Cseq); + gaim_debug(GAIM_DEBUG_MISC, "sscp", "ContentLength: %d\n", ndata->msg.ContentLength); + + if (!ndata->msg.Status) { + gaim_debug(GAIM_DEBUG_MISC, "sscp", "No Status line\n"); + gaim_connection_error(gc, "Error: No Status line"); + sscp_hdr_free(&ndata->msg); + sscp_hdr_init(&ndata->msg); + ndata->sscp_state = SSCP_STATE_RESPLINE; + return 0; + } + rc = atoi(ndata->msg.Status+8); + gaim_debug(GAIM_DEBUG_MISC, "sscp", "con_state %d, Status code: %d\n", ndata->con_state, rc); + + switch (ndata->con_state) { + case CON_STATE_JOIN: + if (rc == 200) { + ndata->confid = g_strdup(ndata->msg.ConferenceID); + tmpbuf = g_strdup_printf("QUERY SSCP/1.0\r\nFrom: %s\r\n" + "Allow: 120,124,126\r\n" + "Conference-ID: %s\r\n" + "Authorization: %s\r\n" + "User-Agent: " USER_AGENT "\r\n\r\n", + gaim_account_get_username(gc->account), + ndata->confid, ndata->AuthInfo); + write(ndata->fd, tmpbuf, strlen(tmpbuf)); + g_free(tmpbuf); + ndata->con_state = CON_STATE_QUERY; + serv_got_joined_chat(gc, last_id++, ndata->confid); + c = gaim_find_conversation(ndata->confid); + gaim_debug(GAIM_DEBUG_MISC, "sscp", "gaim_find_conversation(%s)=%x\n", ndata->confid, c); + if (c) { + if (ndata->msg.Subject) + gaim_chat_set_topic(GAIM_CHAT(c), ndata->confid, ndata->msg.Subject); + } + break; + } + else if (rc < 200) { + sscp_process_oob(gc, ndata, rc); + } else { + gaim_notify_error(gc, NULL, _("Cannot Join Conference"), ndata->msg.Status); + g_free(ndata->confid); + ndata->confid = 0; + goto resend_info; + } + break; + case CON_STATE_QUERY: + if (rc == 200) { + sscp_process_query(gc, ndata); + } + else if (rc < 200) { + sscp_process_oob(gc, ndata, rc); + } else { + c = gaim_find_conversation(ndata->confid); + if (c) + serv_got_chat_left(gc, gaim_chat_get_id(GAIM_CHAT(c))); + sscp_clear_participants(ndata); + g_free(ndata->confid); + ndata->confid = 0; + goto resend_info; + } + break; + case CON_STATE_LEAVE: + if (rc < 200) { + sscp_process_oob(gc, ndata, rc); + } else { + goto resend_info; + } + break; + + case CON_STATE_INFO: + if (rc == 200) { + sscp_process_info(gc, ndata); + } + else if (rc < 200) { + sscp_process_oob(gc, ndata, rc); + } else { + gaim_connection_error(gc, ndata->msg.Status); + /* FIXME: what else am I supposed to do here? */ + } + break; + case CON_STATE_TRYING: + if (rc < 200) { + gaim_debug(GAIM_DEBUG_MISC, "sscp", "con_state %d ignoring %s\n", ndata->con_state, ndata->msg.Status); + break; + } + if (rc != 401) { + gaim_debug(GAIM_DEBUG_MISC, "sscp", "unexpected: %s\n", ndata->msg.Status); + gaim_connection_error(gc, ndata->msg.Status); + /* FIXME: what else am I supposed to do here? */ + break; + } + /* Send Credentials */ + gaim_connection_update_progress(gc, "Encrypting Credentials", 2, 5); + ts = g_strdup_printf("%X%X%X", rand(), rand(), rand()); + gaim_debug(GAIM_DEBUG_MISC, "sscp", "my private key: %s\n", ts); + pubx = sscp_dh("3", ts); + gaim_debug(GAIM_DEBUG_MISC, "sscp", "pubx=%s\n", pubx); + x = sscp_dh(ndata->msg.Authenticate+3, ts); + gaim_debug(GAIM_DEBUG_MISC, "sscp", "shared secret: '%s'\n", x); + pw = gaim_account_get_password(gc->account); + if (!pw) { + gaim_connection_error(gc, "sscp login pw==NULL"); + break; + } + if (!*pw) { + gaim_connection_error(gc, "sscp login pw len=0"); + break; + } + y = base64_enc(pw, strlen(pw)); + gaim_debug(GAIM_DEBUG_MISC, "sscp", "encoded pw='%s'\n", y); + cred = g_malloc(2*strlen(pw)); + solitaire_encrypt(cred, y, x); + tmpbuf = g_strdup_printf("%s:%s:%s", pubx, + gaim_account_get_username(gc->account), cred); + g_free(cred); + cred = base64_enc(tmpbuf, strlen(tmpbuf)); + g_free(tmpbuf); + ndata->AuthInfo = g_strdup_printf("S2 %s", cred); + gaim_connection_update_progress(gc, "Sending Credentials", 3, 5); + tmpbuf = g_strdup_printf("INFO SSCP/1.0\r\nFrom: %s\r\n" + "Allow: 126\r\n" + "Authorization: %s\r\n" + "User-Agent: " USER_AGENT "\r\n\r\n", + gaim_account_get_username(gc->account), + ndata->AuthInfo); + write(ndata->fd, tmpbuf, strlen(tmpbuf)); + g_free(ts); + g_free(pubx); + g_free(x); + g_free(y); + g_free(tmpbuf); + g_free(cred); + ndata->con_state = CON_STATE_SENTAUTH; + break; + case CON_STATE_SENTAUTH: + if (rc < 200) { + sscp_process_oob(gc, ndata, rc); + break; + } + if (rc != 200) { + gaim_debug(GAIM_DEBUG_MISC, "sscp", "Error: %s\n", ndata->msg.Status); + gaim_connection_error(gc, ndata->msg.Status); + /* FIXME: what else am I supposed to do here? */ + break; + } + /* Success! */ + gaim_connection_update_progress(gc, "Connected", 4, 5); + gaim_connection_set_state(gc, GAIM_CONNECTED); + serv_finish_login(gc); + ndata->con_state = CON_STATE_INFO; + m = ndata->members; + ndata->members = 0; + sscp_update_notify(gc, ndata, m); + sscp_process_info(gc, ndata); +resend_info: + ndata->con_state = CON_STATE_INFO; + tmpbuf = g_strdup_printf("INFO SSCP/1.0\r\nFrom: %s\r\n" + "Allow: 122,124,126\r\n" + "Authorization: %s\r\n" + "User-Agent: " USER_AGENT "\r\n\r\n", + gaim_account_get_username(gc->account), + ndata->AuthInfo); + write(ndata->fd, tmpbuf, strlen(tmpbuf)); + g_free(tmpbuf); + break; + } + sscp_hdr_free(&ndata->msg); + sscp_hdr_init(&ndata->msg); + ndata->sscp_state = SSCP_STATE_RESPLINE; + return 0; +} + +static void sscp_process_line(struct sscp_data *ndata, gchar *inbuf) +{ + struct sscp_hdr *msg = &ndata->msg; + + if (!msg->Status) { + if (strncmp(inbuf, "SSCP/1.0", 8)) { + gaim_debug(GAIM_DEBUG_MISC, "sscp", "sscp_parse: ignoring extra data: %s\n", inbuf); + return; + } + msg->Status = strduptrim(inbuf); + ndata->sscp_state = SSCP_STATE_HEADER; + } else { + if (!strncasecmp(inbuf, "conference-id: ", 15)) { + msg->ConferenceID = strduptrim(inbuf+15); + return; + } + if (!strncasecmp(inbuf, "from: ", 6)) { + msg->From = strduptrim(inbuf+6); + return; + } + if (!strncasecmp(inbuf, "to: ", 4)) { + msg->To = strduptrim(inbuf+4); + return; + } + if (!strncasecmp(inbuf, "subject: ", 9)) { + msg->Subject = strduptrim(inbuf+9); + return; + } + if (!strncasecmp(inbuf, "authenticate: ", 14)) { + msg->Authenticate = strduptrim(inbuf+14); + return; + } + if (!strncasecmp(inbuf, "cseq: ", 6)) { + msg->Cseq = strduptrim(inbuf+6); + return; + } + if (!strncasecmp(inbuf, "content-length: ", 16)) { + msg->ContentLength = atoi(inbuf+16); + return; + } + gaim_debug(GAIM_DEBUG_MISC, "sscp", "sscp_parse: ignoring header %s\n", inbuf); + } +} + +static void sscp_parser(GaimConnection *gc, struct sscp_data *ndata, int(*process_func)(GaimConnection *, struct sscp_data *)) +{ + gchar *cur, *end; + int len; + + if (ndata->sscp_state == SSCP_STATE_BODY) { + len = ndata->msg.ContentLength - ndata->inbufused + 1; + gaim_debug(GAIM_DEBUG_MISC, "sscp", "BODY: %d bytes remaining", len); + } + else + len = SSCP_INITIAL_BUFSIZE; + if (ndata->inbuflen < ndata->inbufused + len) { + ndata->inbuflen += len; + ndata->inbuf = g_realloc(ndata->inbuf, ndata->inbuflen); + } + if ((len = read(ndata->fd, ndata->inbuf + ndata->inbufused, len-1)) < 0) { + gaim_connection_error(gc, "Read error"); + return; + } else if (len == 0) { + /* Remote closed the connection, probably */ + return; + } + ndata->inbufused += len; + ndata->inbuf[len] = '\0'; + +sscp_callback_body: + if (ndata->sscp_state == SSCP_STATE_BODY) { + len = ndata->msg.ContentLength; + if (ndata->inbufused >= len) { + if ((*process_func)(gc, ndata)) + return; + } + cur = ndata->inbuf+len; + ndata->inbufused -= len; + if (ndata->inbufused > 0) { + memmove(ndata->inbuf, cur, ndata->inbufused); + ndata->inbuf[ndata->inbufused] = '\0'; + gaim_debug(GAIM_DEBUG_MISC, "sscp", "remander: %d %d bytes after body: %s\n", ndata->fd, ndata->inbufused, ndata->inbuf); + } else { + ndata->inbufused = 0; + return; + } + } + for (cur = ndata->inbuf; cur < ndata->inbuf + ndata->inbufused && (end = strstr(cur, "\r\n")); cur = end + 2) { + *end = '\0'; + if (!*cur) { + /* end of headers reached */ + if (ndata->msg.ContentLength) { + ndata->sscp_state = SSCP_STATE_BODY; + gaim_debug(GAIM_DEBUG_MISC, "sscp", + "reading body %d len=%d cur=%x end=%x inbuf=%x inbufused=%d\n", ndata->fd, + ndata->msg.ContentLength, cur, end, ndata->inbuf, ndata->inbufused); + cur += 2; + break; + } else { + if ((*process_func)(gc, ndata)) + return; + } + } else { + sscp_process_line(ndata, cur); + } + } + if (cur != ndata->inbuf + ndata->inbufused) { /* leftover */ + gaim_debug(GAIM_DEBUG_MISC, "sscp", "remainder: %d cur=%x inbuf=%x inbuflen=%d inbufused=%d\n", ndata->fd, cur, ndata->inbuf, ndata->inbuflen, ndata->inbufused); + ndata->inbufused -= (cur - ndata->inbuf); + if (ndata->inbufused > 0 && ndata->inbufused < ndata->inbuflen) { + memmove(ndata->inbuf, cur, ndata->inbufused); + ndata->inbuf[ndata->inbufused] = '\0'; + gaim_debug(GAIM_DEBUG_MISC, "sscp", "remainder: %d left=%d (inbuflen=%d): %s\n", ndata->fd, ndata->inbufused, ndata->inbuflen, ndata->inbuf); + if (ndata->sscp_state == SSCP_STATE_BODY) + goto sscp_callback_body; + } + else { + ndata->inbufused = 0; + } + } else { + ndata->inbufused = 0; + } +} + + +static void sscp_message_callback(gpointer data, gint source, GaimInputCondition condition) +{ + struct sscp_req *sr = (struct sscp_req *)data; + GaimConnection *gc = sr->gc; + struct sscp_data *ndata = sr->ndata; + + ndata->data = sr; + sscp_parser(gc, ndata, sscp_process_subresponse); +} + + +static void sscp_pmessage_connect(gpointer data, gint source, GaimInputCondition condition) +{ + struct sscp_req *sr = (struct sscp_req *)data; + GaimConnection *gc = sr->gc; + struct sscp_data *ndata = sr->ndata; + char *body, *tmpbuf; + int len; + + if (!g_list_find(gaim_connections_get_all(), gc)) { + gaim_debug(GAIM_DEBUG_ERROR, "sscp", + "sscp_pmessage_connect: gaim_connections_get_all error\n"); + g_free(sr->who); + g_free(sr->message); + g_free(sr->ndata->AuthInfo); + g_free(sr->ndata); + g_free(sr); + return; + } + if (source == 0) { + g_free(sr->who); + g_free(sr->message); + g_free(sr->ndata->AuthInfo); + g_free(sr->ndata); + g_free(sr); + return; + } + sr->ndata->fd = source; + body = g_strdup_printf("%s\r\n", sr->message); + len = strlen(body); + tmpbuf = g_strdup_printf("MESSAGE SSCP/1.0\r\nFrom: %s\r\n" + "To: %s\r\n" + "Authorization: %s\r\n" + "User-Agent: " USER_AGENT "\r\n" + "Content-Length: %d\r\n\r\n%s", + gaim_account_get_username(gc->account), sr->who, + ndata->AuthInfo, len, body); + write(ndata->fd, tmpbuf, strlen(tmpbuf)); + g_free(tmpbuf); + g_free(body); + + ndata->con_state = CON_STATE_SUBREQ; + sr->inpa = gaim_input_add(source, GAIM_INPUT_READ, sscp_message_callback, sr); +} + +static void sscp_message_connect(gpointer data, gint source, GaimInputCondition condition) +{ + struct sscp_req *sr = (struct sscp_req *)data; + GaimConnection *gc = sr->gc; + struct sscp_data *ndata = sr->ndata; + char *body, *tmpbuf; + int len; + + if (!g_list_find(gaim_connections_get_all(), gc)) { + gaim_debug(GAIM_DEBUG_ERROR, "sscp", + "sscp_message_connect: gaim_connections_get_all error\n"); + g_free(sr->message); + g_free(sr->ndata->AuthInfo); + g_free(sr->ndata); + g_free(sr); + return; + } + if (source == 0) { + g_free(sr->message); + g_free(sr->ndata->AuthInfo); + g_free(sr->ndata); + g_free(sr); + return; + } + sr->ndata->fd = source; + body = g_strdup_printf("%s\r\n", sr->message); + len = strlen(body); + tmpbuf = g_strdup_printf("MESSAGE SSCP/1.0\r\nFrom: %s\r\n" + "Authorization: %s\r\n" + "User-Agent: " USER_AGENT "\r\n" + "Content-Length: %d\r\n\r\n%s", + gaim_account_get_username(gc->account), + ndata->AuthInfo, len, body); + write(ndata->fd, tmpbuf, strlen(tmpbuf)); + g_free(tmpbuf); + g_free(body); + + ndata->con_state = CON_STATE_SUBREQ; + sr->inpa = gaim_input_add(source, GAIM_INPUT_READ, sscp_message_callback, sr); +} + +static char *sscp_client_ip(struct sscp_data *ndata, char *who) +{ + GList *mm; + struct sscp_notify_rec *nrec; + + for (mm = ndata->members; mm; mm = g_list_next(mm)) { + nrec = (struct sscp_notify_rec *)mm->data; + if (!strcmp(nrec->uid, who)) + return(nrec->ip); + } + return("?"); +} + + +static void sscp_join_chat(GaimConnection *gc, GHashTable *data) +{ + struct sscp_data *ndata = gc->proto_data; + char *conf, *pw, *pwenc, *tmpbuf, *body; + int len; + + conf = g_hash_table_lookup(data, "conf"); + if (!conf) { + gaim_debug(GAIM_DEBUG_MISC, "sscp", "join: no conf!\n"); + return; + } + pw = g_hash_table_lookup(data, "passwd"); + if (!pw) + pw = ""; + + gaim_debug(GAIM_DEBUG_MISC, "sscp", "join: '%s' pw='%s'\n", conf, pw); + + if (ndata->con_state != CON_STATE_INFO) { + gaim_notify_error(gc, NULL, _("Cannot Join Conference"), _("Confmgr allows participation in only one conference at a time.")); + return; + } + ndata->confid = g_strdup(conf); + ndata->con_state = CON_STATE_JOIN; + body = g_strdup_printf("v=0\r\n" + "o==r\n" + "s=\r\n" + "c=IN IP4 %s\r\n" + "t=0 0\r\n" + "m=video 0 RTP/AVP 0\r\n", sscp_client_ip(ndata, gaim_account_get_username(gc->account))); + len = strlen(body); + if (*pw) { + pwenc = base64_enc(pw, strlen(pw)); + tmpbuf = g_strdup_printf("JOIN SSCP/1.0\r\nFrom: %s\r\n" + "Conference-ID: %s\r\n" + "Password: %s\r\n" + "Authorization: %s\r\n" + "User-Agent: " USER_AGENT "\r\n" + "Content-Length: %d\r\n\r\n%s", + gaim_account_get_username(gc->account), ndata->confid, pwenc, + ndata->AuthInfo, len, body); + g_free(pwenc); + } + else { + tmpbuf = g_strdup_printf("JOIN SSCP/1.0\r\nFrom: %s\r\n" + "Conference-Id: %s\r\n" + "Authorization: %s\r\n" + "User-Agent: " USER_AGENT "\r\n" + "Content-Length: %d\r\n\r\n%s", + gaim_account_get_username(gc->account), ndata->confid, + ndata->AuthInfo, len, body); + } + write(ndata->fd, tmpbuf, strlen(tmpbuf)); + g_free(tmpbuf); + g_free(body); +} + +static void sscp_keepalive(GaimConnection *gc) +{ + struct sscp_data *ndata = gc->proto_data; + char *tmpbuf; + + switch (ndata->con_state) { + case CON_STATE_INFO: + tmpbuf = g_strdup_printf("INFO SSCP/1.0\r\nFrom: %s\r\n" + "Allow: 122,124,126\r\n" + "Authorization: %s\r\n" + "User-Agent: " USER_AGENT "\r\n\r\n", + gaim_account_get_username(gc->account), + ndata->AuthInfo); + write(ndata->fd, tmpbuf, strlen(tmpbuf)); + g_free(tmpbuf); + break; + case CON_STATE_QUERY: + tmpbuf = g_strdup_printf("QUERY SSCP/1.0\r\nFrom: %s\r\n" + "Allow: 120,124,126\r\n" + "Conference-ID: %s\r\n" + "Authorization: %s\r\n" + "User-Agent: " USER_AGENT "\r\n\r\n", + gaim_account_get_username(gc->account), + ndata->confid, ndata->AuthInfo); + write(ndata->fd, tmpbuf, strlen(tmpbuf)); + g_free(tmpbuf); + break; + } +} + +static int sscp_chat_send(GaimConnection *gc, int id, const char *message) +{ + struct sscp_req *sr = g_new0(struct sscp_req, 1); + struct sscp_data *ndata = gc->proto_data; + + sr->gc = gc; + sr->message = g_strdup(message); + sr->ndata = g_new0(struct sscp_data, 1); + sr->ndata->con_state = CON_STATE_SUBCONNECT; + sr->ndata->AuthInfo = g_strdup(ndata->AuthInfo); + sr->ndata->data = sr; + + if (gaim_proxy_connect(gc->account, + gaim_account_get_string(gc->account, "server", CM_SERVER), + gaim_account_get_int(gc->account, "port", CM_PORT), + sscp_message_connect, sr) != 0) { + gaim_notify_error(gc, NULL, _("Unable to send message"), NULL); + g_free(sr->message); + g_free(sr->ndata); + g_free(sr->ndata->AuthInfo); + g_free(sr); + return -EINVAL; + + } + serv_got_chat_in(gc, id, gaim_account_get_username(gc->account), 0, message, time(NULL)); + return 0; +} + +static int sscp_send_im(GaimConnection *gc, const char *who, const char *message, int len, GaimImFlags flags) +{ + struct sscp_req *sr = g_new0(struct sscp_req, 1); + struct sscp_data *ndata = gc->proto_data; + + sr->gc = gc; + sr->who = g_strdup(who); + sr->message = g_strdup(message); + sr->flags = flags; + sr->ndata = g_new0(struct sscp_data, 1); + sr->ndata->con_state = CON_STATE_SUBCONNECT; + sr->ndata->AuthInfo = g_strdup(ndata->AuthInfo); + sr->ndata->data = sr; + + if (gaim_proxy_connect(gc->account, + gaim_account_get_string(gc->account, "server", CM_SERVER), + gaim_account_get_int(gc->account, "port", CM_PORT), + sscp_pmessage_connect, sr) != 0) { + gaim_notify_error(gc, NULL, _("Unable to send message"), NULL); + g_free(sr->who); + g_free(sr->message); + g_free(sr->ndata->AuthInfo); + g_free(sr->ndata); + g_free(sr); + } + return 1; +} + +static void sscp_callback(gpointer data, gint source, GaimInputCondition condition) +{ + GaimConnection *gc = data; + struct sscp_data *ndata = gc->proto_data; + + sscp_parser(gc, ndata, sscp_process_response); +} + + +/* 002 - MSG_CLIENT_LOGIN */ +static void sscp_login_connect(gpointer data, gint source, GaimInputCondition cond) +{ + GaimConnection *gc = data; + struct sscp_data *ndata = (struct sscp_data *)gc->proto_data; + gchar *buf; + + if (!g_list_find(gaim_connections_get_all(), gc)) { + close(source); + return; + } + + if (source < 0) { + gaim_connection_error(gc, _("Unable to connect")); + return; + } + + ndata->fd = source; + ndata->con_state = CON_STATE_TRYING; + ndata->sscp_state = SSCP_STATE_RESPLINE; + + /* Update the login progress status display */ + buf = g_strdup_printf("Logging in: %s", gaim_account_get_username(gc->account)); + gaim_connection_update_progress(gc, buf, 1, 5); + g_free(buf); + + /* Write our signon data */ + buf = g_strdup_printf("INFO SSCP/1.0\r\nFrom: %s\r\n" + "User-Agent: " USER_AGENT "\r\n\r\n", + gaim_account_get_username(gc->account)); + write(source, buf, strlen(buf)); + g_free(buf); + + /* And set up the input watcher */ + gc->inpa = gaim_input_add(ndata->fd, GAIM_INPUT_READ, sscp_callback, gc); +} + +static void sscp_login(GaimAccount *account) +{ + GaimConnection *gc = gaim_account_get_connection(account); + + gaim_connection_update_progress(gc, _("Connecting"), 0, 5); + + gc->proto_data = g_new0(struct sscp_data, 1); + if (gaim_proxy_connect(account, + gaim_account_get_string(account, "server", CM_SERVER), + gaim_account_get_int(account, "port", CM_PORT), + sscp_login_connect, gc) != 0) { + gaim_connection_error(gc, _("Unable to connect")); + } +} + + +static void sscp_close(GaimConnection *gc) +{ + struct sscp_data *ndata = (struct sscp_data *)gc->proto_data; + + if (gc->inpa) + gaim_input_remove(gc->inpa); + + if (!ndata) + return; + + close(ndata->fd); + sscp_update_notify(gc, ndata, (GList *)0); + sscp_hdr_free(&ndata->msg); + if (ndata->inbuf) + g_free(ndata->inbuf); + if (ndata->AuthInfo) + g_free(ndata->AuthInfo); + if (ndata->confid) + g_free(ndata->confid); + g_free(ndata); +} + +static const char* sscp_list_icon(GaimAccount *a, struct buddy *b) +{ + return "sscp"; +} + +static void sscp_list_emblems(struct buddy *b, char **se, char **sw, char **nw, char **ne) +{ + if (b->present == GAIM_BUDDY_OFFLINE) + *se = "offline"; +} + +static GList *sscp_buddy_menu(GaimConnection *gc, const char *who) +{ + GList *m = NULL; + struct proto_buddy_menu *pbm; + + pbm = g_new0(struct proto_buddy_menu, 1); + pbm->label = _("Get Info"); + pbm->callback = sscp_get_info; + pbm->gc = gc; + m = g_list_append(m, pbm); + + return m; +} + +static GList *sscp_chat_info(GaimConnection *gc) +{ + GList *m = NULL; + struct proto_chat_entry *pce; + + pce = g_new0(struct proto_chat_entry, 1); + pce->label = _("Conference:"); + pce->identifier = "conf"; + pce->min = 3; + pce->max = 80; + m = g_list_append(m, pce); + + pce = g_new0(struct proto_chat_entry, 1); + pce->label = _("Password:"); + pce->identifier = "passwd"; + m = g_list_append(m, pce); + + return m; +} + +static GaimPlugin *my_protocol = NULL; + +static GaimPluginProtocolInfo prpl_info = +{ + GAIM_PROTO_SSCP, + OPT_PROTO_CHAT_TOPIC, + NULL, + NULL, + sscp_list_icon, + sscp_list_emblems, + NULL, + NULL, + NULL, + NULL, + sscp_buddy_menu, + sscp_chat_info, + sscp_login, + sscp_close, + sscp_send_im, + NULL, + NULL, + sscp_get_info, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + sscp_add_buddy, + sscp_add_buddies, + sscp_remove_buddy, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + sscp_join_chat, + NULL, + sscp_chat_leave, + NULL, + sscp_chat_send, + sscp_keepalive, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +static GaimPluginInfo info = +{ + 2, /**< api_version */ + GAIM_PLUGIN_PROTOCOL, /**< type */ + NULL, /**< ui_requirement */ + 0, /**< flags */ + NULL, /**< dependencies */ + GAIM_PRIORITY_DEFAULT, /**< priority */ + + "prpl-sscp", /**< id */ + "Confmgr", /**< name */ + VERSION, /**< version */ + /** summary */ + N_("SSCP Protocol Plugin"), + /** description */ + N_("SSCP Protocol Plugin"), + NULL, /**< author */ + "http://www.research.earthlink.net/confmgr", /**< homepage */ + + NULL, /**< load */ + NULL, /**< unload */ + NULL, /**< destroy */ + + NULL, /**< ui_info */ + &prpl_info /**< extra_info */ +}; + +static void init_plugin(GaimPlugin *plugin) +{ + GaimAccountOption *option; + + option = gaim_account_option_string_new(_("Server"), "server", + CM_SERVER); + prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, + option); + + option = gaim_account_option_int_new(_("Port"), "port", CM_PORT); + prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, + option); + + my_protocol = plugin; + + sol_init(); + srand((unsigned int)time((int *)0)); + +} + +GAIM_INIT_PLUGIN(sscp, init_plugin, info); diff -Naur gaim-0.68/src/protocols/sscp/sscp.h sscp_gaim/src/protocols/sscp/sscp.h --- gaim-0.68/src/protocols/sscp/sscp.h 1969-12-31 16:00:00.000000000 -0800 +++ sscp_gaim/src/protocols/sscp/sscp.h 2003-09-08 12:06:21.000000000 -0700 @@ -0,0 +1,71 @@ + +#define CM_SERVER "avl1.research.earthlink.net" +#define CM_PORT 15900 + +struct sscp_hdr { + char *Status; + char *ConferenceID; + char *From; + char *To; + char *Subject; + char *Authenticate; + char *Cseq; + int ContentLength; +}; + +struct sscp_data { + int fd; + char *inbuf; + int inbuflen; + int inbufused; + int con_state; + int sscp_state; + char *AuthInfo; + GList *members; + char *confid; + GList *participants; + gpointer data; + struct sscp_hdr msg; +}; + + +/* + * Notify data: + * looptest (Anna):209.179.6.14:linuxdev.research.earthlink.net/ConfMgr/v0.3.00 + * david@bdt.com (David Beckemeyer):198.144.194.194:198.144.194.194/ConfMgr/v0.4.32SSCP/1.0 200 OK + */ +struct sscp_notify_rec { + char *uid; + char *fname; + char *ip; + char *hname; + char *res; +}; + +struct sscp_req { + GaimConnection *gc; + int inpa; + char *who; + char *message; + GaimImFlags flags; + struct sscp_data *ndata; +}; + + +#define CON_STATE_CLOSED 0 +#define CON_STATE_TRYING 1 +#define CON_STATE_SENTAUTH 2 +#define CON_STATE_INFO 3 +#define CON_STATE_JOIN 4 +#define CON_STATE_QUERY 5 +#define CON_STATE_LEAVE 6 +#define CON_STATE_SUBCONNECT 7 +#define CON_STATE_SUBREQ 8 + +#define SSCP_STATE_RESPLINE 0 +#define SSCP_STATE_HEADER 1 +#define SSCP_STATE_BODY 2 + +#define SSCP_INITIAL_BUFSIZE 1024 + +#define USER_AGENT "ConfMgr/v0.4.00 (compatible; Gaim Plug-in)" diff -Naur gaim-0.68/src/prpl.c sscp_gaim/src/prpl.c --- gaim-0.68/src/prpl.c 2003-08-27 18:18:50.000000000 -0700 +++ sscp_gaim/src/prpl.c 2003-09-08 12:06:21.000000000 -0700 @@ -51,7 +51,7 @@ case GAIM_PROTO_GADUGADU: return "prpl-gg"; break; case GAIM_PROTO_MOO: return "prpl-moo"; break; case GAIM_PROTO_TREPIA: return "prpl-trepia"; break; - + case GAIM_PROTO_SSCP: return "prpl-sscp"; break; default: break; } @@ -76,6 +76,7 @@ else if (!strcmp(id, "prpl-gg")) return GAIM_PROTO_GADUGADU; else if (!strcmp(id, "prpl-moo")) return GAIM_PROTO_MOO; else if (!strcmp(id, "prpl-trepia")) return GAIM_PROTO_TREPIA; + else if (!strcmp(id, "prpl-sscp")) return GAIM_PROTO_SSCP; return -1; } @@ -168,7 +169,7 @@ id->len = len; /* Update the buddy icon for this user. */ - conv = gaim_find_conversation_with_account(realwho, gc->account); + conv = gaim_find_conversation(realwho); /* XXX Buddy Icon should probalby be part of struct buddy instead of this weird global * linked list stuff. */