/* Get and Resume Elite EDition source code Ver .52 (Jahoo!) First off, sorry about the mess and lack of error checking.. I'll clean it up later ;) Get and Resume Elite EDition (GREED) Copyright (C) 1999 Anoakie Turner This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. For more information on the GPL, please go to: http://www.gnu.org/copyleft/gpl.html Contact: Anoakie Turner Anoakie.Turner@asu.edu 13240 N. 94th Pl. Scottsdale, AZ 85260 */ /* ** To compile, use gcc -O2 -Wall -o greed greed.c ** ** ** To compile under any non linux OS, use: ** gcc -O2 -Wall -lsocket -lxnet -o greed greed.c */ #include #include #include #include #include #include #include #include #include #define HTTP 1 #define FTP 2 #define ROLLBACK 4096 #define TIMEOUT_SEC 300 #define CURRENT_VERSION "GREED/0.52" #define GGR "http://www.public.asu.edu/~arturner/greed.tar.gz" #define BASE64 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" typedef int bool; bool OUTPUT_LEVEL; bool STDOUT; int dlstat; long dldots; struct sockaddr_in address; struct timeval timeout = { TIMEOUT_SEC, 0 }; unsigned int dport; time_t start, end; fd_set testfds; typedef struct URLstruct* URLp; struct URLstruct { char server[512]; char file[512]; char name[1024]; char filename[256]; char user[64]; char pass[64]; char left[10]; char size[10]; char total[12]; char cport[5]; char *b64pass; unsigned int port; int protocol; int sockfd; struct hostent *host; struct servent *serv; long long lleft; long long lsize; long long ltotal; FILE *fp; bool done, resume, retry; } URLst; void Download(URLp URL); bool ReadHTTPHeader(URLp URL); bool ReadFTPListing(URLp URL); bool Parse(URLp URL); bool Connect(URLp URL); void ReadString(char *str1, int *ind, char stopchar, int stopint, char* str2); void Base64Encode(char *str1, char *str2); void ReadString(char *str1, int *ind, char stopchar, int stopint, char* str2) /************************************* ** void ReadString(char *str1, int &ind, char stopchar, ** int stopint, char* str2); ** ** Pre: Assigned (str1) && Assigned (ind) && Assigned (stopchar) ** && Assigned (stopint) ** Post: a string starting at ind and ending at (stopchar || ** stopint) is copied int str2 *************************************/ { int stringIndex = 0; while (*ind < stopint && str1[*ind] != stopchar) { str2 [stringIndex++] = str1[*ind]; (*ind)++; } str2 [stringIndex] = '\0'; } bool Parse(URLp URL) /************************************* ** bool Parse(URLp URL); ** ** Parses a URL ** ** Pre: Assigned (URL->name) ** Post: URL->name parsed, URL->(filename, file, protocol, user, pass, cport, server, port) ** set. Returns 1 if successful. *************************************/ { int at = 0; int i = 0; dlstat = 0; dldots = 0; URL->retry = 1; for(i = strlen(URL->name); i > 0 && URL->name[i] != '@'; i--); if(URL->name[i] == '@') at = i; if (strstr(URL->name, "http://") != NULL || strstr(URL->name, "ftp://") != NULL) { if(URL->name[0] == 'f') { i = 6; URL->protocol = FTP; } else { i = 7; URL->protocol = HTTP; } if (at) { if (URL->protocol == FTP) { ReadString(URL->name, &i, ':', at, URL->user); i++,ReadString(URL->name, &i, '\0', at, URL->pass); i++; } else { ReadString(URL->name, &i, '\0', at, URL->pass); i++,URL->pass[i] = '\0'; Base64Encode(URL->pass, URL->user); } } else { strcpy(URL->user, "anonymous"); strcpy(URL->pass, "-anonuser@anonymous.com"); } } else { i = 0; if(URL->name[0] == 'f') URL->protocol = FTP; else URL->protocol = HTTP; } at = i; ReadString(URL->name, &i, ':', strlen(URL->name), URL->server); if (URL->name[i] != ':') { i = at; ReadString(URL->name, &i, '/', strlen(URL->name), URL->server); if (URL->protocol == FTP) strcpy (URL->cport, "21"); else strcpy (URL->cport, "80"); } else i++,ReadString(URL->name, &i, '/', strlen(URL->name), URL->cport); ReadString(URL->name, &i, '\0', strlen(URL->name), URL->file); for (i = strlen(URL->name); i > 0 && URL->name[i] != '/'; i--); i++,ReadString(URL->name, &i, '\0', strlen(URL->name), URL->filename); if (URL->file[0] == '\0') { strcpy (URL->file, "/"); strcpy (URL->filename, "index.html"); } else if (URL->file[strlen(URL->file) - 1] == '/') strcpy (URL->filename, "index.html"); URL->port = atoi(URL->cport); return (1); } bool Connect(URLp URL) /************************************* ** *************************************/ { URL->host = gethostbyname(URL->server); if (URL->host == NULL) { if (OUTPUT_LEVEL > 0) printf("Error resolving host!\n"); URL->retry = 0; return(0); } if (OUTPUT_LEVEL > 1) printf("Connecting to: %s:%d\n", URL->server, URL->port); URL->sockfd = socket(AF_INET, SOCK_STREAM, 0); if (URL->sockfd == -1) { if (OUTPUT_LEVEL > 0) printf("Error opening socket!\n"); URL->retry = 0; return(0); } address.sin_family = AF_INET; address.sin_port = htons(URL->port); memcpy(&address.sin_addr, URL->host->h_addr, URL->host->h_length); if(connect(URL->sockfd, (struct sockaddr *)&address, sizeof(address)) < 0) { if (OUTPUT_LEVEL > 0) printf("Error connecting to server!\n"); URL->retry = 0; return(0); } else if (URL->protocol == HTTP) { write(URL->sockfd, "GET ", 4); write(URL->sockfd, URL->file, strlen(URL->file)); write(URL->sockfd, " HTTP/1.1\r\nHost: ", 17); write(URL->sockfd, URL->server, strlen(URL->server)); write(URL->sockfd, ":", 1); write(URL->sockfd, URL->cport, strlen(URL->cport)); write(URL->sockfd, "\r\nUser-Agent: ", 14); write(URL->sockfd, CURRENT_VERSION, strlen(CURRENT_VERSION)); write(URL->sockfd, "\r\nAccept: *.*, */*\r\n", 20); if (strstr(URL->name, "anonymous") == NULL) { write(URL->sockfd, "Authorization: Basic ", 21); write(URL->sockfd, URL->user, strlen(URL->user)); } write(URL->sockfd, "\r\nRange: bytes=", 15); } else { write(URL->sockfd, "USER ", 5); write(URL->sockfd, URL->user, strlen(URL->user)); write(URL->sockfd, "\r\nPASS ", 7); write(URL->sockfd, URL->pass, strlen(URL->pass)); write(URL->sockfd, "\r\nPASV\r\nREST 0\r\nNOOP\r\n", 22); write(URL->sockfd, "LIST -L ", 8); if (strstr(URL->user, "anonymous") == NULL) write(URL->sockfd, "~", 1); write(URL->sockfd, URL->file, strlen(URL->file)); write(URL->sockfd, "\r\n", 2); URL->resume = 1; } return 1; } bool ReadHTTPHeader(URLp URL) /************************************* ** bool ReadHTTPHeader(URLp URL) ** ** URL->protcol == HTTP, HTTP header read. ** ** Pre: Assigned(URL->size) && Assigned(URL->filename) && Assigned(URL->sockfd) ** Post: URL->fp, URL->lsize, URL->left, and URL->lleft set. Returns 1 ** if successful *************************************/ { int bsize = 0; char *temp, *fin; char buffer[4096]; int i; URL->done = 0; URL->fp = fopen (URL->filename, "a+b"); URL->lsize = ftell(URL->fp); if ((URL->lsize >= URL->ltotal) && URL->ltotal != 0) { if (OUTPUT_LEVEL > 0) printf("Error... File already downloaded!\n"); URL->retry = 0; return(0); } else if (URL->fp == NULL) { if (OUTPUT_LEVEL > 0) printf("Error opening file %s!\n", URL->filename); URL->retry = 0; return(0); } else if (URL->lsize == -1) URL->lsize = 0; sprintf(URL->size, "%lld", URL->lsize); write(URL->sockfd, URL->size, strlen(URL->size)); write(URL->sockfd, "-\r\n\r\n", 5); dldots = URL->lsize / ROLLBACK; bsize = read(URL->sockfd, buffer, sizeof(buffer) - 1); if (!(fin=strstr(buffer,"\r\n\r\n"))) { if (OUTPUT_LEVEL > 0) printf("Error - Webserver header too large!\n"); URL->retry = 0; return(0); } *fin='\0'; fin+=4; if (strstr(buffer,"Transfer-Encoding: chunked")) { if (OUTPUT_LEVEL > 0) printf("Error - \"chunked\" Transfer-Encoding not HTTP/1.1 compliant!\n"); URL->retry = 0; return(0); } if (URL->lsize && !strstr(buffer,"Content-Range:")) { if (OUTPUT_LEVEL > 0) printf("Error - Server says file already retrieved!\n"); URL->retry = 0; return(0); } if (OUTPUT_LEVEL > 1) printf("Resuming from %s bytes.\r\n", URL->size); temp = strstr (buffer, "ength: "); if (temp != NULL) { for (i = 0; temp[i + 7] != '\r'; i++) URL->left[i] = temp[i + 7]; URL->left [i] = '\0'; URL->lleft = atol(URL->left); if (URL->lsize == URL->lleft) { if (OUTPUT_LEVEL > 0) printf("Error, file already downloaded!\n"); URL->retry = 0; return 0; } if (OUTPUT_LEVEL > 1) printf("%s bytes left to download!\r\n", URL->left); URL->resume = 1; URL->lleft -= (bsize-(fin-buffer)); } else { if (OUTPUT_LEVEL > 0) printf("Error in web site file size estimation, or unknown server type!\n\n"); URL->resume = 0; URL->lleft = 2000000000; } fwrite(fin,bsize-(fin-buffer),1,URL->fp); if (OUTPUT_LEVEL > 1) printf("[."); fflush(NULL); return 1; } bool ReadFTPListing (URLp URL) /************************************* ** *************************************/ { int result = 1; int len = 1; int i = 0; int j = 0; int k = 0; int listfd = 0; int datafd = 0; int whitespace = 0; char buffer[64000], buffer2[64000], buffer3[64000], buffer4[64000]; char lhost[5][4]; char *rbuffer = NULL; struct sockaddr_in datastream, liststream; URL->resume = 1; while(result > 0) { result = read(URL->sockfd, buffer + len - 1, sizeof(buffer) + len - 1); len += result; if (strstr(buffer, "200") != NULL) result = 0; } rbuffer = strstr(buffer, "Passive Mode ("); if (rbuffer == NULL) { if (OUTPUT_LEVEL > 0) printf("PASV mode not supported, aborting.\n"); write(URL->sockfd, "\r\nQUIT\r\n", 8); URL->retry = 0; return(0); } else for (i = 14; (unsigned)i < strlen(rbuffer) && rbuffer[i] != ')'; i++) if (rbuffer[i] == ',') { lhost[j][k] = '\0'; j++; k = 0; } else lhost[j][k++] = rbuffer[i]; listfd = socket(AF_INET, SOCK_STREAM, 0); if (listfd == -1) { if (OUTPUT_LEVEL > 0) printf("Error opening socket to FTP server for LIST!\n"); URL->retry = 0; return(0); } liststream.sin_family = AF_INET; liststream.sin_port = htons(atoi(lhost[4])*256 + atoi(lhost[5])); memcpy(&liststream.sin_addr, URL->host->h_addr, URL->host->h_length); if (connect(listfd, (struct sockaddr *)&liststream, sizeof(liststream)) == -1) { if (OUTPUT_LEVEL > 0) printf("Error connecting to FTP socket for LIST!\n"); write(URL->sockfd, "\r\nQUIT\r\n", 8); close(listfd); URL->retry = 1; return(0); } result = 1; len = 1; while(result > 0) { result = read(listfd, buffer2 + len - 1, sizeof(buffer2) + len - 1); len += result; } close(listfd); for (j = 0; (unsigned)j < strlen(buffer2) && whitespace < 4; j++) { if (buffer2[j] == ' ' && buffer2[j + 1] != ' ') whitespace ++; } for (k = 0; (unsigned)j < strlen(buffer2); j++) if (buffer2[j] == ' ' || (buffer2[j] > ('0' - 1) && buffer2[j] < ('9' + 1))) URL->total[k++] = buffer2[j]; else j = strlen(buffer2); URL->total[j - 4] = '\0'; URL->ltotal = atol(URL->total); if (OUTPUT_LEVEL > 1) printf("File size: %lld bytes\n", URL->ltotal); write(URL->sockfd, "PASV\r\nREST 0\r\nNOOP\r\n", 20); result = 1; len = 1; while(result > 0) { result = read(URL->sockfd, buffer3 + len - 1, sizeof(buffer3) + len - 1); len += result; if (strstr(buffer3, "200") != NULL) result = 0; } if (strstr(buffer3, "350") == NULL) URL->retry = 0; rbuffer = strstr(buffer3, "Passive Mode ("); if (rbuffer == NULL) return(0); else for (i = 14, j = 0, k = 0; (unsigned)i < strlen(rbuffer) && rbuffer[i] != ')'; i++) if (rbuffer[i] == ',') { lhost[j][k] = '\0'; j++; k = 0; } else lhost[j][k++] = rbuffer[i]; URL->fp = fopen (URL->filename, "a+b"); URL->lsize = ftell(URL->fp); if (URL->ltotal == 0) URL->ltotal = 2000000000; if (URL->lsize >= URL->ltotal) { if (OUTPUT_LEVEL > 0) printf("Error... It seems this file has already been downloaded.\n"); write(URL->sockfd, "\r\nQUIT\r\n", 8); close(datafd); URL->retry = 0; return(0); } else if (URL->fp == NULL) { if (OUTPUT_LEVEL > 0) printf("Error opening file %s\n", URL->filename); write(URL->sockfd, "\r\nQUIT\r\n", 8); close(datafd); URL->retry = 0; return(0); } else if (URL->lsize < ROLLBACK || !URL->retry) URL->lsize = 0; else URL->lsize -= ROLLBACK; ftruncate(fileno(URL->fp), URL->lsize); sprintf(URL->size, "%lld", URL->lsize); if (OUTPUT_LEVEL > 1) printf("Resuming from %s bytes.\n\n", URL->size); write(URL->sockfd, "TYPE I\r\nREST ", 13); write(URL->sockfd, URL->size, strlen(URL->size)); write(URL->sockfd, "\r\nRETR ", 7); write(URL->sockfd, URL->file, strlen(URL->file)); write(URL->sockfd, "\r\n", 2); dldots = (long long)URL->lsize / ROLLBACK; datafd = socket(AF_INET, SOCK_STREAM, 0); if (datafd == -1) { if (OUTPUT_LEVEL > 0) printf("Error opening socket to FTP server for RETR!\n"); write(URL->sockfd, "\r\nABOR\r\nQUIT\r\n", 14); close(datafd); URL->retry = 1; return(0); } datastream.sin_family = AF_INET; datastream.sin_port = htons(atoi(lhost[4])*256 + atoi(lhost[5])); memcpy(&datastream.sin_addr, URL->host->h_addr, URL->host->h_length); if (connect(datafd, (struct sockaddr *)&datastream, sizeof(datastream)) == -1) { if (OUTPUT_LEVEL > 1) printf("Error connecting to FTP socket for RETR!\n"); write(URL->sockfd, "\r\nABOR\r\nQUIT\r\n", 14); close(datafd); URL->retry = 1; return(0); } result = 1; len = 1; while(result > 0) { result = read(URL->sockfd, buffer4 + len - 1, sizeof(buffer4) + len - 1); len += result; if (strstr(buffer4, "350") != NULL) result = 0; } write(URL->sockfd, "\r\nQUIT\r\n", 8); listfd = URL->sockfd; URL->sockfd = datafd; close (listfd); URL->lleft = URL->ltotal - URL->lsize; if (OUTPUT_LEVEL > 1) printf("[."); return (1); } void Base64Encode(char *str1, char *dest) /************************************* ** *************************************/ { int len = strlen(str1); int i = 0; int j = 0; char str2[(len/3)*4 + 3]; while (j < len - 2) { str2[i++] = BASE64[str1[j]>>2]; str2[i++] = BASE64[((str1[j]&3)<<4)|(str1[j + 1]>>4)]; str2[i++] = BASE64[((str1[j + 1]&15)<<2)|(str1[j + 2]>>6)]; str2[i++] = BASE64[str1[j+2]&63]; j+=3; } switch(len - j) { case 1: str2[i++] = BASE64[str1[j]>>2]; str2[i++] = BASE64[((str1[j]&3)<<4)]; str2[i++] = '='; str2[i++] = '='; break; case 2: str2[i++] = BASE64[str1[j]>>2]; str2[i++] = BASE64[((str1[j]&3)<<4)|(str1[j + 1]>>4)]; str2[i++] = BASE64[((str1[j + 1]&15)<<2)]; str2[i++] = '='; break; default: break; } str2[i] = '\0'; strcpy(dest, str2); } void Download(URLp URL) /************************************* ** void Download(URLp URL) ** ** Downloads up to 4k of data from URL->sockfd and writes it to URL->fp ** ** Pre: Assigned(URL->sockfd) && Assigned(URL->lleft) && Assigned(URL->fp) && URL->done != 1 ** Post: set URL->done = 1, if finished. *************************************/ { int bsize = 1; char buff[ROLLBACK]; switch(select(FD_SETSIZE, &testfds, (fd_set *)0, (fd_set *)0, &timeout)) { case 0: if (OUTPUT_LEVEL > 1) printf("]\ntimeout!\n"); URL->done = 1; break; case -1: exit(0); break; default: bsize = read(URL->sockfd, buff, sizeof(buff)); URL->lleft = URL->lleft - bsize; if (bsize > 0) dlstat += bsize; fwrite(buff, bsize, 1, URL->fp); timeout.tv_sec = TIMEOUT_SEC; if (dlstat > ROLLBACK - 1) { if (OUTPUT_LEVEL > 1) printf("."); dlstat -= ROLLBACK; fflush(NULL); dldots ++; if (dldots%50 == 0) { if (OUTPUT_LEVEL > 1) printf("] -%ldk %.2fk/sec- \n[.", (ROLLBACK * dldots) / 1024, ((float)((ROLLBACK * 50) / 1024)) / ((float)(time((time_t *)0) - start))); start = time((time_t *)0); } } break; } if ((bsize < 1) || (URL->lleft < 1 && URL->resume)) { if (OUTPUT_LEVEL > 1) if (dldots%50 == 0) printf("]\n\nChecking Completion Status...\n"); else printf("] -%ldk %.2fk/sec-\n\nChecking Completion Status...\n", (ROLLBACK * dldots)/1024, ((float)((ROLLBACK * (dldots%50)) / 1024)) / ((float)(time((time_t *)0) - start))); close(URL->sockfd); fclose(URL->fp); URL->done = 1; URL->retry = 0; if (OUTPUT_LEVEL > 1) printf("%s Done!\n\n", URL->filename); } } int main (int argc, char *argv[]) /************************************* ** *************************************/ { URLp URL = &URLst; int i; printf("%s [Get and Resume Elite EDition] - By Anoakie Turner\r\n", CURRENT_VERSION); OUTPUT_LEVEL = 5; STDOUT = 0; for (i = 1; i < argc; i ++) if (argv[i][0] == '-') { switch (argv[i][1]) { case 'N': case 'n': argv[i] = GGR; break; case 'O': case 'o': if (argv[i][2] >= '0' || argv[i][2] <= '9') OUTPUT_LEVEL = argv[i][2] - '0'; break; case 'S': case 's': STDOUT = 0; break; case '?': printf("\n\nUSEAGE: greed URL(1) URL(2) ... URL(N)\n" "i.e.: greed %s ftp://some.where/out/there.tar.gz\n", GGR); exit(0); break; } } for (i = 1; i < argc; i ++) { strcpy(URL->name, argv[i]); if (argv[i][0] != '-' && Parse(URL)) while(URL->retry == 1) { if (Connect(URL)) if ((URL->protocol == HTTP && ReadHTTPHeader(URL)) || (URL->protocol == FTP && ReadFTPListing(URL))) { start = time((time_t *)0); FD_ZERO(&testfds); FD_SET(URL->sockfd, &testfds); while (!URL->done) Download(URL); } close (URL->sockfd); if (URL->retry == 1) { URL->retry = 0; i--; if (OUTPUT_LEVEL > 1) printf("Retrying in 120 seconds...\n"); sleep(120); } } } return(EXIT_SUCCESS); }