Made the daemon 64-bit filesize capable.
[doldaconnect.git] / daemon / fnet-dc.c
index 7cbb32e..d934025 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
@@ -24,7 +24,6 @@
 #include <string.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
-#include <alloca.h>
 #include <wctype.h>
 #include <time.h>
 #include <errno.h>
@@ -102,6 +101,7 @@ struct qcommand
 
 struct dchub
 {
+    struct socket *sk;
     char *inbuf;
     size_t inbufdata, inbufsize;
     struct qcommand *queue;
@@ -109,6 +109,7 @@ struct dchub
     char *charset;
     char *nativename;
     char *nativenick;
+    char **supports;
 };
 
 struct dcexppeer
@@ -126,7 +127,7 @@ struct dcpeer
     struct fnetnode *fn;
     char *inbuf;
     size_t inbufdata, inbufsize;
-    size_t curread, totalsize;
+    off_t curread, totalsize;
     int freeing;
     struct timer *timeout;
     struct qcommand *queue;
@@ -244,6 +245,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;
@@ -483,7 +502,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;
@@ -648,10 +671,14 @@ static void sendpeerlock(struct dcpeer *peer)
 
 static void sendsupports(struct dcpeer *peer)
 {
-    if(peer->dcppemu)
+    if(peer->dcppemu) {
        qstr(peer->sk, "$Supports MiniSlots XmlBZList ADCGet TTHL TTHF GetZBlock ZLIG |");
-    else
-       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)
@@ -717,8 +744,8 @@ static void requestfile(struct dcpeer *peer)
        }
        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)
@@ -727,7 +754,7 @@ static void requestfile(struct dcpeer *peer)
            freedcpeer(peer);
            return;
        }
-       qstrf(peer->sk, "$UGetBlock %zi %zi %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);
     } else {
        /* Use DCCHARSET for $Get paths until further researched... */
        if((buf = icswcstombs(peer->transfer->path, DCCHARSET, NULL)) == NULL)
@@ -736,7 +763,7 @@ static void requestfile(struct dcpeer *peer)
            freedcpeer(peer);
            return;
        }
-       qstrf(peer->sk, "$Get %s$%zi|", buf, peer->transfer->curpos + 1);
+       qstrf(peer->sk, "$Get %s$%ji|", buf, (intmax_t)peer->transfer->curpos + 1);
     }
 }
 
@@ -813,10 +840,14 @@ static void cmd_lock(struct socket *sk, struct fnetnode *fn, char *cmd, char *ar
        *(p++) = 0;
     if(hub->extended)
     {
-       if(hub->dcppemu)
+       if(hub->dcppemu) {
            qstrf(sk, "$Supports UserCommand NoGetINFO NoHello UserIP2 TTHSearch GetZBlock |");
-       else
-           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);
@@ -1109,7 +1140,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|", formataddress(hub->sk->remote, hub->sk->remotelen), args + 4);
        dsk = sk;
        getsock(dsk);
     } else {
@@ -1122,7 +1153,7 @@ 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));
+       postfix = sprintf2(" (%s)|", formataddress(hub->sk->remote, hub->sk->remotelen));
        netdgramconn(dsk = netdupsock(udpsock), (struct sockaddr *)&addr, sizeof(addr));
     }
     
@@ -1230,10 +1261,10 @@ static void cmd_search(struct socket *sk, struct fnetnode *fn, char *cmd, char *
                if(node->f.b.hastth)
                {
                    buf2 = base32encode(node->hashtth, 24);
-                   qstrf(dsk, "%s%s\005%zi%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%zi%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);
            }
@@ -1374,7 +1405,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);
 }
 
@@ -1383,7 +1414,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;
@@ -1402,7 +1434,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;
@@ -1480,6 +1512,36 @@ 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;
@@ -1647,7 +1709,7 @@ 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)
@@ -1655,7 +1717,7 @@ static void cmd_filelength(struct socket *sk, struct dcpeer *peer, char *cmd, ch
        freedcpeer(peer);
        return;
     }
