Properly parse search size expressions as off_t, not int.
[doldaconnect.git] / daemon / fnet-dc.c
index 284c6d9..fd7a79d 100644 (file)
@@ -30,6 +30,7 @@
 #include <bzlib.h>
 #include <zlib.h>
 #include <sys/stat.h>
+#include <stdint.h>
 
 #ifdef HAVE_CONFIG_H
 #include <config.h>
  * 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
@@ -135,10 +118,11 @@ struct dcpeer
     char *inbuf;
     size_t inbufdata, inbufsize;
     off_t curread, totalsize;
-    int freeing;
+    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 */
@@ -156,9 +140,8 @@ 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;
@@ -167,6 +150,7 @@ 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);
@@ -177,6 +161,7 @@ static void updatexmllist(void);
 static void updatexmlbz2list(void);
 static void requestfile(struct dcpeer *peer);
 static void updatelists(int now);
+static int trdestroycb(struct transfer *transfer, struct dcpeer *peer);
 
 static int reservedchar(unsigned char c)
 {
@@ -715,7 +700,7 @@ static void requestfile(struct dcpeer *peer)
        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
@@ -734,7 +719,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);
@@ -751,10 +736,9 @@ 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);
        return;
     }
     if(supports(peer, "adcget"))
@@ -764,7 +748,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);
@@ -776,7 +760,7 @@ static void requestfile(struct dcpeer *peer)
        if((buf = path2nmdc(peer->transfer->path, "UTF-8")) == NULL)
        {
            transferseterror(peer->transfer, TRNSE_NOTFOUND);
-           freedcpeer(peer);
+           peer->close = 1;
            return;
        }
        qstrf(peer->sk, "$UGetBlock %ji %ji %s|", (intmax_t)peer->transfer->curpos, (intmax_t)(peer->transfer->size - peer->transfer->curpos), buf);
@@ -786,7 +770,7 @@ static void requestfile(struct dcpeer *peer)
        if((buf = path2nmdc(peer->transfer->path, DCCHARSET)) == NULL)
        {
            transferseterror(peer->transfer, TRNSE_NOTFOUND);
-           freedcpeer(peer);
+           peer->close = 1;
            return;
        }
        qstrf(peer->sk, "$Get %s$%ji|", buf, (intmax_t)peer->transfer->curpos + 1);
@@ -888,7 +872,7 @@ static void cmd_hubname(struct socket *sk, struct fnetnode *fn, char *cmd, char
     struct dchub *hub;
     
     hub = fn->data;
-    if(hub->nativename == NULL)
+    if(hub->nativename != NULL)
        free(hub->nativename);
     hub->nativename = sstrdup(args);
     buf = icmbstowcs(args, hub->charset);
@@ -1141,7 +1125,7 @@ static void cmd_search(struct socket *sk, struct fnetnode *fn, char *cmd, char *
     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;
@@ -1162,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(hub->sk->remote, hub->sk->remotelen), args + 4);
+       postfix = sprintf2(" (%s)\005%s|", formatsockpeer(hub->sk), args + 4);
        dsk = sk;
        getsock(dsk);
     } else {
@@ -1175,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(hub->sk->remote, hub->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;
@@ -1229,8 +1213,10 @@ static void cmd_search(struct socket *sk, struct fnetnode *fn, char *cmd, char *
                    memcpy(hashtth, buf, 24);
                    free(buf);
                } else {
-                   if((terms[termnum] = icmbstowcs(p, hub->charset)) != NULL)
+                   if((terms[termnum] = icmbstowcs(p, hub->charset)) != NULL) {
+                       wcslower(terms[termnum]);
                        termnum++;
+                   }
                }
            }
            p = p2 + 1;
@@ -1262,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++;
@@ -1274,6 +1261,7 @@ static void cmd_search(struct socket *sk, struct fnetnode *fn, char *cmd, char *
                    break;
                }
            }
