Incremental work on excorcising the transfer iface.
[doldaconnect.git] / daemon / fnet-dc.c
index df04b59..d2343da 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  Dolda Connect - Modular multiuser Direct Connect-style client
- *  Copyright (C) 2004 Fredrik Tolf (fredrik@dolda2000.com)
+ *  Copyright (C) 2004 Fredrik Tolf <fredrik@dolda2000.com>
  *  
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
 #include <stdlib.h>
 #include <stdio.h>
 #include <wchar.h>
-#include <malloc.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <string.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
-#include <alloca.h>
 #include <wctype.h>
 #include <time.h>
 #include <errno.h>
 #include <bzlib.h>
 #include <zlib.h>
 #include <sys/stat.h>
+#include <stdint.h>
 
 #ifdef HAVE_CONFIG_H
 #include <config.h>
@@ -44,7 +43,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
  * considering it was developed without i18n support under Windows */
 #define DCCHARSET "windows-1252"
 
-#ifdef DCPP_MASQUERADE
-/*
- * I honestly don't want to pretend being a client that I'm not, but
- * there are so many hubs that simply do not accept any clients
- * outside their whitelists, for no obvious reasons, so I feel that I
- * am left with little choice. Anyhow, as long as I actually support
- * all the features that my faked DC++ version does, there should be
- * very little harm done.
- */
-#define DCIDTAG "++"
-#define DCIDTAGV "0.674"
-#define DCIDFULL "DC++ 0.674"
-#else
-#define DCIDTAG "Dolda"
-#define DCIDTAGV VERSION
-#define DCIDFULL "DoldaConnect " VERSION
-#endif
-
 #define PEER_CMD 0
 #define PEER_STOP 1
 #define PEER_TRNS 2
@@ -93,6 +74,7 @@ struct command
     char *name;
     void (*handler)(struct socket *sk, void *data, char *cmd, char *args);
     int stop;
+    int limit;
 };
 
 struct qcommand
@@ -101,14 +83,23 @@ struct qcommand
     char *string;
 };
 
+struct qcmdqueue
+{
+    struct qcommand *f, *l;
+    int size;
+};
+
 struct dchub
 {
+    struct socket *sk;
     char *inbuf;
     size_t inbufdata, inbufsize;
-    struct qcommand *queue;
-    int extended;
+    struct qcmdqueue queue;
+    int extended, dcppemu;
+    char *charset;
     char *nativename;
     char *nativenick;
+    char **supports;
 };
 
 struct dcexppeer
@@ -126,18 +117,21 @@ struct dcpeer
     struct fnetnode *fn;
     char *inbuf;
     size_t inbufdata, inbufsize;
-    size_t curread, totalsize;
-    int freeing;
-    struct qcommand *queue;
+    off_t curread, totalsize;
+    int close;
+    struct timer *timeout;
+    struct qcmdqueue queue;
     struct transfer *transfer;
+    struct socket *trpipe;
     int state;
     int ptclose;      /* Close after transfer is complete */
     int accepted;     /* If false, we connected, otherwise, we accepted */
-    int extended;
+    int extended, dcppemu;
     int direction;    /* Using the constants from transfer.h */
     int compress;
     int hascurpos, fetchingtthl, notthl;
     struct tigertreehash tth;
+    char *charset;
     void *cprsdata;
     char *key;
     char *nativename;
@@ -146,16 +140,17 @@ struct dcpeer
 };
 
 static struct fnet dcnet;
-static struct transferiface dctransfer;
 static struct socket *udpsock = NULL;
-static struct socket *tcpsock = NULL;
+static struct lport *tcpsock = NULL;
 static struct dcpeer *peers = NULL;
 int numdcpeers = 0;
 static struct dcexppeer *expected = NULL;
 static char *hmlistname = NULL;
 static char *xmllistname = NULL;
 static char *xmlbz2listname = NULL;
+static struct timer *listwritetimer = NULL;
 
+static struct socket *mktrpipe(struct dcpeer *peer);
 static void peerconnect(struct socket *sk, int err, struct fnetnode *fn);
 static void freedcpeer(struct dcpeer *peer);
 static void transread(struct socket *sk, struct dcpeer *peer);
@@ -165,6 +160,7 @@ 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)
 {
@@ -210,27 +206,42 @@ static char *dcmakekey(char *lock)
     return(key);
 }
 
-static char *pathnmdc2adc(char *path)
+static wchar_t *nmdc2path(char *nmdc, char *charset)
 {
-    char *ret;
-    size_t retsize, retdata;
+    wchar_t *ret, *p;
     
-    if(!strcmp(path, "files.xml") || !strcmp(path, "files.xml.bz2") || !strcmp(path, "MyList.DcLst"))
-       return(sstrdup(path));
-    ret = NULL;
-    retsize = retdata = 0;
-    addtobuf(ret, '/');
-    for(; *path; path++)
-    {
-       if(*path == '\\')
-           addtobuf(ret, '/');
-       else
-           addtobuf(ret, *path);
+    if((ret = icmbstowcs(nmdc, charset)) == NULL)
+       return(NULL);
+    for(p = ret; *p != L'\0'; p++) {
+       if(*p == L'\\')
+           *p = L'/';
     }
-    addtobuf(ret, 0);
     return(ret);
 }
 
+static char *path2nmdc(wchar_t *path, char *charset)
+{
+    char *ret, *p;
+    
+    if((ret = icwcstombs(path, charset)) == NULL)
+       return(NULL);
+    for(p = ret; *p; p++) {
+       if(*p == '/')
+           *p = '\\';
+    }
+    return(ret);
+}
+
+static wchar_t *adc2path(char *adc)
+{
+    return(icmbstowcs(adc, "UTF-8"));
+}
+
+static char *path2adc(wchar_t *path)
+{
+    return(icwcstombs(path, "UTF-8"));
+}
+
 static int isdchash(struct hash *hash)
 {
     if(wcscmp(hash->algo, L"TTH"))
@@ -240,6 +251,24 @@ static int isdchash(struct hash *hash)
     return(1);
 }
 
+/*
+ * Uncomment when used!
+static int hubsupports(struct dchub *hub, char *cap)
+{
+    char **p;
+    
+    if(hub->supports == NULL)
+       return(0);
+    for(p = hub->supports; *p != NULL; p++)
+    {
+       if(!strcasecmp(*p, cap))
+           return(1);
+    }
+    return(0);
+}
+*/
+
 static int supports(struct dcpeer *peer, char *cap)
 {
     char **p;
@@ -353,26 +382,31 @@ static struct dcexppeer *expectpeer(char *nick, struct fnetnode *fn)
     return(ep);
 }
 
-static struct qcommand *newqcmd(struct qcommand **queue, char *string)
+static struct qcommand *newqcmd(struct qcmdqueue *queue, char *string)
 {
     struct qcommand *new;
     
-    while(*queue != NULL)
-       queue = &(*queue)->next;
     new = smalloc(sizeof(*new));
     new->string = sstrdup(string);
-    new->next = *queue;
-    *queue = new;
+    new->next = NULL;
+    if(queue->l == NULL)
+       queue->f = new;
+    else
+       queue->l->next = new;
+    queue->l = new;
+    queue->size++;
     return(new);
 }
 
-static struct qcommand *ulqcmd(struct qcommand **queue)
+static struct qcommand *ulqcmd(struct qcmdqueue *queue)
 {
     struct qcommand *qcmd;
     
-    if((qcmd = *queue) == NULL)
+    if((qcmd = queue->f) == NULL)
        return(NULL);
-    *queue = qcmd->next;
+    if((queue->f = qcmd->next) == NULL)
+       queue->l = NULL;
+    queue->size--;
     return(qcmd);
 }
 
@@ -387,7 +421,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));
@@ -395,7 +431,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 {
@@ -413,7 +449,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;
            }
@@ -421,7 +457,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);
@@ -444,6 +480,14 @@ static void hubrecvchat(struct socket *sk, struct fnetnode *fn, char *from, char
     free(chat);
 }
 
+static void peertimeout(int cancelled, struct dcpeer *peer)
+{
+    peer->timeout = NULL;
+    if(cancelled)
+       return;
+    freedcpeer(peer);
+}
+
 static void sendadc(struct socket *sk, char *arg)
 {
     char *buf;
@@ -469,7 +513,11 @@ static void sendadc(struct socket *sk, char *arg)
     free(buf);
 }
 
-static void sendadcf(struct socket *sk, char *arg, ...)
+#if defined(__GNUC__)
+static void __attribute__ ((format (printf, 2, 3))) sendadcf(struct socket *sk, char *arg, ...) 
+#else
+static void sendadcf(struct socket *sk, char *arg, ...) 
+#endif
 {
     char *buf;
     va_list args;
@@ -577,6 +625,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"))
@@ -585,10 +634,7 @@ static char *getadcid(struct dcpeer *peer)
        ret = sprintf2("TTH/%.39s", buf);
        free(buf);
     } else {
-       if((buf = icwcstombs(peer->transfer->path, "UTF-8")) == NULL)
-           return(NULL);
-       ret = pathnmdc2adc(buf);
-       free(buf);
+       ret = path2adc(peer->transfer->path);
     }
     return(ret);
 }
@@ -603,7 +649,7 @@ static int trresumecb(struct transfer *transfer, wchar_t *cmd, wchar_t *arg, str
            flog(LOG_WARNING, "filter returned no position for \"resume\" on transfer %i", transfer->id);
            freedcpeer(peer);
        } else {
-           transfer->curpos = wcstol(arg, NULL, 10);
+           transfer->curpos = wcstoll(arg, NULL, 10);
            peer->hascurpos = 1;
            requestfile(peer);
        }