-    size = atoi(args);
+    size = strtoll(args, NULL, 10);
     if(peer->transfer->size != size)
     {
        transfersetsize(peer->transfer, size);
@@ -1778,7 +1840,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;
@@ -1797,7 +1859,7 @@ static void cmd_get(struct socket *sk, struct dcpeer *peer, char *cmd, char *arg
        return;
     }
     *(p++) = 0;
-    if((offset = (atoi(p) - 1)) < 0)
+    if((offset = (strtoll(p, NULL, 10) - 1)) < 0)
     {
        freedcpeer(peer);
        return;
@@ -1860,7 +1922,7 @@ static void cmd_get(struct socket *sk, struct dcpeer *peer, char *cmd, char *arg
     lesk = wrapsock(fd);
     transferprepul(peer->transfer, sb.st_size, offset, -1, lesk);
     putsock(lesk);
-    qstrf(sk, "$FileLength %zi|", 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)
@@ -1911,7 +1973,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;
@@ -1930,7 +1992,7 @@ static void cmd_getblock(struct socket *sk, struct dcpeer *peer, char *cmd, char
        return;
     }
     *(p2++) = 0;
-    start = atoi(p);
+    start = strtoll(p, NULL, 10);
     p = p2;
     if((p2 = strchr(p, ' ')) == NULL)
     {
@@ -1938,7 +2000,7 @@ static void cmd_getblock(struct socket *sk, struct dcpeer *peer, char *cmd, char
        return;
     }
     *(p2++) = 0;
-    numbytes = atoi(p);
+    numbytes = strtoll(p, NULL, 10);
     p = p2;
     if(!strcmp(cmd, "$UGetBlock") || !strcmp(cmd, "$UGetZBlock"))
        charset = "UTF-8";
@@ -2000,7 +2062,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);
 }
 
@@ -2008,7 +2070,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;
@@ -2030,8 +2092,8 @@ static void cmd_adcget(struct socket *sk, struct dcpeer *peer, char *cmd, char *
        freedcpeer(peer);
        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))
@@ -2104,8 +2166,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, "|");
@@ -2166,7 +2228,7 @@ 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)
     {
@@ -2183,8 +2245,8 @@ static void cmd_adcsnd(struct socket *sk, struct dcpeer *peer, char *cmd, char *
        freedcpeer(peer);
        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))
@@ -2232,14 +2294,14 @@ 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);
        return;
     }
-    numbytes = atoi(args);
+    numbytes = strtoll(args, NULL, 10);
     if(peer->transfer->size - peer->transfer->curpos != numbytes)
     {
        transfersetsize(peer->transfer, peer->transfer->curpos + numbytes);
@@ -2291,10 +2353,10 @@ static int hubreqconn(struct fnetpeer *peer)
        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);
@@ -2334,12 +2396,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);
@@ -2454,7 +2516,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);
@@ -2532,15 +2594,15 @@ static int hubsearch(struct fnetnode *fn, struct search *srch, struct srchfnnlis
     addtobuf(sstr, 0);
     if(tcpsock != NULL)
     {
-       if(sockgetremotename2(udpsock, fn->sk, &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);
@@ -2573,6 +2635,7 @@ static struct command hubcmds[] =
     {"$UserCommand", cc(cmd_usercommand)},
     {"$GetPass", cc(cmd_getpass)},
     {"$LogedIn", cc(cmd_logedin)}, /* sic */
+    {"$Supports", cc(cmd_hubsupports)},
     {NULL, NULL}
 };
 
@@ -2753,7 +2816,8 @@ static void udpread(struct socket *sk, void *data)
     size_t buflen, hashlen;
     char *nick, *filename, *hubname;
     struct sockaddr_in hubaddr;
-    int size, slots;
+    off_t size;
+    int slots;
     struct fnetnode *fn, *myfn;
     struct dchub *hub;
     struct srchres *sr;
@@ -2789,7 +2853,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)
        {
@@ -2871,10 +2935,13 @@ static void udpread(struct socket *sk, void *data)
        {
            for(fn = fnetnodes; fn != NULL; fn = fn->next)
            {
-               if((fn->fnet == &dcnet) && (fn->sk != NULL) && addreq(fn->sk->remote, (struct sockaddr *)&hubaddr))
+               if((fn->fnet == &dcnet) && ((hub = fn->data) != NULL))
                {
-                   myfn = fn;
-                   break;
+                   if((hub->sk != NULL) && addreq(hub->sk->remote, (struct sockaddr *)&hubaddr))
+                   {
+                       myfn = fn;
+                       break;
+                   }
                }
            }
        }
@@ -3074,33 +3141,34 @@ 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) && (fn->sk->data == fn))
-    {
-       fn->sk->data = NULL;
-       fn->sk->readcb = NULL;
-       fn->sk->errcb = 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)
@@ -3112,6 +3180,14 @@ static void hubdestroy(struct fnetnode *fn)
     free(hub);
 }
 
+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;
@@ -3134,6 +3210,7 @@ static struct fnet dcnet =
     .name = L"dc",
     .connect = hubconnect,
     .destroy = hubdestroy,
+    .kill = hubkill,
     .setnick = hubsetnick,
     .reqconn = hubreqconn,
     .sendchat = hubsendchat,
@@ -3246,7 +3323,7 @@ static void updatehmlist(void)
            if(node->f.b.type == FILE_REG)
            {
                addtobuf(buf, '|');
-               sprintf(numbuf, "%zi", node->size);
+               sprintf(numbuf, "%ji", (intmax_t)node->size);
                bufcat(buf, numbuf, strlen(numbuf));
            }
            addtobuf(buf, 13);
@@ -3421,7 +3498,7 @@ static void updatexmllist(void)
                lev++;
                continue;
            } else {
-               fprintf(fs, "<File Name=\"%s\" Size=\"%zi\"", 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);
@@ -3547,6 +3624,78 @@ static int shareupdate(unsigned long long uusharesize, void *data)
     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;
@@ -3560,11 +3709,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)
@@ -3588,10 +3742,10 @@ static int run(void)
        {
            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;
@@ -3736,6 +3890,15 @@ static struct configvar myvars[] =
      * 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}
 };