+           free(lname);
        }
        if(!skipcheck && (satisfied == termnum))
        {
@@ -1335,7 +1323,6 @@ static void cmd_connecttome(struct socket *sk, struct fnetnode *fn, char *cmd, c
 {
     char *p;
     struct dchub *hub;
-    struct socket *newsk;
     struct sockaddr_in addr;
     
     hub = fn->data;
@@ -1352,7 +1339,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);
+    putsock(netcsconn((struct sockaddr *)&addr, sizeof(addr), (void (*)(struct socket *, int, void *))peerconnect, fn));
     getfnetnode(fn);
     hubhandleaction(sk, fn, cmd, args);
 }
@@ -1364,7 +1351,7 @@ static void sendctm(struct socket *sk, char *nick)
     
     if(tcpsock == NULL)
        return;
-    if(sockgetremotename2(tcpsock, sk, &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));
@@ -1576,7 +1563,7 @@ static void cmd_mynick(struct socket *sk, struct dcpeer *peer, char *cmd, char *
        free(peer->wcsname);
     if((peer->wcsname = icmbstowcs(peer->nativename, peer->charset)) == NULL)
     {
-       freedcpeer(peer);
+       peer->close = 1;
        return;
     }
     if(peer->accepted)
@@ -1599,6 +1586,13 @@ static void cmd_mynick(struct socket *sk, struct dcpeer *peer, char *cmd, char *
     }
 }
 
+static void peerattach(struct dcpeer *peer, struct transfer *transfer)
+{
+    peer->transfer = transfer;
+    CBREG(peer->transfer, trans_filterout, (int (*)(struct transfer *, wchar_t *, wchar_t *, void *))trresumecb, NULL, peer);
+    CBREG(peer->transfer, trans_destroy, (int (*)(struct transfer *, void *))trdestroycb, NULL, peer);
+}
+
 static void cmd_direction(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
 {
     char *p;
@@ -1616,7 +1610,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)
@@ -1624,7 +1618,7 @@ 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;
@@ -1632,21 +1626,21 @@ static void cmd_direction(struct socket *sk, struct dcpeer *peer, char *cmd, cha
        {
            if(confgetint("transfer", "ulquota") && hasupload(&dcnet, peer->wcsname))
            {
-               freedcpeer(peer);
+               peer->close = 1;
                return;
            }
-           transfer = newupload(peer->fn, &dcnet, peer->wcsname, &dctransfer, peer);
+           transfer = newupload(peer->fn, &dcnet, peer->wcsname, (peer->trpipe = mktrpipe(peer))->back);
        } else {
            if((transfer = finddownload(peer->wcsname)) == NULL)
            {
-               freedcpeer(peer);
+               peer->close = 1;
                return;
            }
-           transferattach(transfer, &dctransfer, peer);
+           transferattach(transfer, (peer->trpipe = mktrpipe(peer))->back);
            transfersetstate(transfer, TRNS_HS);
        }
        transfersetnick(transfer, peer->wcsname);
-       peer->transfer = transfer;
+       peerattach(peer, transfer);
        if(peer->extended)
            sendsupports(peer);
        qstrf(sk, "$Direction %s %i|", (peer->direction == TRNSD_UP)?"Upload":"Download", rand() % 10000);
@@ -1672,7 +1666,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);
@@ -1683,18 +1677,18 @@ static void cmd_peerlock(struct socket *sk, struct dcpeer *peer, char *cmd, char
        {
            if(confgetint("transfer", "ulquota") && hasupload(&dcnet, peer->wcsname))
            {
-               freedcpeer(peer);
+               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))->back);
        } else {
            peer->direction = TRNSD_DOWN;
-           transferattach(transfer, &dctransfer, peer);
+           transferattach(transfer, (peer->trpipe = mktrpipe(peer))->back);
            transfersetstate(transfer, TRNS_HS);
        }
        transfersetnick(transfer, peer->wcsname);
-       peer->transfer = transfer;
+       peerattach(peer, transfer);
        qstrf(sk, "$Direction %s %i|", (peer->direction == TRNSD_UP)?"Upload":"Download", rand() % 10000);
        qstrf(sk, "$Key %s|", key);
        free(key);
@@ -1727,6 +1721,7 @@ static void startul(struct dcpeer *peer)
     peer->state = PEER_TRNS;
     transferstartul(peer->transfer, peer->sk);
     peer->sk->writecb = (void (*)(struct socket *, void *))transwrite;
+    transwrite(peer->sk, peer);
 }
 
 static void cmd_filelength(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
@@ -1736,7 +1731,7 @@ static void cmd_filelength(struct socket *sk, struct dcpeer *peer, char *cmd, ch
     
     if(peer->transfer == NULL)
     {
-       freedcpeer(peer);
+       peer->close = 1;
        return;
     }
     size = strtoll(args, NULL, 10);
@@ -1744,7 +1739,8 @@ static void cmd_filelength(struct socket *sk, struct dcpeer *peer, char *cmd, ch
     {
        transfersetsize(peer->transfer, size);
        transfer = peer->transfer;
-       freedcpeer(peer);
+       peer->close = 1;
+       resettransfer(transfer);
        trytransferbypeer(transfer->fnet, transfer->peerid);
        return;
     }
@@ -1767,7 +1763,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)
@@ -1778,7 +1774,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
@@ -1872,24 +1868,24 @@ 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 = (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 = nmdc2path(args, DCCHARSET)) != NULL) {
@@ -1904,13 +1900,13 @@ static void cmd_get(struct socket *sk, struct dcpeer *peer, char *cmd, char *arg
        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);
@@ -1925,7 +1921,7 @@ 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)
@@ -1933,14 +1929,14 @@ static void cmd_get(struct socket *sk, struct dcpeer *peer, char *cmd, char *arg
     if(!peer->transfer->flags.b.minislot && (slotsleft() < 1)) {
        close(fd);
        qstr(sk, "$MaxedOut|");
-       freedcpeer(peer);
+       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);
@@ -1953,12 +1949,12 @@ static void cmd_send(struct socket *sk, struct dcpeer *peer, char *cmd, char *ar
 {
     if(peer->transfer == NULL)
     {
-       freedcpeer(peer);
+       peer->close = 1;
        return;
     }
     if(peer->transfer->localend == NULL)
     {
-       freedcpeer(peer);
+       peer->close = 1;
        return;
     }
     peer->ptclose = 1;
@@ -2006,13 +2002,13 @@ 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;
@@ -2020,7 +2016,7 @@ static void cmd_getblock(struct socket *sk, struct dcpeer *peer, char *cmd, char
     p = p2;
     if((p2 = strchr(p, ' ')) == NULL)
     {
-       freedcpeer(peer);
+       peer->close = 1;
        return;
     }
     *(p2++) = 0;
@@ -2105,17 +2101,17 @@ 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 = strtoll(argv[2], NULL, 10);
@@ -2129,6 +2125,7 @@ static void cmd_adcget(struct socket *sk, struct dcpeer *peer, char *cmd, char *
     } else if(fd >= 0) {
        if((wbuf = adc2path(argv[1])) != NULL)
            transfersetpath(peer->transfer, wbuf);
+       free(wbuf);
        peer->transfer->flags.b.minislot = 1;
     }
     if(fd < 0)
@@ -2258,17 +2255,17 @@ static void cmd_adcsnd(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 = strtoll(argv[2], NULL, 10);
@@ -2278,7 +2275,7 @@ static void cmd_adcsnd(struct socket *sk, struct dcpeer *peer, char *cmd, char *
        if((start != 0) || (numbytes % 24 != 0))
        {
            /* Weird. Bail out. */
-           freedcpeer(peer);
+           peer->close = 1;
            goto out;
        }
        if(peer->timeout != NULL)
@@ -2292,13 +2289,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);
@@ -2306,11 +2303,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;
     }
     
@@ -2324,14 +2320,14 @@ static void cmd_sending(struct socket *sk, struct dcpeer *peer, char *cmd, char
     
     if(peer->transfer == NULL)
     {
-       freedcpeer(peer);
+       peer->close = 1;
        return;
     }
     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);
@@ -2339,7 +2335,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);
     }
 }
 
@@ -2434,9 +2429,9 @@ static int hubsendchat(struct fnetnode *fn, int public, wchar_t *to, wchar_t *st
     return(0);
 }
 
-static void findsizelimit(struct sexpr *sexpr, int *min, int *max)
+static void findsizelimit(struct sexpr *sexpr, off_t *min, off_t *max)
 {
-    int minl, maxl, minr, maxr, retmin, retmax;
+    off_t minl, maxl, minr, maxr, retmin, retmax;
     
     switch(sexpr->op)
     {
@@ -2480,14 +2475,14 @@ static void findsizelimit(struct sexpr *sexpr, int *min, int *max)
        }
     case SOP_SIZELT:
        retmin = 0;
-       retmax = sexpr->d.n - 1;
+       retmax = sexpr->d.sz - 1;
        break;
     case SOP_SIZEEQ:
-       retmin = sexpr->d.n;
-       retmax = sexpr->d.n;
+       retmin = sexpr->d.sz;
+       retmax = sexpr->d.sz;
        break;
     case SOP_SIZEGT:
-       retmin = sexpr->d.n + 1;
+       retmin = sexpr->d.sz + 1;
        retmax = -1;
        break;
     default:
@@ -2538,7 +2533,7 @@ static int hubsearch(struct fnetnode *fn, struct search *srch, struct srchfnnlis
     size_t sstrsize, sstrdata;
     struct sockaddr *name;
     socklen_t namelen;
-    int minsize, maxsize;
+    off_t minsize, maxsize;
     struct hash *hash;
     
     hub = fn->data;
@@ -2571,10 +2566,10 @@ static int hubsearch(struct fnetnode *fn, struct search *srch, struct srchfnnlis
        if(minsize != 0)
        {
            sizebuf2(sstr, sstrdata + 32, 1);
-           sstrdata += snprintf(sstr + sstrdata, sstrsize - sstrdata, "T?F?%i?1?", minsize);
+           sstrdata += snprintf(sstr + sstrdata, sstrsize - sstrdata, "T?F?%ji?1?", (intmax_t)minsize);
        } else if(maxsize != -1) {
            sizebuf2(sstr, sstrdata + 32, 1);
-           sstrdata += snprintf(sstr + sstrdata, sstrsize - sstrdata, "T?T?%i?1?", maxsize);
+           sstrdata += snprintf(sstr + sstrdata, sstrsize - sstrdata, "T?T?%ji?1?", (intmax_t)maxsize);
        } else {
            bufcat(sstr, "F?F?0?1?", 8);
        }
@@ -2688,15 +2683,6 @@ static struct command peercmds[] =
 };
 #undef cc
 
-static void dctransdetach(struct transfer *transfer, struct dcpeer *peer)
-{
-    CBUNREG(transfer, trans_filterout, peer);
-    if(peer->freeing)
-       return;
-    peer->transfer = NULL;
-    freedcpeer(peer);
-}
-
 static void dctransgotdata(struct transfer *transfer, struct dcpeer *peer)
 {
     int ret;
@@ -2707,9 +2693,9 @@ static void dctransgotdata(struct transfer *transfer, struct dcpeer *peer)
     
     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)
+           if((buf = sockgetinbuf(peer->trpipe, &bufsize)) != NULL)
            {
                if(peer->compress == CPRS_NONE)
                {
@@ -2756,7 +2742,7 @@ static void dctransgotdata(struct transfer *transfer, struct dcpeer *peer)
                }
                if(peer->ptclose)
                {
-                   freedcpeer(peer);
+                   peer->close = 1;
                } else {
                    if(peer->timeout == NULL)
                        peer->timeout = timercallback(ntime() + 180, (void (*)(int, void *))peertimeout, peer);
@@ -2772,43 +2758,52 @@ static void dctransgotdata(struct transfer *transfer, struct dcpeer *peer)
     }
 }
 
-static void dctransendofdata(struct transfer *transfer, struct dcpeer *peer)
+static void peerdetach(struct dcpeer *peer)
 {
-    peer->state = PEER_SYNC;
-    dctransgotdata(transfer, peer);
+    CBUNREG(peer->transfer, trans_filterout, peer);
+    CBUNREG(peer->transfer, trans_destroy, peer);
+    peer->trpipe->pnext = NULL;
+    closesock(peer->trpipe);
+    quitsock(peer->trpipe);
+    peer->trpipe = NULL;
+    peer->transfer = NULL;
 }
 
-static void dcwantdata(struct transfer *transfer, struct dcpeer *peer)
+static int trdestroycb(struct transfer *transfer, struct dcpeer *peer)
 {
-    if(transferdatasize(transfer) < 65536)
-       peer->sk->ignread = 0;
+    peerdetach(peer);
+    peer->close = 1;
+    return(0);
 }
 
 static void transread(struct socket *sk, struct dcpeer *peer)
 {
     void *buf;
     size_t bufsize;
-    struct transfer *transfer;
     
-    if((buf = sockgetinbuf(sk, &bufsize)) == NULL)
+    if(peer->transfer == NULL) {
+       freedcpeer(peer);
        return;
-    if(peer->transfer == NULL)
+    }
+    if(sockqueueleft(peer->trpipe) < 0)
+       return;
+    if((buf = sockgetinbuf(sk, &bufsize)) != NULL)
     {
+       if(peer->transfer == NULL)
+       {
+           free(buf);
+           freedcpeer(peer);
+           return;
+       }
+       sockqueue(peer->trpipe, buf, bufsize);
        free(buf);
-       freedcpeer(peer);
-       return;
     }
-    transferputdata(peer->transfer, buf, bufsize);
-    free(buf);
     if(peer->transfer->curpos >= peer->transfer->size)
     {
-       transfer = peer->transfer;
-       transferdetach(transfer);
-       transferendofdata(transfer);
+       peerdetach(peer);
+       peer->close = 1;
        return;
     }
-    if(transferdatasize(peer->transfer) > 65535)
-       sk->ignread = 1;
 }
 
 static void transerr(struct socket *sk, int err, struct dcpeer *peer)
@@ -2820,8 +2815,8 @@ static void transerr(struct socket *sk, int err, struct dcpeer *peer)
        freedcpeer(peer);
        return;
     }
-    transferdetach(transfer);
-    transferendofdata(transfer);
+    peerdetach(peer);
+    peer->close = 1;
 }
 
 static void transwrite(struct socket *sk, struct dcpeer *peer)
@@ -2834,6 +2829,39 @@ static void transwrite(struct socket *sk, struct dcpeer *peer)
        return;
     }
     dctransgotdata(peer->transfer, peer);
+    sockread(peer->trpipe);
+}
+
+static void trpiperead(struct socket *sk, struct dcpeer *peer)
+{
+    dctransgotdata(peer->transfer, peer);
+}
+
+static void trpipewrite(struct socket *sk, struct dcpeer *peer)
+{
+    transread(peer->sk, peer);
+}
+
+static void trpipeerr(struct socket *sk, int errno, struct dcpeer *peer)
+{
+    peer->state = PEER_SYNC;
+    dctransgotdata(peer->transfer, peer);
+    peerdetach(peer);
+    if(peer->state != PEER_CMD)
+       peer->close = 1;
+}
+
+static struct socket *mktrpipe(struct dcpeer *peer)
+{
+    struct socket *sk;
+    
+    sk = netsockpipe();
+    sk->pnext = peer->sk;
+    sk->data = peer;
+    sk->readcb = (void (*)(struct socket *, void *))trpiperead;
+    sk->writecb = (void (*)(struct socket *, void *))trpipewrite;
+    sk->errcb = (void (*)(struct socket *, int, void *))trpipeerr;
+    return(sk);
 }
 
 static void udpread(struct socket *sk, void *data)
