buildtorrent: document proposed filelist feature (not yet implemented in code)
[buildtorrent:buildtorrent.git] / buildtorrent.c
1 /*
2 buildtorrent -- torrent file creation program
3 Copyright (C) 2007,2008,2009 Claude Heiland-Allen
4
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 */
20
21 #include "config.h"
22
23 #include <getopt.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/stat.h>
28 #include <inttypes.h>
29 #include <dirent.h>
30 #include <time.h>
31 /* for correct behaviour ensure this is the POSIX and not the GNU version */
32 #include <libgen.h>
33
34 #include "sha1.h"
35 #include "md5.h"
36
37
38 /******************************************************************************
39 program version string
40 ******************************************************************************/
41 #define bt_version "0.8~svn"
42 const char* __version__ = bt_version;
43
44 /******************************************************************************
45 torrent data structure declarations
46 ******************************************************************************/
47
48 /* structure for torrent file data */
49 struct _bt_data;
50 typedef struct _bt_data *bt_data;
51
52 /* error flags */
53 enum _bt_error {
54   BT_ERROR_NONE = 0,
55   BT_ERROR_NULL,
56   BT_ERROR_TYPE,
57   BT_ERROR_MEMORY,
58   BT_ERROR_IO,
59   BT_ERROR_OVERFLOW
60 };
61 typedef enum _bt_error bt_error;
62
63 /* attempt to support large files */
64 typedef int64_t integer;
65
66 /* constructors */
67 bt_data bt_string(const unsigned int length, const char *data);
68 bt_data bt_integer(const integer number);
69 bt_data bt_list(void);
70 bt_data bt_dictionary(void);
71
72 /* append to a list */
73 bt_error bt_list_append(bt_data list, const bt_data data);
74
75 /* insert into a dictionary, maintaining sorted keys */
76 bt_error bt_dictionary_insert(
77   bt_data dictionary,
78   const unsigned int keylength, const char *keyname,
79   const bt_data data
80 );
81
82 /* deep-copy a structure */
83 bt_data bt_copy(const bt_data data);
84
85 /* destructor */
86 void bt_free(bt_data bd);
87
88 /* write as a torrent file */
89 bt_error bt_write(FILE *f, const bt_data bd);
90
91 /* types of structures */
92 enum _bt_type {
93   BT_TYPE_STRING = 1000,
94   BT_TYPE_INTEGER,
95   BT_TYPE_LIST,
96   BT_TYPE_DICTIONARY
97 };
98 typedef enum _bt_type bt_type;
99
100 /* string structure */
101 struct _bt_string {
102   unsigned int length;
103   char *data;
104 };
105
106 /* integer structure */
107 struct _bt_integer {
108   integer number;
109 };
110
111 /* list structure */
112 struct _bt_list {
113   struct _bt_data *item;
114   struct _bt_list *next;
115 };
116
117 /* dictionary structure */
118 struct _bt_dictionary {
119   struct _bt_string *key;
120   struct _bt_data *value;
121   struct _bt_dictionary *next;
122 };
123
124 /* general structure */
125 struct _bt_data {
126   bt_type type;
127   union {
128     struct _bt_string *string;
129     struct _bt_integer *integer;
130     struct _bt_list *list;
131     struct _bt_dictionary *dictionary;
132   } b;
133 };
134
135 /******************************************************************************
136 allocate a new string structure
137 ******************************************************************************/
138 bt_data bt_string(const unsigned int length, const char *data) {
139   bt_data bd = malloc(sizeof(struct _bt_data));
140   if (!bd) {
141     return NULL;
142   }
143   {
144   struct _bt_string *s = malloc(sizeof(struct _bt_string));
145   if (!s) {
146     free(bd);
147     return NULL;
148   }
149   s->data = malloc(length);
150   if (!s->data) {
151     free(s);
152     free(bd);
153     return NULL;
154   }
155   s->length = length;
156   memcpy(s->data, data, length);
157   bd->type = BT_TYPE_STRING;
158   bd->b.string = s;
159   }
160   return bd;
161 }
162
163 /******************************************************************************
164 allocate a new integer structure
165 ******************************************************************************/
166 bt_data bt_integer(const integer number) {
167   bt_data bd = malloc(sizeof(struct _bt_data));
168   if (!bd) {
169     return NULL;
170   }
171   {
172   struct _bt_integer *n = malloc(sizeof(struct _bt_integer));
173   if (!n) {
174     free(bd);
175     return NULL;
176   }
177   n->number = number;
178   bd->type = BT_TYPE_INTEGER;
179   bd->b.integer = n;
180   }
181   return bd;
182 }
183
184 /******************************************************************************
185 allocate a new empty list structure
186 invariant: bd->b.list != NULL && last(bd->b.list).{item, next} == NULL
187 ******************************************************************************/
188 bt_data bt_list(void) {
189   bt_data bd = malloc(sizeof(struct _bt_data));
190   if (!bd) {
191     return NULL;
192   }
193   {
194   struct _bt_list *l = malloc(sizeof(struct _bt_list));
195   if (!l) {
196     free(bd);
197     return NULL;
198   }
199   l->item = NULL;
200   l->next = NULL;
201   bd->type = BT_TYPE_LIST;
202   bd->b.list = l;
203   }
204   return bd;
205 }
206
207 /******************************************************************************
208 allocate a new empty dictionary structure
209 invariant: bd->b.dictionary != NULL &&
210            last(bd->b.list).{key, value, next} == NULL &&
211            ordered ascending by key
212 ******************************************************************************/
213 bt_data bt_dictionary(void) {
214   bt_data bd = malloc(sizeof(struct _bt_data));
215   if (!bd) {
216     return NULL;
217   }
218   {
219   struct _bt_dictionary *d = malloc(sizeof(struct _bt_dictionary));
220   if (!d) {
221     free(bd);
222     return NULL;
223   }
224   d->key = NULL;
225   d->value = NULL;
226   d->next = NULL;
227   bd->type = BT_TYPE_DICTIONARY;
228   bd->b.dictionary = d;
229   }
230   return bd;
231 }
232
233 /******************************************************************************
234 append an item to a list
235 the item is not copied
236 returns an error flag
237 ******************************************************************************/
238 bt_error bt_list_append(bt_data list, const bt_data data) {
239   if (!list) {
240     return BT_ERROR_NULL;
241   }
242   if (BT_TYPE_LIST != list->type) {
243     return BT_ERROR_TYPE;
244   }
245   if (!list->b.list) {
246     return BT_ERROR_NULL;
247   }
248   {
249   struct _bt_list *current;
250   struct _bt_list *end = malloc(sizeof(struct _bt_list));
251   if (!end) {
252     return BT_ERROR_MEMORY;
253   }
254   end->item = NULL;
255   end->next = NULL;
256   current = list->b.list;
257   while (current->next) {
258     current = current->next;
259   }
260   current->item = data;
261   current->next = end;
262   }
263   return BT_ERROR_NONE;
264 }
265
266 /******************************************************************************
267 insert an item into a dictionary
268 the value is not copied, the key is copied
269 returns an error flag
270 maintains an ascending ordering of key names
271 FIXME: assumes key names are null terminated
272 ******************************************************************************/
273 bt_error bt_dictionary_insert(
274   bt_data dictionary,
275   const unsigned int keylength, const char *keyname,
276   const bt_data data
277 ) {
278   if (!dictionary) {
279     return BT_ERROR_NULL;
280   }
281   if (BT_TYPE_DICTIONARY != dictionary->type) {
282     return BT_ERROR_TYPE;
283   }
284   if (!dictionary->b.dictionary) {
285     return BT_ERROR_NULL;
286   }
287   {
288   struct _bt_dictionary *current;
289   struct _bt_dictionary *lastcurrent = NULL;
290   struct _bt_dictionary *insertee;
291   struct _bt_string *key = malloc(sizeof(struct _bt_string));
292   if (!key) {
293     return BT_ERROR_MEMORY;
294   }
295   key->data = malloc(keylength);
296   if (!key->data) {
297     free(key);
298     return BT_ERROR_MEMORY;
299   }
300   if (!(insertee = malloc(sizeof(struct _bt_dictionary)))) {
301     free(key->data);
302     free(key);
303     return BT_ERROR_MEMORY;
304   }
305   memcpy(key->data, keyname, keylength);
306   key->length = keylength;
307   insertee->key = key;
308   insertee->value = data;
309   insertee->next = NULL;
310   current = dictionary->b.dictionary;
311   while (
312     current->next && current->key && (0 < strcmp(keyname, current->key->data))
313   ) {
314     lastcurrent = current;
315     current = current->next;
316   }
317   if (lastcurrent) {
318     insertee->next = current;
319     lastcurrent->next = insertee;
320   } else {
321     insertee->next = dictionary->b.dictionary;
322     dictionary->b.dictionary = insertee;
323   }
324   }
325   return BT_ERROR_NONE;
326 }
327
328 /******************************************************************************
329 deep copy a data structure
330 FIXME: doesn't handle dictionaries yet
331 ******************************************************************************/
332 bt_data bt_copy(const bt_data data) {
333   if (!data) {
334     return NULL;
335   }
336   switch (data->type) {
337   case (BT_TYPE_STRING): {
338     return bt_string(data->b.string->length, data->b.string->data);
339   }
340   case (BT_TYPE_INTEGER): {
341     return bt_integer(data->b.integer->number);
342   }
343   case (BT_TYPE_LIST): {
344     struct _bt_list *current;
345     bt_error err;
346     bt_data list = bt_list();
347     if (!list) {
348       return NULL;
349     }
350     current = data->b.list;
351     while (current && current->item) {
352       if ((err = bt_list_append(list, bt_copy(current->item)))) {
353         bt_free(list);
354         return NULL;
355       }
356       current = current->next;
357     }
358     return list;
359   }
360   default: {
361     return NULL;
362   }
363   }
364 }
365
366 /******************************************************************************
367 free a string
368 ******************************************************************************/
369 void bt_free_string(struct _bt_string *s) {
370   if (!s) {
371     return;
372   }
373   if (s->data) {
374     free(s->data);
375   }
376   free(s);
377 }
378
379 /******************************************************************************
380 deep free a data structure
381 ******************************************************************************/
382 void bt_free(bt_data bd) {
383   if (!bd) {
384     return;
385   }
386   switch (bd->type) {
387   case (BT_TYPE_STRING): {
388     if (bd->b.string) {
389       bt_free_string(bd->b.string);
390     }
391     break;
392   }
393   case (BT_TYPE_INTEGER): {
394     if (bd->b.integer) {
395       free(bd->b.integer);
396     }
397     break;
398   }
399   case (BT_TYPE_LIST): {
400     struct _bt_list *current;
401     struct _bt_list *next;
402     current = bd->b.list;
403     while (current) {
404       next = current->next;
405       if (current->item) {
406         bt_free(current->item);
407       }
408       free(current);
409       current = next;
410     }
411     break;
412   }
413   case (BT_TYPE_DICTIONARY): {
414     struct _bt_dictionary *current;
415     struct _bt_dictionary *next;
416     current = bd->b.dictionary;
417     while (current) {
418       next = current->next;
419       if (current->key) {
420         bt_free_string(current->key);
421       }
422       if (current->value) {
423         bt_free(current->value);
424       }
425       free(current);
426       current = next;
427     }
428     break;
429   }
430   default:
431     break;
432   }
433   free(bd);
434 }
435
436 /******************************************************************************
437 write a string in torrent encoding
438 ******************************************************************************/
439 bt_error bt_write_string(FILE *f, const struct _bt_string *s) {
440   if (!s) {
441     return BT_ERROR_NULL;
442   }
443   if (!s->data) {
444     return BT_ERROR_NULL;
445   }
446   if (0 > fprintf(f, "%d:", s->length)) {
447     return BT_ERROR_IO;
448   }
449   if (1 != fwrite(s->data, s->length, 1, f)) {
450     return BT_ERROR_IO;
451   }
452   return BT_ERROR_NONE;
453 }
454
455 /******************************************************************************
456 write a data structure in torrent encoding
457 ******************************************************************************/
458 bt_error bt_write(FILE *f, const bt_data bd) {
459   if (!bd) {
460     return BT_ERROR_NULL;
461   }
462   switch (bd->type) {
463   case (BT_TYPE_STRING): {
464     return bt_write_string(f, bd->b.string);
465     break;
466   }
467   case (BT_TYPE_INTEGER): {
468     if (!bd->b.integer) {
469       return BT_ERROR_NULL;
470     }
471     if (0 > fprintf(f, "i%" PRId64 "e", bd->b.integer->number)) {
472       return BT_ERROR_IO;
473     }
474     break;
475   }
476   case (BT_TYPE_LIST): {
477     struct _bt_list *current;
478     bt_error err;
479     current = bd->b.list;
480     if (0 > fprintf(f, "l")) {
481       return BT_ERROR_IO;
482     }
483     while (current->next) {
484       if (current->item) {
485         if ((err = bt_write(f, current->item))) {
486           return err;
487         }
488       }
489       current = current->next;
490     }
491     if (0 > fprintf(f, "e")) {
492       return BT_ERROR_IO;
493     }
494     break;
495   }
496   case (BT_TYPE_DICTIONARY): {
497     struct _bt_dictionary *current;
498     bt_error err;
499     current = bd->b.dictionary;
500     if (0 > fprintf(f, "d")) {
501       return BT_ERROR_IO;
502     }
503     while (current->next) {
504       if (current->key) {
505         if ((err = bt_write_string(f, current->key))) {
506           return err;
507         }
508         if (!current->value) {
509           return BT_ERROR_NULL;
510         }
511         if ((err = bt_write(f, current->value))) {
512           return err;
513         }
514       }
515       current = current->next;
516     }
517     if (0 > fprintf(f, "e")) {
518       return BT_ERROR_IO;
519     }
520     break;
521   }
522   default: {
523     return BT_ERROR_TYPE;
524     break;
525   }
526   }
527   return BT_ERROR_NONE;
528 }
529
530 /******************************************************************************
531 pretty print torrent data
532 ******************************************************************************/
533 void bt_show(bt_data bd, int piecestoo, int indent, int indentstep, int comma) {
534   int i;
535   if (!bd) {
536     for(i = 0; i < indent; i++) {
537       printf(" ");
538     }
539     printf("NULL%s\n", comma ? "," : "");
540     return;
541   }
542   switch (bd->type) {
543   case (BT_TYPE_STRING): {
544     char strbuf[512];
545     int len = bd->b.string->length > 500 ? 500 : bd->b.string->length;
546     memcpy(strbuf, bd->b.string->data, len);
547     strbuf[len] = '\0';
548     for(i = 0; i < indent; i++) {
549       printf(" ");
550     }
551     printf(
552       "\"%s\"%s%s\n", strbuf, comma ? "," : "", len == 500 ? " // truncated!" : ""
553     );
554     break;
555   }
556   case (BT_TYPE_INTEGER): {
557     for(i = 0; i < indent; i++) {
558       printf(" ");
559     }
560     printf("%" PRId64 "%s\n", bd->b.integer->number, comma ? "," : "");
561     break;
562   }
563   case (BT_TYPE_LIST): {
564     struct _bt_list *current;
565     for(i = 0; i < indent; i++) {
566       printf(" ");
567     }
568     printf("[\n");
569     current = bd->b.list;
570     while (current->next) {
571       bt_show(
572         current->item, piecestoo,
573         indent + indentstep, indentstep,
574         current->next->next != NULL
575       );
576       current = current->next;
577     }
578     for(i = 0; i < indent; i++) {
579       printf(" ");
580     }
581     printf("]%s\n", comma ? "," : "");
582     break;
583   }
584   case (BT_TYPE_DICTIONARY): {
585     struct _bt_dictionary *current;
586     char strbuf[512];
587     int len;
588     for(i = 0; i < indent; i++) {
589       printf(" ");
590     }
591     printf("{\n");
592     current = bd->b.dictionary;
593     while (current->next) {
594       len = current->key->length > 500 ? 500 : current->key->length;
595       memcpy(strbuf, current->key->data, len);
596       strbuf[len] = '\0';
597       for(i = 0; i < indent + indentstep; i++) {
598         printf(" ");
599       }
600       printf("\"%s\" =>%s\n", strbuf, len == 500 ? " // truncated!" : "");
601       if (strcmp("pieces", strbuf) == 0) {
602         if (piecestoo) {
603           printf("----------------------------------------");
604           char *hexdigits = "0123456789abcdef";
605           for (i = 0; i < current->value->b.string->length; i++) {
606             if ((i % 20) == 0) {
607               printf("\n");
608             }
609             printf("%c%c",
610               hexdigits[(current->value->b.string->data[i] & 0xF0) >> 4],
611               hexdigits[(current->value->b.string->data[i] & 0x0F)]
612             );
613           }
614           printf("\n----------------------------------------\n");
615         } else {
616           for(i = 0; i < indent + indentstep + indentstep; i++) {
617             printf(" ");
618           }
619           printf("\"...\"%s // pieces not shown\n", current->next->next ? "," : "");
620         }
621       } else {
622         bt_show(
623           current->value, piecestoo, indent + indentstep + indentstep,
624           indentstep, current->next->next != NULL
625         );
626       }
627       current = current->next;
628     }
629     for(i = 0; i < indent; i++) {
630       printf(" ");
631     }
632     printf("}%s\n", comma ? "," : "");
633     break;
634   }
635   }
636 }
637
638
639 /******************************************************************************
640 hash queue data structure
641 the first in the list is a dummy
642 ******************************************************************************/
643 struct _bt_hash_queue {
644   integer size;
645   char *file;
646   struct _bt_hash_queue *next;
647 };
648 typedef struct _bt_hash_queue *bt_hash_queue;
649
650 /******************************************************************************
651 create the dummy first list element
652 ******************************************************************************/
653 bt_hash_queue bt_create_hash_queue() {
654   bt_hash_queue queue = malloc(sizeof(struct _bt_hash_queue));
655   if (!queue) {
656     return NULL;
657   }
658   queue->size = 0;
659   queue->file = NULL;
660   queue->next = NULL;
661   return queue;
662 }
663
664 /******************************************************************************
665 append to the hash queue
666 copies the arguments
667 ******************************************************************************/
668 bt_error bt_hash_enqueue(
669   bt_hash_queue queue, const char *file, integer size, int verbose
670 ) {
671   if (!queue) {
672     return BT_ERROR_NULL;
673   }
674   if (!file) {
675     return BT_ERROR_NULL;
676   }
677   {
678   bt_hash_queue current;
679   bt_hash_queue node = malloc(sizeof(struct _bt_hash_queue));
680   if (!node) {
681     return BT_ERROR_MEMORY;
682   }
683   if (!(node->file = malloc(strlen(file) + 1))) {
684     free(node);
685     return BT_ERROR_MEMORY;
686   }
687   memcpy(node->file, file, strlen(file) + 1);
688   node->size = size;
689   node->next = NULL;
690   current = queue;
691   while (current->next) {
692     current = current->next;
693   }
694   current->next = node;
695   if (verbose) {
696     fprintf(stderr, "%24" PRId64 " : %s\n", size, file);
697   }
698   return BT_ERROR_NONE;
699   }
700 }
701
702 /******************************************************************************
703 append a file to a path string
704 path must be at least length maxlength bytes
705 returns an error flag
706 ******************************************************************************/
707 bt_error bt_joinpath(size_t maxlength, char *path, const char *file) {
708   if (strlen(path) + strlen(file) + 1 + 1 > maxlength) {
709     return BT_ERROR_OVERFLOW;
710   } else {
711     size_t oldlength = strlen(path);
712     if (oldlength > 0) {
713       path[oldlength] = '/';
714       memcpy(path + oldlength + 1, file, strlen(file) + 1);
715     } else {
716       memcpy(path, file, strlen(file) + 1);
717     }
718     return BT_ERROR_NONE;
719   }
720 }
721
722
723 /******************************************************************************
724 md5sum a file
725 ******************************************************************************/
726
727 bt_data bt_md5sum_file(const char *filename, integer length) {
728   struct MD5Context context;
729   char *hexdigits = "0123456789abcdef";
730   unsigned char *buffer = NULL;
731   unsigned char digest[16];
732   char hexdump[33];
733   integer buflen = 262144;
734   integer size = 0;
735   integer left = length;
736   integer i;
737   FILE *file = NULL;
738   if (!filename) {
739     return NULL;
740   }
741   if (!(buffer = malloc(buflen))) {
742     return NULL;
743   }
744   if (!(file = fopen(filename, "rb"))) {
745     free(buffer);
746     return NULL;
747   }
748   MD5Init(&context);
749   while (left > 0) {
750     if (left > buflen) {
751        size = buflen;
752     } else {
753        size = left;
754     }
755     if (1 != fread(buffer, size, 1, file)) {
756       free(buffer);
757       fclose(file);
758       return NULL;
759     }
760     MD5Update(&context, buffer, size);
761     left -= size;
762   }
763   MD5Final(digest, &context);
764   for (i = 0; i < 16; ++i) {
765     hexdump[i+i+0] = hexdigits[(digest[i] & 0xF0) >> 4];
766     hexdump[i+i+1] = hexdigits[(digest[i] & 0x0F)];
767   }
768   free(buffer);
769   fclose(file);
770   return bt_string(32, hexdump);
771 }
772
773
774
775 /******************************************************************************
776 find files recursively
777 directory is the initial directory
778 path is a buffer of maxlength bytes to store the accumulated path
779 pathlist is the accumulated path exploded as a list
780 files is a list to add the found files to
781 returns an error flag
782 ******************************************************************************/
783 bt_error bt_findfiles(
784   DIR *directory, size_t maxlength, char *path, bt_data pathlist,
785   bt_data files, bt_hash_queue queue, int domd5sum, int verbose
786 ) {
787   struct dirent *entry;
788   bt_data file = NULL;
789   bt_data filename = NULL;
790   bt_data filesize = NULL;
791   bt_data filepath = NULL;
792   bt_data md5sum = NULL;
793   bt_error err = BT_ERROR_NONE;
794   while ((entry = readdir(directory))) {
795     if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")) {
796       struct stat s;
797       size_t oldlength = strlen(path);
798       if ((err = bt_joinpath(maxlength, path, entry->d_name))) {
799         return err;
800       }
801       if (!stat(path, &s)) {
802         if (S_ISREG(s.st_mode)) {
803           if ((file = bt_dictionary())) {
804             if ((filesize = bt_integer(s.st_size))) {
805               if ((
806                 filename = bt_string(strlen(entry->d_name), entry->d_name)
807               )) {
808                 if ((filepath = bt_copy(pathlist))) {
809                   if (!(err = bt_list_append(filepath, filename))) {
810                     if (!(err = bt_dictionary_insert(
811                       file, strlen("length"), "length", filesize
812                     ))) {
813     if (domd5sum) {
814       if ((md5sum = bt_md5sum_file(path, s.st_size))) {
815         err = bt_dictionary_insert(file, strlen("md5sum"), "md5sum", md5sum);
816       } else {
817         err = BT_ERROR_IO;
818       }
819     }
820     if (!err) {
821     
822                       if (!(err = bt_dictionary_insert(
823                         file, strlen("path"), "path", filepath
824                       ))) {
825                         if (!(err = bt_list_append(files, file))) {
826                           err = bt_hash_enqueue(queue, path, s.st_size, verbose);
827                         }
828                       }
829                     }
830     }
831                   }
832                 } else {
833                   err = BT_ERROR_MEMORY;
834                 }
835               } else {
836                 err = BT_ERROR_MEMORY;
837               }
838             } else {
839               err = BT_ERROR_MEMORY;
840             }
841           } else {
842             err = BT_ERROR_MEMORY;
843           }
844         } else if (S_ISDIR(s.st_mode)) {
845           DIR* dir;
846           if ((dir = opendir(path))) {
847             if ((
848               filename = bt_string(strlen(entry->d_name), entry->d_name)
849             )) {
850               if ((filepath = bt_copy(pathlist))) {
851                 if (!(err = bt_list_append(filepath, filename))) {
852                   err = bt_findfiles(
853                     dir, maxlength, path, filepath, files, queue, domd5sum, verbose
854                   );
855                   closedir(dir);
856                 }
857               } else {
858                 err = BT_ERROR_MEMORY;
859               }
860             } else {
861               err = BT_ERROR_MEMORY;
862             }
863           } else {
864             err = BT_ERROR_IO;
865           }
866         }
867       } else {
868         err = BT_ERROR_IO;
869       }
870       path[oldlength] = '\0';
871     }
872   }
873   return err;
874 }
875
876
877 /******************************************************************************
878 draw progressbar
879 ******************************************************************************/
880
881 void bt_progressbar(integer piece, integer count) {
882   integer oldblocks = ((piece - 1) * 50) / count;
883   integer blocks = (piece * 50) / count;
884   integer i;
885   char s[53];
886   if (blocks != oldblocks) {
887     s[0] = '[';
888     for (i = 0; i < 50; i++) {
889       s[i+1] = (i < blocks) ? '=' : ((i == blocks) ? '>' : ' ');
890     }
891     s[51] = ']';
892     s[52] = '\0';
893     fprintf(
894       stderr,
895       "\b\b\b\b\b\b\b\b\b\b"
896       "\b\b\b\b\b\b\b\b\b\b"
897       "\b\b\b\b\b\b\b\b\b\b"
898       "\b\b\b\b\b\b\b\b\b\b"
899       "\b\b\b\b\b\b\b\b\b\b"
900       "\b\b%s",
901       s
902     );
903   }
904 }
905
906
907 /******************************************************************************
908 hash files
909 ******************************************************************************/
910
911 bt_data bt_hash_pieces(bt_hash_queue queue, integer size, int verbose) {
912   bt_hash_queue node = NULL;
913   char *hashdata = NULL;
914   integer total = 0;
915   unsigned char *buffer = NULL;
916   unsigned char *bufptr = NULL;
917   integer remain = size;
918   integer left;
919   FILE *file = NULL;
920   integer piececount;
921   integer i;
922   sha1_byte digest[SHA1_DIGEST_LENGTH];
923   if (!queue) {
924     return NULL;
925   }
926   if (!(buffer = malloc(size))) {
927     return NULL;
928   }
929   node = queue;
930   while (node->next) {
931     node = node->next;
932     total += node->size;
933   }
934   piececount = (total + size - 1) / size; /* ceil(total/size) */
935   if (piececount <= 0) { /* FIXME: no idea what to do if there's no data */
936     free(buffer);
937     fprintf(stderr, "torrent has no data, aborting!\n");
938     return NULL;
939   }
940   if (verbose) {
941     fprintf(stderr, "hashing %" PRId64 " pieces\n", piececount);
942     fprintf(stderr, "["
943                     "          "
944                     "          "
945                     "          "
946                     "          "
947                     "          "
948                     "]");
949   }
950   if (!(hashdata = malloc(piececount * SHA1_DIGEST_LENGTH))) {
951     free(buffer);
952     return NULL;
953   }
954   node = queue->next;
955   file = fopen(node->file, "rb");
956   if (!file) {
957     free(buffer);
958     return NULL;
959   }
960   left = node->size;
961   bufptr = buffer;
962   for (i = 0; i < piececount; ++i) {
963     do {
964       if (left <= remain) {
965         /* take all */
966         if (left != 0) { /* don't fail on empty files */
967           if (1 != fread(bufptr, left, 1, file)) {
968             fclose(file);
969             free(buffer);
970             return NULL;
971           }
972           bufptr += left;
973           remain -= left;
974           fclose(file);
975           file = NULL;
976         }
977         node = node->next;
978         if (node) {
979           file = fopen(node->file, "rb");
980           if (!file) {
981             free(buffer);
982             return NULL;
983           }
984           left = node->size;
985         }
986       } else { /* left > remain */
987         /* take as much as we can */
988         if (remain != 0) { /* don't fail on empty files */
989           if (1 != fread(bufptr, remain, 1, file)) {
990             free(buffer);
991             return NULL;
992           }
993           bufptr += remain;
994           left -= remain;
995           remain = 0;
996         }
997       }
998     } while (remain != 0 && node);
999     if (!node && i != piececount - 1) {
1000       /* somehow the pieces don't add up */
1001       if (file) {
1002         fclose(file);
1003       }
1004       free(buffer);
1005       return NULL;
1006     }
1007     /* remain == 0 || i == piececount - 1 */
1008     if (verbose) {
1009       bt_progressbar(i, piececount);
1010     }
1011     SHA1(buffer, size - remain, digest);
1012     memcpy(hashdata + i * SHA1_DIGEST_LENGTH, digest, SHA1_DIGEST_LENGTH);
1013     bufptr = buffer;
1014     remain = size;
1015   }
1016   if (verbose) {
1017     bt_progressbar(piececount, piececount);
1018     fprintf(stderr, "\n");
1019   }
1020   return bt_string(SHA1_DIGEST_LENGTH * piececount, hashdata);
1021 }
1022
1023 /******************************************************************************
1024 parse bencoded data, eg a torrent file
1025 ******************************************************************************/
1026 /*
1027 bt_data bt_parse_torrent(FILE *in) {
1028   
1029 }
1030 */
1031
1032 /******************************************************************************
1033 parse an announce list
1034 format = "url|url|url,url|url|url,url|url|url"
1035 ******************************************************************************/
1036
1037 bt_data bt_parse_announcelist(const char *urls) {
1038   bt_data announcelist;
1039   bt_data tier;
1040   const char *s;
1041   const char *t;
1042   const char *t1;
1043   const char *t2;
1044   if (!urls) {
1045     return NULL;
1046   }
1047   if (strcmp("", urls) == 0) {
1048     return NULL;
1049   }
1050   announcelist = bt_list();
1051   if (!announcelist) {
1052     return NULL;
1053   }
1054   s = urls;
1055   tier = bt_list();
1056   do {
1057     t = NULL;
1058     t1 = strchr(s, '|');
1059     t2 = strchr(s, ',');
1060     if (!t1 && !t2) {
1061       t = s + strlen(s);
1062     } else if (!t1) {
1063       t = t2;
1064     } else if (!t2) {
1065       t = t1;
1066     } else {
1067       t = (t1 < t2) ? t1 : t2;
1068     }
1069     if (t <= s) {
1070       return NULL;
1071     }
1072     if (bt_list_append(tier, bt_string(t - s, s))) {
1073       return NULL;
1074     }
1075     if (t[0] == ',' || t[0] == '\0') {
1076       if (bt_list_append(announcelist, tier)) {
1077         return NULL;
1078       };
1079       if (t[0] != '\0') {
1080         tier = bt_list();
1081       } else {
1082         tier = NULL;
1083       }
1084     }
1085     s = t + 1;
1086   } while (t[0] != '\0');
1087   return announcelist;
1088 }
1089
1090 /******************************************************************************
1091 show usage message
1092 ******************************************************************************/
1093 void bt_usage(void) {
1094   printf(
1095     "Usage:\n"
1096     "  buildtorrent [OPTIONS] -a announceurl input output\n"
1097     "  buildtorrent [OPTIONS] -a announceurl -f files -n name output\n"
1098     "\n"
1099     "options:\n"
1100     "--announce      -a  <announce>   : announce url (required)\n"
1101     "--filelist      -f  <filelist>   : file list (requires '-n')\n"
1102     "--dirname       -n  <dirname>    : torrent directory name\n"
1103     "--announcelist  -A  <announces>  : announce url list\n"
1104     "--piecelength   -l  <length>     : piece length in bytes, default 262144\n"
1105     "--comment       -c  <comment>    : user comment, omitted by default\n"
1106     "--private       -p  <private>    : private flag, either 0 or 1\n"
1107     "--nodate        -D               : omit 'creation date' field\n"
1108     "--nocreator     -C               : omit 'created by' field\n"
1109     "--md5sum        -m               : add an 'md5sum' field for each file\n"
1110     "--show          -s               : show generated torrent structure\n"
1111     "--showall       -S               : show pieces too (implies '-s')\n"
1112     "--quiet         -q               : quiet operation\n"
1113     "--version       -V               : show version of buildtorrent\n"
1114     "--help          -h               : show this help screen\n"
1115   );
1116 }
1117
1118 /******************************************************************************
1119 main program
1120 ******************************************************************************/
1121 int main(int argc, char **argv) {
1122
1123   char *url = NULL;
1124   char *urls = NULL;
1125   char *inname = NULL;
1126   char *namebase = NULL;
1127   char *outfile = NULL;
1128   char *commentstr = NULL;
1129   unsigned int plen = 262144;
1130   int verbose = 1;
1131   int nodate = 0;
1132   int nocreator = 0;
1133   int privated = 0;
1134   int privateopt = 0;
1135   int domd5sum = 0;
1136   int show = 0;
1137   int slen;
1138   int i;
1139
1140   DIR *dir;
1141   FILE *output;
1142   bt_data torrent;
1143   bt_data announce;
1144   bt_data announcelist = NULL;
1145   bt_data info;
1146   bt_data piecelength;
1147   bt_data pieces;
1148   bt_data files;
1149   bt_data name;
1150   bt_data length;
1151   bt_data pathlist;
1152   bt_data creator;
1153   bt_data creationdate;
1154   bt_data comment;
1155   bt_data private;
1156   bt_data md5sum;
1157   bt_hash_queue queue;
1158
1159   struct stat s;
1160   char path[8192];
1161   char nametemp[8192];
1162
1163   while (1) {
1164     int optidx = 0;
1165     static struct option options[] = {
1166       { "announce", 1, 0, 'a' },
1167       { "announcelist", 1, 0, 'A' },
1168       { "piecelength", 1, 0, 'l' },
1169       { "comment", 1, 0, 'c' },
1170       { "private", 1, 0, 'p' },
1171       { "nodate", 0, 0, 'D' },
1172       { "nocreator", 0, 0, 'C' },
1173       { "md5sum", 0, 0, 'm' },
1174       { "show", 0, 0, 's' },
1175       { "showpieces", 0, 0, 'S' },
1176       { "quiet", 0, 0, 'q' },
1177       { "version", 0, 0, 'V' },
1178       { "help", 0, 0, 'h' },
1179       { 0, 0, 0, 0 }
1180     };
1181     char c = getopt_long(argc, argv, "hVqSsmCDa:A:l:c:p:", options, &optidx );
1182     if (c == -1) {
1183       break;
1184     }
1185     switch (c) {
1186     case ('?'):
1187       return 1;
1188     case ('a'):
1189       url = optarg;
1190       break;
1191     case ('A'):
1192       urls = optarg;
1193       break;
1194     case ('l'):
1195       plen = atoi(optarg);
1196       break;
1197     case ('c'):
1198       commentstr = optarg;
1199       break;
1200     case ('p'):
1201       privated = 1;
1202       privateopt = (strcmp(optarg, "0") == 0) ? 0 : 1;
1203       break;
1204     case ('D'):
1205       nodate = 1;
1206       break;
1207     case ('C'):
1208       nocreator = 1;
1209       break;
1210     case ('m'):
1211       domd5sum = 1;
1212       break;
1213     case ('s'):
1214       show = 1;
1215       break;
1216     case ('S'):
1217       show = 2;
1218       break;
1219     case ('q'):
1220       verbose = 0;
1221       break;
1222     case ('V'):
1223       printf(
1224         "buildtorrent " bt_version " (GPL) 2007-9 "
1225         "Claude Heiland-Allen <claudiusmaximus@goto10.org>\n"
1226       );
1227       return 0;
1228     case ('h'):
1229       bt_usage();
1230       return 0;
1231     }
1232   }
1233   if (!url) {
1234     fprintf(stderr, "buildtorrent: announce url required\n");
1235     return 1;
1236   }
1237   if (plen <= 0) { /* avoid division by zero */
1238     fprintf(stderr, "buildtorrent: piece length must be greater than 0\n");
1239     return 1;
1240   }
1241   if (optind + 2 < argc) {
1242     fprintf(stderr, "buildtorrent: too many arguments\n");
1243     return 1;
1244   }
1245   if (optind + 2 > argc) {
1246     fprintf(stderr, "buildtorrent: too few arguments\n");
1247     return 1;
1248   }
1249
1250   inname  = argv[optind];
1251   outfile = argv[optind + 1];
1252
1253   /* handle paths correctly (note: requires POSIX basename(), not GNU) */
1254   strncpy(nametemp, inname, 8191);
1255   nametemp[8191] = '\0';
1256   namebase = basename(nametemp);
1257   slen = strlen(namebase);
1258   for (i = 0; i < slen; ++i) {
1259     if (namebase[i] == '/') {
1260       fprintf(
1261         stderr,
1262         "buildtorrent: BUG! input (\"%s\") munged (\"%s\") contains '/'.\n",
1263         inname,
1264         namebase
1265       );
1266       return 1;
1267     }
1268   }
1269
1270   if (stat(inname, &s)) {
1271     fprintf(stderr, "buildtorrent: could not stat \"%s\"\n", inname);
1272     return 1;
1273   }
1274
1275   if (!(torrent = bt_dictionary())) {
1276     fprintf(stderr, "buildtorrent: couldn't allocate torrent dictionary\n");
1277     return 1;
1278   }
1279   if (!(info = bt_dictionary())) {
1280     fprintf(stderr, "buildtorrent: couldn't allocate info dictionary\n");
1281     return 1;
1282   }
1283   if (!(announce = bt_string(strlen(url), url))) {
1284     fprintf(stderr, "buildtorrent: couldn't allocate announce string\n");
1285     return 1;
1286   }
1287   if (!(piecelength = bt_integer(plen))) {
1288     fprintf(stderr, "buildtorrent: couldn't allocate piece length integer\n");
1289     return 1;
1290   }
1291   if (!(name = bt_string(strlen(namebase), namebase))) {
1292     fprintf(stderr, "buildtorrent: couldn't allocate name string\n");
1293     return 1;
1294   }
1295   if (bt_dictionary_insert(info, strlen("name"), "name", name)) {
1296     fprintf(stderr, "buildtorrent: couldn't insert name into info\n");
1297     return 1;
1298   }
1299   if (bt_dictionary_insert(
1300     info, strlen("piece length"), "piece length", piecelength
1301   )) {
1302     fprintf(stderr, "buildtorrent: couldn't insert piece length into info\n");
1303     return 1;
1304   }
1305   if (urls) {
1306     if (!(announcelist = bt_parse_announcelist(urls))) {
1307       fprintf(stderr, "buildtorrent: error parsing announce-list argument\n");
1308       return 1;
1309     }
1310   }
1311
1312   if (!(queue = bt_create_hash_queue())) {
1313     fprintf(stderr, "buildtorrent: couldn't allocate hash queue\n");
1314     return 1;
1315   }
1316
1317   if (S_ISDIR(s.st_mode)) {
1318
1319     if (!(dir = opendir(inname))) {
1320       fprintf(stderr, "buildtorrent: couldn't open directory\n");
1321       return 1;
1322     }
1323     if (!(files = bt_list())) {
1324       fprintf(stderr, "buildtorrent: couldn't allocate files list\n");
1325       return 1;
1326     }
1327     if (!(pathlist = bt_list())) {
1328       fprintf(stderr, "buildtorrent: couldn't path list\n");
1329       return 1;
1330     }
1331     if (strlen(inname) > 8190) {
1332       fprintf(stderr, "buildtorrent: 'input' argument too long\n");
1333       return 1;
1334     }
1335     memcpy(path, inname, strlen(inname) + 1);
1336     if (bt_findfiles(
1337       dir, 8192, path, pathlist, files, queue, domd5sum, verbose
1338     )) {
1339       fprintf(stderr, "buildtorrent: error finding files\n");
1340       return 1;
1341     }
1342     closedir(dir);
1343     if (bt_dictionary_insert(info, strlen("files"), "files", files)) {
1344       fprintf(stderr, "buildtorrent: couldn't insert files into info\n");
1345       return 1;
1346     }
1347
1348   } else if (S_ISREG(s.st_mode)) {
1349
1350     if (!(length = bt_integer(s.st_size))) {
1351       fprintf(stderr, "buildtorrent: couldn't allocate length integer\n");
1352       return 1;
1353     }
1354     if (bt_dictionary_insert(info, strlen("length"), "length", length)) {
1355       fprintf(stderr, "buildtorrent: couldn't insert length into info\n");
1356       return 1;
1357     }
1358     if (domd5sum) {
1359       if (!(md5sum = bt_md5sum_file(inname, s.st_size))) {
1360         fprintf(stderr, "buildtorrent: couldn't md5sum \"%s\"\n", inname);
1361         return 1;
1362       }
1363       if (bt_dictionary_insert(info, strlen("md5sum"), "md5sum", md5sum)) {
1364         fprintf(stderr, "buildtorrent: couldn't insert md5sum into info\n");
1365         return 1;
1366       }
1367     }
1368     if ((bt_hash_enqueue(queue, inname, s.st_size, verbose))) {
1369       fprintf(stderr, "buildtorrent: error enqueueing file for hashing\n");
1370       return 1;
1371     }
1372
1373   } else {
1374     fprintf(
1375       stderr, "buildtorrent: \"%s\" is neither file nor directory\n", inname
1376     );
1377     return 1;
1378   }
1379
1380   if (!(pieces = bt_hash_pieces(queue, plen, verbose))) {
1381     fprintf(stderr, "buildtorrent: error hashing files\n");
1382     return 1;
1383   }
1384   if (bt_dictionary_insert(info, strlen("pieces"), "pieces", pieces)) {
1385     fprintf(stderr, "buildtorrent: couldn't insert pieces into info\n");
1386     return 1;
1387   }
1388   if (privated) {
1389     if (!(private = bt_integer(privateopt))) {
1390       fprintf(stderr, "buildtorrent: couldn't allocate private integer\n");
1391       return 1;
1392     }
1393     if (bt_dictionary_insert(info, strlen("private"), "private", private)) {
1394       fprintf(stderr, "buildtorrent: couldn't insert private into info\n");
1395       return 1;
1396     }
1397   }  
1398   if (bt_dictionary_insert(torrent, strlen("announce"), "announce", announce)) {
1399     fprintf(stderr, "buildtorrent: couldn't insert announce into torrent\n");
1400     return 1;
1401   }
1402   if (urls) {
1403     if (bt_dictionary_insert(torrent, strlen("announce-list"), "announce-list", announcelist)) {
1404       fprintf(stderr, "buildtorrent: couldn't insert announce-list into torrent\n");
1405       return 1;
1406     }
1407   }
1408   if (bt_dictionary_insert(torrent, strlen("info"), "info", info)) {
1409     fprintf(stderr, "buildtorrent: couldn't insert info into torrent\n");
1410     return 1;
1411   }
1412   if (!nodate) {
1413     if (!(creationdate = bt_integer(time(NULL)))) {
1414       fprintf(stderr, "buildtorrent: couldn't allocate creation date integer\n");
1415       return 1;
1416     }
1417     if (bt_dictionary_insert(
1418       torrent, strlen("creation date"), "creation date", creationdate
1419     )) {
1420       fprintf(stderr, "buildtorrent: couldn't insert creation date into torrent\n");
1421       return 1;
1422     }
1423   }
1424   if (!nocreator) {
1425     if (!(creator = bt_string(strlen("buildtorrent/" bt_version), "buildtorrent/" bt_version))) {
1426       fprintf(stderr, "buildtorrent: couldn't allocate created by string\n");
1427       return 1;
1428     }
1429     if (bt_dictionary_insert(
1430       torrent, strlen("created by"), "created by", creator
1431     )) {
1432       fprintf(stderr, "buildtorrent: couldn't insert created by into torrent\n");
1433       return 1;
1434     }
1435   }
1436   if (commentstr) {
1437     if (!(comment = bt_string(strlen(commentstr), commentstr))) {
1438       fprintf(stderr, "buildtorrent: couldn't allocate comment string\n");
1439       return 1;
1440     }
1441     if (bt_dictionary_insert(
1442       torrent, strlen("comment"), "comment", comment
1443     )) {
1444       fprintf(stderr, "buildtorrent: couldn't insert comment into torrent\n");
1445       return 1;
1446     }
1447   }
1448   if (!(output = fopen(outfile, "wb"))) {
1449     fprintf(stderr, "buildtorrent: couldn't open \"%s\" for writing\n", outfile);
1450     return 1;
1451   }
1452   if (bt_write(output, torrent)) {
1453     fprintf(stderr, "buildtorrent: error writing \"%s\"\n", outfile);
1454     return 1;
1455   }
1456   if (show) {
1457     printf("torrent =>\n");
1458     bt_show(torrent, show == 2, 2, 2, 0);
1459   }
1460   bt_free(torrent);
1461   fclose(output);
1462   return 0;
1463 }
1464
1465 /* EOF */