Major rework to use cbchains on sockets.
[doldaconnect.git] / daemon / fnet-dc.c
index 350255e..804bd2c 100644 (file)
@@ -44,7 +44,7 @@
 #include "transfer.h"
 #include "sysevents.h"
 #include "net.h"
-#include "tiger.h"
+#include <tiger.h>
 
 /*
  * The Direct Connect protocol is extremely ugly. Thus, this code must
@@ -107,6 +107,7 @@ struct dchub
     size_t inbufdata, inbufsize;
     struct qcommand *queue;
     int extended, dcppemu;
+    char *charset;
     char *nativename;
     char *nativenick;
 };
@@ -139,6 +140,7 @@ struct dcpeer
     int compress;
     int hascurpos, fetchingtthl, notthl;
     struct tigertreehash tth;
+    char *charset;
     void *cprsdata;
     char *key;
     char *nativename;
@@ -156,16 +158,20 @@ static struct dcexppeer *expected = NULL;
 static char *hmlistname = NULL;
 static char *xmllistname = NULL;
 static char *xmlbz2listname = NULL;
+static struct timer *listwritetimer = NULL;
 
-static void peerconnect(struct socket *sk, int err, struct fnetnode *fn);
+static int peerconnect(struct socket *sk, int err, struct fnetnode *fn);
+static int peerread(struct socket *sk, struct dcpeer *peer);
+static int peererror(struct socket *sk, int err, struct dcpeer *peer);
 static void freedcpeer(struct dcpeer *peer);
-static void transread(struct socket *sk, struct dcpeer *peer);
-static void transerr(struct socket *sk, int err, struct dcpeer *peer);
-static void transwrite(struct socket *sk, struct dcpeer *peer);
+static void transread(struct dcpeer *peer);
+static void transerr(struct dcpeer *peer);
+static int transwrite(struct socket *sk, struct dcpeer *peer);
 static void updatehmlist(void);
 static void updatexmllist(void);
 static void updatexmlbz2list(void);
 static void requestfile(struct dcpeer *peer);
+static void updatelists(int now);
 
 static int reservedchar(unsigned char c)
 {
@@ -388,7 +394,9 @@ static void hubrecvchat(struct socket *sk, struct fnetnode *fn, char *from, char
     wchar_t *chat, *wfrom, *wpeer;
     char *p, *end;
     struct fnetpeer *peer;
+    struct dchub *hub;
     
+    hub = fn->data;
     end = string + strlen(string);
     while((p = strchr(string, 13)) != NULL)
        memmove(p, p + 1, (end-- - p));
@@ -396,7 +404,7 @@ static void hubrecvchat(struct socket *sk, struct fnetnode *fn, char *from, char
     {
        if((strlen(string) > strlen(from) + 2) && (*string == '<') && !memcmp(string + 1, from, strlen(from)) && (*(string + strlen(from) + 1) == '>'))
            string += strlen(from) + 2;
-       if((wfrom = icmbstowcs(from, DCCHARSET)) == NULL)
+       if((wfrom = icmbstowcs(from, hub->charset)) == NULL)
            return;
        wpeer = swcsdup(wfrom);
     } else {
@@ -414,7 +422,7 @@ static void hubrecvchat(struct socket *sk, struct fnetnode *fn, char *from, char
                *(p++) = 0;
                if(*p == ' ')
                    p++;
-               if((wpeer = icmbstowcs(string + 1, DCCHARSET)) == NULL)
+               if((wpeer = icmbstowcs(string + 1, hub->charset)) == NULL)
                    return;
                string = p;
            }
@@ -422,7 +430,7 @@ static void hubrecvchat(struct socket *sk, struct fnetnode *fn, char *from, char
        if(wpeer == NULL)
            wpeer = swcsdup(L"");
     }
-    if((chat = icmbstowcs(string, DCCHARSET)) == NULL)
+    if((chat = icmbstowcs(string, hub->charset)) == NULL)
     {
        if(wfrom != NULL)
            free(wfrom);
@@ -586,6 +594,7 @@ static char *getadcid(struct dcpeer *peer)
     char *ret;
     int isfilelist;
     
+    isfilelist = 0;
     if(!wcscmp(peer->transfer->path, L"files.xml") || !wcscmp(peer->transfer->path, L"files.xml.bz2") || !wcscmp(peer->transfer->path, L"MyList.DcLst"))
        isfilelist = 1;
     if(!isfilelist && (peer->transfer->hash != NULL) && isdchash(peer->transfer->hash) && supports(peer, "tthf"))
@@ -623,13 +632,13 @@ static int trresumecb(struct transfer *transfer, wchar_t *cmd, wchar_t *arg, str
 
 static void sendmynick(struct dcpeer *peer)
 {
-    struct dchub *hub;
+    struct fnetnode *fn;
     
-    hub = (peer->fn == NULL)?NULL:(peer->fn->data);
-    if(hub == NULL)
-       qstrf(peer->sk, "$MyNick %s|", icswcstombs(confgetstr("cli", "defnick"), DCCHARSET, "DoldaConnectUser-IN"));
+    fn = peer->fn;
+    if(fn == NULL)
+       qstrf(peer->sk, "$MyNick %s|", icswcstombs(confgetstr("cli", "defnick"), peer->charset, "DoldaConnectUser-IN"));
     else
-       qstrf(peer->sk, "$MyNick %s|", hub->nativenick);
+       qstrf(peer->sk, "$MyNick %s|", icswcstombs(fn->mynick, peer->charset, "DoldaConnectUser-IN"));
 }
 
 static void sendpeerlock(struct dcpeer *peer)
@@ -654,6 +663,7 @@ static void requestfile(struct dcpeer *peer)
     
     if(peer->transfer->size == -1)
     {
+       /* Use DCCHARSET for $Get paths until further researched... */
        if((buf = icswcstombs(peer->transfer->path, DCCHARSET, NULL)) == NULL)
        {
            transferseterror(peer->transfer, TRNSE_NOTFOUND);
@@ -722,6 +732,7 @@ static void requestfile(struct dcpeer *peer)
        }
        qstrf(peer->sk, "$UGetBlock %i %i %s|", peer->transfer->curpos, peer->transfer->size - peer->transfer->curpos, buf);
     } else {
+       /* Use DCCHARSET for $Get paths until further researched... */
        if((buf = icswcstombs(peer->transfer->path, DCCHARSET, NULL)) == NULL)
        {
            transferseterror(peer->transfer, TRNSE_NOTFOUND);
@@ -741,7 +752,7 @@ static void sendmyinfo(struct socket *sk, struct fnetnode *fn)
     
     hub = fn->data;
     qstrf(sk, "$MyINFO $ALL %s ", hub->nativenick);
-    buf = tr(icswcstombs(confgetstr("dc", "desc"), DCCHARSET, "Charset_conv_failure"), "$_|_");
+    buf = tr(icswcstombs(confgetstr("dc", "desc"), hub->charset, "Charset_conv_failure"), "$_|_");
     qstrf(sk, "%s", buf);
     hn1 = hn2 = hn3 = 0;
     for(cfn = fnetnodes; cfn != NULL; cfn = cfn->next)
@@ -764,9 +775,9 @@ static void sendmyinfo(struct socket *sk, struct fnetnode *fn)
          confgetint("transfer", "slots")
          );
     qstrf(sk, "$ $");
-    buf = tr(icswcstombs(confgetstr("dc", "speedstring"), DCCHARSET, "Charset_conv_failure"), "$_|_");
+    buf = tr(icswcstombs(confgetstr("dc", "speedstring"), hub->charset, "Charset_conv_failure"), "$_|_");
     qstrf(sk, "%s\x01$", buf);
-    buf = tr(icswcstombs(confgetstr("dc", "email"), DCCHARSET, "Charset_conv_failure"), "$_|_");
+    buf = tr(icswcstombs(confgetstr("dc", "email"), hub->charset, "Charset_conv_failure"), "$_|_");
     qstrf(sk, "%s$", buf);
     qstrf(sk, "%llu$|", sharesize);
 }