@@ -614,31 +660,33 @@ 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)
 {
-#ifdef DCPP_MASQUERADE
-    qstrf(peer->sk, "$Lock EXTENDEDPROTOCOLABCABCABCABCABCABC Pk=DCPLUSPLUS0.674ABCABC|");
-#else
-    qstrf(peer->sk, "$Lock EXTENDEDPROTOCOLABCABCABCABCABCABC Pk=DOLDA%sABCABCABC|", VERSION);
-#endif
+    if(peer->dcppemu)
+       qstrf(peer->sk, "$Lock EXTENDEDPROTOCOLABCABCABCABCABCABC Pk=DCPLUSPLUS0.674ABCABC|");
+    else
+       qstrf(peer->sk, "$Lock EXTENDEDPROTOCOLABCABCABCABCABCABC Pk=DOLDA%sABCABCABC|", VERSION);
 }
 
 static void sendsupports(struct dcpeer *peer)
 {
-#ifdef DCPP_MASQUERADE
-    qstr(peer->sk, "$Supports MiniSlots XmlBZList ADCGet TTHL TTHF GetZBlock ZLIG |");
-#else
-    qstr(peer->sk, "$Supports MiniSlots XmlBZList ADCGet TTHL TTHF GetZBlock ZLIG|");
-#endif
+    if(peer->dcppemu) {
+       qstr(peer->sk, "$Supports MiniSlots XmlBZList ADCGet TTHL TTHF GetZBlock ZLIG |");
+    } else {
+       qstr(peer->sk, "$Supports MiniSlots XmlBZList ADCGet TTHL TTHF");
+       if(!confgetint("dc", "hidedeflate"))
+           qstr(peer->sk, " GetZBlock ZLIG");
+       qstr(peer->sk, "|");
+    }
 }
 
 static void requestfile(struct dcpeer *peer)
@@ -647,16 +695,18 @@ static void requestfile(struct dcpeer *peer)
     
     if(peer->transfer->size == -1)
     {
-       if((buf = icswcstombs(peer->transfer->path, DCCHARSET, NULL)) == NULL)
+       /* Use DCCHARSET for $Get paths until further researched... */
+       if((buf = path2nmdc(peer->transfer->path, DCCHARSET)) == NULL)
        {
            transferseterror(peer->transfer, TRNSE_NOTFOUND);
-           freedcpeer(peer);
+           peer->close = 1;
            return;
        }
        /* The transfer will be restarted later from
         * cmd_filelength when it detects that the sizes
         * don't match. */
        qstrf(peer->sk, "$Get %s$1|", buf);
+       free(buf);
        return;
     }
     if((peer->transfer->hash == NULL) && !peer->notthl)
@@ -668,7 +718,7 @@ static void requestfile(struct dcpeer *peer)
            if((buf = getadcid(peer)) == NULL)
            {
                transferseterror(peer->transfer, TRNSE_NOTFOUND);
-               freedcpeer(peer);
+               peer->close = 1;
                return;
            }
            sendadc(peer->sk, buf);
@@ -685,7 +735,7 @@ static void requestfile(struct dcpeer *peer)
        if(forkfilter(peer->transfer))
        {
            flog(LOG_WARNING, "could not fork filter for transfer %i: %s", peer->transfer->id, strerror(errno));
-           freedcpeer(peer);
+           peer->close = 1;
            return;
        }
        CBREG(peer->transfer, trans_filterout, (int (*)(struct transfer *, wchar_t *, wchar_t *, void *))trresumecb, NULL, peer);
@@ -698,30 +748,33 @@ static void requestfile(struct dcpeer *peer)
        if((buf = getadcid(peer)) == NULL)
        {
            transferseterror(peer->transfer, TRNSE_NOTFOUND);
-           freedcpeer(peer);
+           peer->close = 1;
            return;
        }
        sendadc(peer->sk, buf);
        free(buf);
-       sendadcf(peer->sk, "%i", peer->transfer->curpos);
-       sendadcf(peer->sk, "%i", peer->transfer->size - peer->transfer->curpos);
+       sendadcf(peer->sk, "%ji", (intmax_t)peer->transfer->curpos);
+       sendadcf(peer->sk, "%ji", (intmax_t)(peer->transfer->size - peer->transfer->curpos));
        qstr(peer->sk, "|");
     } else if(supports(peer, "xmlbzlist")) {
-       if((buf = icswcstombs(peer->transfer->path, "UTF-8", NULL)) == NULL)
+       if((buf = path2nmdc(peer->transfer->path, "UTF-8")) == NULL)
        {
            transferseterror(peer->transfer, TRNSE_NOTFOUND);
-           freedcpeer(peer);
+           peer->close = 1;
            return;
        }
-       qstrf(peer->sk, "$UGetBlock %i %i %s|", peer->transfer->curpos, peer->transfer->size - peer->transfer->curpos, buf);
+       qstrf(peer->sk, "$UGetBlock %ji %ji %s|", (intmax_t)peer->transfer->curpos, (intmax_t)(peer->transfer->size - peer->transfer->curpos), buf);
+       free(buf);
     } else {
-       if((buf = icswcstombs(peer->transfer->path, DCCHARSET, NULL)) == NULL)
+       /* Use DCCHARSET for $Get paths until further researched... */
+       if((buf = path2nmdc(peer->transfer->path, DCCHARSET)) == NULL)
        {
            transferseterror(peer->transfer, TRNSE_NOTFOUND);
-           freedcpeer(peer);
+           peer->close = 1;
            return;
        }
-       qstrf(peer->sk, "$Get %s$%i|", buf, peer->transfer->curpos + 1);
+       qstrf(peer->sk, "$Get %s$%ji|", buf, (intmax_t)peer->transfer->curpos + 1);
+       free(buf);
     }
 }
 
@@ -734,7 +787,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)
@@ -750,16 +803,16 @@ static void sendmyinfo(struct socket *sk, struct fnetnode *fn)
        }
     }
     qstrf(sk, "<%s V:%s,M:%c,H:%i/%i/%i,S:%i>",
-         DCIDTAG,
-         DCIDTAGV,
+         (hub->dcppemu)?"++":"Dolda",
+         (hub->dcppemu)?"0.674":VERSION,
          (tcpsock == NULL)?'P':'A',
          hn1, hn2, hn3,
          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);
 }
@@ -798,11 +851,14 @@ static void cmd_lock(struct socket *sk, struct fnetnode *fn, char *cmd, char *ar
        *(p++) = 0;
     if(hub->extended)
     {
-#ifdef DCPP_MASQUERADE
-       qstrf(sk, "$Supports UserCommand NoGetINFO NoHello UserIP2 TTHSearch GetZBlock |");
-#else
-       qstrf(sk, "$Supports UserCommand NoGetINFO NoHello UserIP2 TTHSearch GetZBlock|");
-#endif
+       if(hub->dcppemu) {
+           qstrf(sk, "$Supports UserCommand NoGetINFO NoHello UserIP2 TTHSearch GetZBlock |");
+       } else {
+           qstrf(sk, "$Supports UserCommand NoGetINFO NoHello UserIP2 TTHSearch");
+           if(!confgetint("dc", "hidedeflate"))
+               qstr(sk, " GetZBlock");
+           qstr(sk, "|");
+       }
     }
     key = dcmakekey(args);
     qstrf(sk, "$Key %s|", key);
@@ -819,7 +875,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);
@@ -832,7 +888,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);
@@ -847,7 +903,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);
@@ -860,15 +916,15 @@ static void cmd_nicklist(struct socket *sk, struct fnetnode *fn, char *cmd, char
     struct dchub *hub;
     char *p;
     wchar_t *buf;
-    struct fnetpeer *peer, *npeer;
+    struct fnetpeer *peer;
     
     hub = fn->data;
-    for(peer = fn->peers; peer != NULL; peer = peer->next)
+    for(peer = btreeiter(fn->peers); peer != NULL; peer = btreeiter(NULL))
        peer->flags.b.delete = 1;
     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);
@@ -879,12 +935,7 @@ static void cmd_nicklist(struct socket *sk, struct fnetnode *fn, char *cmd, char
        }
        args = p + 2;
     }
-    for(peer = fn->peers; peer != NULL; peer = npeer)
-    {
-       npeer = peer->next;
-       if(peer->flags.b.delete)
-           fnetdelpeer(peer);
-    }
+    fnetpeerdm(fn);
     hubhandleaction(sk, fn, cmd, args);
 }
 
@@ -896,12 +947,12 @@ static void cmd_oplist(struct socket *sk, struct fnetnode *fn, char *cmd, char *
     struct fnetpeer *peer;
     
     hub = fn->data;
-    for(peer = fn->peers; peer != NULL; peer = peer->next)
+    for(peer = btreeiter(fn->peers); peer != NULL; peer = btreeiter(NULL))
        peer->flags.b.op = 0;
     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;
@@ -928,7 +979,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);
@@ -937,9 +988,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';
@@ -966,7 +1017,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);
@@ -974,7 +1025,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);
@@ -1004,7 +1055,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);
@@ -1014,7 +1065,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;
@@ -1023,15 +1074,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);
@@ -1070,10 +1121,11 @@ 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];
+    wchar_t *terms[32], *lname;
     char hashtth[24];
     
     hub = fn->data;
