# HG changeset patch # User Matti Hamalainen # Date 1337830706 -10800 # Node ID 3e64acb433e8fd18ae44fd313952c138c4719acc # Parent 664b94a58dbe85e63a1851f966ef295fb75359fb Split libnnchat into libnnet and libnnutil diff -r 664b94a58dbe -r 3e64acb433e8 Makefile --- a/Makefile Thu May 24 06:23:43 2012 +0300 +++ b/Makefile Thu May 24 06:38:26 2012 +0300 @@ -5,7 +5,7 @@ # C-compiler, flags and linker flags CC=gcc -CFLAGS=-DHAVE_STRING_H -DHAVE_INT_TYPES +CFLAGS=-DHAVE_STRING_H -DHAVE_STDINT_H LDFLAGS=-lncurses #CFLAGS += -DHAVE_STDINT_H diff -r 664b94a58dbe -r 3e64acb433e8 Makefile.gen --- a/Makefile.gen Thu May 24 06:23:43 2012 +0300 +++ b/Makefile.gen Thu May 24 06:38:26 2012 +0300 @@ -36,7 +36,7 @@ nnchat.c: VERSION -$(NNCHAT_BIN): nnchat.c $(OBJPATH)libnnchat.o $(THLIBS_A) $(EXTRAOBJS) +$(NNCHAT_BIN): nnchat.c $(OBJPATH)libnnutil.o $(OBJPATH)libnnnet.o $(THLIBS_A) $(EXTRAOBJS) $(CC) $(CFLAGS) -o $@ $+ $(LDFLAGS) -DNN_VERSION=\"$(NN_VERSION)\" -I$(THLIBS) # diff -r 664b94a58dbe -r 3e64acb433e8 libnnchat.c --- a/libnnchat.c Thu May 24 06:23:43 2012 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1272 +0,0 @@ -/* - * NNChat - Custom chat client for NewbieNudes.com chatrooms - * Written by Matti 'ccr' Hämäläinen - * (C) Copyright 2008-2012 Tecnic Software productions (TNSP) - */ -#include "libnnchat.h" - - -#ifdef __WIN32 -const char *hstrerror(int err) -{ - static char buf[64]; - snprintf(buf, sizeof(buf), "Error #%d", err); - return buf; -} - -int nn_get_socket_errno(void) -{ - return WSAGetLastError(); -} - -const char *nn_get_socket_errstr(int err) -{ - static char buf[64]; - switch (err) - { - case WSAEADDRINUSE: return "Address already in use"; - case WSAECONNABORTED: return "Software caused connection abort"; - case WSAECONNREFUSED: return "Connection refused"; - case WSAECONNRESET: return "Connection reset by peer"; - case WSAEHOSTUNREACH: return "No route to host"; - case WSAENETDOWN: return "Network is down"; - case WSAETIMEDOUT: return "Connection timed out"; - case WSAHOST_NOT_FOUND: return "Host not found"; - case WSAVERNOTSUPPORTED: return "Wrong WinSock DLL version"; - default: - snprintf(buf, sizeof(buf), "Error #%d", err); - return buf; - break; - } -} -#else -int nn_get_socket_errno(void) -{ - return errno; -} - -const char *nn_get_socket_errstr(int err) -{ - return strerror(err); -} -#endif - - -void nn_conn_err(nn_conn_t *conn, const char *fmt, ...) -{ - if (conn->errfunc != NULL) - { - va_list ap; - va_start(ap, fmt); - conn->errfunc(conn, fmt, ap); - va_end(ap); - } -} - - -static void nn_conn_msg(nn_conn_t *conn, const char *fmt, ...) -{ - if (conn->msgfunc != NULL) - { - va_list ap; - va_start(ap, fmt); - conn->msgfunc(conn, fmt, ap); - va_end(ap); - } -} - -struct hostent *nn_resolve_host(nn_conn_t *conn, const char *name) -{ - struct hostent *res = gethostbyname(name); - if (res == NULL) - nn_conn_err(conn, "Could not resolve hostname: %s\n", strerror(h_errno)); - else - nn_conn_msg(conn, "True hostname for %s is %s\n", name, res->h_name); - - return res; -} - -static const char *nn_proxy_types[] = -{ - "none", - "SOCKS 4", - "SOCKS 4a", - NULL -}; - - -nn_conn_t * nn_conn_new( - void (*errfunc)(nn_conn_t *conn, const char *fmt, va_list ap), - void (*msgfunc)(nn_conn_t *conn, const char *fmt, va_list ap)) -{ - nn_conn_t *conn = th_calloc(1, sizeof(nn_conn_t)); - - if (conn == NULL) - return NULL; - - conn->errfunc = errfunc; - conn->msgfunc = msgfunc; - - return conn; -} - -static BOOL nn_get_addr(struct in_addr *addr, struct hostent *hst) -{ - if (hst != NULL) - { - *addr = *(struct in_addr *) (hst->h_addr); - return TRUE; - } - else - { - addr->s_addr = 0; - return FALSE; - } -} - -int nn_conn_set_proxy(nn_conn_t *conn, int type, int port, const char *host) -{ - if (conn == NULL) - return -1; - - conn->proxy.type = type; - conn->proxy.port = port; - conn->proxy.host = th_strdup(host); - - if (host != NULL) - { - conn->proxy.hst = nn_resolve_host(conn, host); - nn_get_addr(&(conn->proxy.addr), conn->proxy.hst); - } - else - return -2; - - return 0; -} - -int nn_conn_open(nn_conn_t *conn, const int port, const char *host) -{ - struct sockaddr_in dest; - static const char *userid = "James Bond"; - - if (conn == NULL) - return -1; - - conn->port = port; - if (host != NULL) - { - conn->host = th_strdup(host); - conn->hst = nn_resolve_host(conn, host); - } - - nn_get_addr(&(conn->addr), conn->hst); - - /* Prepare for connection */ - dest.sin_family = AF_INET; - - if (conn->proxy.type > NN_PROXY_NONE && conn->proxy.type < NN_PROXY_LAST) - { - dest.sin_port = htons(conn->proxy.port); - dest.sin_addr = conn->proxy.addr; - - nn_conn_msg(conn, "Connecting to %s proxy %s:%d ...\n", - nn_proxy_types[conn->proxy.type], - inet_ntoa(conn->proxy.addr), conn->proxy.port); - } - else - { - dest.sin_port = htons(conn->port); - dest.sin_addr = conn->addr; - - nn_conn_msg(conn, "Connecting to %s:%d ...\n", - inet_ntoa(conn->addr), conn->port); - } - - if ((conn->socket = socket(PF_INET, SOCK_STREAM, 0)) == -1) - { - conn->err = nn_get_socket_errno(); - nn_conn_err(conn, "Could not open socket: %s\n", nn_get_socket_errstr(conn->err)); - goto error; - } - - nn_conn_msg(conn, "Using socket %d.\n", conn->socket); - - if (connect(conn->socket, (struct sockaddr *) &dest, sizeof(dest)) == -1) - { - conn->err = nn_get_socket_errno(); - nn_conn_err(conn, "Could not connect: %s\n", nn_get_socket_errstr(conn->err)); - goto error; - } - - FD_ZERO(&(conn->sockfds)); - FD_SET(conn->socket, &(conn->sockfds)); - - /* Proxy-specific setup */ - if (conn->proxy.type == NN_PROXY_SOCKS4 || conn->proxy.type == NN_PROXY_SOCKS4A) - { - struct nn_socks_t *socksh; - size_t bufsiz = sizeof(struct nn_socks_t) + strlen(userid) + 1; - char *ptr, *buf; - int tries, status = -1; - - if (conn->proxy.type == NN_PROXY_SOCKS4A) - bufsiz += strlen(conn->host) + 1; - - ptr = buf = th_malloc(bufsiz); - if (buf == NULL) - { - conn->err = -1; - nn_conn_err(conn, "Could not allocate memory for SOCKS negotiation buffer, %d bytes.\n", bufsiz); - goto error; - } - - /* Create SOCKS 4/4A request */ - nn_conn_msg(conn, "Initializing proxy negotiation.\n"); - socksh = (struct nn_socks_t *) buf; - socksh->version = 4; - socksh->command = SOCKS_CMD_CONNECT; - socksh->port = htons(port); - if (conn->proxy.type == NN_PROXY_SOCKS4A) - socksh->addr = htonl(0x00000032); - else - socksh->addr = conn->addr.s_addr; - ptr += sizeof(struct nn_socks_t); - - strcpy(ptr, userid); - - if (conn->proxy.type == NN_PROXY_SOCKS4A) - { - ptr += strlen(userid) + 1; - strcpy(ptr, conn->host); - } - - /* Send request */ - nn_conn_reset(conn); - if (!nn_conn_send_buf(conn, buf, bufsiz)) - { - th_free(buf); - nn_conn_err(conn, "Error sending SOCKS proxy request.\n"); - goto error; - } - th_free(buf); - - /* Wait for SOCKS server to reply */ - for (status = tries = 1; tries <= 20 && status > 0; tries++) - { -#ifdef __WIN32 - Sleep(50); -#else - usleep(50000); -#endif - nn_conn_reset(conn); - status = nn_conn_pull(conn); - } - - /* Check results */ - if (status == 0) - { - struct nn_socks_res_t *res = (struct nn_socks_res_t *) &(conn->buf); - if (res->nb != 0) - { - nn_conn_err(conn, "Invalid SOCKS server reply, does not begin with NUL byte (%d).\n", res->nb); - goto error; - } - if (res->result != 0x5a) - { - char *s = NULL; - switch (res->result) - { - case 0x5b: s = "Request rejected or failed"; break; - case 0x5c: s = "Request failed because client is not running identd (or not reachable from the server)"; break; - case 0x5d: s = "Request failed because client's identd could not confirm the user ID string in the request"; break; - default: s = "Unknown SOCKS error response"; break; - } - nn_conn_err(conn, "SOCKS setup failed, 0x%02x: %s.\n", res->result, s); - goto error; - } - nn_conn_msg(conn, "SOCKS connection established!\n"); - } - else if (status < 0) - { - nn_conn_err(conn, "Proxy negotiation failed at try %d with network error: %d\n", tries, status); - goto error; - } - else - { - nn_conn_err(conn, "Proxy negotiation timed out.\n"); - goto error; - } - } - - nn_conn_reset(conn); - conn->status = NN_CONN_OPEN; - return 0; - -error: - conn->status = NN_CONN_CLOSED; - return -2; -} - - -void nn_conn_close(nn_conn_t *conn) -{ - if (conn == NULL) - return; - - if (conn->socket >= 0) - { -#ifdef __WIN32 - closesocket(conn->socket); -#else - close(conn->socket); -#endif - conn->socket = -1; - } - - th_free(conn->host); - th_free(conn->proxy.host); - - conn->status = NN_CONN_CLOSED; - - th_free(conn); -} - - -BOOL nn_conn_send_buf(nn_conn_t *conn, const char *buf, const size_t len) -{ - size_t bufLeft = len; - const char *bufPtr = buf; - - while (bufLeft > 0) - { - ssize_t bufSent; - bufSent = send(conn->socket, bufPtr, bufLeft, 0); - if (bufSent < 0) - { - conn->err = nn_get_socket_errno(); - nn_conn_err(conn, "nn_conn_send_buf() failed: %s", nn_get_socket_errstr(conn->err)); - return FALSE; - } - bufLeft -= bufSent; - bufPtr += bufSent; - } - - return TRUE; -} - -void nn_conn_reset(nn_conn_t *conn) -{ - if (conn != NULL) - { - conn->ptr = conn->in_ptr = conn->buf; - conn->got_bytes = conn->total_bytes = 0; - } -} - - -int nn_conn_pull(nn_conn_t *conn) -{ - int result; - struct timeval socktv; - fd_set tmpfds; - - if (conn == NULL) - return -10; - - /* Prod the input buffer */ - if (conn->in_ptr > conn->buf && conn->in_ptr - conn->ptr > 0) - { - size_t delta = conn->in_ptr - conn->ptr; - memmove(conn->buf, conn->in_ptr, delta); - conn->ptr = conn->buf; - conn->in_ptr -= delta; - conn->total_bytes -= delta; - } - - /* Check for incoming data */ - socktv.tv_sec = 0; - socktv.tv_usec = NN_DELAY_USEC; - tmpfds = conn->sockfds; - - if ((result = select(conn->socket + 1, &tmpfds, NULL, NULL, &socktv)) == -1) - { - conn->err = nn_get_socket_errno(); - if (conn->err != EINTR) - { - nn_conn_err(conn, "Error occured in select(%d, sockfds): %d, %s\n", - socket, conn->err, nn_get_socket_errstr(conn->err)); - return -1; - } - } - else if (FD_ISSET(conn->socket, &tmpfds)) - { - conn->got_bytes = recv(conn->socket, conn->in_ptr, NN_CONNBUF_SIZE - conn->total_bytes, 0); - if (conn->got_bytes < 0) - { - conn->err = nn_get_socket_errno(); - nn_conn_err(conn, "Error in recv: %d, %s\n", conn->err, nn_get_socket_errstr(conn->err)); - return -2; - } - else if (conn->got_bytes == 0) - { - nn_conn_err(conn, "Server closed connection.\n"); - conn->status = NN_CONN_CLOSED; - return -3; - } - else - { - conn->total_bytes += conn->got_bytes; - conn->in_ptr += conn->got_bytes; - return 0; - } - } - - return 1; -} - - -BOOL nn_conn_check(nn_conn_t *conn) -{ - if (conn == NULL) - return FALSE; - - return conn->err == 0 && conn->status == NN_CONN_OPEN; -} - - -BOOL nn_network_init(void) -{ -#ifdef __WIN32 - /* Initialize WinSock, if needed */ - WSADATA wsaData; - int err = WSAStartup(0x0101, &wsaData); - if (err != 0) - { - THERR("Could not initialize WinSock library (err=%d).\n", err); - return FALSE; - } -#endif - return TRUE; -} - - -void nn_network_close(void) -{ -#ifdef __WIN32 - WSACleanup(); -#endif -} - - -BOOL nn_conn_buf_check(nn_conn_t *conn, size_t n) -{ - return conn && conn->ptr && conn->in_ptr && (conn->ptr + n <= conn->in_ptr); -} - - -BOOL nn_conn_buf_skip(nn_conn_t *conn, size_t n) -{ - if (nn_conn_buf_check(conn, n)) - { - conn->ptr += n; - return TRUE; - } - else - return FALSE; -} - - -int nn_conn_buf_strncmp(nn_conn_t *conn, const char *str, const size_t n) -{ - int ret; - if (!nn_conn_buf_check(conn, n)) - return -1; - - if ((ret = strncmp(conn->ptr, str, n)) == 0) - { - conn->ptr += n; - return 0; - } - else - return ret; -} - - -int nn_conn_buf_strcmp(nn_conn_t *conn, const char *str) -{ - return nn_conn_buf_strncmp(conn, str, strlen(str)); -} - - -char *nn_conn_buf_strstr(nn_conn_t *conn, const char *str) -{ - char *pos; - size_t n = strlen(str); - - if (nn_conn_buf_check(conn, n) && ((pos = strstr(conn->ptr, str)) != NULL)) - { - conn->ptr = pos + n; - return pos; - } - else - return NULL; -} - - -#define PUSHCHAR(x) th_vputch(&result, &resSize, &resPos, x) -#define PUSHSTR(x) th_vputs(&result, &resSize, &resPos, x) - -char *nn_encode_str1(const char *str) -{ - const char *s = str; - char *result; - size_t resSize, resPos = 0; - - if (str == NULL) return NULL; - - resSize = strlen(str) + NN_ALLOC_SIZE; - if ((result = th_malloc(resSize)) == NULL) - return NULL; - - while (*s) - { - switch (*s) - { - case 32: - PUSHCHAR('+'); - break; - - default: - if (th_isalnum(*s)) - PUSHCHAR(*s); - else - { - char tmpStr[4]; - snprintf(tmpStr, sizeof(tmpStr), "%2X", (unsigned char) *s); - PUSHCHAR('%'); - PUSHSTR(tmpStr); - } - break; - } - s++; - } - PUSHCHAR(0); - - return result; -} - - -static int nn_get_hexdigit(const int c, const int shift) -{ - int i; - - if (c >= 'A' && c <= 'F') - i = c - 'A' + 10; - else if (c >= 'a' && c <= 'f') - i = c - 'a' + 10; - else if (c >= '0' && c <= '9') - i = c - '0'; - else - return -1; - - return i << shift; -} - - -char *nn_decode_str1(const char *str) -{ - const char *s = str; - char *result; - size_t resSize, resPos = 0; - int c; - - if (str == NULL) return NULL; - - resSize = strlen(str) + NN_ALLOC_SIZE; - if ((result = th_malloc(resSize)) == NULL) - return NULL; - - while (*s) - { - switch (*s) - { - case '+': - PUSHCHAR(' '); - s++; - break; - - case '½': - /* Escape these .. */ - PUSHCHAR('½'); - PUSHCHAR('½'); - s++; - break; - - case '\r': - PUSHCHAR(' '); - s++; - break; - - case '%': - s++; - if (*s == '%') - PUSHCHAR('%'); - else if ((c = nn_get_hexdigit(*s, 4)) >= 0) - { - int i = nn_get_hexdigit(*(++s), 0); - if (i >= 0) - { - PUSHCHAR(c | i); - } - else - { - PUSHCHAR('§'); - PUSHCHAR(*s); - } - } - else - { - PUSHCHAR('§'); - PUSHCHAR(*s); - } - s++; - break; - - default: - PUSHCHAR(*s); - s++; - } - } - PUSHCHAR(0); - - return result; -} - - -char *nn_strip_tags(const char *str) -{ - const char *s = str; - char *result; - size_t resSize, resPos = 0; - - if (str == NULL) return NULL; - - resSize = strlen(str) + NN_ALLOC_SIZE; - if ((result = th_malloc(resSize)) == NULL) - return NULL; - - while (*s) - { - if (*s == '<') - { - while (*s && *s != '>') s++; - if (*s == '>') s++; - } - else - PUSHCHAR(*s++); - } - PUSHCHAR(0); - - return result; -} - - -typedef struct -{ - char c; - char *ent; -} html_entity_t; - - -static const html_entity_t HTMLEntities[] = -{ - { '<', "<" }, - { '>', ">" }, -}; - -static const int numHTMLEntities = sizeof(HTMLEntities) / sizeof(HTMLEntities[0]); - - -char *nn_encode_str2(const char *str) -{ - const char *s = str; - char *result; - size_t resSize, resPos = 0; - - if (str == NULL) return NULL; - - resSize = strlen(str) + NN_ALLOC_SIZE; - if ((result = th_malloc(resSize)) == NULL) - return NULL; - - while (*s) - { - int i; - BOOL found = FALSE; - for (i = 0; i < numHTMLEntities; i++) - if (HTMLEntities[i].c == *s) - { - PUSHSTR(HTMLEntities[i].ent); - found = TRUE; - break; - } - if (!found) PUSHCHAR(*s); - - s++; - } - PUSHCHAR(0); - - return result; -} - - -char *nn_decode_str2(const char *str) -{ - const char *s = str; - char *result; - size_t resSize, resPos = 0; - - if (str == NULL) return NULL; - - resSize = strlen(str); - if ((result = th_malloc(resSize)) == NULL) - return NULL; - - while (*s) - { - if (*s == '&') - { - int i; - BOOL found = FALSE; - for (i = 0; i < numHTMLEntities; i++) - { - const html_entity_t *ent = &HTMLEntities[i]; - int len = strlen(ent->ent); - if (!strncmp(s, ent->ent, len)) - { - PUSHCHAR(ent->c); - s += len; - found = TRUE; - break; - } - } - if (!found) PUSHCHAR(*s++); - } - else - PUSHCHAR(*s++); - } - PUSHCHAR(0); - - return result; -} - - -char *nn_dbldecode_str(const char *str) -{ - char *res, *tmp; - - if ((tmp = nn_decode_str1(str)) == NULL) - return NULL; - - res = nn_decode_str2(tmp); - th_free(tmp); - - return res; -} - - -char *nn_dblencode_str(const char *str) -{ - char *res, *tmp; - - if ((tmp = nn_encode_str2(str)) == NULL) - return NULL; - - res = nn_encode_str1(tmp); - th_free(tmp); - - return res; -} - - -BOOL nn_conn_send_msg(nn_conn_t *conn, const char *user, const char *fmt, ...) -{ - char *tmp, *msg; - va_list ap; - - va_start(ap, fmt); - tmp = th_strdup_vprintf(fmt, ap); - va_end(ap); - - if (tmp == NULL) - return FALSE; - - msg = th_strdup_printf("%s%s", user, tmp); - th_free(tmp); - - if (msg != NULL) - { - BOOL ret = nn_conn_send_buf(conn, msg, strlen(msg) + 1); - th_free(msg); - return ret; - } - else - return FALSE; -} - - -int nn_editbuf_write(nn_editbuf_t *buf, ssize_t pos, int ch) -{ - if (buf->len+1 >= buf->size) return -3; - - if (pos < 0) - return -1; - else if (pos >= buf->len) - buf->data[buf->len++] = ch; - else - buf->data[pos] = ch; - - return 0; -} - - -int nn_editbuf_insert(nn_editbuf_t *buf, ssize_t pos, int ch) -{ - if (buf->len+1 >= buf->size) return -3; - - if (pos < 0) - return -1; - else if (pos >= buf->len) - { - buf->data[buf->len] = ch; - } - else - { - memmove(&(buf->data[pos+1]), &(buf->data[pos]), buf->len - pos + 1); - buf->data[pos] = ch; - } - buf->len++; - return 0; -} - - -int nn_editbuf_delete(nn_editbuf_t *buf, ssize_t pos) -{ - if (pos < 0) - return -1; - else if (pos < buf->len) - { - memmove(&(buf->data[pos]), &(buf->data[pos+1]), buf->len - pos); - buf->len--; - return 0; - } - else - return -2; -} - - -void nn_editbuf_clear(nn_editbuf_t *buf) -{ - buf->len = 0; - buf->pos = 0; -} - - -nn_editbuf_t * nn_editbuf_new(ssize_t n) -{ - nn_editbuf_t *res = th_calloc(1, sizeof(nn_editbuf_t)); - - res->data = (char *) th_malloc(n); - res->size = n; - - return res; -} - - -void nn_editbuf_free(nn_editbuf_t *buf) -{ - if (buf != NULL) - { - th_free(buf->data); - th_free(buf); - } -} - - -nn_editbuf_t * nn_editbuf_copy(nn_editbuf_t *src) -{ - nn_editbuf_t *res; - - assert(src != NULL); - - if (src == NULL) return NULL; - - if ((res = nn_editbuf_new(src->size)) == NULL) - return NULL; - - memcpy(res->data, src->data, src->size); - res->pos = res->len = src->len; - - return res; -} - - -char * nn_editbuf_get_string(nn_editbuf_t *buf, ssize_t start, ssize_t end) -{ - char *str; - ssize_t siz; - - if (buf == NULL) - return NULL; - - if (start < 0 || end > buf->len || start >= buf->len) - return NULL; - - if (end < 0) - { - siz = buf->len - start + 1; - } - else if (start <= end) - { - siz = end - start + 1; - } - else - return NULL; - - if ((str = th_malloc(siz + 1)) == NULL) - return NULL; - - memcpy(str, buf->data + start, siz); - str[siz] = 0; - - return str; -} - - -void nn_editbuf_setpos(nn_editbuf_t *buf, ssize_t pos) -{ - assert(buf != NULL); - - if (pos < 0) - buf->pos = 0; - else if (pos >= buf->len) - buf->pos = buf->len; - else - buf->pos = pos; -} - - -static uint8_t nn_hash_user(const char *name) -{ -/* - int n = 0; - const uint8_t *c = (uint8_t *)name; - uint8_t hash = 0xff; - - while (*c && n < 4) - { - hash = (hash << 1) ^ tolower(*c); - c++; n++; - } - - return (hash & 0xff); -*/ - return tolower(name[0]); -} - - -static void nn_user_insert(nn_user_t **list, nn_user_t *node) -{ - node->next = *list; - *list = node; -} - - -nn_user_t *nn_userhash_foreach(const nn_userhash_t *list, int (*func)(const nn_user_t *)) -{ - int i; - - if (list == NULL) return NULL; - - for (i = 0; i < NN_NUM_BUCKETS; i++) - if (list->buckets[i] != NULL) - { - nn_user_t *curr = list->buckets[i]; - while (curr != NULL) - { - if (func(curr) != 0) - return curr; - curr = curr->next; - } - } - - return NULL; -} - - -nn_user_t *nn_user_find(const nn_userhash_t *list, const char *name) -{ - uint8_t hash; - - if (list == NULL) return NULL; - - hash = nn_hash_user(name); - if (list->buckets[hash] != NULL) - { - nn_user_t *curr = list->buckets[hash]; - while (curr != NULL) - { - if (th_strcasecmp(curr->name, name) == 0) - return curr; - curr = curr->next; - } - } - - return NULL; -} - - -static nn_user_t *nn_user_match_do(nn_user_t *list, const char *pattern, size_t len) -{ - nn_user_t *curr = list; - - while (curr != NULL) - { - if (len <= strlen(curr->name) && th_strncasecmp(curr->name, pattern, len) == 0) - return curr; - curr = curr->next; - } - return NULL; -} - - -nn_user_t *nn_user_match(const nn_userhash_t *list, const char *pattern, const char *current, BOOL again) -{ - uint8_t hash; - - if (list == NULL || pattern == NULL) return NULL; - - hash = nn_hash_user(pattern); - if (list->buckets[hash] != NULL) - { - nn_user_t *curr = list->buckets[hash]; - size_t len = strlen(pattern); - - if (current != NULL) - { - nn_user_t *found = NULL; - while (curr != NULL) - { - if (th_strcasecmp(curr->name, current) == 0) - { - if (again) - return curr; - found = curr->next; - break; - } - curr = curr->next; - } - - if (found != NULL && (found = nn_user_match_do(found, pattern, len)) != NULL) - return found; - } - - if ((curr = nn_user_match_do(list->buckets[hash], pattern, len)) != NULL) - return curr; - } - - return NULL; -} - - -nn_userhash_t *nn_userhash_new(void) -{ - return th_calloc(1, sizeof(nn_userhash_t)); -} - - -int nn_userhash_insert(nn_userhash_t *list, const char *name) -{ - uint8_t hash; - nn_user_t *user; - - /* Check arguments */ - if (list == NULL || name == NULL) - return -1; - - /* Check if username is already there */ - if (nn_user_find(list, name) != NULL) - return 1; - - /* No, we'll add it */ - if ((user = th_calloc(1, sizeof(nn_user_t))) == NULL) - return -3; - - user->name = th_strdup(name); - if (user->name == NULL) - return -4; - - hash = nn_hash_user(name); - nn_user_insert(&(list->buckets[hash]), user); - - return 0; -} - - -int nn_userhash_delete(nn_userhash_t *list, const char *name) -{ - uint8_t hash; - - /* Check arguments */ - if (list == NULL || name == NULL) - return -1; - - /* Check if username is already there */ - hash = nn_hash_user(name); - if (list->buckets[hash] != NULL) - { - nn_user_t *curr, *prev; - curr = list->buckets[hash]; - prev = NULL; - while (curr != NULL) - { - if (th_strcasecmp(curr->name, name) == 0) - { - if (prev) - prev->next = curr->next; - else - list->buckets[hash] = curr->next; - - nn_user_free(curr); - - return 0; - } - else - { - prev = curr; - curr = curr->next; - } - } - } - - return 1; -} - - -nn_user_t *nn_user_copy(const nn_user_t *src) -{ - nn_user_t *user; - - if (src == NULL) return NULL; - - if ((user = th_calloc(1, sizeof(nn_user_t))) == NULL) - return NULL; - - /* Copy relevant data */ - user->name = th_strdup(src->name); - user->lastspoke = src->lastspoke; - user->joined = src->joined; - - return user; -} - - -void nn_user_free(nn_user_t *user) -{ - th_free(user->name); - th_free(user); -} - - -void nn_user_free_list(nn_user_t *list) -{ - nn_user_t *next, *curr = list; - - while (curr != NULL) - { - next = curr->next; - nn_user_free(curr); - curr = next; - } -} - -void nn_userhash_free(nn_userhash_t *hash) -{ - int i; - if (hash == NULL) - return; - - for (i = 0; i < NN_NUM_BUCKETS; i++) - { - nn_user_free_list(hash->buckets[i]); - hash->buckets[i] = NULL; - } - - th_free(hash); -} - - -char *nn_username_encode(char *str) -{ - unsigned char *c = (unsigned char *) str; - if (str == NULL) return NULL; - for (; *c ; c++) - if (*c == ' ') *c = 255; - return str; -} - - -char *nn_username_decode(char *str) -{ - unsigned char *c = (unsigned char *) str; - if (str == NULL) return NULL; - for (; *c ; c++) - if (*c == 255) *c = ' '; - return str; -} - - -nn_window_t *nn_window_new(const char *id) -{ - nn_window_t *res = th_calloc(1, sizeof(nn_window_t)); - - if (res == NULL) return NULL; - - res->data = th_ringbuf_new(NN_BACKBUF_LEN, th_free); - if (res->data == NULL) - { - th_free(res); - return NULL; - } - - res->id = th_strdup(id); - - return res; -} - - -void nn_window_free(nn_window_t *win) -{ - if (win != NULL) - { - th_ringbuf_free(win->data); - th_free(win->id); - th_free(win); - } -} - - -nn_strtuple_t *nn_strtuple_new(size_t len, char *str) -{ - nn_strtuple_t *tuple = th_calloc(1, sizeof(nn_strtuple_t)); - tuple->len = len; - tuple->str = str; - return tuple; -} - - -void nn_strtuple_free(nn_strtuple_t *tuple) -{ - th_free(tuple->str); - th_free(tuple); -} diff -r 664b94a58dbe -r 3e64acb433e8 libnnchat.h --- a/libnnchat.h Thu May 24 06:23:43 2012 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,215 +0,0 @@ -/* - * NNChat - Custom chat client for NewbieNudes.com chatrooms - * Written by Matti 'ccr' Hämäläinen - * (C) Copyright 2008-2012 Tecnic Software productions (TNSP) - */ -#ifndef LIBNNCHAT_H -#define LIBNNCHAT_H - -#include -#include -#include "th_types.h" -#ifdef __WIN32 -#include -#include -typedef uint16_t in_port_t; -typedef uint32_t in_addr_t; -#else -#include -#include -#ifdef HAVE_NETINET_IN_H -#include -#endif -#include -#include -#endif -#include -#include -#include -#include "th_string.h" - -#define NN_TMPBUF_SIZE (4096) -#define NN_ALLOC_SIZE (128) -#define NN_CONNBUF_SIZE (64 * 1024) -#define NN_NUM_BUCKETS (256) -#define NN_DELAY_USEC (15 * 1000) -#define NN_BACKBUF_LEN (512) /* Backbuffer size (in lines) */ - -enum -{ - NN_CONN_UNINIT = 0, - NN_CONN_PROXY_NEG, - NN_CONN_OPEN, - NN_CONN_CLOSED -}; - -enum -{ - NN_PROXY_NONE = 0, - NN_PROXY_SOCKS4, - NN_PROXY_SOCKS4A, - - NN_PROXY_LAST -}; - -enum -{ - SOCKS_CMD_CONNECT = 1, - SOCKS_CMD_BIND = 2 -}; - -struct nn_socks_t -{ - uint8_t version; - uint8_t command; - in_port_t port; - in_addr_t addr; -} __attribute__((__packed__)); - -struct nn_socks_res_t -{ - uint8_t nb; - uint8_t result; - in_port_t port; - in_addr_t addr; -} __attribute__((__packed__)); - -typedef struct _nn_conn_t -{ - struct - { - char *host; - struct hostent *hst; - int type; - int port; - struct in_addr addr; - } proxy; - - char *host; - struct hostent *hst; - int port; - - int socket; - struct in_addr addr; - fd_set sockfds; - - void (*errfunc)(struct _nn_conn_t *conn, const char *fmt, va_list ap); - void (*msgfunc)(struct _nn_conn_t *conn, const char *fmt, va_list ap); - - int err; - int status; - - char buf[NN_CONNBUF_SIZE + 16]; - char *ptr, *in_ptr; - ssize_t got_bytes, total_bytes; -} nn_conn_t; - - -const char *nn_get_errstr(int err); -BOOL nn_network_init(); -void nn_network_close(void); - -struct hostent *nn_resolve_host(nn_conn_t *conn, const char *name); -nn_conn_t * nn_conn_new( - void (*errfunc)(nn_conn_t *conn, const char *fmt, va_list ap), - void (*msgfunc)(nn_conn_t *conn, const char *fmt, va_list ap)); - -int nn_conn_set_proxy(nn_conn_t *conn, int type, int port, const char *host); -int nn_conn_open(nn_conn_t *conn, const int port, const char *host); -void nn_conn_close(nn_conn_t *); -void nn_conn_reset(nn_conn_t *); -int nn_conn_pull(nn_conn_t *); -BOOL nn_conn_send_buf(nn_conn_t *, const char *buf, const size_t len); -BOOL nn_conn_send_msg(nn_conn_t *, const char *user, const char *fmt, ...); -BOOL nn_conn_check(nn_conn_t *); - - -BOOL nn_conn_buf_check(nn_conn_t *conn, size_t n); -BOOL nn_conn_buf_skip(nn_conn_t *conn, size_t n); -int nn_conn_buf_strncmp(nn_conn_t *conn, const char *str, const size_t n); -int nn_conn_buf_strcmp(nn_conn_t *conn, const char *str); -char * nn_conn_buf_strstr(nn_conn_t *conn, const char *str); - - -typedef struct _nn_user_t -{ - char *name; - time_t lastspoke, joined; - struct _nn_user_t *next; -} nn_user_t; - -typedef struct -{ - nn_user_t *buckets[NN_NUM_BUCKETS]; -} nn_userhash_t; - -nn_userhash_t *nn_userhash_new(void); -nn_user_t * nn_userhash_foreach(const nn_userhash_t *, int (*func)(const nn_user_t *)); -nn_user_t * nn_user_match(const nn_userhash_t *list, const char *str, const char *current, BOOL again); -int nn_userhash_insert(nn_userhash_t *, const char *name); -int nn_userhash_delete(nn_userhash_t *, const char *name); -void nn_userhash_free(nn_userhash_t *); -void nn_user_free(nn_user_t *); -void nn_user_free_list(nn_user_t *); -nn_user_t * nn_user_copy(const nn_user_t *src); -nn_user_t * nn_user_find(const nn_userhash_t *list, const char *name); - - -char * nn_encode_str1(const char *str); -char * nn_decode_str1(const char *str); -char * nn_encode_str2(const char *str); -char * nn_decode_str2(const char *str); -char * nn_strip_tags(const char *str); -char * nn_dbldecode_str(const char *str); -char * nn_dblencode_str(const char *str); - -char * nn_username_encode(char *str); -char * nn_username_decode(char *str); - - -typedef struct -{ - ssize_t pos, len, size; - char *data; -} nn_editbuf_t; - -int nn_editbuf_write(nn_editbuf_t *buf, ssize_t pos, int ch); -int nn_editbuf_insert(nn_editbuf_t *buf, ssize_t pos, int ch); -int nn_editbuf_delete(nn_editbuf_t *buf, ssize_t pos); -void nn_editbuf_clear(nn_editbuf_t *buf); -nn_editbuf_t * nn_editbuf_new(ssize_t n); -void nn_editbuf_free(nn_editbuf_t *buf); -nn_editbuf_t * nn_editbuf_copy(nn_editbuf_t *src); -void nn_editbuf_setpos(nn_editbuf_t *buf, ssize_t pos); -char * nn_editbuf_get_string(nn_editbuf_t *buf, ssize_t start, ssize_t end); - - -typedef struct -{ - qringbuf_t *data; /* "Backbuffer" data for this window */ - int pos; /* Current position in the window, 0 = real time */ - BOOL dirty; - - char *id; /* Chatter ID, NULL = main window */ - int num; /* Window number */ - - char *buf; - size_t len, bufsize; - size_t chlen; -} nn_window_t; - -nn_window_t *nn_window_new(const char *); -void nn_window_free(nn_window_t *); - - -typedef struct -{ - size_t len; - char *str; -} nn_strtuple_t; - - -nn_strtuple_t *nn_strtuple_new(size_t, char *); -void nn_strtuple_free(nn_strtuple_t *); - -#endif diff -r 664b94a58dbe -r 3e64acb433e8 libnnnet.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libnnnet.c Thu May 24 06:38:26 2012 +0300 @@ -0,0 +1,540 @@ +/* + * NNChat - Custom chat client for NewbieNudes.com chatrooms + * Written by Matti 'ccr' Hämäläinen + * (C) Copyright 2008-2012 Tecnic Software productions (TNSP) + */ +#include "libnnnet.h" + + + +#ifdef __WIN32 +const char *hstrerror(int err) +{ + static char buf[64]; + snprintf(buf, sizeof(buf), "Error #%d", err); + return buf; +} + +int nn_get_socket_errno(void) +{ + return WSAGetLastError(); +} + +const char *nn_get_socket_errstr(int err) +{ + static char buf[64]; + switch (err) + { + case WSAEADDRINUSE: return "Address already in use"; + case WSAECONNABORTED: return "Software caused connection abort"; + case WSAECONNREFUSED: return "Connection refused"; + case WSAECONNRESET: return "Connection reset by peer"; + case WSAEHOSTUNREACH: return "No route to host"; + case WSAENETDOWN: return "Network is down"; + case WSAETIMEDOUT: return "Connection timed out"; + case WSAHOST_NOT_FOUND: return "Host not found"; + case WSAVERNOTSUPPORTED: return "Wrong WinSock DLL version"; + default: + snprintf(buf, sizeof(buf), "Error #%d", err); + return buf; + break; + } +} +#else +int nn_get_socket_errno(void) +{ + return errno; +} + +const char *nn_get_socket_errstr(int err) +{ + return strerror(err); +} +#endif + + +void nn_conn_err(nn_conn_t *conn, const char *fmt, ...) +{ + if (conn->errfunc != NULL) + { + va_list ap; + va_start(ap, fmt); + conn->errfunc(conn, fmt, ap); + va_end(ap); + } +} + + +static void nn_conn_msg(nn_conn_t *conn, const char *fmt, ...) +{ + if (conn->msgfunc != NULL) + { + va_list ap; + va_start(ap, fmt); + conn->msgfunc(conn, fmt, ap); + va_end(ap); + } +} + +struct hostent *nn_resolve_host(nn_conn_t *conn, const char *name) +{ + struct hostent *res = gethostbyname(name); + if (res == NULL) + nn_conn_err(conn, "Could not resolve hostname: %s\n", strerror(h_errno)); + else + nn_conn_msg(conn, "True hostname for %s is %s\n", name, res->h_name); + + return res; +} + +static const char *nn_proxy_types[] = +{ + "none", + "SOCKS 4", + "SOCKS 4a", + NULL +}; + + +nn_conn_t * nn_conn_new( + void (*errfunc)(nn_conn_t *conn, const char *fmt, va_list ap), + void (*msgfunc)(nn_conn_t *conn, const char *fmt, va_list ap)) +{ + nn_conn_t *conn = th_calloc(1, sizeof(nn_conn_t)); + + if (conn == NULL) + return NULL; + + conn->errfunc = errfunc; + conn->msgfunc = msgfunc; + + return conn; +} + +static BOOL nn_get_addr(struct in_addr *addr, struct hostent *hst) +{ + if (hst != NULL) + { + *addr = *(struct in_addr *) (hst->h_addr); + return TRUE; + } + else + { + addr->s_addr = 0; + return FALSE; + } +} + +int nn_conn_set_proxy(nn_conn_t *conn, int type, int port, const char *host) +{ + if (conn == NULL) + return -1; + + conn->proxy.type = type; + conn->proxy.port = port; + conn->proxy.host = th_strdup(host); + + if (host != NULL) + { + conn->proxy.hst = nn_resolve_host(conn, host); + nn_get_addr(&(conn->proxy.addr), conn->proxy.hst); + } + else + return -2; + + return 0; +} + +int nn_conn_open(nn_conn_t *conn, const int port, const char *host) +{ + struct sockaddr_in dest; + static const char *userid = "James Bond"; + + if (conn == NULL) + return -1; + + conn->port = port; + if (host != NULL) + { + conn->host = th_strdup(host); + conn->hst = nn_resolve_host(conn, host); + } + + nn_get_addr(&(conn->addr), conn->hst); + + /* Prepare for connection */ + dest.sin_family = AF_INET; + + if (conn->proxy.type > NN_PROXY_NONE && conn->proxy.type < NN_PROXY_LAST) + { + dest.sin_port = htons(conn->proxy.port); + dest.sin_addr = conn->proxy.addr; + + nn_conn_msg(conn, "Connecting to %s proxy %s:%d ...\n", + nn_proxy_types[conn->proxy.type], + inet_ntoa(conn->proxy.addr), conn->proxy.port); + } + else + { + dest.sin_port = htons(conn->port); + dest.sin_addr = conn->addr; + + nn_conn_msg(conn, "Connecting to %s:%d ...\n", + inet_ntoa(conn->addr), conn->port); + } + + if ((conn->socket = socket(PF_INET, SOCK_STREAM, 0)) == -1) + { + conn->err = nn_get_socket_errno(); + nn_conn_err(conn, "Could not open socket: %s\n", nn_get_socket_errstr(conn->err)); + goto error; + } + + nn_conn_msg(conn, "Using socket %d.\n", conn->socket); + + if (connect(conn->socket, (struct sockaddr *) &dest, sizeof(dest)) == -1) + { + conn->err = nn_get_socket_errno(); + nn_conn_err(conn, "Could not connect: %s\n", nn_get_socket_errstr(conn->err)); + goto error; + } + + FD_ZERO(&(conn->sockfds)); + FD_SET(conn->socket, &(conn->sockfds)); + + /* Proxy-specific setup */ + if (conn->proxy.type == NN_PROXY_SOCKS4 || conn->proxy.type == NN_PROXY_SOCKS4A) + { + struct nn_socks_t *socksh; + size_t bufsiz = sizeof(struct nn_socks_t) + strlen(userid) + 1; + char *ptr, *buf; + int tries, status = -1; + + if (conn->proxy.type == NN_PROXY_SOCKS4A) + bufsiz += strlen(conn->host) + 1; + + ptr = buf = th_malloc(bufsiz); + if (buf == NULL) + { + conn->err = -1; + nn_conn_err(conn, "Could not allocate memory for SOCKS negotiation buffer, %d bytes.\n", bufsiz); + goto error; + } + + /* Create SOCKS 4/4A request */ + nn_conn_msg(conn, "Initializing proxy negotiation.\n"); + socksh = (struct nn_socks_t *) buf; + socksh->version = 4; + socksh->command = SOCKS_CMD_CONNECT; + socksh->port = htons(port); + if (conn->proxy.type == NN_PROXY_SOCKS4A) + socksh->addr = htonl(0x00000032); + else + socksh->addr = conn->addr.s_addr; + ptr += sizeof(struct nn_socks_t); + + strcpy(ptr, userid); + + if (conn->proxy.type == NN_PROXY_SOCKS4A) + { + ptr += strlen(userid) + 1; + strcpy(ptr, conn->host); + } + + /* Send request */ + nn_conn_reset(conn); + if (!nn_conn_send_buf(conn, buf, bufsiz)) + { + th_free(buf); + nn_conn_err(conn, "Error sending SOCKS proxy request.\n"); + goto error; + } + th_free(buf); + + /* Wait for SOCKS server to reply */ + for (status = tries = 1; tries <= 20 && status > 0; tries++) + { +#ifdef __WIN32 + Sleep(50); +#else + usleep(50000); +#endif + nn_conn_reset(conn); + status = nn_conn_pull(conn); + } + + /* Check results */ + if (status == 0) + { + struct nn_socks_res_t *res = (struct nn_socks_res_t *) &(conn->buf); + if (res->nb != 0) + { + nn_conn_err(conn, "Invalid SOCKS server reply, does not begin with NUL byte (%d).\n", res->nb); + goto error; + } + if (res->result != 0x5a) + { + char *s = NULL; + switch (res->result) + { + case 0x5b: s = "Request rejected or failed"; break; + case 0x5c: s = "Request failed because client is not running identd (or not reachable from the server)"; break; + case 0x5d: s = "Request failed because client's identd could not confirm the user ID string in the request"; break; + default: s = "Unknown SOCKS error response"; break; + } + nn_conn_err(conn, "SOCKS setup failed, 0x%02x: %s.\n", res->result, s); + goto error; + } + nn_conn_msg(conn, "SOCKS connection established!\n"); + } + else if (status < 0) + { + nn_conn_err(conn, "Proxy negotiation failed at try %d with network error: %d\n", tries, status); + goto error; + } + else + { + nn_conn_err(conn, "Proxy negotiation timed out.\n"); + goto error; + } + } + + nn_conn_reset(conn); + conn->status = NN_CONN_OPEN; + return 0; + +error: + conn->status = NN_CONN_CLOSED; + return -2; +} + + +void nn_conn_close(nn_conn_t *conn) +{ + if (conn == NULL) + return; + + if (conn->socket >= 0) + { +#ifdef __WIN32 + closesocket(conn->socket); +#else + close(conn->socket); +#endif + conn->socket = -1; + } + + th_free(conn->host); + th_free(conn->proxy.host); + + conn->status = NN_CONN_CLOSED; + + th_free(conn); +} + + +BOOL nn_conn_send_buf(nn_conn_t *conn, const char *buf, const size_t len) +{ + size_t bufLeft = len; + const char *bufPtr = buf; + + while (bufLeft > 0) + { + ssize_t bufSent; + bufSent = send(conn->socket, bufPtr, bufLeft, 0); + if (bufSent < 0) + { + conn->err = nn_get_socket_errno(); + nn_conn_err(conn, "nn_conn_send_buf() failed: %s", nn_get_socket_errstr(conn->err)); + return FALSE; + } + bufLeft -= bufSent; + bufPtr += bufSent; + } + + return TRUE; +} + +void nn_conn_reset(nn_conn_t *conn) +{ + if (conn != NULL) + { + conn->ptr = conn->in_ptr = conn->buf; + conn->got_bytes = conn->total_bytes = 0; + } +} + + +int nn_conn_pull(nn_conn_t *conn) +{ + int result; + struct timeval socktv; + fd_set tmpfds; + + if (conn == NULL) + return -10; + + /* Prod the input buffer */ + if (conn->in_ptr > conn->buf && conn->in_ptr - conn->ptr > 0) + { + size_t delta = conn->in_ptr - conn->ptr; + memmove(conn->buf, conn->in_ptr, delta); + conn->ptr = conn->buf; + conn->in_ptr -= delta; + conn->total_bytes -= delta; + } + + /* Check for incoming data */ + socktv.tv_sec = 0; + socktv.tv_usec = NN_DELAY_USEC; + tmpfds = conn->sockfds; + + if ((result = select(conn->socket + 1, &tmpfds, NULL, NULL, &socktv)) == -1) + { + conn->err = nn_get_socket_errno(); + if (conn->err != EINTR) + { + nn_conn_err(conn, "Error occured in select(%d, sockfds): %d, %s\n", + socket, conn->err, nn_get_socket_errstr(conn->err)); + return -1; + } + } + else if (FD_ISSET(conn->socket, &tmpfds)) + { + conn->got_bytes = recv(conn->socket, conn->in_ptr, NN_CONNBUF_SIZE - conn->total_bytes, 0); + if (conn->got_bytes < 0) + { + conn->err = nn_get_socket_errno(); + nn_conn_err(conn, "Error in recv: %d, %s\n", conn->err, nn_get_socket_errstr(conn->err)); + return -2; + } + else if (conn->got_bytes == 0) + { + nn_conn_err(conn, "Server closed connection.\n"); + conn->status = NN_CONN_CLOSED; + return -3; + } + else + { + conn->total_bytes += conn->got_bytes; + conn->in_ptr += conn->got_bytes; + return 0; + } + } + + return 1; +} + + +BOOL nn_conn_check(nn_conn_t *conn) +{ + if (conn == NULL) + return FALSE; + + return conn->err == 0 && conn->status == NN_CONN_OPEN; +} + + +BOOL nn_network_init(void) +{ +#ifdef __WIN32 + /* Initialize WinSock, if needed */ + WSADATA wsaData; + int err = WSAStartup(0x0101, &wsaData); + if (err != 0) + { + THERR("Could not initialize WinSock library (err=%d).\n", err); + return FALSE; + } +#endif + return TRUE; +} + + +void nn_network_close(void) +{ +#ifdef __WIN32 + WSACleanup(); +#endif +} + + +BOOL nn_conn_buf_check(nn_conn_t *conn, size_t n) +{ + return conn && conn->ptr && conn->in_ptr && (conn->ptr + n <= conn->in_ptr); +} + + +BOOL nn_conn_buf_skip(nn_conn_t *conn, size_t n) +{ + if (nn_conn_buf_check(conn, n)) + { + conn->ptr += n; + return TRUE; + } + else + return FALSE; +} + + +int nn_conn_buf_strncmp(nn_conn_t *conn, const char *str, const size_t n) +{ + int ret; + if (!nn_conn_buf_check(conn, n)) + return -1; + + if ((ret = strncmp(conn->ptr, str, n)) == 0) + { + conn->ptr += n; + return 0; + } + else + return ret; +} + + +int nn_conn_buf_strcmp(nn_conn_t *conn, const char *str) +{ + return nn_conn_buf_strncmp(conn, str, strlen(str)); +} + + +char *nn_conn_buf_strstr(nn_conn_t *conn, const char *str) +{ + char *pos; + size_t n = strlen(str); + + if (nn_conn_buf_check(conn, n) && ((pos = strstr(conn->ptr, str)) != NULL)) + { + conn->ptr = pos + n; + return pos; + } + else + return NULL; +} + + +BOOL nn_conn_send_msg(nn_conn_t *conn, const char *user, const char *fmt, ...) +{ + char *tmp, *msg; + va_list ap; + + va_start(ap, fmt); + tmp = th_strdup_vprintf(fmt, ap); + va_end(ap); + + if (tmp == NULL) + return FALSE; + + msg = th_strdup_printf("%s%s", user, tmp); + th_free(tmp); + + if (msg != NULL) + { + BOOL ret = nn_conn_send_buf(conn, msg, strlen(msg) + 1); + th_free(msg); + return ret; + } + else + return FALSE; +} diff -r 664b94a58dbe -r 3e64acb433e8 libnnnet.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libnnnet.h Thu May 24 06:38:26 2012 +0300 @@ -0,0 +1,132 @@ +/* + * NNChat - Custom chat client for NewbieNudes.com chatrooms + * Written by Matti 'ccr' Hämäläinen + * (C) Copyright 2008-2012 Tecnic Software productions (TNSP) + */ +#ifndef LIBNNCHAT_H +#define LIBNNCHAT_H + +#include +#include +#include "th_types.h" +#include "th_string.h" + +#ifdef __WIN32 +#include +#include +typedef uint16_t in_port_t; +typedef uint32_t in_addr_t; +#else +#include +#include +#ifdef HAVE_NETINET_IN_H +#include +#endif +#include +#include +#endif +#include +#include +#include + + +#define NN_CONNBUF_SIZE (64 * 1024) +#define NN_DELAY_USEC (15 * 1000) + + +enum +{ + NN_CONN_UNINIT = 0, + NN_CONN_PROXY_NEG, + NN_CONN_OPEN, + NN_CONN_CLOSED +}; + +enum +{ + NN_PROXY_NONE = 0, + NN_PROXY_SOCKS4, + NN_PROXY_SOCKS4A, + + NN_PROXY_LAST +}; + +enum +{ + SOCKS_CMD_CONNECT = 1, + SOCKS_CMD_BIND = 2 +}; + +struct nn_socks_t +{ + uint8_t version; + uint8_t command; + in_port_t port; + in_addr_t addr; +} __attribute__((__packed__)); + +struct nn_socks_res_t +{ + uint8_t nb; + uint8_t result; + in_port_t port; + in_addr_t addr; +} __attribute__((__packed__)); + +typedef struct _nn_conn_t +{ + struct + { + char *host; + struct hostent *hst; + int type; + int port; + struct in_addr addr; + } proxy; + + char *host; + struct hostent *hst; + int port; + + int socket; + struct in_addr addr; + fd_set sockfds; + + void (*errfunc)(struct _nn_conn_t *conn, const char *fmt, va_list ap); + void (*msgfunc)(struct _nn_conn_t *conn, const char *fmt, va_list ap); + + int err; + int status; + + char buf[NN_CONNBUF_SIZE + 16]; + char *ptr, *in_ptr; + ssize_t got_bytes, total_bytes; +} nn_conn_t; + + +const char *nn_get_errstr(int err); +BOOL nn_network_init(); +void nn_network_close(void); + +struct hostent *nn_resolve_host(nn_conn_t *conn, const char *name); +nn_conn_t * nn_conn_new( + void (*errfunc)(nn_conn_t *conn, const char *fmt, va_list ap), + void (*msgfunc)(nn_conn_t *conn, const char *fmt, va_list ap)); + +int nn_conn_set_proxy(nn_conn_t *conn, int type, int port, const char *host); +int nn_conn_open(nn_conn_t *conn, const int port, const char *host); +void nn_conn_close(nn_conn_t *); +void nn_conn_reset(nn_conn_t *); +int nn_conn_pull(nn_conn_t *); +BOOL nn_conn_send_buf(nn_conn_t *, const char *buf, const size_t len); +BOOL nn_conn_send_msg(nn_conn_t *, const char *user, const char *fmt, ...); +BOOL nn_conn_check(nn_conn_t *); + + +BOOL nn_conn_buf_check(nn_conn_t *conn, size_t n); +BOOL nn_conn_buf_skip(nn_conn_t *conn, size_t n); +int nn_conn_buf_strncmp(nn_conn_t *conn, const char *str, const size_t n); +int nn_conn_buf_strcmp(nn_conn_t *conn, const char *str); +char * nn_conn_buf_strstr(nn_conn_t *conn, const char *str); + +#endif diff -r 664b94a58dbe -r 3e64acb433e8 libnnutil.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libnnutil.c Thu May 24 06:38:26 2012 +0300 @@ -0,0 +1,739 @@ +/* + * NNChat - Custom chat client for NewbieNudes.com chatrooms + * Written by Matti 'ccr' Hämäläinen + * (C) Copyright 2008-2012 Tecnic Software productions (TNSP) + */ +#include "libnnutil.h" + + +#define PUSHCHAR(x) th_vputch(&result, &resSize, &resPos, x) +#define PUSHSTR(x) th_vputs(&result, &resSize, &resPos, x) + +char *nn_encode_str1(const char *str) +{ + const char *s = str; + char *result; + size_t resSize, resPos = 0; + + if (str == NULL) return NULL; + + resSize = strlen(str) + NN_ALLOC_SIZE; + if ((result = th_malloc(resSize)) == NULL) + return NULL; + + while (*s) + { + switch (*s) + { + case 32: + PUSHCHAR('+'); + break; + + default: + if (th_isalnum(*s)) + PUSHCHAR(*s); + else + { + char tmpStr[4]; + snprintf(tmpStr, sizeof(tmpStr), "%2X", (unsigned char) *s); + PUSHCHAR('%'); + PUSHSTR(tmpStr); + } + break; + } + s++; + } + PUSHCHAR(0); + + return result; +} + + +static int nn_get_hexdigit(const int c, const int shift) +{ + int i; + + if (c >= 'A' && c <= 'F') + i = c - 'A' + 10; + else if (c >= 'a' && c <= 'f') + i = c - 'a' + 10; + else if (c >= '0' && c <= '9') + i = c - '0'; + else + return -1; + + return i << shift; +} + + +char *nn_decode_str1(const char *str) +{ + const char *s = str; + char *result; + size_t resSize, resPos = 0; + int c; + + if (str == NULL) return NULL; + + resSize = strlen(str) + NN_ALLOC_SIZE; + if ((result = th_malloc(resSize)) == NULL) + return NULL; + + while (*s) + { + switch (*s) + { + case '+': + PUSHCHAR(' '); + s++; + break; + + case '½': + /* Escape these .. */ + PUSHCHAR('½'); + PUSHCHAR('½'); + s++; + break; + + case '\r': + PUSHCHAR(' '); + s++; + break; + + case '%': + s++; + if (*s == '%') + PUSHCHAR('%'); + else if ((c = nn_get_hexdigit(*s, 4)) >= 0) + { + int i = nn_get_hexdigit(*(++s), 0); + if (i >= 0) + { + PUSHCHAR(c | i); + } + else + { + PUSHCHAR('§'); + PUSHCHAR(*s); + } + } + else + { + PUSHCHAR('§'); + PUSHCHAR(*s); + } + s++; + break; + + default: + PUSHCHAR(*s); + s++; + } + } + PUSHCHAR(0); + + return result; +} + + +char *nn_strip_tags(const char *str) +{ + const char *s = str; + char *result; + size_t resSize, resPos = 0; + + if (str == NULL) return NULL; + + resSize = strlen(str) + NN_ALLOC_SIZE; + if ((result = th_malloc(resSize)) == NULL) + return NULL; + + while (*s) + { + if (*s == '<') + { + while (*s && *s != '>') s++; + if (*s == '>') s++; + } + else + PUSHCHAR(*s++); + } + PUSHCHAR(0); + + return result; +} + + +typedef struct +{ + char c; + char *ent; +} html_entity_t; + + +static const html_entity_t HTMLEntities[] = +{ + { '<', "<" }, + { '>', ">" }, +}; + +static const int numHTMLEntities = sizeof(HTMLEntities) / sizeof(HTMLEntities[0]); + + +char *nn_encode_str2(const char *str) +{ + const char *s = str; + char *result; + size_t resSize, resPos = 0; + + if (str == NULL) return NULL; + + resSize = strlen(str) + NN_ALLOC_SIZE; + if ((result = th_malloc(resSize)) == NULL) + return NULL; + + while (*s) + { + int i; + BOOL found = FALSE; + for (i = 0; i < numHTMLEntities; i++) + if (HTMLEntities[i].c == *s) + { + PUSHSTR(HTMLEntities[i].ent); + found = TRUE; + break; + } + if (!found) PUSHCHAR(*s); + + s++; + } + PUSHCHAR(0); + + return result; +} + + +char *nn_decode_str2(const char *str) +{ + const char *s = str; + char *result; + size_t resSize, resPos = 0; + + if (str == NULL) return NULL; + + resSize = strlen(str); + if ((result = th_malloc(resSize)) == NULL) + return NULL; + + while (*s) + { + if (*s == '&') + { + int i; + BOOL found = FALSE; + for (i = 0; i < numHTMLEntities; i++) + { + const html_entity_t *ent = &HTMLEntities[i]; + int len = strlen(ent->ent); + if (!strncmp(s, ent->ent, len)) + { + PUSHCHAR(ent->c); + s += len; + found = TRUE; + break; + } + } + if (!found) PUSHCHAR(*s++); + } + else + PUSHCHAR(*s++); + } + PUSHCHAR(0); + + return result; +} + + +char *nn_dbldecode_str(const char *str) +{ + char *res, *tmp; + + if ((tmp = nn_decode_str1(str)) == NULL) + return NULL; + + res = nn_decode_str2(tmp); + th_free(tmp); + + return res; +} + + +char *nn_dblencode_str(const char *str) +{ + char *res, *tmp; + + if ((tmp = nn_encode_str2(str)) == NULL) + return NULL; + + res = nn_encode_str1(tmp); + th_free(tmp); + + return res; +} + + +int nn_editbuf_write(nn_editbuf_t *buf, ssize_t pos, int ch) +{ + if (buf->len+1 >= buf->size) return -3; + + if (pos < 0) + return -1; + else if (pos >= buf->len) + buf->data[buf->len++] = ch; + else + buf->data[pos] = ch; + + return 0; +} + + +int nn_editbuf_insert(nn_editbuf_t *buf, ssize_t pos, int ch) +{ + if (buf->len+1 >= buf->size) return -3; + + if (pos < 0) + return -1; + else if (pos >= buf->len) + { + buf->data[buf->len] = ch; + } + else + { + memmove(&(buf->data[pos+1]), &(buf->data[pos]), buf->len - pos + 1); + buf->data[pos] = ch; + } + buf->len++; + return 0; +} + + +int nn_editbuf_delete(nn_editbuf_t *buf, ssize_t pos) +{ + if (pos < 0) + return -1; + else if (pos < buf->len) + { + memmove(&(buf->data[pos]), &(buf->data[pos+1]), buf->len - pos); + buf->len--; + return 0; + } + else + return -2; +} + + +void nn_editbuf_clear(nn_editbuf_t *buf) +{ + buf->len = 0; + buf->pos = 0; +} + + +nn_editbuf_t * nn_editbuf_new(ssize_t n) +{ + nn_editbuf_t *res = th_calloc(1, sizeof(nn_editbuf_t)); + + res->data = (char *) th_malloc(n); + res->size = n; + + return res; +} + + +void nn_editbuf_free(nn_editbuf_t *buf) +{ + if (buf != NULL) + { + th_free(buf->data); + th_free(buf); + } +} + + +nn_editbuf_t * nn_editbuf_copy(nn_editbuf_t *src) +{ + nn_editbuf_t *res; + + assert(src != NULL); + + if (src == NULL) return NULL; + + if ((res = nn_editbuf_new(src->size)) == NULL) + return NULL; + + memcpy(res->data, src->data, src->size); + res->pos = res->len = src->len; + + return res; +} + + +char * nn_editbuf_get_string(nn_editbuf_t *buf, ssize_t start, ssize_t end) +{ + char *str; + ssize_t siz; + + if (buf == NULL) + return NULL; + + if (start < 0 || end > buf->len || start >= buf->len) + return NULL; + + if (end < 0) + { + siz = buf->len - start + 1; + } + else if (start <= end) + { + siz = end - start + 1; + } + else + return NULL; + + if ((str = th_malloc(siz + 1)) == NULL) + return NULL; + + memcpy(str, buf->data + start, siz); + str[siz] = 0; + + return str; +} + + +void nn_editbuf_setpos(nn_editbuf_t *buf, ssize_t pos) +{ + assert(buf != NULL); + + if (pos < 0) + buf->pos = 0; + else if (pos >= buf->len) + buf->pos = buf->len; + else + buf->pos = pos; +} + + +static uint8_t nn_hash_user(const char *name) +{ +/* + int n = 0; + const uint8_t *c = (uint8_t *)name; + uint8_t hash = 0xff; + + while (*c && n < 4) + { + hash = (hash << 1) ^ tolower(*c); + c++; n++; + } + + return (hash & 0xff); +*/ + return tolower(name[0]); +} + + +static void nn_user_insert(nn_user_t **list, nn_user_t *node) +{ + node->next = *list; + *list = node; +} + + +nn_user_t *nn_userhash_foreach(const nn_userhash_t *list, int (*func)(const nn_user_t *)) +{ + int i; + + if (list == NULL) return NULL; + + for (i = 0; i < NN_NUM_BUCKETS; i++) + if (list->buckets[i] != NULL) + { + nn_user_t *curr = list->buckets[i]; + while (curr != NULL) + { + if (func(curr) != 0) + return curr; + curr = curr->next; + } + } + + return NULL; +} + + +nn_user_t *nn_user_find(const nn_userhash_t *list, const char *name) +{ + uint8_t hash; + + if (list == NULL) return NULL; + + hash = nn_hash_user(name); + if (list->buckets[hash] != NULL) + { + nn_user_t *curr = list->buckets[hash]; + while (curr != NULL) + { + if (th_strcasecmp(curr->name, name) == 0) + return curr; + curr = curr->next; + } + } + + return NULL; +} + + +static nn_user_t *nn_user_match_do(nn_user_t *list, const char *pattern, size_t len) +{ + nn_user_t *curr = list; + + while (curr != NULL) + { + if (len <= strlen(curr->name) && th_strncasecmp(curr->name, pattern, len) == 0) + return curr; + curr = curr->next; + } + return NULL; +} + + +nn_user_t *nn_user_match(const nn_userhash_t *list, const char *pattern, const char *current, BOOL again) +{ + uint8_t hash; + + if (list == NULL || pattern == NULL) return NULL; + + hash = nn_hash_user(pattern); + if (list->buckets[hash] != NULL) + { + nn_user_t *curr = list->buckets[hash]; + size_t len = strlen(pattern); + + if (current != NULL) + { + nn_user_t *found = NULL; + while (curr != NULL) + { + if (th_strcasecmp(curr->name, current) == 0) + { + if (again) + return curr; + found = curr->next; + break; + } + curr = curr->next; + } + + if (found != NULL && (found = nn_user_match_do(found, pattern, len)) != NULL) + return found; + } + + if ((curr = nn_user_match_do(list->buckets[hash], pattern, len)) != NULL) + return curr; + } + + return NULL; +} + + +nn_userhash_t *nn_userhash_new(void) +{ + return th_calloc(1, sizeof(nn_userhash_t)); +} + + +int nn_userhash_insert(nn_userhash_t *list, const char *name) +{ + uint8_t hash; + nn_user_t *user; + + /* Check arguments */ + if (list == NULL || name == NULL) + return -1; + + /* Check if username is already there */ + if (nn_user_find(list, name) != NULL) + return 1; + + /* No, we'll add it */ + if ((user = th_calloc(1, sizeof(nn_user_t))) == NULL) + return -3; + + user->name = th_strdup(name); + if (user->name == NULL) + return -4; + + hash = nn_hash_user(name); + nn_user_insert(&(list->buckets[hash]), user); + + return 0; +} + + +int nn_userhash_delete(nn_userhash_t *list, const char *name) +{ + uint8_t hash; + + /* Check arguments */ + if (list == NULL || name == NULL) + return -1; + + /* Check if username is already there */ + hash = nn_hash_user(name); + if (list->buckets[hash] != NULL) + { + nn_user_t *curr, *prev; + curr = list->buckets[hash]; + prev = NULL; + while (curr != NULL) + { + if (th_strcasecmp(curr->name, name) == 0) + { + if (prev) + prev->next = curr->next; + else + list->buckets[hash] = curr->next; + + nn_user_free(curr); + + return 0; + } + else + { + prev = curr; + curr = curr->next; + } + } + } + + return 1; +} + + +nn_user_t *nn_user_copy(const nn_user_t *src) +{ + nn_user_t *user; + + if (src == NULL) return NULL; + + if ((user = th_calloc(1, sizeof(nn_user_t))) == NULL) + return NULL; + + /* Copy relevant data */ + user->name = th_strdup(src->name); + user->lastspoke = src->lastspoke; + user->joined = src->joined; + + return user; +} + + +void nn_user_free(nn_user_t *user) +{ + th_free(user->name); + th_free(user); +} + + +void nn_user_free_list(nn_user_t *list) +{ + nn_user_t *next, *curr = list; + + while (curr != NULL) + { + next = curr->next; + nn_user_free(curr); + curr = next; + } +} + +void nn_userhash_free(nn_userhash_t *hash) +{ + int i; + if (hash == NULL) + return; + + for (i = 0; i < NN_NUM_BUCKETS; i++) + { + nn_user_free_list(hash->buckets[i]); + hash->buckets[i] = NULL; + } + + th_free(hash); +} + + +char *nn_username_encode(char *str) +{ + unsigned char *c = (unsigned char *) str; + if (str == NULL) return NULL; + for (; *c ; c++) + if (*c == ' ') *c = 255; + return str; +} + + +char *nn_username_decode(char *str) +{ + unsigned char *c = (unsigned char *) str; + if (str == NULL) return NULL; + for (; *c ; c++) + if (*c == 255) *c = ' '; + return str; +} + + +nn_window_t *nn_window_new(const char *id) +{ + nn_window_t *res = th_calloc(1, sizeof(nn_window_t)); + + if (res == NULL) return NULL; + + res->data = th_ringbuf_new(NN_BACKBUF_LEN, th_free); + if (res->data == NULL) + { + th_free(res); + return NULL; + } + + res->id = th_strdup(id); + + return res; +} + + +void nn_window_free(nn_window_t *win) +{ + if (win != NULL) + { + th_ringbuf_free(win->data); + th_free(win->id); + th_free(win); + } +} + + +nn_strtuple_t *nn_strtuple_new(size_t len, char *str) +{ + nn_strtuple_t *tuple = th_calloc(1, sizeof(nn_strtuple_t)); + tuple->len = len; + tuple->str = str; + return tuple; +} + + +void nn_strtuple_free(nn_strtuple_t *tuple) +{ + th_free(tuple->str); + th_free(tuple); +} diff -r 664b94a58dbe -r 3e64acb433e8 libnnutil.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libnnutil.h Thu May 24 06:38:26 2012 +0300 @@ -0,0 +1,104 @@ +/* + * NNChat - Custom chat client for NewbieNudes.com chatrooms + * Written by Matti 'ccr' Hämäläinen + * (C) Copyright 2008-2012 Tecnic Software productions (TNSP) + */ +#ifndef LIBNNUTIL_H +#define LIBNNUTIL_H + +#include +#include +#include "th_types.h" +#include "th_string.h" + + +#define NN_TMPBUF_SIZE (4096) +#define NN_ALLOC_SIZE (128) +#define NN_NUM_BUCKETS (256) +#define NN_BACKBUF_LEN (512) /* Backbuffer size (in lines) */ + + +typedef struct _nn_user_t +{ + char *name; + time_t lastspoke, joined; + struct _nn_user_t *next; +} nn_user_t; + + +typedef struct +{ + nn_user_t *buckets[NN_NUM_BUCKETS]; +} nn_userhash_t; + + +nn_userhash_t *nn_userhash_new(void); +nn_user_t * nn_userhash_foreach(const nn_userhash_t *, int (*func)(const nn_user_t *)); +nn_user_t * nn_user_match(const nn_userhash_t *list, const char *str, const char *current, BOOL again); +int nn_userhash_insert(nn_userhash_t *, const char *name); +int nn_userhash_delete(nn_userhash_t *, const char *name); +void nn_userhash_free(nn_userhash_t *); +void nn_user_free(nn_user_t *); +void nn_user_free_list(nn_user_t *); +nn_user_t * nn_user_copy(const nn_user_t *src); +nn_user_t * nn_user_find(const nn_userhash_t *list, const char *name); + + +char * nn_encode_str1(const char *str); +char * nn_decode_str1(const char *str); +char * nn_encode_str2(const char *str); +char * nn_decode_str2(const char *str); +char * nn_strip_tags(const char *str); +char * nn_dbldecode_str(const char *str); +char * nn_dblencode_str(const char *str); + +char * nn_username_encode(char *str); +char * nn_username_decode(char *str); + + +typedef struct +{ + ssize_t pos, len, size; + char *data; +} nn_editbuf_t; + +int nn_editbuf_write(nn_editbuf_t *buf, ssize_t pos, int ch); +int nn_editbuf_insert(nn_editbuf_t *buf, ssize_t pos, int ch); +int nn_editbuf_delete(nn_editbuf_t *buf, ssize_t pos); +void nn_editbuf_clear(nn_editbuf_t *buf); +nn_editbuf_t * nn_editbuf_new(ssize_t n); +void nn_editbuf_free(nn_editbuf_t *buf); +nn_editbuf_t * nn_editbuf_copy(nn_editbuf_t *src); +void nn_editbuf_setpos(nn_editbuf_t *buf, ssize_t pos); +char * nn_editbuf_get_string(nn_editbuf_t *buf, ssize_t start, ssize_t end); + + +typedef struct +{ + qringbuf_t *data; /* "Backbuffer" data for this window */ + int pos; /* Current position in the window, 0 = real time */ + BOOL dirty; + + char *id; /* Chatter ID, NULL = main window */ + int num; /* Window number */ + + char *buf; + size_t len, bufsize; + size_t chlen; +} nn_window_t; + +nn_window_t *nn_window_new(const char *); +void nn_window_free(nn_window_t *); + + +typedef struct +{ + size_t len; + char *str; +} nn_strtuple_t; + + +nn_strtuple_t *nn_strtuple_new(size_t, char *); +void nn_strtuple_free(nn_strtuple_t *); + +#endif diff -r 664b94a58dbe -r 3e64acb433e8 nnchat.c --- a/nnchat.c Thu May 24 06:23:43 2012 +0300 +++ b/nnchat.c Thu May 24 06:38:26 2012 +0300 @@ -3,12 +3,10 @@ * Written by Matti 'ccr' Hämäläinen * (C) Copyright 2008-2011 Tecnic Software productions (TNSP) */ -#include "libnnchat.h" -#include +#include "libnnutil.h" +#include "libnnnet.h" #include "th_args.h" #include "th_config.h" -#include -#include #ifdef __WIN32 /* Undefine because both windows.h and curses.h #define it */ #undef MOUSE_MOVED