# HG changeset patch # User Matti Hamalainen # Date 1337830867 -10800 # Node ID 14b685cdbd2c4d13779fecdfe63c1a4d75c1a30c # Parent 3e64acb433e8fd18ae44fd313952c138c4719acc Rename files. diff -r 3e64acb433e8 -r 14b685cdbd2c Makefile.gen --- a/Makefile.gen Thu May 24 06:38:26 2012 +0300 +++ b/Makefile.gen Thu May 24 06:41:07 2012 +0300 @@ -34,9 +34,9 @@ $(THLIBS_A): $(addprefix $(OBJPATH),$(THLIBS_OBJ)) $(AR) cru $@ $+ -nnchat.c: VERSION +main.c: VERSION -$(NNCHAT_BIN): nnchat.c $(OBJPATH)libnnutil.o $(OBJPATH)libnnnet.o $(THLIBS_A) $(EXTRAOBJS) +$(NNCHAT_BIN): main.c $(OBJPATH)util.o $(OBJPATH)network.o $(THLIBS_A) $(EXTRAOBJS) $(CC) $(CFLAGS) -o $@ $+ $(LDFLAGS) -DNN_VERSION=\"$(NN_VERSION)\" -I$(THLIBS) # diff -r 3e64acb433e8 -r 14b685cdbd2c libnnnet.c --- a/libnnnet.c Thu May 24 06:38:26 2012 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,540 +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 "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 3e64acb433e8 -r 14b685cdbd2c libnnnet.h --- a/libnnnet.h Thu May 24 06:38:26 2012 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,132 +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" -#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 3e64acb433e8 -r 14b685cdbd2c libnnutil.c --- a/libnnutil.c Thu May 24 06:38:26 2012 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,739 +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 "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 3e64acb433e8 -r 14b685cdbd2c main.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.c Thu May 24 06:41:07 2012 +0300 @@ -0,0 +1,2158 @@ +/* + * NNChat - Custom chat client for NewbieNudes.com chatrooms + * Written by Matti 'ccr' Hämäläinen + * (C) Copyright 2008-2011 Tecnic Software productions (TNSP) + */ +#include "util.h" +#include "network.h" +#include "th_args.h" +#include "th_config.h" +#ifdef __WIN32 +/* Undefine because both windows.h and curses.h #define it */ +#undef MOUSE_MOVED +#include +#else +#include +#endif +#ifdef HAVE_NCURSES_H +#include +#else +#include +#endif + +#ifdef __WIN32 +#define SET_CONFIG_FILE "nnchat.txt" +#define SET_DIR_SEPARATOR "\\" +#define SET_DELAY (0) +#else +#define SET_CONFIG_FILE ".nnchat" +#define SET_DIR_SEPARATOR "/" +#define SET_DELAY (5) +#endif + +#define SET_NICK_SEPARATOR ':' + +#define SET_MAX_HISTORY (16) /* Command history length */ +#define SET_KEEPALIVE (15*60) /* Ping/keepalive period in seconds */ +#define SET_MAX_WINDOWS (32) + + +/* Options + */ +int optPort = 8005, + optProxyPort = 1080, + optProxyType = NN_PROXY_NONE; +int optUserColor = 0x000000; +char *optServer = "chat.newbienudes.com", + *optProxyServer = NULL, + *optUserName = NULL, + *optUserNameCmd = NULL, + *optUserNameEnc = NULL, + *optPassword = NULL, + *optPasswordCmd = NULL, + *optLogFilename = NULL, + *optSite = "NN", + *optNickSepStr = NULL; +char optNickSep; +BOOL optDaemon = FALSE; +FILE *optLogFile = NULL; +BOOL setIgnoreMode = FALSE; +BOOL optDebug = FALSE; +BOOL optLogEnable = FALSE; + +nn_window_t *chatWindows[SET_MAX_WINDOWS], + *currWin = NULL; +WINDOW *mainWin = NULL, + *statusWin = NULL, + *editWin = NULL; + +qlist_t *setIgnoreList = NULL, + *setIdleMessages = NULL; +nn_userhash_t *nnUsers = NULL; +char *setConfigFile = NULL, + *setBrowser = NULL; +cfgitem_t *cfg = NULL; + + +/* Logging mode flags + */ +enum +{ + LOG_FILE = 1, + LOG_WINDOW = 2, + LOG_STAMP = 4 +}; + + +/* Arguments + */ +optarg_t optList[] = +{ + { 0, '?', "help", "Show this help", OPT_NONE }, + { 1, 'v', "verbose", "Be more verbose", OPT_NONE }, + { 2, 'p', "port", "Connect to port", OPT_ARGREQ }, + { 3, 's', "server", "Server to connect to", OPT_ARGREQ }, + { 4, 'C', "color", "Initial color in RGB hex 000000", OPT_ARGREQ }, + { 5, 'l', "logfile", "Log filename", OPT_ARGREQ }, + { 6, 'D', "daemon", "A pseudo-daemon mode for logging", OPT_NONE }, + { 7, 'f', "force-site", "Force site (default: NN)", OPT_ARGREQ }, + { 8, 'd', "debug", "Enable various debug features", OPT_NONE }, + + {10, '4', "socks4", "SOCKS4 proxy server", OPT_ARGREQ }, + {11, 'A', "socks4a", "SOCKS4A proxy server", OPT_ARGREQ }, + {12, 'P', "proxy-port", "Proxy port (default: 1080)", OPT_ARGREQ }, +}; + +const int optListN = (sizeof(optList) / sizeof(optList[0])); + + +void argShowHelp(void) +{ + th_print_banner(stdout, th_prog_name, + "[options] "); + + th_args_help(stdout, optList, optListN); +} + + +BOOL argHandleOpt(const int optN, char *optArg, char *currArg) +{ + switch (optN) + { + case 0: + argShowHelp(); + exit(0); + break; + + case 1: + th_verbosityLevel++; + break; + + case 2: + optPort = atoi(optArg); + break; + + case 3: + optServer = optArg; + break; + + case 4: + if ((optUserColor = th_get_hex_triplet(optArg)) < 0) + { + THERR("Invalid color argument '%s', should be a RGB hex triplet '000000'.\n", + optArg); + return FALSE; + } + THMSG(1, "Using color #%06x\n", optUserColor); + break; + + case 5: + optLogFilename = optArg; + optLogEnable = TRUE; + break; + + case 7: + optSite = optArg; + break; + + case 6: + optDaemon = TRUE; + THMSG(1, "Running in pseudo-daemon mode.\n"); + break; + + case 8: + optDebug = TRUE; + THMSG(1, "Debug mode enabled.\n"); + break; + + + case 10: + optProxyServer = optArg; + optProxyType = NN_PROXY_SOCKS4; + break; + + case 11: + optProxyServer = optArg; + optProxyType = NN_PROXY_SOCKS4A; + break; + + case 12: + optPort = atoi(optArg); + break; + + + default: + THERR("Unknown option '%s'.\n", currArg); + return FALSE; + } + + return TRUE; +} + + +BOOL argHandleFile(char *currArg) +{ + if (!optUserNameCmd) + optUserNameCmd = currArg; + else if (!optPasswordCmd) + optPasswordCmd = currArg; + else + { + THERR("Username '%s' already specified on commandline!\n", optUserNameCmd); + return FALSE; + } + + return TRUE; +} + +BOOL str_get_timestamp(char *str, size_t len, const char *fmt) +{ + time_t stamp = time(NULL); + struct tm *stamp_tm; + if ((stamp_tm = localtime(&stamp)) != NULL) + { + strftime(str, len, fmt, stamp_tm); + return TRUE; + } + else + { + str[0] = 0; + return FALSE; + } +} + + +char * str_trim_left(char *buf) +{ + while (*buf != 0 && th_isspace(*buf)) buf++; + return buf; +} + +int compareUsername(const void *s1, const void *s2) +{ + return th_strcasecmp((char *) s1, (char *) s2); +} + +nn_window_t *findWindow(const char *id) +{ + int i; + + for (i = 0; i < SET_MAX_WINDOWS; i++) + if (chatWindows[i] != NULL && + chatWindows[i]->id != NULL && + th_strcasecmp(id, chatWindows[i]->id) == 0) + return chatWindows[i]; + + return NULL; +} + + +BOOL openWindow(const char *name, BOOL curwin) +{ + int i; + nn_window_t *res; + if (name == NULL) + return FALSE; + + if ((res = nn_window_new(name)) == NULL) + return FALSE; + + for (i = 1; i < SET_MAX_WINDOWS; i++) + if (chatWindows[i] == NULL) + { + res->num = i; + chatWindows[i] = res; + if (curwin) + currWin = res; + return TRUE; + } + + return FALSE; +} + + +void closeWindow(nn_window_t *win) +{ + int i; + if (win == NULL) return; + + for (i = 1; i < SET_MAX_WINDOWS; i++) + if (chatWindows[i] == win) + { + chatWindows[i] = NULL; + nn_window_free(win); + return; + } +} + + +void updateStatus(void) +{ + char tmpStr[128]; + int i; + + if (statusWin == NULL) return; + + str_get_timestamp(tmpStr, sizeof(tmpStr), "%H:%M:%S"); + + wbkgdset(statusWin, COLOR_PAIR(10)); + werase(statusWin); + + wattrset(statusWin, A_BOLD | COLOR_PAIR(11)); + mvwaddstr(statusWin, 0, 1, tmpStr); + + wattrset(statusWin, A_BOLD | COLOR_PAIR(13)); + waddstr(statusWin, " | "); + wattrset(statusWin, A_BOLD | COLOR_PAIR(16)); + waddstr(statusWin, optUserName); + wattrset(statusWin, A_BOLD | COLOR_PAIR(13)); + + wattrset(statusWin, A_BOLD | COLOR_PAIR(13)); + waddstr(statusWin, " | "); + wattrset(statusWin, A_BOLD | COLOR_PAIR(11)); + snprintf(tmpStr, sizeof(tmpStr), "#%06x", optUserColor); + waddstr(statusWin, tmpStr); + + wattrset(statusWin, A_BOLD | COLOR_PAIR(13)); + waddstr(statusWin, " | WIN: "); + snprintf(tmpStr, sizeof(tmpStr), "%d: %s / %d", + currWin->num + 1, + currWin->id != NULL ? currWin->id : "MAIN", + currWin->pos); + waddstr(statusWin, tmpStr); + + wattrset(statusWin, A_BOLD | COLOR_PAIR(13)); + waddstr(statusWin, " | "); + wattrset(statusWin, A_BOLD | COLOR_PAIR(11)); + + for (i = 0; i < SET_MAX_WINDOWS; i++) + if (chatWindows[i] != NULL && chatWindows[i]->dirty) + { + snprintf(tmpStr, sizeof(tmpStr), "%d ", i + 1); + waddstr(statusWin, tmpStr); + } + + wrefresh(statusWin); +} + + +void printEditBuf(nn_editbuf_t *buf) +{ + char *tmp; + if (editWin == NULL || buf == NULL) return; + + buf->data[buf->len] = 0; + tmp = nn_username_decode(th_strdup(buf->data)); + + werase(editWin); + + wattrset(editWin, A_NORMAL); + + if (buf->pos < buf->len) + { + waddnstr(editWin, tmp, buf->pos); + wattrset(editWin, A_REVERSE); + waddch(editWin, tmp[buf->pos]); + wattrset(editWin, A_NORMAL); + waddnstr(editWin, tmp + buf->pos + 1, buf->len - buf->pos - 1); + } + else + { + waddnstr(editWin, tmp, buf->len); + wattrset(editWin, A_REVERSE); + waddch(editWin, ' '); + wattrset(editWin, A_NORMAL); + } + wrefresh(editWin); + th_free(tmp); +} + + +int printWin(WINDOW *win, const char *fmt) +{ + const char *s = fmt; + int col = 0; + + while (*s) + { + if (*s == '½') + { + s++; + if (*s == '½') + { + waddch(win, ((unsigned char) *s) | col); + s++; + } + else + { + memcpy(&col, s, sizeof(int)); + s += sizeof(int); + } + } + else + { + waddch(win, ((unsigned char) *s) | col); + s++; + } + } + return 0; +} + + +#define QPUTCH(ch) th_vputch(&(win->buf), &(win->bufsize), &(win->len), ch) + +int nn_window_print(nn_window_t *win, const char *fmt) +{ + const char *s = fmt; + int col = 0; + while (*s) + { + if (*s == '½') + { + s++; + if (*s == '½') + { + QPUTCH(*s); + QPUTCH(*s); + win->chlen++; + } + else + { + int val = 0; + while (*s >= '0' && *s <= '9') + { + val *= 10; + val += (*s - '0'); + s++; + } + if (*s != '½') return -1; + + if (val < 9) + col = A_DIM | COLOR_PAIR(val); + else if (val < 30) + col = A_BOLD | COLOR_PAIR(val - 9); + + QPUTCH('½'); + + if (!th_growbuf(&(win->buf), &(win->bufsize), &(win->len), sizeof(int))) + return -2; + + memcpy(win->buf + win->len, &col, sizeof(int)); + win->len += sizeof(int); + } + } + else if (*s == '\n') + { + QPUTCH('\n'); + QPUTCH(0); + th_ringbuf_add(win->data, win->buf); + win->buf = NULL; + win->chlen = 0; + win->dirty = TRUE; + } + else if (*s != '\r') + { + QPUTCH((unsigned char) *s == 255 ? ' ' : *s); + win->chlen++; + } + + s++; + } + + return 0; +} + + +BOOL updateMainWin(BOOL force) +{ + int h, offs; + qringbuf_t *buf; + + /* Check pointers */ + if (mainWin == NULL || currWin == NULL) + return FALSE; + + /* Check if update is forced or if the window is dirty */ + if (!force && !currWin->dirty) + return FALSE; + + /* Compute how many lines from backbuffer fit on the screen */ + buf = currWin->data; + h = getmaxy(mainWin); + + /* Clear and redraw window */ + werase(mainWin); + scrollok(mainWin, 1); + for (offs = buf->size - h - currWin->pos; offs >= 0 && offs < buf->size - currWin->pos && offs < buf->size; offs++) + { + if (buf->data[offs] != NULL) + printWin(mainWin, buf->data[offs]); + } + + currWin->dirty = FALSE; + wrefresh(mainWin); + return TRUE; +} + + +int printFile(FILE *outFile, const char *fmt) +{ + const char *s = fmt; + + while (*s) + { + if (*s == '½') + { + s++; + if (*s == '½') + { + fputc((unsigned char) *s, outFile); + s++; + } + else + { + while (*s && isdigit((int) *s)) s++; + if (*s != '½') return -1; + s++; + } + } + else + { + if ((unsigned char) *s == 255) + fputc(' ', outFile); + else + fputc((unsigned char) *s, outFile); + s++; + } + } + + return 0; +} + +void printMsgV(nn_window_t *win, int flags, const char *fmt, va_list ap) +{ + char tmpStr[128], *buf; + + str_get_timestamp(tmpStr, sizeof(tmpStr), "½17½[½11½%H:%M:%S½17½]½0½ "); + + buf = th_strdup_vprintf(fmt, ap); + + if (optLogFile && (flags & LOG_FILE)) + { + if (flags & LOG_STAMP) printFile(optLogFile, tmpStr); + printFile(optLogFile, buf); + fflush(optLogFile); + } + + if (!optDaemon && (flags & LOG_WINDOW)) + { + nn_window_t *tmp = win != NULL ? win : chatWindows[0]; + if (flags & LOG_STAMP) nn_window_print(tmp, tmpStr); + nn_window_print(tmp, buf); + } + + th_free(buf); +} + +void printMsg(nn_window_t *win, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + printMsgV(win, LOG_STAMP | LOG_WINDOW | LOG_FILE, fmt, ap); + va_end(ap); +} + +void printMsgF(nn_window_t *win, int flags, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + printMsgV(win, flags | LOG_STAMP, fmt, ap); + va_end(ap); +} + +void printMsgQ(nn_window_t *win, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + printMsgV(win, LOG_STAMP | LOG_WINDOW, fmt, ap); + va_end(ap); +} + + +char *errorMessages = NULL; + +void errorMsgV(const char *fmt, va_list ap) +{ + char *tmp = th_strdup_vprintf(fmt, ap); + + printMsg(NULL, "%s", tmp); + + if (errorMessages != NULL) + { + char *tmp2 = th_strdup_printf("%s%s", errorMessages, tmp); + th_free(errorMessages); + th_free(tmp); + errorMessages = tmp2; + } + else + errorMessages = tmp; +} + +void errorMsg(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + errorMsgV(fmt, ap); + va_end(ap); +} + +void errorFunc(struct _nn_conn_t *conn, const char *fmt, va_list ap) +{ + (void) conn; + errorMsgV(fmt, ap); +} + +void messageFunc(struct _nn_conn_t *conn, const char *fmt, va_list ap) +{ + (void) conn; + printMsgV(NULL, LOG_STAMP | LOG_WINDOW | LOG_FILE, fmt, ap); +} + + +BOOL checkIgnoreList(const char *name) +{ + qlist_t *node = setIgnoreList; + while (node != NULL) + { + if (th_strcasecmp(name, (char *) node->data) == 0) + return TRUE; + node = node->next; + } + return FALSE; +} + + +int nnproto_handle_user(nn_conn_t *conn) +{ + static const char *msg = ""; + char *p = conn->ptr; + BOOL isMine, isIgnored = FALSE; + char *s, *t, *userName; + + /* Find start of the message */ + s = strstr(p, msg); + if (!s) return 1; + *s = 0; + s += strlen(msg); + + /* Find end of the message */ + t = strstr(s, ""); + if (!t) return 3; + *t = 0; + + /* Decode message string */ + s = nn_decode_str1(s); + if (!s) return -1; + + /* Decode username */ + userName = nn_decode_str1(p); + if (!userName) + { + th_free(s); + return -2; + } + + /* Check if the username is on our ignore list and + * that it is not our OWN username! + */ + isMine = strcmp(userName, optUserName) == 0; + isIgnored = setIgnoreMode && !isMine && checkIgnoreList(userName); + + /* Is it a special control message? */ + if (*s == '/') + { + /* Ignore room join/leave messages */ + if (!optDebug && (strstr(s, "left the room") || strstr(s, "joined the room from"))) + goto done; + + t = nn_strip_tags(s + 1); + if (!strncmp(t, "BPRV ", 5)) + { + char *name, *tmp, *msg, *h; + nn_window_t *win; + h = nn_decode_str2(t + 1); + + if (!strncmp(t, "BPRV from ", 10)) + { + name = nn_decode_str2(t + 10); + isMine = FALSE; + } + else + { + name = nn_decode_str2(t + 8); + isMine = TRUE; + } + + for (tmp = name; *tmp && *tmp != ':'; tmp++); + if (tmp[0] != 0 && tmp[1] == ' ') + msg = tmp + 2; + else + msg = ""; + *tmp = 0; + + isIgnored = setIgnoreMode && checkIgnoreList(name); + win = findWindow(name); + + if (win != NULL) + { + printMsgF(win, isIgnored ? 0 : LOG_WINDOW, + "½5½<½%d½%s½5½>½0½ %s\n", + isMine ? 14 : 15, isMine ? optUserName : name, msg); + + printMsgF(NULL, LOG_FILE, "½11½%s½0½\n", h); + } + else + { + printMsgF(NULL, isIgnored ? LOG_FILE : (LOG_WINDOW | LOG_FILE), + "½11½%s½0½\n", h); + } + th_free(name); + th_free(h); + } + else + { + /* It's an action (/me) */ + char *h = nn_decode_str2(t); + printMsgF(NULL, isIgnored ? LOG_FILE : (LOG_WINDOW | LOG_FILE), + "½9½* %s½0½\n", h); + th_free(h); + } + th_free(t); + } + else + { + /* It's a normal message */ + char *h; + t = nn_strip_tags(s); + h = nn_decode_str2(t); + printMsgF(NULL, isIgnored ? LOG_FILE : (LOG_WINDOW | LOG_FILE), + "½5½<½%d½%s½5½>½0½ %s\n", isMine ? 14 : 15, userName, h); + th_free(h); + th_free(t); + } + +done: + th_free(s); + th_free(userName); + return 0; +} + + +int nnproto_handle_login(nn_conn_t *conn) +{ + char tmpStr[256]; + str_get_timestamp(tmpStr, sizeof(tmpStr), "%c"); + + if (!nn_conn_buf_strcmp(conn, "FAILURE>")) + { + printMsg(NULL, "½1½Login failure½0½ - ½3½%s½0½\n", tmpStr); + return -2; + } + else if (!nn_conn_buf_strcmp(conn, "SUCCESS>")) + { + printMsg(NULL, "½2½Login success½0½ - ½3½%s½0½\n", tmpStr); + nn_conn_send_msg(conn, optUserNameEnc, "%%2FRequestUserList"); + return 0; + } + else + return 1; +} + + +int nnproto_handle_add_user(nn_conn_t *conn) +{ + char *p, *s, *str = conn->ptr; + nn_window_t *win; + + s = nn_conn_buf_strstr(conn, ""); + if (!s) return 1; + *s = 0; + + p = nn_dbldecode_str(str); + if (!p) return -1; + + win = findWindow(p); + nn_userhash_insert(nnUsers, nn_username_encode(p)); + + printMsg(NULL, "! ½3½%s½0½ ½2½ADDED.½0½\n", p); + if (win != NULL) + printMsg(win, "! ½3½%s½0½ ½2½joined the chat.½0½\n", p); + + th_free(p); + return 0; +} + + +int nnproto_handle_delete_user(nn_conn_t *conn) +{ + char *p, *s, *str = conn->ptr; + nn_window_t *win; + + s = nn_conn_buf_strstr(conn, ""); + if (!s) return 1; + *s = 0; + + p = nn_dbldecode_str(str); + if (!p) return -1; + + win = findWindow(p); + nn_userhash_delete(nnUsers, nn_username_encode(p)); + + printMsg(NULL, "! ½3½%s½0½ ½1½DELETED.½0½\n", p); + if (win != NULL) + printMsg(win, "! ½3½%s½0½ ½1½left the chat.½0½\n", p); + + th_free(p); + return 0; +} + + +int nnproto_handle_num_clients(nn_conn_t *conn) +{ + nn_conn_buf_strstr(conn, ""); + return 0; +} + + +int nnproto_handle_boot(nn_conn_t *conn) +{ + (void) conn; + errorMsg("Booted by server.\n"); + return -1; +} + + +typedef struct +{ + char *cmd; + ssize_t len; + int (*handler)(nn_conn_t *); +} nn_protocolcmd_t; + + +static nn_protocolcmd_t protoCmds[] = +{ + { "", -1, nnproto_handle_user }, + { "", -1, nnproto_handle_delete_user }, + { "", -1, nnproto_handle_add_user }, + { "", -1, nnproto_handle_num_clients }, + { "", -1, nnproto_handle_boot }, +}; + +static const int nprotoCmds = sizeof(protoCmds) / sizeof(protoCmds[0]); + + +int nn_parse_protocol(nn_conn_t *conn) +{ + static BOOL protoCmdsInit = FALSE; + int i; + + if (!protoCmdsInit) + { + for (i = 0; i < nprotoCmds; i++) + protoCmds[i].len = strlen(protoCmds[i].cmd); + + protoCmdsInit = TRUE; + } + + for (i = 0; i < nprotoCmds; i++) + { + if (!nn_conn_buf_strncmp(conn, protoCmds[i].cmd, protoCmds[i].len)) + return protoCmds[i].handler(conn); + } + + if (optDebug) + { + printMsg(NULL, "Unknown protocmd: \"%s\"\n", conn->ptr); + return 0; + } + else + return 1; +} + + +int nn_handle_input(nn_conn_t *conn, char *buf, size_t bufLen) +{ + char *tmpStr, tmpBuf[4096]; + BOOL result; + + /* Trim right */ + bufLen--; + buf[bufLen--] = 0; + while (bufLen > 0 && th_isspace(buf[bufLen])) + buf[bufLen--] = 0; + + /* Decode completed usernames */ + nn_username_decode(buf); + + /* Check for special user commands */ + if (*buf == 0) + { + return 1; + } + else if (!th_strncasecmp(buf, "/color ", 7)) + { + /* Change color */ + int tmpInt; + if ((tmpInt = th_get_hex_triplet(str_trim_left(buf + 7))) < 0) + { + printMsgQ(currWin, "Invalid color value '%s'\n", buf+7); + return 1; + } + optUserColor = tmpInt; + printMsgQ(currWin, "Setting color to #%06x\n", optUserColor); + nn_conn_send_msg(conn, optUserNameEnc, "%%2FSetFontColor%%20%%2Dcolor%%20%06X", optUserColor); + return 0; + } + else if (!th_strncasecmp(buf, "/ignore", 7)) + { + char *name = str_trim_left(buf + 7); + if (strlen(name) > 0) + { + /* Add or remove someone to/from ignore */ + qlist_t *user = th_llist_find_func(setIgnoreList, name, compareUsername); + if (user != NULL) + { + printMsgQ(currWin, "Removed user '%s' from ignore.\n", name); + th_llist_delete_node(&setIgnoreList, user); + } + else + { + printMsgQ(currWin, "Now ignoring '%s'.\n", name); + th_llist_append(&setIgnoreList, th_strdup(name)); + } + } + else + { + /* Just list whomever is in ignore now */ + qlist_t *user = setIgnoreList; + ssize_t nuser = th_llist_length(setIgnoreList); + char *result = th_strdup_printf("Users ignored (%d): ", nuser); + while (user != NULL) + { + if (user->data != NULL) + { + th_pstr_printf(&result, "%s'%s'", result, (char *) user->data); + if (--nuser > 0) + th_pstr_printf(&result, "%s, ", result); + } + user = user->next; + } + printMsgQ(currWin, "%s\n", result); + th_free(result); + } + return 0; + } + else if (!th_strncasecmp(buf, "/query", 6)) + { + char *name = str_trim_left(buf + 6); + if (strlen(name) > 0) + { + nn_user_t *user = nn_user_find(nnUsers, nn_username_encode(name)); + if (user != NULL) + { + name = nn_username_decode(th_strdup(user->name)); + printMsgQ(currWin, "Opening PRV query for '%s'.\n", name); + if (openWindow(name, TRUE)) + printMsgQ(currWin, "In PRV query with '%s'.\n", name); + th_free(name); + } + } + else + { + printMsgQ(currWin, "Usage: /query username\n"); + printMsgQ(currWin, "To close a PRV query, use /close [username]\n"); + printMsgQ(currWin, "/close without username will close the current PRV window.\n"); + } + return 0; + } + else if (!th_strncasecmp(buf, "/win", 4)) + { + /* Change color */ + char *tmp = str_trim_left(buf + 4); + if (strlen(tmp) > 0) + { + int val = atoi(tmp); + if (val >= 1 && val < SET_MAX_WINDOWS) + { + if (chatWindows[val - 1] != NULL) + currWin = chatWindows[val - 1]; + } + else + { + printMsgQ(currWin, "Invalid window number '%s'\n", tmp); + return 1; + } + } + else + { + printMsgQ(currWin, "Window : #%d\n", currWin->num); + printMsgQ(currWin, "ID : %s\n", currWin->id); + } + return 0; + } + else if (!th_strncasecmp(buf, "/close", 6)) + { + char *name = str_trim_left(buf + 6); + if (strlen(name) > 0) + { + nn_window_t *win = findWindow(name); + if (win != NULL) + { + closeWindow(win); + printMsgQ(currWin, "Closed PRV query to '%s'.\n", name); + } + else + { + printMsgQ(currWin, "No PRV query by name '%s'.\n", name); + } + } + else + { + if (currWin != chatWindows[0]) + { + closeWindow(currWin); + currWin = chatWindows[0]; + } + } + return 0; + } + else if (!th_strncasecmp(buf, "/save", 5)) + { + /* Save configuration */ + FILE *cfgfile = fopen(setConfigFile, "w"); + if (cfgfile == NULL) + { + printMsgQ(currWin, "Could not create configuration to file '%s': %s\n", + setConfigFile, strerror(errno)); + return 0; + } + printMsgQ(currWin, "Configuration saved in file '%s', res=%d\n", + setConfigFile, + th_cfg_write(cfgfile, setConfigFile, cfg)); + + fclose(cfgfile); + return 0; + } + else if (!th_strncasecmp(buf, "/w ", 3)) + { + /* Open given username's profile via firefox in a new tab */ + char *name = str_trim_left(buf + 3); + + printMsg(currWin, "Opening profile for: '%s'\n", name); + + tmpStr = nn_encode_str1(name); +#ifdef __WIN32 + { + HINSTANCE status; + snprintf(tmpBuf, sizeof(tmpBuf), "http://www.newbienudes.com/profile/%s/", tmpStr); + th_free(tmpStr); + status = ShellExecute(NULL, "open", tmpBuf, NULL, NULL, SW_SHOWNA); + if (status <= (HINSTANCE) 32) + printMsgQ(currWin, "Could not launch default web browser: %d\n", status); + } +#else + { + int status; + int fds[2]; + pid_t pid; + snprintf(tmpBuf, sizeof(tmpBuf), "openurl(http://www.newbienudes.com/profile/%s/,new-tab)", tmpStr); + th_free(tmpStr); + + if (pipe(fds) == -1) + { + int ret = errno; + printMsgQ(currWin, "Could not open process communication pipe! (%d, %s)\n", ret, strerror(ret)); + return 0; + } + + if ((pid = fork()) < 0) + { + printMsgQ(currWin, "Could not create sub-process!\n"); + } + else if (pid == 0) + { + dup2(fds[1], STDOUT_FILENO); + dup2(fds[0], STDERR_FILENO); + execlp(setBrowser, setBrowser, "-remote", tmpBuf, (void *)NULL); + _exit(errno); + } + + wait(&status); + } +#endif + return 0; + } + else if (!th_strncasecmp(buf, "/who", 4)) + { + /* Alias /who to /listallusers */ + snprintf(tmpBuf, sizeof(tmpBuf), "/listallusers"); + buf = tmpBuf; + } + + if (currWin != chatWindows[0]) + { + if (currWin->id != NULL) + { + snprintf(tmpBuf, sizeof(tmpBuf), "/prv -to %s -msg %s", currWin->id, buf); + buf = tmpBuf; + } + else + { + printMsgQ(NULL, "No target set, exiting prv mode.\n"); + return 1; + } + } + + /* Send double-encoded */ + tmpStr = nn_dblencode_str(nn_username_decode(buf)); + if (tmpStr == 0) return -2; + result = nn_conn_send_msg(conn, optUserNameEnc, "%s", tmpStr); + th_free(tmpStr); + + return result ? 0 : -1; +} + + +void closeWindows(void) +{ + if (mainWin) delwin(mainWin); + if (statusWin) delwin(statusWin); + if (editWin) delwin(editWin); +} + + +BOOL initializeWindows(void) +{ + int w, h; + + getmaxyx(stdscr, h, w); + + closeWindows(); + + mainWin = subwin(stdscr, h - 4, w, 0, 0); + statusWin = subwin(stdscr, 1, w, h - 4, 0); + editWin = subwin(stdscr, 3, w, h - 3, 0); + + if (mainWin == NULL || statusWin == NULL || editWin == NULL) + return FALSE; + + return TRUE; +} + + +void updateWindows(void) +{ + if (mainWin) redrawwin(mainWin); + if (statusWin) redrawwin(statusWin); + if (editWin) redrawwin(editWin); +} + + +BOOL performTabCompletion(nn_editbuf_t *buf) +{ + static char *previous = NULL, *pattern = NULL; + BOOL again = FALSE, hasSeparator = FALSE, newPattern = FALSE, hasSpace = FALSE; + char *str = buf->data; + int mode = 0; + ssize_t endPos, startPos = buf->pos; + + /* previous word */ + if (startPos >= 2 && str[startPos - 1] == ' ' && str[startPos - 2] != ' ') + { + startPos -= 2; + endPos = startPos; + while (startPos > 0 && str[startPos - 1] != ' ') startPos--; + mode = 1; + } + else + /* middle of a word, new pattern */ + if (startPos < buf->len && str[startPos] != ' ') + { + endPos = startPos; + while (startPos > 0 && str[startPos - 1] != ' ') startPos--; + while (endPos < buf->len - 1 && str[endPos + 1] != ' ') endPos++; + newPattern = TRUE; + mode = 2; + } + else + /* previous word, new pattern */ + if (startPos >= 1 && str[startPos - 1] != ' ') + { + startPos -= 1; + endPos = startPos; + while (startPos > 0 && str[startPos - 1] != ' ') startPos--; + newPattern = TRUE; + mode = 3; + } + else + { + if (optDebug) + printMsg(currWin, "no mode\n"); + return FALSE; + } + + if (str[endPos] == optNickSep) + { + endPos--; + if (startPos > 0) + { + if (optDebug) + printMsg(currWin, "str[endPos] == optNickSep && startPos > 0 (%d)\n", startPos); + return FALSE; + } + hasSeparator = TRUE; + } + + if (buf->pos > 0 && str[buf->pos - 1] == ' ') + hasSpace = TRUE; + if (buf->pos <= buf->len && str[buf->pos] == ' ') + hasSpace = TRUE; + + if (newPattern) + { + /* Get pattern, check if it matches previous pattern and set 'again' flag */ + char *npattern = nn_editbuf_get_string(buf, startPos, endPos); + if (pattern && npattern && th_strcasecmp(npattern, pattern) == 0) + again = TRUE; + + th_free(pattern); + pattern = npattern; + + if (!again) + { + th_free(previous); + previous = NULL; + } + } + + if (optDebug) + { + printMsg(currWin, "sPos=%d, ePos=%d <-> bPos=%d, bufLen=%d : pat='%s' (again=%s, hassep=%s, hasspc=%s, newpat=%s, mode=%d)\n", + startPos, endPos, buf->pos, buf->len, pattern, + again ? "yes" : "no", + hasSeparator ? "yes" : "no", + hasSpace ? "yes" : "no", + newPattern ? "yes" : "no", mode); + } + + if (pattern) + { + nn_user_t *user = nn_user_match(nnUsers, pattern, previous, again); + + if (user) + { + int i; + char *c = user->name; + if (optDebug) + printMsg(currWin, "match='%s' / prev='%s'\n", user->name, previous); + + for (i = startPos; i <= endPos; i++) + nn_editbuf_delete(buf, startPos); + + for (i = startPos; *c; i++, c++) + nn_editbuf_insert(buf, i, *c); + + if (!hasSeparator && startPos == 0) + { + nn_editbuf_insert(buf, i++, optNickSep); + startPos++; + } + if (hasSeparator) + startPos++; + if (!hasSpace) + nn_editbuf_insert(buf, i++, ' '); + + nn_editbuf_setpos(buf, startPos + 1 + strlen(user->name)); + + th_free(previous); + previous = th_strdup(user->name); + + return TRUE; + } + } + + return FALSE; +} + + +#define VPUTCH(CH) th_vputch(&bufData, &bufSize, &bufLen, CH) +#define VPUTS(STR) th_vputs(&bufData, &bufSize, &bufLen, STR) + +char *logParseFilename(const char *fmt, int id) +{ + size_t bufSize = strlen(fmt) + TH_BUFGROW, bufLen = 0; + char *bufData = th_malloc(bufSize); + char tmpBuf[32]; + const char *s = fmt; + + while (*s) + { + if (*s == '%') + { + s++; + switch (*s) + { + case 'i': + snprintf(tmpBuf, sizeof(tmpBuf), "%05d", id); + VPUTS(tmpBuf); + break; + + case 'd': + snprintf(tmpBuf, sizeof(tmpBuf), "%d", id); + VPUTS(tmpBuf); + break; + + case '%': + VPUTCH('%'); + break; + } + s++; + } + else + { + VPUTCH(*s); + s++; + } + } + + VPUTCH(0); + return bufData; +} + + +BOOL logFileOpen(void) +{ + char *filename; + + if (optLogFilename == NULL || !optLogEnable) + return FALSE; + + filename = logParseFilename(optLogFilename, optPort); + + if ((optLogFile = fopen(filename, "a")) == NULL) + { + errorMsg("Could not open logfile '%s' for appending!\n", filename); + th_free(filename); + return FALSE; + } + + th_free(filename); + + return TRUE; +} + + +void logFileClose(void) +{ + if (optLogFile) + { + fclose(optLogFile); + optLogFile = NULL; + } +} + + +char *promptRequester(WINDOW *win, const char *info, BOOL allowEmpty) +{ + char tmpBuf[512], *ptr; + ssize_t pos; + int curVis = curs_set(1); + + echo(); + waddstr(win, info); + wgetnstr(win, tmpBuf, sizeof(tmpBuf) - 1); + noecho(); + if (curVis != ERR) + curs_set(curVis); + + for (pos = strlen(tmpBuf) - 1; pos > 0 && th_isspace(tmpBuf[pos]); pos--) + tmpBuf[pos] = 0; + + ptr = str_trim_left(tmpBuf); + + if (allowEmpty || strlen(ptr) > 0) + return th_strdup(ptr); + else + return NULL; +} + + +void printHelp(void) +{ + printMsgQ(currWin, "\n" + "NNChat Help\n" + "===========\n" + "\n" + "F1 This help.\n" + "F2 \n" + ); +} + + +int main(int argc, char *argv[]) +{ + nn_conn_t *conn = NULL; + int curVis = ERR, updateCount = 0; + BOOL argsOK, isError = FALSE, + exitProg = FALSE, + colorSet = FALSE, + cursesInit = FALSE, + networkInit = FALSE, + insertMode = TRUE, + firstUpdate = TRUE; + time_t prevTime; + char *tmpStr; + nn_editbuf_t *editBuf = nn_editbuf_new(NN_TMPBUF_SIZE); + nn_editbuf_t *histBuf[SET_MAX_HISTORY+2]; + int histPos = 0, histMax = 0; + + cfgitem_t *tmpcfg; + char *homeDir = NULL; + + memset(histBuf, 0, sizeof(histBuf)); + + /* Initialize */ + th_init("NNChat", "Newbie Nudes chat client", NN_VERSION, + "Written and designed by Anonymous Finnish Guy (C) 2008-2012", + "This software is freeware, use and distribute as you wish."); + th_verbosityLevel = 0; + + /* Read configuration file */ + tmpcfg = NULL; + th_cfg_add_comment(&tmpcfg, "General settings"); + th_cfg_add_string(&tmpcfg, "username", &optUserName, NULL); + th_cfg_add_string(&tmpcfg, "password", &optPassword, NULL); + + th_cfg_add_comment(&tmpcfg, "Default color as a hex-triplet"); + th_cfg_add_hexvalue(&tmpcfg, "color", &optUserColor, optUserColor); + + th_cfg_add_comment(&tmpcfg, "Default setting of ignore mode"); + th_cfg_add_bool(&tmpcfg, "ignore", &setIgnoreMode, setIgnoreMode); + th_cfg_add_comment(&tmpcfg, "People to be ignored when ignore mode is enabled"); + th_cfg_add_string_list(&tmpcfg, "ignore_list", &setIgnoreList); + + th_cfg_add_comment(&tmpcfg, "Random messages for idle timeout protection. If none are set, plain '.' is used."); + th_cfg_add_string_list(&tmpcfg, "idle_messages", &setIdleMessages); + + th_cfg_add_comment(&tmpcfg, "Character used as nickname auto-completion separator (default is ':')"); + th_cfg_add_string(&tmpcfg, "nick_separator", &optNickSepStr, NULL); + + th_cfg_add_section(&cfg, "general", tmpcfg); + + + tmpcfg = NULL; + th_cfg_add_comment(&tmpcfg, "Chat server hostname or IP address"); + th_cfg_add_string(&tmpcfg, "host", &optServer, optServer); + th_cfg_add_comment(&tmpcfg, "Default port to connect to (8005 = main room, 8003 = passion pit)"); + th_cfg_add_int(&tmpcfg, "port", &optPort, optPort); + th_cfg_add_section(&cfg, "server", tmpcfg); + + tmpcfg = NULL; + th_cfg_add_comment(&tmpcfg, "Proxy server type (0 = none, 1 = SOCKS 4, 2 = SOCKS 4a)"); + th_cfg_add_int(&tmpcfg, "type", &optProxyType, optProxyType); + th_cfg_add_comment(&tmpcfg, "Proxy server host name"); + th_cfg_add_string(&tmpcfg, "host", &optProxyServer, optProxyServer); + th_cfg_add_comment(&tmpcfg, "Proxy port, 1080 is the standard SOCKS port"); + th_cfg_add_int(&tmpcfg, "port", &optProxyPort, optProxyPort); + th_cfg_add_section(&cfg, "proxy", tmpcfg); + + tmpcfg = NULL; + th_cfg_add_comment(&tmpcfg, "Enable logging"); + th_cfg_add_bool(&tmpcfg, "enable", &optLogEnable, optLogEnable); + th_cfg_add_comment(&tmpcfg, "Log filename format"); + th_cfg_add_string(&tmpcfg, "filename", &optLogFilename, optLogFilename); + th_cfg_add_section(&cfg, "logging", tmpcfg); + +#ifdef __WIN32 + { + char tmpPath[MAX_PATH]; + if (SHGetFolderPath(NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL, 0, tmpPath) == S_OK) + homeDir = th_strdup(tmpPath); + + CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); + } +#else + homeDir = th_strdup(getenv("HOME")); +#endif + + if (homeDir != NULL) + { + FILE *cfgfile; + setConfigFile = th_strdup_printf("%s" SET_DIR_SEPARATOR "%s", homeDir, SET_CONFIG_FILE); + + THMSG(0, "Reading configuration from '%s'.\n", setConfigFile); + + if ((cfgfile = fopen(setConfigFile, "r")) != NULL) + { + th_cfg_read(cfgfile, setConfigFile, cfg); + fclose(cfgfile); + } + } + + if (optNickSepStr) + optNickSep = optNickSepStr[0]; + else + optNickSep = SET_NICK_SEPARATOR; + + + setBrowser = getenv("BROWSER"); + if (setBrowser == NULL) + setBrowser = "firefox"; + + /* Parse command line arguments */ + argsOK = th_args_process(argc, argv, optList, optListN, + argHandleOpt, argHandleFile, FALSE); + + if (optUserNameCmd != NULL) + { + optUserName = optUserNameCmd; + optPassword = optPasswordCmd; + } + + if (!argsOK) + return -2; + + /* Allocate userhash */ + if ((nnUsers = nn_userhash_new()) == NULL) + { + THERR("Could not allocate userhash. Fatal error.\n"); + return -105; + } + + /* If no idle messages are set, add default */ + if (setIdleMessages == NULL) + { + th_llist_append(&setIdleMessages, th_strdup(".")); + } + + /* Open logfile */ + logFileOpen(); + + /* Initialize network */ + if (!nn_network_init()) + { + THERR("Could not initialize network subsystem.\n"); + goto err_exit; + } + else + networkInit = TRUE; + + /* Initialize NCurses */ + if (!optDaemon) + { + if (LINES < 0 || LINES > 1000) LINES = 24; + if (COLS < 0 || COLS > 1000) COLS = 80; + initscr(); + raw(); + keypad(stdscr, TRUE); + noecho(); + meta(stdscr, TRUE); + timeout(SET_DELAY); + curVis = curs_set(0); + + if (has_colors()) + { + start_color(); + + init_pair( 1, COLOR_RED, COLOR_BLACK); + init_pair( 2, COLOR_GREEN, COLOR_BLACK); + init_pair( 3, COLOR_YELLOW, COLOR_BLACK); + init_pair( 4, COLOR_BLUE, COLOR_BLACK); + init_pair( 5, COLOR_MAGENTA, COLOR_BLACK); + init_pair( 6, COLOR_CYAN, COLOR_BLACK); + init_pair( 7, COLOR_WHITE, COLOR_BLACK); + init_pair( 8, COLOR_BLACK, COLOR_BLACK); + + init_pair(10, COLOR_BLACK, COLOR_RED); + init_pair(11, COLOR_WHITE, COLOR_RED); + init_pair(12, COLOR_GREEN, COLOR_RED); + init_pair(13, COLOR_YELLOW, COLOR_RED); + init_pair(14, COLOR_BLUE, COLOR_RED); + init_pair(15, COLOR_MAGENTA, COLOR_RED); + init_pair(16, COLOR_CYAN, COLOR_RED); + } + + cursesInit = TRUE; + + if (!initializeWindows()) + goto err_exit; + +#ifdef PDCURSES + PDC_set_title("NNChat v" NN_VERSION); +#endif + + memset(chatWindows, 0, sizeof(chatWindows)); + chatWindows[0] = nn_window_new(NULL); + currWin = chatWindows[0]; + updateStatus(); + } + + /* Check if we have username and password */ + if (cursesInit && (optUserName == NULL || optPassword == NULL)) + { + printWin(editWin, "You can avoid this prompt by issuing '/save' after logging in.\n"); + optUserName = promptRequester(editWin, "NN username: ", FALSE); + optPassword = promptRequester(editWin, "NN password: ", TRUE); + } + + if (optUserName == NULL || optPassword == NULL) + { + errorMsg("Username and/or password not specified.\n"); + goto err_exit; + } + + /* Create a connection */ + conn = nn_conn_new(errorFunc, messageFunc); + if (conn == NULL) + { + errorMsg("Could not create connection structure.\n"); + goto err_exit; + } + + /* Are we using a proxy? */ + if (optProxyType != NN_PROXY_NONE && optProxyServer != NULL) + { + if (nn_conn_set_proxy(conn, optProxyType, optProxyPort, optProxyServer) != 0) + { + errorMsg("Error setting proxy information.\n"); + goto err_exit; + } + } + + /* Okay ... */ + printMsg(currWin, "Trying to resolve host '%s' ...\n", optServer); + conn->host = th_strdup(optServer); + conn->hst = nn_resolve_host(conn, optServer); + if (conn->hst == NULL) + { + errorMsg("Could not resolve hostname: %s.\n", strerror(h_errno)); + goto err_exit; + } + +#ifdef FINAL_BUILD + /* To emulate the official client, we first make a request for + * policy file, even though we don't use it for anything... + */ + if (nn_conn_open(conn, 843, NULL) != 0) + { + errorMsg("Policy file request connection setup failed!\n"); + goto err_exit; + } + + tmpStr = ""; + if (nn_conn_send_buf(conn, tmpStr, strlen(tmpStr) + 1) == FALSE) + { + errorMsg("Failed to send policy file request.\n"); + goto err_exit; + } + else + { + int cres = nn_conn_pull(conn); + if (cres == 0) + { + printMsg(currWin, "Probe got: %s\n", conn->buf); + } + else + { + printMsg(currWin, "Could not get policy probe.\n"); + } + } + nn_conn_close(conn); +#endif + + /* Okay, now do the proper connection ... */ + if (nn_conn_open(conn, optPort, NULL) != 0) + { + errorMsg("Main connection setup failed!\n"); + goto err_exit; + } + + /* Send login command */ + optUserNameEnc = nn_dblencode_str(optUserName); + tmpStr = nn_dblencode_str(optSite); + nn_conn_send_msg(conn, optUserNameEnc, "%%2Flogin%%20%%2Dsite%%20%s%%20%%2Dpassword%%20%s", tmpStr, optPassword); + th_free(tmpStr); + + /* Initialize random numbers */ + prevTime = time(NULL); + srandom((int) prevTime); + + if (cursesInit) + { + /* Initialize rest of interactive UI code */ + nn_editbuf_clear(editBuf); + + /* First update of screen */ + printEditBuf(editBuf); + updateStatus(); + + printMsg(NULL, "%s v%s - %s\n", th_prog_name, th_prog_version, th_prog_fullname); + printMsg(NULL, "%s\n", th_prog_author); + printMsg(NULL, "%s\n", th_prog_license); + } + + /* Enter mainloop */ + nn_conn_reset(conn); + while (!isError && !exitProg) + { + nn_conn_reset(conn); + do { + int cres = nn_conn_pull(conn); + if (cres == 0 && *(conn->in_ptr - 1) == 0) + { + int result = nn_parse_protocol(conn); + if (result > 0) + { + /* Couldn't handle the message for some reason */ + printMsg(currWin, "Could not handle: %s\n", conn->ptr); + } + else if (result < 0) + { + /* Fatal error, quit */ + errorMsg("Fatal error with message: %s\n", conn->ptr); + isError = TRUE; + } + } + else if (cres < 0) + isError = TRUE; + else + break; + } + while (conn->total_bytes > 0 && !isError); + + if (!nn_conn_check(conn)) + isError = TRUE; + + /* Handle user input */ + if (cursesInit) + { + int c, cnt = 0; + BOOL update = FALSE, updateMain = FALSE; + + /* Handle several buffered keypresses at once */ + do + { + c = wgetch(stdscr); + + /* Handle various problematic cases where terminal + * keycodes do not get properly translated by curses + */ + if (c == 0x1b) + { + /* ^[O */ + c = wgetch(stdscr); + if (c == 'O') + { + c = wgetch(stdscr); + switch (c) + { + case 'd': + c = 0x204; + break; + case 'c': + c = 0x206; + break; + default: + if (optDebug) + printMsg(currWin, "Unhandled ESC-O key sequence 0x%02x\n", c); + break; + } + } + /* ^[[ */ + else if (c == '[') + { + c = wgetch(stdscr); + switch (c) + { + case 0x31: + c = wgetch(stdscr); + if (c >= 0x31 && c <= 0x39) + c = KEY_F(c - 0x30); + else + c = ERR; + break; + + case 0x32: + c = KEY_IC; + break; + case 0x33: + c = KEY_DC; + break; + + case 0x35: + c = KEY_PPAGE; + break; + case 0x36: + c = KEY_NPAGE; + break; + + case 0x37: + c = KEY_HOME; + break; + case 0x38: + c = KEY_END; + break; + + default: + if (optDebug) + printMsg(currWin, "Unhandled ESC-[*~ key sequence 0x%02x\n", c); + c = ERR; + break; + } + /* Get the trailing ~ */ + if (c != ERR) + wgetch(stdscr); + } + if (c >= 0x31 && c <= 0x39) + { + /* Chat window switching via Meta/Esc-[1..9] */ + int win = c - 0x31; + if (win < SET_MAX_WINDOWS && chatWindows[win] != NULL) + { + currWin = chatWindows[win]; + update = updateMain = TRUE; + } + c = ERR; + } + else + { + if (optDebug) + printMsg(currWin, "Unhandled ESC key sequence 0x%02x\n", c); + } + } +#if defined(__WIN32) && defined(PDCURSES) + else if (c >= 0x198 && c <= 0x1a0) + { + /* Chat window switching via Meta/Esc-[1..9] */ + int win = c - 0x198; + if (win < SET_MAX_WINDOWS && chatWindows[win] != NULL) + { + currWin = chatWindows[win]; + update = updateMain = TRUE; + } + c = ERR; + } +#endif + + switch (c) + { +#ifdef KEY_RESIZE + case KEY_RESIZE: + resize_term(0, 0); + erase(); + timeout(SET_DELAY); + + if (!initializeWindows()) + { + errorMsg("Error resizing curses chatWindows\n"); + isError = TRUE; + } + update = updateMain = TRUE; + break; +#endif + + case KEY_ENTER: + case '\n': + case '\r': + /* Call the user input handler */ + if (editBuf->len > 0) + { + int result; + + if (histMax > 0) + { + nn_editbuf_free(histBuf[SET_MAX_HISTORY+1]); + histBuf[SET_MAX_HISTORY+1] = NULL; + memmove(&histBuf[2], &histBuf[1], histMax * sizeof(histBuf[0])); + } + + histPos = 0; + histBuf[1] = nn_editbuf_copy(editBuf); + if (histMax < SET_MAX_HISTORY) histMax++; + + nn_editbuf_insert(editBuf, editBuf->len, 0); + result = nn_handle_input(conn, editBuf->data, editBuf->len); + + nn_editbuf_clear(editBuf); + + if (result < 0) + { + errorMsg("Fatal error handling user input: %s\n", editBuf->data); + isError = TRUE; + } + else + { + /* Update time value of last sent message for unidle timeouts */ + prevTime = time(NULL); + } + + updateMain = update = TRUE; + } + break; + + case KEY_UP: /* Backwards in input history */ + if (histPos == 0) + { + nn_editbuf_free(histBuf[0]); + histBuf[0] = nn_editbuf_copy(editBuf); + } + if (histPos < histMax) + { + histPos++; + nn_editbuf_free(editBuf); + editBuf = nn_editbuf_copy(histBuf[histPos]); + update = TRUE; + } + break; + + case KEY_DOWN: /* Forwards in input history */ + if (histPos > 0) + { + histPos--; + nn_editbuf_free(editBuf); + editBuf = nn_editbuf_copy(histBuf[histPos]); + update = TRUE; + } + break; + + case 0x204: /* ctrl+left arrow = Skip words left */ + case 0x20b: + while (editBuf->pos > 0 && isspace((int) editBuf->data[editBuf->pos - 1])) + editBuf->pos--; + while (editBuf->pos > 0 && !isspace((int) editBuf->data[editBuf->pos - 1])) + editBuf->pos--; + update = TRUE; + break; + + case 0x206: /* ctrl+right arrow = Skip words right */ + case 0x210: + while (editBuf->pos < editBuf->len && isspace((int) editBuf->data[editBuf->pos])) + editBuf->pos++; + while (editBuf->pos < editBuf->len && !isspace((int) editBuf->data[editBuf->pos])) + editBuf->pos++; + update = TRUE; + break; + + case KEY_HOME: + nn_editbuf_setpos(editBuf, 0); + update = TRUE; + break; + case KEY_END: + nn_editbuf_setpos(editBuf, editBuf->len); + update = TRUE; + break; + case KEY_LEFT: + nn_editbuf_setpos(editBuf, editBuf->pos - 1); + update = TRUE; + break; + case KEY_RIGHT: + nn_editbuf_setpos(editBuf, editBuf->pos + 1); + update = TRUE; + break; + + case KEY_BACKSPACE: + case 0x08: + case 0x7f: + nn_editbuf_delete(editBuf, editBuf->pos - 1); + nn_editbuf_setpos(editBuf, editBuf->pos - 1); + update = TRUE; + break; + + case KEY_DC: /* Delete character */ + nn_editbuf_delete(editBuf, editBuf->pos); + update = TRUE; + break; + + + case KEY_IC: /* Ins = Toggle insert / overwrite mode */ + insertMode = !insertMode; + update = TRUE; + break; + + case KEY_F(1): /* F1 = Print help */ + printHelp(); + updateMain = TRUE; + break; + + case KEY_F(2): /* F2 = Clear editbuffer */ + nn_editbuf_clear(editBuf); + update = TRUE; + break; + + case KEY_F(5): /* F5 = Ignore mode */ + setIgnoreMode = !setIgnoreMode; + printMsgQ(currWin, "Ignore mode = %s\n", setIgnoreMode ? "ON" : "OFF"); + break; + +#if 0 + case KEY_F(8): /* F8 = Debug */ + optDebug = !optDebug; + update = TRUE; + break; +#endif + + case 0x03: /* ^C = quit */ + case KEY_F(9): /* F9 = Quit */ + printMsg(currWin, "Quitting per user request (%d/0x%x).\n", c, c); + exitProg = TRUE; + break; + + case 0x09: /* Tab = complete username */ + performTabCompletion(editBuf); + update = TRUE; + break; + + case 0x0c: /* Ctrl + L */ + updateWindows(); + update = updateMain = TRUE; + break; + + case KEY_NPAGE: + case KEY_PPAGE: + /* Page Up / Page Down */ + if (currWin != NULL) + { + int oldPos = currWin->pos; + + currWin->pos += (c == KEY_NPAGE) ? -10 : +10; + + if (currWin->pos < 0) + currWin->pos = 0; + else if (currWin->pos >= currWin->data->n - 10) + currWin->pos = currWin->data->n - 10; + + if (oldPos != currWin->pos) + updateMain = TRUE; + } + break; + + case ERR: + /* Ignore */ + break; + + default: + if (isprint(c) || c == 0xe4 || c == 0xf6 || c == 0xc4 || c == 0xd6) + { + if (insertMode) + nn_editbuf_insert(editBuf, editBuf->pos, c); + else + nn_editbuf_write(editBuf, editBuf->pos, c); + nn_editbuf_setpos(editBuf, editBuf->pos + 1); + update = TRUE; + } + else + { + if (optDebug) + printMsg(currWin, "Unhandled key: 0x%02x\n", c); + } + break; + } + } + while (c != ERR && !exitProg && ++cnt < 10); + + updateMainWin(updateMain); + + if (update || firstUpdate) + { + /* Update edit line */ + updateStatus(); + printEditBuf(editBuf); + firstUpdate = FALSE; /* a nasty hack ... */ + } + + } /* cursesInit */ + + if (++updateCount > 10) + { + time_t tmpTime = time(NULL); + if (tmpTime - prevTime > SET_KEEPALIVE) + { + int n = random() % th_llist_length(setIdleMessages); + qlist_t *node = th_llist_get_nth(setIdleMessages, n); + nn_conn_send_msg(conn, optUserNameEnc, node->data); + prevTime = tmpTime; + } + + if (!colorSet) + { + colorSet = TRUE; + nn_conn_send_msg(conn, optUserNameEnc, "%%2FSetFontColor%%20%%2Dcolor%%20%06X", optUserColor); + } + + updateStatus(); + printEditBuf(editBuf); + updateCount = 0; + } + + } + + /* Shutdown */ +err_exit: + th_cfg_free(cfg); + th_free(homeDir); + th_llist_free_func(setIdleMessages, th_free); + nn_userhash_free(nnUsers); + nn_editbuf_free(editBuf); + + { + int i; + for (i = 0; i <= SET_MAX_HISTORY; i++) + nn_editbuf_free(histBuf[i]); + + for (i = 0; i < SET_MAX_WINDOWS; i++) + nn_window_free(chatWindows[i]); + } + +#ifdef __WIN32 + if (errorMessages) + { + char *tmp; + wclear(editWin); + tmp = promptRequester(editWin, "Press enter to quit.\n", FALSE); + th_free(tmp); + } +#endif + + if (cursesInit) + { + if (curVis != ERR) + curs_set(curVis); + closeWindows(); + endwin(); + THMSG(1, "NCurses deinitialized.\n"); + } + +#ifndef __WIN32 + if (errorMessages) + THERR("%s", errorMessages); +#endif + + th_free(optUserNameEnc); + + nn_conn_close(conn); + + if (networkInit) + nn_network_close(); + + THMSG(1, "Connection terminated.\n"); + + logFileClose(); + + return 0; +} diff -r 3e64acb433e8 -r 14b685cdbd2c network.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/network.c Thu May 24 06:41:07 2012 +0300 @@ -0,0 +1,543 @@ +/* + * 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 "network.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 3e64acb433e8 -r 14b685cdbd2c network.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/network.h Thu May 24 06:41:07 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 3e64acb433e8 -r 14b685cdbd2c nnchat.c --- a/nnchat.c Thu May 24 06:38:26 2012 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2158 +0,0 @@ -/* - * NNChat - Custom chat client for NewbieNudes.com chatrooms - * Written by Matti 'ccr' Hämäläinen - * (C) Copyright 2008-2011 Tecnic Software productions (TNSP) - */ -#include "libnnutil.h" -#include "libnnnet.h" -#include "th_args.h" -#include "th_config.h" -#ifdef __WIN32 -/* Undefine because both windows.h and curses.h #define it */ -#undef MOUSE_MOVED -#include -#else -#include -#endif -#ifdef HAVE_NCURSES_H -#include -#else -#include -#endif - -#ifdef __WIN32 -#define SET_CONFIG_FILE "nnchat.txt" -#define SET_DIR_SEPARATOR "\\" -#define SET_DELAY (0) -#else -#define SET_CONFIG_FILE ".nnchat" -#define SET_DIR_SEPARATOR "/" -#define SET_DELAY (5) -#endif - -#define SET_NICK_SEPARATOR ':' - -#define SET_MAX_HISTORY (16) /* Command history length */ -#define SET_KEEPALIVE (15*60) /* Ping/keepalive period in seconds */ -#define SET_MAX_WINDOWS (32) - - -/* Options - */ -int optPort = 8005, - optProxyPort = 1080, - optProxyType = NN_PROXY_NONE; -int optUserColor = 0x000000; -char *optServer = "chat.newbienudes.com", - *optProxyServer = NULL, - *optUserName = NULL, - *optUserNameCmd = NULL, - *optUserNameEnc = NULL, - *optPassword = NULL, - *optPasswordCmd = NULL, - *optLogFilename = NULL, - *optSite = "NN", - *optNickSepStr = NULL; -char optNickSep; -BOOL optDaemon = FALSE; -FILE *optLogFile = NULL; -BOOL setIgnoreMode = FALSE; -BOOL optDebug = FALSE; -BOOL optLogEnable = FALSE; - -nn_window_t *chatWindows[SET_MAX_WINDOWS], - *currWin = NULL; -WINDOW *mainWin = NULL, - *statusWin = NULL, - *editWin = NULL; - -qlist_t *setIgnoreList = NULL, - *setIdleMessages = NULL; -nn_userhash_t *nnUsers = NULL; -char *setConfigFile = NULL, - *setBrowser = NULL; -cfgitem_t *cfg = NULL; - - -/* Logging mode flags - */ -enum -{ - LOG_FILE = 1, - LOG_WINDOW = 2, - LOG_STAMP = 4 -}; - - -/* Arguments - */ -optarg_t optList[] = -{ - { 0, '?', "help", "Show this help", OPT_NONE }, - { 1, 'v', "verbose", "Be more verbose", OPT_NONE }, - { 2, 'p', "port", "Connect to port", OPT_ARGREQ }, - { 3, 's', "server", "Server to connect to", OPT_ARGREQ }, - { 4, 'C', "color", "Initial color in RGB hex 000000", OPT_ARGREQ }, - { 5, 'l', "logfile", "Log filename", OPT_ARGREQ }, - { 6, 'D', "daemon", "A pseudo-daemon mode for logging", OPT_NONE }, - { 7, 'f', "force-site", "Force site (default: NN)", OPT_ARGREQ }, - { 8, 'd', "debug", "Enable various debug features", OPT_NONE }, - - {10, '4', "socks4", "SOCKS4 proxy server", OPT_ARGREQ }, - {11, 'A', "socks4a", "SOCKS4A proxy server", OPT_ARGREQ }, - {12, 'P', "proxy-port", "Proxy port (default: 1080)", OPT_ARGREQ }, -}; - -const int optListN = (sizeof(optList) / sizeof(optList[0])); - - -void argShowHelp(void) -{ - th_print_banner(stdout, th_prog_name, - "[options] "); - - th_args_help(stdout, optList, optListN); -} - - -BOOL argHandleOpt(const int optN, char *optArg, char *currArg) -{ - switch (optN) - { - case 0: - argShowHelp(); - exit(0); - break; - - case 1: - th_verbosityLevel++; - break; - - case 2: - optPort = atoi(optArg); - break; - - case 3: - optServer = optArg; - break; - - case 4: - if ((optUserColor = th_get_hex_triplet(optArg)) < 0) - { - THERR("Invalid color argument '%s', should be a RGB hex triplet '000000'.\n", - optArg); - return FALSE; - } - THMSG(1, "Using color #%06x\n", optUserColor); - break; - - case 5: - optLogFilename = optArg; - optLogEnable = TRUE; - break; - - case 7: - optSite = optArg; - break; - - case 6: - optDaemon = TRUE; - THMSG(1, "Running in pseudo-daemon mode.\n"); - break; - - case 8: - optDebug = TRUE; - THMSG(1, "Debug mode enabled.\n"); - break; - - - case 10: - optProxyServer = optArg; - optProxyType = NN_PROXY_SOCKS4; - break; - - case 11: - optProxyServer = optArg; - optProxyType = NN_PROXY_SOCKS4A; - break; - - case 12: - optPort = atoi(optArg); - break; - - - default: - THERR("Unknown option '%s'.\n", currArg); - return FALSE; - } - - return TRUE; -} - - -BOOL argHandleFile(char *currArg) -{ - if (!optUserNameCmd) - optUserNameCmd = currArg; - else if (!optPasswordCmd) - optPasswordCmd = currArg; - else - { - THERR("Username '%s' already specified on commandline!\n", optUserNameCmd); - return FALSE; - } - - return TRUE; -} - -BOOL str_get_timestamp(char *str, size_t len, const char *fmt) -{ - time_t stamp = time(NULL); - struct tm *stamp_tm; - if ((stamp_tm = localtime(&stamp)) != NULL) - { - strftime(str, len, fmt, stamp_tm); - return TRUE; - } - else - { - str[0] = 0; - return FALSE; - } -} - - -char * str_trim_left(char *buf) -{ - while (*buf != 0 && th_isspace(*buf)) buf++; - return buf; -} - -int compareUsername(const void *s1, const void *s2) -{ - return th_strcasecmp((char *) s1, (char *) s2); -} - -nn_window_t *findWindow(const char *id) -{ - int i; - - for (i = 0; i < SET_MAX_WINDOWS; i++) - if (chatWindows[i] != NULL && - chatWindows[i]->id != NULL && - th_strcasecmp(id, chatWindows[i]->id) == 0) - return chatWindows[i]; - - return NULL; -} - - -BOOL openWindow(const char *name, BOOL curwin) -{ - int i; - nn_window_t *res; - if (name == NULL) - return FALSE; - - if ((res = nn_window_new(name)) == NULL) - return FALSE; - - for (i = 1; i < SET_MAX_WINDOWS; i++) - if (chatWindows[i] == NULL) - { - res->num = i; - chatWindows[i] = res; - if (curwin) - currWin = res; - return TRUE; - } - - return FALSE; -} - - -void closeWindow(nn_window_t *win) -{ - int i; - if (win == NULL) return; - - for (i = 1; i < SET_MAX_WINDOWS; i++) - if (chatWindows[i] == win) - { - chatWindows[i] = NULL; - nn_window_free(win); - return; - } -} - - -void updateStatus(void) -{ - char tmpStr[128]; - int i; - - if (statusWin == NULL) return; - - str_get_timestamp(tmpStr, sizeof(tmpStr), "%H:%M:%S"); - - wbkgdset(statusWin, COLOR_PAIR(10)); - werase(statusWin); - - wattrset(statusWin, A_BOLD | COLOR_PAIR(11)); - mvwaddstr(statusWin, 0, 1, tmpStr); - - wattrset(statusWin, A_BOLD | COLOR_PAIR(13)); - waddstr(statusWin, " | "); - wattrset(statusWin, A_BOLD | COLOR_PAIR(16)); - waddstr(statusWin, optUserName); - wattrset(statusWin, A_BOLD | COLOR_PAIR(13)); - - wattrset(statusWin, A_BOLD | COLOR_PAIR(13)); - waddstr(statusWin, " | "); - wattrset(statusWin, A_BOLD | COLOR_PAIR(11)); - snprintf(tmpStr, sizeof(tmpStr), "#%06x", optUserColor); - waddstr(statusWin, tmpStr); - - wattrset(statusWin, A_BOLD | COLOR_PAIR(13)); - waddstr(statusWin, " | WIN: "); - snprintf(tmpStr, sizeof(tmpStr), "%d: %s / %d", - currWin->num + 1, - currWin->id != NULL ? currWin->id : "MAIN", - currWin->pos); - waddstr(statusWin, tmpStr); - - wattrset(statusWin, A_BOLD | COLOR_PAIR(13)); - waddstr(statusWin, " | "); - wattrset(statusWin, A_BOLD | COLOR_PAIR(11)); - - for (i = 0; i < SET_MAX_WINDOWS; i++) - if (chatWindows[i] != NULL && chatWindows[i]->dirty) - { - snprintf(tmpStr, sizeof(tmpStr), "%d ", i + 1); - waddstr(statusWin, tmpStr); - } - - wrefresh(statusWin); -} - - -void printEditBuf(nn_editbuf_t *buf) -{ - char *tmp; - if (editWin == NULL || buf == NULL) return; - - buf->data[buf->len] = 0; - tmp = nn_username_decode(th_strdup(buf->data)); - - werase(editWin); - - wattrset(editWin, A_NORMAL); - - if (buf->pos < buf->len) - { - waddnstr(editWin, tmp, buf->pos); - wattrset(editWin, A_REVERSE); - waddch(editWin, tmp[buf->pos]); - wattrset(editWin, A_NORMAL); - waddnstr(editWin, tmp + buf->pos + 1, buf->len - buf->pos - 1); - } - else - { - waddnstr(editWin, tmp, buf->len); - wattrset(editWin, A_REVERSE); - waddch(editWin, ' '); - wattrset(editWin, A_NORMAL); - } - wrefresh(editWin); - th_free(tmp); -} - - -int printWin(WINDOW *win, const char *fmt) -{ - const char *s = fmt; - int col = 0; - - while (*s) - { - if (*s == '½') - { - s++; - if (*s == '½') - { - waddch(win, ((unsigned char) *s) | col); - s++; - } - else - { - memcpy(&col, s, sizeof(int)); - s += sizeof(int); - } - } - else - { - waddch(win, ((unsigned char) *s) | col); - s++; - } - } - return 0; -} - - -#define QPUTCH(ch) th_vputch(&(win->buf), &(win->bufsize), &(win->len), ch) - -int nn_window_print(nn_window_t *win, const char *fmt) -{ - const char *s = fmt; - int col = 0; - while (*s) - { - if (*s == '½') - { - s++; - if (*s == '½') - { - QPUTCH(*s); - QPUTCH(*s); - win->chlen++; - } - else - { - int val = 0; - while (*s >= '0' && *s <= '9') - { - val *= 10; - val += (*s - '0'); - s++; - } - if (*s != '½') return -1; - - if (val < 9) - col = A_DIM | COLOR_PAIR(val); - else if (val < 30) - col = A_BOLD | COLOR_PAIR(val - 9); - - QPUTCH('½'); - - if (!th_growbuf(&(win->buf), &(win->bufsize), &(win->len), sizeof(int))) - return -2; - - memcpy(win->buf + win->len, &col, sizeof(int)); - win->len += sizeof(int); - } - } - else if (*s == '\n') - { - QPUTCH('\n'); - QPUTCH(0); - th_ringbuf_add(win->data, win->buf); - win->buf = NULL; - win->chlen = 0; - win->dirty = TRUE; - } - else if (*s != '\r') - { - QPUTCH((unsigned char) *s == 255 ? ' ' : *s); - win->chlen++; - } - - s++; - } - - return 0; -} - - -BOOL updateMainWin(BOOL force) -{ - int h, offs; - qringbuf_t *buf; - - /* Check pointers */ - if (mainWin == NULL || currWin == NULL) - return FALSE; - - /* Check if update is forced or if the window is dirty */ - if (!force && !currWin->dirty) - return FALSE; - - /* Compute how many lines from backbuffer fit on the screen */ - buf = currWin->data; - h = getmaxy(mainWin); - - /* Clear and redraw window */ - werase(mainWin); - scrollok(mainWin, 1); - for (offs = buf->size - h - currWin->pos; offs >= 0 && offs < buf->size - currWin->pos && offs < buf->size; offs++) - { - if (buf->data[offs] != NULL) - printWin(mainWin, buf->data[offs]); - } - - currWin->dirty = FALSE; - wrefresh(mainWin); - return TRUE; -} - - -int printFile(FILE *outFile, const char *fmt) -{ - const char *s = fmt; - - while (*s) - { - if (*s == '½') - { - s++; - if (*s == '½') - { - fputc((unsigned char) *s, outFile); - s++; - } - else - { - while (*s && isdigit((int) *s)) s++; - if (*s != '½') return -1; - s++; - } - } - else - { - if ((unsigned char) *s == 255) - fputc(' ', outFile); - else - fputc((unsigned char) *s, outFile); - s++; - } - } - - return 0; -} - -void printMsgV(nn_window_t *win, int flags, const char *fmt, va_list ap) -{ - char tmpStr[128], *buf; - - str_get_timestamp(tmpStr, sizeof(tmpStr), "½17½[½11½%H:%M:%S½17½]½0½ "); - - buf = th_strdup_vprintf(fmt, ap); - - if (optLogFile && (flags & LOG_FILE)) - { - if (flags & LOG_STAMP) printFile(optLogFile, tmpStr); - printFile(optLogFile, buf); - fflush(optLogFile); - } - - if (!optDaemon && (flags & LOG_WINDOW)) - { - nn_window_t *tmp = win != NULL ? win : chatWindows[0]; - if (flags & LOG_STAMP) nn_window_print(tmp, tmpStr); - nn_window_print(tmp, buf); - } - - th_free(buf); -} - -void printMsg(nn_window_t *win, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - printMsgV(win, LOG_STAMP | LOG_WINDOW | LOG_FILE, fmt, ap); - va_end(ap); -} - -void printMsgF(nn_window_t *win, int flags, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - printMsgV(win, flags | LOG_STAMP, fmt, ap); - va_end(ap); -} - -void printMsgQ(nn_window_t *win, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - printMsgV(win, LOG_STAMP | LOG_WINDOW, fmt, ap); - va_end(ap); -} - - -char *errorMessages = NULL; - -void errorMsgV(const char *fmt, va_list ap) -{ - char *tmp = th_strdup_vprintf(fmt, ap); - - printMsg(NULL, "%s", tmp); - - if (errorMessages != NULL) - { - char *tmp2 = th_strdup_printf("%s%s", errorMessages, tmp); - th_free(errorMessages); - th_free(tmp); - errorMessages = tmp2; - } - else - errorMessages = tmp; -} - -void errorMsg(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - errorMsgV(fmt, ap); - va_end(ap); -} - -void errorFunc(struct _nn_conn_t *conn, const char *fmt, va_list ap) -{ - (void) conn; - errorMsgV(fmt, ap); -} - -void messageFunc(struct _nn_conn_t *conn, const char *fmt, va_list ap) -{ - (void) conn; - printMsgV(NULL, LOG_STAMP | LOG_WINDOW | LOG_FILE, fmt, ap); -} - - -BOOL checkIgnoreList(const char *name) -{ - qlist_t *node = setIgnoreList; - while (node != NULL) - { - if (th_strcasecmp(name, (char *) node->data) == 0) - return TRUE; - node = node->next; - } - return FALSE; -} - - -int nnproto_handle_user(nn_conn_t *conn) -{ - static const char *msg = ""; - char *p = conn->ptr; - BOOL isMine, isIgnored = FALSE; - char *s, *t, *userName; - - /* Find start of the message */ - s = strstr(p, msg); - if (!s) return 1; - *s = 0; - s += strlen(msg); - - /* Find end of the message */ - t = strstr(s, ""); - if (!t) return 3; - *t = 0; - - /* Decode message string */ - s = nn_decode_str1(s); - if (!s) return -1; - - /* Decode username */ - userName = nn_decode_str1(p); - if (!userName) - { - th_free(s); - return -2; - } - - /* Check if the username is on our ignore list and - * that it is not our OWN username! - */ - isMine = strcmp(userName, optUserName) == 0; - isIgnored = setIgnoreMode && !isMine && checkIgnoreList(userName); - - /* Is it a special control message? */ - if (*s == '/') - { - /* Ignore room join/leave messages */ - if (!optDebug && (strstr(s, "left the room") || strstr(s, "joined the room from"))) - goto done; - - t = nn_strip_tags(s + 1); - if (!strncmp(t, "BPRV ", 5)) - { - char *name, *tmp, *msg, *h; - nn_window_t *win; - h = nn_decode_str2(t + 1); - - if (!strncmp(t, "BPRV from ", 10)) - { - name = nn_decode_str2(t + 10); - isMine = FALSE; - } - else - { - name = nn_decode_str2(t + 8); - isMine = TRUE; - } - - for (tmp = name; *tmp && *tmp != ':'; tmp++); - if (tmp[0] != 0 && tmp[1] == ' ') - msg = tmp + 2; - else - msg = ""; - *tmp = 0; - - isIgnored = setIgnoreMode && checkIgnoreList(name); - win = findWindow(name); - - if (win != NULL) - { - printMsgF(win, isIgnored ? 0 : LOG_WINDOW, - "½5½<½%d½%s½5½>½0½ %s\n", - isMine ? 14 : 15, isMine ? optUserName : name, msg); - - printMsgF(NULL, LOG_FILE, "½11½%s½0½\n", h); - } - else - { - printMsgF(NULL, isIgnored ? LOG_FILE : (LOG_WINDOW | LOG_FILE), - "½11½%s½0½\n", h); - } - th_free(name); - th_free(h); - } - else - { - /* It's an action (/me) */ - char *h = nn_decode_str2(t); - printMsgF(NULL, isIgnored ? LOG_FILE : (LOG_WINDOW | LOG_FILE), - "½9½* %s½0½\n", h); - th_free(h); - } - th_free(t); - } - else - { - /* It's a normal message */ - char *h; - t = nn_strip_tags(s); - h = nn_decode_str2(t); - printMsgF(NULL, isIgnored ? LOG_FILE : (LOG_WINDOW | LOG_FILE), - "½5½<½%d½%s½5½>½0½ %s\n", isMine ? 14 : 15, userName, h); - th_free(h); - th_free(t); - } - -done: - th_free(s); - th_free(userName); - return 0; -} - - -int nnproto_handle_login(nn_conn_t *conn) -{ - char tmpStr[256]; - str_get_timestamp(tmpStr, sizeof(tmpStr), "%c"); - - if (!nn_conn_buf_strcmp(conn, "FAILURE>")) - { - printMsg(NULL, "½1½Login failure½0½ - ½3½%s½0½\n", tmpStr); - return -2; - } - else if (!nn_conn_buf_strcmp(conn, "SUCCESS>")) - { - printMsg(NULL, "½2½Login success½0½ - ½3½%s½0½\n", tmpStr); - nn_conn_send_msg(conn, optUserNameEnc, "%%2FRequestUserList"); - return 0; - } - else - return 1; -} - - -int nnproto_handle_add_user(nn_conn_t *conn) -{ - char *p, *s, *str = conn->ptr; - nn_window_t *win; - - s = nn_conn_buf_strstr(conn, ""); - if (!s) return 1; - *s = 0; - - p = nn_dbldecode_str(str); - if (!p) return -1; - - win = findWindow(p); - nn_userhash_insert(nnUsers, nn_username_encode(p)); - - printMsg(NULL, "! ½3½%s½0½ ½2½ADDED.½0½\n", p); - if (win != NULL) - printMsg(win, "! ½3½%s½0½ ½2½joined the chat.½0½\n", p); - - th_free(p); - return 0; -} - - -int nnproto_handle_delete_user(nn_conn_t *conn) -{ - char *p, *s, *str = conn->ptr; - nn_window_t *win; - - s = nn_conn_buf_strstr(conn, ""); - if (!s) return 1; - *s = 0; - - p = nn_dbldecode_str(str); - if (!p) return -1; - - win = findWindow(p); - nn_userhash_delete(nnUsers, nn_username_encode(p)); - - printMsg(NULL, "! ½3½%s½0½ ½1½DELETED.½0½\n", p); - if (win != NULL) - printMsg(win, "! ½3½%s½0½ ½1½left the chat.½0½\n", p); - - th_free(p); - return 0; -} - - -int nnproto_handle_num_clients(nn_conn_t *conn) -{ - nn_conn_buf_strstr(conn, ""); - return 0; -} - - -int nnproto_handle_boot(nn_conn_t *conn) -{ - (void) conn; - errorMsg("Booted by server.\n"); - return -1; -} - - -typedef struct -{ - char *cmd; - ssize_t len; - int (*handler)(nn_conn_t *); -} nn_protocolcmd_t; - - -static nn_protocolcmd_t protoCmds[] = -{ - { "", -1, nnproto_handle_user }, - { "", -1, nnproto_handle_delete_user }, - { "", -1, nnproto_handle_add_user }, - { "", -1, nnproto_handle_num_clients }, - { "", -1, nnproto_handle_boot }, -}; - -static const int nprotoCmds = sizeof(protoCmds) / sizeof(protoCmds[0]); - - -int nn_parse_protocol(nn_conn_t *conn) -{ - static BOOL protoCmdsInit = FALSE; - int i; - - if (!protoCmdsInit) - { - for (i = 0; i < nprotoCmds; i++) - protoCmds[i].len = strlen(protoCmds[i].cmd); - - protoCmdsInit = TRUE; - } - - for (i = 0; i < nprotoCmds; i++) - { - if (!nn_conn_buf_strncmp(conn, protoCmds[i].cmd, protoCmds[i].len)) - return protoCmds[i].handler(conn); - } - - if (optDebug) - { - printMsg(NULL, "Unknown protocmd: \"%s\"\n", conn->ptr); - return 0; - } - else - return 1; -} - - -int nn_handle_input(nn_conn_t *conn, char *buf, size_t bufLen) -{ - char *tmpStr, tmpBuf[4096]; - BOOL result; - - /* Trim right */ - bufLen--; - buf[bufLen--] = 0; - while (bufLen > 0 && th_isspace(buf[bufLen])) - buf[bufLen--] = 0; - - /* Decode completed usernames */ - nn_username_decode(buf); - - /* Check for special user commands */ - if (*buf == 0) - { - return 1; - } - else if (!th_strncasecmp(buf, "/color ", 7)) - { - /* Change color */ - int tmpInt; - if ((tmpInt = th_get_hex_triplet(str_trim_left(buf + 7))) < 0) - { - printMsgQ(currWin, "Invalid color value '%s'\n", buf+7); - return 1; - } - optUserColor = tmpInt; - printMsgQ(currWin, "Setting color to #%06x\n", optUserColor); - nn_conn_send_msg(conn, optUserNameEnc, "%%2FSetFontColor%%20%%2Dcolor%%20%06X", optUserColor); - return 0; - } - else if (!th_strncasecmp(buf, "/ignore", 7)) - { - char *name = str_trim_left(buf + 7); - if (strlen(name) > 0) - { - /* Add or remove someone to/from ignore */ - qlist_t *user = th_llist_find_func(setIgnoreList, name, compareUsername); - if (user != NULL) - { - printMsgQ(currWin, "Removed user '%s' from ignore.\n", name); - th_llist_delete_node(&setIgnoreList, user); - } - else - { - printMsgQ(currWin, "Now ignoring '%s'.\n", name); - th_llist_append(&setIgnoreList, th_strdup(name)); - } - } - else - { - /* Just list whomever is in ignore now */ - qlist_t *user = setIgnoreList; - ssize_t nuser = th_llist_length(setIgnoreList); - char *result = th_strdup_printf("Users ignored (%d): ", nuser); - while (user != NULL) - { - if (user->data != NULL) - { - th_pstr_printf(&result, "%s'%s'", result, (char *) user->data); - if (--nuser > 0) - th_pstr_printf(&result, "%s, ", result); - } - user = user->next; - } - printMsgQ(currWin, "%s\n", result); - th_free(result); - } - return 0; - } - else if (!th_strncasecmp(buf, "/query", 6)) - { - char *name = str_trim_left(buf + 6); - if (strlen(name) > 0) - { - nn_user_t *user = nn_user_find(nnUsers, nn_username_encode(name)); - if (user != NULL) - { - name = nn_username_decode(th_strdup(user->name)); - printMsgQ(currWin, "Opening PRV query for '%s'.\n", name); - if (openWindow(name, TRUE)) - printMsgQ(currWin, "In PRV query with '%s'.\n", name); - th_free(name); - } - } - else - { - printMsgQ(currWin, "Usage: /query username\n"); - printMsgQ(currWin, "To close a PRV query, use /close [username]\n"); - printMsgQ(currWin, "/close without username will close the current PRV window.\n"); - } - return 0; - } - else if (!th_strncasecmp(buf, "/win", 4)) - { - /* Change color */ - char *tmp = str_trim_left(buf + 4); - if (strlen(tmp) > 0) - { - int val = atoi(tmp); - if (val >= 1 && val < SET_MAX_WINDOWS) - { - if (chatWindows[val - 1] != NULL) - currWin = chatWindows[val - 1]; - } - else - { - printMsgQ(currWin, "Invalid window number '%s'\n", tmp); - return 1; - } - } - else - { - printMsgQ(currWin, "Window : #%d\n", currWin->num); - printMsgQ(currWin, "ID : %s\n", currWin->id); - } - return 0; - } - else if (!th_strncasecmp(buf, "/close", 6)) - { - char *name = str_trim_left(buf + 6); - if (strlen(name) > 0) - { - nn_window_t *win = findWindow(name); - if (win != NULL) - { - closeWindow(win); - printMsgQ(currWin, "Closed PRV query to '%s'.\n", name); - } - else - { - printMsgQ(currWin, "No PRV query by name '%s'.\n", name); - } - } - else - { - if (currWin != chatWindows[0]) - { - closeWindow(currWin); - currWin = chatWindows[0]; - } - } - return 0; - } - else if (!th_strncasecmp(buf, "/save", 5)) - { - /* Save configuration */ - FILE *cfgfile = fopen(setConfigFile, "w"); - if (cfgfile == NULL) - { - printMsgQ(currWin, "Could not create configuration to file '%s': %s\n", - setConfigFile, strerror(errno)); - return 0; - } - printMsgQ(currWin, "Configuration saved in file '%s', res=%d\n", - setConfigFile, - th_cfg_write(cfgfile, setConfigFile, cfg)); - - fclose(cfgfile); - return 0; - } - else if (!th_strncasecmp(buf, "/w ", 3)) - { - /* Open given username's profile via firefox in a new tab */ - char *name = str_trim_left(buf + 3); - - printMsg(currWin, "Opening profile for: '%s'\n", name); - - tmpStr = nn_encode_str1(name); -#ifdef __WIN32 - { - HINSTANCE status; - snprintf(tmpBuf, sizeof(tmpBuf), "http://www.newbienudes.com/profile/%s/", tmpStr); - th_free(tmpStr); - status = ShellExecute(NULL, "open", tmpBuf, NULL, NULL, SW_SHOWNA); - if (status <= (HINSTANCE) 32) - printMsgQ(currWin, "Could not launch default web browser: %d\n", status); - } -#else - { - int status; - int fds[2]; - pid_t pid; - snprintf(tmpBuf, sizeof(tmpBuf), "openurl(http://www.newbienudes.com/profile/%s/,new-tab)", tmpStr); - th_free(tmpStr); - - if (pipe(fds) == -1) - { - int ret = errno; - printMsgQ(currWin, "Could not open process communication pipe! (%d, %s)\n", ret, strerror(ret)); - return 0; - } - - if ((pid = fork()) < 0) - { - printMsgQ(currWin, "Could not create sub-process!\n"); - } - else if (pid == 0) - { - dup2(fds[1], STDOUT_FILENO); - dup2(fds[0], STDERR_FILENO); - execlp(setBrowser, setBrowser, "-remote", tmpBuf, (void *)NULL); - _exit(errno); - } - - wait(&status); - } -#endif - return 0; - } - else if (!th_strncasecmp(buf, "/who", 4)) - { - /* Alias /who to /listallusers */ - snprintf(tmpBuf, sizeof(tmpBuf), "/listallusers"); - buf = tmpBuf; - } - - if (currWin != chatWindows[0]) - { - if (currWin->id != NULL) - { - snprintf(tmpBuf, sizeof(tmpBuf), "/prv -to %s -msg %s", currWin->id, buf); - buf = tmpBuf; - } - else - { - printMsgQ(NULL, "No target set, exiting prv mode.\n"); - return 1; - } - } - - /* Send double-encoded */ - tmpStr = nn_dblencode_str(nn_username_decode(buf)); - if (tmpStr == 0) return -2; - result = nn_conn_send_msg(conn, optUserNameEnc, "%s", tmpStr); - th_free(tmpStr); - - return result ? 0 : -1; -} - - -void closeWindows(void) -{ - if (mainWin) delwin(mainWin); - if (statusWin) delwin(statusWin); - if (editWin) delwin(editWin); -} - - -BOOL initializeWindows(void) -{ - int w, h; - - getmaxyx(stdscr, h, w); - - closeWindows(); - - mainWin = subwin(stdscr, h - 4, w, 0, 0); - statusWin = subwin(stdscr, 1, w, h - 4, 0); - editWin = subwin(stdscr, 3, w, h - 3, 0); - - if (mainWin == NULL || statusWin == NULL || editWin == NULL) - return FALSE; - - return TRUE; -} - - -void updateWindows(void) -{ - if (mainWin) redrawwin(mainWin); - if (statusWin) redrawwin(statusWin); - if (editWin) redrawwin(editWin); -} - - -BOOL performTabCompletion(nn_editbuf_t *buf) -{ - static char *previous = NULL, *pattern = NULL; - BOOL again = FALSE, hasSeparator = FALSE, newPattern = FALSE, hasSpace = FALSE; - char *str = buf->data; - int mode = 0; - ssize_t endPos, startPos = buf->pos; - - /* previous word */ - if (startPos >= 2 && str[startPos - 1] == ' ' && str[startPos - 2] != ' ') - { - startPos -= 2; - endPos = startPos; - while (startPos > 0 && str[startPos - 1] != ' ') startPos--; - mode = 1; - } - else - /* middle of a word, new pattern */ - if (startPos < buf->len && str[startPos] != ' ') - { - endPos = startPos; - while (startPos > 0 && str[startPos - 1] != ' ') startPos--; - while (endPos < buf->len - 1 && str[endPos + 1] != ' ') endPos++; - newPattern = TRUE; - mode = 2; - } - else - /* previous word, new pattern */ - if (startPos >= 1 && str[startPos - 1] != ' ') - { - startPos -= 1; - endPos = startPos; - while (startPos > 0 && str[startPos - 1] != ' ') startPos--; - newPattern = TRUE; - mode = 3; - } - else - { - if (optDebug) - printMsg(currWin, "no mode\n"); - return FALSE; - } - - if (str[endPos] == optNickSep) - { - endPos--; - if (startPos > 0) - { - if (optDebug) - printMsg(currWin, "str[endPos] == optNickSep && startPos > 0 (%d)\n", startPos); - return FALSE; - } - hasSeparator = TRUE; - } - - if (buf->pos > 0 && str[buf->pos - 1] == ' ') - hasSpace = TRUE; - if (buf->pos <= buf->len && str[buf->pos] == ' ') - hasSpace = TRUE; - - if (newPattern) - { - /* Get pattern, check if it matches previous pattern and set 'again' flag */ - char *npattern = nn_editbuf_get_string(buf, startPos, endPos); - if (pattern && npattern && th_strcasecmp(npattern, pattern) == 0) - again = TRUE; - - th_free(pattern); - pattern = npattern; - - if (!again) - { - th_free(previous); - previous = NULL; - } - } - - if (optDebug) - { - printMsg(currWin, "sPos=%d, ePos=%d <-> bPos=%d, bufLen=%d : pat='%s' (again=%s, hassep=%s, hasspc=%s, newpat=%s, mode=%d)\n", - startPos, endPos, buf->pos, buf->len, pattern, - again ? "yes" : "no", - hasSeparator ? "yes" : "no", - hasSpace ? "yes" : "no", - newPattern ? "yes" : "no", mode); - } - - if (pattern) - { - nn_user_t *user = nn_user_match(nnUsers, pattern, previous, again); - - if (user) - { - int i; - char *c = user->name; - if (optDebug) - printMsg(currWin, "match='%s' / prev='%s'\n", user->name, previous); - - for (i = startPos; i <= endPos; i++) - nn_editbuf_delete(buf, startPos); - - for (i = startPos; *c; i++, c++) - nn_editbuf_insert(buf, i, *c); - - if (!hasSeparator && startPos == 0) - { - nn_editbuf_insert(buf, i++, optNickSep); - startPos++; - } - if (hasSeparator) - startPos++; - if (!hasSpace) - nn_editbuf_insert(buf, i++, ' '); - - nn_editbuf_setpos(buf, startPos + 1 + strlen(user->name)); - - th_free(previous); - previous = th_strdup(user->name); - - return TRUE; - } - } - - return FALSE; -} - - -#define VPUTCH(CH) th_vputch(&bufData, &bufSize, &bufLen, CH) -#define VPUTS(STR) th_vputs(&bufData, &bufSize, &bufLen, STR) - -char *logParseFilename(const char *fmt, int id) -{ - size_t bufSize = strlen(fmt) + TH_BUFGROW, bufLen = 0; - char *bufData = th_malloc(bufSize); - char tmpBuf[32]; - const char *s = fmt; - - while (*s) - { - if (*s == '%') - { - s++; - switch (*s) - { - case 'i': - snprintf(tmpBuf, sizeof(tmpBuf), "%05d", id); - VPUTS(tmpBuf); - break; - - case 'd': - snprintf(tmpBuf, sizeof(tmpBuf), "%d", id); - VPUTS(tmpBuf); - break; - - case '%': - VPUTCH('%'); - break; - } - s++; - } - else - { - VPUTCH(*s); - s++; - } - } - - VPUTCH(0); - return bufData; -} - - -BOOL logFileOpen(void) -{ - char *filename; - - if (optLogFilename == NULL || !optLogEnable) - return FALSE; - - filename = logParseFilename(optLogFilename, optPort); - - if ((optLogFile = fopen(filename, "a")) == NULL) - { - errorMsg("Could not open logfile '%s' for appending!\n", filename); - th_free(filename); - return FALSE; - } - - th_free(filename); - - return TRUE; -} - - -void logFileClose(void) -{ - if (optLogFile) - { - fclose(optLogFile); - optLogFile = NULL; - } -} - - -char *promptRequester(WINDOW *win, const char *info, BOOL allowEmpty) -{ - char tmpBuf[512], *ptr; - ssize_t pos; - int curVis = curs_set(1); - - echo(); - waddstr(win, info); - wgetnstr(win, tmpBuf, sizeof(tmpBuf) - 1); - noecho(); - if (curVis != ERR) - curs_set(curVis); - - for (pos = strlen(tmpBuf) - 1; pos > 0 && th_isspace(tmpBuf[pos]); pos--) - tmpBuf[pos] = 0; - - ptr = str_trim_left(tmpBuf); - - if (allowEmpty || strlen(ptr) > 0) - return th_strdup(ptr); - else - return NULL; -} - - -void printHelp(void) -{ - printMsgQ(currWin, "\n" - "NNChat Help\n" - "===========\n" - "\n" - "F1 This help.\n" - "F2 \n" - ); -} - - -int main(int argc, char *argv[]) -{ - nn_conn_t *conn = NULL; - int curVis = ERR, updateCount = 0; - BOOL argsOK, isError = FALSE, - exitProg = FALSE, - colorSet = FALSE, - cursesInit = FALSE, - networkInit = FALSE, - insertMode = TRUE, - firstUpdate = TRUE; - time_t prevTime; - char *tmpStr; - nn_editbuf_t *editBuf = nn_editbuf_new(NN_TMPBUF_SIZE); - nn_editbuf_t *histBuf[SET_MAX_HISTORY+2]; - int histPos = 0, histMax = 0; - - cfgitem_t *tmpcfg; - char *homeDir = NULL; - - memset(histBuf, 0, sizeof(histBuf)); - - /* Initialize */ - th_init("NNChat", "Newbie Nudes chat client", NN_VERSION, - "Written and designed by Anonymous Finnish Guy (C) 2008-2012", - "This software is freeware, use and distribute as you wish."); - th_verbosityLevel = 0; - - /* Read configuration file */ - tmpcfg = NULL; - th_cfg_add_comment(&tmpcfg, "General settings"); - th_cfg_add_string(&tmpcfg, "username", &optUserName, NULL); - th_cfg_add_string(&tmpcfg, "password", &optPassword, NULL); - - th_cfg_add_comment(&tmpcfg, "Default color as a hex-triplet"); - th_cfg_add_hexvalue(&tmpcfg, "color", &optUserColor, optUserColor); - - th_cfg_add_comment(&tmpcfg, "Default setting of ignore mode"); - th_cfg_add_bool(&tmpcfg, "ignore", &setIgnoreMode, setIgnoreMode); - th_cfg_add_comment(&tmpcfg, "People to be ignored when ignore mode is enabled"); - th_cfg_add_string_list(&tmpcfg, "ignore_list", &setIgnoreList); - - th_cfg_add_comment(&tmpcfg, "Random messages for idle timeout protection. If none are set, plain '.' is used."); - th_cfg_add_string_list(&tmpcfg, "idle_messages", &setIdleMessages); - - th_cfg_add_comment(&tmpcfg, "Character used as nickname auto-completion separator (default is ':')"); - th_cfg_add_string(&tmpcfg, "nick_separator", &optNickSepStr, NULL); - - th_cfg_add_section(&cfg, "general", tmpcfg); - - - tmpcfg = NULL; - th_cfg_add_comment(&tmpcfg, "Chat server hostname or IP address"); - th_cfg_add_string(&tmpcfg, "host", &optServer, optServer); - th_cfg_add_comment(&tmpcfg, "Default port to connect to (8005 = main room, 8003 = passion pit)"); - th_cfg_add_int(&tmpcfg, "port", &optPort, optPort); - th_cfg_add_section(&cfg, "server", tmpcfg); - - tmpcfg = NULL; - th_cfg_add_comment(&tmpcfg, "Proxy server type (0 = none, 1 = SOCKS 4, 2 = SOCKS 4a)"); - th_cfg_add_int(&tmpcfg, "type", &optProxyType, optProxyType); - th_cfg_add_comment(&tmpcfg, "Proxy server host name"); - th_cfg_add_string(&tmpcfg, "host", &optProxyServer, optProxyServer); - th_cfg_add_comment(&tmpcfg, "Proxy port, 1080 is the standard SOCKS port"); - th_cfg_add_int(&tmpcfg, "port", &optProxyPort, optProxyPort); - th_cfg_add_section(&cfg, "proxy", tmpcfg); - - tmpcfg = NULL; - th_cfg_add_comment(&tmpcfg, "Enable logging"); - th_cfg_add_bool(&tmpcfg, "enable", &optLogEnable, optLogEnable); - th_cfg_add_comment(&tmpcfg, "Log filename format"); - th_cfg_add_string(&tmpcfg, "filename", &optLogFilename, optLogFilename); - th_cfg_add_section(&cfg, "logging", tmpcfg); - -#ifdef __WIN32 - { - char tmpPath[MAX_PATH]; - if (SHGetFolderPath(NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL, 0, tmpPath) == S_OK) - homeDir = th_strdup(tmpPath); - - CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); - } -#else - homeDir = th_strdup(getenv("HOME")); -#endif - - if (homeDir != NULL) - { - FILE *cfgfile; - setConfigFile = th_strdup_printf("%s" SET_DIR_SEPARATOR "%s", homeDir, SET_CONFIG_FILE); - - THMSG(0, "Reading configuration from '%s'.\n", setConfigFile); - - if ((cfgfile = fopen(setConfigFile, "r")) != NULL) - { - th_cfg_read(cfgfile, setConfigFile, cfg); - fclose(cfgfile); - } - } - - if (optNickSepStr) - optNickSep = optNickSepStr[0]; - else - optNickSep = SET_NICK_SEPARATOR; - - - setBrowser = getenv("BROWSER"); - if (setBrowser == NULL) - setBrowser = "firefox"; - - /* Parse command line arguments */ - argsOK = th_args_process(argc, argv, optList, optListN, - argHandleOpt, argHandleFile, FALSE); - - if (optUserNameCmd != NULL) - { - optUserName = optUserNameCmd; - optPassword = optPasswordCmd; - } - - if (!argsOK) - return -2; - - /* Allocate userhash */ - if ((nnUsers = nn_userhash_new()) == NULL) - { - THERR("Could not allocate userhash. Fatal error.\n"); - return -105; - } - - /* If no idle messages are set, add default */ - if (setIdleMessages == NULL) - { - th_llist_append(&setIdleMessages, th_strdup(".")); - } - - /* Open logfile */ - logFileOpen(); - - /* Initialize network */ - if (!nn_network_init()) - { - THERR("Could not initialize network subsystem.\n"); - goto err_exit; - } - else - networkInit = TRUE; - - /* Initialize NCurses */ - if (!optDaemon) - { - if (LINES < 0 || LINES > 1000) LINES = 24; - if (COLS < 0 || COLS > 1000) COLS = 80; - initscr(); - raw(); - keypad(stdscr, TRUE); - noecho(); - meta(stdscr, TRUE); - timeout(SET_DELAY); - curVis = curs_set(0); - - if (has_colors()) - { - start_color(); - - init_pair( 1, COLOR_RED, COLOR_BLACK); - init_pair( 2, COLOR_GREEN, COLOR_BLACK); - init_pair( 3, COLOR_YELLOW, COLOR_BLACK); - init_pair( 4, COLOR_BLUE, COLOR_BLACK); - init_pair( 5, COLOR_MAGENTA, COLOR_BLACK); - init_pair( 6, COLOR_CYAN, COLOR_BLACK); - init_pair( 7, COLOR_WHITE, COLOR_BLACK); - init_pair( 8, COLOR_BLACK, COLOR_BLACK); - - init_pair(10, COLOR_BLACK, COLOR_RED); - init_pair(11, COLOR_WHITE, COLOR_RED); - init_pair(12, COLOR_GREEN, COLOR_RED); - init_pair(13, COLOR_YELLOW, COLOR_RED); - init_pair(14, COLOR_BLUE, COLOR_RED); - init_pair(15, COLOR_MAGENTA, COLOR_RED); - init_pair(16, COLOR_CYAN, COLOR_RED); - } - - cursesInit = TRUE; - - if (!initializeWindows()) - goto err_exit; - -#ifdef PDCURSES - PDC_set_title("NNChat v" NN_VERSION); -#endif - - memset(chatWindows, 0, sizeof(chatWindows)); - chatWindows[0] = nn_window_new(NULL); - currWin = chatWindows[0]; - updateStatus(); - } - - /* Check if we have username and password */ - if (cursesInit && (optUserName == NULL || optPassword == NULL)) - { - printWin(editWin, "You can avoid this prompt by issuing '/save' after logging in.\n"); - optUserName = promptRequester(editWin, "NN username: ", FALSE); - optPassword = promptRequester(editWin, "NN password: ", TRUE); - } - - if (optUserName == NULL || optPassword == NULL) - { - errorMsg("Username and/or password not specified.\n"); - goto err_exit; - } - - /* Create a connection */ - conn = nn_conn_new(errorFunc, messageFunc); - if (conn == NULL) - { - errorMsg("Could not create connection structure.\n"); - goto err_exit; - } - - /* Are we using a proxy? */ - if (optProxyType != NN_PROXY_NONE && optProxyServer != NULL) - { - if (nn_conn_set_proxy(conn, optProxyType, optProxyPort, optProxyServer) != 0) - { - errorMsg("Error setting proxy information.\n"); - goto err_exit; - } - } - - /* Okay ... */ - printMsg(currWin, "Trying to resolve host '%s' ...\n", optServer); - conn->host = th_strdup(optServer); - conn->hst = nn_resolve_host(conn, optServer); - if (conn->hst == NULL) - { - errorMsg("Could not resolve hostname: %s.\n", strerror(h_errno)); - goto err_exit; - } - -#ifdef FINAL_BUILD - /* To emulate the official client, we first make a request for - * policy file, even though we don't use it for anything... - */ - if (nn_conn_open(conn, 843, NULL) != 0) - { - errorMsg("Policy file request connection setup failed!\n"); - goto err_exit; - } - - tmpStr = ""; - if (nn_conn_send_buf(conn, tmpStr, strlen(tmpStr) + 1) == FALSE) - { - errorMsg("Failed to send policy file request.\n"); - goto err_exit; - } - else - { - int cres = nn_conn_pull(conn); - if (cres == 0) - { - printMsg(currWin, "Probe got: %s\n", conn->buf); - } - else - { - printMsg(currWin, "Could not get policy probe.\n"); - } - } - nn_conn_close(conn); -#endif - - /* Okay, now do the proper connection ... */ - if (nn_conn_open(conn, optPort, NULL) != 0) - { - errorMsg("Main connection setup failed!\n"); - goto err_exit; - } - - /* Send login command */ - optUserNameEnc = nn_dblencode_str(optUserName); - tmpStr = nn_dblencode_str(optSite); - nn_conn_send_msg(conn, optUserNameEnc, "%%2Flogin%%20%%2Dsite%%20%s%%20%%2Dpassword%%20%s", tmpStr, optPassword); - th_free(tmpStr); - - /* Initialize random numbers */ - prevTime = time(NULL); - srandom((int) prevTime); - - if (cursesInit) - { - /* Initialize rest of interactive UI code */ - nn_editbuf_clear(editBuf); - - /* First update of screen */ - printEditBuf(editBuf); - updateStatus(); - - printMsg(NULL, "%s v%s - %s\n", th_prog_name, th_prog_version, th_prog_fullname); - printMsg(NULL, "%s\n", th_prog_author); - printMsg(NULL, "%s\n", th_prog_license); - } - - /* Enter mainloop */ - nn_conn_reset(conn); - while (!isError && !exitProg) - { - nn_conn_reset(conn); - do { - int cres = nn_conn_pull(conn); - if (cres == 0 && *(conn->in_ptr - 1) == 0) - { - int result = nn_parse_protocol(conn); - if (result > 0) - { - /* Couldn't handle the message for some reason */ - printMsg(currWin, "Could not handle: %s\n", conn->ptr); - } - else if (result < 0) - { - /* Fatal error, quit */ - errorMsg("Fatal error with message: %s\n", conn->ptr); - isError = TRUE; - } - } - else if (cres < 0) - isError = TRUE; - else - break; - } - while (conn->total_bytes > 0 && !isError); - - if (!nn_conn_check(conn)) - isError = TRUE; - - /* Handle user input */ - if (cursesInit) - { - int c, cnt = 0; - BOOL update = FALSE, updateMain = FALSE; - - /* Handle several buffered keypresses at once */ - do - { - c = wgetch(stdscr); - - /* Handle various problematic cases where terminal - * keycodes do not get properly translated by curses - */ - if (c == 0x1b) - { - /* ^[O */ - c = wgetch(stdscr); - if (c == 'O') - { - c = wgetch(stdscr); - switch (c) - { - case 'd': - c = 0x204; - break; - case 'c': - c = 0x206; - break; - default: - if (optDebug) - printMsg(currWin, "Unhandled ESC-O key sequence 0x%02x\n", c); - break; - } - } - /* ^[[ */ - else if (c == '[') - { - c = wgetch(stdscr); - switch (c) - { - case 0x31: - c = wgetch(stdscr); - if (c >= 0x31 && c <= 0x39) - c = KEY_F(c - 0x30); - else - c = ERR; - break; - - case 0x32: - c = KEY_IC; - break; - case 0x33: - c = KEY_DC; - break; - - case 0x35: - c = KEY_PPAGE; - break; - case 0x36: - c = KEY_NPAGE; - break; - - case 0x37: - c = KEY_HOME; - break; - case 0x38: - c = KEY_END; - break; - - default: - if (optDebug) - printMsg(currWin, "Unhandled ESC-[*~ key sequence 0x%02x\n", c); - c = ERR; - break; - } - /* Get the trailing ~ */ - if (c != ERR) - wgetch(stdscr); - } - if (c >= 0x31 && c <= 0x39) - { - /* Chat window switching via Meta/Esc-[1..9] */ - int win = c - 0x31; - if (win < SET_MAX_WINDOWS && chatWindows[win] != NULL) - { - currWin = chatWindows[win]; - update = updateMain = TRUE; - } - c = ERR; - } - else - { - if (optDebug) - printMsg(currWin, "Unhandled ESC key sequence 0x%02x\n", c); - } - } -#if defined(__WIN32) && defined(PDCURSES) - else if (c >= 0x198 && c <= 0x1a0) - { - /* Chat window switching via Meta/Esc-[1..9] */ - int win = c - 0x198; - if (win < SET_MAX_WINDOWS && chatWindows[win] != NULL) - { - currWin = chatWindows[win]; - update = updateMain = TRUE; - } - c = ERR; - } -#endif - - switch (c) - { -#ifdef KEY_RESIZE - case KEY_RESIZE: - resize_term(0, 0); - erase(); - timeout(SET_DELAY); - - if (!initializeWindows()) - { - errorMsg("Error resizing curses chatWindows\n"); - isError = TRUE; - } - update = updateMain = TRUE; - break; -#endif - - case KEY_ENTER: - case '\n': - case '\r': - /* Call the user input handler */ - if (editBuf->len > 0) - { - int result; - - if (histMax > 0) - { - nn_editbuf_free(histBuf[SET_MAX_HISTORY+1]); - histBuf[SET_MAX_HISTORY+1] = NULL; - memmove(&histBuf[2], &histBuf[1], histMax * sizeof(histBuf[0])); - } - - histPos = 0; - histBuf[1] = nn_editbuf_copy(editBuf); - if (histMax < SET_MAX_HISTORY) histMax++; - - nn_editbuf_insert(editBuf, editBuf->len, 0); - result = nn_handle_input(conn, editBuf->data, editBuf->len); - - nn_editbuf_clear(editBuf); - - if (result < 0) - { - errorMsg("Fatal error handling user input: %s\n", editBuf->data); - isError = TRUE; - } - else - { - /* Update time value of last sent message for unidle timeouts */ - prevTime = time(NULL); - } - - updateMain = update = TRUE; - } - break; - - case KEY_UP: /* Backwards in input history */ - if (histPos == 0) - { - nn_editbuf_free(histBuf[0]); - histBuf[0] = nn_editbuf_copy(editBuf); - } - if (histPos < histMax) - { - histPos++; - nn_editbuf_free(editBuf); - editBuf = nn_editbuf_copy(histBuf[histPos]); - update = TRUE; - } - break; - - case KEY_DOWN: /* Forwards in input history */ - if (histPos > 0) - { - histPos--; - nn_editbuf_free(editBuf); - editBuf = nn_editbuf_copy(histBuf[histPos]); - update = TRUE; - } - break; - - case 0x204: /* ctrl+left arrow = Skip words left */ - case 0x20b: - while (editBuf->pos > 0 && isspace((int) editBuf->data[editBuf->pos - 1])) - editBuf->pos--; - while (editBuf->pos > 0 && !isspace((int) editBuf->data[editBuf->pos - 1])) - editBuf->pos--; - update = TRUE; - break; - - case 0x206: /* ctrl+right arrow = Skip words right */ - case 0x210: - while (editBuf->pos < editBuf->len && isspace((int) editBuf->data[editBuf->pos])) - editBuf->pos++; - while (editBuf->pos < editBuf->len && !isspace((int) editBuf->data[editBuf->pos])) - editBuf->pos++; - update = TRUE; - break; - - case KEY_HOME: - nn_editbuf_setpos(editBuf, 0); - update = TRUE; - break; - case KEY_END: - nn_editbuf_setpos(editBuf, editBuf->len); - update = TRUE; - break; - case KEY_LEFT: - nn_editbuf_setpos(editBuf, editBuf->pos - 1); - update = TRUE; - break; - case KEY_RIGHT: - nn_editbuf_setpos(editBuf, editBuf->pos + 1); - update = TRUE; - break; - - case KEY_BACKSPACE: - case 0x08: - case 0x7f: - nn_editbuf_delete(editBuf, editBuf->pos - 1); - nn_editbuf_setpos(editBuf, editBuf->pos - 1); - update = TRUE; - break; - - case KEY_DC: /* Delete character */ - nn_editbuf_delete(editBuf, editBuf->pos); - update = TRUE; - break; - - - case KEY_IC: /* Ins = Toggle insert / overwrite mode */ - insertMode = !insertMode; - update = TRUE; - break; - - case KEY_F(1): /* F1 = Print help */ - printHelp(); - updateMain = TRUE; - break; - - case KEY_F(2): /* F2 = Clear editbuffer */ - nn_editbuf_clear(editBuf); - update = TRUE; - break; - - case KEY_F(5): /* F5 = Ignore mode */ - setIgnoreMode = !setIgnoreMode; - printMsgQ(currWin, "Ignore mode = %s\n", setIgnoreMode ? "ON" : "OFF"); - break; - -#if 0 - case KEY_F(8): /* F8 = Debug */ - optDebug = !optDebug; - update = TRUE; - break; -#endif - - case 0x03: /* ^C = quit */ - case KEY_F(9): /* F9 = Quit */ - printMsg(currWin, "Quitting per user request (%d/0x%x).\n", c, c); - exitProg = TRUE; - break; - - case 0x09: /* Tab = complete username */ - performTabCompletion(editBuf); - update = TRUE; - break; - - case 0x0c: /* Ctrl + L */ - updateWindows(); - update = updateMain = TRUE; - break; - - case KEY_NPAGE: - case KEY_PPAGE: - /* Page Up / Page Down */ - if (currWin != NULL) - { - int oldPos = currWin->pos; - - currWin->pos += (c == KEY_NPAGE) ? -10 : +10; - - if (currWin->pos < 0) - currWin->pos = 0; - else if (currWin->pos >= currWin->data->n - 10) - currWin->pos = currWin->data->n - 10; - - if (oldPos != currWin->pos) - updateMain = TRUE; - } - break; - - case ERR: - /* Ignore */ - break; - - default: - if (isprint(c) || c == 0xe4 || c == 0xf6 || c == 0xc4 || c == 0xd6) - { - if (insertMode) - nn_editbuf_insert(editBuf, editBuf->pos, c); - else - nn_editbuf_write(editBuf, editBuf->pos, c); - nn_editbuf_setpos(editBuf, editBuf->pos + 1); - update = TRUE; - } - else - { - if (optDebug) - printMsg(currWin, "Unhandled key: 0x%02x\n", c); - } - break; - } - } - while (c != ERR && !exitProg && ++cnt < 10); - - updateMainWin(updateMain); - - if (update || firstUpdate) - { - /* Update edit line */ - updateStatus(); - printEditBuf(editBuf); - firstUpdate = FALSE; /* a nasty hack ... */ - } - - } /* cursesInit */ - - if (++updateCount > 10) - { - time_t tmpTime = time(NULL); - if (tmpTime - prevTime > SET_KEEPALIVE) - { - int n = random() % th_llist_length(setIdleMessages); - qlist_t *node = th_llist_get_nth(setIdleMessages, n); - nn_conn_send_msg(conn, optUserNameEnc, node->data); - prevTime = tmpTime; - } - - if (!colorSet) - { - colorSet = TRUE; - nn_conn_send_msg(conn, optUserNameEnc, "%%2FSetFontColor%%20%%2Dcolor%%20%06X", optUserColor); - } - - updateStatus(); - printEditBuf(editBuf); - updateCount = 0; - } - - } - - /* Shutdown */ -err_exit: - th_cfg_free(cfg); - th_free(homeDir); - th_llist_free_func(setIdleMessages, th_free); - nn_userhash_free(nnUsers); - nn_editbuf_free(editBuf); - - { - int i; - for (i = 0; i <= SET_MAX_HISTORY; i++) - nn_editbuf_free(histBuf[i]); - - for (i = 0; i < SET_MAX_WINDOWS; i++) - nn_window_free(chatWindows[i]); - } - -#ifdef __WIN32 - if (errorMessages) - { - char *tmp; - wclear(editWin); - tmp = promptRequester(editWin, "Press enter to quit.\n", FALSE); - th_free(tmp); - } -#endif - - if (cursesInit) - { - if (curVis != ERR) - curs_set(curVis); - closeWindows(); - endwin(); - THMSG(1, "NCurses deinitialized.\n"); - } - -#ifndef __WIN32 - if (errorMessages) - THERR("%s", errorMessages); -#endif - - th_free(optUserNameEnc); - - nn_conn_close(conn); - - if (networkInit) - nn_network_close(); - - THMSG(1, "Connection terminated.\n"); - - logFileClose(); - - return 0; -} diff -r 3e64acb433e8 -r 14b685cdbd2c util.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/util.c Thu May 24 06:41:07 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 "util.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); +}