visiste: 1671
//HTTP Download Class //by mamo139 #ifndef _HTTP_DOWNLOAD_CLASS_H #define _HTTP_DOWNLOAD_CLASS_H #define HTTP_DOWNLOAD_CLASS_VERSION "0.01" #define DEBUG_MODE #include <iostream> #include <string> #include <winsock2.h> #include "matrici.h" #pragma comment(lib,"wsock32.lib") #define HD_HTTP_DEFAULT_PORT 80 #define HD_DOWNLOAD_BUFFER_DEFAULT_SIZE 1048576 typedef enum { HD_STATUS_UNINITIALIZED, //bisogna ancora eseguire initialize(), l'oggetto è nuovo! HD_STATUS_INITIALIZED, //initialize() è stata eseguita con successo HD_STATUS_FILE_REDIRECTED, //ci siamo connessi e ricevuto codice http 302 FOUND HD_STATUS_FILE_NOT_FOUND, //ci siamo connessi e ricevuto codice http 404 NOT FOUND HD_STATUS_FILE_OK, //ci siamo connessi e ricevuto codice http 200 OK HD_STATUS_HTTP_CODE_NOT_RECOGNIZED, //ci siamo connessi e abbiamo ricevuto un codice http non previsto HD_STATUS_PROTOCOL_NOT_HTTP11, //la risposta non utilizza protocollo HTTP/1.1 HD_STATUS_DOWNLOADING, //è in corso il download singolo e senza possibilità di pause and resume HD_STATUS_MULTIDOWNLOADING, //è in corso il multidownload HD_STATUS_MULTIDOWNLOADING_ASSEMBLING, //sta unendo tutti i file di download parziale HD_STATUS_MULTIDOWNLOADING_ASSEMBLING_ERROR, //errore durante l'assemblamento dei file parziali HD_STATUS_DOWNLOAD_COMPLETED, //il download è stato completato HD_STATUS_SAVED, //il download è stato salvato, serve per fare in modo che stop() non cancelli i dati HD_STATUS_STOPPED, //il download è stato stoppato HD_STATUS_PAUSED, //il download è stato messo in pausa HD_STATUS_RESUME //quando viene dato l'ordine di ricominciare il download } http_download_status; typedef struct{ //struttura che ci serve per mandare i dati ai processi asincroni incaricati del multidownload int id_segmento; //id del segmento in cui si scarica void *oggetto; //puntatore all'istanza della classe char *file_parziale; //nome del file parziale } partial_download_data; int hd_is_file(char *pathname); //funzione che controlla se esiste un file class http_download { private: long file_size; //grandezza del file da scaricare, 0 se non impostata, -1 se non comunicata long buffer_size; //grandezza dei buffer di download int status; //a che punto si trova la classe int multidownload; //numero di segmenti in cui dividere il download se disponibile /*matrice multidownload_points contenente i punti di partenza e posizione dei segmenti [id][0] contiene i punti partenza [id][1] contiene il punto a cui siamo [id+1][0] contiene il punto di arrivo [id][2] contiene lo status, 0 se inattivo, 1 se sta scaricando, 2 se download terminato*/ matrice multidownload_points; char url[10000]; //indirizzo completo("http://www.esempio.it/cartella/file.txt") int porta; //porta sulla quale è in ascolto il server http char host[10000]; //indirizzo server ("www.esempio.it") char host_url[10000]; //indirizzo dentro il server ("/cartella/file.txt") char file[10000]; //come salvare il file nel pc bool is_range_accepted; //vale 1 se il server accetta il comando range, 0 altrimenti bool is_size_comunicated; //vale 1 se conosciamo la dimensione del file, 0 altrimenti //da qui in poi variabili di comunicazione interna int luncher_ready_to_pause; int luncher_ready_to_stop; int vanilla_ready_to_stop; public: http_download(); //constructor ~http_download(); //destructor /* initialize() è il primo metodo da utilizzare, serve per dire all'oggetto quale file scaricare e dove scaricaro. Inputs: _url: l'indirizzo del file (es. http://www.miosito.it/cartella/file.zip ) _file: il nome con cui salvare il file scaricato _multidownload: il numero di segmenti in cui dividere il file al fine di avvelocizzarne il download, se 0 procede con un download "plain vanilla" */ int initialize(char *_url, char *_file, int _multidownload); /*settings() è un metodo che si puo eseguire dopo initialize() per modificare alcune opzioni di default. Se si inserisce il valore 0 è come confermare il valore di default. Inputs: _buffer_size: la grandezza dei buffer utilizzati nel download _porta_http: la porta dell'host su cui è in ascolto l'http server se il metodo non viene utilizzato venogno usate in automatico le opzioni di default*/ int settings(long _buffer_size, int _porta_http); /* start() avvia il download asincrono del file, seguendo le condizioni specificate dalla funzione initialize(). */ int start(); /*stop() termina il download in modo irreversibile*/ int stop(); /*pause() puo essere usato solo quando si è nello status HD_STATUS_MULTIDOWNLOADING return: 1 se la funzione ha successo 0 se la funzione non ha successo*/ int pause(); /*resume() puo essere usato solo quando si è nello status HD_STATUS_MULTIDOWNLOADING return: 1 se la funzione ha successo 0 se la funzione non ha successo*/ int resume(); /*save() puo essere usato solo quando si è nello status HD_STATUS_MULTIDOWNLOADING salva il download in modo tale che si possa riprendere in futuro. return: 1 se la funzione ha successo 0 se la funzione non ha successo*/ int save(); /*load() puo essere usato solo quando si è nello status HD_STATUS_MULTIDOWNLOADING carica il salvataggio precedentemente creato con save() e cancella il salvataggio. return: 1 se la funzione ha successo 0 se la funzione non ha successo*/ int load(char *_load_file); /*non ancora implementata*/ int backup(char *_save_file); /*get_status() resituisce lo status dell'oggetto, molto utile per monitorare la situazione, soprattutto nelle fasi asincrone*/ int get_status(); /*print() visualizza tutti i dati*/ int print(); /*funzioni utilizzate internamente dai metodi, non utilizzare!!!*/ friend int hd_connessione_header(http_download *download); friend int hd_connessione_header_partial_content(http_download *download); friend int hd_connessione_download_vanilla(http_download *download); friend int hd_connessione_multidownload_segmento(partial_download_data *dati); friend int hd_multidownload_luncher(http_download *download); friend int hd_multidownload_files_assembler(http_download *download, partial_download_data *dati); }; #endif
#include "http_download.h" http_download::http_download(){ file_size = 0; buffer_size = 0; multidownload = 0; status = HD_STATUS_UNINITIALIZED; is_range_accepted = 0; is_size_comunicated = 0; //variabili di comunicazione interna luncher_ready_to_pause = 0; luncher_ready_to_stop = 0; vanilla_ready_to_stop = 0; } http_download::~http_download(){ } int http_download::initialize(char *_url, char *_file, int _multidownload){ int x; if(status != HD_STATUS_UNINITIALIZED) return 0; if(_multidownload<0) return 0; //url strncpy(url, _url, strlen(_url)+1); //file strncpy(file, _file, strlen(_file)+1); //host for(x=7; x<(int)strlen(_url) ;x++) if(_url[x] == '/'){ break; } strncpy(host, &url[7], x-7); host[x-7] = '\0'; //host_url strncpy(host_url, &url[x], strlen(_url)-x+1); host_url[strlen(_url)-x+1] = '\0'; //multidownload multidownload = _multidownload; //porta http porta = HD_HTTP_DEFAULT_PORT; //download buffer size buffer_size = HD_DOWNLOAD_BUFFER_DEFAULT_SIZE; //aggiornamento status status = HD_STATUS_INITIALIZED; return 1; } int http_download::settings(long _buffer_size, int _porta_http){ if(status == HD_STATUS_INITIALIZED){ if(_buffer_size != 0) buffer_size = _buffer_size; if(_porta_http != 0) porta = _porta_http; return 1; } else{ #ifdef DEBUG_MODE printf("WARNING settings(): l'oggetto non è nello status HD_STATUS_INITIALIZED\n"); #endif return 0; } } int http_download::start(){ long x; DWORD thread_id; //controllo dello status (che deve essere HD_STATUS_INITIALIZED) prima di procedere if(status != HD_STATUS_INITIALIZED){ #ifdef DEBUG_MODE printf("WARNING start(): You are not in the right status.\n"); #endif return 0; } /*ci connettiamo al server con la funzione hd_connessione_asincrona_header per ricevere info sul file e controllare che effettivamente esista, il ciclo serve per seguire le redirezioni (302 found) fino a quando non si trova il vero indirizzo*/ while(status == HD_STATUS_FILE_REDIRECTED || status == HD_STATUS_INITIALIZED){ hd_connessione_header(this); //controlliamo i possibili errori if(status == HD_STATUS_PROTOCOL_NOT_HTTP11){ #ifdef DEBUG_MODE printf("ERROR start(): HD_STATUS_PROTOCOL_NOT_HTTP11\n"); #endif return 0; } if(status == HD_STATUS_FILE_NOT_FOUND){ #ifdef DEBUG_MODE printf("start(): HTTP PROTOCOL 404: HD_STATUS_FILE_NOT_FOUND\n"); #endif return 0; } if(status == HD_STATUS_HTTP_CODE_NOT_RECOGNIZED){ #ifdef DEBUG_MODE printf("ERROR start(): HD_STATUS_HTTP_CODE_NOT_RECOGNIZED, forse non supportato\n"); #endif return 0; } } /*se il server ci ha comuncato la grandezza del file da scaricare allora controlliamo se il server accetta il range, se lo accetta procediamo al multidownload e possibilità di pausa, se non lo accetta allora dobbiamo fare un download normale*/ if(is_size_comunicated){ #ifdef DEBUG_MODE printf("start(): File size: %d bytes\n",file_size); #endif /*il server non ci ha comunicato di accettare i range, ma non tutte le speranze sono perdute perche il server non è obbligato a comunicarci la disponibilità del servizio: possiamo inviare al server una richiesta con range per vedere se il servizio effettivamente esiste*/ if(!is_range_accepted){ //inviamo una richiesta con range sperando di rivere un 206 PARTIAL CONTENT hd_connessione_header_partial_content(this); } /*se il server accetta il range-bytes allora possiamo spezzare il download in piu segmenti*/ if(is_range_accepted){ //range accettato #ifdef DEBUG_MODE printf("start(): Accept-Ranges: bytes\n"); #endif //prepariamo matrice dati per il multidownload multidownload_points.create_with_value(multidownload+1, 3, 0); for(x=0; x<multidownload; x++){ multidownload_points[x][0] = (file_size/multidownload)*x; //impostiamo punto di partenza segmento multidownload_points[x][1] = multidownload_points[x][0]; //impostiamo punto attuale del segmento } multidownload_points[multidownload][0] = file_size; #ifdef DEBUG_MODE printf("start(): multidownload possibile, segmenti %d\n", multidownload); #endif if(multidownload == 0) //se è preteso, avviamo un download normale, senza multidownload ne pausa CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) hd_connessione_download_vanilla, this, 0, &thread_id); else //avviamo in modo asincrono la funzione che si occuperà di avviare il multidownload CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) hd_multidownload_luncher, this, 0, &thread_id); } else{ //range non accettato //senza range, avviamo un download normale, senza multidownload ne pausa CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) hd_connessione_download_vanilla, this, 0, &thread_id); } } else{ //file size non comunicato #ifdef DEBUG_MODE printf("start(): File size not comunicated :(\n",file_size); #endif //senza grandezza file, avviamo un download normale, senza multidownload ne pausa CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) hd_connessione_download_vanilla, this, 0, &thread_id); } return 1; } int http_download::stop(){ long x,y; if(status == HD_STATUS_DOWNLOADING){ status = HD_STATUS_STOPPED; while(!vanilla_ready_to_stop) Sleep(10); vanilla_ready_to_stop = 0; //qui devo cancellare il file dove sono stati scaricati i dati status = HD_STATUS_UNINITIALIZED; return 1; } else if(status == HD_STATUS_MULTIDOWNLOADING || status == HD_STATUS_PAUSED || status == HD_STATUS_SAVED){ status = HD_STATUS_STOPPED; //controlliamo ed eventualmente aspettiamo che i download parziali siano tutti terminati while(1){ y=0; for(x=0;x<multidownload;x++) if(multidownload_points[x][2] == 0) y++; if(y==multidownload) break; Sleep(10); } //aspettiamo che luncher abbia ricevuto il segnale while(1){ if(luncher_ready_to_stop == 1) break; Sleep(10); } luncher_ready_to_stop = 0; //togliamo le cose che vanno tolte e resettiamo alcune variabili multidownload_points.~matrice(); file_size = 0; is_range_accepted = 0; is_size_comunicated = 0; luncher_ready_to_pause = 0; luncher_ready_to_stop = 0; vanilla_ready_to_stop = 0; /*qui si dovranno cancellare i file temporanei, ma se si è in stato HD_STATUS_SAVED vanno lasciati ;)*/ status = HD_STATUS_INITIALIZED; return 1; } else{ #ifdef DEBUG_MODE printf("WARNING stop(): non si puo usare stop() in questa situazione.\n",file_size); #endif return 0; } } int http_download::pause(){ if(status != HD_STATUS_MULTIDOWNLOADING){ #ifdef DEBUG_MODE printf("WARNING pause(): puo essere usata solo in stato HD_STATUS_MULTIDOWNLOADING\n",file_size); #endif return 0; } status = HD_STATUS_PAUSED; /*ora attendiamo che la funzione asincrona incaricata della gestione del multidownload, che è hd_multidownload_luncher(), rilevi la chiusura dei download parziali e si metta a sua volta in pausa. questo meccanismo serve per non terminare il metodo pause() prima che la pausa non sia avvenuto a tutti gli effetti. In questo senso la funzione pause() è "bloccante" di modificare la variabile luncher_ready_to_pause se ne occupa solamente hd_multidownload_luncher()*/ while(!luncher_ready_to_pause) Sleep(50); #ifdef DEBUG_MODE printf("pause(): pausa bloccante avvenuta con successo.\n"); #endif return 1; } int http_download::resume(){ if(status != HD_STATUS_PAUSED){ #ifdef DEBUG_MODE printf("WARNING resume(): puo essere usata solo in stato HD_STATUS_PAUSED\n",file_size); #endif return 0; } /*di riavviare tutto si occuperà hd_multidownload_luncher()*/ status = HD_STATUS_RESUME; return 1; } int http_download::save(){ long x,y; char *_file_matrice; char *_main_content; FILE *main; /*la funzione save() è possibile solo nello stato HD_STATUS_PAUSED*/ if(status != HD_STATUS_PAUSED){ #ifdef DEBUG_MODE printf("WARNING save(): puo essere usata solo in stato HD_STATUS_PAUSED\n",file_size); #endif return 0; } //controlliamo ed eventualmente aspettiamo che i download parziali siano tutti terminati while(1){ y=0; for(x=0;x<multidownload;x++) if(multidownload_points[x][2] == 0) y++; if(y==multidownload) break; Sleep(10); } //salviamo la matrice multidownload_points _file_matrice = new char[10000]; sprintf(_file_matrice,"%s.points",file); multidownload_points.save_in_file(_file_matrice); //salviamo i dati principali _main_content = new char[100000]; main = fopen(file, "wb"); //scriviamo magic number fwrite("HD_FILE\0", 1, 8, main); //scriviamo url fwrite(url, 1, strlen(url)+1, main); //scriviamo porta itoa(porta, _file_matrice, 10); //utilizziamo _file_matrice come buffer visto che non ci serve piu ;) fwrite(_file_matrice, 1, strlen(_file_matrice)+1, main); fclose(main); delete [] _file_matrice; delete [] _main_content; status = HD_STATUS_SAVED; //alla fine del salvataggio stoppiamo il download stop(); return 1; } int http_download::load(char *_load_file){ long x,y; char *_load_file_matrice; char *_url; char *_porta; FILE *load_file_stream; char *buffer; long lbuffer; if(status != HD_STATUS_UNINITIALIZED){ #ifdef DEBUG_MODE printf("WARNING load(): puo essere usata solo in stato HD_STATUS_UNINITIALIZED\n"); #endif return 0; } if(hd_is_file(_load_file) == 0){ //controllo esistenza file #ifdef DEBUG_MODE printf("ERROR load(): file non esite :(\n"); #endif return 0; } _load_file_matrice = new char[10000]; sprintf(_load_file_matrice,"%s.points",_load_file); if(hd_is_file(_load_file_matrice) == 0){ //controllo esistenza file matrice #ifdef DEBUG_MODE printf("ERROR load(): file matrice non esite :(\n"); #endif delete [] _load_file_matrice; return 0; } load_file_stream = fopen(_load_file, "rb"); fseek(load_file_stream, 0, SEEK_END); lbuffer = ftell(load_file_stream); rewind(load_file_stream); buffer = new char[lbuffer]; fread(buffer, 1, lbuffer, load_file_stream); fclose(load_file_stream); if(strncmp(buffer, "HD_FILE", 7) != 0){ //controllo il magic number #ifdef DEBUG_MODE printf("ERROR load(): magic number non corrisponde, non posso aprirlo :(\n"); #endif delete [] _load_file_matrice; delete [] buffer; return 0; } y=0; for(x=0;x<lbuffer;x++){ if(buffer[x] == '\0') y++; if(y==2) break; } y=x; _url = &buffer[8]; _porta = &buffer[x+1]; //carico multidownload_points multidownload_points.load_from_file(_load_file_matrice); //carico multidownload multidownload = multidownload_points.rows()-1; //carico file sprintf(file,"%s",_load_file); //carico url strncpy(url, _url, strlen(_url)+1); //carico host for(x=7; x<(int)strlen(_url) ;x++) if(_url[x] == '/'){ break; } strncpy(host, &_url[7], x-7); host[x-7] = '\0'; //carico host_url strncpy(host_url, &url[x], strlen(_url)-x+1); host_url[strlen(_url)-x+1] = '\0'; //carico porta porta = atoi(_porta); //download buffer size buffer_size = HD_DOWNLOAD_BUFFER_DEFAULT_SIZE; //aggiornamento status status = HD_STATUS_INITIALIZED; delete [] buffer; delete [] _load_file_matrice; return 1; } int http_download::backup(char *_save_file){ //long x; //partial_download_data *_dati_multidownload; ////creiamo la struttura partial_download_data //_dati_multidownload = new partial_download_data[multidownload]; //for(x=0;x<multidownload;x++){ //carichiamo i dati che ci servono nella struttura // _dati_multidownload[x].id_segmento = x; // _dati_multidownload[x].file_parziale = new char[strlen(file)+5]; // sprintf(_dati_multidownload[x].file_parziale,"%s.%03d",file,x); //} return 1; } int http_download::get_status(){ return status; } int http_download::print(){ if(status == HD_STATUS_UNINITIALIZED){ #ifdef DEBUG_MODE printf("WARNING print(): non puoi eseguire a HD_STATUS_UNINITIALIZED.\n"); #endif return 0; } printf("file: %s\n", file); printf("url: %s\n", url); printf("host: %s\n", host); printf("host_url: %s\n", host_url); printf("porta: %d\n", porta); printf("download: %d\n", multidownload); multidownload_points.print(); return 1; } int hd_connessione_download_vanilla(http_download *download){ long x=0; struct sockaddr_in indirizzo; struct hostent *ip; WORD socket_version; WSADATA socket_data; SOCKET sock; int sock_err; char *richiesta_http; char *risposta_http; char header_buffer; FILE *file_stream; long downloaded_bytes, written_bytes, total_written_bytes=0, total_downloaded_bytes=0; char *download_buffer; long _buffer_size = download->buffer_size; //verifichiamo che l'oggetto download sia pronto if(download->status != HD_STATUS_FILE_OK){ #ifdef DEBUG_MODE printf("WARNING hd_connessione_download_vanilla(): not the right status.\n"); #endif return 0; } //avviamo socket socket_version = MAKEWORD(2, 2); WSAStartup(socket_version, &socket_data); //attiva la lib winsock 2.2 //dati per la connessione ip = gethostbyname(download->host); indirizzo.sin_family = AF_INET; //tipo di indirizzo indirizzo.sin_port = htons(download->porta); //porta in cui è in ascolto server HTTP memcpy(&indirizzo.sin_addr, ip->h_addr, ip->h_length); //copia l'indirizzo nella struttura indirizzo //richiesta HTTP richiesta_http = new char[10000]; sprintf(richiesta_http,"GET %s HTTP/1.1\n"\ "host: %s\n"\ "Keep-Alive: 300\n"\ "Connection: keep-alive\n"\ "\n\0"\ , download->host_url, download->host); #ifdef DEBUG_MODE printf("%s\n",richiesta_http); #endif //ci connettiamo sock = socket(AF_INET, SOCK_STREAM, 0); sock_err = connect(sock, (struct sockaddr *)&indirizzo, sizeof(indirizzo)); if(!sock || sock_err != 0){ #ifdef DEBUG_MODE printf("ERROR hd_connessione_download_vanilla(): socket: %d connect: %d\n", sock, sock_err); #endif return 0; } //mandiamo richiesta HTTP send(sock, richiesta_http, strlen(richiesta_http)+1, 0); //riceviamo risposta: header HTTP risposta_http = new char[10000]; while( recv(sock, &header_buffer, 1, 0) != 0 ){ risposta_http[x] = header_buffer; x++; if(risposta_http[x-1] == '\x0a' && risposta_http[x-3] == '\x0a')//controllo per vedere se l'header è finito break; } risposta_http[x] = '\0'; #ifdef DEBUG_MODE printf("%s\n",risposta_http); #endif //controlliamo header HTTP ricevuto in risposta if( strncmp("HTTP/1.1", risposta_http, 8) != 0 ){ //controlliamo che il protocollo sia HTTP/1.1 download->status = HD_STATUS_PROTOCOL_NOT_HTTP11; delete [] richiesta_http; delete [] risposta_http; shutdown(sock, 2); closesocket(sock); WSACleanup(); return 0; } if( strncmp("200", &risposta_http[9], 3) == 0 ){ //verifichiamo se tutto ok e iniziamo a scaricare download->status = HD_STATUS_DOWNLOADING; download_buffer = new char[_buffer_size]; //alloco la memoria per il buffer file_stream = fopen(download->file, "wb"); //apro il file in cui salvare i dati //riceviamo risposta: file while (1){ //prendiamo il file un po alla volta downloaded_bytes = recv(sock, download_buffer, _buffer_size, 0); if(!(downloaded_bytes > 0)) break; written_bytes = fwrite(download_buffer, 1, downloaded_bytes, file_stream); //scrivo i dati total_written_bytes += written_bytes; total_downloaded_bytes += downloaded_bytes; #ifdef DEBUG_MODE printf("d:%d w:%d\n",total_downloaded_bytes,total_written_bytes); #endif if(download->status == HD_STATUS_STOPPED){ //controlliamo di non aver ricevuto l'ordine di fermarci download->vanilla_ready_to_stop = 1; break; } } fclose(file_stream); //chiudiamo file delete [] download_buffer; //disallochiamo buffer } else{ download->status = HD_STATUS_HTTP_CODE_NOT_RECOGNIZED; delete [] richiesta_http; delete [] risposta_http; shutdown(sock, 2); closesocket(sock); WSACleanup(); return 0; } delete [] richiesta_http; delete [] risposta_http; shutdown(sock, 2); closesocket(sock); WSACleanup(); /*questo controllo serve per fare in modo di non segnare come HD_STATUS_DOWNLOAD_COMPLETED un oggetto che invece è stato stoppato ed è in status HD_STATUS_STOPPED*/ if(download->status == HD_STATUS_DOWNLOADING) download->status = HD_STATUS_DOWNLOAD_COMPLETED; return 1; } int hd_connessione_header_partial_content(http_download *download){ long x=0; struct sockaddr_in indirizzo; struct hostent *ip; WORD socket_version; WSADATA socket_data; SOCKET sock; int sock_err; char *richiesta_http; char *risposta_http; char header_buffer; //verifichiamo che la richiesta partial content sia inviata con status HD_STATUS_FILE_OK if(download->status != HD_STATUS_FILE_OK){ #ifdef DEBUG_MODE printf("WARNING hd_connessione_header_partial_content(): not the right status.\n"); #endif return 0; } //avviamo socket socket_version = MAKEWORD(2, 2); WSAStartup(socket_version, &socket_data); //attiva la lib winsock 2.2 //dati per la connessione ip = gethostbyname(download->host); indirizzo.sin_family = AF_INET; //tipo di indirizzo indirizzo.sin_port = htons(download->porta); //porta in cui è in ascolto server HTTP memcpy(&indirizzo.sin_addr, ip->h_addr, ip->h_length); //copia l'indirizzo nella struttura indirizzo //richiesta HTTP partial download richiesta_http = new char[10000]; sprintf(richiesta_http,"GET %s HTTP/1.1\n"\ "host: %s\n"\ "Keep-Alive: 300\n"\ "Connection: keep-alive\n"\ "Range: bytes=1-\n"\ "\n\0"\ , download->host_url, download->host); #ifdef DEBUG_MODE printf("%s\n",richiesta_http); #endif //ci connettiamo sock = socket(AF_INET, SOCK_STREAM, 0); sock_err = connect(sock, (struct sockaddr *)&indirizzo, sizeof(indirizzo)); if(!sock || sock_err != 0){ #ifdef DEBUG_MODE printf("ERROR hd_connessione_header_partial_content(): socket: %d connect: %d\n", sock, sock_err); #endif return 0; } //mandiamo richiesta HTTP send(sock, richiesta_http, strlen(richiesta_http)+1, 0); //riceviamo risposta: header HTTP risposta_http = new char[10000]; while( recv(sock, &header_buffer, 1, 0) != 0 ){ risposta_http[x] = header_buffer; x++; if(risposta_http[x-1] == '\x0a' && risposta_http[x-3] == '\x0a')//controllo per vedere se l'header è finito break; } risposta_http[x] = '\0'; #ifdef DEBUG_MODE printf("%s\n",risposta_http); #endif shutdown(sock, 2); closesocket(sock); WSACleanup(); //controlliamo header HTTP ricevuto in risposta if( strncmp("HTTP/1.1", risposta_http, 8) != 0 ){ //controlliamo che il protocollo sia HTTP/1.1 download->status = HD_STATUS_PROTOCOL_NOT_HTTP11; delete [] richiesta_http; delete [] risposta_http; return 0; } if( strncmp("206", &risposta_http[9], 3) == 0 ){ //verifichiamo se il download parziale è stato accettato download->is_range_accepted = 1; return 1; } return 1; } int hd_connessione_header(http_download *download){ long x=0,y; struct sockaddr_in indirizzo; struct hostent *ip; WORD socket_version; WSADATA socket_data; SOCKET sock; int sock_err; char *richiesta_http; char *risposta_http; char header_buffer; //verifichiamo che l'oggetto download sia pronto if(download->status != HD_STATUS_INITIALIZED && download->status != HD_STATUS_FILE_REDIRECTED){ #ifdef DEBUG_MODE printf("WARNING hd_connessione_header(): not the right status.\n"); #endif return 0; } //avviamo socket socket_version = MAKEWORD(2, 2); WSAStartup(socket_version, &socket_data); //attiva la lib winsock 2.2 //dati per la connessione ip = gethostbyname(download->host); indirizzo.sin_family = AF_INET; //tipo di indirizzo indirizzo.sin_port = htons(download->porta); //porta in cui è in ascolto server HTTP memcpy(&indirizzo.sin_addr, ip->h_addr, ip->h_length); //copia l'indirizzo nella struttura indirizzo //richiesta HTTP richiesta_http = new char[10000]; sprintf(richiesta_http,"GET %s HTTP/1.1\n"\ "host: %s\n"\ "Keep-Alive: 300\n"\ "Connection: keep-alive\n"\ "\n\0"\ , download->host_url, download->host); #ifdef DEBUG_MODE printf("%s\n",richiesta_http); #endif //ci connettiamo sock = socket(AF_INET, SOCK_STREAM, 0); sock_err = connect(sock, (struct sockaddr *)&indirizzo, sizeof(indirizzo)); if(!sock || sock_err != 0){ #ifdef DEBUG_MODE printf("ERROR hd_connessione_header(): socket: %d connect: %d\n", sock, sock_err); #endif return 0; } //mandiamo richiesta HTTP send(sock, richiesta_http, strlen(richiesta_http)+1, 0); //riceviamo risposta: header HTTP risposta_http = new char[10000]; while( recv(sock, &header_buffer, 1, 0) != 0 ){ risposta_http[x] = header_buffer; x++; if(risposta_http[x-1] == '\x0a' && risposta_http[x-3] == '\x0a')//controllo per vedere se l'header è finito break; } risposta_http[x] = '\0'; #ifdef DEBUG_MODE printf("%s\n",risposta_http); #endif shutdown(sock, 2); closesocket(sock); WSACleanup(); //controlliamo header HTTP ricevuto in risposta if( strncmp("HTTP/1.1", risposta_http, 8) != 0 ){ //controlliamo che il protocollo sia HTTP/1.1 download->status = HD_STATUS_PROTOCOL_NOT_HTTP11; delete [] richiesta_http; delete [] risposta_http; return 0; } if( strncmp("404", &risposta_http[9], 3) == 0 ){ //verifichiamo se si è verificato l'errore 404 not found download->status = HD_STATUS_FILE_NOT_FOUND; } else if( strncmp("302", &risposta_http[9], 3) == 0 ){ //verifichiamo se c'è un redirect con 302 found download->status = HD_STATUS_FILE_REDIRECTED; //cerchiamo il nuovo indirizzo dentro l'header della risposta for(x=0; x<(int)strlen(risposta_http) ;x++){ //iniziamo ricerca indirizzo if(strncmp("\x0aLocation: ", &risposta_http[x], 11)==0){ //se incontriamo la sequenza "\x0aLocation: " for(y=x+11;;y++){ //leggiamo l'indirizzo if(risposta_http[y] == '\x0d'){ //fine dell'indirizzo risposta_http[y] = '\0'; //terminiamo la stringa break; } } y = x+11; //&risposta_http[y] è diventata la stringa contenente il nuovo indirizzo break; //abbiamo trovato il nuovo indirizzo, abbandoniamo la ricerca } } //modifichiamo l'oggetto in modo da poter scaricare dal nuovo indirizzo //url strncpy(download->url, &risposta_http[y], strlen(&risposta_http[y])+1); //host for(x=7; x<(int)strlen(&risposta_http[y]) ;x++) if(risposta_http[y+x] == '/'){ break; } strncpy(download->host, &risposta_http[y+7], x-7); download->host[x-7] = '\0'; //host_url strncpy(download->host_url, &risposta_http[y+x], strlen(&risposta_http[y])-x+1); download->host_url[strlen(&risposta_http[y])-x+1] = '\0'; } else if( strncmp("200", &risposta_http[9], 3) == 0 ){ //verifichiamo se siamo pronti al download download->status = HD_STATUS_FILE_OK; //controlliamo se il server ci ha comunicato la possiblità di usare range bytes for(x=0; x<(int)strlen(risposta_http) ;x++){ //cerchiamo il messaggio "Accept-Ranges: bytes" if(strncmp("Accept-Ranges: bytes", &risposta_http[x], strlen("Accept-Ranges: bytes\0"))==0){ download->is_range_accepted = 1; break; } } //controlliamo se il server ci ha detto quanto è grande il file e in tal caso prendiamo il dato for(x=0; x<(int)strlen(risposta_http) ;x++){ //cerchiamo il messaggio "Content-Lenght: " if(strncmp("Content-Length: ", &risposta_http[x], strlen("Content-Length: \0"))==0){ download->is_size_comunicated = 1; for(y=x+strlen("Content-Length: \0");;y++){ //leggiamo il numero if(risposta_http[y] == '\x0d'){ //fine del numero risposta_http[y] = '\0'; //terminiamo la stringa download->file_size = atoi(&risposta_http[x+strlen("Content-Length: \0")]); //preleviamo numero risposta_http[y] = '\x0d'; //ripristiniamo la stringa break; } } break; } } } else{ //se riceviamo un codice http che non è previsto dagli if precedenti download->status = HD_STATUS_HTTP_CODE_NOT_RECOGNIZED; } delete [] richiesta_http; delete [] risposta_http; return 1; } int hd_multidownload_luncher(http_download *download){ DWORD thread_id; long x,stato1,stato2; partial_download_data *dati_multidownload; dati_multidownload = new partial_download_data[download->multidownload]; for(x=0;x<download->multidownload;x++){ //carichiamo i dati che ci servono nella struttura dati_multidownload[x].id_segmento = x; dati_multidownload[x].oggetto = (void *) download; dati_multidownload[x].file_parziale = new char[strlen(download->file)+5]; sprintf(dati_multidownload[x].file_parziale,"%s.%03d",download->file,x); } for(x=0;x<download->multidownload;x++){ //avviamo i thread asincroni download->multidownload_points[x][2] = 1; CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) hd_connessione_multidownload_segmento, &dati_multidownload[x], 0, &thread_id); } //segnaliamo che la procedura di multidownload sta iniziando download->status = HD_STATUS_MULTIDOWNLOADING; while(1){ //ciclo per la gestione dei download parziali stato1=0;stato2=0; for(x=0;x<download->multidownload;x++){ //controlliamo i thread uno ad uno //se il thread si è chiuso e lo status lo consente if(download->multidownload_points[x][2] == 0 && download->status == HD_STATUS_MULTIDOWNLOADING){ download->multidownload_points[x][2] = 1; CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) hd_connessione_multidownload_segmento, &dati_multidownload[x], 0, &thread_id); //lo riavviamo } else if (download->multidownload_points[x][2] == 2) stato2++; //contiamo il numero di segmenti terminati else if (download->multidownload_points[x][2] == 1) stato1++; //e il numero di segmenti in download } if(stato2 == download->multidownload){ //se tutti i segmenti hanno finito di scaricare download->status = HD_STATUS_MULTIDOWNLOADING_ASSEMBLING; //segnaliamo che il download è completato break; //e chiudiamo questo ciclo di controllo } if(download->status == HD_STATUS_PAUSED){ //controlliamo se siamo in pausa while(1){ if(download->status == HD_STATUS_RESUME){ download->status = HD_STATUS_MULTIDOWNLOADING; download->luncher_ready_to_pause = 0; break; } //prima di stoppare luncher ci assicuriamo che tutti i parziali si siano fermati if(download->status == HD_STATUS_STOPPED){ download->luncher_ready_to_pause = 0; break; } if(stato1 == 0){ //comunichiamo che la pausa è avvenuta download->luncher_ready_to_pause = 1; } stato1=0; //essendo in loop necessitiamo di ciclo alternativo per controllo stato1 for(x=0;x<download->multidownload;x++) if(download->multidownload_points[x][2] == 1) stato1++; Sleep(50); } } if(download->status == HD_STATUS_STOPPED){ //controlliamo se abbiamo ricevuto l'ordine di fermarci while(1){ //prima di stoppare luncher ci assicuriamo che tutti i parziali si siano fermati if(download->status == HD_STATUS_STOPPED && stato1 == 0){ download->luncher_ready_to_stop = 1; break; } stato1=0; //essendo in loop necessitiamo di ciclo alternativo per controllo stato1 for(x=0;x<download->multidownload;x++) if(download->multidownload_points[x][2] == 1) stato1++; Sleep(50); } break; } Sleep(100); } //se il download è completato assembliamo i file parziali if(download->status == HD_STATUS_MULTIDOWNLOADING_ASSEMBLING){ if(hd_multidownload_files_assembler(download,dati_multidownload) == 1) download->status = HD_STATUS_DOWNLOAD_COMPLETED; else download->status = HD_STATUS_MULTIDOWNLOADING_ASSEMBLING_ERROR; } delete [] dati_multidownload; #ifdef DEBUG_MODE printf("hd_multidownload_luncher(): chiusura...\n"); #endif return 1; } int hd_connessione_multidownload_segmento(partial_download_data *dati){ long x; http_download *download; int id = dati->id_segmento; //id del segmento download = (http_download *) dati->oggetto; //puntatore all'istanza della nostra classe per il download download->multidownload_points[id][2] = 1; //segnaliamo che il processo parziale sta tentando il download struct sockaddr_in indirizzo; struct hostent *ip; WORD socket_version; WSADATA socket_data; SOCKET sock; int sock_err; char *richiesta_http; char *risposta_http; char header_buffer; FILE *file_stream; long downloaded_bytes, written_bytes, total_downloaded_bytes=0, total_written_bytes=0; char *download_buffer; long _buffer_size = download->buffer_size; //avviamo socket socket_version = MAKEWORD(2, 2); WSAStartup(socket_version, &socket_data); //attiva la lib winsock 2.2 //dati per la connessione ip = gethostbyname(download->host); indirizzo.sin_family = AF_INET; //tipo di indirizzo indirizzo.sin_port = htons(download->porta); //porta in cui è in ascolto server HTTP memcpy(&indirizzo.sin_addr, ip->h_addr, ip->h_length); //copia l'indirizzo nella struttura indirizzo //richiesta HTTP partial download //qui impostiamo il punto del file da cui vogliamo i dati e fino a dove li vogliamo richiesta_http = new char[10000]; sprintf(richiesta_http,"GET %s HTTP/1.1\n"\ "host: %s\n"\ "Keep-Alive: 300\n"\ "Connection: keep-alive\n"\ "Range: bytes=%d-%d\n"\ "\n\0"\ , download->host_url, download->host, (long)download->multidownload_points[id][1], (long)download->multidownload_points[id+1][0]-1); //ci connettiamo sock = socket(AF_INET, SOCK_STREAM, 0); sock_err = connect(sock, (struct sockaddr *)&indirizzo, sizeof(indirizzo)); if(!sock || sock_err != 0){ #ifdef DEBUG_MODE printf("ERROR hd_connessione_multidownload_segmento() id %d: socket: %d connect: %d\n", id, sock, sock_err); #endif download->multidownload_points[id][2] = 0; //segnaliamo che il processo si sta chiudendo return 0; } //mandiamo richiesta HTTP send(sock, richiesta_http, strlen(richiesta_http)+1, 0); //riceviamo risposta: header HTTP risposta_http = new char[10000]; x=0; while( recv(sock, &header_buffer, 1, 0) != 0 ){ risposta_http[x] = header_buffer; x++; if(risposta_http[x-1] == '\x0a' && risposta_http[x-3] == '\x0a')//controllo per vedere se l'header è finito break; } risposta_http[x] = '\0'; #ifdef DEBUG_MODE printf("id: %d\n%s\n%s\n",id,richiesta_http,risposta_http); #endif //verifichiamo se tutto ok e iniziamo a scaricare if( strncmp("206", &risposta_http[9], 3) == 0 ){ download_buffer = new char[_buffer_size]; //alloco la memoria per il buffer if(download->multidownload_points[id][1] == download->multidownload_points[id][0]) file_stream = fopen(dati->file_parziale, "wb"); //apro il file in cui salvare i dati else file_stream = fopen(dati->file_parziale, "ab"); //apro il file in cui salvare i dati //riceviamo risposta: file while (1){ //prendiamo il file un po alla volta downloaded_bytes = recv(sock, download_buffer, _buffer_size, 0); if(downloaded_bytes <= 0) break; written_bytes = fwrite(download_buffer, 1, downloaded_bytes, file_stream); //scrivo i dati total_downloaded_bytes += downloaded_bytes; total_written_bytes += written_bytes; //aggiorno la situazione nella matrice download_points download->multidownload_points[id][1] += downloaded_bytes; #ifdef DEBUG_MODE printf("hd_connessione_multidownload_segmento() id %d: d:%d p:%d\n", id, total_downloaded_bytes, (long)download->multidownload_points[id][1]); #endif if(download->status == HD_STATUS_STOPPED) //chiudiamo il processo se c'è l'ordine di stop break; if(download->status == HD_STATUS_PAUSED || download->status == HD_STATUS_SAVED) //chiudiamo il processo se siamo in pausa break; } fclose(file_stream); //chiudiamo file delete [] download_buffer; //disallochiamo buffer } else if( strncmp("503", &risposta_http[9], 3) == 0 ){ #ifdef DEBUG_MODE printf("hd_connessione_multidownload_segmento() id %d: ricevuta risposta 503, aspettiamo 10 secondi\n", id); #endif Sleep(10000); delete [] richiesta_http; delete [] risposta_http; shutdown(sock, 2); closesocket(sock); WSACleanup(); download->multidownload_points[id][2] = 0; //segnaliamo che il processo si sta chiudendo return 0; } else if( strncmp("416", &risposta_http[9], 3) == 0 ){ #ifdef DEBUG_MODE printf("ERROR hd_connessione_multidownload_segmento() id %d: ricevuta risposta 416, assumiamo di aver finito\n", id); #endif delete [] richiesta_http; delete [] risposta_http; shutdown(sock, 2); closesocket(sock); WSACleanup(); download->multidownload_points[id][2] = 2; //segnaliamo che il processo si sta chiudendo return 0; } else{ #ifdef DEBUG_MODE printf("ERROR hd_connessione_multidownload_segmento() id %d: il serve ci ha negato il download parziale 206 :(\n", id); #endif delete [] richiesta_http; delete [] risposta_http; shutdown(sock, 2); closesocket(sock); WSACleanup(); download->multidownload_points[id][2] = 0; //segnaliamo che il processo si sta chiudendo return 0; } delete [] richiesta_http; delete [] risposta_http; shutdown(sock, 2); closesocket(sock); WSACleanup(); if(download->multidownload_points[id][1] >= download->multidownload_points[id+1][0]-1){ download->multidownload_points[id][2] = 2; //segnaliamo che il processo ha scaricato tutto } else download->multidownload_points[id][2] = 0; //segnaliamo che il processo si sta chiudendo return 1; } int hd_multidownload_files_assembler(http_download *download, partial_download_data *dati){ long x; FILE *f_stream; FILE *pf_stream; char *pf_name; char *copy_buffer; long bytes_copied, total_bytes_copied=0, ps_size; pf_name = new char[10000]; copy_buffer = new char[download->buffer_size]; f_stream = fopen(download->file,"wb"); //apriamo file generale //apriamo i file parziali uno ad uno e li copiamo nel file generale for(x=0; x<download->multidownload ;x++){ pf_stream = fopen(dati[x].file_parziale, "rb"); if(pf_stream == NULL) return 0; ps_size = (long)(download->multidownload_points[x+1][0]-download->multidownload_points[x][0]); //grandezza segmento total_bytes_copied=0; while(1){ bytes_copied = fread(copy_buffer,1,download->buffer_size,pf_stream); if(bytes_copied<=0) break; if(total_bytes_copied+bytes_copied > ps_size) fwrite(copy_buffer, 1, ps_size-total_bytes_copied, f_stream); else fwrite(copy_buffer, 1, bytes_copied, f_stream); total_bytes_copied += bytes_copied; } fclose(pf_stream); } fclose(f_stream); delete [] pf_name; delete [] copy_buffer; return 1; } int hd_is_file(char *pathname){ FILE *file; file = fopen(pathname, "rb"); if(file == NULL) return 0; fclose(file); return 1; }