@@ -2842,6 +2870,7 @@ static void udpread(struct socket *sk, void *data)
     size_t buflen, hashlen;
     char *nick, *filename, *hubname;
     struct sockaddr_in hubaddr;
+    struct sockaddr *addrbuf;
     off_t size;
     int slots;
     struct fnetnode *fn, *myfn;
@@ -2961,13 +2990,15 @@ static void udpread(struct socket *sk, void *data)
        {
            for(fn = fnetnodes; fn != NULL; fn = fn->next)
            {
-               if((fn->fnet == &dcnet) && ((hub = fn->data) != NULL))
+               if((fn->fnet == &dcnet) && ((hub = fn->data) != NULL) && !sockpeeraddr(hub->sk, &addrbuf, NULL))
                {
-                   if((hub->sk != NULL) && addreq(hub->sk->remote, (struct sockaddr *)&hubaddr))
+                   if((hub->sk != NULL) && addreq(addrbuf, (struct sockaddr *)&hubaddr))
                    {
                        myfn = fn;
+                       free(addrbuf);
                        break;
                    }
+                   free(addrbuf);
                }
            }
        }
@@ -3003,9 +3034,11 @@ static void hubread(struct socket *sk, struct fnetnode *fn)
     struct command *cmd;
     char *newbuf;
     size_t datalen, cnlen;