@@ -1094,7 +1146,7 @@ static void cmd_search(struct socket *sk, struct fnetnode *fn, char *cmd, char *
            goto out;
        prefix = sprintf2("$SR %s ", hub->nativenick);
        infix = sprintf2(" %i/%i\005", slotsleft(), confgetint("transfer", "slots"));
-       postfix = sprintf2(" (%s)\005%s|", formataddress(fn->sk->remote, fn->sk->remotelen), args + 4);
+       postfix = sprintf2(" (%s)\005%s|", formatsockpeer(hub->sk), args + 4);
        dsk = sk;
        getsock(dsk);
     } else {
@@ -1107,8 +1159,8 @@ static void cmd_search(struct socket *sk, struct fnetnode *fn, char *cmd, char *
        addr.sin_port = htons(atoi(p2));
        prefix = sprintf2("$SR %s ", hub->nativenick);
        infix = sprintf2(" %i/%i\005", slotsleft(), confgetint("transfer", "slots"));
-       postfix = sprintf2(" (%s)|", formataddress(fn->sk->remote, fn->sk->remotelen));
-       netdgramconn(dsk = netdupsock(udpsock), (struct sockaddr *)&addr, sizeof(addr));
+       postfix = sprintf2(" (%s)|", formatsockpeer(hub->sk));
+       dsk = netdgramconn(udpsock, (struct sockaddr *)&addr, sizeof(addr));
     }
     
     minsize = maxsize = -1;
@@ -1153,15 +1205,18 @@ 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) {
+                       wcslower(terms[termnum]);
                        termnum++;
+                   }
                }
            }
            p = p2 + 1;
@@ -1193,11 +1248,12 @@ static void cmd_search(struct socket *sk, struct fnetnode *fn, char *cmd, char *
        }
        if(!skipcheck)
        {
+           lname = wcslower(swcsdup(node->name));
            for(i = 0; i < termnum; i++)
            {
                if(tersat[i] >= 0)
                    continue;
-               if(wcsexists(node->name, terms[i]))
+               if(wcsstr(lname, terms[i]))
                {
                    tersat[i] = level;
                    satisfied++;
@@ -1205,18 +1261,20 @@ static void cmd_search(struct socket *sk, struct fnetnode *fn, char *cmd, char *
                    break;
                }
            }
+           free(lname);
        }
        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)
                {
                    buf2 = base32encode(node->hashtth, 24);
-                   qstrf(dsk, "%s%s\005%i%sTTH:%.39s%s", prefix, buf, node->size, infix, buf2, postfix);
+                   qstrf(dsk, "%s%s\005%ji%sTTH:%.39s%s", prefix, buf, (intmax_t)node->size, infix, buf2, postfix);
                    free(buf2);
                } else {
-                   qstrf(dsk, "%s%s\005%i%s%s%s", prefix, buf, node->size, infix, hub->nativename, postfix);
+                   qstrf(dsk, "%s%s\005%ji%s%s%s", prefix, buf, (intmax_t)node->size, infix, hub->nativename, postfix);
                }
                free(buf);
            }
@@ -1294,7 +1352,7 @@ static void sendctm(struct socket *sk, char *nick)
     
     if(tcpsock == NULL)
        return;
-    if(sockgetremotename(tcpsock, &addr, &addrlen) < 0)
+    if(lstgetremotename2(tcpsock, sk, &addr, &addrlen) < 0)
        return;
     if(addr->sa_family == AF_INET)
        qstrf(sk, "$ConnectToMe %s %s|", nick, formataddress(addr, addrlen));
@@ -1357,7 +1415,7 @@ static void cmd_to(struct socket *sk, struct fnetnode *fn, char *cmd, char *args
        return;
     *p2 = 0;
     p2 += 2;
-    hubrecvchat(fn->sk, fn, p, p2);
+    hubrecvchat(hub->sk, fn, p, p2);
     hubhandleaction(sk, fn, cmd, args);
 }
 