@@ -825,7 +836,7 @@ static void cmd_hubname(struct socket *sk, struct fnetnode *fn, char *cmd, char
     if(hub->nativename == NULL)
        free(hub->nativename);
     hub->nativename = sstrdup(args);
-    buf = icmbstowcs(args, DCCHARSET);
+    buf = icmbstowcs(args, hub->charset);
     fnetsetname(fn, (buf == NULL)?L"Hubname conv error":buf);
     if(buf != NULL)
        free(buf);
@@ -838,7 +849,7 @@ static void cmd_hello(struct socket *sk, struct fnetnode *fn, char *cmd, char *a
     struct dchub *hub;
     
     hub = fn->data;
-    if((nick = icmbstowcs(args, DCCHARSET)) == NULL)
+    if((nick = icmbstowcs(args, hub->charset)) == NULL)
        return;
     if(strcmp(args, hub->nativenick) && (fnetfindpeer(fn, nick) == NULL))
        fnetaddpeer(fn, nick, nick);
@@ -853,7 +864,7 @@ static void cmd_quit(struct socket *sk, struct fnetnode *fn, char *cmd, char *ar
     struct dchub *hub;
     
     hub = fn->data;
-    if((nick = icmbstowcs(args, DCCHARSET)) == NULL)
+    if((nick = icmbstowcs(args, hub->charset)) == NULL)
        return;
     if((peer = fnetfindpeer(fn, nick)) != NULL)
        fnetdelpeer(peer);
@@ -874,7 +885,7 @@ static void cmd_nicklist(struct socket *sk, struct fnetnode *fn, char *cmd, char
     while((p = strstr(args, "$$")) != NULL)
     {
        *p = 0;
-       if((buf = icmbstowcs(args, DCCHARSET)) != NULL)
+       if((buf = icmbstowcs(args, hub->charset)) != NULL)
        {
            if((peer = fnetfindpeer(fn, buf)) == NULL)
                peer = fnetaddpeer(fn, buf, buf);
@@ -907,7 +918,7 @@ static void cmd_oplist(struct socket *sk, struct fnetnode *fn, char *cmd, char *
     while((p = strstr(args, "$$")) != NULL)
     {
        *p = 0;
-       if((buf = icmbstowcs(args, DCCHARSET)) != NULL)
+       if((buf = icmbstowcs(args, hub->charset)) != NULL)
        {
            if((peer = fnetfindpeer(fn, buf)) != NULL)
                peer->flags.b.op = 1;
@@ -934,7 +945,7 @@ static void cmd_myinfo(struct socket *sk, struct fnetnode *fn, char *cmd, char *
     if((p2 = strchr(p, ' ')) == NULL)
        return;
     *p2 = 0;
-    if((buf = icmbstowcs(p, DCCHARSET)) == NULL)
+    if((buf = icmbstowcs(p, hub->charset)) == NULL)
        return;
     if((peer = fnetfindpeer(fn, buf)) == NULL)
        peer = fnetaddpeer(fn, buf, buf);
@@ -943,9 +954,9 @@ static void cmd_myinfo(struct socket *sk, struct fnetnode *fn, char *cmd, char *
     if((p2 = strstr(p, "$ $")) == NULL)
        return;
     *p2 = 0;
-    if((buf = icmbstowcs(p, DCCHARSET)) == NULL)
+    if((buf = icmbstowcs(p, hub->charset)) == NULL)
        return;
-    if((wcslen(buf) > 0) && (buf[wcslen(buf) - 1] == L'>') && ((wp = wcschr(buf, L'<')) != NULL))
+    if((wcslen(buf) > 0) && (buf[wcslen(buf) - 1] == L'>') && ((wp = wcsrchr(buf, L'<')) != NULL))
     {
        buf[wcslen(buf) - 1] = L'\0';
        *(wp++) = L'\0';
@@ -972,7 +983,7 @@ static void cmd_myinfo(struct socket *sk, struct fnetnode *fn, char *cmd, char *
     if((p2 = strchr(p, '$')) == NULL)
        return;
     *(p2 - 1) = 0;
-    if((buf = icmbstowcs(p, DCCHARSET)) == NULL)
+    if((buf = icmbstowcs(p, hub->charset)) == NULL)
        return;
     fnetpeersetstr(peer, L"dc-speed", buf);
     free(buf);
@@ -980,7 +991,7 @@ static void cmd_myinfo(struct socket *sk, struct fnetnode *fn, char *cmd, char *
     if((p2 = strchr(p, '$')) == NULL)
        return;
     *p2 = 0;
-    if((buf = icmbstowcs(p, DCCHARSET)) == NULL)
+    if((buf = icmbstowcs(p, hub->charset)) == NULL)
        return;
     fnetpeersetstr(peer, L"email", buf);
     free(buf);
@@ -1010,7 +1021,7 @@ static void cmd_forcemove(struct socket *sk, struct fnetnode *fn, char *cmd, cha
     } else {
        freeargs = 0;
     }
-    if((newfn = fnetinitconnect(L"dc", args, NULL)) != NULL)
+    if((newfn = fnetinitconnect(L"dc", fn->owner, args, NULL)) != NULL)
     {
        linkfnetnode(newfn);
        putfnetnode(newfn);
@@ -1020,7 +1031,7 @@ static void cmd_forcemove(struct socket *sk, struct fnetnode *fn, char *cmd, cha
        free(args);
 }
 
-static char *getdcpath(struct sharecache *node, size_t *retlen)
+static char *getdcpath(struct sharecache *node, size_t *retlen, char *charset)
 {
     char *buf, *buf2;
     size_t len, len2;
@@ -1029,15 +1040,15 @@ static char *getdcpath(struct sharecache *node, size_t *retlen)
        return(NULL);
     if(node->parent == shareroot)
     {
-       if((buf = icwcstombs(node->name, DCCHARSET)) == NULL)
+       if((buf = icwcstombs(node->name, charset)) == NULL)
            return(NULL);
        if(retlen != NULL)
            *retlen = strlen(buf);
        return(buf);
     } else {
-       if((buf2 = icwcstombs(node->name, DCCHARSET)) == NULL)
+       if((buf2 = icwcstombs(node->name, charset)) == NULL)
            return(NULL);
-       if((buf = getdcpath(node->parent, &len)) == NULL)
+       if((buf = getdcpath(node->parent, &len, charset)) == NULL)
        {
            free(buf2);
            return(NULL);
@@ -1076,7 +1087,8 @@ static void cmd_search(struct socket *sk, struct fnetnode *fn, char *cmd, char *
     struct sockaddr_in addr;
     struct sharecache *node;
     int minsize, maxsize;
-    int dotth, buflen;
+    int dotth;
+    size_t buflen;
     int termnum, satisfied, skipcheck;
     int level, tersat[32];
     wchar_t *terms[32];
@@ -1159,14 +1171,15 @@ static void cmd_search(struct socket *sk, struct fnetnode *fn, char *cmd, char *
                if(!dotth && !strncmp(p, "TTH:", 4))
                {
                    dotth = 1;
-                   if((buf = base32decode(p + 4, &buflen)) == NULL)
-                       goto out;
-                   if(buflen != 24)
+                   if(((buf = base32decode(p + 4, &buflen)) == NULL) || (buflen != 24))
+                   {
+                       free(buf);
                        goto out;
+                   }
                    memcpy(hashtth, buf, 24);
                    free(buf);
                } else {
-                   if((terms[termnum] = icmbstowcs(p, DCCHARSET)) != NULL)
+                   if((terms[termnum] = icmbstowcs(p, hub->charset)) != NULL)
                        termnum++;
                }
            }
@@ -1214,7 +1227,8 @@ static void cmd_search(struct socket *sk, struct fnetnode *fn, char *cmd, char *
        }
        if(!skipcheck && (satisfied == termnum))
        {
-           if((buf = getdcpath(node, NULL)) != NULL)
+           /* Use DCCHARSET in $Get paths until further researched... */
+           if((buf = getdcpath(node, NULL, DCCHARSET)) != NULL)
            {
                if(node->f.b.hastth)
                {
@@ -1288,7 +1302,7 @@ static void cmd_connecttome(struct socket *sk, struct fnetnode *fn, char *cmd, c
     addr.sin_port = htons(atoi(p));
     if(!inet_aton(args, &addr.sin_addr))
        return;
-    newsk = netcsconn((struct sockaddr *)&addr, sizeof(addr), (void (*)(struct socket *, int, void *))peerconnect, fn);
+    newsk = netcsconn((struct sockaddr *)&addr, sizeof(addr), (int (*)(struct socket *, int, void *))peerconnect, fn);
     getfnetnode(fn);
     hubhandleaction(sk, fn, cmd, args);
 }
@@ -1300,7 +1314,7 @@ static void sendctm(struct socket *sk, char *nick)
     
     if(tcpsock == NULL)
        return;
-    if(sockgetremotename(tcpsock, &addr, &addrlen) < 0)
+    if(sockgetremotename2(tcpsock, sk, &addr, &addrlen) < 0)
        return;
     if(addr->sa_family == AF_INET)
        qstrf(sk, "$ConnectToMe %s %s|", nick, formataddress(addr, addrlen));
@@ -1405,8 +1419,9 @@ static void cmd_sr(struct socket *sk, struct fnetnode *fn, char *cmd, char *args
     if((p2 = strstr(p, " (")) == NULL)
        return;
     *p2 = 0;
-    if((wnick = icmbstowcs(nick, DCCHARSET)) == NULL)
+    if((wnick = icmbstowcs(nick, hub->charset)) == NULL)
        return;
+    /* Use DCCHARSET in $Get paths until further researched... */
     if((wfile = icmbstowcs(filename, DCCHARSET)) == NULL)
     {
        free(wnick);
@@ -1448,7 +1463,7 @@ static void cmd_getpass(struct socket *sk, struct fnetnode *fn, char *cmd, char
     
     hub = fn->data;
     pw = wpfind(fn->args, L"password");
-    if((pw == NULL) || ((mbspw = icwcstombs(pw, DCCHARSET)) == NULL))
+    if((pw == NULL) || ((mbspw = icwcstombs(pw, hub->charset)) == NULL))
     {
        killfnetnode(fn);
        return;
@@ -1478,7 +1493,7 @@ static void cmd_mynick(struct socket *sk, struct dcpeer *peer, char *cmd, char *
     peer->nativename = sstrdup(args);
     if(peer->wcsname != NULL)
        free(peer->wcsname);
-    if((peer->wcsname = icmbstowcs(peer->nativename, DCCHARSET)) == NULL)
+    if((peer->wcsname = icmbstowcs(peer->nativename, peer->charset)) == NULL)
     {
        freedcpeer(peer);
        return;
@@ -1534,6 +1549,11 @@ static void cmd_direction(struct socket *sk, struct dcpeer *peer, char *cmd, cha
        peer->direction = mydir;
        if(peer->direction == TRNSD_UP)
        {
+           if(confgetint("transfer", "ulquota") && hasupload(&dcnet, peer->wcsname))
+           {
+               freedcpeer(peer);
+               return;
+           }
            transfer = newupload(peer->fn, &dcnet, peer->wcsname, &dctransfer, peer);
        } else {
            if((transfer = finddownload(peer->wcsname)) == NULL)
@@ -1580,6 +1600,11 @@ static void cmd_peerlock(struct socket *sk, struct dcpeer *peer, char *cmd, char
            sendsupports(peer);
        if((transfer = finddownload(peer->wcsname)) == NULL)
        {
+           if(confgetint("transfer", "ulquota") && hasupload(&dcnet, peer->wcsname))
+           {
+               freedcpeer(peer);
+               return;
+           }
            peer->direction = TRNSD_UP;
            transfer = newupload(peer->fn, &dcnet, peer->wcsname, &dctransfer, peer);
        } else {
@@ -1591,6 +1616,7 @@ static void cmd_peerlock(struct socket *sk, struct dcpeer *peer, char *cmd, char
        peer->transfer = transfer;
        qstrf(sk, "$Direction %s %i|", (peer->direction == TRNSD_UP)?"Upload":"Download", rand() % 10000);
        qstrf(sk, "$Key %s|", key);
+       free(key);
     } else {
        if(peer->key != NULL)
            free(peer->key);
@@ -1609,8 +1635,6 @@ static void startdl(struct dcpeer *peer)
        canceltimer(peer->timeout);
     peer->state = PEER_TRNS;
     transferstartdl(peer->transfer, peer->sk);
-    peer->sk->readcb = (void (*)(struct socket *, void *))transread;
-    peer->sk->errcb = (void (*)(struct socket *, int, void *))transerr;
 }
 
 static void startul(struct dcpeer *peer)
@@ -1619,7 +1643,7 @@ static void startul(struct dcpeer *peer)
        canceltimer(peer->timeout);
     peer->state = PEER_TRNS;
     transferstartul(peer->transfer, peer->sk);
-    peer->sk->writecb = (void (*)(struct socket *, void *))transwrite;
+    CBREG(peer->sk, socket_write, (int (*)(struct socket *, void *))transwrite, NULL, peer);
 }
 
 static void cmd_filelength(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
@@ -1791,6 +1815,7 @@ static void cmd_get(struct socket *sk, struct dcpeer *peer, char *cmd, char *arg
     }
     if(fd < 0)
     {
+       /* Use DCCHARSET in $Get paths until further researched... */
        if((node = resdcpath(args, DCCHARSET, '\\')) == NULL)
        {
            qstrf(sk, "$Error File not in share|");
@@ -1919,6 +1944,7 @@ static void cmd_getblock(struct socket *sk, struct dcpeer *peer, char *cmd, char
     if(!strcmp(cmd, "$UGetBlock") || !strcmp(cmd, "$UGetZBlock"))
        charset = "UTF-8";
     else
+       /* Use DCCHARSET in $Get paths until further researched... */
        charset = DCCHARSET;
     if(!strcmp(cmd, "$GetZBlock") || !strcmp(cmd, "$UGetZBlock"))
        initcompress(peer, CPRS_ZLIB);
@@ -2189,12 +2215,7 @@ static void cmd_adcsnd(struct socket *sk, struct dcpeer *peer, char *cmd, char *
            goto out;
        }
        startdl(peer);
-       if(peer->inbufdata > 0)
-       {
-           sockpushdata(sk, peer->inbuf, peer->inbufdata);
-           peer->inbufdata = 0;
-           transread(sk, peer);
-       }
+       transread(peer);
     } else {
        /* We certainly didn't request this...*/
        freedcpeer(peer);
@@ -2222,12 +2243,7 @@ static void cmd_sending(struct socket *sk, struct dcpeer *peer, char *cmd, char
        return;
     }
     startdl(peer);
-    if(peer->inbufdata > 0)
-    {
-       sockpushdata(sk, peer->inbuf, peer->inbufdata);
-       peer->inbufdata = 0;
-       transread(sk, peer);
-    }
+    transread(peer);
 }
 
 /*
@@ -2262,7 +2278,7 @@ static int hubreqconn(struct fnetpeer *peer)
        errno = EFAULT;
        return(1);
     }
-    if((mbsnick = icwcstombs(peer->id, DCCHARSET)) == NULL)
+    if((mbsnick = icwcstombs(peer->id, hub->charset)) == NULL)
        return(1); /* Shouldn't happen, of course, but who knows... */
     if(tcpsock != NULL)
     {
@@ -2281,12 +2297,12 @@ static int hubsendchat(struct fnetnode *fn, int public, wchar_t *to, wchar_t *st
     char *mbsstring, *mbsto;
     
     hub = fn->data;
-    if((mbsto = icwcstombs(to, DCCHARSET)) == NULL)
+    if((mbsto = icwcstombs(to, hub->charset)) == NULL)
     {
        errno = EILSEQ;
        return(1);
     }
-    if((mbsstring = icwcstombs(string, DCCHARSET)) == NULL)
+    if((mbsstring = icwcstombs(string, hub->charset)) == NULL)
     {
        errno = EILSEQ;
        return(1);
@@ -2401,8 +2417,10 @@ static struct hash *findsehash(struct sexpr *sexpr)
            return(h1);
        break;
     case SOP_OR:
-       h1 = findsehash(sexpr->l);
-       h2 = findsehash(sexpr->r);
+       if((h1 = findsehash(sexpr->l)) == NULL)
+           return(NULL);
+       if((h2 = findsehash(sexpr->r)) == NULL)
+           return(NULL);
        if(hashcmp(h1, h2))
            return(h1);
        break;
@@ -2456,10 +2474,10 @@ static int hubsearch(struct fnetnode *fn, struct search *srch, struct srchfnnlis
        if(minsize != 0)
        {
            sizebuf2(sstr, sstrdata + 32, 1);
-           snprintf(sstr + sstrdata, sstrsize - sstrdata, "T?F?%i?1?", minsize);
+           sstrdata += snprintf(sstr + sstrdata, sstrsize - sstrdata, "T?F?%i?1?", minsize);
        } else if(maxsize != -1) {
            sizebuf2(sstr, sstrdata + 32, 1);
-           snprintf(sstr + sstrdata, sstrsize - sstrdata, "T?T?%i?1?", maxsize);
+           sstrdata += snprintf(sstr + sstrdata, sstrsize - sstrdata, "T?T?%i?1?", maxsize);
        } else {
            bufcat(sstr, "F?F?0?1?", 8);
        }
@@ -2467,6 +2485,7 @@ static int hubsearch(struct fnetnode *fn, struct search *srch, struct srchfnnlis
        {
            for(cur = list; cur != NULL; cur = cur->next)
            {
+               /* Use DCCHARSET in $Get paths until further researched... */
                if((buf = icwcstombs(cur->str, DCCHARSET)) == NULL)
                {
                    /* Can't find anything anyway if the search expression
@@ -2504,7 +2523,7 @@ static int hubsearch(struct fnetnode *fn, struct search *srch, struct srchfnnlis
     addtobuf(sstr, 0);
     if(tcpsock != NULL)
     {
-       if(sockgetremotename(udpsock, &name, &namelen) < 0)
+       if(sockgetremotename2(udpsock, fn->sk, &name, &namelen) < 0)
        {
            flog(LOG_WARNING, "cannot get address of UDP socket");
        } else {
@@ -2525,7 +2544,7 @@ static int hubsearch(struct fnetnode *fn, struct search *srch, struct srchfnnlis
 #undef qstrf
 
 #define cc(c) ((void (*)(struct socket *, void *, char *, char *))(c))
-struct command hubcmds[] =
+static struct command hubcmds[] =
 {
     {"$Lock", cc(cmd_lock)},
     {"$HubName", cc(cmd_hubname)},
@@ -2548,7 +2567,7 @@ struct command hubcmds[] =
     {NULL, NULL}
 };
 
-struct command peercmds[] =
+static struct command peercmds[] =
 {
     {"$MyNick", cc(cmd_mynick)},
     {"$Lock", cc(cmd_peerlock)},
@@ -2573,7 +2592,7 @@ struct command peercmds[] =
 
 static void dctransdetach(struct transfer *transfer, struct dcpeer *peer)
 {
-    CBUNREG(transfer, trans_filterout, peer);
+    CBUNREG(transfer, trans_filterout, trresumecb, peer);
     if(peer->freeing)
        return;
     peer->transfer = NULL;
@@ -2584,7 +2603,7 @@ static void dctransgotdata(struct transfer *transfer, struct dcpeer *peer)
 {
     int ret;
     void *buf;
-    char outbuf[1024];
+    unsigned char outbuf[1024];
     z_stream *cstr;
     size_t bufsize;
     
@@ -2648,7 +2667,7 @@ static void dctransgotdata(struct transfer *transfer, struct dcpeer *peer)
                    transfersetstate(transfer, TRNS_HS);
                    socksettos(peer->sk, confgetint("fnet", "fnptos"));
                    transfer->flags.b.minislot = 0;
-                   peer->sk->writecb = NULL;
+                   CBUNREG(peer->sk, socket_write, transwrite, peer);
                }
            }
        }
@@ -2667,22 +2686,22 @@ static void dcwantdata(struct transfer *transfer, struct dcpeer *peer)
        peer->sk->ignread = 0;
 }
 
-static void transread(struct socket *sk, struct dcpeer *peer)
+static void transread(struct dcpeer *peer)
 {
-    void *buf;
-    size_t bufsize;
+    size_t num;
     struct transfer *transfer;
     
-    if((buf = sockgetinbuf(sk, &bufsize)) == NULL)
-       return;
     if(peer->transfer == NULL)
     {
-       free(buf);
        freedcpeer(peer);
        return;
     }
-    transferputdata(peer->transfer, buf, bufsize);
-    free(buf);
+    if(peer->inbufsize > peer->transfer->size - peer->transfer->curpos)
+       num = peer->transfer->size - peer->transfer->curpos;
+    else
+       num = peer->inbufsize;
+    transferputdata(peer->transfer, peer->inbuf, num);
+    memmove(peer->inbuf, peer->inbuf + num, peer->inbufsize -= num);
     if(peer->transfer->curpos >= peer->transfer->size)
     {
        transfer = peer->transfer;
@@ -2691,39 +2710,38 @@ static void transread(struct socket *sk, struct dcpeer *peer)
        return;
     }
     if(transferdatasize(peer->transfer) > 65535)
-       sk->ignread = 1;
+       peer->sk->ignread = 1;
 }
 
-static void transerr(struct socket *sk, int err, struct dcpeer *peer)
+static void transerr(struct dcpeer *peer)
 {
     struct transfer *transfer;
 
     if((transfer = peer->transfer) == NULL)
-    {
-       freedcpeer(peer);
        return;
-    }
     transferdetach(transfer);
     transferendofdata(transfer);
 }
 
-static void transwrite(struct socket *sk, struct dcpeer *peer)
+static int transwrite(struct socket *sk, struct dcpeer *peer)
 {
     if((peer->state != PEER_TRNS) && (peer->state != PEER_SYNC))
-       return;
+       return(1);
     if(peer->transfer == NULL)
     {
        freedcpeer(peer);
-       return;
+       return(1);
     }
     dctransgotdata(peer->transfer, peer);
+    return(0);
 }
 
-static void udpread(struct socket *sk, void *data)
+static int udpread(struct socket *sk, void *data)
 {
     char *buf, *p, *p2, *hashbuf;
     size_t buflen, hashlen;
     char *nick, *filename, *hubname;
+    struct sockaddr_in hubaddr;
     int size, slots;
     struct fnetnode *fn, *myfn;
     struct dchub *hub;
@@ -2732,7 +2750,7 @@ static void udpread(struct socket *sk, void *data)
     struct hash *hash;
     
     if((buf = sockgetinbuf(sk, &buflen)) == NULL)
-       return;
+       return(0);
     buf = srealloc(buf, buflen + 1);
     buf[buflen] = 0;
     if(!strncmp(buf, "$SR ", 4))
@@ -2742,7 +2760,7 @@ static void udpread(struct socket *sk, void *data)
        if((p2 = strchr(p, ' ')) == NULL)
        {
            free(buf);
-           return;
+           return(0);
        }
        *p2 = 0;
        p = p2 + 1;
@@ -2750,14 +2768,14 @@ static void udpread(struct socket *sk, void *data)
        if((p2 = strchr(p, 5)) == NULL)
        {
            free(buf);
-           return;
+           return(0);
        }
        *p2 = 0;
        p = p2 + 1;
        if((p2 = strchr(p, ' ')) == NULL)
        {
            free(buf);
-           return;
+           return(0);
        }
        *p2 = 0;
        size = atoi(p);
@@ -2765,7 +2783,7 @@ static void udpread(struct socket *sk, void *data)
        if((p2 = strchr(p, '/')) == NULL)
        {
            free(buf);
-           return;
+           return(0);
        }
        *p2 = 0;
        slots = atoi(p);
@@ -2773,26 +2791,42 @@ static void udpread(struct socket *sk, void *data)
        if((p2 = strchr(p, 5)) == NULL)
        {
            free(buf);
-           return;
+           return(0);
        }
        p = p2 + 1;
        hubname = p;
        if((p2 = strstr(p, " (")) == NULL)
        {
            free(buf);
-           return;
+           return(0);
        }
        *p2 = 0;
-       if((wnick = icmbstowcs(nick, DCCHARSET)) == NULL)
+       p = p2 + 2;
+       if((p2 = strchr(p, ':')) == NULL)
        {
            free(buf);
-           return;
+           return(0);
        }
+       *(p2++) = 0;
+       hubaddr.sin_family = AF_INET;
+       if(!inet_aton(p, &hubaddr.sin_addr))
+       {
+           free(buf);
+           return(0);
+       }
+       p = p2;
+       if((p2 = strchr(p, ')')) == NULL)
+       {
+           free(buf);
+           return(0);
+       }
+       *p2 = 0;
+       hubaddr.sin_port = htons(atoi(p));
+       /* Use DCCHARSET in $Get paths until further researched... */
        if((wfile = icmbstowcs(filename, DCCHARSET)) == NULL)
        {
-           free(wnick);
            free(buf);
-           return;
+           return(0);
        }
        myfn = NULL;
        hash = NULL;
@@ -2822,6 +2856,25 @@ static void udpread(struct socket *sk, void *data)
                }
            }
        }
+       if(myfn == NULL)
+       {
+           for(fn = fnetnodes; fn != NULL; fn = fn->next)
+           {
+               if((fn->fnet == &dcnet) && (fn->sk != NULL) && addreq(fn->sk->remote, (struct sockaddr *)&hubaddr))
+               {
+                   myfn = fn;
+                   break;
+               }
+           }
+       }
+       hub = NULL;
+       if(myfn != NULL)
+           hub = myfn->data;
+       if((wnick = icmbstowcs(nick, (hub == NULL)?DCCHARSET:(hub->charset))) == NULL)
+       {
+           free(buf);
+           return(0);
+       }
        sr = newsrchres(&dcnet, wfile, wnick);
        if(sr->peernick != NULL)
            free(sr->peernick);
@@ -2838,6 +2891,7 @@ static void udpread(struct socket *sk, void *data)
        freesrchres(sr);
     }
     free(buf);
+    return(0);
 }
 
 static void hubread(struct socket *sk, struct fnetnode *fn)
@@ -2878,7 +2932,7 @@ static int hubsetnick(struct fnetnode *fn, wchar_t *newnick)
     char *buf;
     
     hub = fn->data;
-    if((buf = icwcstombs(newnick, DCCHARSET)) == NULL)
+    if((buf = icwcstombs(newnick, (hub == NULL)?DCCHARSET:(hub->charset))) == NULL)
        return(1);
     if((strchr(buf, ' ') != NULL) || (strchr(buf, '|') != NULL) || (strchr(buf, '$') != NULL))
     {
@@ -2900,6 +2954,8 @@ static struct dchub *newdchub(struct fnetnode *fn)
 {
     struct dchub *new;
     wchar_t *emu;
+    wchar_t *wcharset;
+    char *charset;
     
     new = smalloc(sizeof(*new));
     memset(new, 0, sizeof(*new));
@@ -2913,6 +2969,22 @@ static struct dchub *newdchub(struct fnetnode *fn)
        if(*emu == L'n')
            new->dcppemu = 0;
     }
+    charset = NULL;
+    if((wcharset = wpfind(fn->args, L"charset")) != NULL)
+    {
+       if((charset = icwcstombs(wcharset, "US-ASCII")) != NULL)
+       {
+           if(!havecharset(charset))
+           {
+               free(charset);
+               charset = NULL;
+           }
+       }
+    }
+    if(charset != NULL)
+       new->charset = charset;
+    else
+       new->charset = sstrdup(DCCHARSET);
     if(hubsetnick(fn, fn->mynick))
        fnetsetnick(fn, L"DoldaConnectUser-IN");
     /* IN as in Invalid Nick */
@@ -2961,11 +3033,9 @@ static void freedcpeer(struct dcpeer *peer)
     }
     if(peer->timeout != NULL)
        canceltimer(peer->timeout);
-    if(peer->sk->data == peer)
-       peer->sk->data = NULL;
-    peer->sk->readcb = NULL;
-    peer->sk->writecb = NULL;
-    peer->sk->errcb = NULL;
+    /* XXX: Unregister transwrite from peer->sk->socket_write? */
+    CBUNREG(peer->sk, socket_read, peerread, peer);
+    CBUNREG(peer->sk, socket_err, peererror, peer);
     putsock(peer->sk);
     endcompress(peer);
     if(peer->supports != NULL)
@@ -2982,6 +3052,8 @@ static void freedcpeer(struct dcpeer *peer)
        free(peer->wcsname);
     if(peer->nativename != NULL)
        free(peer->nativename);
+    if(peer->charset != NULL)
+       free(peer->charset);
     if(peer->fn != NULL)
        putfnetnode(peer->fn);
     while((qcmd = ulqcmd(&peer->queue)) != NULL)
@@ -2992,11 +3064,9 @@ static void freedcpeer(struct dcpeer *peer)
 
 static void hubconnect(struct fnetnode *fn)
 {
-    fn->sk->readcb = (void (*)(struct socket *, void *))hubread;
-    fn->sk->errcb = (void (*)(struct socket *, int, void *))huberr;
-    getfnetnode(fn);
+    CBREG(fn->sk, socket_read, (int (*)(struct socket *, void *))hubread, (void (*)(void *))putfnetnode, getfnetnode(fn));
+    CBREG(fn->sk, socket_err, (int (*)(struct socket *, int, void *))huberr, (void (*)(void *))putfnetnode, getfnetnode(fn));
     fn->data = newdchub(fn);
-    fn->sk->data = fn;
     return;
 }
 
@@ -3008,11 +3078,8 @@ static void hubdestroy(struct fnetnode *fn)
     hub = (struct dchub *)fn->data;
     if(fn->sk != NULL)
     {
-       if(fn->sk->data == fn)
-       {
-           fn->sk->data = NULL;
-           putfnetnode(fn);
-       }
+       CBUNREG(fn->sk, socket_read, hubread, fn);
+       CBUNREG(fn->sk, socket_err, huberr, fn);
     }
     if(hub == NULL)
        return;
@@ -3022,6 +3089,8 @@ static void hubdestroy(struct fnetnode *fn)
        free(hub->nativename);
     if(hub->nativenick != NULL)
        free(hub->nativenick);
+    if(hub->charset != NULL)
+       free(hub->charset);
     if(hub->inbuf != NULL)
        free(hub->inbuf);
     free(hub);
@@ -3056,48 +3125,61 @@ static struct fnet dcnet =
     .filebasename = dcbasename
 };
 
-static void peerread(struct socket *sk, struct dcpeer *peer)
+static void cmdread(struct dcpeer *peer)
 {
-    char *newbuf, *p;
-    size_t datalen;
+    char *p;
     struct command *cmd;
 
+    p = peer->inbuf;
+    while((peer->inbufdata > 0) && (p = memchr(peer->inbuf, '|', peer->inbufdata)) != NULL)
+    {
+       *(p++) = 0;
+       newqcmd(&peer->queue, peer->inbuf);
+       for(cmd = peercmds; cmd->handler != NULL; cmd++)
+       {
+           if(!memcmp(peer->inbuf, cmd->name, strlen(cmd->name)) && ((peer->inbuf[strlen(cmd->name)] == ' ') || (peer->inbuf[strlen(cmd->name)] == '|')))
+               break;
+       }
+       memmove(peer->inbuf, p, peer->inbufdata -= p - peer->inbuf);
+       if(cmd->stop)
+       {
+           peer->state = PEER_STOP;
+           break;
+       }
+    }
+}
+
+static int peerread(struct socket *sk, struct dcpeer *peer)
+{
+    char *newbuf;
+    size_t datalen;
+
     if((newbuf = sockgetinbuf(sk, &datalen)) == NULL)
-       return;
+       return(0);
     sizebuf2(peer->inbuf, peer->inbufdata + datalen, 1);
     memcpy(peer->inbuf + peer->inbufdata, newbuf, datalen);
     free(newbuf);
     peer->inbufdata += datalen;
     if(peer->state == PEER_CMD)
     {
-       p = peer->inbuf;
-       while((peer->inbufdata > 0) && (p = memchr(peer->inbuf, '|', peer->inbufdata)) != NULL)
-       {
-           *(p++) = 0;
-           newqcmd(&peer->queue, peer->inbuf);
-           for(cmd = peercmds; cmd->handler != NULL; cmd++)
-           {
-               if(!memcmp(peer->inbuf, cmd->name, strlen(cmd->name)) && ((peer->inbuf[strlen(cmd->name)] == ' ') || (peer->inbuf[strlen(cmd->name)] == '|')))
-                   break;
-           }
-           memmove(peer->inbuf, p, peer->inbufdata -= p - peer->inbuf);
-           if(cmd->stop)
-           {
-               peer->state = PEER_STOP;
-               break;
-           }
-       }
+       cmdread(peer);
+    } else if(peer->state == PEER_TRNS) {
+       transread(peer);
     } else if(peer->state == PEER_TTHL) {
        handletthl(peer);
     }
+    return(0);
 }
 
-static void peererror(struct socket *sk, int err, struct dcpeer *peer)
+static int peererror(struct socket *sk, int err, struct dcpeer *peer)
 {
+    if(peer->state == PEER_TRNS)
+       transerr(peer);
     freedcpeer(peer);
+    return(0);
 }
 
-static void peerconnect(struct socket *sk, int err, struct fnetnode *fn)
+static int peerconnect(struct socket *sk, int err, struct fnetnode *fn)
 {
     struct dcpeer *peer;
     struct dchub *hub;
@@ -3105,34 +3187,35 @@ static void peerconnect(struct socket *sk, int err, struct fnetnode *fn)
     if(err != 0)
     {
        putfnetnode(fn);
-       return;
+       putsock(sk);
+       return(1);
     }
     hub = fn->data;
     peer = newdcpeer(sk);
     peer->fn = fn;
     peer->accepted = 0;
     peer->dcppemu = hub->dcppemu;
-    sk->readcb = (void (*)(struct socket *, void *))peerread;
-    sk->errcb = (void (*)(struct socket *, int, void *))peererror;
-    sk->data = peer;
+    CBREG(sk, socket_read, (int (*)(struct socket *, void *))peerread, NULL, peer);
+    CBREG(sk, socket_err, (int (*)(struct socket *, int, void *))peererror, NULL, peer);
     socksettos(sk, confgetint("fnet", "fnptos"));
     putsock(sk);
     peer->timeout = timercallback(ntime() + 180, (void (*)(int, void *))peertimeout, peer);
     sendmynick(peer);
     sendpeerlock(peer);
+    return(1);
 }
 
-static void peeraccept(struct socket *sk, struct socket *newsk, void *data)
+static int peeraccept(struct socket *sk, struct socket *newsk, void *data)
 {
     struct dcpeer *peer;
     
     peer = newdcpeer(newsk);
     peer->accepted = 1;
-    newsk->readcb = (void (*)(struct socket *, void *))peerread;
-    newsk->errcb = (void (*)(struct socket *, int, void *))peererror;
-    newsk->data = peer;
+    CBREG(sk, socket_read, (int (*)(struct socket *, void *))peerread, NULL, peer);
+    CBREG(sk, socket_err, (int (*)(struct socket *, int, void *))peererror, NULL, peer);
     socksettos(newsk, confgetint("fnet", "fnptos"));
     peer->timeout = timercallback(ntime() + 180, (void (*)(int, void *))peertimeout, peer);
+    return(0);
 }
 
 static void updatehmlist(void)
@@ -3150,6 +3233,7 @@ static void updatehmlist(void)
     while(1)
     {
        ic = 0;
+       /* Use DCCHARSET in $Get paths until further researched... */
        if((buf2 = icwcstombs(node->name, DCCHARSET)) != NULL)
        {
            for(i = 0; i < lev; i++)
@@ -3430,11 +3514,33 @@ static void updatexmlbz2list(void)
     fclose(real);
 }
 
-static int shareupdate(unsigned long long uusharesize, void *data)
+static void listtimercb(int cancelled, void *uudata)
+{
+    listwritetimer = NULL;
+    if(!cancelled)
+       updatelists(1);
+}
+
+static void updatelists(int now)
 {
+    if((hmlistname == NULL) || (xmllistname == NULL) || (xmlbz2listname == NULL))
+       now = 1;
+    if(!now)
+    {
+       if(listwritetimer == NULL)
+           listwritetimer = timercallback(ntime() + 300, listtimercb, NULL);
+       return;
+    }
+    if(listwritetimer != NULL)
+       canceltimer(listwritetimer);
     updatehmlist();
     updatexmllist();
     updatexmlbz2list();
+}
+
+static int shareupdate(unsigned long long uusharesize, void *data)
+{
+    updatelists(0);
     return(0);
 }
 
@@ -3527,7 +3633,7 @@ static int updateudpport(struct configvar *var, void *uudata)
        flog(LOG_WARNING, "could not create new DC UDP socket, reverting to old: %s", strerror(errno));
        return(0);
     }
-    newsock->readcb = udpread;
+    CBREG(newsock, socket_read, udpread, NULL, NULL);
     if(udpsock != NULL)
        putsock(udpsock);
     udpsock = newsock;
@@ -3569,7 +3675,7 @@ static int init(int hup)
            flog(LOG_CRIT, "could not create DC UDP socket: %s", strerror(errno));
            return(1);
        }
-       udpsock->readcb = udpread;
+       CBREG(udpsock, socket_read, udpread, NULL, NULL);
        addr.sin_port = htons(confgetint("dc", "tcpport"));
        if((tcpsock = netcslisten(SOCK_STREAM, (struct sockaddr *)&addr, sizeof(addr), peeraccept, NULL)) == NULL)
            flog(LOG_INFO, "could not listen to a remote address, going into passive mode");