-    char *p;
+    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 */
@@ -3013,25 +3046,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;
+       *(p2++) = 0;
        for(cmd = hubcmds; cmd->handler != NULL; cmd++)
        {
            cnlen = strlen(cmd->name);
-           if(!strncmp(hub->inbuf, cmd->name, cnlen) && ((hub->inbuf[cnlen] == ' ') || (hub->inbuf[cnlen] == 0)))
+           if(!strncmp(p, cmd->name, cnlen) && ((p[cnlen] == ' ') || (p[cnlen] == 0)))
                break;
        }
        if((cmd->limit == 0) || (hub->queue.size < cmd->limit))
-           newqcmd(&hub->queue, hub->inbuf);
-       memmove(hub->inbuf, p, hub->inbufdata -= p - hub->inbuf);
-       datalen = hub->inbufdata;
-       p = hub->inbuf;
+           newqcmd(&hub->queue, p);
+       p = p2;
     }
-    if(hub->queue.size > 1000)
-       sk->ignread = 1;
+    memmove(hub->inbuf, p, hub->inbufdata -= p - hub->inbuf);
 }
 
 static void huberr(struct socket *sk, int err, struct fnetnode *fn)
@@ -3110,7 +3140,6 @@ static struct dcpeer *newdcpeer(struct socket *sk)
     
     new = smalloc(sizeof(*new));
     memset(new, 0, sizeof(*new));
