diff options
author | Xavier Roche <xroche@users.noreply.github.com> | 2012-03-19 12:57:43 +0000 |
---|---|---|
committer | Xavier Roche <xroche@users.noreply.github.com> | 2012-03-19 12:57:43 +0000 |
commit | 64cc4a88da8887ef1f7f4d90be0158d2cc76222d (patch) | |
tree | e72af709fbce8bc495f51e7f0518de9a9a2c3b7f /src/mmsrip/main.c | |
parent | 844ecc37072d515513177c65a8c9dc35c9cdfc1a (diff) |
httrack 3.40.4
Diffstat (limited to 'src/mmsrip/main.c')
-rw-r--r-- | src/mmsrip/main.c | 753 |
1 files changed, 753 insertions, 0 deletions
diff --git a/src/mmsrip/main.c b/src/mmsrip/main.c new file mode 100644 index 0000000..a575b4a --- /dev/null +++ b/src/mmsrip/main.c @@ -0,0 +1,753 @@ +/* + * $RCSfile: main.c,v $ + * $Date: 2006/01/23 20:30:42 $ - $Revision: 1.32 $ + * + * This file is distributed as a part of MMSRIP ( MMS Ripper ). + * Copyright (c) 2005-2006 Nicolas BENOIT + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#define _GNU_SOURCE + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <time.h> +#include <sys/time.h> +#include "common.h" +#include "mms.h" +#include "error.h" + +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#endif + +/* + * options + */ +#if defined(SOLARIS) || defined(sun) +static char * options = "ahvtqko:d:g:"; +#else +static char * options = "-ahvtqko:d:g:"; +#endif + +#ifdef HAVE_GETOPT_LONG +static struct option long_options[] = { + {"about", 0, NULL, 'a'}, + {"version", 0, NULL, 'v'}, + {"help", 0, NULL, 'h'}, + {"test", 0, NULL, 't'}, + {"quiet", 0, NULL, 'q'}, + {"trick", 0, NULL, 'k'}, + {"output", 1, NULL, 'o'}, + {"delay", 1, NULL, 'd'}, + {"debug", 1, NULL, 'g'}, + {NULL, 0, NULL, 0 } +}; +#endif + +/* + * usage + */ +void +usage ( void ) +{ + fprintf ( stderr, "%s (%s) version %s\n\n", PROGRAM_SHORT_NAME, PROGRAM_FULL_NAME, PROGRAM_VERSION ); + fprintf ( stderr, "usage: %s <[-oFILE] stream url> ...\n\n", PROGRAM_SHORT_NAME ); + +#ifdef HAVE_GETOPT_LONG + fprintf ( stderr, "General Options:\n\t" \ + "-a, --about\t\tshow information about %s\n\t" \ + "-h, --help\t\tshow this help\n\t" \ + "-v, --version\t\tshow version number\n\n" \ + "Program Behaviour:\n\t" \ + "-oFILE, --output=FILE\toutput to specified file (can be repeated)\n\t" \ + "-gFILE, --debug=FILE\toutput debug info to specified file\n\t" \ + "-q, --quiet\t\tquiet mode (can be repeated)\n\t" \ + "-dDELAY, --delay=DELAY\tsave the stream during DELAY seconds and exit\n\t" \ + "-k, --trick\t\tattempt to trick recalcitrant servers\n\t" \ + "-t, --test\t\ttest mode (check stream availability)\n\n", PROGRAM_SHORT_NAME ); +#else + fprintf ( stderr, "General Options:\n\t" \ + "-a\tshow information about %s\n\t" \ + "-h\tshow this help\n\t" \ + "-v\tshow version number\n\n" \ + "Program Behaviour:\n\t" \ + "-oFILE\toutput to specified file (can be repeated)\n\t" \ + "-gFILE\toutput debug info to specified file\n\t" \ + "-q\tquiet mode (can be repeated)\n\t" \ + "-dDELAY\tsave the stream during DELAY seconds and exit\n\t" \ + "-k\tattempt to trick recalcitrant servers\n\t" \ + "-t\ttest mode (check stream availability)\n\n", PROGRAM_SHORT_NAME ); +#endif + + return; +} + + +/* + * main + */ +int +main ( int argc, + char *argv[] ) +{ + MMS *mms; + FILE *f; + FILE *stddebug = NULL; + char *output_filename; + STREAM_LIST *stream_list; + STREAM_LIST *block; + char c; + int ret = MMS_RET_SUCCESS; + int quiet = 0; + int trick = MMS_TRICK_DISABLED; + int retry = 0; + int test = 0; + int delay = 0; + time_t end = 0; + float speed = 0.0f; + struct timeval speed_last_update; + struct timeval now; + float elapsed_time; + ssize_t len_written; + uint64_t total_len_written = 0; + uint64_t old_total_len_written = 0; + + if ( ( stream_list = (STREAM_LIST *) malloc(sizeof(STREAM_LIST)) ) == NULL ) + { + error ( "main", "early initialization failed" ); + return 1; + } + + stream_list->next = NULL; + stream_list->stream = NULL; + stream_list->output = NULL; + + block = stream_list; + +#ifdef HAVE_GETOPT_LONG + while ( ( c = getopt_long(argc, argv, options, long_options, NULL) ) != -1 ) +#elif defined(SOLARIS) || defined(sun) + /* Implementation of getopt in Solaris is a bit strange, it returns -1 even if there are still args to parse... */ + while ( optind < argc ) +#else + while ( ( c = getopt(argc, argv, options) ) != -1 ) +#endif + { +#if defined(SOLARIS) || defined(sun) + c = getopt ( argc, argv, options ); +#endif + switch (c) + { + case 'h': + { + fprintf ( stderr, "\n" ); + usage(); + return 0; + } + + case 'v': + { + fprintf ( stderr, "%s version %s\n", PROGRAM_SHORT_NAME, PROGRAM_VERSION); + return 0; + } + + case 'a': + { + fprintf ( stderr, "\n" ); + fprintf ( stderr, "%s (%s) version %s\n\n", PROGRAM_SHORT_NAME, PROGRAM_FULL_NAME, PROGRAM_VERSION); + fprintf ( stderr, "Written by %s.\n", PROGRAM_AUTHORS ); + fprintf ( stderr, "With a lot of help from %s.\n\n", PROGRAM_HELPERS ); + fprintf ( stderr, "This program is free software; you can redistribute it and/or\nmodify it under the terms " \ + "of the GNU General Public License\nas published by the Free Software Foundation; either version 2" \ + ",\nor (at your option) any later version.\n\n" ); + return 0; + } + + case 't': + { + test = 1; + break; + } + + case 'q': + { + quiet += 1; + break; + } + + case 'k': + { + trick = MMS_TRICK_ENABLED; + break; + } + + case 'o': + { + if ( optarg != NULL ) + { + if ( block->stream != NULL ) + { + if ( ( block->next = malloc ( sizeof(STREAM_LIST) ) ) == NULL ) + { + error ( "main", "early initialization failed" ); + ret = MMS_RET_ERROR; + goto clean; + } + + block = block->next; + block->next = NULL; + block->stream = NULL; + } + + if ( block->output != NULL ) + free ( block->output ); + + block->output = (char *) strdup ( optarg ); + } + + break; + } + + case 'g': + { + if ( optarg != NULL ) + { + if ( stddebug != NULL ) + fclose ( stddebug ); + + if ( ( stddebug = fopen ( optarg, "w" ) ) == NULL ) + { + if ( quiet < 2 ) +#ifdef HAVE_VSNPRINTF + warning ( NULL, "unable to write debug info in \'%s\'", optarg ); +#else + warning ( NULL, "unable to write debug info in specified file" ); +#endif + } + else + { + fprintf ( stddebug, "%s (%s) version %s\n\n", PROGRAM_SHORT_NAME, PROGRAM_FULL_NAME, PROGRAM_VERSION); + fprintf ( stddebug, "--> debug log begins now\n" ); + } + } + + break; + } + + case 'd': + { + if ( optarg != NULL ) + { + delay = atoi( optarg ); + + if ( delay < 0 ) + delay = 0; + } + + break; + } + + +#if defined(SOLARIS) || defined(sun) + case -1: + { + if ( optind >= argc ) + break; +#else + case 1: + { + if ( optarg != NULL ) + { +#endif + if ( block->stream != NULL ) + { + if ( ( block->next = malloc ( sizeof(STREAM_LIST) ) ) == NULL ) + { + error ( "main", "early initialization failed" ); + ret = MMS_RET_ERROR; + goto clean; + } + + block = block->next; + block->next = NULL; + block->output = NULL; + } +#if defined(SOLARIS) || defined(sun) + /* optind is not incremented when meeting something else than an option, so we do that... */ + block->stream = (char *) strdup ( argv[optind++] ); +#else + block->stream = (char *) strdup ( optarg ); + } +#endif + break; + } + } + } + + if ( stream_list->stream == NULL ) + { + usage ( ); + ret = MMS_RET_ERROR; + goto clean; + } + + if ( !quiet ) + { + fprintf ( stderr, "\n" ); + fprintf ( stderr, "%s (%s) version %s\n\n", PROGRAM_SHORT_NAME, PROGRAM_FULL_NAME, PROGRAM_VERSION ); + } + + for ( block=stream_list; block!=NULL; block=(STREAM_LIST*)block->next ) + { + if ( block->stream == NULL ) + { + if ( quiet < 2 ) + { + if ( block->output == NULL ) +#ifdef HAVE_VSNPRINTF + error ( "main", "invalid invocation of %s", PROGRAM_SHORT_NAME ); +#else + error ( "main", "invalid invocation of mmsrip" ); +#endif + else +#ifdef HAVE_VSNPRINTF + error ( "main", "output to \'%s\' is not attached to any stream", block->output ); +#else + error ( "main", "one of the -o output is not attached to any stream" ); +#endif + } + + ret = MMS_RET_ERROR; + goto clean; + } + + if ( block->output == NULL ) + { + char *tmp = strchr ( block->stream, '/' ); + char *interro_ptr = strchr ( block->stream, '?' ); + + if ( interro_ptr == NULL ) + output_filename = strrchr ( block->stream, '/' ); + else + { + do /* we look for the last '/' before the first '?' */ + { + output_filename = tmp; + tmp = strchr ( tmp+1, '/' ); + } + while ( ( tmp < interro_ptr ) && ( tmp != NULL ) ); + } + + if ( output_filename == NULL ) + { + if ( quiet < 2 ) +#ifdef HAVE_VSNPRINTF + error ( "main", "malformed url: \'%s\'", block->stream ); +#else + error ( "main", "malformed url" ); +#endif + + ret = MMS_RET_ERROR; + continue; + } + + ++output_filename; + + if ( strlen ( output_filename ) == 0 ) + { + if ( quiet < 2 ) +#ifdef HAVE_VSNPRINTF + error ( "main", "malformed url: \'%s\'", block->stream ); +#else + error ( "main", "malformed url" ); +#endif + + ret = MMS_RET_ERROR; + continue; + } + + block->output = (char *) strdup ( output_filename ); + + /* we clean filenames that look like 'stream.asf?digest=7Q2bjXo&provider=lala' */ + if ( ( interro_ptr = strchr(block->output,'?') ) != NULL ) + *interro_ptr = '\0'; + } + } + + if ( ret != MMS_RET_SUCCESS ) + goto clean; + + if ( delay != 0 ) + end = time(NULL) + delay; + + for ( block=stream_list; block!=NULL; block=(STREAM_LIST*)(retry?block:block->next) ) + { + output_filename = block->output; + retry = 0; + + if ( !test ) + { + if ( ( f = fopen ( output_filename, "w" ) ) == NULL ) + { + if ( quiet < 2 ) +#ifdef HAVE_VSNPRINTF + error ( "main", "unable to write in \'%s\'", output_filename ); +#else + error ( "main", "unable to write in output file" ); +#endif + + ret = MMS_RET_ERROR; + continue; + } + } + else + f = NULL; + + if ( ( mms = mms_create ( block->stream, f, stddebug, trick, test?1:(quiet>>1) ) ) == NULL ) + { + if ( quiet < 2 ) + error ( "main", "unable to create mms struct" ); + + if ( !test ) + { + fclose ( f ); + remove ( output_filename ); + } + + ret = MMS_RET_ERROR; + continue; + } + + if ( !quiet ) + fprintf ( stderr, "Connecting ...\r" ); + + if ( mms_connect ( mms ) != MMS_RET_SUCCESS ) + { + if ( quiet < 2 ) + error ( "main", "unable to connect" ); + + mms_destroy ( mms ); + + if ( !test ) + { + fclose ( f ); + remove ( output_filename ); + } + + ret = MMS_RET_ERROR; + continue; + } + + if ( !quiet ) + fprintf ( stderr, "Handshaking ...\r" ); + + if ( mms_handshake ( mms ) != MMS_RET_SUCCESS ) + { + if ( ( quiet < 2 ) && ( !test ) ) + error ( "main", "unable to handshake" ); + + mms_disconnect ( mms ); + mms_destroy ( mms ); + + if ( !test ) + { + fclose ( f ); + remove ( output_filename ); + } + + if ( !quiet ) + { + if ( !test ) + fprintf ( stderr, "Stream \'%s\' is not good.\n\n", block->stream ); + else + fprintf ( stderr, "Stream \'%s\' is not good.\n", block->stream ); + } + + ret = MMS_RET_ERROR; + continue; + } + + if ( test ) + { + if ( !quiet ) + fprintf ( stderr, "Stream \'%s\' is available.\n", block->stream ); + + mms_disconnect ( mms ); + mms_destroy ( mms ); + continue; + } + + if ( !quiet ) + fprintf ( stderr, "Getting header ...\r" ); + + if ( ( len_written = mms_write_stream_header ( mms ) ) == MMS_RET_ERROR ) + { + if ( quiet < 2 ) + error ( "main", "unable to write stream header" ); + + mms_disconnect ( mms ); + mms_destroy ( mms ); + fclose ( f ); + remove ( output_filename ); + ret = MMS_RET_ERROR; + continue; + } + + total_len_written = len_written; + + if ( !quiet ) + fprintf ( stderr, "Rip is about to start ...\r" ); + + if ( mms_begin_rip ( mms ) != MMS_RET_SUCCESS ) + { + if ( quiet < 2 ) + error ( "main", "unable to begin the rip" ); + + mms_disconnect ( mms ); + mms_destroy ( mms ); + fclose ( f ); + remove ( output_filename ); + ret = -1; + continue; + } + + if ( mms->is_live == MMS_LIVE ) + { + if ( !quiet ) + { + + fprintf ( stderr, " \r" ); + fprintf ( stderr, "%s: %d bytes written (--.- kbps)\r", output_filename, (ssize_t)total_len_written ); + gettimeofday ( &speed_last_update, NULL ); + } + + while ( 1 ) + { + len_written = mms_write_stream_data ( mms ); + + if ( len_written == 0 ) + break; + else if ( len_written == MMS_RET_ERROR ) + { + if ( quiet < 2 ) + error ( "main", "unable to write stream data" ); + + ret = MMS_RET_ERROR; + break; + } + else if ( len_written == MMS_RET_NO_AUTH ) + { + if ( trick == MMS_TRICK_DISABLED ) + { + /* we retry with the trick enabled */ + if ( quiet < 2 ) + fprintf ( stderr, "\r*** retrying with the anti-recalcitrant servers trick enabled ***\n" ); + + trick = MMS_TRICK_ENABLED; + retry = 1; + } + else + { + /* it's definitely not working */ + if ( quiet < 2 ) + error ( "main", "unable to write stream data" ); + + ret = MMS_RET_NO_AUTH; + } + + break; + } + + total_len_written += len_written; + + if ( !quiet ) + { + gettimeofday ( &now, NULL ); + + if ( now.tv_sec > speed_last_update.tv_sec ) + { + elapsed_time = (now.tv_sec - speed_last_update.tv_sec) + ((now.tv_usec - speed_last_update.tv_usec) / 1000000.0f); + + if ( elapsed_time >= 1.0f ) + { + speed = ( ( ((float) (total_len_written-old_total_len_written)) / elapsed_time) / 1024.0f ); + old_total_len_written = total_len_written; + gettimeofday ( &speed_last_update, NULL ); + } + } + + if ( speed > 0.0f ) + { + if ( (speed / 1024.0f) >= 1.0f ) + fprintf ( stderr, "%s: %d bytes written (%.1f mbps) \r", output_filename, (ssize_t)total_len_written, speed/1024.0f ); + else + fprintf ( stderr, "%s: %d bytes written (%.1f kbps) \r", output_filename, (ssize_t)total_len_written, speed ); + } + else + fprintf ( stderr, "%s: %d bytes written (--.- kbps) \r", output_filename, (ssize_t)total_len_written ); + } + + fflush ( f ); + + if ( delay != 0 ) + { + if ( end <= time(NULL) ) + { + delay = -1; + break; + } + } + } + + /* for a live, we should rewrite the header */ + + if ( ( ret == MMS_RET_SUCCESS ) && ( !quiet ) && ( !retry ) ) + fprintf ( stderr, "%s: %d bytes written \r", output_filename, (ssize_t)total_len_written ); + } + else + { + register const double coef = 100.0 / (double) mms->expected_file_size; + + if ( !quiet ) + { + fprintf ( stderr, " \r" ); + fprintf ( stderr, "%s: %.2f%% (--.- kbps)\r", output_filename, (double) total_len_written * coef ); + gettimeofday ( &speed_last_update, NULL ); + } + + while ( 1 ) + { + len_written = mms_write_stream_data ( mms ); + + if ( len_written == 0 ) + break; + else if ( len_written == MMS_RET_ERROR ) + { + if ( quiet < 2 ) + error ( "main", "unable to write stream data" ); + + ret = MMS_RET_ERROR; + break; + } + else if ( len_written == MMS_RET_NO_AUTH ) + { + if ( trick == MMS_TRICK_DISABLED ) + { + /* we retry with the trick enabled */ + if ( quiet < 2 ) + fprintf ( stderr, "\r*** retrying with the anti-recalcitrant servers trick enabled ***\n" ); + + trick = MMS_TRICK_ENABLED; + retry = 1; + } + else + { + /* it's definitely not working */ + if ( quiet < 2 ) + error ( "main", "unable to write stream data" ); + + ret = MMS_RET_NO_AUTH; + } + + break; + } + + total_len_written += len_written; + + if ( !quiet ) + { + gettimeofday ( &now, NULL ); + + if ( now.tv_sec > speed_last_update.tv_sec ) + { + elapsed_time = (now.tv_sec - speed_last_update.tv_sec) + ((now.tv_usec - speed_last_update.tv_usec) / 1000000.0f); + + if ( elapsed_time >= 1.0f ) + { + speed = ( ( ((float) (total_len_written-old_total_len_written)) / elapsed_time) / 1024.0f ); + old_total_len_written = total_len_written; + gettimeofday ( &speed_last_update, NULL ); + } + } + + if ( speed > 0.0f ) + { + if ( (speed / 1024.0f) >= 1.0f ) + fprintf ( stderr, "%s: %.2f%% (%.1f mbps) \r", output_filename, (double) total_len_written * coef, speed/1024.0f ); + else + fprintf ( stderr, "%s: %.2f%% (%.1f kbps) \r", output_filename, (double) total_len_written * coef, speed ); + } + else + fprintf ( stderr, "%s: %.2f%% (--.- kbps) \r", output_filename, (double) total_len_written * coef ); + } + + fflush ( f ); + + if ( delay != 0 ) + { + if ( end <= time(NULL) ) + { + delay = -1; + break; + } + } + } + + if ( ( ret == MMS_RET_SUCCESS ) && ( !quiet ) && ( delay != -1 ) && ( !retry ) ) + fprintf ( stderr, "%s: 100.00%% \r", output_filename ); + } + + mms_disconnect ( mms ); + mms_destroy ( mms ); + + fclose ( f ); + + if ( delay == -1 ) + break; + } + + clean: + if ( !quiet ) + fprintf ( stderr, "\n" ); + + if ( stddebug != NULL ) + { + fprintf ( stddebug, "\n\n--> debug log ends now\n" ); + fclose ( stddebug ); + } + + for ( block=stream_list; block!=NULL; ) + { + STREAM_LIST *old; + old = block; + block = (STREAM_LIST *) block->next; + + if ( old->stream != NULL ) + free ( old->stream ); + + if ( old->output != NULL ) + free ( old->output ); + + free ( old ); + } + + return (ret<0) ? (ret*-1) : ret; +} |