1 /*
2  * This file Copyright (C) 2012-2014 Mnemosyne LLC
3  *
4  * It may be used under the GNU GPL versions 2 or 3
5  * or any future license endorsed by Mnemosyne LLC.
6  *
7  */
8 
9 #include <stdio.h> /* fprintf() */
10 #include <stdlib.h> /* strtoul(), EXIT_FAILURE */
11 
12 #include <libtransmission/transmission.h>
13 #include <libtransmission/error.h>
14 #include <libtransmission/file.h>
15 #include <libtransmission/makemeta.h>
16 #include <libtransmission/tr-getopt.h>
17 #include <libtransmission/utils.h>
18 #include <libtransmission/version.h>
19 
20 #include "units.h"
21 
22 #define MY_NAME "transmission-create"
23 
24 #define MAX_TRACKERS 128
25 static uint32_t const KiB = 1024;
26 static tr_tracker_info trackers[MAX_TRACKERS];
27 static int trackerCount = 0;
28 static bool isPrivate = false;
29 static bool showVersion = false;
30 static char const* comment = NULL;
31 static char const* outfile = NULL;
32 static char const* infile = NULL;
33 static uint32_t piecesize_kib = 0;
34 
35 static tr_option options[] =
36 {
37     { 'p', "private", "Allow this torrent to only be used with the specified tracker(s)", "p", false, NULL },
38     { 'o', "outfile", "Save the generated .torrent to this filename", "o", true, "<file>" },
39     { 's', "piecesize", "Set how many KiB each piece should be, overriding the preferred default", "s", true, "<size in KiB>" },
40     { 'c', "comment", "Add a comment", "c", true, "<comment>" },
41     { 't', "tracker", "Add a tracker's announce URL", "t", true, "<url>" },
42     { 'V', "version", "Show version number and exit", "V", false, NULL },
43     { 0, NULL, NULL, NULL, false, NULL }
44 };
45 
getUsage(void)46 static char const* getUsage(void)
47 {
48     return "Usage: " MY_NAME " [options] <file|directory>";
49 }
50 
parseCommandLine(int argc,char const * const * argv)51 static int parseCommandLine(int argc, char const* const* argv)
52 {
53     int c;
54     char const* optarg;
55 
56     while ((c = tr_getopt(getUsage(), argc, argv, options, &optarg)) != TR_OPT_DONE)
57     {
58         switch (c)
59         {
60         case 'V':
61             showVersion = true;
62             break;
63 
64         case 'p':
65             isPrivate = true;
66             break;
67 
68         case 'o':
69             outfile = optarg;
70             break;
71 
72         case 'c':
73             comment = optarg;
74             break;
75 
76         case 't':
77             if (trackerCount + 1 < MAX_TRACKERS)
78             {
79                 trackers[trackerCount].tier = trackerCount;
80                 trackers[trackerCount].announce = (char*)optarg;
81                 ++trackerCount;
82             }
83 
84             break;
85 
86         case 's':
87             if (optarg != NULL)
88             {
89                 char* endptr = NULL;
90                 piecesize_kib = strtoul(optarg, &endptr, 10);
91 
92                 if (endptr != NULL && *endptr == 'M')
93                 {
94                     piecesize_kib *= KiB;
95                 }
96             }
97 
98             break;
99 
100         case TR_OPT_UNK:
101             infile = optarg;
102             break;
103 
104         default:
105             return 1;
106         }
107     }
108 
109     return 0;
110 }
111 
tr_getcwd(void)112 static char* tr_getcwd(void)
113 {
114     char* result;
115     tr_error* error = NULL;
116 
117     result = tr_sys_dir_get_current(&error);
118 
119     if (result == NULL)
120     {
121         fprintf(stderr, "getcwd error: \"%s\"", error->message);
122         tr_error_free(error);
123         result = tr_strdup("");
124     }
125 
126     return result;
127 }
128 
tr_main(int argc,char * argv[])129 int tr_main(int argc, char* argv[])
130 {
131     char* out2 = NULL;
132     tr_metainfo_builder* b = NULL;
133 
134     tr_logSetLevel(TR_LOG_ERROR);
135     tr_formatter_mem_init(MEM_K, MEM_K_STR, MEM_M_STR, MEM_G_STR, MEM_T_STR);
136     tr_formatter_size_init(DISK_K, DISK_K_STR, DISK_M_STR, DISK_G_STR, DISK_T_STR);
137     tr_formatter_speed_init(SPEED_K, SPEED_K_STR, SPEED_M_STR, SPEED_G_STR, SPEED_T_STR);
138 
139     if (parseCommandLine(argc, (char const* const*)argv) != 0)
140     {
141         return EXIT_FAILURE;
142     }
143 
144     if (showVersion)
145     {
146         fprintf(stderr, MY_NAME " " LONG_VERSION_STRING "\n");
147         return EXIT_SUCCESS;
148     }
149 
150     if (infile == NULL)
151     {
152         fprintf(stderr, "ERROR: No input file or directory specified.\n");
153         tr_getopt_usage(MY_NAME, getUsage(), options);
154         fprintf(stderr, "\n");
155         return EXIT_FAILURE;
156     }
157 
158     if (outfile == NULL)
159     {
160         tr_error* error = NULL;
161         char* base = tr_sys_path_basename(infile, &error);
162 
163         if (base == NULL)
164         {
165             fprintf(stderr, "ERROR: Cannot deduce output path from input path: %s\n", error->message);
166             return EXIT_FAILURE;
167         }
168 
169         char* end = tr_strdup_printf("%s.torrent", base);
170         char* cwd = tr_getcwd();
171         outfile = out2 = tr_buildPath(cwd, end, NULL);
172         tr_free(cwd);
173         tr_free(end);
174         tr_free(base);
175     }
176 
177     if (trackerCount == 0)
178     {
179         if (isPrivate)
180         {
181             fprintf(stderr, "ERROR: no trackers specified for a private torrent\n");
182             return EXIT_FAILURE;
183         }
184         else
185         {
186             printf("WARNING: no trackers specified\n");
187         }
188     }
189 
190     printf("Creating torrent \"%s\" ...", outfile);
191     fflush(stdout);
192 
193     b = tr_metaInfoBuilderCreate(infile);
194 
195     if (b == NULL)
196     {
197         fprintf(stderr, "ERROR: Cannot find specified input file or directory.\n");
198         return EXIT_FAILURE;
199     }
200 
201     if (piecesize_kib != 0)
202     {
203         tr_metaInfoBuilderSetPieceSize(b, piecesize_kib * KiB);
204     }
205 
206     tr_makeMetaInfo(b, outfile, trackers, trackerCount, comment, isPrivate);
207 
208     while (!b->isDone)
209     {
210         tr_wait_msec(500);
211         putc('.', stdout);
212         fflush(stdout);
213     }
214 
215     putc(' ', stdout);
216 
217     switch (b->result)
218     {
219     case TR_MAKEMETA_OK:
220         printf("done!");
221         break;
222 
223     case TR_MAKEMETA_URL:
224         printf("bad announce URL: \"%s\"", b->errfile);
225         break;
226 
227     case TR_MAKEMETA_IO_READ:
228         printf("error reading \"%s\": %s", b->errfile, tr_strerror(b->my_errno));
229         break;
230 
231     case TR_MAKEMETA_IO_WRITE:
232         printf("error writing \"%s\": %s", b->errfile, tr_strerror(b->my_errno));
233         break;
234 
235     case TR_MAKEMETA_CANCELLED:
236         printf("cancelled");
237         break;
238     }
239 
240     putc('\n', stdout);
241 
242     tr_metaInfoBuilderFree(b);
243     tr_free(out2);
244     return EXIT_SUCCESS;
245 }
246