-    new->transfer = NULL;
     getsock(sk);
     new->sk = sk;
     if(confgetint("dc", "dcppemu"))
@@ -3129,7 +3158,6 @@ static void freedcpeer(struct dcpeer *peer)
     int i;
     struct qcommand *qcmd;
     
-    peer->freeing = 1;
     if(peers == peer)
        peers = peer->next;
     if(peer->next != NULL)
@@ -3137,13 +3165,7 @@ static void freedcpeer(struct dcpeer *peer)
     if(peer->prev != NULL)
        peer->prev->next = peer->next;
     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);
-    }
+       peerdetach(peer);
     if(peer->timeout != NULL)
        canceltimer(peer->timeout);
     if(peer->sk->data == peer)
@@ -3196,7 +3218,7 @@ static void hubdestroy(struct fnetnode *fn)
     struct qcommand *qcmd;
     
     hub = (struct dchub *)fn->data;
-    putsock(hub->sk);
+    quitsock(hub->sk);
     while((qcmd = ulqcmd(&hub->queue)) != NULL)
        freeqcmd(qcmd);
     if(hub->supports != NULL)
@@ -3221,26 +3243,9 @@ static void hubkill(struct fnetnode *fn)
     struct dchub *hub;
     
     hub = (struct dchub *)fn->data;
