updated on Thu Jan 26 16:09:46 UTC 2012
[aur-mirror.git] / transmission-b4rt / cli.c
blob33bf32274c254ca88a01773fcbbac360586e5202
1 /******************************************************************************
2 * $Id: cli.c 10084 2010-02-02 22:45:22Z charles $
4 * Copyright (c) 2005-2006 Transmission authors and contributors
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <signal.h>
31 #include <libtransmission/transmission.h>
32 #include <libtransmission/bencode.h>
33 #include <libtransmission/makemeta.h>
34 #include <libtransmission/tr-getopt.h>
35 #include <libtransmission/utils.h> /* tr_wait_msec */
36 #include <libtransmission/version.h>
37 #include <libtransmission/web.h> /* tr_webRun */
39 #define LINEWIDTH 80
40 #define MY_NAME "transmissioncli"
42 //Torrentflux
43 #define TOF_DISPLAY_INTERVAL 5
44 #define TOF_DISPLAY_INTERVAL_STR "5"
45 #define TOF_DIEWHENDONE 0
46 #define TOF_DIEWHENDONE_STR "0"
47 #define TOF_CMDFILE_MAXLEN 65536
48 //END
50 static tr_bool showInfo = 0;
51 static tr_bool showScrape = 0;
52 static tr_bool isPrivate = 0;
53 static tr_bool verify = 0;
54 static sig_atomic_t gotsig = 0;
55 static sig_atomic_t manualUpdate = 0;
57 static const char * torrentPath = NULL;
58 static const char * finishCall = NULL;
59 static const char * sourceFile = NULL;
60 static const char * comment = NULL;
62 #define MAX_ANNOUNCE 128
63 static tr_tracker_info announce[MAX_ANNOUNCE];
64 static int announceCount = 0;
66 /* Torrentflux -START- */
67 //static volatile char tf_shutdown = 0;
68 static int TOF_dieWhenDone = TOF_DIEWHENDONE;
69 static int TOF_seedLimit = 0;
70 static int TOF_displayInterval = TOF_DISPLAY_INTERVAL;
71 static int TOF_checkCmd = 0;
73 static const char * TOF_owner = NULL;
74 static char * TOF_statFile = NULL;
75 static FILE * TOF_statFp = NULL;
76 static char * TOF_cmdFile = NULL;
77 static FILE * TOF_cmdFp = NULL;
78 static char TOF_message[512];
79 /* -END- */
81 static const struct tr_option options[] =
83 { 'a', "announce", "Set the new torrent's announce URL", "a", 1, "<url>" },
84 { 'b', "blocklist", "Enable peer blocklists", "b", 0, NULL },
85 { 'B', "no-blocklist", "Disable peer blocklists", "B", 0, NULL },
86 { 'c', "comment", "Set the new torrent's comment", "c", 1, "<comment>" },
87 { 'd', "downlimit", "Set max download speed in KB/s", "d", 1, "<speed>" },
88 { 'D', "no-downlimit", "Don't limit the download speed", "D", 0, NULL },
89 { 910, "encryption-required", "Encrypt all peer connections", "er", 0, NULL },
90 { 911, "encryption-preferred", "Prefer encrypted peer connections", "ep", 0, NULL },
91 { 912, "encryption-tolerated", "Prefer unencrypted peer connections", "et", 0, NULL },
92 { 'f', "finish", "Run a script when the torrent finishes", "f", 1, "<script>" },
93 { 'g', "config-dir", "Where to find configuration files", "g", 1, "<path>" },
94 { 'i', "info", "Show torrent details and exit", "i", 0, NULL },
95 { 'm', "portmap", "Enable portmapping via NAT-PMP or UPnP", "m", 0, NULL },
96 { 'M', "no-portmap", "Disable portmapping", "M", 0, NULL },
97 { 'n', "new", "Create a new torrent", "n", 1, "<source>" },
98 { 'p', "port", "Port for incoming peers (Default: " TR_DEFAULT_PEER_PORT_STR ")", "p", 1, "<port>" },
99 { 'r', "private", "Set the new torrent's 'private' flag", "r", 0, NULL },
100 { 's', "scrape", "Scrape the torrent and exit", "s", 0, NULL },
101 { 't', "tos", "Peer socket TOS (0 to 255, default=" TR_DEFAULT_PEER_SOCKET_TOS_STR ")", "t", 1, "<tos>" },
102 { 'u', "uplimit", "Set max upload speed in KB/s", "u", 1, "<speed>" },
103 { 'U', "no-uplimit", "Don't limit the upload speed", "U", 0, NULL },
104 { 'v', "verify", "Verify the specified torrent", "v", 0, NULL },
105 { 'w', "download-dir", "Where to save downloaded data", "w", 1, "<path>" },
106 //Torrentflux Commands:
107 { 'E', "display-interval","Time between updates of stat-file (default = "TOF_DISPLAY_INTERVAL_STR")","E",1,"<int>"},
108 { 'L', "seedlimit","Seed-Limit (Percent) to reach before shutdown","L",1,"<int>"},
109 { 'O', "owner","Name of the owner (default = 'n/a')","O",1,"<string>"},
110 { 'W', "die-when-done", "Auto-Shutdown when done (0 = Off, 1 = On, default = "TOF_DIEWHENDONE_STR")","W",1,NULL},
111 //END
112 { 0, NULL, NULL, NULL, 0, NULL }
115 static const char *
116 getUsage( void )
118 return "A fast and easy BitTorrent client\n"
119 "\n"
120 "Usage: " MY_NAME " [options] <file|url|magnet>";
123 static int parseCommandLine( tr_benc*, int argc, const char ** argv );
125 static void sigHandler( int signal );
127 /* Torrentflux -START- */
128 static int TOF_processCommands(tr_session *h);
129 static int TOF_execCommand(tr_session *h, char *s);
130 static void TOF_print ( char *printmsg );
131 static void TOF_free ( void );
132 static int TOF_initStatus ( void );
133 static void TOF_writeStatus ( const tr_stat *s, const tr_info *info,
134 const int state, const char *status );
135 static int TOF_initCommand ( void );
136 static int TOF_writePID ( void );
137 static void TOF_deletePID ( void );
138 static int TOF_writeAllowed ( void );
139 /* -END- */
142 static char*
143 tr_strlratio( char * buf,
144 double ratio,
145 size_t buflen )
147 if( (int)ratio == TR_RATIO_NA )
148 tr_strlcpy( buf, _( "None" ), buflen );
149 else if( (int)ratio == TR_RATIO_INF )
150 tr_strlcpy( buf, "Inf", buflen );
151 else if( ratio < 10.0 )
152 tr_snprintf( buf, buflen, "%.2f", ratio );
153 else if( ratio < 100.0 )
154 tr_snprintf( buf, buflen, "%.1f", ratio );
155 else
156 tr_snprintf( buf, buflen, "%.0f", ratio );
157 return buf;
160 static int
161 is_rfc2396_alnum( char ch )
163 return ( '0' <= ch && ch <= '9' )
164 || ( 'A' <= ch && ch <= 'Z' )
165 || ( 'a' <= ch && ch <= 'z' );
168 static void
169 escape( char * out,
170 const uint8_t * in,
171 int in_len ) /* rfc2396 */
173 const uint8_t *end = in + in_len;
175 while( in != end )
176 if( is_rfc2396_alnum( *in ) )
177 *out++ = (char) *in++;
178 else
179 out += tr_snprintf( out, 4, "%%%02X", (unsigned int)*in++ );
181 *out = '\0';
184 static void
185 torrentCompletenessChanged( tr_torrent * torrent UNUSED,
186 tr_completeness completeness UNUSED,
187 void * user_data UNUSED )
189 system( finishCall );
192 static tr_bool waitingOnWeb;
194 static void
195 onTorrentFileDownloaded( tr_session * session UNUSED,
196 long response_code UNUSED,
197 const void * response,
198 size_t response_byte_count,
199 void * ctor )
201 tr_ctorSetMetainfo( ctor, response, response_byte_count );
202 waitingOnWeb = FALSE;
205 static int leftToScrape = 0;
207 static void
208 scrapeDoneFunc( tr_session * session UNUSED,
209 long response_code,
210 const void * response,
211 size_t response_byte_count,
212 void * host )
214 tr_benc top, *files;
216 if( !tr_bencLoad( response, response_byte_count, &top, NULL )
217 && tr_bencDictFindDict( &top, "files", &files )
218 && files->val.l.count >= 2 )
220 int64_t complete = -1, incomplete = -1, downloaded = -1;
221 tr_benc * hash = &files->val.l.vals[1];
222 tr_bencDictFindInt( hash, "complete", &complete );
223 tr_bencDictFindInt( hash, "incomplete", &incomplete );
224 tr_bencDictFindInt( hash, "downloaded", &downloaded );
225 printf( "%4d seeders, %4d leechers, %5d downloads at %s\n",
226 (int)complete, (int)incomplete, (int)downloaded,
227 (char*)host );
228 tr_bencFree( &top );
230 else
231 fprintf( stderr, "Unable to parse response (http code %lu) at %s",
232 response_code,
233 (char*)host );
235 --leftToScrape;
237 tr_free( host );
240 static void
241 dumpInfo( FILE * out,
242 const tr_info * inf )
244 int i;
245 int prevTier = -1;
246 tr_file_index_t ff;
248 fprintf( out, "hash:\t" );
249 for( i = 0; i < SHA_DIGEST_LENGTH; ++i )
250 fprintf( out, "%02x", inf->hash[i] );
251 fprintf( out, "\n" );
253 fprintf( out, "name:\t%s\n", inf->name );
255 for( i = 0; i < inf->trackerCount; ++i )
257 if( prevTier != inf->trackers[i].tier )
259 prevTier = inf->trackers[i].tier;
260 fprintf( out, "\ntracker tier #%d:\n", ( prevTier + 1 ) );
262 fprintf( out, "\tannounce:\t%s\n", inf->trackers[i].announce );
265 fprintf( out, "size:\t%" PRIu64 " (%" PRIu64 " * %d + %" PRIu64 ")\n",
266 inf->totalSize, inf->totalSize / inf->pieceSize,
267 inf->pieceSize, inf->totalSize % inf->pieceSize );
269 if( inf->comment && *inf->comment )
270 fprintf( out, "comment:\t%s\n", inf->comment );
271 if( inf->creator && *inf->creator )
272 fprintf( out, "creator:\t%s\n", inf->creator );
273 if( inf->isPrivate )
274 fprintf( out, "private flag set\n" );
276 fprintf( out, "file(s):\n" );
277 for( ff = 0; ff < inf->fileCount; ++ff )
278 fprintf( out, "\t%s (%" PRIu64 ")\n", inf->files[ff].name,
279 inf->files[ff].length );
282 static void
283 getStatusStr( const tr_stat * st, const tr_info *information )
287 char TOF_eta[50];
288 if( st->activity & TR_STATUS_CHECK_WAIT )
290 TOF_writeStatus(st, information, 1, "Waiting to verify local files" );
292 else if( st->activity & TR_STATUS_CHECK )
295 tr_snprintf( buf, buflen,
296 "Verifying local files (%.2f%%, %.2f%% valid)",
297 tr_truncd( 100 * st->recheckProgress, 2 ),
298 tr_truncd( 100 * st->percentDone, 2 ) );
300 sprintf(TOF_eta, "%.2f%% Verifying local files",100 * st->recheckProgress);
301 TOF_writeStatus(st, information, 1, TOF_eta );
303 else if( st->activity & TR_STATUS_DOWNLOAD )
318 if( TOF_writeAllowed() )
320 strcpy(TOF_eta,"");
321 if ( st->eta > 0 )
323 if ( st->eta < 604800 ) // 7 days
325 if ( st->eta >= 86400 ) // 1 day
326 sprintf(TOF_eta, "%d:",
327 st->eta / 86400);
329 if ( st->eta >= 3600 ) // 1 hour
330 sprintf(TOF_eta, "%s%02d:",
331 TOF_eta,((st->eta % 86400) / 3600));
333 if ( st->eta >= 60 ) // 1 Minute
334 sprintf(TOF_eta, "%s%02d:",
335 TOF_eta,((st->eta % 3600) / 60));
337 sprintf(TOF_eta, "%s%02d",
338 TOF_eta,(st->eta % 60));
340 else
341 sprintf(TOF_eta, "-");
344 if ((st->seeders < -1) && (st->peersConnected == 0))
345 sprintf(TOF_eta, "Connecting to Peers");
347 TOF_writeStatus(st, information, 1, TOF_eta );
350 else if( st->activity & TR_STATUS_SEED )
358 if (TOF_dieWhenDone == 1)
360 TOF_print( "Die-when-done set, setting shutdown-flag...\n" );
361 gotsig = 1;
365 else
367 if (TOF_seedLimit == -1)
369 TOF_print( "Sharekill set to -1, setting shutdown-flag...\n" );
370 gotsig = 1;
372 else if ( ( TOF_seedLimit > 0 ) && ( ( st->ratio * 100.0 ) > (float)TOF_seedLimit ) )
374 sprintf( TOF_message, "Seed-limit %d%% reached, setting shutdown-flag...\n", TOF_seedLimit );
375 TOF_print( TOF_message );
376 gotsig = 1;
379 TOF_writeStatus(st, information, 1, "Download Succeeded" );
381 if( st->error )
383 sprintf( TOF_message, "error: %s\n", st->errorString );
384 TOF_print( TOF_message );
388 static const char*
389 getConfigDir( int argc, const char ** argv )
391 int c;
392 const char * configDir = NULL;
393 const char * optarg;
394 const int ind = tr_optind;
396 while(( c = tr_getopt( getUsage( ), argc, argv, options, &optarg ))) {
397 if( c == 'g' ) {
398 configDir = optarg;
399 break;
403 tr_optind = ind;
405 if( configDir == NULL )
406 configDir = tr_getDefaultConfigDir( MY_NAME );
408 return configDir;
412 main( int argc,
413 char ** argv )
415 int i, error;
416 tr_session * h;
417 tr_ctor * ctor;
418 tr_torrent * tor = NULL;
419 tr_benc settings;
420 const tr_info * information;
421 const char * configDir;
422 char cwd[1024];
423 tr_bool haveSource;
424 tr_bool haveAnnounce;
425 uint8_t * fileContents;
426 size_t fileLength;
428 printf( "Transmission %s - http://www.transmissionbt.com/ - modified for Torrentflux-b4rt\n",
429 LONG_VERSION_STRING );
431 /* user needs to pass in at least one argument */
432 if( argc < 2 ) {
433 tr_getopt_usage( MY_NAME, getUsage( ), options );
434 return EXIT_FAILURE;
437 /* load the defaults from config file + libtransmission defaults */
438 tr_bencInitDict( &settings, 0 );
439 configDir = getConfigDir( argc, (const char**)argv );
440 //tr_sessionLoadSettings( &settings, configDir, MY_NAME );
442 /* the command line overrides defaults */
443 if( parseCommandLine( &settings, argc, (const char**)argv ) )
445 printf("Invalid commandline option given\n");
446 return EXIT_FAILURE;
449 /* Check the options for validity */
450 if( !torrentPath ) {
451 fprintf( stderr, "No torrent specified!\n" );
452 return EXIT_FAILURE;
454 tr_bencDictRemove( &settings, TR_PREFS_KEY_DOWNLOAD_DIR );
455 getcwd( cwd, sizeof( cwd ) );
456 tr_bencDictAddStr( &settings, TR_PREFS_KEY_DOWNLOAD_DIR, cwd );
458 /* don't bind the port if we're just running the CLI
459 to get metainfo or to create a torrent */
460 if( showInfo || showScrape || ( sourceFile != NULL ) )
461 tr_bencDictAddInt( &settings, TR_PREFS_KEY_PEER_PORT, -1 );
463 h = tr_sessionInit( "cli", configDir, FALSE, &settings );
465 haveSource = sourceFile && *sourceFile;
466 haveAnnounce = announceCount > 0;
468 if( haveSource && !haveAnnounce )
469 fprintf( stderr, "Did you mean to create a torrent without a tracker's announce URL?\n" );
471 if( haveSource ) /* creating a torrent */
473 int err;
474 tr_metainfo_builder * b;
475 fprintf( stderr, "creating torrent \"%s\"\n", torrentPath );
477 b = tr_metaInfoBuilderCreate( sourceFile );
478 tr_makeMetaInfo( b, torrentPath, announce, announceCount, comment, isPrivate );
479 while( !b->isDone )
481 tr_wait_msec( 1000 );
482 printf( "." );
485 err = b->result;
486 tr_metaInfoBuilderFree( b );
487 return err;
490 ctor = tr_ctorNew( h );
491 tr_ctorSetMetainfoFromFile( ctor, torrentPath );
492 tr_ctorSetPaused( ctor, TR_FORCE, showScrape );
493 tr_ctorSetDownloadDir( ctor, TR_FORCE, cwd );
494 tr_ctorSetDownloadDir( ctor, TR_FALLBACK, cwd );
496 if( showScrape )
498 tr_info info;
500 if( !tr_torrentParse( ctor, &info ) )
502 int i;
503 const time_t start = time( NULL );
504 for( i = 0; i < info.trackerCount; ++i )
506 if( info.trackers[i].scrape )
508 const char * scrape = info.trackers[i].scrape;
509 char escaped[SHA_DIGEST_LENGTH * 3 + 1];
510 char * url, *host;
511 escape( escaped, info.hash, SHA_DIGEST_LENGTH );
512 url = tr_strdup_printf( "%s%cinfo_hash=%s",
513 scrape,
514 strchr( scrape,
515 '?' ) ? '&' : '?',
516 escaped );
517 tr_urlParse( scrape, -1, NULL, &host, NULL, NULL );
518 ++leftToScrape;
520 tr_webRun( h, url, NULL, scrapeDoneFunc, host );
521 tr_free( url );
525 fprintf( stderr, "scraping %d trackers:\n", leftToScrape );
527 while( leftToScrape > 0 && ( ( time( NULL ) - start ) < 20 ) )
528 tr_wait_msec( 250 );
530 goto cleanup;
532 //* Torrentflux -START- */
533 if (TOF_owner == NULL)
535 sprintf( TOF_message, "No owner supplied, using 'n/a'.\n" );
536 TOF_print( TOF_message );
537 TOF_owner = malloc((4) * sizeof(char));
538 if (TOF_owner == NULL)
540 sprintf( TOF_message, "Error : not enough mem for malloc\n" );
541 TOF_print( TOF_message );
542 goto failed;
546 // Output for log
547 sprintf( TOF_message, "transmission %s starting up :\n", LONG_VERSION_STRING );
548 TOF_print( TOF_message );
549 sprintf( TOF_message, " - torrent : %s\n", torrentPath );
550 TOF_print( TOF_message );
551 sprintf( TOF_message, " - owner : %s\n", TOF_owner );
552 TOF_print( TOF_message );
553 sprintf( TOF_message, " - dieWhenDone : %d\n", TOF_dieWhenDone );
554 TOF_print( TOF_message );
555 sprintf( TOF_message, " - seedLimit : %d\n", TOF_seedLimit );
556 TOF_print( TOF_message );
557 sprintf( TOF_message, " - bindPort : %d\n", tr_sessionGetPeerPort(h) );
558 TOF_print( TOF_message );
559 sprintf( TOF_message, " - uploadLimit : %d\n", tr_sessionGetSpeedLimit(h, TR_UP ) );
560 TOF_print( TOF_message );
561 sprintf( TOF_message, " - downloadLimit : %d\n", tr_sessionGetSpeedLimit(h, TR_DOWN ) );
562 TOF_print( TOF_message );
563 sprintf( TOF_message, " - natTraversal : %d\n", tr_sessionIsPortForwardingEnabled(h) );
564 TOF_print( TOF_message );
565 sprintf( TOF_message, " - displayInterval : %d\n", TOF_displayInterval );
566 TOF_print( TOF_message );
567 sprintf( TOF_message, " - downloadDir : %s\n", tr_sessionGetDownloadDir(h) );
568 TOF_print( TOF_message );
570 if (finishCall != NULL)
572 sprintf( TOF_message, " - finishCall : %s\n", finishCall );
573 TOF_print( TOF_message );
575 /* -END- */
578 if( showInfo )
580 tr_info info;
582 if( !tr_torrentParse( ctor, &info ) )
584 dumpInfo( stdout, &info );
585 tr_metainfoFree( &info );
588 tr_ctorFree( ctor );
589 goto cleanup;
592 tor = tr_torrentNew( ctor, &error );
593 sprintf( TOF_message, " - downloadDir from torrent object, usually loaded from resume data : %s\n", tr_torrentGetDownloadDir( tor ) );
594 TOF_print( TOF_message );
595 tr_ctorFree( ctor );
596 if( !tor )
598 //fprintf( stderr, "Failed opening torrent file `%s'\n", torrentPath );
600 sprintf( TOF_message, "Failed opening torrent file %s'\n", torrentPath );
601 TOF_print( TOF_message );
602 tr_sessionClose( h );
603 return EXIT_FAILURE;
606 signal( SIGINT, sigHandler );
607 #ifndef WIN32
608 signal( SIGHUP, sigHandler );
609 #endif
610 tr_torrentSetCompletenessCallback( tor, torrentCompletenessChanged, NULL );
611 tr_torrentStart( tor );
613 if( verify )
615 verify = 0;
616 tr_torrentVerify( tor );
619 /* Torrentflux -START */
621 // initialize status-facility
622 if (TOF_initStatus() == 0)
624 sprintf( TOF_message, "Failed to init status-facility. exit transmission.\n" );
625 TOF_print( TOF_message );
626 goto failed;
629 // initialize command-facility
630 if (TOF_initCommand() == 0)
632 sprintf( TOF_message, "Failed to init command-facility. exit transmission.\n" );
633 TOF_print( TOF_message );
634 goto failed;
637 // write pid
638 if (TOF_writePID() == 0)
640 sprintf( TOF_message, "Failed to write pid-file. exit transmission.\n" );
641 TOF_print( TOF_message );
642 goto failed;
645 sprintf( TOF_message, "Transmission up and running.\n" );
647 information = tr_torrentInfo( tor );
648 /* -END- */
651 for( ; ; )
653 //char line[LINEWIDTH];
654 const tr_stat * st;
655 const char * messageName[] = { NULL, "Tracker gave a warning:",
656 "Tracker gave an error:",
657 "Error:" };
659 /* Torrentflux -START */
660 TOF_checkCmd++;
662 if( TOF_checkCmd == TOF_displayInterval)
664 TOF_checkCmd = 1;
665 /* If Torrentflux wants us to shutdown */
666 if (TOF_processCommands(h))
667 gotsig = 1;
669 /* -END- */
671 tr_wait_msec( 200 );
674 if( gotsig )
676 gotsig = 0;
677 //printf( "\nStopping torrent...\n" );
678 tr_torrentStop( tor );
681 if( manualUpdate )
683 manualUpdate = 0;
684 if( !tr_torrentCanManualUpdate( tor ) )
686 fprintf(
687 stderr,
688 "\nReceived SIGHUP, but can't send a manual update now\n" );
690 else
692 fprintf( stderr,
693 "\nReceived SIGHUP: manual update scheduled\n" );
694 tr_torrentManualUpdate( tor );
698 st = tr_torrentStat( tor );
699 if( st->activity & TR_STATUS_STOPPED )
701 break;
704 getStatusStr( st, information);
705 //printf( "\r%-*s", LINEWIDTH, line );
707 if( messageName[st->error] )
708 fprintf( stderr, "\n%s: %s\n", messageName[st->error], st->errorString );
713 const tr_stat * st;
714 st = tr_torrentStat( tor );
716 TOF_print("Transmission shutting down...\n");
718 /* Try for 5 seconds to delete any port mappings for nat traversal */
719 tr_sessionSetPortForwardingEnabled( h, 0 );
720 for( i = 0; i < 10; i++ )
722 if( TR_PORT_UNMAPPED == tr_sessionIsPortForwardingEnabled( h ) )
724 /* Port mappings were deleted */
725 break;
727 tr_wait_msec( 500 );
729 if (st->percentDone >= 1)
730 TOF_writeStatus(st, information, 0, "Download Succeeded" );
731 else
732 TOF_writeStatus(st, information, 0, "Torrent Stopped" );
734 TOF_deletePID();
736 TOF_print("Transmission exit.\n");
738 TOF_free();
741 cleanup:
743 //tr_sessionSaveSettings( h, configDir, &settings );
745 printf( "\n" );
746 tr_bencFree( &settings );
747 tr_sessionClose( h );
748 return EXIT_SUCCESS;
750 failed:
751 TOF_free();
752 tr_torrentFree( tor );
753 tr_sessionClose( h );
754 return EXIT_FAILURE;
758 /***
759 ****
760 ****
761 ****
762 ***/
764 static int
765 parseCommandLine( tr_benc * d, int argc, const char ** argv )
767 int64_t downloadLimit, uploadLimit;
768 int c;
769 const char * optarg;
771 while(( c = tr_getopt( getUsage( ), argc, argv, options, &optarg )))
773 switch( c )
775 case 'a': if( announceCount + 1 < MAX_ANNOUNCE ) {
776 announce[announceCount].tier = announceCount;
777 announce[announceCount].announce = (char*) optarg;
778 ++announceCount;
780 break;
781 case 'b': tr_bencDictAddBool( d, TR_PREFS_KEY_BLOCKLIST_ENABLED, TRUE );
782 break;
783 case 'B': tr_bencDictAddBool( d, TR_PREFS_KEY_BLOCKLIST_ENABLED, FALSE );
784 break;
785 case 'c': comment = optarg;
786 break;
788 case 'd': downloadLimit=atoi( optarg );
789 switch (downloadLimit) {
790 case 0:
791 downloadLimit = -1;
792 break;
793 case -2:
794 downloadLimit = 0;
795 break;
797 if (downloadLimit>=0)
799 tr_bencDictAddInt( d, TR_PREFS_KEY_DSPEED, downloadLimit );
800 tr_bencDictAddInt( d, TR_PREFS_KEY_DSPEED_ENABLED, 1 );
802 else
803 tr_bencDictAddInt( d, TR_PREFS_KEY_DSPEED_ENABLED, 0 );
804 break;
805 case 'D': tr_bencDictAddBool( d, TR_PREFS_KEY_DSPEED_ENABLED, FALSE );
806 break;
807 case 'f': finishCall = optarg;
808 break;
809 case 'g': /* handled above */
810 break;
811 case 'i': showInfo = 1;
812 break;
813 case 'm': tr_bencDictAddBool( d, TR_PREFS_KEY_PORT_FORWARDING, TRUE );
814 break;
815 case 'M': tr_bencDictAddBool( d, TR_PREFS_KEY_PORT_FORWARDING, FALSE );
816 break;
817 case 'n': sourceFile = optarg; break;
818 case 'p': tr_bencDictAddInt( d, TR_PREFS_KEY_PEER_PORT, atoi( optarg ) );
819 break;
820 case 'r': isPrivate = 1;
821 break;
822 case 's': showScrape = 1;
823 break;
824 case 't': tr_bencDictAddInt( d, TR_PREFS_KEY_PEER_SOCKET_TOS, atoi( optarg ) );
825 break;
826 case 'u':
827 uploadLimit=atoi( optarg );
828 switch (uploadLimit) {
829 case 0:
830 uploadLimit = -1;
831 break;
832 case -2:
833 uploadLimit = 0;
834 break;
836 if (uploadLimit>=0)
838 tr_bencDictAddInt( d, TR_PREFS_KEY_USPEED, uploadLimit );
839 tr_bencDictAddInt( d, TR_PREFS_KEY_USPEED_ENABLED, 1 );
841 else
842 tr_bencDictAddInt( d, TR_PREFS_KEY_USPEED_ENABLED, 0 );
843 break;
844 case 'U': tr_bencDictAddBool( d, TR_PREFS_KEY_USPEED_ENABLED, FALSE );
845 break;
846 case 'v': verify = 1;
847 break;
848 case 'w': tr_bencDictAddStr( d, TR_PREFS_KEY_DOWNLOAD_DIR, optarg );
849 break;
850 case 'E':
851 TOF_displayInterval = atoi( optarg );
852 break;
853 case 'L':
854 TOF_seedLimit = atoi( optarg );
855 break;
856 case 'O':
857 TOF_owner = optarg;
858 break;
859 case 'W':
860 TOF_dieWhenDone = atoi( optarg );
861 break;
862 case 910: tr_bencDictAddInt( d, TR_PREFS_KEY_ENCRYPTION, TR_ENCRYPTION_REQUIRED );
863 break;
864 case 911: tr_bencDictAddInt( d, TR_PREFS_KEY_ENCRYPTION, TR_ENCRYPTION_PREFERRED );
865 break;
866 case 912: tr_bencDictAddInt( d, TR_PREFS_KEY_ENCRYPTION, TR_CLEAR_PREFERRED );
867 break;
868 case TR_OPT_UNK:
869 torrentPath = optarg;
870 break;
871 default: return 1;
875 return 0;
878 static void
879 sigHandler( int signal )
881 switch( signal )
883 case SIGINT:
884 gotsig = 1; break;
886 #ifndef WIN32
887 case SIGHUP:
888 manualUpdate = 1; break;
890 #endif
891 default:
892 break;
897 /* Torrentflux -START- */
898 static void TOF_print( char *printmsg )
900 time_t rawtime;
901 struct tm * timeinfo;
902 time(&rawtime);
903 timeinfo = localtime(&rawtime);
905 fprintf(stderr, "[%4d/%02d/%02d - %02d:%02d:%02d] %s",
906 timeinfo->tm_year + 1900,
907 timeinfo->tm_mon + 1,
908 timeinfo->tm_mday,
909 timeinfo->tm_hour,
910 timeinfo->tm_min,
911 timeinfo->tm_sec,
912 ((printmsg != NULL) && (strlen(printmsg) > 0)) ? printmsg : ""
916 static int TOF_initStatus( void )
918 int len = strlen(torrentPath) + 5;
919 TOF_statFile = malloc((len + 1) * sizeof(char));
920 if (TOF_statFile == NULL) {
921 TOF_print( "Error : TOF_initStatus: not enough mem for malloc\n" );
922 return 0;
925 sprintf( TOF_statFile, "%s.stat", torrentPath );
927 sprintf( TOF_message, "Initialized status-facility. (%s)\n", TOF_statFile );
928 TOF_print( TOF_message );
929 return 1;
932 static int TOF_initCommand( void )
934 int len = strlen(torrentPath) + 4;
935 TOF_cmdFile = malloc((len + 1) * sizeof(char));
936 if (TOF_cmdFile == NULL) {
937 TOF_print( "Error : TOF_initCommand: not enough mem for malloc\n" );
938 return 0;
940 sprintf( TOF_cmdFile, "%s.cmd", torrentPath );
942 sprintf( TOF_message, "Initialized command-facility. (%s)\n", TOF_cmdFile );
943 TOF_print( TOF_message );
945 // remove command-file if exists
946 TOF_cmdFp = NULL;
947 TOF_cmdFp = fopen(TOF_cmdFile, "r");
948 if (TOF_cmdFp != NULL)
950 fclose(TOF_cmdFp);
951 sprintf( TOF_message, "Removing command-file. (%s)\n", TOF_cmdFile );
952 TOF_print( TOF_message );
953 remove(TOF_cmdFile);
954 TOF_cmdFp = NULL;
956 return 1;
959 static int TOF_writePID( void )
961 FILE * TOF_pidFp;
962 char TOF_pidFile[strlen(torrentPath) + 4];
964 sprintf(TOF_pidFile,"%s.pid",torrentPath);
966 TOF_pidFp = fopen(TOF_pidFile, "w+");
967 if (TOF_pidFp != NULL)
969 fprintf(TOF_pidFp, "%d", getpid());
970 fclose(TOF_pidFp);
971 sprintf( TOF_message, "Wrote pid-file: %s (%d)\n",
972 TOF_pidFile , getpid() );
973 TOF_print( TOF_message );
974 return 1;
976 else
978 sprintf( TOF_message, "Error opening pid-file for writting: %s (%d)\n",
979 TOF_pidFile , getpid() );
980 TOF_print( TOF_message );
981 return 0;
985 static void TOF_deletePID( void )
987 char TOF_pidFile[strlen(torrentPath) + 4];
989 sprintf(TOF_pidFile,"%s.pid",torrentPath);
991 sprintf( TOF_message, "Removing pid-file: %s (%d)\n", TOF_pidFile , getpid() );
992 TOF_print( TOF_message );
994 remove(TOF_pidFile);
997 static void TOF_writeStatus( const tr_stat *s, const tr_info *info, const int state, const char *status )
999 if( !TOF_writeAllowed() && state != 0 ) return;
1001 TOF_statFp = fopen(TOF_statFile, "w+");
1002 if (TOF_statFp != NULL)
1004 float TOF_pd,TOF_ratio;
1005 int TOF_seeders,TOF_leechers;
1007 TOF_seeders = ( s->seeders < 0 ) ? 0 : s->seeders;
1008 TOF_leechers = ( s->leechers < 0 ) ? 0 : s->leechers;
1010 if (state == 0 && s->percentDone < 1)
1011 TOF_pd = ( -100.0 * s->percentDone ) - 100;
1012 else
1013 TOF_pd = 100.0 * s->percentDone;
1015 TOF_ratio = s->ratio < 0 ? 0 : s->ratio;
1017 fprintf(TOF_statFp,
1018 "%d\n%.1f\n%s\n%.1f kB/s\n%.1f kB/s\n%s\n%d (%d)\n%d (%d)\n%.1f\n%d\n%" PRIu64 "\n%" PRIu64 "\n%" PRIu64,
1019 state, /* State */
1020 TOF_pd, /* Progress */
1021 status, /* Status text */
1022 s->pieceDownloadSpeed, /* Download speed */ // versus rawDownloadSpeed
1023 s->pieceUploadSpeed, /* Upload speed */ // versus rawUploadSpeed
1024 TOF_owner, /* Owner */
1025 s->peersSendingToUs, TOF_seeders, /* Seeder */
1026 s->peersGettingFromUs, TOF_leechers, /* Leecher */
1027 100.0 * TOF_ratio, /* ratio */
1028 TOF_seedLimit, /* seedlimit */
1029 s->uploadedEver, /* uploaded bytes */
1030 s->downloadedEver, /* downloaded bytes */
1031 info->totalSize /* global size */
1033 fclose(TOF_statFp);
1036 else
1038 sprintf( TOF_message, "Error opening stat-file for writting: %s\n", TOF_statFile );
1039 TOF_print( TOF_message );
1043 static int TOF_processCommands(tr_session * h)
1045 /* return values:
1046 * 0 :: do not shutdown transmission
1047 * 1 :: shutdown transmission
1050 /* Now Process the CommandFile */
1052 int commandCount = 0;
1053 int isNewline;
1054 long fileLen;
1055 long index;
1056 long startPos;
1057 long totalChars;
1058 char currentLine[128];
1059 char *fileBuffer;
1060 char *fileCurrentPos;
1062 /* Try opening the CommandFile */
1063 TOF_cmdFp = NULL;
1064 TOF_cmdFp = fopen(TOF_cmdFile, "r");
1066 /* File does not exist */
1067 if( TOF_cmdFp == NULL )
1068 return 0;
1070 sprintf( TOF_message, "Processing command-file %s...\n", TOF_cmdFile );
1071 TOF_print( TOF_message );
1073 // get length
1074 fseek(TOF_cmdFp, 0L, SEEK_END);
1075 fileLen = ftell(TOF_cmdFp);
1076 rewind(TOF_cmdFp);
1078 if ( fileLen >= TOF_CMDFILE_MAXLEN || fileLen < 1 )
1080 if( fileLen >= TOF_CMDFILE_MAXLEN )
1081 sprintf( TOF_message, "Size of command-file too big, skip. (max-size: %d)\n", TOF_CMDFILE_MAXLEN );
1082 else
1083 sprintf( TOF_message, "No commands found in command-file.\n" );
1085 TOF_print( TOF_message );
1086 /* remove file */
1087 remove(TOF_cmdFile);
1088 goto finished;
1091 fileBuffer = calloc(fileLen + 1, sizeof(char));
1092 if (fileBuffer == NULL)
1094 TOF_print( "Not enough memory to read command-file\n" );
1095 /* remove file */
1096 remove(TOF_cmdFile);
1097 goto finished;
1100 fread(fileBuffer, fileLen, 1, TOF_cmdFp);
1101 fclose(TOF_cmdFp);
1102 remove(TOF_cmdFile);
1103 TOF_cmdFp = NULL;
1104 totalChars = 0L;
1105 fileCurrentPos = fileBuffer;
1107 while (*fileCurrentPos)
1109 index = 0L;
1110 isNewline = 0;
1111 startPos = totalChars;
1112 while (*fileCurrentPos)
1114 if (!isNewline)
1116 if ( *fileCurrentPos == 10 )
1117 isNewline = 1;
1119 else if (*fileCurrentPos != 10)
1121 break;
1123 ++totalChars;
1124 if ( index < 127 )
1125 currentLine[index++] = *fileCurrentPos++;
1126 else
1128 fileCurrentPos++;
1129 break;
1133 if ( index > 1 )
1135 commandCount++;
1136 currentLine[index - 1] = '\0';
1138 if (TOF_execCommand(h, currentLine))
1140 free(fileBuffer);
1141 return 1;
1146 if (commandCount == 0)
1147 TOF_print( "No commands found in command-file.\n" );
1149 free(fileBuffer);
1151 finished:
1152 return 0;
1155 static int TOF_execCommand(tr_session *h, char *s)
1157 int i, uploadLimit, downloadLimit;
1158 int len = strlen(s);
1159 char opcode;
1160 char workload[len];
1162 opcode = s[0];
1163 for (i = 0; i < len - 1; i++)
1164 workload[i] = s[i + 1];
1165 workload[len - 1] = '\0';
1167 switch (opcode)
1169 case 'q':
1170 TOF_print( "command: stop-request, setting shutdown-flag...\n" );
1171 return 1;
1173 case 'u':
1174 if (strlen(workload) < 1)
1176 TOF_print( "invalid upload-rate...\n" );
1177 return 0;
1180 uploadLimit = atoi(workload);
1181 sprintf( TOF_message, "command: setting upload-rate to %d...\n", uploadLimit );
1182 TOF_print( TOF_message );
1184 tr_sessionSetSpeedLimit( h, TR_UP, uploadLimit );
1185 tr_sessionLimitSpeed( h, TR_UP, uploadLimit > 0 );
1187 return 0;
1189 case 'd':
1190 if (strlen(workload) < 1)
1192 TOF_print( "invalid download-rate...\n" );
1193 return 0;
1196 downloadLimit = atoi(workload);
1197 sprintf( TOF_message, "command: setting download-rate to %d...\n", downloadLimit );
1198 TOF_print( TOF_message );
1200 tr_sessionSetSpeedLimit( h, TR_DOWN, downloadLimit );
1201 tr_sessionLimitSpeed( h, TR_DOWN, downloadLimit > 0 );
1202 return 0;
1204 case 'w':
1205 if (strlen(workload) < 1)
1207 TOF_print( "invalid die-when-done flag...\n" );
1208 return 0;
1211 switch (workload[0])
1213 case '0':
1214 TOF_print( "command: setting die-when-done to 0\n" );
1215 TOF_dieWhenDone = 0;
1216 break;
1217 case '1':
1218 TOF_print( "command: setting die-when-done to 1\n" );
1219 TOF_dieWhenDone = 1;
1220 break;
1221 default:
1222 sprintf( TOF_message, "invalid die-when-done flag: %c...\n", workload[0] );
1223 TOF_print( TOF_message );
1225 return 0;
1227 case 'l':
1228 if (strlen(workload) < 1)
1230 TOF_print( "invalid sharekill ratio...\n" );
1231 return 0;
1234 TOF_seedLimit = atoi(workload);
1235 sprintf( TOF_message, "command: setting sharekill to %d...\n", TOF_seedLimit );
1236 TOF_print( TOF_message );
1237 return 0;
1239 default:
1240 sprintf( TOF_message, "op-code unknown: %c\n", opcode );
1241 TOF_print( TOF_message );
1243 return 0;
1246 static int TOF_writeAllowed ( void )
1248 /* We want to write status every <TOF_displayInterval> seconds,
1249 but we also want to start in the first round */
1250 if( TOF_checkCmd == 1 ) return 1;
1251 return 0;
1254 static void TOF_free ( void )
1256 free(TOF_cmdFile);
1257 free(TOF_statFile);
1258 if(strcmp(TOF_owner,"n/a") == 0) free(TOF_owner);
1261 /* -END- */