/* * Copyright 2006,2008 Nick White * * This file is part of GetHT * * GetHT 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 3 of the License, or * (at your option) any later version. * * GetHT 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 GetHT. If not, see . * */ #include #include #include #include #include #include #include "getht.h" #include "issue.h" int read_func(void *ptr, size_t size, size_t nmemb, FILE *stream) { return fread(ptr, size, nmemb, stream); } int write_func(void *ptr, size_t size, size_t nmemb, FILE *stream) { return fwrite(ptr, size, nmemb, stream); } int update_progress(void *data, double dltotal, double dlnow, double ultotal, double ulnow); int save_file(char *uri, char *filepath, char *filetitle, long resume_offset, struct config * options) /* Save the file *uri to *filepath */ { if(!options->quiet) { printf("Downloading %s ",filetitle); fflush(stdout); } if(options->curl_handle) { FILE *file; file = fopen(filepath, resume_offset?"a":"w"); if(!file) { fprintf(stderr,"Error: cannot open file %s for writing.\n",filepath); return 1; } curl_easy_setopt(options->curl_handle, CURLOPT_URL, uri); curl_easy_setopt(options->curl_handle, CURLOPT_READFUNCTION, read_func); curl_easy_setopt(options->curl_handle, CURLOPT_WRITEFUNCTION, write_func); curl_easy_setopt(options->curl_handle, CURLOPT_WRITEDATA, file); if(options->proxy.type) { curl_easy_setopt(options->curl_handle, CURLOPT_PROXYTYPE, options->proxy.type); curl_easy_setopt(options->curl_handle, CURLOPT_PROXY, options->proxy.address); if(options->proxy.port) curl_easy_setopt(options->curl_handle, CURLOPT_PROXYPORT, options->proxy.port); if(options->proxy.auth) curl_easy_setopt(options->curl_handle, CURLOPT_PROXYAUTH, options->proxy.auth); if(options->proxy.user[0] && options->proxy.pass[0]) { char userpass[STR_MAX]; snprintf(userpass, STR_MAX, "%s:%s", options->proxy.user, options->proxy.pass); curl_easy_setopt(options->curl_handle, CURLOPT_PROXYUSERPWD, userpass); } } if(!options->quiet) { curl_easy_setopt(options->curl_handle, CURLOPT_NOPROGRESS, 0); curl_easy_setopt(options->curl_handle, CURLOPT_PROGRESSFUNCTION, update_progress); curl_easy_setopt(options->curl_handle, CURLOPT_PROGRESSDATA, &resume_offset); } else curl_easy_setopt(options->curl_handle, CURLOPT_NOPROGRESS, 1); curl_easy_setopt(options->curl_handle, CURLOPT_RESUME_FROM, resume_offset); /* create a buffer to hold any curl errors */ char errorinfo[CURL_ERROR_SIZE]; curl_easy_setopt(options->curl_handle, CURLOPT_ERRORBUFFER, errorinfo); if(curl_easy_perform(options->curl_handle)) { remove(filepath); fprintf(stderr,"\nError, could not download %s: %s\n",uri, errorinfo); return 1; } fclose(file); if(!options->quiet) printf("\rDownloaded %s \n",filetitle); } else { fprintf(stderr,"Error: curl failed to initialise.\n"); printf("Could not download %s\n",uri); return 1; } return 0; } int update_progress(void *data, double dltotal, double dlnow, double ultotal, double ulnow) /* Print status information */ { double frac; long *startsize = NULL; startsize = (long *)data; long cur = (long) dlnow + *startsize; long total = (long) dltotal + *startsize; if(cur > 0) frac = 100 * (double) cur / (double) total; else frac = 0; printf("\b\b\b\b\b\b\b: %3.0lf%% ", frac); fflush(stdout); return 0; } double getremotefilesize(char *uri, struct config * options) { double filesize; if(options->curl_handle) { curl_easy_setopt(options->curl_handle, CURLOPT_URL, uri); curl_easy_setopt(options->curl_handle, CURLOPT_READFUNCTION, read_func); /* don't download or return either body or header */ curl_easy_setopt(options->curl_handle, CURLOPT_NOBODY, 1); curl_easy_setopt(options->curl_handle, CURLOPT_HEADER, 0); if(options->proxy.type) { curl_easy_setopt(options->curl_handle, CURLOPT_PROXYTYPE, options->proxy.type); curl_easy_setopt(options->curl_handle, CURLOPT_PROXY, options->proxy.address); if(options->proxy.port) curl_easy_setopt(options->curl_handle, CURLOPT_PROXYPORT, options->proxy.port); if(options->proxy.auth) curl_easy_setopt(options->curl_handle, CURLOPT_PROXYAUTH, options->proxy.auth); if(options->proxy.user[0] && options->proxy.pass[0]) { char userpass[STR_MAX]; snprintf(userpass, STR_MAX, "%s:%s", options->proxy.user, options->proxy.pass); curl_easy_setopt(options->curl_handle, CURLOPT_PROXYUSERPWD, userpass); } } curl_easy_setopt(options->curl_handle, CURLOPT_NOPROGRESS, 1); if(curl_easy_perform(options->curl_handle)) filesize = -1; curl_easy_getinfo(options->curl_handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &filesize); } else filesize = -1; curl_easy_reset(options->curl_handle); return filesize; } char * checkdir(char * dir) /* Checks that dir is writable. If necessary prompt * the user for a new directory and return it. */ { int dirchanged = 0; if(!opendir(dir)) while(mkdir(dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) { fprintf(stderr,"Cannot create directory %s\n", dir); printf("Please enter the path of a save directory: "); scanf("%s", dir); dirchanged = 1; } if(dirchanged) return dir; else return 0; } char * getissuedir(struct config * options, iss * issue) /* Returns and prepares download directory */ { char * newsavedir; char * newdir; newsavedir = malloc(STR_MAX); newdir = malloc(STR_MAX); /* Check that main save path is ok */ newsavedir = checkdir(options->save_path); /* Save the new save path if it changed */ if(newsavedir) { char getht_path[STR_MAX]; snprintf(getht_path,STR_MAX,"%s/.getht",getenv("HOME")); updateconfig(getht_path, &options); } snprintf(newdir,STR_MAX,"%s/%i_%i-%i",options->save_path, issue->date.year,issue->date.firstmonth,issue->date.lastmonth); /* Check that specific issue path is ok */ checkdir(newdir); return newdir; } void downloadsection(struct config * options, sec * section, char * downdir, int force) /* Download section pointed to */ { char filename[STR_MAX]; char filepath[STR_MAX]; snprintf(filename,STR_MAX,"section_%i.pdf", section->number); snprintf(filepath,STR_MAX,"%s/%s", downdir, filename); if(!force){ struct stat fileinfo; /* see if local file exists */ if(stat(filepath, &fileinfo)) save_file(section->uri, filepath, filename, 0, options); else { /* get size of local file */ long localsize = 0; localsize = (long) fileinfo.st_size; /* get size of remote file */ long remotesize = 0; remotesize = (long) getremotefilesize(section->uri, options); /* if size of local file != size of remote file, resume */ if(remotesize > 0 && localsize < remotesize) save_file(section->uri, filepath, filename, localsize, options); else { if(!options->quiet) printf("Skipping download of completed section %i\n", section->number); } } } else save_file(section->uri, filepath, filename, 0, options); } void downloadissue(struct config * options, iss * issue, int force) /* Download issue pointed to */ { char downdir[STR_MAX]; strncpy(downdir, getissuedir(options, issue), STR_MAX); printf("Downloading %s to %s\n",issue->title, downdir); int count; for(count = 0; count <= issue->no_of_sections; count++) downloadsection(options, issue->section[count], downdir, force); }