-    hub->sk->close = 1;
-}
-
-static wchar_t *dcbasename(wchar_t *filename)
-{
-    wchar_t *ret;
-    
-    if((ret = wcsrchr(filename, L'\\')) != NULL)
-       return(ret + 1);
-    return(filename);
+    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",
@@ -3251,7 +3256,6 @@ static struct fnet dcnet =
     .reqconn = hubreqconn,
     .sendchat = hubsendchat,
     .search = hubsearch,
-    .filebasename = dcbasename
 };
 
 static void peerread(struct socket *sk, struct dcpeer *peer)
@@ -3260,6 +3264,13 @@ static void peerread(struct socket *sk, struct dcpeer *peer)
     size_t datalen, cnlen;
     struct command *cmd;
 
+    if(peer->state == PEER_CMD) {
+       if((peer->queue.size > 50) || (peer->inbufdata > 65536))
+           return;
+    } else if(peer->state == PEER_TTHL) {
+    } else {
+       return;
+    }
     if((newbuf = sockgetinbuf(sk, &datalen)) == NULL)
        return;
     sizebuf2(peer->inbuf, peer->inbufdata + datalen, 1);
@@ -3285,16 +3296,11 @@ static void peerread(struct socket *sk, struct dcpeer *peer)
            {
                peer->state = PEER_STOP;
                break;
-           } else {
-               if(peer->queue.size > 50)
-                   sk->ignread = 1;
            }
        }
     } else if(peer->state == PEER_TTHL) {
        handletthl(peer);
     }
