Use proper e-mail address format everywhere.
[doldaconnect.git] / daemon / fnet-dc.c
index 82081a8..880b1e3 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>
@@ -44,7 +42,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
@@ -110,6 +108,7 @@ struct dchub
     char *charset;
     char *nativename;
     char *nativenick;
+    char **supports;
 };
 
 struct dcexppeer
@@ -158,6 +157,7 @@ static struct dcexppeer *expected = NULL;
 static char *hmlistname = NULL;
 static char *xmllistname = NULL;
 static char *xmlbz2listname = NULL;
+static struct timer *listwritetimer = NULL;
 
 static void peerconnect(struct socket *sk, int err, struct fnetnode *fn);
 static void freedcpeer(struct dcpeer *peer);
@@ -168,6 +168,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)
 {
@@ -243,6 +244,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;
@@ -726,7 +745,7 @@ static void requestfile(struct dcpeer *peer)
            freedcpeer(peer);
            return;
        }
-       qstrf(peer->sk, "$UGetBlock %i %i %s|", peer->transfer->curpos, peer->transfer->size - peer->transfer->curpos, buf);
+       qstrf(peer->sk, "$UGetBlock %zi %zi %s|", peer->transfer->curpos, peer->transfer->size - peer->transfer->curpos, buf);
     } else {
        /* Use DCCHARSET for $Get paths until further researched... */
        if((buf = icswcstombs(peer->transfer->path, DCCHARSET, NULL)) == NULL)
@@ -735,7 +754,7 @@ static void requestfile(struct dcpeer *peer)
            freedcpeer(peer);
            return;
        }
-       qstrf(peer->sk, "$Get %s$%i|", buf, peer->transfer->curpos + 1);
+       qstrf(peer->sk, "$Get %s$%zi|", buf, peer->transfer->curpos + 1);
     }
 }
 
@@ -1017,7 +1036,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);
@@ -1083,7 +1102,8 @@ static void cmd_search(struct socket *sk, struct fnetnode *fn, char *cmd, char *
     struct sockaddr_in addr;
     struct sharecache *node;
     int minsize, maxsize;
-    int dotth, buflen;
+    int dotth;
+    size_t buflen;
     int termnum, satisfied, skipcheck;
     int level, tersat[32];
     wchar_t *terms[32];
@@ -1166,10 +1186,11 @@ 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 {
@@ -1227,10 +1248,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%i%sTTH:%.39s%s", prefix, buf, node->size, infix, buf2, postfix);
+                   qstrf(dsk, "%s%s\005%zi%sTTH:%.39s%s", prefix, buf, 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%zi%s%s%s", prefix, buf, node->size, infix, hub->nativename, postfix);
                }
                free(buf);
            }
@@ -1308,7 +1329,7 @@ static void sendctm(struct socket *sk, char *nick)
     
     if(tcpsock == NULL)
        return;
-    if(sockgetremotename(tcpsock, &addr, &addrlen) < 0)
+    if(sockgetremotename2(tcpsock, sk, &addr, &addrlen) < 0)
        return;
     if(addr->sa_family == AF_INET)
        qstrf(sk, "$ConnectToMe %s %s|", nick, formataddress(addr, addrlen));
@@ -1477,6 +1498,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;
@@ -1543,6 +1594,11 @@ static void cmd_direction(struct socket *sk, struct dcpeer *peer, char *cmd, cha
        peer->direction = mydir;
        if(peer->direction == TRNSD_UP)
        {
+           if(confgetint("transfer", "ulquota") && hasupload(&dcnet, peer->wcsname))
+           {
+               freedcpeer(peer);
+               return;
+           }
            transfer = newupload(peer->fn, &dcnet, peer->wcsname, &dctransfer, peer);
        } else {
            if((transfer = finddownload(peer->wcsname)) == NULL)
@@ -1589,6 +1645,11 @@ static void cmd_peerlock(struct socket *sk, struct dcpeer *peer, char *cmd, char
            sendsupports(peer);
        if((transfer = finddownload(peer->wcsname)) == NULL)
        {
+           if(confgetint("transfer", "ulquota") && hasupload(&dcnet, peer->wcsname))
+           {
+               freedcpeer(peer);
+               return;
+           }
            peer->direction = TRNSD_UP;
            transfer = newupload(peer->fn, &dcnet, peer->wcsname, &dctransfer, peer);
        } else {
@@ -1847,7 +1908,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 %i|", peer->transfer->size);
+    qstrf(sk, "$FileLength %zi|", peer->transfer->size);
 }
 
 static void cmd_send(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
@@ -2413,8 +2474,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;
@@ -2517,7 +2580,7 @@ static int hubsearch(struct fnetnode *fn, struct search *srch, struct srchfnnlis
     addtobuf(sstr, 0);
     if(tcpsock != NULL)
     {
-       if(sockgetremotename(udpsock, &name, &namelen) < 0)
+       if(sockgetremotename2(udpsock, fn->sk, &name, &namelen) < 0)
        {
            flog(LOG_WARNING, "cannot get address of UDP socket");
        } else {
@@ -2538,7 +2601,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)},
@@ -2558,10 +2621,11 @@ struct command hubcmds[] =
     {"$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)},
@@ -2597,7 +2661,7 @@ static void dctransgotdata(struct transfer *transfer, struct dcpeer *peer)
 {
     int ret;
     void *buf;
-    char outbuf[1024];
+    unsigned char outbuf[1024];
     z_stream *cstr;
     size_t bufsize;
     
@@ -3071,6 +3135,7 @@ static void hubconnect(struct fnetnode *fn)
 
 static void hubdestroy(struct fnetnode *fn)
 {
+    int i;
     struct dchub *hub;
     struct qcommand *qcmd;
     
@@ -3078,12 +3143,20 @@ static void hubdestroy(struct fnetnode *fn)
     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;
     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)
@@ -3229,7 +3302,7 @@ static void updatehmlist(void)
            if(node->f.b.type == FILE_REG)
            {
                addtobuf(buf, '|');
-               sprintf(numbuf, "%i", node->size);
+               sprintf(numbuf, "%zi", node->size);
                bufcat(buf, numbuf, strlen(numbuf));
            }
            addtobuf(buf, 13);
@@ -3404,7 +3477,7 @@ static void updatexmllist(void)
                lev++;
                continue;
            } else {
-               fprintf(fs, "<File Name=\"%s\" Size=\"%i\"", namebuf, node->size);
+               fprintf(fs, "<File Name=\"%s\" Size=\"%zi\"", namebuf, node->size);
                if(node->f.b.hastth)
                {
                    hashbuf = base32encode(node->hashtth, 24);
@@ -3500,14 +3573,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;
@@ -3521,11 +3688,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)
@@ -3671,12 +3843,36 @@ 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}},
     {CONF_VAR_END}
 };