home *** CD-ROM | disk | FTP | other *** search
/ Chip 2004 February / Chip_2004-02_cd1.bin / tema / stream / download / asfrec / source / asfrecorder.c
C/C++ Source or Header  |  2000-12-16  |  116KB  |  3,084 lines

  1. /******************************************************************************\
  2.  
  3.   asfrecorder.c - by an unknown author, so no one is to blame.
  4.                   Well, blame it on Microsoft.
  5.  
  6.                   Version 1.1 - Platinum Edition
  7.  
  8.                   new features:
  9.                   -proxy and password support,
  10.                   -a randomized client identifier (GUID),
  11.                   -fixed problems with some asf files using partitioned headers,
  12.                   -enhanced resume functionality to avoid broken ASF files.
  13.                   -timecode correction for live recordings (enables seeking)
  14.  
  15.                   And that's it! This is my last ASFRecorder update!
  16.  
  17. ------------------------------- How to Compile ---------------------------------
  18.  
  19.   Compile console-version on Linux (tested on SUSE)
  20.  
  21.      gcc asfrecorder.c -o asfrecorder
  22.  
  23.   Compile console-version on SUN OS:
  24.  
  25.      gcc -lsocket -lnsl asfrecorder.c -o asfrecorder
  26.  
  27.   Compile console-version with GCC 2.95.2 using CygWin B20.1 / MINGW32
  28.  
  29.      gcc -mno-cygwin -Wl,-s source/asfrecorder.c -o asfrecorder.exe -lws2_32 
  30.  
  31.   Compile GUI version with GCC 2.95.2 using CygWin B20.1 / MINGW32
  32.  
  33.      make GUI
  34.  
  35.   Compile console-version with VisualC++
  36.  
  37.      nmake /F NMAKEFILE
  38.  
  39.   Compile GUI version with VisualC++ (DirectShow SDK might be needed)
  40.  
  41.      nmake /F NMAKEFILE GUI=wingui
  42.  
  43.  ------------------------------------------------------------------------------
  44.  
  45.   Episode I
  46.   
  47.   The starship Voyager -- millions and millions of lightyears away from home --
  48.   has encountered a quantum anomaly which completely wiped out the information
  49.   stored on all of Voyager's secondary computer cores. This included the
  50.   entire audio, video, fine arts and literature database and most of the
  51.   holodeck programs. Fortunately, the Emergency Medical Hologram (the Holo Doc)
  52.   has taken no damage.
  53.  
  54.   Months later, the Voyager and its crew -- desperately lingering for enter-
  55.   tainment -- approached a planet which owned a rich selection of cultural,
  56.   music and video programmes, but refused to offer this excellent material for
  57.   downloading to the Voyager's computer core. "We only permit streaming access!",
  58.   they kept responding. "And we are using one of the Universe's most advanced
  59.   Active Stream Formats, which we are very proud of! It allows us to protect
  60.   the rights we claim on this content."
  61.  
  62.   Janeway: "Tuvok, tactical analysis."
  63.  
  64.   Tuvok:   "The adversary has no defense systems. His protocols are
  65.             simple and vulnerable. An attack will have a high probability
  66.             of success."
  67.  
  68.   Janeway: "I had expected at least a minimal barrier against intrusion. Is
  69.             there no kind of encryption? Not even an authorization procedure?"
  70.  
  71.   Tuvok:   "The computer reports that no security measures have been taken."
  72.  
  73.   Janeway: "Are they just too damn stupid or are they trying to trick us?
  74.             Well, let's take our chance. On my command, download streaming
  75.             content to our computer core. Begin transport now!"
  76.  
  77. ------------------------------------------------------------------------------
  78.  
  79.   Episode II
  80.  
  81.   The Voyager orbited the planet for three days and secretly captured about
  82.   seven million gigaquads of most exciting multimedia data.
  83.  
  84.   Kim:     "Captain, the planet's hailing us!"
  85.  
  86.   Janeway: "On screen."
  87.  
  88.   Prime    "Ha! You Bastards! You tricked us! You stole our most valuable
  89.   Minister: goods! Our culture, our spirit, our souls! How dare you! Devils!"
  90.  
  91.   Janeway: (smiling) "No, we didn't steal it. Actually, we only replicated it.
  92.             Didn't you find the switch to enable Digital Rights Management?"
  93.            (laughing) Our pleasure."
  94.  
  95.   [Prime Minister gasps for air and ends the transmission.]
  96.  
  97.   Janeway: "Fire quantum torpedoes! Destroy all evidence! We don't want to let
  98.             anybody know we just violated the prime directive, do we?"
  99.   
  100.   Neelix:  (whispering to Tom Paris): "This script really sucks. Why is she
  101.             firing at those poor people?"
  102.  
  103.   Paris:   "Yeah, you are absolutely right. This idiot of a scriptwriter
  104.             should be programming cool stuff instead of writing such hideous
  105.             crap."
  106.  
  107.   7 of 9:  "Yes, even the Borg would refuse to act in such a cheap play."
  108.  
  109.   Tuvok:   "I totally agree. This script is illogical and most disturbing."
  110.  
  111.  
  112.   Argh! Mutiny! The Voyager crew doesn't like my scriptwriting skills! So I
  113.   better get going to add some cool stuff. Have a look at the fix_timecode()
  114.   function to find some pure, compressed coolness.
  115.  
  116.   Fortunately there will be no Episode III... ;-)
  117.  
  118.  
  119. \******************************************************************************/
  120.  
  121. /* Standard ANSI-C headers */
  122. #include <stdlib.h>
  123. #include <stdio.h>
  124. #include <string.h>
  125. #include <signal.h>
  126. #include <stdarg.h>
  127. #include <time.h>
  128.  
  129. #ifdef WIN32
  130.  
  131. #define WIN32_LEAN_AND_MEAN
  132. /* networking headers for Windows platform */
  133. #include <winsock2.h>
  134. /* networking compatibility for Windows platform */
  135. #define errno WSAGetLastError()
  136.  
  137. #else /* WIN32 */
  138.  
  139. #ifdef  __MINGW32__
  140.  
  141. #ifdef GUI
  142. /* we need to rename the main() function for MINGW32 */
  143. /* because otherwise WinMain() will never be called */
  144. #define main(argc,argv) renamed_main(argc,argv)
  145. #endif
  146.  
  147. #ifdef OLD_WINDOWS_HEADERS
  148.  
  149. /* networking headers for CygWin/EGCS 1.1 and old windows headers */
  150. #include <Windows32/Base.h>
  151. #include <Windows32/Sockets.h>
  152. typedef int caddr_t;
  153.  
  154. #else /* OLD_WINDOWS_HEADERS */
  155.  
  156. /* networking headers for GCC 2.95.2 */
  157. #include <winsock2.h>
  158. #undef errno
  159.  
  160. #endif /* OLD_WINDOWS_HEADERS */
  161.  
  162. /* networking compatibility for CygWin/MINGW32 */
  163. #define errno WSAGetLastError()
  164. /* define the WIN32 constant now */
  165. #define WIN32
  166.  
  167. #else /* __MINGW32__ */
  168.  
  169. /* networking headers for Unix platform */
  170. #include <sys/socket.h>
  171. #include <netinet/in.h>
  172. #include <netdb.h>
  173. #include <errno.h>
  174. /* networking compatibility for Unix platform */
  175. #define SOCKET int
  176. #define SOCKET_ERROR -1
  177. #define INVALID_SOCKET -1
  178. typedef struct sockaddr_in SOCKADDR_IN;
  179. #define closesocket close
  180. /* string compatbilitiy for Unix platform */
  181. #define stricmp strcasecmp
  182. #define strnicmp strncasecmp
  183.  
  184. #endif /* __MINGW32__ */
  185.  
  186. #endif /* WIN32 */
  187.  
  188.  
  189. /* Prototypes for this source code module */
  190. int main_function(int argc, char **argv);
  191.  
  192.  
  193. /* largest chunk size this program can handle */
  194. #define MAX_CHUNK_SIZE 65536
  195.  
  196.  
  197. /* maximum number of URLs to handle at a time */
  198. #define MAXURLS 100
  199.  
  200.  
  201. /* The header ID to look for, all offsets relative to this ID */
  202.  
  203. unsigned char HDR_ID[16] = {0xa1,0xdc,0xab,0x8c,0x47,0xa9,0xcf,0x11,
  204.                             0x8e,0xe4,0x00,0xc0,0x0c,0x20,0x53,0x65};
  205.  
  206. /* Offsets of some critical information in ASF header */
  207.  
  208. #define HDR_TOTAL_SIZE_8               0x28
  209. #define HDR_NUM_PACKETS_8              0x38
  210. #define HDR_FINE_TOTALTIME_8           0x40
  211. #define HDR_FINE_PLAYTIME_8            0x48
  212. #define HDR_PLAYTIME_OFFSET_4          0x50
  213. #define HDR_FLAGS_4                    0x58
  214. #define HDR_ASF_CHUNKLENGTH_4          0x5c
  215. #define HDR_ASF_CHUNKLENGTH_CONFIRM_4  0x60
  216.  
  217. #define DATSEG_HDR_SIZE 0x32
  218. #define DATSEG_NUMCHUNKS_4 0x28
  219.  
  220.  
  221. /* Chunk types */
  222.  
  223. #define HEADER_CHUNK (('H' << 8) + '$')
  224. #define DATA_CHUNK   (('D' << 8) + '$')
  225. #define END_CHUNK    (('E' << 8) + '$')
  226.  
  227.  
  228. /* The type of content on the server */
  229. typedef enum
  230. {
  231.     connect_failed = 0,
  232.     server_error,
  233.     password_required,
  234.     unknown_content,
  235.     live_content,
  236.     prerecorded_content,
  237.     redirector_content,
  238. } ContentType;
  239.  
  240.  
  241. /* a custom structure be filled with some important */
  242. /* information about the ASF file */
  243. struct HeaderInfo
  244. {
  245.     ContentType contenttype;
  246.     unsigned int redirsize;
  247.  
  248.     unsigned int header_offs;
  249.     unsigned int totalsize_hi, totalsize_lo;
  250.     unsigned int totaltime_hi, totaltime_lo;
  251.     unsigned int offset;
  252.     unsigned int chunklength;
  253.     unsigned int chunklength2;
  254.     unsigned int flags;
  255.     unsigned int endofheader_offs;
  256.  
  257.     unsigned int time;
  258. };
  259.  
  260.  
  261.  
  262. #ifndef GUI
  263.  
  264. /* Stub routines for non-existent GUI code. */
  265.  
  266. int gui_initialize()
  267. {
  268.     return 1;
  269. }
  270.  
  271. void gui_uninitialize()
  272. {
  273. }
  274.  
  275. void gui_setbatchmode(int flag)
  276. {
  277. }
  278.  
  279. int gui_startedfromdesktop()
  280. {
  281.     return 0;
  282. }
  283.  
  284.  
  285. int gui_start_transmission(char *url, char *filename, int filenamesize, unsigned int totaltime, unsigned int maxtime)
  286. {
  287.     return 1;
  288. }
  289.  
  290. void gui_progressinfo(int bytes, char *timecode, int progress, int seqno, int currenttime)
  291. {
  292. //    printf("%5d kB (%2d%%), tc: %s, seq: %d\n", bytes/1024, progress/100, timecode, seqno);
  293. }
  294.  
  295. void gui_modify_duration(unsigned int totaltime)
  296. {
  297. }
  298.  
  299. void gui_finished_transmission()
  300. {
  301. }
  302.  
  303. void gui_not_idle(int flag)
  304. {
  305. }
  306.  
  307. void gui_prepareasyncsocket(SOCKET s)
  308. {
  309. }
  310.  
  311. void gui_restoresyncsocket(SOCKET s)
  312. {
  313. }
  314.  
  315. void gui_return_on_network_activity()
  316. {
  317. }
  318.  
  319. void gui_return_on_network_connect(int *retval)
  320. {
  321. }
  322.  
  323. int gui_nonblocking_socket_check(int num)
  324. {
  325.     return 0;
  326. }
  327.  
  328. void gui_waitforuseraction(void)
  329. {
  330. }
  331.  
  332.  
  333. void gui_showtext(char *text, ...)
  334. {
  335.     va_list args;
  336.     va_start(args, text);
  337.     vprintf(text, args);
  338.     va_end(args);
  339. }
  340.  
  341. void gui_setstatus(char *statustext, ...)
  342. {
  343.     va_list args;
  344.     va_start(args, statustext);
  345.     vprintf(statustext, args);
  346.     va_end(args);
  347. }
  348.  
  349. void gui_seterror(char *errortext, ...)
  350. {
  351.     va_list args;
  352.     va_start(args, errortext);
  353.     vprintf(errortext, args);
  354.     va_end(args);
  355. }
  356.  
  357. void gui_criticalerror(char *errortext, ...)
  358. {
  359.     va_list args;
  360.     va_start(args, errortext);
  361.     vprintf(errortext, args);
  362.     va_end(args);
  363. }
  364.  
  365. void gui_logtext(char *text, ...)
  366. {
  367.     va_list args;
  368.     va_start(args, text);
  369.     vprintf(text, args);
  370.     va_end(args);
  371. }
  372.  
  373. char *gui_translate_errorcode(int error)
  374. {
  375.     static char errorstring[32];
  376.     sprintf(errorstring, "%d", error);
  377.     return errorstring;
  378. }
  379.  
  380. int gui_getpassword(char **username, char **password)
  381. {
  382.     return 0;
  383. }
  384.  
  385. int gui_getproxy(char **proxy)
  386. {
  387.     return 0;
  388. }
  389.  
  390. #else
  391.  
  392. /* Prototypes for GUI code in external module */
  393.  
  394. extern int  gui_initialize(void);
  395. extern void gui_uninitialize(void);
  396. extern void gui_setbatchmode(int flag);
  397. extern int  gui_startedfromdesktop(void);
  398.  
  399. extern int  gui_start_transmission(char *url, char *filename, int filenamesize, unsigned int totaltime, unsigned int maxtime);
  400. extern void gui_finished_transmission();
  401. extern void gui_progressinfo(int bytes, char *timecode, int progress, int seqno, int currenttime);
  402. extern void gui_modify_duration(unsigned int totaltime);
  403. extern void gui_not_idle(int flag);
  404.  
  405. extern void gui_prepareasyncsocket(SOCKET s);
  406. extern void gui_restoresyncsocket(SOCKET s);
  407. extern void gui_return_on_network_activity();
  408. extern void gui_return_on_network_connect(int *retval);
  409. extern int  gui_nonblocking_socket_check(int num);
  410. extern void gui_waitforuseraction(void);
  411.  
  412. extern void gui_showtext(char *text, ...);
  413. extern void gui_setstatus(char *statustext, ...);
  414. extern void gui_seterror(char *errortext, ...);
  415. extern void gui_criticalerror(char *errortext, ...);
  416. extern void gui_logtext(char *text, ...);
  417. extern char *gui_translate_errorcode(int error);
  418.  
  419. extern int gui_getpassword(char **username, char **password);
  420. extern int gui_getproxy(char **proxy);
  421.  
  422. #endif
  423.  
  424.  
  425. /* protocol stuff */
  426. #define DEFAULT_PORT 80
  427.  
  428.  
  429. /* global flag for exiting the download */
  430. int abortflag = 0;
  431.  
  432.  
  433.  
  434. /* helper functions */
  435.  
  436. /* get 64bit integer (Little endian) */
  437.  
  438. void get_quad(unsigned char *pos, unsigned int *hi, unsigned int *lo)
  439. {
  440.     unsigned char c1 = *pos++, c2 = *pos++, c3 = *pos++, c4 = *pos++;
  441.     unsigned char c5 = *pos++, c6 = *pos++, c7 = *pos++, c8 = *pos++;
  442.     (*hi) = (c8<<24) + (c7<<16) + (c6<<8) + c5;
  443.     (*lo) = (c4<<24) + (c3<<16) + (c2<<8) + c1;
  444. }
  445.  
  446. /* write 64bit integer to stream (Little endian) */
  447.  
  448. void write_quad(FILE *outfile, unsigned int hi, unsigned int lo)
  449. {
  450.     unsigned char c1 = (lo & 0xff), c2 = ((lo>>8) & 0xff), c3 = ((lo>>16) & 0xff), c4 = ((lo>>24) & 0xff);
  451.     unsigned char c5 = (hi & 0xff), c6 = ((hi>>8) & 0xff), c7 = ((hi>>16) & 0xff), c8 = ((hi>>24) & 0xff);
  452.     fputc(c1, outfile); fputc(c2, outfile); fputc(c3, outfile); fputc(c4, outfile);
  453.     fputc(c5, outfile); fputc(c6, outfile); fputc(c7, outfile); fputc(c8, outfile);
  454. }
  455.  
  456. /* get 32bit integer (Little endian) */
  457.  
  458. void get_long(unsigned char *pos, unsigned int *val)
  459. {
  460.     unsigned char c1 = *pos++, c2 = *pos++, c3 = *pos++, c4 = *pos++;
  461.     (*val) = (c4<<24) + (c3<<16) + (c2<<8) + c1;
  462. }
  463.  
  464. /* put 32 bit integer (Little endian) */
  465.  
  466. void put_long(unsigned char *pos, unsigned int val)
  467. {
  468.     *pos++ = (val & 0xff); *pos++ = ((val>>8) & 0xff), *pos++ = ((val>>16) & 0xff), *pos++ = ((val>>24) & 0xff);
  469. }
  470.  
  471. /* write a 32 bit integer to stream (Little endian) */
  472.  
  473. void write_long(FILE *outfile, unsigned int val)
  474. {
  475.     unsigned char c1 = (val & 0xff), c2 = ((val>>8) & 0xff), c3 = ((val>>16) & 0xff), c4 = ((val>>24) & 0xff);
  476.     fputc(c1, outfile); fputc(c2, outfile); fputc(c3, outfile); fputc(c4, outfile);
  477. }
  478.  
  479. /* get short integer (Little endian) */
  480.  
  481. void get_short(unsigned char *pos, unsigned short *val)
  482. {
  483.     unsigned char c1 = *pos++, c2 = *pos++;
  484.     (*val) = (c2<<8) + c1;
  485. }
  486.  
  487. /* write a 32 bit integer to stream (Little endian) */
  488.  
  489. void write_short(FILE *outfile, unsigned short val)
  490. {
  491.     unsigned char c1 = (val & 0xff), c2 = ((val>>8) & 0xff);
  492.     fputc(c1, outfile); fputc(c2, outfile);
  493. }
  494.  
  495.  
  496. /* 64 bit subtraction with unsigned integers */
  497.  
  498. void quad_subtraction(unsigned int *dst_hi, unsigned int *dst_lo, unsigned int hi, unsigned int lo)
  499. {
  500.     unsigned int tmp;
  501.  
  502.     *dst_hi -= hi;
  503.  
  504.     tmp = *dst_lo;
  505.     *dst_lo -= lo;
  506.  
  507.     /* handle "carry bit" */
  508.     if (*dst_lo > tmp) *dst_hi--;
  509. }
  510.  
  511.  
  512. /* timecode helper functions */
  513.  
  514. /* get position of timecode in chunk (-1 if unknown) */
  515.  
  516. int whereis_timecode(unsigned char *Buffer)
  517. {
  518.     int tc_start = -1;
  519.  
  520.     /* timecode position varies with type of chunk */
  521.     switch(Buffer[3])
  522.     {
  523.     case 0x00: tc_start =  5; break;
  524.     case 0x01: tc_start =  5; break;
  525.     case 0x08: tc_start =  6; break;
  526.     case 0x09: tc_start =  6; break;
  527.     case 0x10: tc_start =  7; break;
  528.     case 0x11: tc_start =  7; break;
  529.     case 0x48: tc_start =  8; break;
  530.     case 0x49: tc_start =  8; break;
  531.     case 0x50: tc_start =  9; break;
  532.     case 0x51: tc_start =  9; break;
  533.     default:
  534.         tc_start = -1;
  535.         gui_logtext("unknown format type: $%02x\n", (int)Buffer[3]);
  536.     }
  537.  
  538.     return tc_start;
  539. }
  540.  
  541.  
  542. /* Create a randomized GUID string to avoid filtering ASFRecorder's */
  543. /* stream requests by means of detecting any hardcoded GUIDs */
  544.  
  545. void randomize_guid(unsigned char *buf)
  546. {
  547.     int digit, dig;
  548.     time_t curtime;
  549.  
  550.     *buf++='{';
  551.     time(&curtime);
  552.     srand(curtime);
  553.     for (digit=0; digit <32; digit++)
  554.     {
  555.         if (digit==8 || digit == 12 || digit == 16 || digit == 20) *buf++='-';
  556.  
  557.         dig = rand()%0xf;
  558.         if (dig<10)
  559.             *buf++='0'+dig;
  560.         else
  561.             *buf++='A'+(dig-10);
  562.     }
  563.     *buf++='}';
  564.     *buf++='\0';
  565. }
  566.  
  567.  
  568. /* create a timecode string from a timecode in milliseconds */
  569.  
  570. char *createtimestring(unsigned int timecode)
  571. {
  572.     static char timecodestring[64];
  573.  
  574.     if (timecode/1000 >= 24*3600)
  575.         sprintf(timecodestring, "%d:%02d:%02d:%02d.%03d", (timecode/1000)/(24*3600), ((timecode/1000)/3600)%24, ((timecode/1000)/60)%60, (timecode/1000)%60, timecode%1000);
  576.     else
  577.         if (timecode/1000 >= 3600)
  578.             sprintf(timecodestring, "%d:%02d:%02d.%03d", (timecode/1000)/3600, ((timecode/1000)/60)%60, (timecode/1000)%60, timecode%1000);
  579.         else
  580.             if (timecode/1000 >= 60)
  581.                 sprintf(timecodestring, "%d:%02d.%03d", (timecode/1000)/60, (timecode/1000)%60, timecode%1000);
  582.             else
  583.                 sprintf(timecodestring, "%d.%03d", (timecode/1000)%60, timecode%1000);
  584.     return timecodestring;
  585. }
  586.  
  587.  
  588. /* subroutine for base64 encoding (used in HTTP Basic authentification) */
  589.  
  590. const char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
  591.  
  592. int base64enc(char *data,char *out)
  593. {
  594.     int len = strlen(data);
  595.     
  596.     int i,index;
  597.     int val;
  598.     
  599.     for (i=0, index=0; i<len; i+=3, index+=4)
  600.     {
  601.         int quad,trip;
  602.         
  603.         quad = 0;
  604.         trip = 0;
  605.         
  606.         val = (0xff & data[i]);
  607.         val <<= 8;
  608.         if ((i+1) < len) {
  609.             val |= (0xff & data[i+1]);
  610.             trip = 1;
  611.         }
  612.         val <<= 8;
  613.         if ((i+2) < len) {
  614.             val |= (0xff & data[i+2]);
  615.             quad = 1;
  616.         }
  617.         out[index+3] = alphabet[(quad? (val & 0x3f): 64)];
  618.         val >>= 6;
  619.         out[index+2] = alphabet[(trip? (val & 0x3f): 64)];
  620.         val >>= 6;
  621.         out[index+1] = alphabet[val & 0x3f];
  622.         val >>= 6;
  623.         out[index+0] = alphabet[val & 0x3f];
  624.     }
  625.     
  626.     out[++index] = 0;
  627.     
  628.     return index;
  629. }
  630.  
  631.     
  632. /* helper routine for ASF header parsing */
  633.  
  634. int find_id(unsigned char *id, unsigned char *Buffer, int bufsize)
  635. {
  636.     int offs, i, found;
  637.     for (offs = 0; offs < bufsize-16; offs++) {
  638.         found=1;
  639.         for (i=0; i<16; i++) if (Buffer[offs+i] != id[i]) {found = 0; break;}
  640.         if (found) return(offs);
  641.     }
  642.     return -1;
  643. }
  644.  
  645.  
  646.  
  647. /* These routines are used to parse the packet and find */
  648. /* and modify timecodes for live streams */
  649.  
  650. unsigned int get_data(unsigned char **bp, unsigned int type)
  651. {
  652.     unsigned int res = 0;
  653.     switch (type) {
  654.         case 0: break;
  655.         case 1: res = *(*bp+0); *bp+=1; break;
  656.         case 2: res = *(*bp+0) + (*(*bp+1) << 8); *bp+=2; break;
  657.         case 3: res = *(*bp+0) + (*(*bp+1) << 8) + (*(*bp+2) << 16) + (*(*bp+3) << 24); *bp+=4; break;
  658.         default: gui_logtext("unknown format type %d\n", type); break;
  659.     }
  660.     return res;
  661. }
  662.  
  663. int fix_timecodes(unsigned char *Buffer, int bodylength, unsigned int deltatime, unsigned int in_chunk_no, struct HeaderInfo *hi)
  664. {
  665.     /* This coding style is actually meant to confuse you. */
  666.     /* Don't ask what I am doing here. Just don't ask!     */
  667.     /* You are not supposed to understand this...          */
  668.     /* But be assured that this is good for you ;-)        */
  669.     unsigned char *ptr1,b1;
  670.     unsigned int f1,f2,l1,l2,l3,l4,l5,l6,l7,l8,n1,n;
  671.     unsigned int v1,v2,v3,v4,v5=0,v6,v7,v8,v9,v10;
  672.     if (!(Buffer[0]==0x82&&Buffer[1]==0x00&&Buffer[2]==0x00)){
  673.         gui_logtext("Illegal header bytes in chunk %d!\n",in_chunk_no);return 1;}
  674.     ptr1=&Buffer[3];b1=*ptr1++;f1=(b1&0x80)>>7;
  675.     if(f1==0){l1=(b1&0x60)>>5;l2=(b1&0x18)>>3;l3=(b1&0x06)>>1;f2=(b1&0x01);}
  676.     else{gui_logtext("Chunks carrying error correction information are not supported yet!\n");return 1;}
  677.     b1=*ptr1++;l4=(b1&0xc0)>>6;l5=(b1&0x30)>>4;l6=(b1&0x0c)>>2;l7=(b1&0x03);
  678.     v1=get_data(&ptr1,l1);v2=get_data(&ptr1,l3);v3=get_data(&ptr1,l2);v4=get_data(&ptr1,3);
  679.     v4=(deltatime<v4)?v4-=deltatime:0;put_long(ptr1-4,v4);
  680.     if(l1==0)v1=hi->chunklength;if(l3==0)v2=in_chunk_no;if(v3>0)memset(Buffer+v1-v3,0,v3);
  681.     ptr1+=2;b1=*ptr1++;l8=(b1&0x80)>>7;if(l8==0)l8=2;else l8=2; /* ;-) */
  682.     n1=(b1&0x1f);if(f2==0){n1=1;l8=0;}for(n=0;(n<n1);n++){
  683.     if(f2!=0)v5=get_data(&ptr1,l4);v6=get_data(&ptr1,l5);v7=get_data(&ptr1,l6);v8=get_data(&ptr1,l7);
  684.     if(v8==8){get_long(ptr1+4,&v10);v10=(deltatime<v10)?v10-=deltatime:0;put_long(ptr1+4,v10);}
  685.     if(v8>0)ptr1+=v8;if(f2!=0)v9=get_data(&ptr1,l8);else v9=(v1-v3)-(ptr1-Buffer);if(v9>0)ptr1+=v9;
  686.     if((unsigned int)(ptr1-Buffer)>hi->chunklength){
  687.         gui_logtext("WARNING! buffer pointer %d bytes after packet!\n",(unsigned int)(ptr1-Buffer)-hi->chunklength);return 1;}
  688.     if ((unsigned int)(ptr1-Buffer)>hi->chunklength-v3){
  689.         gui_logtext("WARNING! buffer pointer %d bytes after packet-padlen!\n",(unsigned int)(ptr1-Buffer)-(hi->chunklength-v3));return 1;}}
  690.     if((ptr1-Buffer)<(int)hi->chunklength-(int)v3)
  691.         gui_logtext("NOTE: %d bytes not covered by payload\n",hi->chunklength-v3-(ptr1-Buffer));
  692.     return 0;
  693.     /* Ha, ha! Never ever before in my life I produced such an unreadable code! ;-) */
  694. }
  695.  
  696.  
  697.  
  698. /* Replace specific characters in the URL string by an escape sequence */
  699.  
  700. /* works like strcpy(), but without return argument */
  701. void escape_url_string(char *outbuf, char *inbuf)
  702. {
  703.     unsigned char c;
  704.     do
  705.     {
  706.         c = *inbuf++;
  707.         
  708.         if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
  709.             c=='-' || c=='_'  || c=='.' || c=='!' || c=='~'  || /* mark characters */
  710.             c=='*' || c=='\'' || c=='(' || c==')' ||
  711.             c=='%' ||                                           /* do not touch escape character */
  712.             c==';' || c=='/'  || c=='?' || c==':' || c=='@'  || /* reserved characters */
  713.             c=='&' || c=='='  || c=='+' || c=='$' || c==','  || /* see RFC 2396 */
  714.             c=='\0' )
  715.         {
  716.             *outbuf++ = c;
  717.         }
  718.         else
  719.         {
  720.            /* all others will be escaped */
  721.             unsigned char c1 = ((c & 0xf0) >> 4); 
  722.             unsigned char c2 =  (c & 0x0f);
  723.             if (c1 < 10) c1+='0'; else c1+='A';
  724.             if (c2 < 10) c2+='0'; else c2+='A';
  725.             *outbuf++ = '%';
  726.             *outbuf++ = c1;
  727.             *outbuf++ = c2;
  728.         }
  729.         
  730.     } while (c != '\0');
  731. }
  732.  
  733.  
  734. /* Replace escape sequences in an URL (or a part of an URL) */
  735.  
  736. /* works like strcpy(), but without return argument */
  737. void unescape_url_string(char *outbuf, char *inbuf)
  738. {
  739.     unsigned char c;
  740.     do
  741.     {
  742.         c = *inbuf++;
  743.         if (c == '%')
  744.         {
  745.             unsigned char c1 = *inbuf++;
  746.             unsigned char c2 = *inbuf++;
  747.             if (((c1>='0' && c1<='9') || (c1>='A' && c1<='F')) &&
  748.                 ((c2>='0' && c2<='9') || (c2>='A' && c2<='F')) )
  749.             {
  750.                 if (c1>='0' && c1<='9') c1-='0'; else c1-='A';
  751.                 if (c2>='0' && c2<='9') c2-='0'; else c2-='A';
  752.                 c = (c1<<4) + c2;
  753.             }
  754.         }
  755.         *outbuf++ = c;
  756.  
  757.     } while (c != '\0');
  758. }
  759.  
  760.  
  761.  
  762. /* generate a valid filename */
  763.  
  764. /* NOTE: this currently implements proper rules for Windows only */
  765.  
  766. void generate_valid_filename(char *buffer)
  767. {
  768.     char *inbuf = buffer, *outbuf = buffer;
  769.     unsigned char c;
  770.     int length = 0;
  771.     do
  772.     {
  773.         c = *inbuf++;
  774.         if (
  775. #ifdef WIN32
  776.         /* Windows does not allow the following characters in file names */
  777.         /* and filename length (including '\0') is restricted to 255 characters */
  778.  
  779.         (c!='/' && c!='\\' && c!=':' && c!='*' && c!='?' &&
  780.          c!='"' && c!='<'  && c!='>' && c!='|' && length < 254) || c=='\0'
  781. #else
  782.         /* Unix */
  783.         /* disallow forward slash (directory separator)... anything else?!?! */
  784.  
  785.          c!='/'
  786. #endif
  787.         )
  788.         {
  789.             *outbuf++ = c;
  790.             length++;
  791.         }
  792.  
  793.     } while (c != '\0');
  794. }
  795.  
  796.  
  797.  
  798. /* this routine can be called from the GUI to signal an abort */
  799.  
  800. void gui_abort()
  801. {
  802.     abortflag = 1;
  803. }
  804.  
  805.  
  806.  
  807. /* ctrl-c handler */
  808. /* WIN32: Beware! The ctrl-c handler is called in a different thread. */
  809.  
  810. void ctrlc(int sig)
  811. {
  812.     gui_seterror("\n***CTRL-C\n");
  813.     abortflag = 1;
  814.     signal(SIGINT, ctrlc);
  815. }
  816.  
  817.  
  818.  
  819. /* The following (blocking) socket calls are encapsulated in an event loop */
  820. /* that allows the GUI code to handle user input during a blocking network */
  821. /* operation */
  822.  
  823. /* The GUI code must set the socket to non-blocking mode, install an event */
  824. /* notification mechanism on the socket and return on detection of a network */
  825. /* event. */
  826.  
  827. SOCKET my_socket(int af, int type, int protocol)
  828. {
  829.     SOCKET s;
  830.     s = socket(af, type, protocol);
  831.  
  832.     if (s != INVALID_SOCKET)
  833.     {
  834.         gui_prepareasyncsocket(s);
  835.     }
  836.     return s;
  837. }
  838.  
  839. int my_closesocket(SOCKET s)
  840. {
  841.     gui_restoresyncsocket(s);
  842.  
  843.     return closesocket(s);
  844. }
  845.  
  846. int my_recv(SOCKET s, char *buf, int len, int flags)
  847. {
  848.     int returnval=0;
  849.     while (!abortflag)
  850.     {
  851.         returnval = recv(s, buf, len, flags);
  852.         if (returnval == SOCKET_ERROR && gui_nonblocking_socket_check(errno))
  853.             gui_return_on_network_activity();
  854.         else
  855.             break;
  856.     }
  857.  
  858.     return returnval;
  859. }
  860.  
  861. int my_send(SOCKET s, const char *buf, int len, int flags)
  862. {
  863.     int returnval=0;
  864.     while (!abortflag)
  865.     {
  866.         returnval = send(s, buf, len, flags);
  867.         if (returnval == SOCKET_ERROR && gui_nonblocking_socket_check(errno))
  868.             gui_return_on_network_activity();
  869.         else
  870.             break;
  871.     }
  872.  
  873.     return returnval;
  874. }
  875.  
  876.  
  877. int my_connect(SOCKET s, const struct sockaddr *name, int namelen)
  878. {
  879.     int returnval=0;
  880.     while (!abortflag)
  881.     {
  882.         returnval = connect(s, name, namelen);
  883.         if (returnval == SOCKET_ERROR && gui_nonblocking_socket_check(errno))
  884.             gui_return_on_network_connect(&returnval);
  885.         break;
  886.     }
  887.  
  888.     return returnval;
  889. }
  890.  
  891.  
  892. /* Stream input routines. This code uses a simple, circular buffer */
  893.  
  894. /* the circular stream buffer. */
  895. #define STREAMBUFSIZE 4096
  896. char StreamBuffer[STREAMBUFSIZE];
  897. int sb_inpos = 0;
  898. int sb_outpos = 0;
  899. int networkeof;
  900. int streameof;
  901.  
  902.  
  903. /* reset circular stream buffer */
  904. void flushstream()
  905. {
  906.     sb_inpos = 0;
  907.     sb_outpos = 0;
  908.  
  909.     networkeof = 0;
  910.     streameof = 0;
  911. }
  912.  
  913.  
  914. /* check end of stream status */
  915. int eos()
  916. {
  917.     return streameof;
  918. }
  919.  
  920.  
  921. /* read one or more bytes from the stream. */
  922. int readfromstream(SOCKET conn_socket, char *dest, int length, FILE *rawfile)
  923. {
  924.     int got = 0;
  925.     int totalread = 0;
  926.  
  927.     while ((!streameof) && (length >0))
  928.     {
  929.         int lag;
  930.  
  931.         lag = (STREAMBUFSIZE + sb_outpos - sb_inpos) % STREAMBUFSIZE;
  932.         if (lag == 0) lag = STREAMBUFSIZE;
  933.  
  934.         /* refill circular buffer only when at least half of it is empty */
  935.         if ((!networkeof) && (lag > STREAMBUFSIZE/2))
  936.         {
  937.             int retval;
  938.             int toend = sb_outpos - sb_inpos - 1;
  939.             if (toend < 0) toend = STREAMBUFSIZE - sb_inpos;
  940.             if (toend > 0)
  941.             {
  942.                 retval = my_recv(conn_socket, &StreamBuffer[sb_inpos], toend, 0 );
  943.                 if (retval == SOCKET_ERROR)
  944.                 {
  945.                     gui_seterror("recv() failed: %s\n",gui_translate_errorcode(errno));
  946.                     networkeof = 1;
  947.                 }
  948.  
  949.                 if (retval == 0)
  950.                 {
  951.                     networkeof = 1;
  952.                 }
  953.  
  954.                 if (retval > 0)
  955.                 {
  956.                     if (rawfile != NULL)
  957.                     {
  958.                         fwrite(&StreamBuffer[sb_inpos], 1, retval, rawfile);
  959.                     }
  960.                     sb_inpos += retval;
  961.                 }
  962.             }
  963.         }
  964.  
  965.         /* check if circular buffer contains data */
  966.         if (sb_outpos != sb_inpos)
  967.         {
  968.             /* report EOF only when circular buffer is empty */
  969.  
  970.             sb_inpos = sb_inpos % STREAMBUFSIZE;
  971.  
  972.             while (length > 0)
  973.             {
  974.                 *dest++ = StreamBuffer[sb_outpos++];
  975.                 sb_outpos = sb_outpos % STREAMBUFSIZE;
  976.                 length--;
  977.                 totalread++;
  978.  
  979.                 if (sb_outpos == sb_inpos) break;
  980.             }
  981.         }
  982.         else
  983.         {
  984.             if (networkeof)
  985.             {
  986.                 streameof = 1;
  987.                 break;
  988.             }
  989.         }
  990.  
  991.     }
  992.     return totalread;
  993. }
  994.  
  995.  
  996. /* The core routine for ASF download/extraction. */
  997.  
  998. int collectdata(int headeronly,
  999.                 int dumpheaders,
  1000.                 FILE *rawfile,
  1001.                 FILE *outfile,
  1002.                 int bytesread,
  1003.                 unsigned char *Buffer,
  1004.                 int bufsize,
  1005.                 int sendlen,
  1006.                 struct hostent *hp,
  1007.                 char *server_name,
  1008.                 unsigned int addr,
  1009.                 unsigned short port,
  1010.                 int socket_type,
  1011.                 char *file,
  1012.                 struct HeaderInfo *hi,
  1013.                 int maxtime)
  1014. {
  1015.     SOCKET  conn_socket;
  1016.     int retval;
  1017.  
  1018.     SOCKADDR_IN server;
  1019.  
  1020.     int eol;
  1021.     int hdrpos;
  1022.     int linepos;
  1023.     int linenum;
  1024.     char HTTPHeader[1024];
  1025.     int resume = 0;
  1026.  
  1027.     char HTTPLine[512];
  1028.     char HTTPMessage[128];
  1029.     char *hdrptr;
  1030.     int errorcode;
  1031.  
  1032.     memset(hi, 0, sizeof(*hi));
  1033.     hi->contenttype = connect_failed;
  1034.  
  1035.     /* Dump the outgoing HTTP request */
  1036.     if (dumpheaders)
  1037.     {
  1038.         gui_logtext("\nHTTP-Request:\n%s", Buffer);
  1039.     }
  1040.  
  1041.     if (bytesread > 0) resume = 1;
  1042.  
  1043.     /* Copy the resolved information into the sockaddr_in structure */
  1044.     memset(&server,0,sizeof(server));
  1045.     if (hp!=NULL)
  1046.     {
  1047.         memcpy(&(server.sin_addr),hp->h_addr,hp->h_length);
  1048.         server.sin_family = hp->h_addrtype;
  1049.         strcpy(server_name, hp->h_name);
  1050.     }
  1051.     else
  1052.     {
  1053.         memcpy(&(server.sin_addr), &addr, 4);
  1054.         server.sin_family = AF_INET;
  1055.     }
  1056.     server.sin_port = htons(port);
  1057.  
  1058.     conn_socket = my_socket(AF_INET,socket_type,0); /* Open a socket */
  1059.     if (conn_socket < 0)
  1060.     {
  1061.         gui_seterror("socket() failed: %s\n", gui_translate_errorcode(errno));
  1062.     }
  1063.     else
  1064.     {
  1065.         gui_setstatus("connecting to: %s\n",server_name);
  1066.  
  1067.         if (my_connect(conn_socket,(struct sockaddr*)&server,sizeof(server))
  1068.             == SOCKET_ERROR)
  1069.         {
  1070.             gui_seterror("connect() failed: %s\n",gui_translate_errorcode(errno));
  1071.         }
  1072.         else
  1073.         {
  1074.             gui_setstatus("sending request [%d bytes]\n",sendlen);
  1075.  
  1076.             retval = my_send(conn_socket,Buffer,sendlen,0);
  1077.             if (retval == SOCKET_ERROR)
  1078.             {
  1079.                 gui_seterror("send() failed: %s\n",gui_translate_errorcode(errno));
  1080.             }
  1081.             else
  1082.             {
  1083.                 unsigned char Features[256];
  1084.                 unsigned char ContentType[256];
  1085.  
  1086.                 gui_setstatus("waiting for reply...\n");
  1087.  
  1088.                 flushstream();
  1089.  
  1090.                 errorcode = 0;
  1091.                 strcpy(HTTPMessage, "<none>");
  1092.  
  1093.                 strcpy(ContentType, "");
  1094.                 strcpy(Features, "");
  1095.  
  1096.                 eol = 0;
  1097.                 hdrpos = 0;
  1098.                 linepos = 0;
  1099.                 linenum = 0;
  1100.                 for (;;)
  1101.                 {
  1102.                     char c;
  1103.  
  1104.                     if (readfromstream(conn_socket, &c, 1, rawfile) == 1)
  1105.                     {
  1106.                         if ((c != '\r') && (c != '\n'))
  1107.                         {
  1108.                             eol = 0;
  1109.                             HTTPLine[linepos++] = c;
  1110.                         }
  1111.                         else
  1112.                             HTTPLine[linepos++] = 0;
  1113.  
  1114.                         if (c == '\n')
  1115.                         {
  1116.                             if (eol == 0)
  1117.                             {
  1118.                                 linepos = 0;
  1119.                                 eol = 1;
  1120.                                 linenum++;
  1121.  
  1122.                                 hdrptr = HTTPLine;
  1123.  
  1124.                                 /* Parse first line of HTTP reply */
  1125.                                 if (linenum == 1)
  1126.                                 {
  1127.                                     if ((!strnicmp(hdrptr, "HTTP/1.0 ", 9)) ||
  1128.                                         (!strnicmp(hdrptr, "HTTP/1.1 ", 9))   )
  1129.                                     {
  1130.                                         hdrptr+=9;
  1131.                                         sscanf(hdrptr, "%d", &errorcode);
  1132.                                         hdrptr+=4;
  1133.                                         strcpy(HTTPMessage, hdrptr);
  1134.                                     }
  1135.                                     else
  1136.                                     {
  1137.                                         gui_seterror("Illegal server reply! Expected HTTP/1.0 or HTTP/1.1\n");
  1138.                                         hi->contenttype = unknown_content;
  1139.                                     }
  1140.                                 }
  1141.                                 else
  1142.                                 {
  1143.                                     /* Parse all other lines of HTTP reply */
  1144.                                     if (!strnicmp(hdrptr, "Content-Type: ", 14))
  1145.                                     {
  1146.                                         hdrptr+=14;
  1147.                                         strncpy(ContentType, hdrptr, sizeof(ContentType));
  1148.                                     }
  1149.  
  1150.                                     /* Parse all other lines of HTTP reply */
  1151.                                     if (!strnicmp(hdrptr, "Pragma: ", 8))
  1152.                                     {
  1153.                                         hdrptr+=8;
  1154.                                         if (!strnicmp(hdrptr, "features=", 9))
  1155.                                         {
  1156.                                             hdrptr+=9;
  1157.                                             strncpy(Features, hdrptr, sizeof(Features));
  1158.                                         }
  1159.                                     }
  1160.                                 }
  1161.                             }
  1162.                             else
  1163.                             {
  1164.                                 HTTPHeader[hdrpos++] = 0;
  1165.                                 break;
  1166.                             }
  1167.                         }
  1168.  
  1169.                         HTTPHeader[hdrpos++] = c;
  1170.                     }
  1171.                     else
  1172.                     {
  1173.                         gui_seterror("readfromstream() returned other than 1!\n");
  1174.                         if (eos()) break;
  1175.                     }
  1176.                 }
  1177.  
  1178.                 hi->contenttype = unknown_content;
  1179.  
  1180.                 /* Determine whether this is live content or not */
  1181.                 if (!stricmp(ContentType, "application/octet-stream"))
  1182.                 {
  1183.                     if (strstr(Features, "broadcast"))
  1184.                     {
  1185.                         hi->contenttype = live_content;
  1186.                     }
  1187.                     else
  1188.                     {
  1189.                         hi->contenttype = prerecorded_content;
  1190.                     }
  1191.                 }
  1192.                 else
  1193.                 {
  1194.                     if ((!stricmp(ContentType, "audio/x-ms-wax")) ||
  1195.                         (!stricmp(ContentType, "audio/x-ms-wma")) ||
  1196.                         (!stricmp(ContentType, "video/x-ms-asf")) ||
  1197.                         (!stricmp(ContentType, "video/x-ms-afs")) ||
  1198.                         (!stricmp(ContentType, "video/x-ms-wvx")) ||
  1199.                         (!stricmp(ContentType, "video/x-ms-wmv")) ||
  1200.                         (!stricmp(ContentType, "video/x-ms-wma"))   )
  1201.                     {
  1202.                         hi->contenttype = redirector_content;
  1203.                         /* try to read as much data as fits into the buffer */
  1204.                         /* bufsize should be enough for any redirector files */
  1205.                         if (!eos()) hi->redirsize = readfromstream(conn_socket, Buffer, bufsize, rawfile);
  1206.                     }
  1207.                     else
  1208.                     {
  1209.                         if ( (!stricmp(ContentType, "text/html")) ||
  1210.                              (!stricmp(ContentType, "text/plain"))  )
  1211.                         {
  1212.                             /* well, maybe we are lucky and this HTML or plaintext */
  1213.                             /* file inexpectedly contains some redirector content  */
  1214.                             /* (due to a badly configured webserver!) */
  1215.                             if (!eos()) hi->redirsize = readfromstream(conn_socket, Buffer, bufsize, rawfile);
  1216.                             hdrptr = Buffer;
  1217.                             /* eat whitespaces */
  1218.                             while( (*hdrptr == ' ')  || (*hdrptr == '\t') ||
  1219.                                    (*hdrptr == '\r') || (*hdrptr == '\n')   ) hdrptr++;
  1220.                             if ((!strnicmp(hdrptr, "<ASX",   4)) ||
  1221.                                 (!strnicmp(hdrptr, "< ASX",  5)) ||
  1222.                                 (!strnicmp(hdrptr, "ASF ",   4)) ||
  1223.                                 (!strnicmp(hdrptr, "ASF\t",  4)) ||
  1224.                                 (!strnicmp(hdrptr, "http://",7)) ||
  1225.                                 (!strnicmp(hdrptr, "mms://", 6)) ||
  1226.                                 (!strnicmp(hdrptr, "mmst://",7)) ||
  1227.                                 (!strnicmp(hdrptr, "mmsu://",7)) ||
  1228.                                 (!strnicmp(hdrptr, "[Reference]", 11)) )
  1229.                             {
  1230.                                 hi->contenttype = redirector_content;
  1231.                             }
  1232.                         }
  1233.                         else
  1234.                         {
  1235.                             gui_logtext("unknown content-type: %s\n", ContentType);
  1236.                         }
  1237.                     }
  1238.                 }
  1239.  
  1240.                 if (errorcode == 200 || errorcode == 401)
  1241.                     gui_setstatus("reply: %d - %s\n", errorcode, HTTPMessage);
  1242.                 else
  1243.                     gui_seterror("reply: %d - %s\n", errorcode, HTTPMessage);
  1244.  
  1245.                 /* Dump the incoming HTTP reply */
  1246.                 if (dumpheaders)
  1247.                 {
  1248.                     gui_logtext("\nHTTP-Reply:\n%s\n", HTTPHeader);
  1249.                 }
  1250.  
  1251.                 if (errorcode == 200)
  1252.                 {
  1253.                 }
  1254.                 else
  1255.                 {
  1256.                     if (errorcode == 401)
  1257.                     {
  1258.                         hi->contenttype = password_required;
  1259.                     }
  1260.                     else
  1261.                     {
  1262.                         hi->contenttype = server_error;
  1263.                     }
  1264.                 }
  1265.  
  1266.                 /* handle live or prerecorded content */
  1267.                 if ((!eos()) && ((hi->contenttype == live_content) || (hi->contenttype == prerecorded_content)) )
  1268.                 {
  1269.                     unsigned int starttime_hi = 0xffffffff;
  1270.                     unsigned int starttime    = 0xffffffff;
  1271.                     unsigned int startseqno   = 0xffffffff;
  1272.                     unsigned int maxtimecode  = 0;
  1273.                     int          endofheaderposition = 0;
  1274.                     int          numdatachunks       = 0;
  1275.                     int          sizeofdatachunks    = 0;
  1276.  
  1277.                     int          header_length = 0;
  1278.                     int          header_offset = 0;
  1279.  
  1280.                     /* The main loop for chunk extraction/ASF generation */
  1281.                     for (;;)
  1282.                     {
  1283.                         unsigned char c1;
  1284.                         unsigned char c2;
  1285.  
  1286.                         unsigned short type;
  1287.  
  1288.                         unsigned char l1;
  1289.                         unsigned char l2;
  1290.                         int length;
  1291.                         int length2;
  1292.                         int bodylength;
  1293.  
  1294.                         unsigned char s1;
  1295.                         unsigned char s2;
  1296.                         unsigned char s3;
  1297.                         unsigned char s4;
  1298.                         unsigned int seqno;
  1299.  
  1300.                         unsigned char u1;
  1301.                         unsigned char u2;
  1302.                         unsigned short partflag;
  1303.  
  1304.                         unsigned int timecode;
  1305.  
  1306.                         int progress; /* scaled from 0 to 10000 */
  1307.  
  1308.                         char timecodestring[100];
  1309.                         int got;
  1310.  
  1311.                         /* Check for EOF and extract chunk header */
  1312.                         /* bytes are read one by one so this code */
  1313.                         /* remains portable to non-INTEL platforms */
  1314.  
  1315.                         if (eos()) { gui_setstatus("Connection reset\n"); break; }
  1316.  
  1317.                         /* read basic chunk type */
  1318.                         readfromstream(conn_socket, &c1, 1, rawfile);
  1319.                         readfromstream(conn_socket, &c2, 1, rawfile);
  1320.                         type = (c2<<8) + c1;
  1321.  
  1322.                         /* These header types correspond to "H$", "D$" and "E$" */
  1323.                         /* (Header, Data and End) */
  1324.                         if ((type != HEADER_CHUNK) && (type != DATA_CHUNK) && (type != END_CHUNK))
  1325.                         {
  1326.                             gui_logtext("Unknown header type: $%04x\n", type);
  1327.                         }
  1328.  
  1329.                         if (type == END_CHUNK)
  1330.                         {
  1331.                             gui_setstatus("Transfer complete.\n");
  1332.                             break;
  1333.                         }
  1334.  
  1335.                         if (eos()) { gui_setstatus("Connection reset\n"); break; }
  1336.  
  1337.                         /* read chunk length (max 64k) */
  1338.                         readfromstream(conn_socket, &l1, 1, rawfile);
  1339.                         readfromstream(conn_socket, &l2, 1, rawfile);
  1340.                         length = (l2<<8) + l1;
  1341.  
  1342.                         if (eos()) { gui_setstatus("Connection reset\n"); break; }
  1343.  
  1344.                         /* read chunk sequence number */
  1345.                         readfromstream(conn_socket, &s1, 1, rawfile);
  1346.                         readfromstream(conn_socket, &s2, 1, rawfile);
  1347.                         readfromstream(conn_socket, &s3, 1, rawfile);
  1348.                         readfromstream(conn_socket, &s4, 1, rawfile);
  1349.                         seqno   = (s4<<24) + (s3<<16) + (s2<<8) + s1;
  1350.  
  1351.                         if (eos()) { gui_setstatus("Connection reset\n"); break; }
  1352.  
  1353.                         /* read two unknown bytes */
  1354.                         readfromstream(conn_socket, &u1, 1, rawfile);
  1355.                         readfromstream(conn_socket, &u2, 1, rawfile);
  1356.                         partflag = (u2<<8) + u1;
  1357.  
  1358.                         if (eos()) { gui_setstatus("Connection reset\n"); break; }
  1359.  
  1360.                         /* read second length entry (length confirmation) */
  1361.                         readfromstream(conn_socket, &l1, 1, rawfile);
  1362.                         readfromstream(conn_socket, &l2, 1, rawfile);
  1363.                         length2 = (l2<<8) + l1;
  1364.  
  1365.                         if (eos()) { gui_setstatus("Connection reset\n"); break; }
  1366.  
  1367.                         /* Sanity check on chunk header. Second length entry must match the first. */
  1368.                         if (length2 != length)
  1369.                         {
  1370.                             gui_logtext("Length confirmation doesn't match!\n");
  1371.                             break;
  1372.                         }
  1373.  
  1374.                         /* calculate length of chunk body. */
  1375.                         bodylength = length-8;
  1376.  
  1377.                         /* check if the body length exceeds our buffer size */
  1378.                         if (bodylength > bufsize)
  1379.                         {
  1380.                             gui_logtext("Buffer too small. Chunk is %d bytes!\n", length);
  1381.                             break;
  1382.                         }
  1383.  
  1384.                         /* check length of chunk body */
  1385.                         if (bodylength <= 0)
  1386.                         {
  1387.                             gui_logtext("Chunk has no body!\n");
  1388.                             break;
  1389.                         }
  1390.  
  1391.                         /* Read chunk's body data */
  1392.                         if (type != HEADER_CHUNK) header_offset = 0;
  1393.                         got = readfromstream(conn_socket, Buffer + header_offset, bodylength, rawfile);
  1394.                         bodylength = header_offset + bodylength;
  1395.                         got        = header_offset + got;
  1396.  
  1397.                         /* Try to extract a timecode from all known chunk/content types */
  1398.                         strcpy(timecodestring, "???");
  1399.                         timecode = 0;
  1400.  
  1401.                         /* this only applies to data chunks */
  1402.                         if (type == DATA_CHUNK)
  1403.                         {
  1404.                             int tc_start;
  1405.  
  1406.                             if (headeronly) break;
  1407.  
  1408.                             /* save the first seqno available as a reference */
  1409.                             if (startseqno == 0xffffffff)
  1410.                             {
  1411.                                 startseqno = seqno;
  1412.                             }
  1413.  
  1414.                             /* fix the seqno for live recordings only */
  1415.                             if (hi->time == 0)
  1416.                             {
  1417.                                 /* refer seqno to the point we "zapped in" (for live streams) */
  1418.                                 if (startseqno != 0xffffffff)
  1419.                                     seqno -= startseqno;
  1420.                             }
  1421.  
  1422.                             /* find the location of the time code */
  1423.                             if ((tc_start = whereis_timecode(Buffer)) > 0)
  1424.                             {
  1425.                                 /* The timecode is an integer value defining milliseconds */
  1426.                                 /* enough range for about 50 days! */
  1427.                                 get_long(&Buffer[tc_start], &timecode);
  1428.  
  1429.                                 /* save the first timecode available as a reference */
  1430.                                 if (starttime == 0xffffffff)
  1431.                                     starttime = timecode;
  1432.  
  1433.                                 /* fix timecode for live recordings only */
  1434.                                 if (hi->time == 0)
  1435.                                 {
  1436.                                     /* refer timecode to the point we "zapped in" (live streams) */
  1437.                                     timecode -= starttime;
  1438.  
  1439.                                     /* this fixes the timecodes in the memory buffer */
  1440.                                     fix_timecodes(Buffer, bodylength, starttime, seqno, hi);
  1441.                                 }
  1442.  
  1443.                                 /* save max. timecode value */
  1444.                                 if (timecode > maxtimecode)
  1445.                                     maxtimecode = timecode;
  1446.  
  1447.                                 /* create a string with a human-readable form of the timecode */
  1448.                                 strcpy(timecodestring, createtimestring(timecode));
  1449.                             }
  1450.                         }
  1451.  
  1452.                         /* calculate progress indicator (scale: 0....10000) */
  1453.                         if (hi->time == 0)
  1454.                              /* live streams */
  1455.                             if (maxtime == 0)   /* unlimited recording */
  1456.                                 progress = 0;
  1457.                             else                /* limited time recording */
  1458.                                 progress = (int)((double)timecode*10000/(maxtime*60*1000));
  1459.                         else
  1460.                             /* prerecorded content */
  1461.                             progress = (int)((double)timecode*10000/hi->time);
  1462.  
  1463.                         /* Print current position in stream download */
  1464.                         gui_logtext("%5d kB (%2d%%), HDR: $%04x, part: $%04x, %4d bytes, seq $%08x, tc: %s\n", bytesread/1024, progress/100, (int)type, (int)partflag, length, seqno, timecodestring);
  1465.  
  1466.                         /* Extract the block size from the ASF header. */
  1467.                         /* This block size is essential in the ASF file */
  1468.                         /* format. All data chunks in the ASF file must */
  1469.                         /* have this length, even if network transmission */
  1470.                         /* sent us smaller chunks! */
  1471.                         if (type == HEADER_CHUNK)
  1472.                         {
  1473.                             int offs;
  1474.                             
  1475.                             /* Headers may be split into several parts with */
  1476.                             /* a rising sequence number. This case occurs in */
  1477.                             /* audio books on broadcast.com, for example. */
  1478.  
  1479.                             /* This indicates the first header part */
  1480.                             if (partflag & 0x0400)
  1481.                             {
  1482.                                 header_offset = 0;
  1483.                                 get_long(&Buffer[16], &header_length);
  1484.                                 header_length += 50;
  1485.                             }
  1486.  
  1487.                             /* header progress indicator */
  1488.                             if (bodylength < header_length)
  1489.                                 gui_setstatus("receiving ASF header (%d/%d)!\n", bodylength, header_length);
  1490.  
  1491.                             /* Skip parsing the header if it hasn't been received completely */
  1492.                             if (!(partflag & 0x0800))
  1493.                             {
  1494.                                 /* next header partition will be appended */
  1495.                                 header_offset = bodylength;
  1496.  
  1497.                                 /* this prevents saving a partial header to the output file */
  1498.                                 bodylength = header_length;
  1499.                             }
  1500.                             else
  1501.                             {
  1502.                                 /* finally got the header */
  1503.                                 gui_setstatus("received ASF header!\n");
  1504.  
  1505.                                 /* find a specific object in this header */
  1506.                                 offs = find_id(HDR_ID,Buffer, got);
  1507.                                 if (offs == -1)
  1508.                                 {
  1509.                                     gui_criticalerror("Unable to parse this ASF header!\n");
  1510.                                     break;
  1511.                                 }
  1512.                                 else
  1513.                                 {
  1514.                                     /* extract required information */
  1515.                                     hi->header_offs = offs;
  1516.                                     get_quad(&Buffer[offs+HDR_TOTAL_SIZE_8], &hi->totalsize_hi, &hi->totalsize_lo);
  1517.                                     get_quad(&Buffer[offs+HDR_FINE_TOTALTIME_8], &hi->totaltime_hi, &hi->totaltime_lo);
  1518.                                     get_long(&Buffer[offs+HDR_PLAYTIME_OFFSET_4], &hi->offset);
  1519.                                     get_long(&Buffer[offs+HDR_FLAGS_4], &hi->flags);
  1520.                                     get_long(&Buffer[offs+HDR_ASF_CHUNKLENGTH_4], &hi->chunklength);
  1521.                                     get_long(&Buffer[offs+HDR_ASF_CHUNKLENGTH_CONFIRM_4], &hi->chunklength2);
  1522.                                     
  1523.                                     /* check if the extracted chunk length looks good */
  1524.                                     if (!(hi->chunklength < MAX_CHUNK_SIZE && (hi->chunklength == hi->chunklength2)))
  1525.                                     {
  1526.                                         gui_criticalerror(
  1527.                                             "Unable to capture this stream!\n"
  1528.                                             "This one uses variable chunk sizes,\n"
  1529.                                             "which is not supported. Sorry! ;-)\n");
  1530.                                         
  1531.                                         hi->contenttype = unknown_content;
  1532.                                         break;
  1533.                                     }
  1534.                                     
  1535.                                     /* calculate playtime in milliseconds (0 for live streams) */
  1536.                                     if (hi->totaltime_hi == 0 && hi->totaltime_lo == 0)
  1537.                                     {
  1538.                                         hi->time = 0; /* live streams */
  1539.                                     }
  1540.                                     else
  1541.                                         hi->time = (int)((double)429496.7296 * hi->totaltime_hi) + (hi->totaltime_lo / 10000) - hi->offset;
  1542.  
  1543.                                     /* store position where the ASF header segment ends */
  1544.                                     /* and the chunk data segment starts */
  1545.                                     hi->endofheader_offs = bodylength - DATSEG_HDR_SIZE;
  1546.                                     
  1547.                                     if (!headeronly)
  1548.                                         gui_setstatus("receiving stream...\n");
  1549.                                     else
  1550.                                         break;
  1551.                                 }
  1552.                             }
  1553.                         }
  1554.  
  1555.                         /* check chunk body for completeness */
  1556.                         if (got < bodylength)
  1557.                         {
  1558.                             if (outfile != NULL && type == DATA_CHUNK)
  1559.                                 gui_logtext("Received incomplete chunk... (Chunk is NOT saved)\n");
  1560.                         }
  1561.                         else
  1562.                         {
  1563.                             /* Ignore media type header if the download */
  1564.                             /* resumes a previous transmission */
  1565.                             if (type == HEADER_CHUNK && resume)
  1566.                             {
  1567.                                 gui_logtext("Not saving header since this is a RESUME.\n");
  1568.                             }
  1569.                             else
  1570.                             {
  1571.                                 /* count how many bytes we accepted from server */
  1572.                                 bytesread += got;
  1573.  
  1574.                                 /* some statistics for data chunks only */
  1575.                                 if (type == DATA_CHUNK)
  1576.                                 {
  1577.                                     /* count number of chunks */
  1578.                                     numdatachunks++;
  1579.  
  1580.                                     /* count total size of chunks */
  1581.                                     sizeofdatachunks += hi->chunklength;
  1582.                                 }
  1583.  
  1584.                                 if (outfile != NULL)
  1585.                                 {
  1586.                                     if (type == DATA_CHUNK)
  1587.                                     {
  1588.                                         /* When saving data chunks, seek to appropriate position */
  1589.                                         /* in file. This should prevent problems when resuming to */
  1590.                                         /* download files containing a truncated last chunk */
  1591.                                         fseek(outfile, hi->endofheader_offs + DATSEG_HDR_SIZE + seqno * hi->chunklength, SEEK_SET);
  1592.                                     }
  1593.  
  1594.                                     /* Save chunk body to ASF file */
  1595.                                     fwrite(Buffer, 1, got, outfile);
  1596.  
  1597.                                     /* Fill up unused bytes in this chunk. */
  1598.                                     /* ASF requires equally sized chunks for */
  1599.                                     /* the main stream content */
  1600.                                     if (type == DATA_CHUNK && got < (int)hi->chunklength)
  1601.                                     {
  1602.                                         int i;
  1603.                                         for (i=0; i < (int)hi->chunklength-got; i++) fputc(0, outfile);
  1604.                                     }
  1605.                                 }
  1606.  
  1607.                                 /* send progress information to main GUI */
  1608.                                 gui_progressinfo(bytesread, timecodestring, progress, seqno, timecode);
  1609.  
  1610.                                 /* set a new total time for the stream */
  1611.                                 /* (important for preview and slider functionality) */
  1612.                                 if (hi->time == 0)
  1613.                                 {
  1614.                                     if (maxtime == 0)
  1615.                                         gui_modify_duration(timecode);
  1616.                                     else
  1617.                                         gui_modify_duration(maxtime*60*1000);
  1618.                                 }
  1619.                             }
  1620.                         }
  1621.  
  1622.                         /* use recording time limit, if specified */
  1623.                         if (maxtime != 0)
  1624.                         {
  1625.                             if ((int)timecode >= (maxtime*60*1000))
  1626.                             {
  1627.                                 gui_setstatus("maxtime reached.\n");
  1628.                                 break;
  1629.                             }
  1630.                         }
  1631.  
  1632.                         /* check for end request */
  1633.                         if (abortflag) break;
  1634.  
  1635.                     } /* for (;;) */
  1636.  
  1637.                     /* fix total file size and for live streams the header information as well */
  1638.                     if (outfile != NULL)
  1639.                     {
  1640.                         unsigned int filesize_lo;
  1641.                         unsigned int filesize_hi;
  1642.  
  1643.                         /* Determine file size of .ASF file */
  1644.                         fseek(outfile, 0, SEEK_END);
  1645.                         filesize_hi = 0;
  1646.                         filesize_lo = ftell(outfile);
  1647.  
  1648.                         /* write this file size to the ASF header */
  1649.                         fseek(outfile, hi->header_offs + HDR_TOTAL_SIZE_8, SEEK_SET);
  1650.                         write_quad(outfile, filesize_hi, filesize_lo);
  1651.  
  1652.                         /* Correct some details for live streams */
  1653.                         if (hi->time == 0)
  1654.                         {
  1655.                             double totaltime;
  1656.                             unsigned int totaltime_hi;
  1657.                             unsigned int totaltime_lo;
  1658.  
  1659.                             double playtime;
  1660.                             unsigned int playtime_hi;
  1661.                             unsigned int playtime_lo;
  1662.  
  1663.                             unsigned int num_chunks_hi;
  1664.                             unsigned int num_chunks_lo;
  1665.  
  1666.                             unsigned int segmentsize_hi;
  1667.                             unsigned int segmentsize_lo;
  1668.  
  1669.                             /* bugfixed calculations, these were broken in V 1.0 */
  1670.                             totaltime = (double)(maxtimecode) * 10000;
  1671.                             totaltime_hi = (int)(totaltime / 4294967296.0);
  1672.                             totaltime_lo = (int)(totaltime - ((double)totaltime_hi * 4294967296.0));
  1673.  
  1674.                             playtime = (double)(maxtimecode - hi->offset) * 10000;
  1675.                             playtime_hi = (int)(playtime / 4294967296.0);
  1676.                             playtime_lo = (int)(playtime - ((double)playtime_hi * 4294967296.0));
  1677.  
  1678.                             num_chunks_hi = 0;
  1679.                             num_chunks_lo = numdatachunks;
  1680.  
  1681.                             /* write correct number of packets for stream */
  1682.                             fseek(outfile, hi->header_offs + HDR_NUM_PACKETS_8, SEEK_SET);
  1683.                             write_quad(outfile, num_chunks_hi, num_chunks_lo);
  1684.  
  1685.                             /* write correct totaltime for stream */
  1686.                             fseek(outfile, hi->header_offs + HDR_FINE_TOTALTIME_8, SEEK_SET);
  1687.                             write_quad(outfile, totaltime_hi, totaltime_lo);
  1688.  
  1689.                             /* write correct playtime for stream */
  1690.                             fseek(outfile, hi->header_offs + HDR_FINE_PLAYTIME_8, SEEK_SET);
  1691.                             write_quad(outfile, playtime_hi, playtime_lo);
  1692.  
  1693.                             /* enable seeking in file */
  1694.                             fseek(outfile, hi->header_offs + HDR_FLAGS_4, SEEK_SET);
  1695.                             write_long(outfile, 0x00000002);
  1696.  
  1697.                             /* write total size of data chunk segment */
  1698.                             segmentsize_hi = 0;
  1699.                             segmentsize_lo = DATSEG_HDR_SIZE + sizeofdatachunks;
  1700.                             fseek(outfile, hi->endofheader_offs + 0x10, SEEK_SET);
  1701.                             write_quad(outfile, segmentsize_hi, segmentsize_lo);
  1702.  
  1703.                             /* write total number of chunks */
  1704.                             fseek(outfile, hi->endofheader_offs + DATSEG_NUMCHUNKS_4, SEEK_SET);
  1705.                             write_long(outfile, numdatachunks);
  1706.                         }
  1707.                     }
  1708.  
  1709.                     if (abortflag)
  1710.                     {
  1711.                         gui_seterror("Transfer aborted!\n");
  1712.                     }
  1713.                 }
  1714.             }
  1715.         }
  1716.         my_closesocket(conn_socket);
  1717.     }
  1718.  
  1719.     return abortflag;
  1720. }
  1721.  
  1722.  
  1723. enum
  1724. {
  1725.     mode_determinefiletype,
  1726.     mode_searchforbracket,
  1727.     mode_expectkeyword,
  1728.     mode_readkeyword,
  1729.     mode_expectoption,
  1730.     mode_readoption,
  1731.     mode_expectargumentoroption,
  1732.     mode_argumentstart,
  1733.     mode_argument,
  1734.     mode_argumentquotes,
  1735.     mode_expectclosingbrackets,
  1736. };
  1737.  
  1738. enum
  1739. {
  1740.     submode_none,
  1741.     submode_eatwhite
  1742. };
  1743.  
  1744. typedef enum
  1745. {
  1746.     unknownfile = 0,
  1747.     xmlfile,
  1748.     inifile,
  1749.     plainfile,
  1750.  
  1751. } ASXFileType;
  1752.  
  1753.  
  1754. /* parse a redirection file (either from disk or from memory) */
  1755. /* builds a list of stream references (URLs) */
  1756.  
  1757. int parse_redirection( unsigned char *filename, unsigned char *buffer, unsigned int redir_size, unsigned int maxtime, unsigned short portnum )
  1758. {
  1759.     int result = 0;
  1760.  
  1761.     FILE *infile;
  1762.  
  1763.     ASXFileType filetype;
  1764.     int error = 0;
  1765.  
  1766.     unsigned char *pos;
  1767.     int c;
  1768.     int iswhite;
  1769.     int isalpha;
  1770.  
  1771.     int mode;
  1772.     int submode;
  1773.  
  1774.     unsigned char *urlptr;
  1775.     char URLBuffer[4096];
  1776.     int my_argc;
  1777.     int urlcounter;
  1778.     unsigned char *(my_argv[MAXURLS]);
  1779.  
  1780.     unsigned char Keyword[128];
  1781.     unsigned char *keywordptr = NULL;
  1782.  
  1783.     unsigned char OptionBuffer[512];
  1784.     unsigned char *optionptr = NULL;
  1785.     int optionc = 0;
  1786.     unsigned char *(optionnamev[256]);
  1787.     unsigned char *(optionargv[256]);
  1788.  
  1789.     int keywordfinished;
  1790.     int termination;
  1791.  
  1792.     unsigned char TextLine[512];
  1793.     unsigned char *lineptr;
  1794.     unsigned char CurrentScope[128];
  1795.  
  1796.     my_argc = 0;
  1797.     urlcounter = 0;
  1798.     urlptr = URLBuffer;
  1799.  
  1800.     /* read file, if filename given and fill buffer */
  1801.     if (filename != NULL && buffer == NULL)
  1802.     {
  1803.         /* if filename starts with http:// or mms://, it is not a filename */
  1804.         /* in this case, parse_redirection() returns with result 0 */
  1805.         if (strnicmp("http://",  filename, 7) &&
  1806.             strnicmp("mms://",   filename, 6) &&
  1807.             strnicmp("mmsu://",  filename, 7) &&
  1808.             strnicmp("mmst://",  filename, 7)   )
  1809.         {
  1810.             infile = fopen( filename, "rb" );
  1811.             if (infile != NULL)
  1812.             {
  1813.                 fseek(infile, 0, SEEK_END);
  1814.                 redir_size = ftell(infile);
  1815.  
  1816.                 fseek(infile, 0, SEEK_SET);
  1817.                 if ((buffer = malloc(redir_size)) != NULL)
  1818.                 {
  1819.                     fread(buffer, 1, redir_size, infile);
  1820.                 }
  1821.                 fclose(infile);
  1822.             }
  1823.         }
  1824.     }
  1825.  
  1826.     if (buffer != NULL && redir_size > 0)
  1827.     {
  1828.         pos = buffer;
  1829.         filetype = unknownfile;
  1830.         mode = mode_determinefiletype;
  1831.         submode = submode_eatwhite;
  1832.         keywordptr = Keyword;
  1833.  
  1834.         /* the first argument passed to main_function must be the program name */
  1835.         my_argv[my_argc++] = urlptr;
  1836.         urlptr += (sprintf(urlptr, "ASFRecorder")+1);
  1837.  
  1838.         /* explicity specify recording time, if different from 0 */
  1839.         if (maxtime != 0)
  1840.         {
  1841.             my_argv[my_argc++] = urlptr;
  1842.             urlptr += (sprintf(urlptr, "-m")+1);
  1843.             my_argv[my_argc++] = urlptr;
  1844.             urlptr += (sprintf(urlptr, "%d", maxtime)+1);
  1845.         }
  1846.  
  1847.         /* explicity specify port number, if different from default */
  1848.         if (portnum != DEFAULT_PORT)
  1849.         {
  1850.             my_argv[my_argc++] = urlptr;
  1851.             urlptr += (sprintf(urlptr, "-p")+1);
  1852.             my_argv[my_argc++] = urlptr;
  1853.             urlptr += (sprintf(urlptr, "%d", portnum)+1);
  1854.         }
  1855.  
  1856.         /* The following code is an XML parser with a very forgiving syntax check   */
  1857.         /* It doesn't comply to any "official" XML syntax rules, but it works fine. */
  1858.  
  1859.         /* It extracts keywords, options and arguments and can determine opening    */
  1860.         /* and closing of the scope of a keyword (<ASX>, </ASX>, <ASX OPTION />)    */
  1861.         /* example of syntax: <KEYWORD OPTION1=ARGUMENT OPTION2="ARGUMENT">         */
  1862.  
  1863.         /* character loop */
  1864.         while ( (!error) && (pos <= (buffer+redir_size)) && (filetype == unknownfile || filetype == xmlfile) )
  1865.         {
  1866.             if (pos == buffer+redir_size) {
  1867.                 c = EOF;
  1868.                 pos++;
  1869.             } else
  1870.                 c = *pos++;
  1871.  
  1872.             if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == EOF )
  1873.                 iswhite = 1;
  1874.             else
  1875.                 iswhite = 0;
  1876.  
  1877.             if ((c >= 'a' && c <= 'z') || c >= 'A' && c <= 'Z')
  1878.                 isalpha = 1;
  1879.             else
  1880.                 isalpha = 0;
  1881.  
  1882.             if (submode == submode_eatwhite && iswhite) continue;
  1883.  
  1884.             keywordfinished = 0;
  1885.  
  1886.             switch(mode)
  1887.             {
  1888.             case mode_determinefiletype:
  1889.                 if (c == '<') {
  1890.                     /* fall through */
  1891.                 }
  1892.                 else {
  1893.                     if (c == '[') {
  1894.                         /* file is probably an INI file */
  1895.                         filetype = inifile;
  1896.                     }
  1897.                     else {
  1898.                         if (isalpha) {
  1899.                             /* collect first plaintext keyword */
  1900.                             *keywordptr++ = c;
  1901.                             submode = submode_none;
  1902.                         }
  1903.                         else {
  1904.                             *keywordptr++ = '\0';
  1905.                             /* check if keyword matches "ASF" or any know protocol extension */
  1906.                             if (!stricmp(Keyword, "ASF")   ||
  1907.                                  stricmp(Keyword, "http")  ||
  1908.                                  stricmp(Keyword, "mms")   ||
  1909.                                  stricmp(Keyword, "mmst")  ||
  1910.                                  stricmp(Keyword, "mmsu") ) {
  1911.                                 /* file type is probably a plain ASX file */
  1912.                                 filetype = plainfile;
  1913.                             }
  1914.                             else {
  1915.                                 gui_logtext("ASF syntax error 1!\n"); error = 1;
  1916.                             }
  1917.                         }
  1918.                     }
  1919.                     break;
  1920.                 }
  1921.                 /* fall through */
  1922.  
  1923.             case mode_searchforbracket:
  1924.                 if (c == '<') {
  1925.                     termination=0; keywordptr = Keyword; optionptr = OptionBuffer;
  1926.                     optionc = 0;
  1927.                     mode = mode_expectkeyword; submode = submode_eatwhite;
  1928.                 }
  1929.                 break;
  1930.  
  1931.             case mode_expectkeyword:
  1932.             {
  1933.                 if (c == '/') {
  1934.                     termination = 1;
  1935.                     break;
  1936.                 }
  1937.                 else {
  1938.                     if (isalpha) {
  1939.                         mode = mode_readkeyword; submode = submode_none;
  1940.                         /* fall through */
  1941.                     }
  1942.                     else {
  1943.                         gui_logtext("ASF syntax error 2!\n"); error = 1;
  1944.                         break;
  1945.                     }
  1946.                 }
  1947.             }
  1948.             /* fall through */
  1949.  
  1950.             case mode_readkeyword:
  1951.                 if (!iswhite) {
  1952.                     if (isalpha) {
  1953.                         *keywordptr++ = c;
  1954.                     }
  1955.                     else {
  1956.                         if (c == '/') {
  1957.                             *keywordptr++ = '\0';
  1958.                             termination = 1;
  1959.                             mode = mode_expectclosingbrackets; submode = submode_eatwhite;
  1960.                         }
  1961.                         else {
  1962.                             if (c == '>') {
  1963.                                 *keywordptr++ = '\0';
  1964.                                 keywordfinished = 1;
  1965.                             }
  1966.                             else {
  1967.                                 gui_logtext("ASF syntax error 3!\n"); error = 1;
  1968.                             }
  1969.                         }
  1970.                     }
  1971.                 }
  1972.                 else {
  1973.                     *keywordptr++ = '\0';
  1974.                     mode = mode_expectoption; submode = submode_eatwhite;
  1975.                 }
  1976.                 break;
  1977.  
  1978.             case mode_expectoption:
  1979.                 {
  1980.                     if (isalpha)
  1981.                     {
  1982.                         optionnamev[optionc] = optionptr; optionargv[optionc] = NULL;
  1983.                         optionc++;
  1984.                         mode = mode_readoption; submode = submode_none;
  1985.                         /* fall through */
  1986.                     }
  1987.                     else {
  1988.                         if (c == '/') {
  1989.                             termination = 1;
  1990.                             mode = mode_expectclosingbrackets; submode = submode_eatwhite;
  1991.                             break;
  1992.                         }
  1993.                         else {
  1994.                             if (c == '>') {
  1995.                                 keywordfinished = 1;
  1996.                                 break;
  1997.                             }
  1998.                             else {
  1999.                                 gui_logtext("ASF syntax error 4!\n"); error = 1;
  2000.                                 break;
  2001.                             }
  2002.                         }
  2003.                     }
  2004.                 }
  2005.                 /* fall through */
  2006.  
  2007.             case mode_readoption:
  2008.                 {
  2009.                     if (isalpha) {
  2010.                         *optionptr++ = c;
  2011.                         break;
  2012.                     }
  2013.                     else {
  2014.                         *optionptr++ = '\0';
  2015.                          mode = mode_expectargumentoroption; submode = submode_eatwhite;
  2016.  
  2017.                          if (iswhite) break;
  2018.  
  2019.                          /* fall through */
  2020.                     }
  2021.                 }
  2022.                 /* fall through */
  2023.  
  2024.             case mode_expectargumentoroption:
  2025.                 {
  2026.                     if (c == '/') {
  2027.                         termination = 1;
  2028.                         mode = mode_expectclosingbrackets; submode = submode_eatwhite;
  2029.                     }
  2030.                     else {
  2031.                         if (c == '>') {
  2032.                             keywordfinished = 1;
  2033.                         }
  2034.                         else {
  2035.                             if (c == '=') {
  2036.                                 optionargv[optionc-1] = optionptr;
  2037.                                 mode = mode_argumentstart; submode = submode_eatwhite;
  2038.                             }
  2039.                             else {
  2040.                                 gui_logtext("ASF syntax error 5!\n"); error = 1;
  2041.                             }
  2042.                         }
  2043.                     }
  2044.                 }
  2045.                 break;
  2046.  
  2047.             case mode_argumentstart:
  2048.                 {
  2049.                     if (c == '"') {
  2050.                         mode = mode_argumentquotes; submode = submode_none;
  2051.                         break;
  2052.                     }
  2053.                     else {
  2054.                         if (c == '/') {
  2055.                             gui_logtext("ASF syntax error 6!\n"); error = 1;
  2056.                             break;
  2057.                         }
  2058.                         else {
  2059.                             mode = mode_argument; submode = submode_eatwhite;
  2060.                             if (iswhite) break;
  2061.                             /* fall through */
  2062.                         }
  2063.                     }
  2064.                 }
  2065.                 /* fall through */
  2066.  
  2067.             case mode_argument:
  2068.                 {
  2069.                     if (c == '"' || c == '=') {
  2070.                         gui_logtext("ASF syntax error 7!\n"); error = 1;
  2071.                     }
  2072.                     else {
  2073.                         if (iswhite)
  2074.                         {
  2075.                             *optionptr++ = '\0';
  2076.                             mode = mode_expectoption; submode = submode_eatwhite;
  2077.                         }
  2078.                         else {
  2079.                             if (c == '/') {
  2080.                                 *optionptr++ = '\0';
  2081.                                 termination = 1;
  2082.                                 mode = mode_expectclosingbrackets; submode = submode_eatwhite;
  2083.                             }
  2084.                             else {
  2085.                                 if (c == '>') {
  2086.                                     *optionptr++ = '\0';
  2087.                                     keywordfinished = 1;
  2088.                                 }
  2089.                                 else {
  2090.                                     *optionptr++ = c;
  2091.                                     submode = submode_none;
  2092.                                 }
  2093.                             }
  2094.                         }
  2095.                     }
  2096.                 }
  2097.                 break;
  2098.  
  2099.             case mode_argumentquotes:
  2100.                 {
  2101.                     if (c != '"') {
  2102.                         *optionptr++ = c;
  2103.                     }
  2104.                     else {
  2105.                         *optionptr++ = '\0';
  2106.  
  2107.                         mode = mode_expectoption; submode = submode_eatwhite;
  2108.                     }
  2109.                 }
  2110.                 break;
  2111.  
  2112.             case mode_expectclosingbrackets:
  2113.                 {
  2114.                     if (c == '>') {
  2115.                         keywordfinished = 1;
  2116.                     }
  2117.                     else {
  2118.                         gui_logtext("ASF syntax error 8!\n"); error = 1;
  2119.                     }
  2120.                 }
  2121.                 break;
  2122.             }
  2123.  
  2124.             if (keywordfinished)
  2125.             {
  2126.                 /* begin to search for next keyword starting with a "<" */
  2127.                 mode = mode_searchforbracket; submode = submode_eatwhite;
  2128.  
  2129. #if 0 /* just for debugging */
  2130.                 int i;
  2131.  
  2132.                 if (termination)
  2133.                     gui_logtext("Keyword: /%s\n", Keyword);
  2134.                 else
  2135.                     gui_logtext("Keyword %s\n", Keyword);
  2136.                 for (i=0; i<optionc; i++)
  2137.                 {
  2138.                     if (optionargv[i] == NULL)
  2139.                         gui_logtext("option #%d %s\n", i+1, optionnamev[i]);
  2140.                     else
  2141.                         gui_logtext("option #%d %s = %s\n", i+1, optionnamev[i], optionargv[i]);
  2142.                 }
  2143. #endif
  2144.  
  2145.                 if (!stricmp(Keyword, "ASX"))
  2146.                 {
  2147.                     /* signal the calling main_function that we parsed this ASX file */
  2148.                     result = 1;
  2149.                 }
  2150.  
  2151.                 if (!stricmp(Keyword, "Entry") && (!termination))
  2152.                 {
  2153.                     /* set a marker for every new entry */
  2154.  
  2155.                     /* this is because one entry can have multiple references */
  2156.                     /* pointing to the same stream on different servers       */
  2157.                     /* (or lower-bitrate streams) for redundancy              */
  2158.  
  2159.                     /* this marker allows us to download only one stream of   */
  2160.                     /* these multiple references                              */
  2161.  
  2162.                     my_argv[my_argc++] = urlptr;
  2163.                     urlptr += (sprintf(urlptr, "-e")+1);
  2164.                 }
  2165.  
  2166.                 if ((!stricmp(Keyword, "Ref")) || (!stricmp(Keyword, "EntryRef")))
  2167.                 {
  2168.                     if (optionc > 0)
  2169.                     {
  2170.                         if ( (!stricmp("href", optionnamev[0])) && (optionargv[0] != NULL) )
  2171.                         {
  2172.                             /* store this URL for later download */
  2173.                             my_argv[my_argc++] = urlptr;
  2174.                             urlptr += (sprintf(urlptr, "%s", optionargv[0])+1);
  2175.                             urlcounter++;
  2176.                         }
  2177.                     }
  2178.                 }
  2179.             }
  2180.  
  2181.             if (error)
  2182.             {
  2183.                 /* try to recover after an error */
  2184.                 mode = mode_searchforbracket; submode = submode_eatwhite;
  2185.                 error = 0;
  2186.             }
  2187.         }
  2188.  
  2189.         if (filetype == inifile)
  2190.         {
  2191.             /* signal the calling main_function that we parsed this INI file */
  2192.             result = 1;
  2193.  
  2194.             pos = buffer;
  2195.             lineptr = TextLine;
  2196.             strcpy(CurrentScope, "");
  2197.  
  2198.             /* character loop */
  2199.             while ( (!error) && ( pos <= (buffer+redir_size) ) )
  2200.             {
  2201.                 int linecomplete = 0;
  2202.  
  2203.                 if (pos == buffer+redir_size) {
  2204.                     c = EOF;
  2205.                     pos++;
  2206.                 }   else
  2207.                     c = *pos++;
  2208.  
  2209.  
  2210.                 if ((c != '\r') && (c != '\n') && (c != EOF))
  2211.                     *lineptr++ = c;
  2212.                 else
  2213.                     *lineptr++ = 0;
  2214.  
  2215.                 if (c == '\n' || c == EOF)
  2216.                 {
  2217.                     lineptr = TextLine;
  2218.                     linecomplete = 1;
  2219.                 }
  2220.  
  2221.                 if (linecomplete)
  2222.                 {
  2223.                     if (TextLine[0] == '[')
  2224.                     {
  2225.                         strcpy(CurrentScope, TextLine);
  2226.  
  2227.                         if (!stricmp(CurrentScope, "[Reference]"))
  2228.                         {
  2229.                             /* set new entry marker (-e option) */
  2230.                             my_argv[my_argc++] = urlptr;
  2231.                             urlptr += (sprintf(urlptr, "-e")+1);
  2232.                             /* all following URLs belong to this single entry */
  2233.                         }
  2234.                     }
  2235.                     else
  2236.                     {
  2237.                         if (!stricmp(CurrentScope, "[Reference]"))
  2238.                         {
  2239.                             /* URLs begin with with REFxx= where xx is any number */
  2240.                             if (!strnicmp(TextLine, "REF", 3))
  2241.                             {
  2242.                                 unsigned char *ptr = TextLine+3;
  2243.                                 /* skip numbers */
  2244.                                 while ((*ptr) >= '0' && (*ptr) <= '9') ptr++;
  2245.                                 if ((*ptr) == '=')
  2246.                                 {
  2247.                                     ptr++;
  2248.  
  2249.                                     /* save the resulting URL string */
  2250.                                     my_argv[my_argc++] = urlptr;
  2251.                                     urlptr += (sprintf(urlptr, "%s",ptr)+1);
  2252.                                     urlcounter++;
  2253.                                 }
  2254.                             }
  2255.                         }
  2256.                     }
  2257.                 }
  2258.             }
  2259.         }
  2260.  
  2261.         if (filetype == plainfile)
  2262.         {
  2263.             /* signal the calling main_function that we parsed this INI file */
  2264.             result = 1;
  2265.  
  2266.             pos = buffer;
  2267.             lineptr = TextLine;
  2268.  
  2269.             /* set new entry marker (-e option) */
  2270.             my_argv[my_argc++] = urlptr;
  2271.             urlptr += (sprintf(urlptr, "-e")+1);
  2272.             /* all following URLs belong to this single entry */
  2273.  
  2274.             /* character loop */
  2275.             while ( (!error) && ( pos <= (buffer+redir_size) ) )
  2276.             {
  2277.                 int linecomplete = 0;
  2278.  
  2279.                 if (pos == buffer+redir_size) {
  2280.                     c = EOF;
  2281.                     pos++;
  2282.                 } else
  2283.                     c = *pos++;
  2284.  
  2285.                 if ((c != '\r') && (c != '\n') && (c != EOF))
  2286.                     *lineptr++ = c;
  2287.                 else
  2288.                     *lineptr++ = 0;
  2289.  
  2290.                 if (c == '\n' || c == EOF)
  2291.                 {
  2292.                     lineptr = TextLine;
  2293.                     linecomplete = 1;
  2294.                 }
  2295.  
  2296.                 if (linecomplete)
  2297.                 {
  2298.                     unsigned char *ptr = TextLine;
  2299.  
  2300.                     /* eat whitespaces */
  2301.                     while( (*ptr == ' ') || (*ptr == '\t') ) ptr++;
  2302.  
  2303.                     /* don't know if multiple lines are allowed in this */
  2304.                     /* plain ASX file format but who cares */
  2305.                     if ((!strnicmp(ptr, "ASF ",  4)) ||
  2306.                         (!strnicmp(ptr, "ASF\t", 4))   )
  2307.                     {
  2308.                         ptr += 4;
  2309.  
  2310.                         /* eat whitespaces */
  2311.                         while( (*ptr == ' ') || (*ptr == '\t') ) ptr++;
  2312.  
  2313.                         /* save the resulting URL string */
  2314.                         my_argv[my_argc++] = urlptr;
  2315.                         urlptr += (sprintf(urlptr, "%s",ptr)+1);
  2316.                         urlcounter++;
  2317.                     }
  2318.                     else
  2319.                     {
  2320.                         /* some providers use ASX files that contain the URL only */
  2321.                         if ((!strnicmp(ptr, "http://",  7)) ||
  2322.                             (!strnicmp(ptr, "mms://",   6)) ||
  2323.                             (!strnicmp(ptr, "mmsu://",  7)) ||
  2324.                             (!strnicmp(ptr, "mmst://",  7)) )
  2325.                         {
  2326.                             /* save the resulting URL string */
  2327.                             my_argv[my_argc++] = urlptr;
  2328.                             urlptr += (sprintf(urlptr, "%s",ptr)+1);
  2329.                             urlcounter++;
  2330.                         }
  2331.                     }
  2332.                 }
  2333.             }
  2334.         }
  2335.  
  2336. #if 0 /* just for debugging */
  2337.         if (my_argc > 1)
  2338.         {
  2339.             int url;
  2340.  
  2341.             gui_logtext("List of arguments for main_function()\n");
  2342.  
  2343.             for (url = 0; url < my_argc; url++)
  2344.             {
  2345.                 gui_criticalerror("arg #%d: %s\n", url, my_argv[url]);
  2346.             }
  2347.         }
  2348. #endif
  2349.         /* recursively call main_function() with the extracted URLs */
  2350.         if (urlcounter > 0)
  2351.             main_function(my_argc, (char**)my_argv);
  2352.     }
  2353.  
  2354.     return result;
  2355. }
  2356.  
  2357.  
  2358. typedef enum 
  2359. {
  2360.     ShowUsage,
  2361.     NoArguments,
  2362.     BadOption,
  2363. } UsageMode;
  2364.  
  2365. /* Program usage information */
  2366.  
  2367. void Usage(char *progname, UsageMode mode)
  2368. {
  2369.     char Buffer[1024];
  2370.  
  2371.     char *bufptr;
  2372.     char *nameptr, *tmpptr;
  2373.  
  2374.     if (mode == NoArguments) {
  2375.         if (gui_startedfromdesktop()) {
  2376.             return;
  2377.         }
  2378.     }
  2379.  
  2380.     for (nameptr = progname, tmpptr = progname; (tmpptr = strchr(tmpptr, '\\')) != NULL ; nameptr = ++tmpptr);
  2381.     
  2382.     bufptr = Buffer;
  2383.     
  2384.     if (mode != BadOption)
  2385.     {
  2386.         if (gui_startedfromdesktop())
  2387.             bufptr+=sprintf(bufptr,"\nPROGRAM ARGUMENTS INFORMATION:\n\n");
  2388.         else
  2389.             bufptr+=sprintf(bufptr,"\nPROGRAM USAGE FROM CONSOLE:\n\n");
  2390.  
  2391.         bufptr+=sprintf(bufptr,"%s [-a <user:passwd>] [-p <port>] [-P <proxy:port>]\n", nameptr);
  2392.         bufptr+=sprintf(bufptr,"[-m <minutes>] [-e] <one or more stream references> [-b] [-r] [-d]\n");
  2393.         bufptr+=sprintf(bufptr,"\nDownload and store Windows Media Player .ASF streams!\n\n");
  2394.         bufptr+=sprintf(bufptr,"<stream references> must point to mms:// or http:// streams\n");
  2395.         bufptr+=sprintf(bufptr,"or to an .asx redirection URL/file referencing such a stream.\n");
  2396.         bufptr+=sprintf(bufptr,"Partial downloads will be resumed.\n");
  2397.         bufptr+=sprintf(bufptr,"\nOnly HTTP streaming is supported (no UDP or TCP).\n\n");
  2398.     }
  2399.     if (mode == BadOption)
  2400.     {
  2401.         bufptr+=sprintf(bufptr,"\nYOU SPECIFIED AN INVALID OPTION!:\n\n");
  2402.         bufptr+=sprintf(bufptr,"List of options: [-a <username:password>] [-p <portnum>] [-m <minutes>] [-e] [-b] [-r] [-d]\n\n");
  2403.     }
  2404.     bufptr+=sprintf(bufptr,"-a authorization with username and password\n");
  2405.     bufptr+=sprintf(bufptr,"-p specifies the port number to connect to\n");
  2406.     bufptr+=sprintf(bufptr,"-P specifies a proxy (e.g. http://proxy:8080)\n");
  2407.     bufptr+=sprintf(bufptr,"-r enables generation of a raw streaming file.\n");
  2408.     bufptr+=sprintf(bufptr,"-d dumps HTTP headers (for your information).\n");
  2409.     bufptr+=sprintf(bufptr,"-b enables batch mode (non-blocking GUI operation)\n");
  2410.     bufptr+=sprintf(bufptr,"-m specifies max. recording time in minutes.\n");
  2411.     bufptr+=sprintf(bufptr,"-e downloads only the first stream available\n");
  2412.     bufptr+=sprintf(bufptr,"   from the following list of references.\n\n");
  2413.     
  2414.     if (mode != BadOption)
  2415.     {
  2416.         if (!gui_startedfromdesktop())
  2417.             bufptr+=sprintf(bufptr,"Hit Ctrl-C to terminate the download.\n\n");
  2418.     }
  2419.     gui_showtext(Buffer);
  2420. }
  2421.  
  2422.  
  2423. /* A simple main() code that works for both console and GUI application */
  2424.  
  2425. int main(int argc, char **argv)
  2426. {
  2427. #ifdef WIN32
  2428.     WSADATA wsaData;
  2429. #endif
  2430.  
  2431.     if (gui_initialize())
  2432.     {
  2433.  
  2434. #ifdef WIN32
  2435.  
  2436. #define WS_MAJOR 2
  2437. #define WS_MINOR 0
  2438.  
  2439.         /* Open Winsock */
  2440.         if (WSAStartup(((WS_MINOR<<8)+WS_MAJOR),&wsaData) != 0)
  2441.         {
  2442.             gui_criticalerror("WSAStartup failed: %s\n",gui_translate_errorcode(errno));
  2443.             WSACleanup();
  2444.             gui_uninitialize();
  2445.             return -1;
  2446.         }
  2447.         else
  2448.         {
  2449.             /* Confirm that the WinSock DLL supports 2.2.*/
  2450.             /* Note that if the DLL supports versions greater    */
  2451.             /* than 2.2 in addition to 2.2, it will still return */
  2452.             /* 2.2 in wVersion since that is the version we      */
  2453.             /* requested.                                        */
  2454.             
  2455.             if ( (LOBYTE( wsaData.wVersion ) <  WS_MAJOR ) || 
  2456.                 ((LOBYTE( wsaData.wVersion ) == WS_MAJOR ) && (HIBYTE( wsaData.wVersion ) < WS_MINOR )) )
  2457.             {
  2458.                 /* Tell the user that we could not find a usable */
  2459.                 /* WinSock DLL.                                  */
  2460.                 gui_criticalerror("Your WinSock version is too old!\n"
  2461.                                   "Required is version %d.%d\n"
  2462.                                   "Your DLL is version %d.%d\n",
  2463.                                   WS_MAJOR, WS_MINOR,
  2464.                                   (int)LOBYTE( wsaData.wVersion ),
  2465.                                   (int)HIBYTE( wsaData.wVersion ));
  2466.                 WSACleanup( );
  2467.                 gui_uninitialize();
  2468.                 return -1;
  2469.             }
  2470.         }
  2471. #endif 
  2472.         main_function(argc, argv);
  2473.  
  2474.         gui_waitforuseraction();    /* gui_waitforuseraction may call main_function again */
  2475.  
  2476. #ifdef WIN32
  2477.         WSACleanup();
  2478. #endif
  2479.     }
  2480.  
  2481.     gui_uninitialize();
  2482.  
  2483.     return 0;
  2484. }
  2485.  
  2486.  
  2487. /* Parameter and URL parsing, HTTP request generation, opening output files */
  2488.  
  2489. int main_function(int argc, char **argv)
  2490. {
  2491.     /* these static flags/pointers are supposed to remain set */
  2492.     /* (even for multiple invocations of the main_function)   */
  2493.     static int createrawfile = 0;
  2494.     static int dumpheaders   = 0;
  2495.     static int batchmode     = 0;
  2496.  
  2497.     static char *username = NULL;
  2498.     static char *password = NULL;
  2499.     static char *proxy = NULL;
  2500.  
  2501.     unsigned char Buffer[MAX_CHUNK_SIZE];
  2502.     int i;
  2503.     unsigned int addr;
  2504.     struct hostent *hp = NULL;
  2505.  
  2506.     char *bufptr;
  2507.     char *urlptr;
  2508.     char *dashptr;
  2509.     char *colonptr;
  2510.  
  2511.     unsigned char *(URL[MAXURLS]);
  2512.     unsigned short portnum;
  2513.     unsigned short portnum_url[MAXURLS];
  2514.     unsigned short portnum_proxy;
  2515.     unsigned int maxtime;
  2516.     unsigned int maxtime_url[MAXURLS];
  2517.     int entry;
  2518.     int entry_url[MAXURLS];
  2519.     int urlc;
  2520.     int url;
  2521.  
  2522.     unsigned char file[512];
  2523.     unsigned char full_path[512];
  2524.     unsigned char server_name[512];
  2525.  
  2526.     char *tmpptr;
  2527.     char *fileptr;
  2528.     char *dotptr;
  2529.  
  2530.     unsigned char filename[512];
  2531.  
  2532.     unsigned char rawfilename[512];
  2533.     FILE *outfile, *rawfile;
  2534.     int oldsize;
  2535.  
  2536.     unsigned int offset_hi; /* the start position in the ASF file */
  2537.     unsigned int offset_lo; /* (64 bit large integer) */
  2538.  
  2539.     struct HeaderInfo hdrinfo;
  2540.  
  2541.     /* tell the GUI that this process is working */
  2542.     gui_not_idle(1);
  2543.  
  2544.     /* clear arrays & local structures */
  2545.     memset(&hdrinfo, 0, sizeof(hdrinfo));
  2546.     memset(URL, 0, sizeof(URL));
  2547.  
  2548.     /* Setup CTRL-C handler */
  2549.     signal(SIGINT, &ctrlc);
  2550.  
  2551.     /* set some defaults */
  2552.     urlc = 0;
  2553.     strcpy(file, "");
  2554.     strcpy(server_name, "");
  2555.     portnum = DEFAULT_PORT;
  2556.     abortflag = 0;
  2557.     maxtime = 0;
  2558.     entry = -1;
  2559.  
  2560.     /* Argument parsing */
  2561.     if (argc > 1)
  2562.     {
  2563.         int err = 0;
  2564.  
  2565.         for( i=1 ; (!err) && (i < argc) ; i++)
  2566.         {
  2567. #ifdef WIN32
  2568.             if ( (argv[i][0] == '-') || (argv[i][0] == '/') )
  2569. #else
  2570.             if ( (argv[i][0] == '-') )
  2571. #endif
  2572.             {
  2573.                 switch(argv[i][1])
  2574.                 {
  2575.                 case 'a':
  2576.                     if (argc > i+1)
  2577.                     {
  2578.                         colonptr = strchr(argv[++i],':');
  2579.                         if (colonptr == NULL) {
  2580.                             gui_seterror("Invalid syntax for username:password! Forgot colon?\n"); err=1;}
  2581.                         else
  2582.                         {
  2583.                             *colonptr = 0;
  2584.                             username = argv[i];
  2585.                             password = colonptr+1;
  2586.                         }
  2587.                     }
  2588.                     else {
  2589.                         gui_seterror("insufficient args\n"); err=1;}
  2590.                     break;
  2591.                 case 'p':
  2592.                     if (argc > i+1)
  2593.                         portnum = atoi(argv[++i]);
  2594.                     else {
  2595.                         gui_seterror("insufficient args\n"); err=1;}
  2596.                     break;
  2597.                 case 'P':
  2598.                     if (argc > i+1)
  2599.                         proxy = argv[++i];
  2600.                     else {
  2601.                         gui_seterror("insufficient args\n"); err=1;}
  2602.                     break;
  2603.                 case 'r':
  2604.                     createrawfile = 1;
  2605.                     break;
  2606.                 case 'd':
  2607.                     dumpheaders = 1;
  2608.                     break;
  2609.                 case 'm':
  2610.                     if (argc > i+1)
  2611.                         maxtime = atoi(argv[++i]);
  2612.                     else {
  2613.                         gui_seterror("insufficient args\n"); err=1;}
  2614.                     break;
  2615.                 case 'b':
  2616.                     batchmode = 1;
  2617.                     break;
  2618.                 case 'e':
  2619.                     entry++;
  2620.                     break;
  2621.                 case '?':
  2622.                     Usage(argv[0], ShowUsage);
  2623.                     gui_not_idle(0);
  2624.                     return 0;
  2625.                     break;
  2626.                 default:
  2627.                     Usage(argv[0], BadOption);
  2628.                     gui_not_idle(0);
  2629.                     return 0;
  2630.                     break;
  2631.                 }
  2632.             }
  2633.             else
  2634.             {
  2635.                 if (urlc < MAXURLS-1)
  2636.                 {
  2637.                     (URL[urlc]) = argv[i];
  2638.                     portnum_url[urlc] = portnum;
  2639.                     maxtime_url[urlc] = maxtime;
  2640.                     entry_url[urlc] = entry;
  2641.                     urlc++;
  2642.                 }
  2643.             }
  2644.         }
  2645.         if (err)
  2646.         {
  2647.             gui_not_idle(0);
  2648.             return 0;
  2649.         }
  2650.     }
  2651.  
  2652.  
  2653.     /* Print program usage */
  2654.     if (urlc == 0)
  2655.     {
  2656.         Usage(argv[0], NoArguments);
  2657.         gui_not_idle(0);
  2658.         return 0;
  2659.     }
  2660.  
  2661.     gui_getproxy(&proxy);
  2662.     if (proxy != NULL) if (!strcmp(proxy, "")) proxy = NULL;
  2663.  
  2664.     gui_setbatchmode(batchmode);
  2665.  
  2666.     /* Loop thorugh all specified <stream references> */
  2667.     for (url = 0; ( ((!abortflag) || batchmode) && (url < urlc)) ; url++)
  2668.     {
  2669.         /* try to interpret the stream reference as an on-disk redirection file first.    */
  2670.         /* subroutine will return TRUE if this was successful. We need to pass on maxtime */
  2671.         /* and portnum specifications because these can be different for any URL given    */
  2672.         if ( !parse_redirection( URL[url], NULL, 0, maxtime_url[url], portnum_url[url] ) )
  2673.         {
  2674.             unsigned char escaped_url[512];
  2675.  
  2676.             /* else try to parse string as an URL stream reference */
  2677.             /* Extract server name and path to stream */
  2678.             gui_setstatus("Parsing URL: '%s'\n", URL[url]);
  2679.  
  2680.             /* replace unescaped characters (e.g. spaces) by escape sequences */
  2681.             escape_url_string(escaped_url, URL[url]);
  2682.  
  2683.             urlptr = escaped_url;
  2684.             if (!strnicmp("http://", urlptr, 7)) urlptr+=7;
  2685.             if (!strnicmp("mms://",  urlptr, 6)) urlptr+=6;
  2686.             if (!strnicmp("mmsu://", urlptr, 7)) urlptr+=7;
  2687.             if (!strnicmp("mmst://", urlptr, 7)) urlptr+=7;
  2688.             dashptr = strchr(urlptr, '/');
  2689.             if (dashptr == NULL)
  2690.             {
  2691.                 dashptr = urlptr+strlen(urlptr);
  2692.                 gui_criticalerror("Invalid URL format!\n");
  2693.             }
  2694.             else
  2695.             {
  2696.                 strncpy(server_name, urlptr, dashptr-urlptr);
  2697.                 server_name[dashptr-urlptr] = 0;
  2698.                 strcpy(file, dashptr);
  2699.  
  2700.                 colonptr = strchr(server_name, ':');
  2701.                 if (colonptr != NULL)
  2702.                 {
  2703.                     portnum_url[url] = (unsigned short)atol(colonptr+1);
  2704.                     *colonptr = '\0';
  2705.                     if (portnum_url[url] == 0)
  2706.                     {
  2707.                         gui_criticalerror("Invalid URL format!\n");
  2708.                         strcpy(server_name, "");
  2709.                         strcpy(file, "");
  2710.                     }
  2711.                 }
  2712.             }
  2713.  
  2714.             /* parse proxy URL */
  2715.             if (proxy != NULL)
  2716.             {
  2717.                 if (portnum_url[url] == DEFAULT_PORT)
  2718.                     sprintf(full_path, "http://%s%s", server_name, file);
  2719.                 else
  2720.                     sprintf(full_path, "http://%s:%d%s", server_name, portnum_url[url], file);
  2721.                 urlptr = proxy;
  2722.                 if (!strnicmp("http://", urlptr, 7)) urlptr+=7;
  2723.  
  2724.                 dashptr = strchr(urlptr, '/');
  2725.                 if (dashptr != NULL)
  2726.                 {
  2727.                     gui_criticalerror("Invalid proxy URL format!\n");
  2728.                 }
  2729.                 else
  2730.                 {
  2731.                     colonptr = strchr(urlptr, ':');
  2732.                     if (colonptr != NULL)
  2733.                     {
  2734.                         portnum_proxy = (unsigned short)atol(colonptr+1);
  2735.                         if (portnum_proxy == 0)
  2736.                         {
  2737.                             gui_criticalerror("Invalid proxy URL format!\n");
  2738.                             strcpy(server_name, "");
  2739.                             strcpy(file, "");
  2740.                         }
  2741.                         else
  2742.                         {
  2743.                             portnum_url[url] = portnum_proxy;
  2744.                         }
  2745.                     }
  2746.                     else
  2747.                         colonptr = urlptr+strlen(urlptr);
  2748.  
  2749.                     strncpy(server_name, urlptr, colonptr-urlptr);
  2750.                     server_name[colonptr-urlptr] = 0;
  2751.                 }
  2752.             }
  2753.  
  2754.             if (strcmp(server_name,""))
  2755.             {
  2756.                 /* Attempt to detect if we should call gethostbyname() or gethostbyaddr() */
  2757.                 gui_setstatus("Resolving host: '%s'\n", server_name);
  2758.  
  2759.                 addr = 0;
  2760.  
  2761.                 if (isalpha(server_name[0]))
  2762.                 {
  2763.                     /* Server address is a name */
  2764.                     hp = gethostbyname(server_name);
  2765.                     if (hp == NULL)
  2766.                     {
  2767.                         gui_seterror("Unable to resolve host '%s'!\n", server_name);
  2768.                     }
  2769.                 }
  2770.                 else
  2771.                 {
  2772.                     /* Convert nnn.nnn address to a usable one */
  2773.                     addr = inet_addr(server_name);
  2774.                     if ((hp = gethostbyaddr((char *)&addr,4,AF_INET))!=NULL)
  2775.                     {
  2776.                         strcpy(server_name, hp->h_name);
  2777.                     }
  2778.                 }
  2779.  
  2780.                 if ( ((!abortflag) || batchmode) && (hp != NULL || addr != 0) )
  2781.                 {
  2782.                     unsigned char randomized_guid[80];
  2783.                     char passwordcombo[512];
  2784.                     char base64buf[1000];
  2785.  
  2786.                     randomize_guid(randomized_guid);
  2787.  
  2788.                     strcpy(filename, "");
  2789.  
  2790.                     /* loop until we got a valid password */
  2791.                     while (!abortflag)
  2792.                     {
  2793.                         gui_start_transmission(URL[url], filename, sizeof(filename), 0, 0);
  2794.                         
  2795.                         /* cook up first HTTP request to send */
  2796.                         
  2797.                         /* This is the initial HTTP request of media player.
  2798.                         It is used to query for the media type header of
  2799.                         the stream (needed for checking if the codecs are
  2800.                         installed at the client and for obtaining the type
  2801.                         of stream (live stream, pre-recorded content etc..)
  2802.                         
  2803.                         Note that the request-context changes with every
  2804.                         new HTTP request. */
  2805.                         
  2806.                         bufptr = Buffer;
  2807.                         bufptr+=sprintf(bufptr,"GET %s HTTP/1.0\r\n", proxy == NULL ? file:full_path);
  2808.                         if (username != NULL || password != NULL)
  2809.                         {
  2810.                             sprintf(passwordcombo, "%s:%s", username!=NULL?username:"", password!=NULL?password:"");
  2811.                             base64enc(passwordcombo,base64buf);
  2812.                             bufptr += sprintf(bufptr,"Authorization: Basic %s\r\n",base64buf);
  2813.                         }
  2814.                         bufptr+=sprintf(bufptr,"Accept: */*\r\n");
  2815.                         bufptr+=sprintf(bufptr,"User-Agent: NSPlayer/4.1.0.3856\r\n");
  2816.                         if (proxy == NULL) bufptr+=sprintf(bufptr,"Host: %s\r\n", server_name);
  2817.                         bufptr+=sprintf(bufptr,"Pragma: no-cache,rate=1.000000,stream-time=0,stream-offset=0:0,request-context=1,max-duration=0\r\n");
  2818.                         bufptr+=sprintf(bufptr,"Pragma: xClientGUID=%s\r\n", randomized_guid);
  2819.                         bufptr+=sprintf(bufptr,"Connection: Close\r\n\r\n");
  2820.                         
  2821.                         /* Call the subroutine that does all the work */
  2822.                         
  2823.                         abortflag = 0;  /* reset global abort flag */
  2824.                         
  2825.                         collectdata(1, dumpheaders,
  2826.                             NULL,                       /* file for raw output or NULL */
  2827.                             NULL,                       /* output file (NULL, header only!) */
  2828.                             0,                          /* previous size of output file */
  2829.                             Buffer,                     /* adress of data buffer */
  2830.                             sizeof(Buffer),             /* size of data buffer */
  2831.                             ((int)bufptr-(int)Buffer),  /* size of HTTP request */
  2832.                             hp,                         /* hostent structure or NULL */
  2833.                             server_name,                /* server name string */
  2834.                             addr,                       /* IP address in network byte order */
  2835.                             portnum_url[url],           /* port number (80 in most cases) */
  2836.                             SOCK_STREAM,                /* type of socket (SOCK_STREAM) */
  2837.                             file,                       /* path to ASF stream on server */
  2838.                             &hdrinfo,                   /* adress of hdr info structure */
  2839.                             maxtime_url[url]);          /* maximum time for recording */
  2840.                         
  2841.                         gui_finished_transmission();
  2842.  
  2843.                         if (hdrinfo.contenttype != password_required)
  2844.                         {
  2845.                             /* leave loop if anything else than "401 - unauthorized" returned */
  2846.                             break;
  2847.                         }
  2848.                         else
  2849.                         {
  2850.                             if (batchmode && (username != NULL || password != NULL))
  2851.                             {
  2852.                                 gui_seterror("Authorization failed!\n");
  2853.                                 break;
  2854.                             }
  2855.                             if (!gui_getpassword(&username, &password))
  2856.                             {
  2857.                                 gui_seterror("Authorization cancelled!\n");
  2858.                                 break;
  2859.                             }
  2860.                         }
  2861.                     }
  2862.  
  2863.                     if (!abortflag)
  2864.                     {
  2865.  
  2866.                         if ( hdrinfo.contenttype == redirector_content )
  2867.                         {
  2868.                             /* redirection file now resides in Buffer, so call the parser */
  2869.                             /* the parser itself will call the main_function recursively */
  2870.                             parse_redirection(NULL, Buffer, hdrinfo.redirsize, maxtime_url[url], portnum_url[url] );
  2871.                         }
  2872.  
  2873.                         if ((hdrinfo.contenttype == prerecorded_content || hdrinfo.contenttype == live_content) )
  2874.                         {
  2875.                             /* Cut away leading sub directories from the URL and extract the file name */
  2876.                             for (fileptr = file, tmpptr = file; (tmpptr = strchr(tmpptr, '/')) != NULL ; fileptr = ++tmpptr);
  2877.  
  2878.                             /* replace escape characters in filename */
  2879.                             unescape_url_string(filename, fileptr);
  2880.  
  2881.                             /* remove any disallowed characters (OS-specific!) */
  2882.                             generate_valid_filename(filename);
  2883.  
  2884.                             /* the GUI is allowed to modify the filename (target file selection) */
  2885.                             /* GUI can also cancel the download at this point */
  2886.                             if (!gui_start_transmission(URL[url], filename, sizeof(filename), hdrinfo.time, (maxtime*60*1000)))
  2887.                             {
  2888.                                 gui_seterror("transfer cancelled.\n");
  2889.                             }
  2890.                             else
  2891.                             {
  2892.                                 /* Open output ASF file */
  2893.                                 gui_logtext("Opening file '%s' for output\n", filename);
  2894.  
  2895.                                 /* open either an existing or a new output file */
  2896.  
  2897.                                 /* try to open existing file for binary reading and writing */
  2898.                                 /* (but not for live content) */
  2899.                                 outfile = NULL;
  2900.                                 if (hdrinfo.contenttype != live_content)
  2901.                                     outfile = fopen(filename, "rb+");
  2902.  
  2903.                                 /* second option: create a new file for binary writing */
  2904.                                 if (outfile == NULL)
  2905.                                     outfile = fopen(filename, "wb");
  2906.  
  2907.                                 if (outfile == NULL)
  2908.                                 {
  2909.                                     gui_seterror("Failed to open '%s'.\n", filename);
  2910.                                 }
  2911.                                 else
  2912.                                 {
  2913.                                     /* If desired, open raw file. It will contain */
  2914.                                     /* the raw streaming data for further analysis */
  2915.  
  2916.                                     /* This file cannot be played in Media Player */
  2917.  
  2918.                                     /* Note the raw file will contain the HTTP reply */
  2919.                                     /* and additional chunk headers */
  2920.  
  2921.                                     if (createrawfile)
  2922.                                     {
  2923.                                         for (dotptr = fileptr+strlen(fileptr), tmpptr = fileptr; (tmpptr = strchr(tmpptr, '.')) != NULL ; dotptr = tmpptr++);
  2924.                                         strncpy(rawfilename, fileptr, (dotptr-fileptr));
  2925.                                         rawfilename[(dotptr-fileptr)] = 0;
  2926.                                         strcat(rawfilename, "_raw");
  2927.                                         strcat(rawfilename, dotptr);
  2928.  
  2929.                                         gui_logtext("Opening raw file '%s' for output\n", rawfilename);
  2930.                                         rawfile = fopen(rawfilename, "ab");
  2931.                                         if (rawfile == NULL)
  2932.                                         {
  2933.                                             gui_seterror("Failed to open %s'.\n", rawfilename);
  2934.                                         }
  2935.                                         else
  2936.                                             fseek(rawfile, 0, SEEK_END);
  2937.                                     } else rawfile = NULL;
  2938.  
  2939.                                     /* Appending is not possible for live content! */
  2940.                                     oldsize = 0;
  2941.                                     if (hdrinfo.contenttype != live_content)
  2942.                                     {
  2943.                                         /* Determine file size of existing .ASF file */
  2944.                                         fseek(outfile, 0, SEEK_END);
  2945.                                         oldsize = ftell(outfile);
  2946.                                     }
  2947.  
  2948.                                     /* Check if this download requires a resume */
  2949.                                     if (oldsize > 0)
  2950.                                     {
  2951.                                         /* Enable resuming the download */
  2952.                                         /* ANSI-C does not support file operations >4GB */
  2953.                                         /* but who wants to stream 4 GIG files anyway? */
  2954.                                         gui_logtext("Appending to existing file!\n");
  2955.                                         offset_hi = 0;
  2956.                                         offset_lo = oldsize;
  2957.                                     }
  2958.                                     else
  2959.                                     {
  2960.                                         /* Otherwise tell the server to send from the beginning */
  2961.                                         offset_hi = 0xffffffff;
  2962.                                         offset_lo = 0xffffffff;
  2963.                                     }
  2964.  
  2965.                                     /* cook up second HTTP request to send */
  2966.  
  2967.                                     if (hdrinfo.contenttype == prerecorded_content)
  2968.                                     {
  2969.                                     /* This is the HTTP request that enables downloading
  2970.                                     prerecorded (=seekable) content.
  2971.  
  2972.                                       The stream-offset parameter defines the start offset
  2973.                                       in the ASF file on the server,
  2974.  
  2975.                                         The stream-time is the timecode (milliseconds) for
  2976.                                         seeking within the stream. This parameter is not used
  2977.                                         by this program. */
  2978.  
  2979.                                         bufptr = Buffer;
  2980.                                         bufptr+=sprintf(bufptr,"GET %s HTTP/1.0\r\n", proxy == NULL ? file:full_path);
  2981.                                         if (username != NULL || password != NULL)
  2982.                                             bufptr += sprintf(bufptr,"Authorization: Basic %s\r\n",base64buf);
  2983.                                         bufptr+=sprintf(bufptr,"Accept: */*\r\n");
  2984.                                         bufptr+=sprintf(bufptr,"User-Agent: NSPlayer/4.1.0.3856\r\n");
  2985.                                         if (proxy == NULL) bufptr+=sprintf(bufptr,"Host: %s\r\n", server_name);
  2986.                                         bufptr+=sprintf(bufptr,"Pragma: no-cache,rate=1.000000,stream-time=0,stream-offset=%u:%u,request-context=2,max-duration=%u\r\n",offset_hi, offset_lo, hdrinfo.time);
  2987.                                         bufptr+=sprintf(bufptr,"Pragma: xPlayStrm=1\r\n");
  2988.                                         bufptr+=sprintf(bufptr,"Pragma: xClientGUID=%s\r\n", randomized_guid);
  2989.                                         bufptr+=sprintf(bufptr,"Pragma: stream-switch-count=1\r\n");
  2990.                                         bufptr+=sprintf(bufptr,"Pragma: stream-switch-entry=ffff:1:0\r\n");
  2991.                                         bufptr+=sprintf(bufptr,"Connection: Close\r\n\r\n");
  2992.                                     }
  2993.  
  2994.                                     if (hdrinfo.contenttype == live_content)
  2995.                                     {
  2996.                                     /* This is the HTTP request that enables downloading
  2997.                                     live (=broadcast) content.
  2998.  
  2999.                                         The offset-stuff is not used here. */
  3000.  
  3001.                                         bufptr = Buffer;
  3002.                                         bufptr+=sprintf(bufptr,"GET %s HTTP/1.0\r\n", proxy == NULL ? file:full_path);
  3003.                                         if (username != NULL || password != NULL)
  3004.                                             bufptr += sprintf(bufptr,"Authorization: Basic %s\r\n",base64buf);
  3005.                                         bufptr+=sprintf(bufptr,"Accept: */*\r\n");
  3006.                                         bufptr+=sprintf(bufptr,"User-Agent: NSPlayer/4.1.0.3856\r\n");
  3007.                                         if (proxy == NULL) bufptr+=sprintf(bufptr,"Host: %s\r\n", server_name);
  3008.                                         bufptr+=sprintf(bufptr,"Pragma: no-cache,rate=1.000000,request-context=2\r\n",offset_hi, offset_lo);
  3009.                                         bufptr+=sprintf(bufptr,"Pragma: xPlayStrm=1\r\n");
  3010.                                         bufptr+=sprintf(bufptr,"Pragma: xClientGUID=%s\r\n", randomized_guid);
  3011.                                         bufptr+=sprintf(bufptr,"Pragma: stream-switch-count=1\r\n");
  3012.                                         bufptr+=sprintf(bufptr,"Pragma: stream-switch-entry=ffff:1:0\r\n");
  3013.                                         bufptr+=sprintf(bufptr,"Connection: Close\r\n\r\n");
  3014.                                     }
  3015.  
  3016.                                     /* Call the subroutine that does all the work */
  3017.  
  3018.                                     abortflag = 0;  /* reset global abort flag */
  3019.  
  3020.                                     collectdata(0, dumpheaders,
  3021.                                         rawfile,                    /* file for raw output or NULL */
  3022.                                         outfile,                    /* output file */
  3023.                                         oldsize,                    /* previous size of output file */
  3024.                                         Buffer,                     /* adress of data buffer */
  3025.                                         sizeof(Buffer),             /* size of data buffer */
  3026.                                         ((int)bufptr-(int)Buffer),  /* size of HTTP request */
  3027.                                         hp,                         /* hostent structure or NULL */
  3028.                                         server_name,                /* server name string */
  3029.                                         addr,                       /* IP address in network byte order */
  3030.                                         portnum_url[url],           /* port number (80 in most cases) */
  3031.                                         SOCK_STREAM,                /* type of socket (SOCK_STREAM) */
  3032.                                         file,                       /* path to ASF stream on server */
  3033.                                         &hdrinfo,                   /* adress of hdr info structure */
  3034.                                         maxtime_url[url]);          /* maximum time for recording */
  3035.  
  3036.                                     /* TODO: */
  3037.  
  3038.                                     /* Tranmission is not yet checked for success!          */
  3039.                                     /* For prerecorded content, a transfer interrupted by   */
  3040.                                     /* technical problems should be automatically retried   */
  3041.                                     /* (at least when in batch mode)                        */
  3042.                                     /* Resuming of live content could be implemented rather */
  3043.                                     /* easily now                                           */
  3044.  
  3045.                                     /* now handle the -e option */
  3046.                                     /* useful when several alternative URL references point */
  3047.                                     /* to the same content. This method is often used by    */
  3048.                                     /* content providers to allow server reduncandy.        */
  3049.                                     if (entry_url[url] != -1)
  3050.                                     {
  3051.                                         int entry = entry_url[url];
  3052.                                         for (;;)
  3053.                                         {
  3054.                                             /* skip following URLs with same entry number */
  3055.                                             if ((url+1) < urlc)
  3056.                                             {
  3057.                                                 if (entry_url[url+1] == entry) url++;
  3058.                                                 else break;
  3059.                                             }
  3060.                                             else break;
  3061.                                             /* the outer for (;;) loop will add 1 more to url */
  3062.                                         }
  3063.                                     }
  3064.  
  3065.                                     /* Close files, cleanup and exit */
  3066.                                     if (rawfile != NULL)
  3067.                                     {
  3068.                                         fclose(rawfile); rawfile = NULL;
  3069.                                     }
  3070.                                     fclose(outfile);
  3071.                                 }
  3072.                                 gui_finished_transmission();
  3073.                             }
  3074.                         }
  3075.                     }
  3076.                 }
  3077.             }
  3078.         }
  3079.     }
  3080.     /* tell the GUI that this process is no longer working */
  3081.     gui_not_idle(0);
  3082.     return 0;
  3083. }
  3084.