#include <netdb.h>
#include <unistd.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/socket.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include "toc.h"
static int toc_fd;
static int seqno;
static unsigned int peer_ver=0;
int state;
int permdeny = PERMIT_PERMITALL;
int toc_login(char *username, char *password)
{
char *config;
struct in_addr *sin;
char buf[80];
char buf2[2048];
toc_debug_printf("looking up host! %s", aim_host);
sin = (struct in_addr *)get_address(aim_host);
if (!sin) {
set_state(STATE_OFFLINE);
toc_msg_printf(TOC_CONNECT_MSGS,"Unable to lookup %s", aim_host);
return -1;
}
snprintf(toc_addy, sizeof(toc_addy), "%s", inet_ntoa(*sin));
snprintf(buf, sizeof(buf), "Connecting to %s", inet_ntoa(*sin));
toc_msg_printf(TOC_CONNECT_MSGS,"%s",buf);
toc_fd = connect_address(sin->s_addr, aim_port);
if (toc_fd < 0) {
set_state(STATE_OFFLINE);
toc_msg_printf(TOC_CONNECT_MSGS,"Connect to %s failed", inet_ntoa(*sin));
return -1;
}
free(sin);
toc_msg_printf(TOC_CONNECT_MSGS,"Signon: %s",username);
if (toc_signon(username, password) < 0) {
set_state(STATE_OFFLINE);
toc_msg_printf(TOC_CONNECT_MSGS,"Disconnected.");
return -1;
}
toc_msg_printf(TOC_CONNECT_MSGS,"Waiting for reply...");
if (toc_wait_signon() < 0) {
set_state(STATE_OFFLINE);
toc_msg_printf(TOC_CONNECT_MSGS,"Authentication Failed");
return -1;
}
snprintf(aim_username, sizeof(aim_username), "%s", username);
snprintf(aim_password, sizeof(aim_password), "%s", password);
save_prefs();
toc_msg_printf(TOC_CONNECT_MSGS,"Retrieving config...");
if ((config=toc_wait_config()) == NULL) {
toc_msg_printf(TOC_CONNECT_MSGS,"No Configuration\n");
set_state(STATE_OFFLINE);
return -1;
}
init_lists();
parse_toc_buddy_list(config);
snprintf(buf2, sizeof(buf2), "toc_init_done");
sflap_send(buf2, -1, TYPE_DATA);
serv_finish_login();
return 0;
}
void toc_close()
{
seqno = 0;
state = STATE_OFFLINE;
toc_remove_input_stream(toc_fd);
close(toc_fd);
toc_fd=-1;
}
int toc_signon(char *username, char *password)
{
char buf[BUF_LONG];
int res;
struct signon so;
toc_debug_printf("State = %d\n", state);
strncpy(aim_username, username, sizeof(aim_username));
if ((res = write(toc_fd, FLAPON, strlen(FLAPON))) < 0)
return res;
state = STATE_FLAPON;
if ((res = wait_reply(buf, sizeof(buf)) < 0))
return res;
if (state != STATE_SIGNON_REQUEST) {
toc_debug_printf( "State should be %d, but is %d instead\n", STATE_SIGNON_REQUEST, state);
return -1;
}
snprintf(so.username, sizeof(so.username), "%s", username);
so.ver = ntohl(1);
so.tag = ntohs(1);
so.namelen = htons(strlen(so.username));
sflap_send((char *)&so, ntohs(so.namelen) + 8, TYPE_SIGNON);
snprintf(buf, sizeof(buf),
"toc_signon %s %d %s %s %s \"%s\"",
login_host, login_port, normalize(username), roast_password(password), LANGUAGE, REVISION);
toc_debug_printf("Send: %s\n", buf);
return sflap_send(buf, -1, TYPE_DATA);
}
int toc_wait_signon()
{
char buf[BUF_LEN];
int res;
res = wait_reply(buf, sizeof(buf));
if (res < 0)
return res;
if (state != STATE_SIGNON_ACK) {
toc_debug_printf("State should be %d, but is %d instead\n",STATE_SIGNON_ACK, state);
return -1;
}
return 0;
}
int wait_reply(char *buffer, int buflen)
{
int res=6;
struct sflap_hdr *hdr=(struct sflap_hdr *)buffer;
char *c;
while((res = read(toc_fd, buffer, 1))) {
if (res < 0)
return res;
if (buffer[0] == '*')
break;
}
res = read(toc_fd, buffer+1, sizeof(struct sflap_hdr) - 1);
if (res < 0)
return res;
res += 1;
toc_debug_printf( "Rcv: %s %s\n",print_header(buffer), "");
while (res < (sizeof(struct sflap_hdr) + ntohs(hdr->len))) {
res += read(toc_fd, buffer + res, (ntohs(hdr->len) + sizeof(struct sflap_hdr)) - res);
}
if (res >= sizeof(struct sflap_hdr))
buffer[res]='\0';
else
return res - sizeof(struct sflap_hdr);
switch(hdr->type) {
case TYPE_SIGNON:
memcpy(&peer_ver, buffer + sizeof(struct sflap_hdr), 4);
peer_ver = ntohl(peer_ver);
seqno = ntohs(hdr->seqno);
state = STATE_SIGNON_REQUEST;
break;
case TYPE_DATA:
if (!strncasecmp(buffer + sizeof(struct sflap_hdr), "SIGN_ON:", strlen("SIGN_ON:")))
state = STATE_SIGNON_ACK;
else if (!strncasecmp(buffer + sizeof(struct sflap_hdr), "CONFIG:", strlen("CONFIG:"))) {
state = STATE_CONFIG;
} else if (state != STATE_ONLINE && !strncasecmp(buffer + sizeof(struct sflap_hdr), "ERROR:", strlen("ERROR:"))) {
c = strtok(buffer + sizeof(struct sflap_hdr) + strlen("ERROR:"), ":");
translate_toc_error_code(c);
toc_debug_printf("ERROR CODE: %s\n",c);
}
toc_debug_printf("Data: %s\n",buffer + sizeof(struct sflap_hdr));
break;
default:
toc_debug_printf("Unknown/unimplemented packet type %d\n",hdr->type);
}
return res;
}
int sflap_send(char *buf, int olen, int type)
{
int len;
int slen=0;
struct sflap_hdr hdr;
char obuf[MSG_LEN];
if (strlen(buf) > (MSG_LEN - sizeof(hdr))) {
buf[MSG_LEN - sizeof(hdr) - 3] = '"';
buf[MSG_LEN - sizeof(hdr) - 2] = '\0';
}
toc_debug_printf("%s [Len %d]\n", buf, strlen(buf));
if (olen < 0)
len = escape_message(buf);
else
len = olen;
hdr.ast = '*';
hdr.type = type;
hdr.seqno = htons(seqno++ & 0xffff);
hdr.len = htons(len + (type == TYPE_SIGNON ? 0 : 1));
toc_debug_printf("Escaped message is '%s'\n",buf);
memcpy(obuf, &hdr, sizeof(hdr));
slen += sizeof(hdr);
memcpy(&obuf[slen], buf, len);
slen += len;
if (type != TYPE_SIGNON) {
obuf[slen]='\0';
slen += 1;
}
return write(toc_fd, obuf, slen);
}
unsigned char *roast_password(char *pass)
{
static char rp[256];
static char *roast = ROAST;
int pos=2;
int x;
strcpy(rp, "0x");
for (x=0;(x<150) && pass[x]; x++)
pos+=sprintf(&rp[pos],"%02x", pass[x] ^ roast[x % strlen(roast)]);
rp[pos]='\0';
return rp;
}
char *print_header(void *hdr_v)
{
static char s[80];
struct sflap_hdr *hdr = (struct sflap_hdr *)hdr_v;
snprintf(s,sizeof(s), "[ ast: %c, type: %d, seqno: %d, len: %d ]",
hdr->ast, hdr->type, ntohs(hdr->seqno), ntohs(hdr->len));
return s;
}
int toc_callback(int fd)
{
char *buf;
char *c;
char **args = NULL;
char *dup,*raw;
char *l;
int numargs =0;
buf = malloc(BUF_LONG);
if (wait_reply(buf, BUF_LONG) < 0) {
toc_signoff();
toc_debug_printf("need to do proper sign off on this\n");
toc_msg_printf(TOC_CONNECT_MSGS,"Connection Closed");
return -1;
}
dup = strdup(buf+sizeof(struct sflap_hdr));
raw = rindex(dup,':');
c=strtok(buf+sizeof(struct sflap_hdr),":");
if (!strcasecmp(c,"UPDATE_BUDDY")) {
char *uc, *t;
int logged, evil, idle, type = 0;
time_t signon;
time_t time_idle;
numargs = 7;
args = (char **) malloc(sizeof(char *)*numargs);
use_handler(TOC_RAW_HANDLE,TOC_UPDATE_BUDDY,raw);
c = strtok(NULL,":");
args[0] = strdup(c);
l = strtok(NULL,":");
args[1] = strdup(l);
t = strtok(NULL, ":");
args[2] = strdup(t);
sscanf(t, "%d", &evil);
t = strtok(NULL, ":");
args[3] = strdup(t);
sscanf(t, "%ld", &signon);
t = strtok(NULL, ":");
args[4] = strdup(t);
sscanf(t, "%d", &idle);
uc = strtok(NULL, ":");
args[5] = strdup(uc);
if (!strncasecmp(l,"T",1))
logged = 1;
else
logged = 0;
if (uc[0] == 'A')
type |= UC_AOL;
switch(uc[1]) {
case 'A':
type |= UC_ADMIN;
break;
case 'U':
type |= UC_UNCONFIRMED;
break;
case 'O':
type |= UC_NORMAL;
break;
default:
break;
}
switch(uc[2]) {
case 'U':
type |= UC_UNAVAILABLE;
break;
default:
break;
}
if (idle) {
time(&time_idle);
time_idle -= idle*60;
} else
time_idle = 0;
serv_got_update(c, logged, evil, signon, time_idle, type);
args[6] = NULL;
use_handler(TOC_HANDLE,TOC_UPDATE_BUDDY,args);
} else if (!strcasecmp(c, "ERROR")) {
use_handler(TOC_RAW_HANDLE,TOC_ERROR,raw);
c = strtok(NULL,":");
translate_toc_error_code(c);
args = (char **) malloc(sizeof(char *)*1 + 1);
numargs = 1;
args[0] = strdup(c);
use_handler(TOC_HANDLE,TOC_ERROR,args);
toc_debug_printf("ERROR: %s",c);
} else if (!strcasecmp(c, "NICK")) {
use_handler(TOC_RAW_HANDLE,TOC_NICK,raw);
c = strtok(NULL,":");
snprintf(aim_username, sizeof(aim_username), "%s", c);
numargs = 2;
args = (char **) malloc(sizeof(char *)*numargs);
args[0] = strdup(c);
args[1] = NULL;
use_handler(TOC_HANDLE,TOC_NICK,args);
} else if (!strcasecmp(c, "IM_IN")) {
char *away, *message;
int a = 0;
use_handler(TOC_RAW_HANDLE,TOC_IM_IN,raw);
c = strtok(NULL,":");
away = strtok(NULL,":");
message = away;
while(*message && (*message != ':'))
message++;
message++;
if (!strncasecmp(away, "T", 1))
a = 1;
if ( serv_got_im(c, message,a) > 0 ) {
numargs = 3;
args = (char **) malloc(sizeof(char *)*numargs);
args[0] = strdup(c);
args[1] = strdup(message);
args[2] = NULL;
use_handler(TOC_HANDLE,TOC_IM_IN,args);
}
} else if (!strcasecmp(c, "GOTO_URL")) {
char *name;
char *url;
char tmp[256];
use_handler(TOC_RAW_HANDLE,TOC_GOTO_URL,raw);
name = strtok(NULL, ":");
url = strtok(NULL, ":");
snprintf(tmp, sizeof(tmp), "http://%s:%d/%s", toc_addy, aim_port, url);
numargs = 2;
args = (char **) malloc(sizeof(char *)*numargs);
args[0] = strdup(tmp);
args[1] = NULL;
use_handler(TOC_HANDLE,TOC_GOTO_URL,args);
} else if (!strcasecmp(c, "EVILED")) {
int lev;
char *name = NULL;
char *levc;
use_handler(TOC_RAW_HANDLE,TOC_EVILED,raw);
levc = strtok(NULL, ":");
sscanf(levc, "%d", &lev);
name = strtok(NULL, ":");
toc_debug_printf("evil: %s | %d\n", name, lev);
numargs = 3;
my_evil = lev;
args = (char **) malloc(sizeof(char *)*numargs);
if ( name != NULL )
args[0] = strdup(name);
else
args[0] = NULL;
args[1] = strdup(levc);
args[2] = NULL;
use_handler(TOC_HANDLE,TOC_EVILED,args);
} else if (!strcasecmp(c, "CHAT_JOIN")) {
char *name,*idc;
int id;
use_handler(TOC_RAW_HANDLE,TOC_CHAT_JOIN,raw);
idc = strtok(NULL, ":");
sscanf(idc, "%d", &id);
name = strtok(NULL, ":");
serv_got_joined_chat(id, name);
numargs = 3;
args = (char **) malloc(sizeof(char *)*numargs);
args[0] = strdup(idc);
args[1] = strdup(name);
args[2] = NULL;
use_handler(TOC_HANDLE,TOC_CHAT_JOIN,args);
} else if (!strcasecmp(c, "DIR_STATUS")) {
char *status;
use_handler(TOC_RAW_HANDLE,TOC_DIR_STATUS,raw);
status = strtok(NULL,":");
numargs = 2;
args = (char **) malloc(sizeof(char *)*numargs);
args[0] = strdup(status);
args[1] = NULL;
use_handler(TOC_HANDLE,TOC_DIR_STATUS,args);
} else if (!strcasecmp(c, "CHAT_UPDATE_BUDDY")) {
int id;
char *in,*idc;
char *buddy;
LLE t;
struct buddy_chat *b = NULL;
use_handler(TOC_RAW_HANDLE,TOC_CHAT_UPDATE_BUDDY,raw);
idc = strtok(NULL, ":");
sscanf(idc, "%d", &id);
in = strtok(NULL, ":");
for ( TLL(buddy_chats,t) ) {
b = (struct buddy_chat *)t->data;
if (id == b->id)
break;
b = NULL;
}
if (!b)
return -2;
if (!strcasecmp(in, "T")) {
while((buddy = strtok(NULL, ":")) != NULL) {
AddToLL(b->in_room, buddy,NULL);
if ( b->init_chat ) {
args = (char **) malloc(sizeof(char *)*3);
args[0] = strdup(b->name);
args[1] = strdup(buddy);
args[2] = NULL;
use_handler(TOC_HANDLE,TOC_BUDDY_JOIN_CHAT,args);
free(args[0]); free(args[1]); free(args); args = NULL;
}
}
b->init_chat = 1;
} else {
while((buddy = strtok(NULL, ":")) != NULL) {
RemoveFromLLByKey(b->in_room, buddy);
args = (char **) malloc(sizeof(char *)*3);
args[0] = strdup(b->name);
args[1] = strdup(buddy);
args[2] = NULL;
use_handler(TOC_HANDLE,TOC_BUDDY_LEFT_CHAT,args);
free(args[0]); free(args[1]); free(args); args = NULL;
}
}
} else if (!strcasecmp(c, "CHAT_LEFT")) {
char *idc;
int id;
use_handler(TOC_RAW_HANDLE,TOC_CHAT_LEFT,raw);
idc = strtok(NULL, ":");
sscanf(idc, "%d", &id);
serv_got_chat_left(id);
numargs = 2;
args = (char **) malloc(sizeof(char *)*numargs);
args[0] = strdup(idc);
args[1] = NULL;
use_handler(TOC_HANDLE,TOC_CHAT_LEFT,args);
} else if (!strcasecmp(c, "CHAT_IN")) {
int id, w;
char *m,*idc;
char *who, *whisper, *chan;
struct buddy_chat *b;
use_handler(TOC_RAW_HANDLE,TOC_CHAT_IN,raw);
idc = strtok(NULL, ":");
sscanf(idc, "%d", &id);
who = strtok(NULL, ":");
whisper = strtok(NULL, ":");
m = whisper;
while(*m && (*m != ':')) m++;
m++;
if (!strcasecmp(whisper, "T"))
w = 1;
else
w = 0;
b = buddy_chat_getbyid(id);
if ( ! b ) {
chan = (char *) malloc(50);
strcpy(chan,"ERROR Couldn't lookup chan!");
} else {
chan = (char *) malloc(strlen(b->name)+1);
strcpy(chan,b->name);
}
numargs = 6;
args = (char **) malloc(sizeof(char *)*numargs);
args[0] = strdup(idc);
args[1] = strdup(who);
args[2] = strdup(whisper);
args[3] = strdup(m);
args[4] = chan;
args[5] = NULL;
use_handler(TOC_HANDLE,TOC_CHAT_IN,args);
} else if (!strcasecmp(c, "CHAT_INVITE")) {
char *name;
char *who;
char *message,*idc;
int id, *pid;
use_handler(TOC_RAW_HANDLE,TOC_CHAT_INVITE,raw);
name = strtok(NULL, ":");
idc = strtok(NULL, ":");
sscanf(idc, "%d", &id);
who = strtok(NULL, ":");
message = strtok(NULL, ":");
pid = (int *) malloc(sizeof(int));
*pid = id;
AddToLL(invited_chats,name,pid);
numargs = 5;
args = (char **) malloc(sizeof(char *)*numargs);
args[0] = strdup(name);
args[1] = strdup(idc);
args[2] = strdup(who);
args[3] = strdup(message);
args[4] = NULL;
use_handler(TOC_HANDLE,TOC_CHAT_INVITE,args);
} else {
toc_debug_printf("don't know what to do with %s\n", c);
}
free(dup);
free(buf);
if ( args != NULL ) {
int x;
for (x=0;x< numargs; x++)
if ( args[x] != NULL ) {
free(args[x]);
}
free(args);
}
return 1;
}
char *toc_wait_config()
{
static char buf[BUF_LEN];
int res;
res = wait_reply(buf, sizeof(buf));
if (res < 0)
return NULL;
if (state != STATE_CONFIG) {
toc_debug_printf("State should be %d, but is %d instead\n",STATE_CONFIG, state);
return NULL;
}
state = STATE_ONLINE;
toc_add_input_stream(toc_fd,&toc_callback);
return buf;
}
void parse_toc_buddy_list(char *config)
{
char *c;
char current[256];
char *name;
LL bud = CreateLL();
c = strncmp(config + sizeof(struct sflap_hdr),"CONFIG:",strlen("CONFIG:"))?
strtok(config, "\n"):
strtok(config + sizeof(struct sflap_hdr)+strlen("CONFIG:"), "\n");
do {
if (c == NULL)
break;
if (*c == 'g') {
strncpy(current,c+2, sizeof(current));
add_group(current);
} else if (*c == 'b') {
add_buddy(current, c+2);
AddToLL(bud, c+2, NULL);
} else if (*c == 'p') {
name = malloc(strlen(c+2) + 2);
snprintf(name, strlen(c+2) + 1, "%s", c+2);
AddToLL(permit, name, NULL);
} else if (*c == 'd') {
name = malloc(strlen(c+2) + 2);
snprintf(name, strlen(c+2) + 1, "%s", c+2);
AddToLL(deny, name,NULL);
} else if (*c == 'm') {
sscanf(c + strlen(c) - 1, "%d", &permdeny);
if (permdeny == 0)
permdeny = PERMIT_PERMITALL;
}
} while ((c=strtok(NULL,"\n")));
serv_add_buddies(bud);
FreeLL(bud);
serv_set_permit_deny();
}
int toc_signoff() {
FreeLL(groups);
FreeLL(buddy_chats);
FreeLL(invited_chats);
FreeLL(permit);
FreeLL(deny);
deny = groups = permit = buddy_chats = invited_chats = NULL;
toc_debug_printf("LEAKING MEMORY LIKE A BITCH in toc_signoff!");
serv_close();
toc_msg_printf(TOC_CONNECT_MSGS,"%s signed off",aim_username);
return 1;
}
void toc_build_config(char *s, int len)
{
struct group *g;
struct buddy *b;
LLE t,t1;
LL mem;
int pos=0;
toc_debug_printf("FIX this permdeny hack shit!");
if (!permdeny)
permdeny = PERMIT_PERMITALL;
pos += snprintf(&s[pos], len - pos, "m %d\n", permdeny);
for ( TLL(groups,t) ) {
g = (struct group *)t->data;
pos += snprintf(&s[pos], len - pos, "g %s\n", g->name);
mem = g->members;
for ( TLL(mem,t1) ) {
b = (struct buddy *)t1->data;
pos += snprintf(&s[pos], len - pos, "b %s\n", b->name);
}
}
;
for ( TLL(permit,t) ) {
toc_debug_printf("permit: added %s\n",(char *)t->key);
pos += snprintf(&s[pos], len - pos, "p %s\n", (char *)t->key);
}
for ( TLL(deny,t) ) {
toc_debug_printf("deny: added %s\n",(char *)t->key);
pos += snprintf(&s[pos], len - pos, "d %s\n", (char *)t->key);
}
}
void translate_toc_error_code(char *c) {
int no = atoi(c);
char *w = strtok(NULL, ":");
char buf[256];
switch ( no ) {
case 901:
snprintf(buf, sizeof(buf), "%s not currently logged in.", w);
break;
case 902:
snprintf(buf, sizeof(buf), "Warning of %s not allowed.", w);
break;
case 903:
snprintf(buf, sizeof(buf), "A message has been dropped, you are exceeding the server speed limit.");
break;
case 950:
snprintf(buf, sizeof(buf), "Chat in %s is not available.", w);
break;
case 960:
snprintf(buf, sizeof(buf), "You are sending messages too fast to %s.", w);
break;
case 961:
snprintf(buf, sizeof(buf), "You missed an IM from %s because it was too big.", w);
break;
case 962:
snprintf(buf, sizeof(buf), "You missed an IM from %s because it was sent too fast.", w);
break;
case 970:
snprintf(buf, sizeof(buf), "Failure.");
break;
case 971:
snprintf(buf, sizeof(buf), "Too many matches.");
break;
case 972:
snprintf(buf, sizeof(buf), "Need more qualifiers.");
break;
case 973:
snprintf(buf, sizeof(buf), "Dir service temporarily unavailable.");
break;
case 974:
snprintf(buf, sizeof(buf), "Email lookup restricted.");
break;
case 975:
snprintf(buf, sizeof(buf), "Keyword ignored.");
break;
case 976:
snprintf(buf, sizeof(buf), "No keywords.");
break;
case 977:
snprintf(buf, sizeof(buf), "User has no directory information.");
break;
case 978:
snprintf(buf, sizeof(buf), "Country not supported.");
break;
case 979:
snprintf(buf, sizeof(buf), "Failure unknown: %s.", w);
break;
case 980:
snprintf(buf, sizeof(buf), "Incorrect nickname or password.");
break;
case 981:
snprintf(buf, sizeof(buf), "The service is temporarily unavailable.");
break;
case 982:
snprintf(buf, sizeof(buf), "Your warning level is currently too high to log in.");
break;
case 983:
snprintf(buf, sizeof(buf), "You have been connecting and disconnecting too frequently. Wait ten minutes and try again. If you continue to try, you will need to wait even longer.");
break;
case 989:
snprintf(buf, sizeof(buf), "An unknown signon error has occurred: %s.", w);
break;
default:
snprintf(buf, sizeof(buf), "An unknown error, %d, has occured. Info: %s", no, w);
}
toc_msg_printf(TOC_TRANSLATED_ERROR,buf);
return;
}