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