/** _ ** (_)_ __ __ ___ _ __ _ __ ** | | ' \/ _/ _ \ ' \| ' \ ** |_|_|_|_\__\___/_|_|_|_|_|_| ** ** 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; }