#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("ERROR 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("ERROR 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: HD_STATUS_PROTOCOL_NOT_HTTP11\n"); #endif
return 0;
}
if(status == HD_STATUS_FILE_NOT_FOUND){
#ifdef DEBUG_MODE
printf("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: HD_STATUS_HTTP_CODE_NOT_RECOGNIZED\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("stop(): non puo essere usato se non si sta scaricando\n",file_size
); #endif
return 0;
}
}
int http_download::pause(){
if(status != HD_STATUS_MULTIDOWNLOADING){
#ifdef DEBUG_MODE
printf("pause(): puo essere usata solo in stato HD_STATUS_MULTIDOWNLOADING\n",file_size
); #endif
return 0;
}
status = HD_STATUS_PAUSED;
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("resume(): puo essere usata solo in stato HD_STATUS_PAUSED\n",file_size
); #endif
return 0;
}
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("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("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("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("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("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("ERROR print(): non puoi eseguire a HD_STATUS_UNINITIALIZED.\n"); #endif
return 0;
}
printf("host_url: %s\n", host_url
); 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("ERROR 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
#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("ERROR 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
#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("ERROR 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
#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;
}