@@ -1366,7 +1424,8 @@ static void cmd_sr(struct socket *sk, struct fnetnode *fn, char *cmd, char *args
     struct dchub *hub;
     char *p, *p2, *buf;
     char *nick, *filename, *hubname;
-    int size, slots;
+    off_t size;
+    int slots;
     size_t buflen;
     struct srchres *sr;
     wchar_t *wnick, *wfile;
@@ -1385,7 +1444,7 @@ static void cmd_sr(struct socket *sk, struct fnetnode *fn, char *cmd, char *args
     if((p2 = strchr(p, ' ')) == NULL)
        return;
     *p2 = 0;
-    size = atoi(p);
+    size = strtoll(p, NULL, 10);
     p = p2 + 1;
     if((p2 = strchr(p, '/')) == NULL)
        return;
@@ -1399,9 +1458,10 @@ 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;
-    if((wfile = icmbstowcs(filename, DCCHARSET)) == NULL)
+    /* Use DCCHARSET in $Get paths until further researched... */
+    if((wfile = nmdc2path(filename, DCCHARSET)) == NULL)
     {
        free(wnick);
        return;
@@ -1437,16 +1497,12 @@ static void cmd_usercommand(struct socket *sk, struct fnetnode *fn, char *cmd, c
 static void cmd_getpass(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
 {
     struct dchub *hub;
-    struct wcspair *arg;
+    wchar_t *pw;
     char *mbspw;
     
     hub = fn->data;
-    for(arg = fn->args; arg != NULL; arg = arg->next)
-    {
-       if(!wcscmp(arg->key, L"password"))
-           break;
-    }
-    if((arg == NULL) || ((mbspw = icwcstombs(arg->val, DCCHARSET)) == NULL))
+    pw = wpfind(fn->args, L"password");
+    if((pw == NULL) || ((mbspw = icwcstombs(pw, hub->charset)) == NULL))
     {
        killfnetnode(fn);
        return;
@@ -1466,18 +1522,49 @@ static void cmd_logedin(struct socket *sk, struct fnetnode *fn, char *cmd, char
     hubhandleaction(sk, fn, cmd, args);
 }
 
+static void cmd_hubsupports(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
+{
+    struct dchub *hub;
+    int i;
+    char *p, *p2;
+    char **arr;
+    size_t arrsize, arrdata;
+    
+    hub = fn->data;
+    if(hub->supports != NULL)
+    {
+       for(i = 0; hub->supports[i] != NULL; i++)
+           free(hub->supports[i]);
+       free(hub->supports);
+    }
+    arr = NULL;
+    arrsize = arrdata = 0;
+    p = args;
+    do
+    {
+       if((p2 = strchr(p, ' ')) != NULL)
+           *(p2++) = 0;
+       if(*p == 0)
+           continue;
+       addtobuf(arr, sstrdup(p));
+    } while((p = p2) != NULL);
+    addtobuf(arr, NULL);
+    hub->supports = arr;
+}
+
 static void cmd_mynick(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
 {
     struct dcexppeer *expect;
+    struct dchub *hub;
 
     if(peer->nativename != NULL)
        free(peer->nativename);
     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);
+       peer->close = 1;
        return;
     }
     if(peer->accepted)
@@ -1491,8 +1578,10 @@ static void cmd_mynick(struct socket *sk, struct dcpeer *peer, char *cmd, char *
        {
            peer->fn = NULL;
        } else {
+           hub = expect->fn->data;
            peer->fn = expect->fn;
            getfnetnode(peer->fn);
+           peer->dcppemu = hub->dcppemu;
            freeexppeer(expect);
        }
     }
@@ -1515,7 +1604,7 @@ static void cmd_direction(struct socket *sk, struct dcpeer *peer, char *cmd, cha
     {
        if((peer->transfer == NULL) || (mydir != peer->direction))
        {
-           freedcpeer(peer);
+           peer->close = 1;
            return;
        }
        if(peer->direction == TRNSD_DOWN)
@@ -1523,20 +1612,25 @@ static void cmd_direction(struct socket *sk, struct dcpeer *peer, char *cmd, cha
     } else {
        if(peer->wcsname == NULL)
        {
-           freedcpeer(peer);
+           peer->close = 1;
            return;
        }
        peer->direction = mydir;
        if(peer->direction == TRNSD_UP)
        {
-           transfer = newupload(peer->fn, &dcnet, peer->wcsname, &dctransfer, peer);
+           if(confgetint("transfer", "ulquota") && hasupload(&dcnet, peer->wcsname))
+           {
+               peer->close = 1;
+               return;
+           }
+           transfer = newupload(peer->fn, &dcnet, peer->wcsname, peer->trpipe = mktrpipe(peer));
        } else {
            if((transfer = finddownload(peer->wcsname)) == NULL)
            {
-               freedcpeer(peer);
+               peer->close = 1;
                return;
            }
-           transferattach(transfer, &dctransfer, peer);
+           transferattach(transfer, peer->trpipe = mktrpipe(peer));
            transfersetstate(transfer, TRNS_HS);
        }
        transfersetnick(transfer, peer->wcsname);
@@ -1566,7 +1660,7 @@ static void cmd_peerlock(struct socket *sk, struct dcpeer *peer, char *cmd, char
     {
        if(peer->wcsname == NULL)
        {
-           freedcpeer(peer);
+           peer->close = 1;
            return;
        }
        sendmynick(peer);
@@ -1575,17 +1669,23 @@ 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))
+           {
+               peer->close = 1;
+               return;
+           }
            peer->direction = TRNSD_UP;
-           transfer = newupload(peer->fn, &dcnet, peer->wcsname, &dctransfer, peer);
+           transfer = newupload(peer->fn, &dcnet, peer->wcsname, peer->trpipe = mktrpipe(peer));
        } else {
            peer->direction = TRNSD_DOWN;
-           transferattach(transfer, &dctransfer, peer);
+           transferattach(transfer, peer->trpipe = mktrpipe(peer));
            transfersetstate(transfer, TRNS_HS);
        }
        transfersetnick(transfer, peer->wcsname);
        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);
@@ -1600,6 +1700,8 @@ static void cmd_key(struct socket *sk, struct dcpeer *peer, char *cmd, char *arg
 
 static void startdl(struct dcpeer *peer)
 {
+    if(peer->timeout != NULL)
+       canceltimer(peer->timeout);
     peer->state = PEER_TRNS;
     transferstartdl(peer->transfer, peer->sk);
     peer->sk->readcb = (void (*)(struct socket *, void *))transread;
@@ -1608,6 +1710,8 @@ static void startdl(struct dcpeer *peer)
 
 static void startul(struct dcpeer *peer)
 {
+    if(peer->timeout != NULL)
+       canceltimer(peer->timeout);
     peer->state = PEER_TRNS;
     transferstartul(peer->transfer, peer->sk);
     peer->sk->writecb = (void (*)(struct socket *, void *))transwrite;
@@ -1615,20 +1719,21 @@ static void startul(struct dcpeer *peer)
 
 static void cmd_filelength(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
 {
-    int size;
+    off_t size;
     struct transfer *transfer;
     
     if(peer->transfer == NULL)
     {
-       freedcpeer(peer);
+       peer->close = 1;
        return;
     }
-    size = atoi(args);
+    size = strtoll(args, NULL, 10);
     if(peer->transfer->size != size)
     {
        transfersetsize(peer->transfer, size);
        transfer = peer->transfer;
-       freedcpeer(peer);
+       peer->close = 1;
+       resettransfer(transfer);
        trytransferbypeer(transfer->fnet, transfer->peerid);
        return;
     }
@@ -1651,7 +1756,7 @@ static void cmd_error(struct socket *sk, struct dcpeer *peer, char *cmd, char *a
        resettransfer(peer->transfer);
        return;
     }
-    freedcpeer(peer);
+    peer->close = 1;
 }
 
 static void cmd_maxedout(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
@@ -1662,7 +1767,7 @@ static void cmd_maxedout(struct socket *sk, struct dcpeer *peer, char *cmd, char
        resettransfer(peer->transfer);
        return;
     }
-    freedcpeer(peer);
+    peer->close = 1;
 }
 
 static struct
@@ -1746,7 +1851,7 @@ static struct sharecache *resdcpath(char *path, char *charset, char sep)
 
 static void cmd_get(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
 {
-    int offset;
+    off_t offset;
     char *p, *buf;
     wchar_t *buf2;
     struct sharecache *node;
@@ -1756,47 +1861,45 @@ static void cmd_get(struct socket *sk, struct dcpeer *peer, char *cmd, char *arg
     
     if(peer->transfer == NULL)
     {
-       freedcpeer(peer);
+       peer->close = 1;
        return;
     }
     if((p = strchr(args, '$')) == NULL)
     {
-       freedcpeer(peer);
+       peer->close = 1;
        return;
     }
     *(p++) = 0;
-    if((offset = (atoi(p) - 1)) < 0)
+    if((offset = (strtoll(p, NULL, 10) - 1)) < 0)
     {
-       freedcpeer(peer);
+       peer->close = 1;
        return;
     }
     if(((fd = openfilelist(args)) < 0) && (errno != 0))
     {
        qstr(sk, "$Error Could not send file list|");
-       freedcpeer(peer);
+       peer->close = 1;
        return;
     } else if(fd >= 0) {
-       if((buf2 = icsmbstowcs(args, DCCHARSET, NULL)) != NULL)
+       if((buf2 = nmdc2path(args, DCCHARSET)) != NULL) {
            transfersetpath(peer->transfer, buf2);
+           free(buf2);
+       }
+       peer->transfer->flags.b.minislot = 1;
     }
     if(fd < 0)
     {
-       if(slotsleft() < 1)
-       {
-           qstr(sk, "$MaxedOut|");
-           freedcpeer(peer);
-           return;
-       }
+       /* Use DCCHARSET in $Get paths until further researched... */
        if((node = resdcpath(args, DCCHARSET, '\\')) == NULL)
        {
            qstrf(sk, "$Error File not in share|");
-           freedcpeer(peer);
+           peer->close = 1;
            return;
        }
        if((fd = opensharecache(node)) < 0)
        {
            qstrf(sk, "$Error %s|", strerror(errno));
-           freedcpeer(peer);
+           peer->close = 1;
            return;
        }
        buf = getfspath(node);
@@ -1811,32 +1914,40 @@ static void cmd_get(struct socket *sk, struct dcpeer *peer, char *cmd, char *arg
        close(fd);
        flog(LOG_WARNING, "could not stat file %ls: %s", node->name, strerror(errno));
        qstrf(sk, "$Error|");
-       freedcpeer(peer);
+       peer->close = 1;
+       return;
+    }
+    if(sb.st_size < 65536)
+       peer->transfer->flags.b.minislot = 1;
+    if(!peer->transfer->flags.b.minislot && (slotsleft() < 1)) {
+       close(fd);
+       qstr(sk, "$MaxedOut|");
+       peer->close = 1;
        return;
     }
     if((offset != 0) && (lseek(fd, offset, SEEK_SET) < 0))
     {
        close(fd);
        qstrf(sk, "$Error Offset out of range|");
-       freedcpeer(peer);
+       peer->close = 1;
        return;
     }
     lesk = wrapsock(fd);
     transferprepul(peer->transfer, sb.st_size, offset, -1, lesk);
     putsock(lesk);
-    qstrf(sk, "$FileLength %i|", peer->transfer->size);
+    qstrf(sk, "$FileLength %ji|", (intmax_t)peer->transfer->size);
 }
 
 static void cmd_send(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
 {
     if(peer->transfer == NULL)
     {
-       freedcpeer(peer);
+       peer->close = 1;
        return;
     }
     if(peer->transfer->localend == NULL)
     {
-       freedcpeer(peer);
+       peer->close = 1;
        return;
     }
     peer->ptclose = 1;
@@ -1875,7 +1986,7 @@ static void cmd_getblock(struct socket *sk, struct dcpeer *peer, char *cmd, char
 {
     int fd;
     char *p, *p2;
-    int start, numbytes;
+    off_t start, numbytes;
     char *charset, *buf;
     wchar_t *buf2;
     struct sharecache *node;
@@ -1884,29 +1995,30 @@ static void cmd_getblock(struct socket *sk, struct dcpeer *peer, char *cmd, char
     
     if(peer->transfer == NULL)
     {
-       freedcpeer(peer);
+       peer->close = 1;
        return;
     }
     p = args;
     if((p2 = strchr(p, ' ')) == NULL)
     {
-       freedcpeer(peer);
+       peer->close = 1;
        return;
     }
     *(p2++) = 0;
-    start = atoi(p);
+    start = strtoll(p, NULL, 10);
     p = p2;
     if((p2 = strchr(p, ' ')) == NULL)
     {
-       freedcpeer(peer);
+       peer->close = 1;
        return;
     }
     *(p2++) = 0;
-    numbytes = atoi(p);
+    numbytes = strtoll(p, NULL, 10);
     p = p2;
     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);
@@ -1915,16 +2027,14 @@ static void cmd_getblock(struct socket *sk, struct dcpeer *peer, char *cmd, char
        qstr(sk, "$Error Could not send file list|");
        return;
     } else if(fd >= 0) {
-       if((buf2 = icsmbstowcs(args, charset, NULL)) != NULL)
+       if((buf2 = nmdc2path(args, charset)) != NULL) {
            transfersetpath(peer->transfer, buf2);
+           free(buf2);
+       }
+       peer->transfer->flags.b.minislot = 1;
     }
     if(fd < 0)
     {
-       if(slotsleft() < 1)
-       {
-           qstr(sk, "$MaxedOut|");
-           return;
-       }
        if((node = resdcpath(p, charset, '\\')) == NULL)
        {
            qstr(sk, "$Error File not in cache|");
@@ -1949,6 +2059,13 @@ static void cmd_getblock(struct socket *sk, struct dcpeer *peer, char *cmd, char
        qstr(sk, "$Error|");
        return;
     }
+    if(sb.st_size < 65536)
+       peer->transfer->flags.b.minislot = 1;
+    if(!peer->transfer->flags.b.minislot && (slotsleft() < 1)) {
+       close(fd);
+       qstr(sk, "$MaxedOut|");
+       return;
+    }
     if((start != 0) && ((start >= sb.st_size) || (lseek(fd, start, SEEK_SET) < 0)))
     {
        close(fd);
@@ -1960,7 +2077,7 @@ static void cmd_getblock(struct socket *sk, struct dcpeer *peer, char *cmd, char
     lesk = wrapsock(fd);
     transferprepul(peer->transfer, sb.st_size, start, start + numbytes, lesk);
     putsock(lesk);
-    qstrf(sk, "$Sending %i|", numbytes);
+    qstrf(sk, "$Sending %ji|", (intmax_t)numbytes);
     startul(peer);
 }
 
@@ -1968,7 +2085,7 @@ static void cmd_adcget(struct socket *sk, struct dcpeer *peer, char *cmd, char *
 {
     int i;
     char **argv, *buf;
-    int start, numbytes;
+    off_t start, numbytes;
     struct sharecache *node;
     struct stat sb;
     struct socket *lesk;
@@ -1977,21 +2094,21 @@ static void cmd_adcget(struct socket *sk, struct dcpeer *peer, char *cmd, char *
     
     if(peer->transfer == NULL)
     {
-       freedcpeer(peer);
+       peer->close = 1;
        return;
     }
     if((argv = parseadc(args)) == NULL)
     {
-       freedcpeer(peer);
+       peer->close = 1;
        return;
     }
     if(parrlen(argv) < 4)
     {
-       freedcpeer(peer);
+       peer->close = 1;
        goto out;
     }
-    start = atoi(argv[2]);
-    numbytes = atoi(argv[3]);
+    start = strtoll(argv[2], NULL, 10);
+    numbytes = strtoll(argv[3], NULL, 10);
     node = NULL;
     fd = -1;
     if(((fd = openfilelist(argv[1])) < 0) && (errno != 0))
@@ -1999,16 +2116,12 @@ static void cmd_adcget(struct socket *sk, struct dcpeer *peer, char *cmd, char *
        qstr(sk, "$Error Could not send file list|");
        goto out;
     } else if(fd >= 0) {
-       if((wbuf = icsmbstowcs(argv[1], "UTF-8", NULL)) != NULL)
+       if((wbuf = adc2path(argv[1])) != NULL)
            transfersetpath(peer->transfer, wbuf);
+       peer->transfer->flags.b.minislot = 1;
     }
     if(fd < 0)
     {
-       if(slotsleft() < 1)
-       {
-           qstr(sk, "$MaxedOut|");
-           goto out;
-       }
        if(!strncmp(argv[1], "TTH/", 4))
        {
            if((node = findbytth(argv[1] + 4)) == NULL)
@@ -2048,6 +2161,12 @@ static void cmd_adcget(struct socket *sk, struct dcpeer *peer, char *cmd, char *
            qstr(sk, "$Error|");
            goto out;
        }
+       if(sb.st_size < 65536)
+           peer->transfer->flags.b.minislot = 1;
+       if(!peer->transfer->flags.b.minislot && (slotsleft() < 1)) {
+           qstr(sk, "$MaxedOut|");
+           goto out;
+       }
        if((start != 0) && ((start >= sb.st_size) || (lseek(fd, start, SEEK_SET) < 0)))
        {
            qstr(sk, "$Error Offset out of range|");
@@ -2062,8 +2181,8 @@ static void cmd_adcget(struct socket *sk, struct dcpeer *peer, char *cmd, char *
        qstr(sk, "$ADCSND");
        sendadc(sk, "file");
        sendadc(sk, argv[1]);
-       sendadcf(sk, "%i", start);
-       sendadcf(sk, "%i", numbytes);
+       sendadcf(sk, "%ji", (intmax_t)start);
+       sendadcf(sk, "%ji", (intmax_t)numbytes);
        if(peer->compress == CPRS_ZLIB)
            sendadc(sk, "ZL1");
        qstr(sk, "|");
@@ -2111,6 +2230,8 @@ static void handletthl(struct dcpeer *peer)
     }
     if(peer->curread >= peer->totalsize)
     {
+       if(peer->timeout == NULL)
+           peer->timeout = timercallback(ntime() + 180, (void (*)(int, void *))peertimeout, peer);
        peer->state = PEER_CMD;
        synctigertree(&peer->tth);
        restigertree(&peer->tth, buf);
@@ -2122,33 +2243,35 @@ static void handletthl(struct dcpeer *peer)
 static void cmd_adcsnd(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
 {
     char **argv;
-    int start, numbytes;
+    off_t start, numbytes;
     
     if(peer->transfer == NULL)
     {
-       freedcpeer(peer);
+       peer->close = 1;
        return;
     }
     if((argv = parseadc(args)) == NULL)
     {
-       freedcpeer(peer);
+       peer->close = 1;
        return;
     }
     if(parrlen(argv) < 4)
     {
-       freedcpeer(peer);
+       peer->close = 1;
        goto out;
     }
-    start = atoi(argv[2]);
-    numbytes = atoi(argv[3]);
+    start = strtoll(argv[2], NULL, 10);
+    numbytes = strtoll(argv[3], NULL, 10);
     if(!strcmp(argv[0], "tthl"))
     {
        if((start != 0) || (numbytes % 24 != 0))
        {
            /* Weird. Bail out. */
-           freedcpeer(peer);
+           peer->close = 1;
            goto out;
        }
+       if(peer->timeout != NULL)
+           canceltimer(peer->timeout);
        peer->state = PEER_TTHL;
        peer->totalsize = numbytes;
        peer->curread = 0;
@@ -2158,13 +2281,13 @@ static void cmd_adcsnd(struct socket *sk, struct dcpeer *peer, char *cmd, char *
     } else if(!strcmp(argv[0], "file")) {
        if(start != peer->transfer->curpos)
        {
-           freedcpeer(peer);
+           peer->close = 1;
            goto out;
        }
        if(start + numbytes != peer->transfer->size)
        {
            transfersetsize(peer->transfer, start + numbytes);
-           freedcpeer(peer);
+           peer->close = 1;
            goto out;
        }
        startdl(peer);
@@ -2172,11 +2295,10 @@ static void cmd_adcsnd(struct socket *sk, struct dcpeer *peer, char *cmd, char *
        {
            sockpushdata(sk, peer->inbuf, peer->inbufdata);
            peer->inbufdata = 0;
-           transread(sk, peer);
        }
     } else {
        /* We certainly didn't request this...*/
-       freedcpeer(peer);
+       peer->close = 1;
        goto out;
     }
     
@@ -2186,18 +2308,18 @@ static void cmd_adcsnd(struct socket *sk, struct dcpeer *peer, char *cmd, char *
 
 static void cmd_sending(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
 {
-    int numbytes;
+    off_t numbytes;
     
     if(peer->transfer == NULL)
     {
-       freedcpeer(peer);
+       peer->close = 1;
        return;
     }
-    numbytes = atoi(args);
+    numbytes = strtoll(args, NULL, 10);
     if(peer->transfer->size - peer->transfer->curpos != numbytes)
     {
        transfersetsize(peer->transfer, peer->transfer->curpos + numbytes);
-       freedcpeer(peer);
+       peer->close = 1;
        return;
     }
     startdl(peer);
@@ -2205,7 +2327,6 @@ static void cmd_sending(struct socket *sk, struct dcpeer *peer, char *cmd, char
     {
        sockpushdata(sk, peer->inbuf, peer->inbufdata);
        peer->inbufdata = 0;
-       transread(sk, peer);
     }
 }
 
@@ -2241,14 +2362,14 @@ 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)
     {
-       sendctm(peer->fn->sk, mbsnick);
+       sendctm(hub->sk, mbsnick);
        expectpeer(mbsnick, peer->fn);
     } else {
-       qstrf(peer->fn->sk, "$RevConnectToMe %s %s|", hub->nativenick, mbsnick);
+       qstrf(hub->sk, "$RevConnectToMe %s %s|", hub->nativenick, mbsnick);
     }
     free(mbsnick);
     return(0);
@@ -2260,12 +2381,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);
@@ -2288,12 +2409,12 @@ static int hubsendchat(struct fnetnode *fn, int public, wchar_t *to, wchar_t *st
     {
        if(*to == L'\0')
        {
-           qstrf(fn->sk, "<%s> %s|", hub->nativenick, mbsstring);
+           qstrf(hub->sk, "<%s> %s|", hub->nativenick, mbsstring);
        } else {
-           qstrf(fn->sk, "$To: %s From: %s $<%s> %s|", mbsto, hub->nativenick, hub->nativenick, mbsstring);
+           qstrf(hub->sk, "$To: %s From: %s $<%s> %s|", mbsto, hub->nativenick, hub->nativenick, mbsstring);
        }
     } else {
-       qstrf(fn->sk, "$To: %s From: %s $<%s> %s|", mbsto, hub->nativenick, hub->nativenick, mbsstring);
+       qstrf(hub->sk, "$To: %s From: %s $<%s> %s|", mbsto, hub->nativenick, hub->nativenick, mbsstring);
     }
     free(mbsto);
     free(mbsstring);
@@ -2380,8 +2501,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;
@@ -2406,7 +2529,7 @@ static int hubsearch(struct fnetnode *fn, struct search *srch, struct srchfnnlis
     struct hash *hash;
     
     hub = fn->data;
-    if((fn->state != FNN_EST) || (fn->sk == NULL) || (fn->sk->state != SOCK_EST))
+    if((fn->state != FNN_EST) || (hub->sk == NULL) || (hub->sk->state != SOCK_EST))
        return(1);
     list = findsexprstrs(srch->sexpr);
     findsizelimit(srch->sexpr, &minsize, &maxsize);
@@ -2435,10 +2558,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);
        }
@@ -2446,6 +2569,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
@@ -2483,15 +2607,15 @@ 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, hub->sk, &name, &namelen) < 0)
        {
            flog(LOG_WARNING, "cannot get address of UDP socket");
        } else {
-           qstrf(fn->sk, "$Search %s %s|", formataddress(name, namelen), sstr);
+           qstrf(hub->sk, "$Search %s %s|", formataddress(name, namelen), sstr);
            free(name);
        }
     } else {
-       qstrf(fn->sk, "$Search Hub:%s %s|", hub->nativenick, sstr);
+       qstrf(hub->sk, "$Search Hub:%s %s|", hub->nativenick, sstr);
     }
     free(sstr);
     freesl(&list);
@@ -2504,7 +2628,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)},
@@ -2514,20 +2638,21 @@ struct command hubcmds[] =
     {"$OpList", cc(cmd_oplist)},
     {"$MyINFO", cc(cmd_myinfo)},
     {"$ForceMove", cc(cmd_forcemove)},
-    {"$Search", cc(cmd_search)},
-    {"$MultiSearch", cc(cmd_search)},
-    {"$ConnectToMe", cc(cmd_connecttome)},
-    {"$RevConnectToMe", cc(cmd_revconnecttome)},
+    {"$Search", cc(cmd_search), .limit = 100},
+    {"$MultiSearch", cc(cmd_search), .limit = 50},
+    {"$ConnectToMe", cc(cmd_connecttome), .limit = 200},
+    {"$RevConnectToMe", cc(cmd_revconnecttome), .limit = 500},
     {"$GetNetInfo", cc(cmd_getnetinfo)},
     {"$To:", cc(cmd_to)},
     {"$SR", cc(cmd_sr)},
     {"$UserCommand", cc(cmd_usercommand)},
     {"$GetPass", cc(cmd_getpass)},
     {"$LogedIn", cc(cmd_logedin)}, /* sic */
+    {"$Supports", cc(cmd_hubsupports)},
     {NULL, NULL}
 };
 
-struct command peercmds[] =
+static struct command peercmds[] =
 {
     {"$MyNick", cc(cmd_mynick)},
     {"$Lock", cc(cmd_peerlock)},
@@ -2544,32 +2669,39 @@ struct command peercmds[] =
     {"$GetZBlock", cc(cmd_getblock)},
     {"$UGetZBlock", cc(cmd_getblock)},
     {"$ADCGET", cc(cmd_adcget)},
-    {"$ADCSND", cc(cmd_adcsnd), 1},
-    {"$Sending", cc(cmd_sending), 1},
+    {"$ADCSND", cc(cmd_adcsnd), .stop = 1},
+    {"$Sending", cc(cmd_sending), .stop = 1},
     {NULL, NULL}
 };
 #undef cc
 
+static struct socket *mktrpipe(struct dcpeer *peer)
+{
+    struct socket *sk;
+    
+    sk = netsockpipe();
+    sk->data = peer;
+    return(sk);
+}
+
 static void dctransdetach(struct transfer *transfer, struct dcpeer *peer)
 {
     CBUNREG(transfer, trans_filterout, peer);
-    if(peer->freeing)
-       return;
     peer->transfer = NULL;
-    freedcpeer(peer);
+    peer->close = 1;
 }
 
 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;
     
     if((peer->state == PEER_TRNS) || (peer->state == PEER_SYNC))
     {
-       if(sockqueuesize(peer->sk) < 65536)
+       if(sockqueueleft(peer->sk) > 0)
        {
            if((buf = transfergetdata(transfer, &bufsize)) != NULL)
            {
@@ -2620,10 +2752,13 @@ static void dctransgotdata(struct transfer *transfer, struct dcpeer *peer)
                {
                    freedcpeer(peer);
                } else {
+                   if(peer->timeout == NULL)
+                       peer->timeout = timercallback(ntime() + 180, (void (*)(int, void *))peertimeout, peer);
                    peer->state = PEER_CMD;
                    endcompress(peer);
                    transfersetstate(transfer, TRNS_HS);
                    socksettos(peer->sk, confgetint("fnet", "fnptos"));
+                   transfer->flags.b.minislot = 0;
                    peer->sk->writecb = NULL;
                }
            }
@@ -2637,18 +2772,14 @@ static void dctransendofdata(struct transfer *transfer, struct dcpeer *peer)
     dctransgotdata(transfer, peer);
 }
 
-static void dcwantdata(struct transfer *transfer, struct dcpeer *peer)
-{
-    if(transferdatasize(transfer) < 65536)
-       peer->sk->ignread = 0;
-}
-
 static void transread(struct socket *sk, struct dcpeer *peer)
 {
     void *buf;
     size_t bufsize;
     struct transfer *transfer;
     
+    if(transferdatasize(peer->transfer) < 0)
+       return;
     if((buf = sockgetinbuf(sk, &bufsize)) == NULL)
        return;
     if(peer->transfer == NULL)
@@ -2666,8 +2797,12 @@ static void transread(struct socket *sk, struct dcpeer *peer)
        transferendofdata(transfer);
        return;
     }
-    if(transferdatasize(peer->transfer) > 65535)
-       sk->ignread = 1;
+}
+
+static void dcwantdata(struct transfer *transfer, struct dcpeer *peer)
+{
+    if(transferdatasize(transfer) > 0)
+       transread(peer->sk, peer);
 }
 
 static void transerr(struct socket *sk, int err, struct dcpeer *peer)
@@ -2700,7 +2835,10 @@ static void udpread(struct socket *sk, void *data)
     char *buf, *p, *p2, *hashbuf;
     size_t buflen, hashlen;
     char *nick, *filename, *hubname;
-    int size, slots;
+    struct sockaddr_in hubaddr;
+    struct sockaddr *addrbuf;
+    off_t size;
+    int slots;
     struct fnetnode *fn, *myfn;
     struct dchub *hub;
     struct srchres *sr;
@@ -2736,7 +2874,7 @@ static void udpread(struct socket *sk, void *data)
            return;
        }
        *p2 = 0;
-       size = atoi(p);
+       size = strtoll(p, NULL, 10);
        p = p2 + 1;
        if((p2 = strchr(p, '/')) == NULL)
        {
@@ -2759,14 +2897,30 @@ static void udpread(struct socket *sk, void *data)
            return;
        }
        *p2 = 0;
-       if((wnick = icmbstowcs(nick, DCCHARSET)) == NULL)
+       p = p2 + 2;
+       if((p2 = strchr(p, ':')) == NULL)
+       {
+           free(buf);
+           return;
+       }
+       *(p2++) = 0;
+       hubaddr.sin_family = AF_INET;
+       if(!inet_aton(p, &hubaddr.sin_addr))
+       {
+           free(buf);
+           return;
+       }
+       p = p2;
+       if((p2 = strchr(p, ')')) == NULL)
        {
            free(buf);
            return;
        }
-       if((wfile = icmbstowcs(filename, DCCHARSET)) == NULL)
+       *p2 = 0;
+       hubaddr.sin_port = htons(atoi(p));
+       /* Use DCCHARSET in $Get paths until further researched... */
+       if((wfile = nmdc2path(filename, DCCHARSET)) == NULL)
        {
-           free(wnick);
            free(buf);
            return;
        }
@@ -2798,6 +2952,30 @@ static void udpread(struct socket *sk, void *data)
                }
            }
        }
+       if(myfn == NULL)
+       {
+           for(fn = fnetnodes; fn != NULL; fn = fn->next)
+           {
+               if((fn->fnet == &dcnet) && ((hub = fn->data) != NULL) && !sockpeeraddr(hub->sk, &addrbuf, NULL))
+               {
+                   if((hub->sk != NULL) && addreq(addrbuf, (struct sockaddr *)&hubaddr))
+                   {
+                       myfn = fn;
+                       free(addrbuf);
+                       break;
+                   }
+                   free(addrbuf);
+               }
+           }
+       }
+       hub = NULL;
+       if(myfn != NULL)
+           hub = myfn->data;
+       if((wnick = icmbstowcs(nick, (hub == NULL)?DCCHARSET:(hub->charset))) == NULL)
+       {
+           free(buf);
+           return;
+       }
        sr = newsrchres(&dcnet, wfile, wnick);
        if(sr->peernick != NULL)
            free(sr->peernick);
@@ -2819,11 +2997,14 @@ static void udpread(struct socket *sk, void *data)
 static void hubread(struct socket *sk, struct fnetnode *fn)
 {
     struct dchub *hub;
+    struct command *cmd;
     char *newbuf;
-    size_t datalen;
-    char *p;
+    size_t datalen, cnlen;
+    char *p, *p2;
     
     hub = (struct dchub *)fn->data;
+    if(hub->queue.size > 1000)
+       return;
     if((newbuf = sockgetinbuf(sk, &datalen)) == NULL)
        return;
     if(hub->inbufdata > 500000) /* Discard possible malicious data */
@@ -2831,16 +3012,22 @@ static void hubread(struct socket *sk, struct fnetnode *fn)
     sizebuf2(hub->inbuf, hub->inbufdata + datalen, 1);
     memcpy(hub->inbuf + hub->inbufdata, newbuf, datalen);
     free(newbuf);
-    p = hub->inbuf + hub->inbufdata;
+    p = hub->inbuf;
     hub->inbufdata += datalen;
-    while((datalen > 0) && ((p = memchr(p, '|', datalen)) != NULL))
+    while((p - hub->inbuf < hub->inbufdata) && ((p2 = memchr(p, '|', hub->inbufdata - (p - hub->inbuf))) != NULL))
     {
-       *(p++) = 0;
-       newqcmd(&hub->queue, hub->inbuf);
-       memmove(hub->inbuf, p, hub->inbufdata -= p - hub->inbuf);
-       datalen = hub->inbufdata;
-       p = hub->inbuf;
+       *(p2++) = 0;
+       for(cmd = hubcmds; cmd->handler != NULL; cmd++)
+       {
+           cnlen = strlen(cmd->name);
+           if(!strncmp(p, cmd->name, cnlen) && ((p[cnlen] == ' ') || (p[cnlen] == 0)))
+               break;
+       }
+       if((cmd->limit == 0) || (hub->queue.size < cmd->limit))
+           newqcmd(&hub->queue, p);
+       p = p2;
     }
+    memmove(hub->inbuf, p, hub->inbufdata -= p - hub->inbuf);
 }
 
 static void huberr(struct socket *sk, int err, struct fnetnode *fn)
@@ -2854,7 +3041,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))
     {
@@ -2875,10 +3062,38 @@ static int hubsetnick(struct fnetnode *fn, wchar_t *newnick)
 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));
     fn->data = new;
+    if(confgetint("dc", "dcppemu"))
+       new->dcppemu = 1;
+    if((emu = wpfind(fn->args, L"dcppemu")) != NULL)
+    {
+       if(*emu == L'y')
+           new->dcppemu = 1;
+       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 */
@@ -2894,6 +3109,8 @@ static struct dcpeer *newdcpeer(struct socket *sk)
     new->transfer = NULL;
     getsock(sk);
     new->sk = sk;
+    if(confgetint("dc", "dcppemu"))
+       new->dcppemu = 1;
     new->next = peers;
     new->prev = NULL;
     if(peers != NULL)
@@ -2908,21 +3125,25 @@ static void freedcpeer(struct dcpeer *peer)
     int i;
     struct qcommand *qcmd;
     
-    peer->freeing = 1;
     if(peers == peer)
        peers = peer->next;
     if(peer->next != NULL)
        peer->next->prev = peer->prev;
     if(peer->prev != NULL)
        peer->prev->next = peer->next;
+    if(peer->trpipe != NULL) {
+       closesock(peer->trpipe);
+       putsock(peer->trpipe);
+    }
     if(peer->transfer != NULL)
     {
        if(peer->transfer->dir == TRNSD_UP)
            peer->transfer->close = 1;
        if(peer->transfer->dir == TRNSD_DOWN)
            resettransfer(peer->transfer);
-       transferdetach(peer->transfer);
     }
+    if(peer->timeout != NULL)
+       canceltimer(peer->timeout);
     if(peer->sk->data == peer)
        peer->sk->data = NULL;
     peer->sk->readcb = NULL;
@@ -2944,6 +3165,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)
@@ -2952,78 +3175,75 @@ static void freedcpeer(struct dcpeer *peer)
     numdcpeers--;
 }
 
-static void hubconnect(struct fnetnode *fn)
+static void hubconnect(struct fnetnode *fn, struct socket *sk)
 {
-    fn->sk->readcb = (void (*)(struct socket *, void *))hubread;
-    fn->sk->errcb = (void (*)(struct socket *, int, void *))huberr;
-    getfnetnode(fn);
-    fn->data = newdchub(fn);
-    fn->sk->data = fn;
+    struct dchub *hub;
+    
+    sk->readcb = (void (*)(struct socket *, void *))hubread;
+    sk->errcb = (void (*)(struct socket *, int, void *))huberr;
+    fn->data = hub = newdchub(fn);
+    sk->data = fn;
+    getsock(hub->sk = sk);
     return;
 }
 
 static void hubdestroy(struct fnetnode *fn)
 {
+    int i;
     struct dchub *hub;
     struct qcommand *qcmd;
     
     hub = (struct dchub *)fn->data;
-    if(fn->sk != NULL)
-    {
-       if(fn->sk->data == fn)
-       {
-           fn->sk->data = NULL;
-           putfnetnode(fn);
-       }
-    }
-    if(hub == NULL)
-       return;
+    putsock(hub->sk);
     while((qcmd = ulqcmd(&hub->queue)) != NULL)
        freeqcmd(qcmd);
+    if(hub->supports != NULL)
+    {
+       for(i = 0; hub->supports[i] != NULL; i++)
+           free(hub->supports[i]);
+       free(hub->supports);
+    }
     if(hub->nativename != NULL)
        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);
 }
 
-static wchar_t *dcbasename(wchar_t *filename)
+static void hubkill(struct fnetnode *fn)
 {
-    wchar_t *ret;
+    struct dchub *hub;
     
-    if((ret = wcsrchr(filename, L'\\')) != NULL)
-       return(ret + 1);
-    return(filename);
+    hub = (struct dchub *)fn->data;
+    closesock(hub->sk);
 }
 
-static struct transferiface dctransfer =
-{
-    .detach = (void (*)(struct transfer *, void *))dctransdetach,
-    .gotdata = (void (*)(struct transfer *, void *))dctransgotdata,
-    .endofdata = (void (*)(struct transfer *, void *))dctransendofdata,
-    .wantdata = (void (*)(struct transfer *, void *))dcwantdata
-};
-
 static struct fnet dcnet =
 {
     .name = L"dc",
     .connect = hubconnect,
     .destroy = hubdestroy,
+    .kill = hubkill,
     .setnick = hubsetnick,
     .reqconn = hubreqconn,
     .sendchat = hubsendchat,
     .search = hubsearch,
-    .filebasename = dcbasename
 };
 
 static void peerread(struct socket *sk, struct dcpeer *peer)
 {
     char *newbuf, *p;
-    size_t datalen;
+    size_t datalen, cnlen;
     struct command *cmd;
 
+    if(peer->state == PEER_CMD) {
+       if((peer->queue.size > 50) || (peer->inbufdata > 65536))
+           return;
+    }
     if((newbuf = sockgetinbuf(sk, &datalen)) == NULL)
        return;
     sizebuf2(peer->inbuf, peer->inbufdata + datalen, 1);
@@ -3036,12 +3256,14 @@ static void peerread(struct socket *sk, struct dcpeer *peer)
        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)] == '|')))
+               cnlen = strlen(cmd->name);
+               if(!strncmp(peer->inbuf, cmd->name, cnlen) && ((peer->inbuf[cnlen] == ' ') || (peer->inbuf[cnlen] == 0)))
                    break;
            }
+           if((cmd->limit == 0) || (peer->queue.size < cmd->limit))
+               newqcmd(&peer->queue, peer->inbuf);
            memmove(peer->inbuf, p, peer->inbufdata -= p - peer->inbuf);
            if(cmd->stop)
            {
@@ -3062,25 +3284,30 @@ static void peererror(struct socket *sk, int err, struct dcpeer *peer)
 static void peerconnect(struct socket *sk, int err, struct fnetnode *fn)
 {
     struct dcpeer *peer;
+    struct dchub *hub;
     
     if(err != 0)
     {
        putfnetnode(fn);
+       putsock(sk);
        return;
     }
+    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;
     socksettos(sk, confgetint("fnet", "fnptos"));
     putsock(sk);
+    peer->timeout = timercallback(ntime() + 180, (void (*)(int, void *))peertimeout, peer);
     sendmynick(peer);
     sendpeerlock(peer);
 }
 
-static void peeraccept(struct socket *sk, struct socket *newsk, void *data)
+static void peeraccept(struct lport *lp, struct socket *newsk, void *data)
 {
     struct dcpeer *peer;
     
@@ -3090,6 +3317,7 @@ static void peeraccept(struct socket *sk, struct socket *newsk, void *data)
     newsk->errcb = (void (*)(struct socket *, int, void *))peererror;
     newsk->data = peer;
     socksettos(newsk, confgetint("fnet", "fnptos"));
+    peer->timeout = timercallback(ntime() + 180, (void (*)(int, void *))peertimeout, peer);
 }
 
 static void updatehmlist(void)
@@ -3099,6 +3327,7 @@ static void updatehmlist(void)
     char *buf, *buf2, numbuf[32];
     size_t bufsize, bufdata;
     int fd, ibuf;
+    FILE *out;
     
     bufdata = 0;
     buf = smalloc(bufsize = 65536);
@@ -3107,6 +3336,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++)
@@ -3116,7 +3346,7 @@ static void updatehmlist(void)
            if(node->f.b.type == FILE_REG)
            {
                addtobuf(buf, '|');
-               sprintf(numbuf, "%i", node->size);
+               sprintf(numbuf, "%ji", (intmax_t)node->size);
                bufcat(buf, numbuf, strlen(numbuf));
            }
            addtobuf(buf, 13);
@@ -3155,27 +3385,28 @@ static void updatehmlist(void)
        free(hmlistname);
        hmlistname = NULL;
     } else {
+       out = fdopen(fd, "w");
        /*
         * I do not want to implement a good Huffman encoder, and it's not
         * like Huffman encoding actually yields any impressive results
         * for DC file lists anyway, so I'll just output a bogus
         * tree. Implement a good encoder if you want to.
         */
-       write(fd, "HE3\r\0", 5);
-       write(fd, &bufdata, 4);
+       fwrite("HE3\r\0", 1, 5, out);
+       fwrite(&bufdata, 4, 1, out); /* XXX: Endian unsafe */
        ibuf = 256;
-       write(fd, &ibuf, 2);
+       fwrite(&ibuf, 2, 1, out);
        ibuf = 8;
        for(i = 0; i < 256; i++)
        {
-           write(fd, &i, 1);
-           write(fd, &ibuf, 1);
+           fwrite(&i, 1, 1, out);
+           fwrite(&ibuf, 1, 1, out);
        }
        for(i = 0; i < 256; i++)
-           write(fd, &i, 1);
+           fwrite(&i, 1, 1, out);
        for(buf2 = buf; bufdata > 0;)
        {
-           if((ret = write(fd, buf2, bufdata)) < 0)
+           if((ret = fwrite(buf2, 1, bufdata, out)) < 0)
            {
                flog(LOG_WARNING, "could not write file list: %s", strerror(errno));
                break;
@@ -3183,7 +3414,7 @@ static void updatehmlist(void)
            bufdata -= ret;
            buf2 += ret;
        }
-       close(fd);
+       fclose(out);
     }
     free(buf);
 }
@@ -3271,7 +3502,10 @@ static void updatexmllist(void)
     for(i = 0; i < sizeof(cidbuf) - 1; i++)
        cidbuf[i] = (rand() % ('Z' - 'A' + 1)) + 'A';
     cidbuf[i] = 0;
-    fprintf(fs, "<FileListing Version=\"1\" CID=\"%s\" Base=\"/\" Generator=\"%s\">\r\n", cidbuf, DCIDFULL);
+    if(confgetint("dc", "dcppemu"))
+       fprintf(fs, "<FileListing Version=\"1\" CID=\"%s\" Base=\"/\" Generator=\"DC++ 0.674\">\r\n", cidbuf);
+    else
+       fprintf(fs, "<FileListing Version=\"1\" CID=\"%s\" Base=\"/\" Generator=\"%s\">\r\n", cidbuf, "DoldaConnect" VERSION);
     
     node = shareroot->child;
     lev = 0;
@@ -3288,7 +3522,7 @@ static void updatexmllist(void)
                lev++;
                continue;
            } else {
-               fprintf(fs, "<File Name=\"%s\" Size=\"%i\"", namebuf, node->size);
+               fprintf(fs, "<File Name=\"%s\" Size=\"%ji\"", namebuf, (intmax_t)node->size);
                if(node->f.b.hastth)
                {
                    hashbuf = base32encode(node->hashtth, 24);
@@ -3316,11 +3550,10 @@ static void updatexmllist(void)
        }
     }
     
-#ifdef DCPP_MASQUERADE
-    fprintf(fs, "</FileListing>");
-#else
-    fprintf(fs, "</FileListing>\r\n");
-#endif
+    if(confgetint("dc", "dcppemu"))
+       fprintf(fs, "</FileListing>");
+    else
+       fprintf(fs, "</FileListing>\r\n");
     fclose(fs);
 }
 
@@ -3385,14 +3618,108 @@ 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() + confgetint("cli", "hashwritedelay"), listtimercb, NULL);
+       return;
+    }
+    if(listwritetimer != NULL)
+       canceltimer(listwritetimer);
     updatehmlist();
     updatexmllist();
     updatexmlbz2list();
+}
+
+static int shareupdate(unsigned long long uusharesize, void *data)
+{
+    updatelists(0);
     return(0);
 }
 
+static char *quotestr(char *str)
+{
+    unsigned char *buf;
+    unsigned char *p;
+    size_t bufsize, bufdata;
+    wchar_t *wbuf;
+    static char *enc = NULL;
+    size_t encsize, encdata;
+    
+    buf = NULL;
+    bufsize = bufdata = 0;
+    for(p = (unsigned char *)str; *p; p++)
+    {
+       if(*p == '\b')
+           bufcat(buf, "\\b", 2);
+       else if(*p == '\t')
+           bufcat(buf, "\\t", 2);
+       else if(*p == '\n')
+           bufcat(buf, "\\n", 2);
+       else if(*p == '\r')
+           bufcat(buf, "\\r", 2);
+       else if(*p == '\\')
+           bufcat(buf, "\\\\", 2);
+       else if(*p >= 32)
+           addtobuf(buf, *p);
+       else
+           bprintf(buf, "\\x%02x", *p);
+    }
+    addtobuf(buf, 0);
+    if(enc != NULL)
+       free(enc);
+    enc = NULL;
+    if((wbuf = icmbstowcs((char *)buf, DCCHARSET)) != NULL)
+    {
+       enc = icwcstombs(wbuf, NULL);
+       free(wbuf);
+    }
+    if(enc == NULL)
+    {
+       encsize = encdata = 0;
+       for(p = buf; *p; p++) {
+           if(*p < 128)
+               addtobuf(enc, *p);
+           else
+               bprintf(buf, "\\x%x", *p);
+       }
+    }
+    free(buf);
+    return(enc);
+}
+
+static void logunimpl(char *list, char *cmd, char *args)
+{
+    FILE *log;
+    
+    if((log = fopen("/tmp/dc-unimpl", "a")) == NULL)
+    {
+       flog(LOG_WARNING, "could not open unimpl log: %s", strerror(errno));
+       return;
+    }
+    fputs(list, log);
+    fputc('\t', log);
+    fputs(quotestr(cmd), log);
+    if(args != NULL)
+    {
+       fputc('\t', log);
+       fputs(quotestr(args), log);
+    }
+    fputc('\n', log);
+    fclose(log);
+}
+
 static void dispatchcommand(struct qcommand *qcmd, struct command *cmdlist, struct socket *sk, void *data)
 {
     char *p;
@@ -3406,11 +3733,16 @@ static void dispatchcommand(struct qcommand *qcmd, struct command *cmdlist, stru
            break;
     }
     if(cmd->handler != NULL)
+    {
        cmd->handler(sk, data, qcmd->string, p);
-/*
-    else
-       flog(LOG_DEBUG, "Unimplemented DC command: %s \"%s\"", qcmd->string, p?p:"noargs");
-*/
+    } else if(confgetint("dc", "logunimpl")) {
+       if(cmdlist == hubcmds)
+           logunimpl("hub", qcmd->string, p);
+       else if(cmdlist == peercmds)
+           logunimpl("peer", qcmd->string, p);
+       else
+           logunimpl("other?!", qcmd->string, p);
+    }
 }
 
 static int run(void)
@@ -3419,9 +3751,10 @@ static int run(void)
     struct dchub *hub;
     struct dcpeer *peer, *nextpeer;
     struct qcommand *qcmd;
-    int ret;
+    int ret, quota;
     
     ret = 0;
+    quota = 20;
     for(fn = fnetnodes; fn != NULL; fn = nextfn)
     {
        nextfn = fn->next;
@@ -3430,31 +3763,48 @@ static int run(void)
        if(fn->data == NULL)
            continue;
        hub = (struct dchub *)fn->data;
-       if((qcmd = ulqcmd(&hub->queue)) != NULL)
+       while((quota > 0) && ((qcmd = ulqcmd(&hub->queue)) != NULL))
        {
            if(*qcmd->string == '$')
            {
-               if((fn->sk != NULL) && (fn->sk->state == SOCK_EST))
-                   dispatchcommand(qcmd, hubcmds, fn->sk, fn);
+               if((hub->sk != NULL) && (hub->sk->state == SOCK_EST))
+                   dispatchcommand(qcmd, hubcmds, hub->sk, fn);
            } else if(*qcmd->string != 0) {
-               hubrecvchat(fn->sk, fn, NULL, qcmd->string);
+               hubrecvchat(hub->sk, fn, NULL, qcmd->string);
            }
            freeqcmd(qcmd);
            ret = 1;
-           break;
+           quota--;
        }
+       if(hub->queue.size < 1000)
+           hubread(hub->sk, fn);
+       if(quota < 1)
+           break;
     }
-    for(peer = peers; peer != NULL; peer = nextpeer)
+    quota = 20;
+    for(peer = peers; peer != NULL; peer = peer->next)
     {
-       nextpeer = peer->next;
-       if((qcmd = ulqcmd(&peer->queue)) != NULL)
+       while(!peer->close && (quota > 0) && ((qcmd = ulqcmd(&peer->queue)) != NULL))
        {
+           if(peer->timeout != NULL)
+               canceltimer(peer->timeout);
+           peer->timeout = timercallback(ntime() + 180, (void (*)(int, void *))peertimeout, peer);
            if(*qcmd->string == '$')
                dispatchcommand(qcmd, peercmds, peer->sk, peer);
            freeqcmd(qcmd);
            ret = 1;
-           break;
+           quota--;
        }
+       if((peer->queue.size < 50) && (peer->inbufdata < 500000))
+           peerread(peer->sk, peer);
+       if(quota < 1)
+           break;
+    }
+    for(peer = peers; peer != NULL; peer = nextpeer)
+    {
+       nextpeer = peer->next;
+       if(peer->close)
+           freedcpeer(peer);
     }
     return(ret);
 }
@@ -3489,7 +3839,7 @@ static int updateudpport(struct configvar *var, void *uudata)
 static int updatetcpport(struct configvar *var, void *uudata)
 {
     struct sockaddr_in addr;
-    struct socket *newsock;
+    struct lport *newsock;
     
     memset(&addr, 0, sizeof(addr));
     addr.sin_family = AF_INET;
@@ -3497,7 +3847,7 @@ static int updatetcpport(struct configvar *var, void *uudata)
     if((newsock = 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");
     if(tcpsock != NULL)
-       putsock(tcpsock);
+       closelport(tcpsock);
     tcpsock = newsock;
     return(0);
 }
@@ -3512,7 +3862,7 @@ static int init(int hup)
        if(udpsock != NULL)
            putsock(udpsock);
        if(tcpsock != NULL)
-           putsock(tcpsock);
+           closelport(tcpsock);
        addr.sin_family = AF_INET;
        memset(&addr.sin_addr, 0, sizeof(addr.sin_addr));
        addr.sin_port = htons(confgetint("dc", "udpport"));
@@ -3553,11 +3903,41 @@ static void terminate(void)
 
 static struct configvar myvars[] =
 {
+    /** Specifies the share description reported to other DC users. */
     {CONF_VAR_STRING, "desc", {.str = L""}},
+    /** Specifies the speed reported to other DC users. Normal values
+     * are 28.8Kbps, 33.6Kbps, 56Kbps, Satellite, ISDN, DSL, Cable,
+     * LAN(T1) or LAN(T3)*/
     {CONF_VAR_STRING, "speedstring", {.str = L"LAN(T1)"}},
+    /** The e-mail address to report to other DC users. */
     {CONF_VAR_STRING, "email", {.str = L"spam@spam.org"}},
+    /** Specifies a specific UDP port to use for DC search results. If
+     * left unspecified, a port is allocated dynamically. Useful for
+     * NAT routers (see also the net.visibleipv4 address for those
+     * cases). */
     {CONF_VAR_INT, "udpport", {.num = 0}},
+    /** Specifies a specific TCP port to use for DC peer
+     * connections. If left unspecified, a port is allocated
+     * dynamically. Useful for NAT routers (see also the
+     * net.visibleipv4 address for those cases). */
     {CONF_VAR_INT, "tcpport", {.num = 0}},
+    /** If set to true, doldacond will do its best to emulate DC++
+     * (currently v0.674). This should be left off if at all possible,
+     * since turning it on will violate the rules of most hubs and
+     * thus give hub owners an actual reason to kick you if it is
+     * detected. It might be needed for some of the more bone-headed
+     * hub owners, though. Note that DC++ emulation can also be turned
+     * on or off for individual hubs, overriding this setting. */
+    {CONF_VAR_BOOL, "dcppemu", {.num = 0}},
+    /** Use for debugging. If set to true, doldacond will log all
+     * unknown commands it receives, and their arguments, to
+     * /tmp/dc-unimpl. */
+    {CONF_VAR_BOOL, "logunimpl", {.num = 0}},
+    /** If set to true, doldacond will hide its support for deflate
+     * compression of transfers from other clients, so that they will
+     * not request compressed uploads. Compressed transfers may
+     * consume a non-trivial amount of CPU time on slower machines. */
+    {CONF_VAR_BOOL, "hidedeflate", {.num = 0}},
     {CONF_VAR_END}
 };