Chat-O-Matic/libs/libimcomm/imcomm.c

771 lines
16 KiB
C
Raw Normal View History

/** _
** (_)_ __ __ ___ _ __ _ __
** | | ' \/ _/ _ \ ' \| ' \
** |_|_|_|_\__\___/_|_|_|_|_|_|
**
** Copyright (C) 2003-2005, Claudio Leite
** All rights reserved.
**
** Please see the file 'COPYING' for licensing information.
**/
#include "imcomm.h"
IMCOMM_HANDLES *handles = NULL;
int endianness;
int nodes_to_delete = 0;
#ifdef MACINTOSH_CLASSIC
extern short refnum;
#endif
/* PROTO */
void *
imcomm_create_handle(void)
{
IMCOMM *handle;
IMCOMM_HANDLES *tmp;
int xx;
handle = malloc(sizeof(IMCOMM));
handle->proxymode = PROXY_TYPE_NONE;
handle->proxyserver = NULL;
handle->proxyport = 0;
handle->to_delete = 0;
handle->ischild = 0;
handle->seqnum = 0x1000;
handle->snacreq = 0;
handle->families = NULL;
handle->num_families = 0;
handle->buddylist = NULL;
handle->buddies_online = NULL;
handle->isidle = 0;
handle->isinvisible = 0;
handle->last_operation_time = time(NULL);
handle->profile_str = NULL;
handle->away_msg = NULL;
handle->icondata = 0;
handle->iconlen = 0;
handle->socket = 0;
handle->connected = 0;
handle->srv_pause = 0;
handle->data = NULL;
handle->header_pos = 0;
handle->oscarport = 5190;
#ifdef MD5_LOGIN
handle->pw = NULL;
handle->sn = NULL;
#endif
#ifdef IMCOMM_KEEPALIVE
handle->last_keepalive_time = 0;
#endif
#ifdef SEND_QUEUES
handle->s_queue = NULL;
#endif
#ifdef MACINTOSH_CLASSIC
handle->readable = 0;
#endif
endianness = getbyteorder();
for (xx = 0; xx < NUM_CALLBACKS; xx++)
handle->callbacks[xx] = NULL;
if (handles == NULL) {
handles = malloc(sizeof(IMCOMM_HANDLES));
handles->handle = handle;
handles->next = NULL;
} else {
tmp = handles;
while (tmp->next != NULL)
tmp = tmp->next;
tmp->next = malloc(sizeof(IMCOMM_HANDLES));
tmp->next->handle = handle;
tmp->next->next = NULL;
}
return (void *) handle;
}
/* PROTO */
int
imcomm_delete_handle_now(void *vhandle)
{
IMCOMM_HANDLES *tmp, *tr;
if (handles == NULL)
return 0;
if (handles->handle == (IMCOMM *) vhandle) {
tmp = handles;
handles = handles->next;
imcomm_delete_handle_only((void *) tmp->handle);
free(tmp);
} else {
for (tr = handles; tr;) {
if (tr->handle == (IMCOMM *) vhandle) {
tmp = tr;
tr = tr->next;
imcomm_delete_handle_only((void *) tmp->handle);
free(tmp);
continue;
}
tr = tr->next;
}
}
return 1;
}
/* PROTO */
int
imcomm_delete_handle(void *vhandle)
{
((IMCOMM *) vhandle)->to_delete = 1;
nodes_to_delete = 1;
return 1;
}
/* PROTO */
int
imcomm_delete_handle_only(void *vhandle)
{
IMCOMM *handle = (IMCOMM *) vhandle;
if (handle->buddylist)
imcomm_delete_buddylist(handle->buddylist);
if (handle->buddies_online)
imcomm_delete_buddylist(handle->buddies_online);
if (handle->families)
imcomm_delete_familieslist(handle->families);
if (handle->profile_str)
free(handle->profile_str);
if (handle->away_msg)
free(handle->away_msg);
if (handle->icondata)
free(handle->icondata);
if (handle->data)
free(handle->data);
#ifdef MD5_LOGIN
if (handle->sn)
free(handle->sn);
if (handle->pw)
free(handle->pw);
#endif
if (handle->proxyserver)
free(handle->proxyserver);
if (handle->socket != -1)
shutdown(handle->socket, 0x02);
free(vhandle);
return 1;
}
/* PROTO */
void
imcomm_delete_buddylist(struct IMComm_BuddyList * buddylist)
{
struct IMComm_BuddyList *tr, *tmp;
for (tr = buddylist; tr;) {
if (tr->sn)
free(tr->sn);
if (tr->formattedsn)
free(tr->formattedsn);
tmp = tr;
tr = tr->next;
free(tmp);
}
}
/* PROTO */
void
imcomm_delete_familieslist(struct IMComm_Families * families)
{
struct IMComm_Families *tr, *tmp;
for (tr = families; tr;) {
tmp = tr;
tr = tr->next;
free(tmp);
}
}
/* PROTO */
void
imcomm_set_oscar_port(void *handle, uint16_t port)
{
((IMCOMM *) handle)->oscarport = port;
}
/* PROTO */
void *
imcomm_create_child_handle(void *parent)
{
IMCOMM *handle;
IMCOMM_HANDLES *tmp;
int xx;
handle = malloc(sizeof(IMCOMM));
handle->ischild = 1;
handle->proxymode = ((IMCOMM *) parent)->proxymode;
handle->proxyserver = ((IMCOMM *) parent)->proxyserver;
handle->proxyport = ((IMCOMM *) parent)->proxyport;
handle->parent = parent;
handle->to_delete = 0;
handle->seqnum = 0x1000;
handle->snacreq = 0;
handle->families = NULL;
handle->buddylist = NULL;
handle->buddies_online = NULL;
handle->isidle = 0;
handle->last_operation_time = time(NULL);
handle->profile_str = NULL;
handle->away_msg = NULL;
handle->icondata = 0;
handle->iconlen = 0;
handle->socket = 0;
handle->connected = 0;
handle->data = NULL;
handle->header_pos = 0;
#ifdef IMCOMM_KEEPALIVE
handle->last_keepalive_time = 0;
#endif
#ifdef SEND_QUEUES
handle->s_queue = NULL;
#endif
#ifdef MACINTOSH_CLASSIC
handle->readable = 0;
#endif
for (xx = 0; xx < NUM_CALLBACKS; xx++)
handle->callbacks[xx] = ((IMCOMM *) parent)->callbacks[xx];
if (handles == NULL) {
handles = malloc(sizeof(IMCOMM_HANDLES));
handles->handle = handle;
handles->next = NULL;
} else {
tmp = handles;
while (tmp->next != NULL)
tmp = tmp->next;
tmp->next = malloc(sizeof(IMCOMM_HANDLES));
tmp->next->handle = handle;
tmp->next->next = NULL;
}
return (void *) handle;
}
/* PROTO */
void
remove_deleted_handles(void)
{
IMCOMM_HANDLES *trav, *tmp;
if (handles == NULL)
return;
if (handles->handle->to_delete == 1) {
/*
* the last thing it'll do before deleting itself is to send
* a callback saying it's about to be deleted.
*/
if (handles->handle->callbacks[IMCOMM_HANDLE_DELETED])
handles->handle->
callbacks[IMCOMM_HANDLE_DELETED] ((void *) handles->
handle);
tmp = handles;
handles = handles->next;
imcomm_delete_handle_only(tmp->handle);
free(tmp);
remove_deleted_handles();
} else {
for (trav = handles; trav->next;) {
if (trav->next->handle->to_delete == 1) {
tmp = trav->next;
trav->next = trav->next->next;
trav = trav->next;
if (tmp->handle->callbacks[IMCOMM_HANDLE_DELETED])
tmp->handle->
callbacks[IMCOMM_HANDLE_DELETED] ((void *) tmp->
handle);
imcomm_delete_handle_only(tmp->handle);
free(tmp);
}
}
}
}
/* PROTO */
void
imcomm_set_proxy(void *handle, int type, char *proxyserver, uint16_t proxyport)
{
((IMCOMM *) handle)->proxymode = type;
((IMCOMM *) handle)->proxyserver = strdup(proxyserver);
((IMCOMM *) handle)->proxyport = proxyport;
}
/* PROTO */
void
imcomm_register_callback(void *handle, int event, void (*ptr) ())
{
((IMCOMM *) handle)->callbacks[event] = ptr;
}
/* PROTO */
IMCOMM_RET
imcomm_select(int nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds, struct timeval * timeout)
{
IMCOMM_HANDLES *tmp;
IMCOMM_RET ret = IMCOMM_RET_OK;
int maxfd = nfds;
#ifdef FULL_PACKET_AT_ONCE
int bytesread;
#endif
/*
* trim the handle list
*/
if (nodes_to_delete == 1) {
remove_deleted_handles();
nodes_to_delete = 0;
}
tmp = handles;
#ifdef MACINTOSH_CLASSIC
TCPiopb pb;
OSErr err;
for (tmp = handles; tmp != NULL; tmp = tmp->next) {
if (tmp->handle->readable == 0)
continue;
pb.ioCRefNum = refnum;
pb.csCode = TCPStatus;
pb.tcpStream = tmp->handle->s;
pb.csParam.status.userDataPtr = (Ptr) tmp->handle;
err = PBControlSync((ParmBlkPtr) & pb);
if (err != noErr)
continue;
if (pb.csParam.status.amtUnreadData >= 6) {
mactcp_recv(tmp->handle, (char *) tmp->handle->header, 6);
tmp->handle->data_len = two_to_16(tmp->handle->header + 4);
tmp->handle->data = malloc(tmp->handle->data_len);
mactcp_recv(tmp->handle, (char *) tmp->handle->data,
tmp->handle->data_len);
ret =
flap_decode(tmp->handle, tmp->handle->header,
tmp->handle->data);
tmp->handle->data = NULL;
}
}
#else
while (tmp != NULL) {
if ((int) tmp->handle->socket > maxfd)
maxfd = tmp->handle->socket;
if (tmp->handle->socket != -1)
FD_SET(tmp->handle->socket, readfds);
tmp = tmp->next;
}
if (select(maxfd + 1, readfds, writefds, exceptfds, timeout) == -1)
return IMCOMM_RET_ERROR;
for (tmp = handles; tmp; tmp = tmp->next) {
if (tmp->handle->socket == -1)
continue;
#ifdef SEND_QUEUES
if (tmp->handle->s_queue != NULL)
flap_sendnext(tmp->handle);
#endif
if (FD_ISSET(tmp->handle->socket, readfds)) {
#ifdef FULL_PACKET_AT_ONCE
bytesread = 0;
do {
if ((bytesread +=
recv(tmp->handle->socket,
tmp->handle->header + bytesread, 6 - bytesread,
0)) <= 0) {
shutdown(tmp->handle->socket, 0x02);
tmp->handle->socket = -1;
tmp->handle->data = NULL;
tmp->handle->connected = 0;
if (tmp->handle->callbacks[IMCOMM_ERROR])
tmp->handle->callbacks[IMCOMM_ERROR] (tmp->handle,
IMCOMM_ERROR_DISCONNECTED);
return IMCOMM_RET_ERROR;
}
} while (bytesread < 6);
tmp->handle->data_len = two_to_16(tmp->handle->header + 4);
tmp->handle->data = malloc(tmp->handle->data_len);
bytesread = 0;
do {
if ((bytesread +=
recv(tmp->handle->socket,
tmp->handle->data + bytesread,
tmp->handle->data_len - bytesread, 0)) <= 0) {
shutdown(tmp->handle->socket, 0x02);
tmp->handle->socket = -1;
tmp->handle->connected = 0;
tmp->handle->data = NULL;
if (tmp->handle->callbacks[IMCOMM_ERROR])
tmp->handle->callbacks[IMCOMM_ERROR] (tmp->handle,
IMCOMM_ERROR_DISCONNECTED);
return IMCOMM_RET_ERROR;
}
} while (bytesread < tmp->handle->data_len);
ret =
flap_decode(tmp->handle, tmp->handle->header,
tmp->handle->data);
tmp->handle->data = NULL;
#else
if (tmp->handle->header_pos < 6) {
if (recv
(tmp->handle->socket,
&tmp->handle->header[tmp->handle->header_pos], 1,
0) <= 0) {
shutdown(tmp->handle->socket, 0x02);
tmp->handle->socket = -1;
tmp->handle->connected = 0;
tmp->handle->header_pos = 0;
tmp->handle->data = NULL;
if (tmp->handle->callbacks[IMCOMM_ERROR])
tmp->handle->callbacks[IMCOMM_ERROR] (tmp->handle,
IMCOMM_ERROR_DISCONNECTED);
return IMCOMM_RET_ERROR;
}
tmp->handle->header_pos++;
if (tmp->handle->header_pos == 6) {
tmp->handle->data_len =
two_to_16(tmp->handle->header + 4);
tmp->handle->data = malloc(tmp->handle->data_len);
tmp->handle->data_pos = 0;
}
} else {
if (recv
(tmp->handle->socket,
&tmp->handle->data[tmp->handle->data_pos], 1,
0) <= 0) {
free(tmp->handle->data);
tmp->handle->data = NULL;
shutdown(tmp->handle->socket, 0x02);
tmp->handle->socket = -1;
tmp->handle->connected = 0;
tmp->handle->header_pos = 0;
if (tmp->handle->callbacks[IMCOMM_ERROR])
tmp->handle->callbacks[IMCOMM_ERROR] (tmp->handle,
IMCOMM_ERROR_DISCONNECTED);
return IMCOMM_RET_ERROR;
}
tmp->handle->data_pos++;
if (tmp->handle->data_pos == tmp->handle->data_len) {
ret =
flap_decode(tmp->handle, tmp->handle->header,
tmp->handle->data);
tmp->handle->data = NULL;
tmp->handle->header_pos = 0;
}
}
#endif
}
/*
* This seems to slow down the DOS port on slower machines,
* so let's get rid of it...
*/
#ifdef IMCOMM_KEEPALIVE
if (time(NULL) - tmp->handle->last_keepalive_time > 300) {
tmp->handle->last_keepalive_time = time(NULL);
if (tmp->handle->srv_pause == 0)
flap_send(tmp->handle, 0x05, NULL, 0, 0);
}
#endif
#if !defined(__DJGPP__) && !defined(NO_AUTO_IDLE)
imcomm_set_idle_time(tmp->handle,
time(NULL) -
tmp->handle->last_operation_time);
#endif
}
#endif
return ret;
}
/* PROTO */
int
imcomm_internal_add_buddy(void *handle, char *sn, const unsigned long idletime, const unsigned long onlinetime, int isaway)
{
IMCOMM_BUDDYLIST *temp, *trav;
char *sname;
sname = imcomm_simplify_sn(sn);
for (trav = ((IMCOMM *) handle)->buddies_online; trav != NULL;
trav = trav->next) {
if (strcmp(sname, trav->sn) == 0) {
free(sname);
return 0;
}
}
temp = malloc(sizeof(IMCOMM_BUDDYLIST));
temp->sn = sname;
temp->formattedsn = NULL;
temp->next = NULL;
temp->isaway = isaway;
temp->idletime = idletime;
temp->onlinetime = onlinetime;
if (((IMCOMM *) handle)->buddies_online == NULL)
((IMCOMM *) handle)->buddies_online = temp;
else {
for (trav = ((IMCOMM *) handle)->buddies_online;
trav->next != NULL; trav = trav->next);
trav->next = temp;
}
return 1;
}
/* PROTO */
void
imcomm_update_buddy_times(void *handle, const char *sn, int type, unsigned long value)
{
IMCOMM_BUDDYLIST *trav = ((IMCOMM *) handle)->buddies_online;
char *sname;
sname = imcomm_simplify_sn(sn);
for (; trav != NULL; trav = trav->next) {
if (strcmp(sname, trav->sn) == 0) {
switch (type) {
case 1:
trav->idletime = value;
if (((IMCOMM *) handle)->callbacks[IMCOMM_IM_IDLEINFO])
((IMCOMM *) handle)->
callbacks[IMCOMM_IM_IDLEINFO] (handle, sn, value);
break;
case 2:
trav->onlinetime = value;
break;
}
}
}
free(sname);
}
/* PROTO */
void
imcomm_update_buddy_away(void *handle, const char *sn, int isaway)
{
char *sname;
IMCOMM_BUDDYLIST *trav = ((IMCOMM *) handle)->buddies_online;
sname = imcomm_simplify_sn(sn);
for (; trav != NULL; trav = trav->next) {
if (strcmp(sname, trav->sn) == 0) {
if (isaway) {
if (!trav->isaway) {
trav->isaway = 1;
if (((IMCOMM *) handle)->
callbacks[IMCOMM_IM_BUDDYAWAY])
((IMCOMM *) handle)->
callbacks[IMCOMM_IM_BUDDYAWAY] (handle, sn);
break;
}
} else if (trav->isaway) {
trav->isaway = 0;
if (((IMCOMM *) handle)->callbacks[IMCOMM_IM_BUDDYUNAWAY])
((IMCOMM *) handle)->
callbacks[IMCOMM_IM_BUDDYUNAWAY] (handle, sn);
break;
}
}
}
free(sname);
}
/* PROTO */
void
imcomm_internal_delete_buddy(void *handle, const char *sn)
{
char *sname;
IMCOMM_BUDDYLIST *temp, *trav;
if (((IMCOMM *) handle)->buddies_online == NULL)
return;
sname = imcomm_simplify_sn(sn);
if (strcmp(((IMCOMM *) handle)->buddies_online->sn, sname) == 0) {
temp = ((IMCOMM *) handle)->buddies_online;
((IMCOMM *) handle)->buddies_online =
((IMCOMM *) handle)->buddies_online->next;
free(temp->sn);
free(temp);
free(sname);
} else {
for (trav = ((IMCOMM *) handle)->buddies_online;
trav->next != NULL; trav = trav->next) {
if (strcmp(trav->next->sn, sname) == 0) {
temp = trav->next;
trav->next = trav->next->next;
free(temp->sn);
free(temp);
free(sname);
break;
}
}
}
}
/* PROTO */
char *
imcomm_simplify_sn(const char *sn)
{
char *temp;
int x, count;
temp = malloc(strlen(sn) + 1);
for (x = 0, count = 0; x < (int) strlen(sn); x++) {
if (sn[x] == ' ')
continue;
temp[count] = tolower(sn[x]);
count++;
}
temp = realloc(temp, count + 1);
temp[count] = 0;
return temp;
}
/* PROTO */
void
imcomm_set_idle_time(void *handle, uint32_t idlesecs)
{
pkt_t *packet;
packet = pkt_init(4);
pkt_add32(packet, idlesecs);
if (idlesecs > 600) {
if (((IMCOMM *) handle)->isidle == 0) {
((IMCOMM *) handle)->isidle = 1;
snac_sendpkt(handle, 0x01, 0x11, packet, 0);
}
} else {
if (((IMCOMM *) handle)->isidle && idlesecs == 0) {
((IMCOMM *) handle)->isidle = 0;
snac_sendpkt(handle, 0x01, 0x11, packet, 0);
}
}
pkt_free(packet);
}
/* PROTO */
void
imcomm_set_profile(void *handle, char *profile)
{
if (((IMCOMM *) handle)->profile_str != NULL)
free(((IMCOMM *) handle)->profile_str);
((IMCOMM *) handle)->profile_str = (unsigned char *) strdup(profile);
if (((IMCOMM *) handle)->socket != 0)
snac_set_location_info(handle);
}
/* PROTO */
void
imcomm_set_invisible(void *handle, int inv)
{
((IMCOMM *) handle)->isinvisible = inv;
snac_send_cli_update(handle);
}
/* PROTO */
void
imcomm_set_away(void *handle, char *msg)
{
IMCOMM *tmp = (IMCOMM *) handle;
tmp->isaway = 1;
tmp->away_msg = (unsigned char *) strdup(msg);
if (tmp->socket != 0)
snac_set_location_info(handle);
}
/* PROTO */
void
imcomm_set_unaway(void *handle)
{
IMCOMM *tmp = (IMCOMM *) handle;
if (tmp->isaway) {
free(tmp->away_msg);
tmp->isaway = 0;
tmp->away_msg = NULL;
if (tmp->socket != 0)
snac_set_location_info(handle);
}
}
/* PROTO */
int
imcomm_compare_nicks(void *handle, const char *s1, const char *s2)
{
char *s3, *s4;
int ret = 0;
s3 = imcomm_simplify_sn(s1);
s4 = imcomm_simplify_sn(s2);
if (strcmp(s3, s4) == 0)
ret = 1;
free(s3);
free(s4);
return ret;
}
/* PROTO */
uint16_t
imcomm_get_max_message_size(void *handle)
{
return ((IMCOMM *) handle)->max_message_size;
}