-    if(peer->inbufdata > 500000)
-       sk->ignread = 1;
 }
 
 static void peererror(struct socket *sk, int err, struct dcpeer *peer)
@@ -3310,7 +3316,6 @@ static void peerconnect(struct socket *sk, int err, struct fnetnode *fn)
     if(err != 0)
     {
        putfnetnode(fn);
-       putsock(sk);
        return;
     }
     hub = fn->data;
@@ -3322,13 +3327,12 @@ static void peerconnect(struct socket *sk, int err, struct fnetnode *fn)
     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;
     
@@ -3348,6 +3352,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);
@@ -3405,27 +3410,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;
@@ -3433,7 +3439,7 @@ static void updatehmlist(void)
            bufdata -= ret;
            buf2 += ret;
        }
-       close(fd);
+       fclose(out);
     }
     free(buf);
 }
@@ -3796,15 +3802,14 @@ static int run(void)
            quota--;
        }
        if(hub->queue.size < 1000)
-           hub->sk->ignread = 0;
+           hubread(hub->sk, fn);
        if(quota < 1)
            break;
     }
     quota = 20;
-    for(peer = peers; peer != NULL; peer = nextpeer)
+    for(peer = peers; peer != NULL; peer = peer->next)
     {
-       nextpeer = peer->next;
-       while((quota > 0) && ((qcmd = ulqcmd(&peer->queue)) != NULL))
+       while(!peer->close && (quota > 0) && ((qcmd = ulqcmd(&peer->queue)) != NULL))
        {
            if(peer->timeout != NULL)
                canceltimer(peer->timeout);
@@ -3816,10 +3821,16 @@ static int run(void)
            quota--;
        }
        if((peer->queue.size < 50) && (peer->inbufdata < 500000))
-           peer->sk->ignread = 0;
+           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);
 }
 
@@ -3853,7 +3864,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;
@@ -3861,7 +3872,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);
 }
@@ -3876,7 +3887,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"));