]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ip/torrent.c
torrent: calculate piece length depending on file size
[plan9front.git] / sys / src / cmd / ip / torrent.c
1 #include <u.h>
2 #include <libc.h>
3 #include <mp.h>
4 #include <libsec.h>
5
6 typedef struct Dict Dict;
7 typedef struct Piece Piece;
8 typedef struct File File;
9 typedef struct Stats Stats;
10
11 struct Dict
12 {
13         char    typ;    // i, d, s, l
14         Dict    *val;
15         Dict    *next;
16         char    *start, *end;
17         int     len;
18         char    str[];
19 };
20
21 struct Piece
22 {
23         uchar   *hash;
24         int     len;
25         int     brk;
26 };
27
28 struct File
29 {
30         File    *next;
31         char    *name;
32         int     fd;
33         vlong   off;
34         vlong   len;
35 };
36
37 struct Stats
38 {
39         Lock;
40         vlong   up;
41         vlong   down;
42         vlong   left;
43 };
44
45 enum {
46         MAXIO = 16*1024,
47 };
48
49 int debug;
50 int killgroup = -1;
51 int port = 6881;
52 char *deftrack = "http://exodus.desync.com/announce";
53 char *mntweb = "/mnt/web";
54 uchar infohash[20];
55 uchar peerid[20];
56 int blocksize;
57
58 int npieces;
59 Piece *pieces;
60
61 int nhavemap;
62 uchar *havemap;
63 int nhavepieces;
64
65 File *files;
66 Stats stats;
67
68 void
69 freedict(Dict *d)
70 {
71         if(d){
72                 if(d->val != d)
73                         freedict(d->val);
74                 freedict(d->next);
75                 free(d);
76         }
77 }
78
79 char*
80 bparse(char *s, char *e, Dict **dp)
81 {
82         char *x, t;
83         Dict *d;
84         int n;
85
86         *dp = nil;
87         if(s >= e)
88                 return e;
89
90         t = *s;
91         switch(t){
92         case 'd':
93         case 'l':
94                 x = s++;
95                 d = nil;
96                 while(s < e){
97                         if(*s == 'e'){
98                                 s++;
99                                 break;
100                         }
101                         if(t == 'd'){
102                                 s = bparse(s, e, dp);
103                                 if((d = *dp) == nil)
104                                         break;
105                         } else
106                                 d = *dp = mallocz(sizeof(*d), 1);
107                         d->typ = t;
108                         d->start = x;
109                         if(s < e){
110                                 s = bparse(s, e, &d->val);
111                                 dp = &d->next;
112                                 d->end = s;
113                         }
114                         x = s;
115                 }
116                 if(d)
117                         d->end = s;
118                 return s;
119         case 'i':
120                 x = ++s;
121                 if((s = memchr(x, 'e', e - x)) == nil)
122                         return e;
123                 n = s - x;
124                 s++;
125                 break;
126         default:
127                 if((x = memchr(s, ':', e - s)) == nil)
128                         return e;
129                 x++;
130                 if((n = atoi(s)) < 0)
131                         return e;
132                 s = x + n;
133                 if((s > e) || (s < x)){
134                         n = e - x;
135                         s = e;
136                 }
137                 t = 's';
138         }
139         d = mallocz(sizeof(*d) + n+1, 1);
140         d->typ = t;
141         memmove(d->str, x, d->len = n);
142         d->str[n] = 0;
143         *dp = d;
144         return s;
145 }
146
147 char*
148 dstr(Dict *d)
149 {
150         if(d && (d->typ == 's' || d->typ == 'i'))
151                 return d->str;
152         return nil;
153 }
154
155 Dict*
156 dlook(Dict *d, char *s)
157 {
158         for(; d && d->typ == 'd'; d = d->next)
159                 if(d->len && strcmp(d->str, s) == 0)
160                         return d->val;
161         return nil;
162 }
163
164 int
165 readall(int fd, char **p)
166 {
167         int n, r;
168
169         n = 0;
170         *p = nil;
171         while(*p = realloc(*p, n+1024)){
172                 if((r = read(fd, *p+n, 1024)) <= 0)
173                         break;
174                 n += r;
175         }
176         return n;
177 }
178
179 int
180 rwpiece(int wr, int index, uchar *data, int len, int poff)
181 {
182         vlong off;
183         int n, m;
184         File *f;
185
186         if(len <= 0 || poff >= pieces[index].len)
187                 return 0;
188         if(len+poff > pieces[index].len)
189                 len = pieces[index].len - poff;
190         off = (vlong)index * blocksize;
191         off += poff;
192         for(f = files; f; f = f->next)
193                 if((f->off+f->len) > off)
194                         break;
195         off -= f->off;
196         n = ((off + len) > f->len) ? f->len - off : len;
197         if((n = (wr ? pwrite(f->fd, data, n, off) : pread(f->fd, data, n, off))) <= 0)
198                 return -1;
199         if((m = rwpiece(wr, index, data + n, len - n, poff + n)) < 0)
200                 return -1;
201         return n+m;
202 }
203
204 int
205 havepiece(int x)
206 {
207         uchar *p, m, hash[20];
208         int n;
209
210         m = 0x80>>(x&7);
211         if(havemap[x>>3] & m)
212                 return 1;
213         p = malloc(blocksize);
214         n = pieces[x].len;
215         if(rwpiece(0, x, p, n, 0) != n){
216                 free(p);
217                 return 0;
218         }
219         sha1(p, n, hash, nil);
220         free(p);
221         if(memcmp(hash, pieces[x].hash, 20))
222                 return 0;
223         lock(&stats);
224         if((havemap[x>>3] & m) == 0){
225                 havemap[x>>3] |= m;
226                 nhavepieces++;
227                 stats.left -= pieces[x].len;
228         }
229         unlock(&stats);
230         return 1;
231 }
232
233 int
234 pickpiece(uchar *map)
235 {
236         int i, x, r, k;
237         uchar m;
238
239         r = -1;
240         k = 0;
241         for(i = 0; i<nhavemap; i++){
242                 if(map[i] == 0)
243                         continue;
244                 for(x = i<<3, m = 0x80; m; m >>= 1, x++){
245                         if((~map[i] | havemap[i]) & m)
246                                 continue;
247                         if(nrand(++k) == 0)
248                                 r = x;
249                 }
250         }
251         return r;
252 }
253
254 int
255 unpack(uchar *s, int n, char *fmt, ...)
256 {
257         va_list arg;
258         uchar *b, *e;
259
260         b = s;
261         e = b + n;
262         va_start(arg, fmt);
263         for(; *fmt; fmt++) {
264                 switch(*fmt){
265                 case '_':
266                         s++;
267                         break;
268                 case 'b':
269                         if(s+1 > e) goto Err;
270                         *va_arg(arg, int*) = *s++;
271                         break;
272                 case 'l':
273                         if(s+4 > e) goto Err;
274                         *va_arg(arg, int*) = s[0]<<24 | s[1]<<16 | s[2]<<8 | s[3];
275                         s += 4;
276                         break;
277                 }
278         }
279         va_end(arg);
280         return s - b;
281 Err:
282         va_end(arg);
283         return -1;
284 }
285
286 int
287 pack(uchar *s, int n, char *fmt, ...)
288 {
289         va_list arg;
290         uchar *b, *e;
291         int i;
292
293         b = s;
294         e = b + n;
295         va_start(arg, fmt);
296         for(; *fmt; fmt++) {
297                 switch(*fmt){
298                 case '_':
299                         i = 0;
300                         if(0){
301                 case 'b':
302                         i = va_arg(arg, int);
303                         }
304                         if(s+1 > e) goto Err;
305                         *s++ = i & 0xFF;
306                         break;
307                 case 'l':
308                         i = va_arg(arg, int);
309                         if(s+4 > e) goto Err;
310                         *s++ = (i>>24) & 0xFF;
311                         *s++ = (i>>16) & 0xFF;
312                         *s++ = (i>>8) & 0xFF;
313                         *s++ = i & 0xFF;
314                         break;
315                 case '*':
316                         i = va_arg(arg, int);
317                         if(s+i > e) goto Err;
318                         memmove(s, va_arg(arg, uchar*), i);
319                         s += i;
320                         break;
321                 }
322         }
323         va_end(arg);
324         return s - b;
325 Err:
326         va_end(arg);
327         return -1;
328 }
329
330
331
332 int
333 peer(int fd, int incoming, char *addr)
334 {
335         uchar buf[64+MAXIO], *map, *told, *p, m;
336         int mechoking, hechoking;
337         int mewant, hewant;
338         int workpiece;
339         int i, o, l, x, n;
340
341         if(debug) fprint(2, "peer %s: %s connected\n", addr, incoming ? "incoming" : "outgoing");
342
343         for(i=0; i<2; i++){
344                 if((incoming && i) || (!incoming && !i)){
345                         if(debug) fprint(2, "peer %s: -> handshake\n", addr);
346                         n = pack(buf, sizeof(buf), "*________**", 
347                                 20, "\x13BitTorrent protocol",
348                                 sizeof(infohash), infohash,
349                                 sizeof(peerid), peerid);
350                         if(write(fd, buf, n) != n)
351                                 return 1;
352                 }
353                 if((incoming && !i) || (!incoming && i)){
354                         n = 20 + 8 + sizeof(infohash);
355                         if((n = readn(fd, buf, n)) != n)
356                                 return 1;
357                         if(memcmp(buf, "\x13BitTorrent protocol", 20))
358                                 return 0;
359                         if(memcmp(infohash, buf + 20 + 8, sizeof(infohash)))
360                                 return 0;
361                         if(debug) fprint(2, "peer %s: <- handshake\n", addr);
362                 }
363         }
364         if(readn(fd, buf, sizeof(peerid)) != sizeof(peerid))
365                 return 1;
366         if(memcmp(peerid, buf, sizeof(peerid)) == 0)
367                 return 0;
368         if(debug) fprint(2, "peer %s: peerid %.*s\n", addr, sizeof(peerid), (char*)buf);
369
370         mechoking = 1;
371         hechoking = 1;
372         mewant = 0;
373         hewant = 0;
374         workpiece = -1;
375         map = mallocz(nhavemap, 1);
376         told = malloc(nhavemap);
377
378         if(debug) fprint(2, "peer %s: -> bitfield %d\n", addr, nhavemap);
379         memmove(told, havemap, nhavemap);
380         n = pack(buf, sizeof(buf), "lb*", nhavemap+1, 0x05, nhavemap, told);
381         if(write(fd, buf, n) != n)
382                 goto Out;
383
384         for(;;){
385                 for(i=0; i<nhavemap; i++){
386                         if(told[i] != havemap[i]){
387                                 for(x = i<<3, m = 0x80; m; m >>= 1, x++){
388                                         if((~havemap[i] | told[i] | map[i]) & m)
389                                                 continue;
390                                         told[i] |= m;
391                                         if(debug) fprint(2, "peer %s: -> have %d\n", addr, x);
392                                         n = pack(buf, sizeof(buf), "lbl", 1+4, 0x04, x);
393                                         if(write(fd, buf, n) != n)
394                                                 goto Out;
395                                 }
396                         }
397                         if(!mewant && (map[i] & ~havemap[i])){
398                                 mewant = 1;
399                                 if(debug) fprint(2, "peer %s: -> interested\n", addr);
400                                 n = pack(buf, sizeof(buf), "lb", 1, 0x02);
401                                 if(write(fd, buf, n) != n)
402                                         goto Out;
403                         }
404                 }
405                 if(!hechoking && mewant){
406                         x = workpiece;
407                         if(x >= 0 && pieces[x].brk < pieces[x].len)
408                                 {}
409                         else x = pickpiece(map);
410                         if(x >= 0){
411                                 o = pieces[x].brk;
412                                 l = pieces[x].len - o;
413                                 if(l > MAXIO)
414                                         l = MAXIO;
415                                 if(debug) fprint(2, "peer %s: -> request %d %d %d\n", addr, x, o, l);
416                                 n = pack(buf, sizeof(buf), "lblll", 1+4+4+4, 0x06, x, o, l);
417                                 if(write(fd, buf, n) != n)
418                                         goto Out;
419                                 workpiece = x;
420                         }
421                 }
422                 if(mechoking && hewant){
423                         mechoking = 0;
424                         if(debug) fprint(2, "peer %s: -> unchoke\n", addr);
425                         n = pack(buf, sizeof(buf), "lb", 1, 0x01);
426                         if(write(fd, buf, n) != n)
427                                 goto Out;
428                 }
429
430                 if(readn(fd, buf, 4) != 4)
431                         break;
432                 unpack(buf, 4, "l", &n);
433                 if(n < 0 || n > sizeof(buf))
434                         break;
435                 if(n == 0)
436                         continue;
437                 if(readn(fd, buf, n) != n)
438                         break;
439
440                 n--;
441                 p = buf+1;
442                 switch(*buf){
443                 case 0x00:      // Choke
444                         hechoking = 1;
445                         workpiece = -1;
446                         if(debug) fprint(2, "peer %s: <- choke\n", addr);
447                         break;
448                 case 0x01:      // Unchoke
449                         hechoking = 0;
450                         if(debug) fprint(2, "peer %s: <- unchoke\n", addr);
451                         break;
452                 case 0x02:      // Interested
453                         hewant = 1;
454                         if(debug) fprint(2, "peer %s: <- interested\n", addr);
455                         break;
456                 case 0x03:      // Notinterested
457                         hewant = 0;
458                         if(debug) fprint(2, "peer %s: <- notinterested\n", addr);
459                         break;
460                 case 0x04:      // Have <piceindex>
461                         if(unpack(p, n, "l", &x) < 0)
462                                 goto Out;
463                         if(debug) fprint(2, "peer %s: <- have %d\n", addr, x);
464                         if(x < 0 || x >= npieces)
465                                 continue;
466                         map[x>>3] |= 0x80>>(x&7);
467                         break;
468                 case 0x05:      // Bitfield
469                         if(debug) fprint(2, "peer %s: <- bitfield %d\n", addr, n);
470                         if(n != nhavemap)
471                                 continue;
472                         memmove(map, p, n);
473                         break;
474                 case 0x06:      // Request <index> <begin> <length>
475                         if(unpack(p, n, "lll", &x, &o, &l) < 0)
476                                 goto Out;
477                         if(debug) fprint(2, "peer %s: <- request %d %d %d\n", addr, x, o, l);
478                         if(x < 0 || x >= npieces)
479                                 continue;
480                         if(!hewant || mechoking || (~havemap[x>>3]&(0x80>>(x&7))))
481                                 continue;
482                         if(debug) fprint(2, "peer %s: -> piece %d %d\n", addr, x, o);
483                         n = 4+1+4+4;
484                         if(l > MAXIO)
485                                 l = MAXIO;
486                         if((l = rwpiece(0, x, buf + n, l, o)) <= 0)
487                                 continue;
488                         n = pack(buf, sizeof(buf), "lbll", 1+4+4+l, 0x07, x, o);
489                         n += l;
490                         if(write(fd, buf, n) != n)
491                                 goto Out;
492                         lock(&stats);
493                         stats.up += n;
494                         unlock(&stats);
495                         break;
496                 case 0x07:      // Piece <index> <begin> <block>
497                         if(unpack(p, n, "ll", &x, &o) != 8)
498                                 goto Out;
499                         p += 8;
500                         n -= 8;
501                         lock(&stats);
502                         stats.down += n;
503                         unlock(&stats);
504                         if(debug) fprint(2, "peer %s: <- piece %d %d %d\n", addr, x, o, n);
505                         if(x < 0 || x >= npieces)
506                                 continue;
507                         if((pieces[x].brk != o) || (havemap[x>>3]&(0x80>>(x&7))))
508                                 continue;
509                         if(rwpiece(1, x, p, n, o) == n){
510                                 if((pieces[x].brk = o+n) == pieces[x].len){
511                                         if(!havepiece(x))
512                                                 pieces[x].brk = 0;
513                                 }
514                         }
515                         break;
516                 case 0x08:      // Cancel <index> <begin> <length>
517                         if(unpack(p, n, "lll", &x, &o, &l) < 0)
518                                 goto Out;
519                         if(debug) fprint(2, "peer %s: <- cancel %d %d %d\n", addr, x, o, l);
520                         break;
521                 case 0x09:      // Port <port>
522                         if(unpack(p, n, "l", &x) < 0)
523                                 goto Out;
524                         if(debug) fprint(2, "peer %s: <- port %d\n", addr, x);
525                         break;
526                 }
527         }
528
529 Out:
530         free(told);
531         free(map);
532         return 1;
533 }
534
535 void
536 server(void)
537 {
538         char addr[64], adir[40], ldir[40];
539         int afd, lfd, dfd;
540         NetConnInfo *ni;
541
542         afd = -1;
543         for(port=6881; port<6890; port++){
544                 snprint(addr, sizeof(addr), "tcp!*!%d", port);
545                 if((afd = announce(addr, adir)) >= 0)
546                         break;
547         }
548         if(afd < 0){
549                 fprint(2, "announce: %r");
550                 return;
551         }
552         if(rfork(RFFDG|RFPROC|RFMEM))
553                 return;
554         for(;;){
555                 if((lfd = listen(adir, ldir)) < 0){
556                         fprint(2, "listen: %r");
557                         break;
558                 }
559                 if(rfork(RFFDG|RFPROC|RFMEM)){
560                         close(lfd);
561                         continue;
562                 }
563                 if((dfd = accept(lfd, ldir)) < 0){
564                         fprint(2, "accept: %r");
565                         break;
566                 }
567                 ni = getnetconninfo(ldir, dfd);
568                 peer(dfd, 1, ni ? ni->raddr : "???");
569                 if(ni) freenetconninfo(ni);     
570                 break;
571         }
572         exits(0);
573 }
574
575 void
576 client(char *ip, char *port)
577 {
578         static Dict *peers;
579         static QLock peerslk;
580         int try, fd;
581         char *addr;
582         Dict *d;
583
584         if(ip == nil || port == nil)
585                 return;
586
587         d = mallocz(sizeof(*d) + 64, 1);
588         snprint(addr = d->str, 64, "tcp!%s!%s", ip, port);
589         qlock(&peerslk);
590         if(dlook(peers, addr)){
591                 qunlock(&peerslk);
592                 free(d);
593                 return;
594         }
595         d->len = strlen(addr);
596         d->typ = 'd';
597         d->val = d;
598         d->next = peers;
599         peers = d;
600         qunlock(&peerslk);
601
602         if(debug) fprint(2, "client %s\n", addr);
603
604         if(rfork(RFFDG|RFPROC|RFMEM))
605                 return;
606         for(try = 0; try < 10; try++){
607                 if((fd = dial(addr, nil, nil, nil)) >= 0){
608                         if(!peer(fd, 0, addr))
609                                 break;
610                         close(fd);
611                 }
612                 sleep((1000<<try)+nrand(5000));
613         }
614         exits(0);
615 }
616
617 int
618 hopen(char *url, ...)
619 {
620         int conn, ctlfd, fd, n;
621         char buf[1024+1];
622         va_list arg;
623
624         snprint(buf, sizeof buf, "%s/clone", mntweb);
625         if((ctlfd = open(buf, ORDWR)) < 0)
626                 return -1;
627         if((n = read(ctlfd, buf, sizeof buf-1)) <= 0){
628                 close(ctlfd);
629                 return -1;
630         }
631         buf[n] = 0;
632         conn = atoi(buf);
633         va_start(arg, url);
634         strcpy(buf, "url ");
635         n = 4+vsnprint(buf+4, sizeof(buf)-4, url, arg);
636         va_end(arg);
637         if(write(ctlfd, buf, n) != n){
638         ErrOut:
639                 close(ctlfd);
640                 return -1;
641         }
642         snprint(buf, sizeof buf, "%s/%d/body", mntweb, conn);
643         if((fd = open(buf, OREAD)) < 0)
644                 goto ErrOut;
645         close(ctlfd);
646         return fd;
647 }
648
649 void
650 tracker(char *url)
651 {
652         static Dict *trackers;
653         static QLock trackerslk;
654
655         char *event, *p;
656         Dict *d, *l;
657         int n, fd;
658
659         if(url == nil)
660                 return;
661
662         qlock(&trackerslk);
663         if(dlook(trackers, url)){
664                 qunlock(&trackerslk);
665                 return;
666         }
667         n = strlen(url);
668         d = mallocz(sizeof(*d) + n+1, 1);
669         strcpy(d->str, url);
670         d->len = n;
671         d->typ = 'd';
672         d->val = d;
673         d->next = trackers;
674         trackers = d;
675         url = d->str;
676         qunlock(&trackerslk);
677
678         if(debug) fprint(2, "tracker %s\n", url);
679
680         if(rfork(RFPROC|RFMEM))
681                 return;
682
683         event = "&event=started";
684         for(;;){
685                 vlong up, down, left;
686
687                 lock(&stats);
688                 up = stats.up;
689                 down = stats.down;
690                 left = stats.left;
691                 unlock(&stats);
692
693                 d = nil;
694                 if((fd = hopen("%s?info_hash=%.*H&peer_id=%.*H&port=%d&"
695                         "uploaded=%lld&downloaded=%lld&left=%lld&compact=1&no_peer_id=1%s",
696                         url, sizeof(infohash), infohash, sizeof(peerid), peerid, port,
697                         up, down, left, event)) >= 0){
698                         event = "";
699                         n = readall(fd, &p);
700                         close(fd);
701                         bparse(p, p+n, &d);
702                         free(p);
703                 } else if(debug) fprint(2, "tracker %s: %r\n", url);
704                 if(l = dlook(d, "peers")){
705                         if(l->typ == 's'){
706                                 uchar *b, *e;
707
708                                 b = (uchar*)l->str;
709                                 e = b + l->len;
710                                 for(; b+6 <= e; b += 6){
711                                         char ip[16], port[6];
712
713                                         snprint(ip, sizeof(ip), "%d.%d.%d.%d", b[0], b[1], b[2], b[3]);
714                                         snprint(port, sizeof(port), "%d", b[4]<<8 | b[5]);
715                                         client(ip, port);
716                                 }
717                         } else for(; l && l->typ == 'l'; l = l->next)
718                                 client(dstr(dlook(l->val, "ip")), dstr(dlook(l->val, "port")));
719                 }
720                 n = 0;
721                 if(p = dstr(dlook(d, "interval")))
722                         n = atoi(p);
723                 if(n < 10 | n > 60*60)
724                         n = 2*60;
725                 freedict(d);
726                 sleep(n * 1000 + nrand(5000));
727         }
728 }
729
730 int
731 Hfmt(Fmt *f)
732 {
733         uchar *s, *e;
734         s = va_arg(f->args, uchar*);
735         if(f->flags & FmtPrec)
736                 e = s + f->prec;
737         else
738                 e = s + strlen((char*)s);
739         for(; s < e; s++)
740                 if(fmtprint(f, *s && ((*s >= '0' && *s <= '9') || 
741                         (*s >= 'a' && *s <= 'z') ||
742                         (*s >= 'A' && *s <= 'Z') || 
743                         strchr(".-_~", *s)) ? "%c" : "%%%.2x", *s) < 0)
744                         return -1;
745         return 0;
746 }
747
748 int
749 mktorrent(int fd, char *url)
750 {
751         uchar *b, h[20];
752         Dir *d;
753         int n;
754
755         if((d = dirfstat(fd)) == nil)
756                 return -1;
757         if(d->qid.type == QTDIR){
758                 free(d);
759                 werrstr("file is a directory");
760                 return -1;
761         }
762         if(d->length == 0){
763                 free(d);
764                 werrstr("empty file");
765                 return -1;
766         }
767         npieces = 1;
768         for(blocksize = 256*1024;;blocksize<<=1){
769                 npieces = (d->length + blocksize-1) / blocksize;
770                 if(npieces <= 8*1024 || blocksize >= 2*1024*1024)
771                         break;
772         }
773         print("d");
774         print("8:announce%ld:%s", strlen(url), url);
775         print("4:info");
776         print("d");
777         print("4:name%ld:%s", strlen(d->name), d->name);
778         print("6:lengthi%llde", d->length);
779         print("12:piece lengthi%de", blocksize);
780         print("6:pieces%d:", npieces*sizeof(h));
781         free(d);
782         b = malloc(blocksize);
783         while((n = readn(fd, b, blocksize)) > 0){
784                 sha1(b, n, h, nil);
785                 if(write(1, h, sizeof(h)) != sizeof(h)){
786                         free(b);
787                         return -1;
788                 }
789                 npieces--;
790         }
791         free(b);
792         if(npieces){
793                 werrstr("read failed: %r");
794                 return -1;
795         }
796         print("e");
797         print("e");
798         return 0;
799 }
800
801 int
802 mkdirs(char *s)
803 {
804         char *p;
805         int f;
806
807         if(access(s, AEXIST) == 0)
808                 return 0;
809         for(p=strchr(s+1, '/'); p; p=strchr(p+1, '/')){
810                 *p = 0;
811                 if(access(s, AEXIST)){
812                         if((f = create(s, OREAD, DMDIR | 0777)) < 0){
813                                 *p = '/';
814                                 return -1;
815                         }
816                         close(f);
817                 }
818                 *p = '/';
819         }
820         return 0;
821 }
822
823 char*
824 fixnamedup(char *s)
825 {
826         int n, l;
827         char *d;
828         Rune r;
829
830         n = 0;
831         d = strdup(s);
832         l = strlen(d);
833         while(*s){
834                 s += chartorune(&r, s);
835                 if(r == ' ')
836                         r = 0xa0;
837                 if((n + runelen(r)) >= l){
838                         l += 64;
839                         d = realloc(d, l);
840                 }
841                 n += runetochar(d + n, &r);
842         }
843         d[n] = 0;
844         return cleanname(d);
845 }
846
847 int
848 killnote(void *, char *)
849 {
850         postnote(PNGROUP, killgroup, "kill");
851         return 0;
852 }
853
854 void
855 usage(void)
856 {
857         fprint(2, "usage: %s [ -vsdpc ] [ -m mtpt ] [ -t url ] [ file ]\n", argv0);
858         exits("usage");
859 }
860
861 void
862 main(int argc, char *argv[])
863 {
864         int sflag, pflag, vflag, cflag, fd, i, n;
865         Dict *info, *torrent, *d;
866         char *p, *s, *e, *url;
867         File **fp, *f;
868         vlong len;
869
870         fmtinstall('H', Hfmt);
871         url = nil;
872         sflag = pflag = vflag = cflag = 0;
873         ARGBEGIN {
874         case 'm':
875                 mntweb = EARGF(usage());
876                 break;
877         case 't':
878                 url = EARGF(usage());
879                 break;
880         case 's':
881                 sflag = 1;
882                 break;
883         case 'p':
884                 pflag = 1;
885                 break;
886         case 'v':
887                 vflag = 1;
888                 break;
889         case 'c':
890                 cflag = 1;
891                 break;
892         case 'd':
893                 debug++;
894                 break;
895         default:
896                 usage();
897         } ARGEND;
898
899         fd = 0;
900         if(*argv)
901                 if((fd = open(*argv, OREAD)) < 0)
902                         sysfatal("open: %r");
903         if(cflag){
904                 if(url == nil)
905                         url = deftrack;
906                 if(mktorrent(fd, url) < 0)
907                         sysfatal("%r");
908                 exits(0);
909         }
910         if((n = readall(fd, &p)) <= 0)
911                 sysfatal("read torrent: %r");
912         bparse(p, p+n, &torrent);
913         if((d = info = dlook(torrent, "info")) == nil)
914                 sysfatal("no meta info in torrent");
915         for(s = e = d->start; d && d->typ == 'd'; d = d->next)
916                 e = d->end;
917         sha1((uchar*)s, e - s, (uchar*)infohash, nil);
918         free(p);
919
920         fp = &files;
921         if(d = dlook(info, "files")){           
922                 for(; d && d->typ == 'l'; d = d->next){
923                         Dict *di;
924
925                         if((s = dstr(dlook(d->val, "length"))) == nil)
926                                 continue;
927                         f = mallocz(sizeof(*f), 1);
928                         f->len = atoll(s);
929                         f->name = dstr(dlook(info, "name"));
930                         for(di = dlook(d->val, "path"); di && di->typ == 'l'; di = di->next)
931                                 if(s = dstr(di->val))
932                                         f->name = f->name ? smprint("%s/%s", f->name, s) : s;
933                         *fp = f;
934                         fp = &f->next;
935                 }
936         } else if(s = dstr(dlook(info, "length"))){
937                 f = mallocz(sizeof(*f), 1);
938                 f->len = atoll(s);
939                 f->name = dstr(dlook(info, "name"));
940                 *fp = f;
941         }
942         len = 0;
943         for(f = files; f; f = f->next){
944                 if(f->name == nil || f->len <= 0)
945                         sysfatal("bogus file entry in meta info");
946                 f->name = fixnamedup(f->name);
947                 if(vflag) fprint(pflag ? 2 : 1, "%s\n", f->name);
948                 if((f->fd = open(f->name, ORDWR)) < 0){
949                         if(mkdirs(f->name) < 0)
950                                 sysfatal("mkdirs: %r");
951                         if((f->fd = create(f->name, ORDWR, 0666)) < 0)
952                                 sysfatal("create: %r");
953                 }
954                 f->off = len;
955                 len += f->len;
956         }
957         if(len <= 0)
958                 sysfatal("no files in torrent");
959
960         if((s = dstr(dlook(info, "piece length"))) == nil)
961                 sysfatal("missing piece length in meta info");
962         if((blocksize = atoi(s)) <= 0)
963                 sysfatal("bogus piece length in meta info");
964         d = dlook(info, "pieces");
965         if(d == nil || d->typ != 's' || d->len <= 0 || d->len % 20)
966                 sysfatal("bad or no pices in meta info");
967         npieces = d->len / 20;
968         pieces = mallocz(sizeof(Piece) * npieces, 1);
969         nhavemap = (npieces+7) / 8;
970         havemap = mallocz(nhavemap, 1);
971         for(i = 0; i<npieces; i++){
972                 pieces[i].hash = (uchar*)d->str + i*20;
973                 if(len < blocksize)
974                         pieces[i].len = len;
975                 else
976                         pieces[i].len = blocksize;
977                 len -= pieces[i].len;
978                 stats.left += pieces[i].len;
979         }
980         if(len)
981                 sysfatal("pieces do not match file length");
982
983         for(i = 0; i<npieces; i++)
984                 havepiece(i);
985
986         srand(time(0));
987         atnotify(killnote, 1);
988         switch(i = rfork(RFPROC|RFMEM|RFNOTEG)){
989         case -1:
990                 sysfatal("fork: %r");
991         case 0:
992                 memmove(peerid, "-NF9001-", 8);
993                 for(i=8; i<sizeof(peerid); i++)
994                         peerid[i] = nrand(10)+'0';
995                 server();
996                 tracker(url);
997                 tracker(dstr(dlook(torrent, "announce")));
998                 for(d = dlook(torrent, "announce-list"); d && d->typ == 'l'; d = d->next)
999                         if(d->val && d->val->typ == 'l')
1000                                 tracker(dstr(d->val->val));
1001                 while(waitpid() != -1)
1002                         ;
1003                 break;
1004         default:
1005                 killgroup = i;
1006                 while((nhavepieces < npieces) || sflag){
1007                         if(pflag)
1008                                 print("%d %d\n", nhavepieces, npieces);
1009                         sleep(1000);
1010                 }
1011         }
1012         postnote(PNGROUP, killgroup, "kill");
1013         exits(0);
1014 }