summaryrefslogtreecommitdiff
path: root/src/htsback.c
diff options
context:
space:
mode:
authorXavier Roche <xroche@users.noreply.github.com>2012-03-19 12:36:11 +0000
committerXavier Roche <xroche@users.noreply.github.com>2012-03-19 12:36:11 +0000
commitad5b7acc19290ff91e0f42a0de448a26760fcf99 (patch)
tree2d1867758835fd0c4e443ff3cc7e5c774af85874 /src/htsback.c
Imported httrack 3.20.2
Diffstat (limited to 'src/htsback.c')
-rw-r--r--src/htsback.c2462
1 files changed, 2462 insertions, 0 deletions
diff --git a/src/htsback.c b/src/htsback.c
new file mode 100644
index 0000000..d99564f
--- /dev/null
+++ b/src/htsback.c
@@ -0,0 +1,2462 @@
+/* ------------------------------------------------------------ */
+/*
+HTTrack Website Copier, Offline Browser for Windows and Unix
+Copyright (C) Xavier Roche and other contributors
+
+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 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+Important notes:
+
+- We hereby ask people using this source NOT to use it in purpose of grabbing
+emails addresses, or collecting any other private information on persons.
+This would disgrace our work, and spoil the many hours we spent on it.
+
+
+Please visit our Website: http://www.httrack.com
+*/
+
+
+/* ------------------------------------------------------------ */
+/* File: httrack.c subroutines: */
+/* backing system (multiple socket download) */
+/* Author: Xavier Roche */
+/* ------------------------------------------------------------ */
+
+#include "htsback.h"
+
+/* specific definitions */
+#include "htsbase.h"
+#include "htsnet.h"
+#include "htsthread.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+/* END specific definitions */
+
+//#if HTS_WIN
+#include "htsftp.h"
+#if HTS_USEZLIB
+#include "htszlib.h"
+#endif
+//#endif
+
+#if HTS_WIN
+#ifndef __cplusplus
+// DOS
+#include <process.h> /* _beginthread, _endthread */
+#endif
+#else
+#endif
+
+#undef test_flush
+#define test_flush if (opt->flush) { if (opt->log) { fflush(opt->log); } if (opt->errlog) { fflush(opt->errlog); } }
+
+#define VT_CLREOL "\33[K"
+
+
+// ---
+// routines de backing
+// retourne l'index d'un lien dans un tableau de backing
+int back_index(lien_back* back,int back_max,char* adr,char* fil,char* sav) {
+ int i=0;
+ int index=-1;
+ while( i<back_max ) {
+ if (back[i].status>=0) // réception OU prêt
+ if (strfield2(back[i].url_adr,adr)) {
+ if (strcmp(back[i].url_fil,fil)==0) {
+ if (index==-1) /* first time we meet, store it */
+ index=i;
+ else if (strcmp(back[i].url_sav,sav)==0) { /* oops, check sav too */
+ index=i;
+ return index;
+ }
+ }
+ }
+ i++;
+ }
+ return index;
+}
+
+// nombre d'entrées libres dans le backing
+int back_available(lien_back* back,int back_max) {
+ int i;
+ int nb=0;
+ for(i=0;i<back_max;i++)
+ if (back[i].status==-1) /* libre */
+ nb++;
+ return nb;
+}
+
+// retourne estimation de la taille des html et fichiers stockés en mémoire
+LLint back_incache(lien_back* back,int back_max) {
+ int i;
+ LLint sum=0;
+ for(i=0;i<back_max;i++)
+ if (back[i].status!=-1)
+ if (back[i].r.adr) // ne comptabilier que les blocs en mémoire
+ sum+=max(back[i].r.size,back[i].r.totalsize);
+ return sum;
+}
+
+// le lien a-t-il été mis en backing?
+HTS_INLINE int back_exist(lien_back* back,int back_max,char* adr,char* fil,char* sav) {
+ return (back_index(back,back_max,adr,fil,sav)>=0);
+}
+
+// nombre de sockets en tâche de fond
+int back_nsoc(lien_back* back,int back_max) {
+ int n=0;
+ int i;
+ for(i=0;i<back_max;i++)
+ if (back[i].status>0) // réception uniquement
+ n++;
+
+ return n;
+}
+
+// objet (lien) téléchargé ou transféré depuis le cache
+//
+// fermer les paramètres de transfert,
+// et notamment vérifier les fichiers compressés (décompresser), callback etc.
+int back_finalize(httrackp* opt,cache_back* cache,lien_back* back,int p) {
+ if (
+ (back[p].status == 0) // ready
+ &&
+ (!back[p].testmode) // not test mode
+ &&
+ (back[p].r.statuscode>0) // not internal error
+ ) {
+ char* state="unknown";
+
+ /* décompression */
+#if HTS_USEZLIB
+ if (back[p].r.compressed) {
+ if (back[p].r.size > 0) {
+ //if ( (back[p].r.adr) && (back[p].r.size>0) ) {
+ // stats
+ back[p].compressed_size=back[p].r.size;
+ // en mémoire -> passage sur disque
+ if (!back[p].r.is_write) {
+ back[p].tmpfile[0]='\0';
+ strcpy(back[p].tmpfile,tempnam(NULL,"httrz"));
+ if (back[p].tmpfile[0]) {
+ back[p].r.out=fopen(back[p].tmpfile,"wb");
+ if (back[p].r.out) {
+ if ((back[p].r.adr) && (back[p].r.size>0)) {
+ if ((INTsys)fwrite(back[p].r.adr,1,(INTsys)back[p].r.size,back[p].r.out) != back[p].r.size) {
+ back[p].r.statuscode=-1;
+ strcpy(back[p].r.msg,"Write error when decompressing");
+ }
+ } else {
+ back[p].tmpfile[0]='\0';
+ back[p].r.statuscode=-1;
+ strcpy(back[p].r.msg,"Empty compressed file");
+ }
+ } else {
+ back[p].tmpfile[0]='\0';
+ back[p].r.statuscode=-1;
+ strcpy(back[p].r.msg,"Open error when decompressing");
+ }
+ }
+ }
+ // fermer fichier sortie
+ if (back[p].r.out!=NULL) {
+ fclose(back[p].r.out);
+ back[p].r.out=NULL;
+ }
+ // décompression
+ if (back[p].tmpfile[0] && back[p].url_sav[0]) {
+ LLint size;
+ filecreateempty(back[p].url_sav); // filenote & co
+ if ((size = hts_zunpack(back[p].tmpfile,back[p].url_sav))>=0) {
+ back[p].r.size=back[p].r.totalsize=size;
+ // fichier -> mémoire
+ if (!back[p].r.is_write) {
+ back[p].r.adr=readfile(back[p].url_sav);
+ if (!back[p].r.adr) {
+ back[p].r.statuscode=-1;
+ strcpy(back[p].r.msg,"Read error when decompressing");
+ }
+ remove(back[p].url_sav);
+ }
+ }
+ remove(back[p].tmpfile);
+ }
+ // stats
+ HTS_STAT.total_packed+=back[p].compressed_size;
+ HTS_STAT.total_unpacked+=back[p].r.size;
+ HTS_STAT.total_packedfiles++;
+ // unflag
+ }
+ }
+ back[p].r.compressed=0;
+#endif
+
+ /* Stats */
+ if (cache->txt) {
+ char flags[32];
+ char s[256];
+ time_t tt;
+ struct tm* A;
+ tt=time(NULL);
+ A=localtime(&tt);
+ strftime(s,250,"%H:%M:%S",A);
+
+ flags[0]='\0';
+ /* input flags */
+ if (back[p].is_update)
+ strcat(flags, "U"); // update request
+ else
+ strcat(flags, "-");
+ if (back[p].range_req_size)
+ strcat(flags, "R"); // range request
+ else
+ strcat(flags, "-");
+ /* state flags */
+ if (back[p].r.is_file) // direct to disk
+ strcat(flags, "F");
+ else
+ strcat(flags, "-");
+ /* output flags */
+ if (!back[p].r.notmodified)
+ strcat(flags, "M"); // modified
+ else
+ strcat(flags, "-");
+ if (back[p].r.is_chunk) // chunked
+ strcat(flags, "C");
+ else
+ strcat(flags, "-");
+ if (back[p].r.compressed)
+ strcat(flags, "Z"); // gzip
+ else
+ strcat(flags, "-");
+ fprintf(cache->txt,"%s\t"LLintP"/"LLintP"\t%s\t", s,
+ back[p].r.size, back[p].r.totalsize,
+ flags);
+ }
+ if (back[p].r.statuscode==200) {
+ if (back[p].r.size>=0) {
+ if (strcmp(back[p].url_fil,"/robots.txt") !=0 ) {
+ HTS_STAT.stat_bytes+=back[p].r.size;
+ HTS_STAT.stat_files++;
+ }
+ if ( (!back[p].r.notmodified) && (opt->is_update) ) {
+ HTS_STAT.stat_updated_files++; // page modifiée
+ if (opt->log!=NULL) {
+ fspc(opt->log,"info");
+ if (back[p].is_update) {
+ fprintf(opt->log,"engine: transfer-status: link updated: %s%s -> %s"LF,back[p].url_adr,back[p].url_fil,back[p].url_sav);
+ } else {
+ fprintf(opt->log,"engine: transfer-status: link added: %s%s -> %s"LF,back[p].url_adr,back[p].url_fil,back[p].url_sav);
+ }
+ test_flush;
+ }
+ if (cache->txt) {
+ if (back[p].is_update) {
+ state="updated";
+ } else {
+ state="added";
+ }
+ }
+ } else {
+ if ( (opt->debug>0) && (opt->log!=NULL) ) {
+ fspc(opt->log,"info"); fprintf(opt->log,"engine: transfer-status: link recorded: %s%s -> %s"LF,back[p].url_adr,back[p].url_fil,back[p].url_sav);
+ test_flush;
+ }
+ if (cache->txt) {
+ if (opt->is_update)
+ state="untouched";
+ else
+ state="added";
+ }
+ }
+ } else {
+ if ( (opt->debug>0) && (opt->log!=NULL) ) {
+ fspc(opt->log,"info"); fprintf(opt->log,"engine: transfer-status: empty file? (%d, '%s'): %s%s"LF,back[p].r.statuscode,back[p].r.msg,back[p].url_adr,back[p].url_fil);
+ test_flush;
+ }
+ if (cache->txt) {
+ state="empty";
+ }
+ }
+ } else {
+ if ( (opt->debug>0) && (opt->log!=NULL) ) {
+ fspc(opt->log,"info"); fprintf(opt->log,"engine: transfer-status: link error (%d, '%s'): %s%s"LF,back[p].r.statuscode,back[p].r.msg,back[p].url_adr,back[p].url_fil);
+ }
+ if (cache->txt) {
+ state="error";
+ }
+ }
+ if (cache->txt) {
+ fprintf(cache->txt,
+ "%d\t"
+ "%s ('%s')\t"
+ "%s\t"
+ "%s%s\t"
+ "%s%s\t%s\t"
+ "(from %s%s)"
+ LF,
+ back[p].r.statuscode,
+ state, escape_check_url_addr(back[p].r.msg),
+ escape_check_url_addr(back[p].r.contenttype),
+ ((back[p].r.etag[0])?"etag:":((back[p].r.lastmodified[0])?"date:":"")), escape_check_url_addr((back[p].r.etag[0])?back[p].r.etag:(back[p].r.lastmodified)),
+ escape_check_url_addr(back[p].url_adr),escape_check_url_addr(back[p].url_fil),escape_check_url_addr(back[p].url_sav),
+ escape_check_url_addr(back[p].referer_adr),escape_check_url_addr(back[p].referer_fil)
+ );
+ if (opt->flush)
+ fflush(cache->txt);
+ }
+
+ /* Cache */
+ cache_mayadd(opt,cache,&back[p].r,back[p].url_adr,back[p].url_fil,back[p].url_sav);
+
+ // status finished callback
+#if HTS_ANALYSTE
+ hts_htmlcheck_xfrstatus(&back[p]);
+#endif
+ return 0;
+ }
+ return -1;
+}
+
+
+// effacer entrée
+int back_delete(lien_back* back,int p) {
+ if (p>=0) { // on sait jamais..
+ // Vérificateur d'intégrité
+ #if DEBUG_CHECKINT
+ _CHECKINT(&back[p],"Appel back_delete")
+ #endif
+#if HTS_DEBUG_CLOSESOCK
+ char info[256];
+ sprintf(info,"back_delete: #%d\n",p);
+ DEBUG_W2(info);
+#endif
+
+ // Libérer tous les sockets, handles, buffers..
+ if (back[p].r.soc!=INVALID_SOCKET) {
+#if HTS_DEBUG_CLOSESOCK
+ DEBUG_W("back_delete: deletehttp\n");
+#endif
+ deletehttp(&back[p].r);
+ back[p].r.soc=INVALID_SOCKET;
+ }
+
+#if HTS_USEOPENSSL
+ /* Free OpenSSL structures */
+ if (back[p].r.ssl_con) {
+ SSL_shutdown(back[p].r.ssl_con);
+ SSL_free(back[p].r.ssl_con);
+ back[p].r.ssl_con=NULL;
+ }
+ /*
+ if (back[p].r.ssl_soc) {
+ BIO_free_all(back[p].r.ssl_soc);
+ back[p].r.ssl_soc=NULL;
+ }
+ */
+#endif
+
+ if (back[p].r.adr!=NULL) { // reste un bloc à désallouer
+ freet(back[p].r.adr);
+ back[p].r.adr=NULL;
+ }
+ if (back[p].chunk_adr!=NULL) { // reste un bloc à désallouer
+ freet(back[p].chunk_adr);
+ back[p].chunk_adr=NULL;
+ back[p].chunk_size=0;
+ back[p].is_chunk=0;
+ }
+ // if (back[p].r.is_file) { // fermer fichier entrée
+ if (back[p].r.fp!=NULL) {
+ fclose(back[p].r.fp);
+ back[p].r.fp=NULL;
+ }
+ // }
+
+ /* fichier de sortie */
+ if (back[p].r.out!=NULL) { // fermer fichier sortie
+ fclose(back[p].r.out);
+ back[p].r.out=NULL;
+ }
+
+ if (back[p].r.is_write) { // ecriture directe
+ /* écrire date "remote" */
+ if (strnotempty(back[p].url_sav)) // normalement existe si on a un fichier de sortie
+ if (strnotempty(back[p].r.lastmodified)) // last-modified existe
+ if (fexist(back[p].url_sav)) // ainsi que le fichier
+ set_filetime_rfc822(back[p].url_sav,back[p].r.lastmodified);
+
+ /* executer commande utilisateur après chargement du fichier */
+ usercommand(0,NULL,back[p].url_sav);
+ back[p].r.is_write=0;
+ }
+
+ // Tout nettoyer
+ memset(&back[p], 0, sizeof(lien_back));
+ back[p].r.soc=INVALID_SOCKET; back[p].r.location=back[p].location_buffer;
+
+ // Le plus important: libérer le champ
+ back[p].status=-1;
+ }
+ return 0;
+}
+
+/* Space left on backing stack */
+int back_stack_available(lien_back* back,int back_max) {
+ int p=0,n=0;
+ for( ; p < back_max ; p++ )
+ if ( back[p].status == -1 )
+ n++;
+ return n;
+}
+
+// ajouter un lien en backing
+int back_add(lien_back* back,int back_max,httrackp* opt,cache_back* cache,char* adr,char* fil,char* save,char* referer_adr,char* referer_fil,int test,short int* pass2_ptr) {
+ int p=0;
+
+ // vérifier cohérence de adr et fil (non vide!)
+ if (strnotempty(adr)==0) {
+ if ((opt->debug>1) && (opt->errlog!=NULL)) {
+ fspc(opt->errlog,"debug"); fprintf(opt->errlog,"error: adr is empty for back_add"LF);
+ }
+ return -1; // erreur!
+ }
+ if (strnotempty(fil)==0) {
+ if ((opt->debug>1) && (opt->errlog!=NULL)) {
+ fspc(opt->errlog,"debug"); fprintf(opt->errlog,"error: fil is empty for back_add"LF);
+ }
+ return -1; // erreur!
+ }
+ // FIN vérifier cohérence de adr et fil (non vide!)
+
+ // rechercher emplacement
+ while((p<back_max) && back[p].status!=-1) p++;
+ if (back[p].status==-1) { // ok on a de la place
+ back[p].send_too[0]='\0'; // éventuels paramètres supplémentaires à transmettre au serveur
+
+ // ne sert à rien normalement
+ if (back[p].r.soc!=INVALID_SOCKET) {
+#if HTS_DEBUG_CLOSESOCK
+ DEBUG_W("back_add: deletehttp\n");
+#endif
+ deletehttp(&back[p].r);
+ }
+
+ // effacer r
+ memset(&(back[p].r), 0, sizeof(htsblk)); back[p].r.soc=INVALID_SOCKET; back[p].r.location=back[p].location_buffer;
+
+ // créer entrée
+ strcpy(back[p].url_adr,adr);
+ strcpy(back[p].url_fil,fil);
+ strcpy(back[p].url_sav,save);
+ back[p].pass2_ptr=pass2_ptr;
+ // copier referer si besoin
+ strcpy(back[p].referer_adr,"");
+ strcpy(back[p].referer_fil,"");
+ if ((referer_adr) && (referer_fil)) { // existe
+ if ((strnotempty(referer_adr)) && (strnotempty(referer_fil))) { // non vide
+ if (referer_adr[0]!='!') { // non détruit
+ if (strcmp(referer_adr,"file://")) { // PAS file://
+ if (strcmp(referer_adr,"primary")) { // pas referer 1er lien
+ strcpy(back[p].referer_adr,referer_adr);
+ strcpy(back[p].referer_fil,referer_fil);
+ }
+ }
+ }
+ }
+ }
+ // sav ne sert à rien pour le moment
+ back[p].r.size=0; // rien n'a encore été chargé
+ back[p].r.soc=INVALID_SOCKET; // pas de socket
+ back[p].r.adr=NULL; // pas de bloc de mémoire
+ back[p].r.is_write=0; // à priori stockage en mémoire
+ back[p].maxfile_html=opt->maxfile_html;
+ back[p].maxfile_nonhtml=opt->maxfile_nonhtml;
+ back[p].testmode=test; // mode test?
+ if (!opt->http10) // option "forcer 1.0" désactivée
+ back[p].http11=1; // autoriser http/1.1
+ back[p].head_request=0;
+ if (strcmp(back[p].url_sav,BACK_ADD_TEST)==0) // HEAD
+ back[p].head_request=1;
+ else if (strcmp(back[p].url_sav,BACK_ADD_TEST2)==0) // test en GET
+ back[p].head_request=2; // test en get
+
+
+ /* Stop requested - abort backing */
+ if (opt->state.stop) {
+ back[p].r.statuscode=-1; // fatal
+ strcpy(back[p].r.msg,"mirror stopped by user");
+ back[p].status=0; // terminé
+ if ((opt->debug>0) && (opt->log!=NULL)) {
+ fspc(opt->log,"warning"); fprintf(opt->log,"File not added due to mirror cancel: %s%s"LF,adr,fil); test_flush;
+ }
+ return 0;
+ }
+
+
+ // tester cache
+ if ((strcmp(adr,"file://")) /* pas fichier */
+ && ( (!test) || (cache->type==1) ) /* cache prioritaire, laisser passer en test! */
+ && ( (strnotempty(save)) || (strcmp(fil,"/robots.txt")==0) ) ) { // si en test on ne doit pas utiliser le cache sinon telescopage avec le 302..
+ //if ((!test) && (strcmp(adr,"file://"))
+ //if ((!test) && (strncmp(adr,"ftp://",6)) && (strcmp(adr,"file://"))
+#if HTS_FAST_CACHE
+ long int hash_pos;
+ int hash_pos_return=0;
+#else
+ char* a=NULL;
+#endif
+#if HTS_FAST_CACHE
+ if (cache->hashtable) {
+#else
+ if (cache->use) {
+#endif
+ char buff[HTS_URLMAXSIZE*4];
+#if HTS_FAST_CACHE
+ strcpy(buff,adr); strcat(buff,fil);
+ hash_pos_return=inthash_read((inthash)cache->hashtable,buff,(long int*)&hash_pos);
+#else
+ buff[0]='\0'; strcat(buff,"\n"); strcat(buff,adr); strcat(buff,"\n"); strcat(buff,fil); strcat(buff,"\n");
+ a=strstr(cache->use,buff);
+#endif
+
+ // Ok, noté en cache->. mais bien présent dans le cache ou sur disque?
+#if HTS_FAST_CACHE
+ if (hash_pos_return) {
+#else
+ if (a) {
+#endif
+ if (!test) { // non mode test
+#if HTS_FAST_CACHE
+ int pos=hash_pos;
+#else
+ int pos=-1;
+ a+=strlen(buff);
+ sscanf(a,"%d",&pos); // lire position
+#endif
+ if (pos<0) { // pas de mise en cache data, vérifier existence
+ if (fsize(antislash(save)) <= 0) { // fichier existe pas ou est vide!
+#if HTS_FAST_CACHE
+ hash_pos_return=0;
+#else
+ a=NULL;
+#endif
+ // dévalider car non présent sur disque dans structure originale!!!
+ // sinon, le fichier est ok à priori, mais on renverra un if-modified-since pour
+ // en être sûr
+ if (opt->norecatch) { // tester norecatch
+ if (!fexist(antislash(save))) { // fichier existe pas mais déclaré: on l'a effacé
+ FILE* fp=fopen(antislash(save),"wb");
+ if (fp) fclose(fp);
+ if (opt->log!=NULL) {
+ fspc(opt->log,"warning"); fprintf(opt->log,"File must have been erased by user, ignoring: %s%s"LF,back[p].url_adr,back[p].url_fil); test_flush;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ //
+ } else
+#if HTS_FAST_CACHE
+ hash_pos_return=0;
+#else
+ a=NULL;
+#endif
+
+ // Existe pas en cache, ou bien pas de cache présent
+#if HTS_FAST_CACHE
+ if (hash_pos_return) { // OK existe en cache (et données aussi)!
+#else
+ if (a!=NULL) { // OK existe en cache (et données aussi)!
+#endif
+ if (cache->type==1) { // cache prioritaire (pas de test if-modified..)
+ // dans ce cas on peut également lire des réponses cachées comme 404,302...
+ // lire dans le cache
+ if (!test)
+ back[p].r=cache_read(opt,cache,adr,fil,save);
+ else
+ back[p].r=cache_read(opt,cache,adr,fil,NULL); // charger en tête uniquement du cache
+ if (!back[p].r.location)
+ back[p].r.location=back[p].location_buffer;
+ else { /* recopier */
+ strcpy(back[p].location_buffer,back[p].r.location);
+ back[p].r.location=back[p].location_buffer;
+ }
+
+ /* Interdiction taille par le wizard? --> détruire */
+ if (back[p].r.statuscode != -1) { // pas d'erreur de lecture
+ if (!back_checksize(opt,&back[p],0)) {
+ back[p].status=0; // FINI
+ back[p].r.statuscode=-1;
+ if (!back[p].testmode)
+ strcpy(back[p].r.msg,"Cached file skipped (too big)");
+ else
+ strcpy(back[p].r.msg,"Test: Cached file skipped (too big)");
+ return 0;
+ }
+ }
+
+ if (back[p].r.statuscode != -1) { // pas d'erreur de lecture
+ if ((opt->debug>0) && (opt->log!=NULL)) {
+ if (!test) {
+ fspc(opt->log,"debug"); fprintf(opt->log,"File immediately loaded from cache: %s%s"LF,back[p].url_adr,back[p].url_fil); test_flush;
+ } else {
+ fspc(opt->log,"debug"); fprintf(opt->log,"File immediately tested from cache: %s%s"LF,back[p].url_adr,back[p].url_fil); test_flush;
+ }
+ }
+ back[p].r.notmodified=1; // fichier non modifié
+ back[p].status=0; // OK prêt
+
+ // finalize transfer
+ if (!test) {
+ if (back[p].r.statuscode>0) {
+ back_finalize(opt,cache,back,p);
+ }
+ }
+
+ return 0;
+ } else { // erreur
+ // effacer r
+ memset(&(back[p].r), 0, sizeof(htsblk)); back[p].r.soc=INVALID_SOCKET; back[p].r.location=back[p].location_buffer;
+ // et continuer (chercher le fichier)
+ }
+
+ } else if (cache->type==2) { // si en cache, demander de tester If-Modified-Since
+ htsblk* r=cache_header(opt,cache,adr,fil);
+
+ /* Interdiction taille par le wizard? */
+ {
+ LLint save_totalsize=back[p].r.totalsize;
+ back[p].r.totalsize=r->totalsize;
+ if (!back_checksize(opt,&back[p],1)) {
+ r=NULL;
+ //
+ back[p].status=0; // FINI
+ deletehttp(&back[p].r); back[p].r.soc=INVALID_SOCKET;
+ if (!back[p].testmode)
+ strcpy(back[p].r.msg,"File too big");
+ else
+ strcpy(back[p].r.msg,"Test: File too big");
+ return 0;
+ }
+ back[p].r.totalsize=save_totalsize;
+ }
+
+ if (r) {
+ if (r->statuscode==200) { // uniquement des 200 (OK)
+ if (strnotempty(r->etag)) { // ETag (RFC2616)
+ /*
+ - If both an entity tag and a Last-Modified value have been
+ provided by the origin server, SHOULD use both validators in
+ cache-conditional requests. This allows both HTTP/1.0 and
+ HTTP/1.1 caches to respond appropriately.
+ */
+ if (strnotempty(r->lastmodified))
+ sprintf(back[p].send_too,"If-None-Match: %s\r\nIf-Modified-Since: %s\r\n",r->etag,r->lastmodified);
+ else
+ sprintf(back[p].send_too,"If-None-Match: %s\r\n",r->etag);
+ }
+ else if (strnotempty(r->lastmodified))
+ sprintf(back[p].send_too,"If-Modified-Since: %s\r\n",r->lastmodified);
+ else if (strnotempty(cache->lastmodified))
+ sprintf(back[p].send_too,"If-Modified-Since: %s\r\n",cache->lastmodified);
+
+ /* this is an update of a file */
+ if (strnotempty(back[p].send_too))
+ back[p].is_update=1;
+ back[p].r.req.nocompression=1; /* Do not compress when updating! */
+
+ }
+ /* else if (strnotempty(cache->lastmodified))
+ sprintf(back[p].send_too,"If-Modified-Since: %s\r\n",cache->lastmodified);
+ */
+ }
+#if DEBUGCA
+ printf("..is modified test %s\n",back[p].send_too);
+#endif
+ }
+ // Okay, pas trouvé dans le cache
+ // Et si le fichier existe sur disque?
+ // Pas dans le cache: fichier n'a pas été transféré du tout, donc pas sur disque?
+ } else {
+ if (fexist(save)) { // fichier existe? aghl!
+ LLint sz=fsize(save);
+ // Bon, là il est possible que le fichier ait été partiellement transféré
+ // (s'il l'avait été en totalité il aurait été inscrit dans le cache ET existerait sur disque)
+ // PAS de If-Modified-Since, on a pas connaissance des données à la date du cache
+ // On demande juste les données restantes si le date est valide (206), tout sinon (200)
+ if ((ishtml(save) != 1) && (ishtml(back[p].url_fil)!=1)) { // NON HTML (liens changés!!)
+ if (sz>0) { // Fichier non vide? (question bête, sinon on transfert tout!)
+ if (strnotempty(cache->lastmodified)) { /* pas de If-.. possible */
+ /*if ( (!opt->http10) && (strnotempty(cache->lastmodified)) ) { */ /* ne pas forcer 1.0 */
+#if DEBUGCA
+ printf("..if unmodified since %s size "LLintP"\n",cache->lastmodified,(LLint)sz);
+#endif
+ if ((opt->debug>1) && (opt->log!=NULL)) {
+ fspc(opt->log,"debug"); fprintf(opt->log,"File partially present ("LLintP" bytes): %s%s"LF,(LLint)sz,back[p].url_adr,back[p].url_fil); test_flush;
+ }
+
+ /* impossible - don't have etag or date
+ if (strnotempty(back[p].r.etag)) { // ETag (RFC2616)
+ sprintf(back[p].send_too,"If-None-Match: %s\r\n",back[p].r.etag);
+ back[p].http11=1; // En tête 1.1
+ } else if (strnotempty(back[p].r.lastmodified)) {
+ sprintf(back[p].send_too,"If-Unmodified-Since: %s\r\n",back[p].r.lastmodified);
+ back[p].http11=1; // En tête 1.1
+ } else
+ */
+ if (strlen(cache->lastmodified)) {
+ sprintf(back[p].send_too,
+ "If-Unmodified-Since: %s\r\nRange: bytes="LLintP"-\r\n"
+ ,cache->lastmodified,(LLint)sz);
+ back[p].http11=1; // En tête 1.1
+ back[p].range_req_size=sz;
+ back[p].r.req.range_used=1;
+ back[p].r.req.nocompression=1;
+ } else {
+ if ((opt->debug>0) && (opt->log!=NULL)) {
+ fspc(opt->log,"warning"); fprintf(opt->log,"Could not find timestamp for partially present file, restarting (lost "LLintP" bytes): %s%s"LF,(LLint)sz,back[p].url_adr,back[p].url_fil); test_flush;
+ }
+ }
+
+ } else {
+ if ((opt->debug>0) && (opt->errlog!=NULL)) {
+ fspc(opt->errlog,"warning");
+ /*
+ if (opt->http10)
+ fprintf(opt->errlog,"File partially present (%d bytes) retransfered due to HTTP/1.0 settings: %s%s"LF,sz,back[p].url_adr,back[p].url_fil);
+ else
+ */
+ fprintf(opt->errlog,"File partially present ("LLintP" bytes) retransfered due to lack of cache: %s%s"LF,(LLint)sz,back[p].url_adr,back[p].url_fil);
+ test_flush;
+ }
+ /* Sinon requête normale... */
+ back[p].http11=0;
+ }
+ } else if (opt->norecatch) { // tester norecatch
+ filenote(save,NULL); // ne pas purger tout de même
+ back[p].status=0; // OK prêt
+ back[p].r.statuscode=-1; // erreur
+ strcpy(back[p].r.msg,"Null-size file not recaught");
+ return 0;
+ }
+ } else {
+ if ((opt->debug>0) && (opt->errlog!=NULL)) {
+ fspc(opt->errlog,"warning");
+ fprintf(opt->errlog,"HTML file ("LLintP" bytes) retransfered due to lack of cache: %s%s"LF,(LLint)sz,back[p].url_adr,back[p].url_fil);
+ test_flush;
+ }
+ /* Sinon requête normale... */
+ back[p].http11=0;
+ }
+ }
+ }
+ }
+
+
+ {
+ ///htsblk r; non directement dans la structure-réponse!
+ T_SOC soc;
+
+ // ouvrir liaison, envoyer requète
+ // ne pas traiter ou recevoir l'en tête immédiatement
+ memset(&(back[p].r), 0, sizeof(htsblk)); back[p].r.soc=INVALID_SOCKET; back[p].r.location=back[p].location_buffer;
+ // recopier proxy
+ memcpy(&(back[p].r.req.proxy), &opt->proxy, sizeof(opt->proxy));
+ // et user-agent
+ strcpy(back[p].r.req.user_agent,opt->user_agent);
+ strcpy(back[p].r.req.lang_iso,opt->lang_iso);
+ back[p].r.req.user_agent_send=opt->user_agent_send;
+ // et http11
+ back[p].r.req.http11=back[p].http11;
+ back[p].r.req.nocompression=opt->nocompression;
+
+ // mode ftp, court-circuit!
+ if (strfield(back[p].url_adr,"ftp://")) {
+ if (back[p].testmode) {
+ if ((opt->debug>1) && (opt->errlog!=NULL)) {
+ fspc(opt->errlog,"debug"); fprintf(opt->errlog,"error: forbidden test with ftp link for back_add"LF);
+ }
+ return -1; // erreur pas de test permis
+ }
+ if (!(back[p].r.req.proxy.active && opt->ftp_proxy)) { // connexion directe, gérée en thread
+ back[p].status=1000; // connexion ftp
+#if USE_BEGINTHREAD
+ launch_ftp(&(back[p]));
+#else
+ {
+ char nid[32];
+ sprintf(nid,"htsftp%d-in_progress.lock",p);
+ strcpy(back[p].location_buffer,fconcat(opt->path_log,nid));
+ }
+ launch_ftp(&(back[p]),back[p].location_buffer,opt->exec);
+#endif
+ return 0;
+ }
+ }
+#if HTS_USEOPENSSL
+ else if (strfield(back[p].url_adr,"https://")) { // let's rock
+ back[p].r.ssl = 1;
+ // back[p].r.ssl_soc = NULL;
+ back[p].r.ssl_con = NULL;
+ }
+#endif
+
+#if HTS_XGETHOST
+#if HDEBUG
+ printf("back_solve..\n");
+#endif
+ back[p].status=101; // tentative de résolution du nom de host
+ soc=INVALID_SOCKET; // pas encore ouverte
+ back_solve(&back[p]); // préparer
+ if (host_wait(&back[p])) { // prêt, par ex fichier ou dispo dans dns
+#if HDEBUG
+ printf("ok, dns cache ready..\n");
+#endif
+ soc=http_xfopen(0,0,0,back[p].send_too,adr,fil,&(back[p].r));
+ if (soc==INVALID_SOCKET) {
+ back[p].status=0; // fini, erreur
+ }
+ }
+//
+#else
+//
+#if CNXDEBUG
+ printf("XFopen..\n");
+#endif
+
+ if (strnotempty(back[p].send_too)) // envoyer un if-modified-since
+#if HTS_XCONN
+ soc=http_xfopen(0,0,0,back[p].send_too,adr,fil,&(back[p].r));
+#else
+ soc=http_xfopen(0,0,1,back[p].send_too,adr,fil,&(back[p].r));
+#endif
+ else
+#if HTS_XCONN
+ soc=http_xfopen(test,0,0,NULL,adr,fil,&(back[p].r));
+#else
+ soc=http_xfopen(test,0,1,NULL,adr,fil,&(back[p].r));
+#endif
+#endif
+ if (opt->timeout>0) { // gestion du opt->timeout
+ back[p].timeout=opt->timeout;
+ back[p].timeout_refresh=time_local();
+ } else {
+ back[p].timeout=-1; // pas de gestion (default)
+ }
+
+ if (opt->rateout>0) { // gestion d'un taux minimum de transfert toléré
+ back[p].rateout=opt->rateout;
+ back[p].rateout_time=time_local();
+ } else {
+ back[p].rateout=-1; // pas de gestion (default)
+ }
+
+ // Note: on charge les code-page erreurs (erreur 404, etc) dans le cas où cela est
+ // rattrapable (exemple: 301,302 moved xxx -> refresh sur la page!)
+ //if ((back[p].statuscode!=200) || (soc<0)) { // ERREUR HTTP/autre
+
+#if CNXDEBUG
+printf("Xfopen ok, poll..\n");
+#endif
+
+#if HTS_XGETHOST
+ if (soc!=INVALID_SOCKET)
+ if (back[p].status==101) { // pas d'erreur
+ if (!back[p].r.is_file)
+ back[p].status=100; // connexion en cours
+ else
+ back[p].status=1; // fichier
+ }
+
+#else
+ if (soc==INVALID_SOCKET) { // erreur socket
+ back[p].status=0; // FINI
+ //if (back[p].soc!=INVALID_SOCKET) deletehttp(back[p].soc);
+ back[p].r.soc=INVALID_SOCKET;
+ } else {
+ if (!back[p].r.is_file)
+#if HTS_XCONN
+ back[p].status=100; // connexion en cours
+#else
+ back[p].status=99; // chargement en tête en cours
+#endif
+ else
+ back[p].status=1; // chargement fichier
+#if BDEBUG==1
+ printf("..loading header\n");
+#endif
+ }
+#endif
+
+ }
+
+
+ // note: si il y a erreur (404,etc) status=2 (terminé/échec) mais
+ // le lien est considéré comme traité
+ //if (back[p].soc<0) // erreur
+ // return -1;
+
+ return 0;
+ } else {
+ if ((opt->debug>1) && (opt->errlog!=NULL)) {
+ fspc(opt->errlog,"debug"); fprintf(opt->errlog,"error: no space left in stack for back_add"LF);
+ }
+ return -1; // plus de place
+ }
+}
+
+
+
+#if HTS_XGETHOST
+#if USE_BEGINTHREAD
+// lancement multithread du robot
+PTHREAD_TYPE Hostlookup(void* iadr_p) {
+ char iadr[256];
+ t_dnscache* cache=_hts_cache(); // adresse du cache
+ t_hostent* hp;
+ int error_found=0;
+
+ // recopier (après id:pass)
+#if DEBUGDNS
+ printf("resolv in background: %s\n",jump_identification(iadr_p));
+#endif
+ strcpy(iadr,jump_identification(iadr_p));
+ // couper éventuel :
+ {
+ char *a;
+ if ( (a=jump_toport(iadr)) )
+ *a='\0'; // get rid of it
+ }
+ freet(iadr_p);
+
+ // attendre que le cache dns soit prêt
+ while(_hts_lockdns(-1)); // attendre libération
+ _hts_lockdns(1); // locker
+ while(cache->n) {
+ if (strcmp(cache->iadr,iadr)==0) {
+ error_found=1;
+ }
+ cache=cache->n; // calculer queue
+ }
+ if (strcmp(cache->iadr,iadr)==0) {
+ error_found=1;
+ }
+
+ if (!error_found) {
+ // en gros copie de hts_gethostbyname sans le return
+ cache->n=(t_dnscache*) calloct(1,sizeof(t_dnscache));
+ if (cache->n!=NULL) {
+ t_fullhostent fullhostent_buffer;
+ strcpy(cache->n->iadr,iadr);
+ cache->n->host_length=0; /* pour le moment rien */
+ cache->n->n=NULL;
+ _hts_lockdns(0); // délocker
+
+ /* resolve */
+#if DEBUGDNS
+ printf("gethostbyname() in progress for %s\n",iadr);
+#endif
+ cache->n->host_length=-1;
+ memset(cache->n->host_addr, 0, sizeof(cache->n->host_addr));
+ hp=vxgethostbyname(iadr, &fullhostent_buffer);
+ if (hp!=NULL) {
+ memcpy(cache->n->host_addr, hp->h_addr, hp->h_length);
+ cache->n->host_length = hp->h_length;
+ }
+ } else
+ _hts_lockdns(0); // délocker
+ } else {
+#if DEBUGDNS
+ printf("aborting resolv for %s (found)\n",iadr);
+#endif
+ _hts_lockdns(0); // délocker
+ }
+ // fin de copie de hts_gethostbyname
+
+#if DEBUGDNS
+ printf("quitting resolv for %s (result: %d)\n",iadr,(cache->n!=NULL)?cache->n->host_length:(-999));
+#endif
+
+ return PTHREAD_RETURN; /* _endthread implied */
+}
+#endif
+
+// attendre que le host (ou celui du proxy) ait été résolu
+// si c'est un fichier, la résolution est immédiate
+// idem pour ftp://
+void back_solve(lien_back* back) {
+ if ((!strfield(back->url_adr,"file://")) && (!strfield(back->url_adr,"ftp://"))) {
+ //## if (back->url_adr[0]!=lOCAL_CHAR) { // qq chose à préparer
+ char* a;
+ if (!(back->r.req.proxy.active))
+ a=back->url_adr;
+ else
+ a=back->r.req.proxy.name;
+ a = jump_protocol(a);
+ if (!hts_dnstest(a)) { // non encore testé!..
+ // inscire en thread
+#if HTS_WIN
+ // Windows
+#if USE_BEGINTHREAD
+ {
+ char* p = calloct(strlen(a)+2,1);
+ if (p) {
+ strcpy(p,a);
+ _beginthread( Hostlookup , 0, p );
+ }
+ }
+#else
+ /*t_hostent* h=*/
+ /*hts_gethostbyname(a);*/ // calcul
+#endif
+#else
+#if USE_BEGINTHREAD
+ char* p = calloct(strlen(a)+2,1);
+ if (p) {
+ strcpy(p,a);
+ _beginthread( Hostlookup , 0, p );
+ }
+#else
+ // Sous Unix, le gethostbyname() est bloquant..
+ /*t_hostent* h=*/
+ /*hts_gethostbyname(a);*/ // calcul
+#endif
+#endif
+ }
+ }
+}
+
+// détermine si le host a pu être résolu
+int host_wait(lien_back* back) {
+ if ((!strfield(back->url_adr,"file://")) && (!strfield(back->url_adr,"ftp://"))) {
+ //## if (back->url_adr[0]!=lOCAL_CHAR) {
+ if (!(back->r.req.proxy.active)) {
+ return (hts_dnstest(back->url_adr));
+ } else {
+ return (hts_dnstest(back->r.req.proxy.name));
+ }
+ } else return 1; // prêt, fichier local
+}
+#endif
+
+
+// élimine les fichiers non html en backing (anticipation)
+// cleanup non-html files in backing to save backing space
+// and allow faster "save in cache" operation
+void back_clean(httrackp* opt,cache_back* cache,lien_back* back,int back_max) {
+ int i;
+ for(i=0;i<back_max;i++) {
+ if (back[i].status == 0) { // ready
+ if (!back[i].testmode) { // not test mode
+ if (strnotempty(back[i].url_sav)) { // filename exists
+ if (back[i].r.is_write) { // not in memory (on disk, ready)
+ if (back[i].r.size>0) { // size>0
+ if (back[i].r.statuscode==200) { // HTTP "OK"
+ if (!is_hypertext_mime(back[i].r.contenttype)) { // not HTML/hypertext
+ if (!may_be_hypertext_mime(back[i].r.contenttype)) { // may NOT be parseable mime type
+ if (back[i].pass2_ptr) {
+ // finalize
+ // // back_finalize(opt,cache,back,i);
+ // stats
+ //HTS_STAT.stat_bytes+=back[i].r.size;
+ //HTS_STAT.stat_files++;
+ //if ( (!back[i].r.notmodified) && (opt->is_update) ) {
+ // HTS_STAT.stat_updated_files++; // page modifiée
+ //}
+ //cache_mayadd(opt,cache,&back[i].r,back[i].url_adr,back[i].url_fil,back[i].url_sav);
+ *back[i].pass2_ptr=-1; // Done!
+ back_delete(back,i); // Delete backing entry
+ if ((opt->debug>0) && (opt->log!=NULL)) {
+ fspc(opt->log,"info"); fprintf(opt->log,"File successfully written in background: %s"LF,back[i].url_sav); test_flush;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+
+// attente (gestion des buffers des sockets)
+void back_wait(lien_back* back,int back_max,httrackp* opt,cache_back* cache,TStamp stat_timestart) {
+ int i;
+ T_SOC nfds=INVALID_SOCKET;
+ fd_set fds,fds_c,fds_e; // fds pour lecture, connect (write), et erreur
+ int nsockets; // nbre sockets
+ LLint max_read_bytes; // max bytes read per sockets
+ struct timeval tv;
+ int do_wait=0;
+ int gestion_timeout=0;
+ int busy_recv=0; // pas de données pour le moment
+ int busy_state=0; // pas de connexions
+ int max_loop; // nombre de boucles max à parcourir..
+#if HTS_ANALYSTE
+ int max_loop_chk=0;
+#endif
+
+
+ // max. number of loops
+ max_loop=8;
+
+#if 1
+ // Cleanup the stack to save space!
+ back_clean(opt,cache,back,back_max);
+#endif
+
+ // recevoir tant qu'il y a des données (avec un maximum de max_loop boucles)
+ do_wait=0;
+ gestion_timeout=0;
+ do {
+ int max_c;
+ busy_state=busy_recv=0;
+
+#if 0
+ check_rate(stat_timestart,opt->maxrate); // vérifier taux de transfert
+#endif
+ // inscrire les sockets actuelles, et rechercher l'ID la plus élevée
+ FD_ZERO(&fds);
+ FD_ZERO(&fds_c);
+ FD_ZERO(&fds_e);
+ nsockets=0;
+ max_read_bytes=TAILLE_BUFFER; // maximum bytes that can be read
+ nfds=INVALID_SOCKET;
+
+ max_c=1;
+ for(i=0;i<back_max;i++) {
+
+ // en cas de gestion du connect préemptif
+#if HTS_XCONN
+ if (back[i].status==100) { // connexion
+ do_wait=1;
+
+ // noter socket write
+ FD_SET(back[i].r.soc,&fds_c);
+
+ // noter socket erreur
+ FD_SET(back[i].r.soc,&fds_e);
+
+ // calculer max
+ if (max_c) {
+ max_c=0;
+ nfds=back[i].r.soc;
+ } else if (back[i].r.soc>nfds) {
+ // ID socket la plus élevée
+ nfds=back[i].r.soc;
+ }
+
+ } else
+#endif
+#if HTS_XGETHOST
+ if (back[i].status==101) { // attente
+ // rien à faire..
+ } else
+#endif
+ // poll pour la lecture sur les sockets
+ if ((back[i].status>0) && (back[i].status<100)) { // en réception http
+
+#if BDEBUG==1
+ //printf("....socket in progress: %d\n",back[i].r.soc);
+#endif
+ // non local et non ftp
+ if (!back[i].r.is_file) {
+ //## if (back[i].url_adr[0]!=lOCAL_CHAR) {
+
+ // vérification de sécurité
+ if (back[i].r.soc!=INVALID_SOCKET) { // hey, you never know..
+ do_wait=1;
+
+ // noter socket read
+ FD_SET(back[i].r.soc,&fds);
+
+ // noter socket error
+ FD_SET(back[i].r.soc,&fds_e);
+
+ // incrémenter nombre de sockets
+ nsockets++;
+
+ // calculer max
+ if (max_c) {
+ max_c=0;
+ nfds=back[i].r.soc;
+ } else if (back[i].r.soc>nfds) {
+ // ID socket la plus élevée
+ nfds=back[i].r.soc;
+ }
+ } else {
+ back[i].r.statuscode=-4;
+ if (back[i].status==100)
+ strcpy(back[i].r.msg,"Connect Error");
+ else
+ strcpy(back[i].r.msg,"Receive Error");
+ back[i].status=0; // terminé
+ if ((opt->debug>0) && (opt->log!=NULL)) {
+ fspc(opt->log,"warning"); fprintf(opt->log,"Unexpected socket error during pre-loop"LF); test_flush;
+ }
+ }
+#if WIDE_DEBUG
+ else {
+ DEBUG_W("PANIC!!! Socket is invalid in a poll test!\n");
+ }
+#endif
+
+ }
+
+ }
+ }
+ nfds++;
+
+ if (do_wait) { // attendre
+ // temps d'attente max: 2.5 seconde
+ tv.tv_sec=HTS_SOCK_SEC;
+ tv.tv_usec=HTS_SOCK_MS;
+
+#if BDEBUG==1
+ printf("..select\n");
+#endif
+
+ // poller les sockets-attention au noyau sous Unix..
+#if HTS_WIDE_DEBUG
+ DEBUG_W("select\n");
+#endif
+ select(nfds,&fds,&fds_c,&fds_e,&tv);
+#if HTS_WIDE_DEBUG
+ DEBUG_W("select done\n");
+#endif
+ }
+
+ // maximum data which can be received for a socket, if limited
+ if (nsockets) {
+ if (opt->maxrate>0) {
+ max_read_bytes = ( check_downloadable_bytes(opt->maxrate) / nsockets );
+ }
+ }
+ if (!max_read_bytes)
+ busy_recv=0;
+
+ // recevoir les données arrivées
+ for(i=0;i<back_max;i++) {
+
+ if (back[i].status>0) {
+ if (!back[i].r.is_file) { // not file..
+ if (back[i].r.soc!=INVALID_SOCKET) { // hey, you never know..
+ int err=FD_ISSET(back[i].r.soc,&fds_e);
+ if (err) {
+ if (back[i].r.soc!=INVALID_SOCKET) {
+#if HTS_DEBUG_CLOSESOCK
+ DEBUG_W("back_wait: deletehttp\n");
+#endif
+ deletehttp(&back[i].r);
+ }
+ back[i].r.soc=INVALID_SOCKET;
+ back[i].r.statuscode=-4;
+ if (back[i].status==100)
+ strcpy(back[i].r.msg,"Connect Error");
+ else
+ strcpy(back[i].r.msg,"Receive Error");
+ back[i].status=0; // terminé
+ }
+ }
+ }
+ }
+
+ // ---- FLAG WRITE MIS A UN?: POUR LE CONNECT
+ if (back[i].status==100) { // attendre connect
+ int dispo=0;
+ // vérifier l'existance de timeout-check
+ if (!gestion_timeout)
+ if (back[i].timeout>0)
+ gestion_timeout=1;
+
+ // connecté?
+ dispo=FD_ISSET(back[i].r.soc,&fds_c);
+ if (dispo) { // ok connected!!
+ busy_state=1;
+
+#if HTS_USEOPENSSL
+ /* SSL mode */
+ if (back[i].r.ssl) {
+ // handshake not yet launched
+ if (!back[i].r.ssl_con) {
+ SSL_CTX_set_options(openssl_ctx, SSL_OP_ALL);
+ // new session
+ back[i].r.ssl_con = SSL_new(openssl_ctx);
+ if (back[i].r.ssl_con) {
+ SSL_clear(back[i].r.ssl_con);
+ if (SSL_set_fd(back[i].r.ssl_con, back[i].r.soc) == 1) {
+ SSL_set_connect_state(back[i].r.ssl_con);
+ back[i].status = 102; /* handshake wait */
+ } else
+ back[i].r.statuscode=-6;
+ } else
+ back[i].r.statuscode=-6;
+ }
+ /* Error */
+ if (back[i].r.statuscode == -6) {
+ strcpy(back[i].r.msg, "bad SSL/TLS handshake");
+ deletehttp(&back[i].r);
+ back[i].r.soc=INVALID_SOCKET;
+ back[i].r.statuscode=-5;
+ back[i].status=0;
+ }
+ }
+
+#endif
+
+#if BDEBUG==1
+ printf("..connect ok on socket %d\n",back[i].r.soc);
+#endif
+
+ if ((back[i].r.soc != INVALID_SOCKET) && (back[i].status==100)) {
+ /* limit nb. connections/seconds to avoid server overload */
+ if (opt->maxconn>0) {
+ Sleep(1000/opt->maxconn);
+ }
+
+ if (back[i].timeout>0) { // refresh timeout si besoin est
+ back[i].timeout_refresh=time_local();
+ }
+ if (back[i].rateout>0) { // le taux de transfert de base sur le début de la connexion
+ back[i].rateout_time=time_local();
+ }
+ // envoyer header
+ //if (strcmp(back[i].url_sav,BACK_ADD_TEST)!=0) // vrai get
+ if (!back[i].head_request)
+ http_sendhead(opt->cookie,0,back[i].send_too,back[i].url_adr,back[i].url_fil,back[i].referer_adr,back[i].referer_fil,&back[i].r);
+ else if (back[i].head_request==2) // test en GET!
+ http_sendhead(opt->cookie,0,back[i].send_too,back[i].url_adr,back[i].url_fil,back[i].referer_adr,back[i].referer_fil,&back[i].r);
+ else // test!
+ http_sendhead(opt->cookie,1,back[i].send_too,back[i].url_adr,back[i].url_fil,back[i].referer_adr,back[i].referer_fil,&back[i].r);
+ back[i].status=99; // attendre en tête maintenant
+ }
+ }
+
+ // attente gethostbyname
+ }
+#if HTS_USEOPENSSL
+ else if (back[i].status==102) { // wait for SSL handshake
+ /* SSL mode */
+ if (back[i].r.ssl) {
+ int conn_code;
+ if ((conn_code = SSL_connect(back[i].r.ssl_con)) <= 0) {
+ /* non blocking I/O, will retry */
+ int err_code = SSL_get_error(back[i].r.ssl_con, conn_code);
+ if (
+ (err_code != SSL_ERROR_WANT_READ)
+ &&
+ (err_code != SSL_ERROR_WANT_WRITE)
+ ) {
+ char tmp[256];
+ tmp[0]='\0';
+ ERR_error_string(err_code, tmp);
+ back[i].r.msg[0]='\0';
+ strncat(back[i].r.msg, tmp, sizeof(back[i].r.msg) - 2);
+ if (!strnotempty(back[i].r.msg)) {
+ sprintf(back[i].r.msg, "SSL/TLS error %d", err_code);
+ }
+ deletehttp(&back[i].r);
+ back[i].r.soc=INVALID_SOCKET;
+ back[i].r.statuscode=-5;
+ back[i].status=0;
+ }
+ } else { /* got it! */
+ back[i].status=100; // back to waitconnect
+ }
+ } else {
+ strcpy(back[i].r.msg, "unexpected SSL/TLS error");
+ deletehttp(&back[i].r);
+ back[i].r.soc=INVALID_SOCKET;
+ back[i].r.statuscode=-5;
+ back[i].status=0;
+ }
+
+ }
+#endif
+#if HTS_XGETHOST
+ else if (back[i].status==101) { // attendre gethostbyname
+#if DEBUGDNS
+ //printf("status 101 for %s\n",back[i].url_adr);
+#endif
+
+ if (!gestion_timeout)
+ if (back[i].timeout>0)
+ gestion_timeout=1;
+
+ if (host_wait(&back[i])) { // prêt
+ back[i].status=100; // attente connexion
+ if (back[i].timeout>0) { // refresh timeout si besoin est
+ back[i].timeout_refresh=time_local();
+ }
+ if (back[i].rateout>0) { // le taux de transfert de base sur le début de la connexion
+ back[i].rateout_time=time_local();
+ }
+
+ back[i].r.soc=http_xfopen(0,0,0,back[i].send_too,back[i].url_adr,back[i].url_fil,&(back[i].r));
+ if (back[i].r.soc==INVALID_SOCKET) {
+ back[i].status=0; // fini, erreur
+ if (back[i].r.soc!=INVALID_SOCKET) {
+#if HTS_DEBUG_CLOSESOCK
+ DEBUG_W("back_wait(2): deletehttp\n");
+#endif
+ deletehttp(&back[i].r);
+ }
+ back[i].r.soc=INVALID_SOCKET;
+ back[i].r.statuscode=-5;
+ if (strnotempty(back[i].r.msg)==0)
+ strcpy(back[i].r.msg,"Unable to resolve host name");
+ }
+ }
+
+
+ // ---- FLAG READ MIS A UN?: POUR LA RECEPTION
+ }
+#endif
+#if USE_BEGINTHREAD
+ // ..rien à faire, c'est magic les threads
+#else
+ else if (back[i].status==1000) { // en réception ftp
+ if (!fexist(back[i].location_buffer)) { // terminé
+ FILE* fp;
+ fp=fopen(fconcat(back[i].location_buffer,".ok"),"rb");
+ if (fp) {
+ int j=0;
+ fscanf(fp,"%d ",&(back[i].r.statuscode));
+ while(!feof(fp)) {
+ int c = fgetc(fp);
+ if (c!=EOF)
+ back[i].r.msg[j++]=c;
+ }
+ back[i].r.msg[j++]='\0';
+ fclose(fp);
+ remove(fconcat(back[i].location_buffer,".ok"));
+ strcpy(fconcat(back[i].location_buffer,".ok"),"");
+ } else {
+ strcpy(back[i].r.msg,"Unknown ftp result, check if file is ok");
+ back[i].r.statuscode=-1;
+ }
+ back[i].status=0;
+ // finalize transfer
+ if (back[i].r.statuscode>0) {
+ back_finalize(opt,cache,back,i);
+ }
+ }
+ }
+#endif
+ else if ((back[i].status>0) && (back[i].status<1000)) { // en réception http
+ int dispo=0;
+
+ // vérifier l'existance de timeout-check
+ if (!gestion_timeout)
+ if (back[i].timeout>0)
+ gestion_timeout=1;
+
+ // données dispo?
+ //## if (back[i].url_adr[0]!=lOCAL_CHAR)
+ if (!back[i].r.is_file) {
+ dispo=FD_ISSET(back[i].r.soc,&fds);
+ }
+ else
+ dispo=1;
+
+ // Check transfer rate!
+ if (!max_read_bytes)
+ dispo=0; // limit transfer rate
+
+ if (dispo) { // données dispo
+ LLint retour_fread;
+ busy_recv=1; // on récupère encore
+#if BDEBUG==1
+ printf("..data available on socket %d\n",back[i].r.soc);
+#endif
+
+
+ // range size hack old location
+
+#if HTS_DIRECTDISK
+ // Court-circuit:
+ // Peut-on stocker le fichier directement sur disque?
+ // Ahh que ca serait vachement mieux et que ahh que la mémoire vous dit merci!
+ if (back[i].status) {
+ if (back[i].r.is_write==0) { // mode mémoire
+ if (back[i].r.adr==NULL) { // rien n'a été écrit
+ if (!back[i].testmode) { // pas mode test
+ if (strnotempty(back[i].url_sav)) {
+ if (strcmp(back[i].url_fil,"/robots.txt")) {
+ if (back[i].r.statuscode==200) { // 'OK'
+ if (!is_hypertext_mime(back[i].r.contenttype)) { // pas HTML
+ if (opt->getmode&2) { // on peut ecrire des non html
+ back[i].r.is_write=1; // écrire
+ if (back[i].r.compressed
+ &&
+ /* .gz are *NOT* depacked!! */
+ (strfield(get_ext(back[i].url_sav),"gz") == 0)
+ ) {
+ back[i].tmpfile[0]='\0';
+ strcpy(back[i].tmpfile,tempnam(NULL,"httrZ"));
+ if (back[i].tmpfile[0])
+ back[i].r.out=fopen(back[i].tmpfile,"wb");
+ } else {
+ back[i].r.compressed=0;
+ back[i].r.out=filecreate(back[i].url_sav);
+ }
+#if HDEBUG
+ printf("direct-disk: %s\n",back[i].url_sav);
+#endif
+ if ((opt->debug>1) && (opt->log!=NULL)) {
+ fspc(opt->log,"debug"); fprintf(opt->log,"File received from net to disk: %s%s"LF,back[i].url_adr,back[i].url_fil); test_flush;
+ }
+
+ if (back[i].r.out==NULL) {
+ if (opt->errlog) {
+ fspc(opt->errlog,"error");
+ fprintf(opt->errlog,"Unable to save file %s"LF,back[i].url_sav);
+ test_flush;
+ }
+ back[i].r.is_write=0; // erreur, abandonner
+#if HDEBUG
+ printf("..error!\n");
+#endif
+ }
+#if HTS_WIN==0
+ else chmod(back[i].url_sav,HTS_ACCESS_FILE);
+#endif
+ } else { // on coupe tout!
+ if ((opt->debug>1) && (opt->log!=NULL)) {
+ fspc(opt->log,"debug"); fprintf(opt->log,"File cancelled (non HTML): %s%s"LF,back[i].url_adr,back[i].url_fil); test_flush;
+ }
+ back[i].status=0; // terminé
+ if (!back[i].testmode)
+ back[i].r.statuscode=-10; // EUHH CANCEL
+ else
+ back[i].r.statuscode=-10; // "TEST OK"
+ if (back[i].r.soc!=INVALID_SOCKET) {
+#if HTS_DEBUG_CLOSESOCK
+ DEBUG_W("back_wait(3): deletehttp\n");
+#endif
+ deletehttp(&back[i].r);
+ }
+ back[i].r.soc=INVALID_SOCKET;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+#endif
+
+ // réception de données depuis socket ou fichier
+ if (back[i].status) {
+ if (back[i].status==99) // recevoir par bloc de lignes
+ retour_fread=http_xfread1(&(back[i].r),0);
+ else if (back[i].status==98) { // recevoir longueur chunk en hexa caractère par caractère
+ // backuper pour lire dans le buffer chunk
+ htsblk r;
+ memcpy(&r, &(back[i].r), sizeof(htsblk));
+ back[i].r.is_write=0; // mémoire
+ back[i].r.adr=back[i].chunk_adr; // adresse
+ back[i].r.size=back[i].chunk_size; // taille taille chunk
+ back[i].r.totalsize=-1; // total inconnu
+ back[i].r.out=NULL;
+ back[i].r.is_file=0;
+ //
+ // ligne par ligne
+ retour_fread=http_xfread1(&(back[i].r),-1);
+ // modifier et restaurer
+ back[i].chunk_adr=back[i].r.adr; // adresse
+ back[i].chunk_size=back[i].r.size; // taille taille chunk
+ memcpy(&(back[i].r), &r, sizeof(htsblk)); // restaurer véritable r
+ }
+ else if (back[i].is_chunk) { // attention chunk, limiter taille à lire
+#if CHUNKDEBUG==1
+ printf("read %d bytes\n",(int)min(back[i].r.totalsize-back[i].r.size,max_read_bytes));
+#endif
+ retour_fread=(int) http_xfread1(&(back[i].r),(int) min(back[i].r.totalsize-back[i].r.size,max_read_bytes));
+ } else
+ retour_fread=(int) http_xfread1(&(back[i].r),(int) max_read_bytes);
+ // retour_fread=http_fread1(&(back[i].r));
+ } else
+ retour_fread=-1; // interruption ou annulation interne (peut ne pas être une erreur)
+
+ // Si réception chunk, tester si on est pas à la fin!
+ if (back[i].status==1) {
+ if (back[i].is_chunk) { // attendre prochain chunk
+ if (back[i].r.size==back[i].r.totalsize) { // fin chunk!
+ //printf("chunk end at %d\n",back[i].r.size);
+ back[i].status=98; // prochain chunk
+ if (back[i].chunk_adr!=NULL) { freet(back[i].chunk_adr); back[i].chunk_adr=NULL; } back[i].chunk_size=0;
+ retour_fread=0; // pas d'erreur
+#if CHUNKDEBUG==1
+ printf("waiting for next chunk header (soc %d)..\n",back[i].r.soc);
+#endif
+ }
+ }
+ }
+
+ if (retour_fread < 0) { // erreur réception
+ back[i].status=0; // terminé
+ if (back[i].r.soc!=INVALID_SOCKET) {
+#if HTS_DEBUG_CLOSESOCK
+ DEBUG_W("back_wait(4): deletehttp\n");
+#endif
+ deletehttp(&back[i].r);
+ }
+ back[i].r.soc=INVALID_SOCKET;
+#if CHUNKDEBUG==1
+ if (back[i].is_chunk)
+ printf("must be the last chunk for %s (connection closed) - %d/%d\n",back[i].url_fil,back[i].r.size,back[i].r.totalsize);
+#endif
+ //if ((back[i].r.statuscode==-1) && (strnotempty(back[i].r.msg)==0)) {
+ if ((back[i].r.statuscode<0) && (strnotempty(back[i].r.msg)==0)) {
+#if HDEBUG
+ printf("error interruped: %s\n",back[i].r.adr);
+#endif
+ if (back[i].r.size>0)
+ strcat(back[i].r.msg,"Interrupted transfer");
+ else
+ strcat(back[i].r.msg,"No data (connection closed)");
+ back[i].r.statuscode=-4;
+ }
+
+ // finalize transfer
+ if (back[i].r.statuscode>0) {
+ back_finalize(opt,cache,back,i);
+ }
+
+ if (back[i].r.totalsize>0) { // tester totalsize
+ //if ((back[i].r.totalsize>0) && (back[i].status==99)) { // tester totalsize
+ if (back[i].r.totalsize!=back[i].r.size) { // pas la même!
+ if (!opt->tolerant) {
+ //#if HTS_CL_IS_FATAL
+ if (back[i].r.adr) freet(back[i].r.adr); back[i].r.adr=NULL;
+ if (back[i].r.size<back[i].r.totalsize)
+ back[i].r.statuscode=-4; // recatch
+ sprintf(back[i].r.msg,"Incorrect length ("LLintP" Bytes, "LLintP" expected)",back[i].r.size,back[i].r.totalsize);
+ } else {
+ //#else
+ // Un warning suffira..
+ if (cache->errlog!=NULL) {
+ fspc(cache->errlog,"warning"); fprintf(cache->errlog,"Incorrect length ("LLintP"!="LLintP" expected) for %s%s"LF,back[i].r.size,back[i].r.totalsize,back[i].url_adr,back[i].url_fil);
+ }
+ //#endif
+ }
+ }
+ }
+#if BDEBUG==1
+ printf("transfer ok\n");
+#endif
+ } else if (retour_fread > 0) { // pas d'erreur de réception et data
+ if (back[i].timeout>0) { // refresh timeout si besoin est
+ back[i].timeout_refresh=time_local();
+ }
+
+ // Traitement des en têtes chunks ou en têtes
+ if (back[i].status==98) { // réception taille chunk en hexa ( après les en têtes, peut ne pas
+ if (back[i].chunk_size>=2) {
+ int chunk_size=-1;
+ // être présent)
+ if (back[i].chunk_adr[back[i].chunk_size-1]==10) { // LF, fin ligne chunk
+ char chunk_data[64];
+ if (back[i].chunk_size<32) { // pas trop gros
+ back[i].chunk_adr[ back[i].chunk_size-1]='\0'; // octet nul
+ strcpy(chunk_data,""); // hex number
+ strcat(chunk_data,back[i].chunk_adr);
+#if CHUNKDEBUG==1
+ printf("chunk received and read: %s\n",chunk_data);
+#endif
+ if (back[i].r.totalsize<0)
+ back[i].r.totalsize=0; // initialiser à 0
+ if (sscanf(chunk_data,"%x",&chunk_size) == 1) {
+ back[i].r.totalsize+=chunk_size; // noter taille
+ back[i].r.adr=(char*) realloct(back[i].r.adr,(INTsys) back[i].r.totalsize + 1);
+ if (!back[i].r.adr) {
+ if (cache->errlog!=NULL) {
+ fprintf(cache->errlog,"Error: Not enough memory ("LLintP") for %s%s"LF,back[i].r.totalsize,back[i].url_adr,back[i].url_fil);
+ }
+ }
+#if CHUNKDEBUG==1
+ printf("chunk length: %d - next total "LLintP":\n",chunk_size,back[i].r.totalsize);
+#endif
+ } else
+ if (cache->errlog!=NULL) {
+ fprintf(cache->errlog,"Warning: Illegal chunk (%s) for %s%s"LF,back[i].chunk_adr,back[i].url_adr,back[i].url_fil);
+ }
+ } else {
+ if (cache->errlog!=NULL) {
+ fprintf(cache->errlog,"Warning: Chunk too big ("LLintP") for %s%s"LF,back[i].chunk_size,back[i].url_adr,back[i].url_fil);
+ }
+ }
+
+ // ok, continuer sur le body
+
+ // si chunk non nul continuer (ou commencer)
+ if (chunk_size>0) {
+ back[i].status=1; // continuer body
+#if CHUNKDEBUG==1
+ printf("waiting for body (chunk)\n");
+#endif
+ } else { // chunk nul, c'est la fin
+#if CHUNKDEBUG==1
+ printf("chunk end, total: %d\n",back[i].r.size);
+#endif
+ back[i].status=0; // fin
+ // finalize transfer
+ back_finalize(opt,cache,back,i);
+ if (back[i].r.soc!=INVALID_SOCKET) {
+#if HTS_DEBUG_CLOSESOCK
+ DEBUG_W("back_wait(5): deletehttp\n");
+#endif
+ deletehttp(&back[i].r); back[i].r.soc=INVALID_SOCKET;
+
+ /* Tester totalsize en fin de chunk */
+ if ((back[i].r.totalsize>0)) { // tester totalsize
+ if (back[i].r.totalsize!=back[i].r.size) { // pas la même!
+#if HTS_CL_IS_FATAL
+ if (back[i].r.adr) { freet(back[i].r.adr); back[i].r.adr=NULL; }
+ back[i].r.statuscode=-1;
+ strcpy(back[i].r.msg,"Incorrect length");
+#else
+ // Un warning suffira..
+ if (cache->errlog!=NULL) {
+ fspc(cache->errlog,"warning"); fprintf(cache->errlog,"Incorrect length ("LLintP"!="LLintP" expected) for %s%s"LF,back[i].r.size,back[i].r.totalsize,back[i].url_adr,back[i].url_fil);
+ }
+#endif
+ }
+ }
+
+
+ }
+ }
+
+ // effacer buffer (chunk en tete)
+ if (back[i].chunk_adr!=NULL) {
+ freet(back[i].chunk_adr);
+ back[i].chunk_adr=NULL;
+ back[i].chunk_size=0;
+ }
+
+ } // chunk LF?
+ } // taille buffer chunk>2
+ //
+ } else if (back[i].status==99) { // en têtes (avant le chunk si il est présent)
+ //
+ if (back[i].r.size>=2) {
+ // double LF
+ if (
+ ((back[i].r.adr[back[i].r.size-1]==10) && (back[i].r.adr[back[i].r.size-2]==10))
+ ||
+ (back[i].r.adr[0] == '<') /* bogus server */
+ ) {
+ char rcvd[2048];
+ int ptr=0;
+ int noFreebuff=0;
+
+#if BDEBUG==1
+ printf("..ok, header received\n");
+#endif
+
+ /* Hack for zero-length headers */
+ if (back[i].r.adr[0] != '<') {
+
+ // ----------------------------------------
+ // traiter en-tête!
+ // status-line à récupérer
+ ptr+=binput(back[i].r.adr+ptr,rcvd,2000);
+ if (strnotempty(rcvd)==0)
+ ptr+=binput(back[i].r.adr+ptr,rcvd,2000); // "certains serveurs buggés envoient un \n au début" (RFC)
+
+ // traiter status-line
+ treatfirstline(&back[i].r,rcvd);
+
+#if HDEBUG
+ printf("(Buffer) Status-Code=%d\n",back[i].r.statuscode);
+#endif
+ if (_DEBUG_HEAD) {
+ if (ioinfo) {
+ fprintf(ioinfo,"response for %s%s:\r\ncode=%d\r\n",jump_identification(back[i].url_adr),back[i].url_fil,back[i].r.statuscode);
+ fprintfio(ioinfo,back[i].r.adr,">>> ");
+ fprintf(ioinfo,"\r\n");
+ fflush(ioinfo);
+ } // en-tête
+ }
+
+ // header // ** !attention! HTTP/0.9 non supporté
+ do {
+ ptr+=binput(back[i].r.adr+ptr,rcvd,2000);
+#if HDEBUG
+ printf("(buffer)>%s\n",rcvd);
+#endif
+ /*
+ if (_DEBUG_HEAD) {
+ if (ioinfo) {
+ fprintf(ioinfo,"(buffer)>%s\r\n",rcvd);
+ fflush(ioinfo);
+ }
+ }
+ */
+
+ if (strnotempty(rcvd))
+ treathead(opt->cookie,back[i].url_adr,back[i].url_fil,&back[i].r,rcvd); // traiter
+
+ // parfois les serveurs buggés renvoient un content-range avec un 200
+ if (back[i].r.statuscode==200) // 'OK'
+ if (strfield(rcvd,"content-range:")) // Avec un content-range: relisez les RFC..
+ back[i].r.statuscode=206; // FORCER A 206 !!!!!
+
+ } while(strnotempty(rcvd));
+ // ----------------------------------------
+
+ // libérer mémoire -- après! --
+ if (back[i].r.adr!=NULL) { freet(back[i].r.adr); back[i].r.adr=NULL; }
+ } else {
+ // assume text/html, OK
+ treatfirstline(&back[i].r, back[i].r.adr);
+ noFreebuff=1;
+ }
+
+
+
+ /*
+ Status code and header-response hacks
+ */
+
+
+ // Check response : 203 == 200
+ if (back[i].r.statuscode==203) { // 'Non-Authoritative Information'
+ back[i].r.statuscode=200; // forcer "OK"
+ } else if (back[i].r.statuscode == 100) {
+ back[i].status=99;
+ back[i].r.size=0;
+ back[i].r.totalsize=0;
+ back[i].chunk_size=0;
+ back[i].r.statuscode=-1;
+ back[i].r.msg[0]='\0';
+ if ((opt->debug>1) && (opt->log!=NULL)) {
+ fspc(opt->log,"debug"); fprintf(opt->log,"Status 100 detected for %s%s, continuing headers"LF,back[i].url_adr,back[i].url_fil); test_flush;
+ }
+ continue;
+ }
+
+ /*
+ Solve "false" 416 problems
+ */
+ if (back[i].r.statuscode==416) { // 'Requested Range Not Satisfiable'
+ // Example:
+ // Range: bytes=2830-
+ // ->
+ // Content-Range: bytes */2830
+ if (back[i].range_req_size == back[i].r.crange) {
+ deletehttp(&back[i].r); back[i].r.soc=INVALID_SOCKET;
+ back[i].status=0; // READY
+ back[i].r.size=back[i].r.totalsize=back[i].range_req_size;
+ filenote(back[i].url_sav,NULL);
+ back[i].r.statuscode=304; // NOT MODIFIED
+ if ((opt->debug>1) && (opt->log!=NULL)) {
+ fspc(opt->log,"debug"); fprintf(opt->log,"File seems complete (good 416 message), breaking connection: %s%s"LF,back[i].url_adr,back[i].url_fil); test_flush;
+ }
+ }
+ }
+
+ // transform 406 into 200 ; we'll catch embedded links inside the choice page
+ if (back[i].r.statuscode==406) { // 'Not Acceptable'
+ back[i].r.statuscode=200;
+ }
+
+ // Various hacks to limit re-transfers when updating a mirror
+ // Force update if same size detected
+ if (opt->sizehack) {
+ // We already have the file
+ // and ask the remote server for an update
+ // Some servers, especially dynamic pages severs, always
+ // answer that the page has been modified since last visit
+ // And answer with a 200 (OK) response, and the same page
+ // If the size is the same, and the option has been set, we assume
+ // that the file is identical - and therefore let's break the connection
+ if (back[i].is_update) { // mise à jour
+ if (back[i].r.statuscode==200) { // 'OK'
+ htsblk r = cache_read(opt,cache,back[i].url_adr,back[i].url_fil,NULL); // lire entrée cache
+ if (r.statuscode == 200) { // OK pas d'erreur cache
+ LLint len1,len2;
+ len1=r.totalsize;
+ len2=back[i].r.totalsize;
+ if (r.size>0)
+ len1=r.size;
+ if (len1>0) {
+ if (len1 == len2) { // tailles identiques
+ back[i].r.statuscode=304; // forcer NOT MODIFIED
+ deletehttp(&back[i].r); back[i].r.soc=INVALID_SOCKET;
+ if ((opt->debug>1) && (opt->log!=NULL)) {
+ fspc(opt->log,"debug"); fprintf(opt->log,"File seems complete (same size), breaking connection: %s%s"LF,back[i].url_adr,back[i].url_fil); test_flush;
+ }
+ }
+ }
+ } else {
+ if (opt->errlog!=NULL) {
+ fspc(opt->errlog,"warning"); fprintf(opt->errlog,"File seems complete (same size), but there was a cache read error: %s%s"LF,back[i].url_adr,back[i].url_fil); test_flush;
+ }
+ }
+ if (r.adr) {
+ freet(r.adr);
+ }
+ }
+ }
+ }
+
+ // Various hacks to limit re-transfers when updating a mirror
+ // Detect already downloaded file (with another browser, for example)
+ if (opt->sizehack) {
+ if (!back[i].is_update) { // mise à jour
+ if (back[i].r.statuscode==200) { // 'OK'
+ if (!is_hypertext_mime(back[i].r.contenttype)) { // not HTML
+ if (strnotempty(back[i].url_sav)) { // target found
+ int size = fsize(back[i].url_sav); // target size
+ if (size >= 0) {
+ if (back[i].r.totalsize == size) { // same size!
+ deletehttp(&back[i].r); back[i].r.soc=INVALID_SOCKET;
+ back[i].status=0; // READY
+ back[i].r.size=back[i].r.totalsize;
+ filenote(back[i].url_sav,NULL);
+ back[i].r.statuscode=304; // NOT MODIFIED
+ if ((opt->debug>1) && (opt->log!=NULL)) {
+ fspc(opt->log,"debug"); fprintf(opt->log,"File seems complete (same size file discovered), breaking connection: %s%s"LF,back[i].url_adr,back[i].url_fil); test_flush;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Various hacks to limit re-transfers when updating a mirror
+ // Detect bad range: header
+ if (opt->sizehack) {
+ // We have request for a partial file (with a 'Range: NNN-' header)
+ // and received a complete file notification (200), with 'Content-length: NNN'
+ // it might be possible that we had the complete file
+ // this is the case in *most* cases, so break the connection
+ if (back[i].r.is_write==0) { // mode mémoire
+ if (back[i].r.adr==NULL) { // rien n'a été écrit
+ if (!back[i].testmode) { // pas mode test
+ if (strnotempty(back[i].url_sav)) {
+ if (strcmp(back[i].url_fil,"/robots.txt")) {
+ if (back[i].r.statuscode==200) { // 'OK'
+ if (!is_hypertext_mime(back[i].r.contenttype)) { // pas HTML
+ if (back[i].r.statuscode==200) { // "OK"
+ if (back[i].range_req_size>0) { // but Range: requested
+ if (back[i].range_req_size == back[i].r.totalsize) { // And same size
+#if HTS_DEBUG_CLOSESOCK
+ DEBUG_W("back_wait(skip_range): deletehttp\n");
+#endif
+ deletehttp(&back[i].r); back[i].r.soc=INVALID_SOCKET;
+ back[i].status=0; // READY
+ back[i].r.size=back[i].r.totalsize;
+ filenote(back[i].url_sav,NULL);
+ back[i].r.statuscode=304; // NOT MODIFIED
+ if ((opt->debug>1) && (opt->log!=NULL)) {
+ fspc(opt->log,"debug"); fprintf(opt->log,"File seems complete (reget failed), breaking connection: %s%s"LF,back[i].url_adr,back[i].url_fil); test_flush;
+ }
+ }
+ }
+ }
+
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ // END - Various hacks to limit re-transfers when updating a mirror
+
+ /*
+ End of status code and header-response hacks
+ */
+
+
+
+ /* Interdiction taille par le wizard? */
+ if (back[i].r.soc!=INVALID_SOCKET) {
+ if (!back_checksize(opt,&back[i],1)) {
+ back[i].status=0; // FINI
+ deletehttp(&back[i].r); back[i].r.soc=INVALID_SOCKET;
+ if (!back[i].testmode)
+ strcpy(back[i].r.msg,"File too big");
+ else
+ strcpy(back[i].r.msg,"Test: File too big");
+ }
+ }
+
+ /* sinon, continuer */
+ /* if (back[i].r.soc!=INVALID_SOCKET) { // ok récupérer body? */
+ // head: terminé
+ if (back[i].head_request) {
+ if ((opt->debug>1) && (opt->log!=NULL)) {
+ fspc(opt->log,"debug"); fprintf(opt->log,"Tested file: %s%s"LF,back[i].url_adr,back[i].url_fil); test_flush;
+ }
+#if HTS_DEBUG_CLOSESOCK
+ DEBUG_W("back_wait(head request): deletehttp\n");
+#endif
+ // Couper connexion
+ deletehttp(&back[i].r); back[i].r.soc=INVALID_SOCKET;
+ back[i].status=0; // terminé
+ }
+ // traiter une éventuelle erreur 304 (cache à jour utilisable)
+ else if (back[i].r.statuscode==304) { // document à jour dans le cache
+ // lire dans le cache
+ // ** NOTE: pas de vérif de la taille ici!!
+#if HTS_DEBUG_CLOSESOCK
+ DEBUG_W("back_wait(file is not modified): deletehttp\n");
+#endif
+ deletehttp(&back[i].r); back[i].r.soc=INVALID_SOCKET;
+ back[i].r=cache_read(opt,cache,back[i].url_adr,back[i].url_fil,back[i].url_sav);
+ if (!back[i].r.location)
+ back[i].r.location=back[i].location_buffer;
+ else { /* recopier */
+ strcpy(back[i].location_buffer,back[i].r.location);
+ back[i].r.location=back[i].location_buffer;
+ }
+
+ // hack:
+ // In case of 'if-unmodified-since' hack, a 304 status can be sent
+ // then, force 'ok' status
+ if (back[i].r.statuscode == -1) {
+ if (fexist(back[i].url_sav)) {
+ back[i].r.statuscode=200; // OK
+ if ((opt->debug>0) && (opt->log!=NULL)) {
+ fspc(opt->log,"debug"); fprintf(opt->log,"Not-modified status without cache guessed: %s%s"LF,back[i].url_adr,back[i].url_fil); test_flush;
+ }
+ }
+ }
+
+ // Status is okay?
+ if (back[i].r.statuscode!=-1) { // pas d'erreur de lecture
+ back[i].status=0; // OK prêt
+ back[i].r.notmodified=1; // NON modifié!
+ if ((opt->debug>0) && (opt->log!=NULL)) {
+ fspc(opt->log,"debug"); fprintf(opt->log,"File loaded after test from cache: %s%s"LF,back[i].url_adr,back[i].url_fil); test_flush;
+ }
+
+ // finalize
+ if (back[i].r.statuscode>0) {
+ back_finalize(opt,cache,back,i);
+ }
+
+#if DEBUGCA
+ printf("..document à jour après requète: %s%s\n",back[i].url_adr,back[i].url_fil);
+#endif
+
+ //printf(">%s status %d\n",back[p].r.contenttype,back[p].r.statuscode);
+ } else { // erreur
+ back[i].status=0; // terminé
+ //printf("erreur cache\n");
+
+ }
+
+ } else if ((back[i].r.statuscode==301)
+ || (back[i].r.statuscode==302)
+ || (back[i].r.statuscode==303)
+ || (back[i].r.statuscode==307)
+ || (back[i].r.statuscode==412)
+ || (back[i].r.statuscode==416)
+ ) { // Ne pas prendre le html, erreurs connues et gérées
+#if HTS_DEBUG_CLOSESOCK
+ DEBUG_W("back_wait(301,302,303,307,412,416..): deletehttp\n");
+#endif
+ // Couper connexion
+ deletehttp(&back[i].r); back[i].r.soc=INVALID_SOCKET;
+ back[i].status=0; // terminé
+ // finalize
+ if (back[i].r.statuscode>0) {
+ back_finalize(opt,cache,back,i);
+ }
+ } else { // il faut aller le chercher
+
+ // effacer buffer (requète)
+ if (!noFreebuff) {
+ if (back[i].r.adr!=NULL) {
+ freet(back[i].r.adr);
+ back[i].r.adr=NULL;
+ }
+ back[i].r.size=0;
+ }
+
+ // traiter 206 (partial content)
+ // xxc SI CHUNK VERIFIER QUE CA MARCHE??
+ if (back[i].r.statuscode==206) { // on nous envoie un morceau (la fin) coz une partie sur disque!
+ LLint sz=fsize(back[i].url_sav);
+#if HDEBUG
+ printf("partial content: "LLintP" on disk..\n",(LLint)sz);
+#endif
+ if (sz>=0) {
+ if (!is_hypertext_mime(back[i].r.contenttype)) { // pas HTML
+ if (opt->getmode&2) { // on peut ecrire des non html **sinon ben euhh sera intercepté plus loin, donc rap sur ce qui va sortir**
+ filenote(back[i].url_sav,NULL); // noter fichier comme connu
+ back[i].r.out=fopen(fconv(back[i].url_sav),"ab"); // append
+ if (back[i].r.out) {
+ back[i].r.is_write=1; // écrire
+ back[i].r.size=sz; // déja écrit
+ back[i].r.statuscode=200; // Forcer 'OK'
+ if (back[i].r.totalsize>0)
+ back[i].r.totalsize+=sz; // plus en fait
+ fseek(back[i].r.out,0,SEEK_END); // à la fin
+#if HDEBUG
+ printf("continue interrupted file\n");
+#endif
+ } else { // On est dans la m**
+ back[i].status=0; // terminé (voir plus loin)
+ strcpy(back[i].r.msg,"Can not open partial file");
+ }
+ }
+ } else { // mémoire
+ FILE* fp=fopen(fconv(back[i].url_sav),"rb");
+ if (fp) {
+ LLint alloc_mem=sz + 1;
+ if (back[i].r.totalsize>0)
+ alloc_mem+=back[i].r.totalsize; // AJOUTER RESTANT!
+ if ( (back[i].r.adr=(char*) malloct((INTsys) alloc_mem)) ) {
+ back[i].r.size=sz;
+ if (back[i].r.totalsize>0)
+ back[i].r.totalsize+=sz; // plus en fait
+ if (((int) fread(back[i].r.adr,1,(INTsys)sz,fp)) != sz) {
+ back[i].status=0; // terminé (voir plus loin)
+ strcpy(back[i].r.msg,"Can not read partial file");
+ } else {
+ back[i].r.statuscode=200; // Forcer 'OK'
+#if HDEBUG
+ printf("continue in mem interrupted file\n");
+#endif
+ }
+ } else {
+ back[i].status=0; // terminé (voir plus loin)
+ strcpy(back[i].r.msg,"No memory for partial file");
+ }
+ fclose(fp);
+ } else { // Argh..
+ back[i].status=0; // terminé (voir plus loin)
+ strcpy(back[i].r.msg,"Can not open partial file");
+ }
+ }
+ } else { // Non trouvé??
+ back[i].status=0; // terminé (voir plus loin)
+ strcpy(back[i].r.msg,"Can not find partial file");
+ }
+ // Erreur?
+ if (back[i].status==0) {
+ if (back[i].r.soc!=INVALID_SOCKET) {
+#if HTS_DEBUG_CLOSESOCK
+ DEBUG_W("back_wait(206 solve problems): deletehttp\n");
+#endif
+ deletehttp(&back[i].r);
+ }
+ back[i].r.soc=INVALID_SOCKET;
+ //back[i].r.statuscode=206; ????????
+ back[i].r.statuscode=-5;
+ if (strnotempty(back[i].r.msg))
+ strcpy(back[i].r.msg,"Error attempting to solve status 206 (partial file)");
+ }
+ }
+
+ if (back[i].status!=0) { // non terminé (erreur)
+ if (!back[i].testmode) { // fichier normal
+
+ if (!back[i].r.is_chunk) { // pas de chunk
+ //if (back[i].r.http11!=2) { // pas de chunk
+ back[i].is_chunk=0;
+ back[i].status=1; // start body
+ } else {
+#if CHUNKDEBUG==1
+ printf("chunk encoding detected %s..\n",back[i].url_fil);
+#endif
+ back[i].is_chunk=1;
+ back[i].chunk_adr=NULL;
+ back[i].chunk_size=0;
+ back[i].status=98; // start body wait chunk
+ }
+ if (back[i].rateout>0) {
+ back[i].rateout_time=time_local(); // refresh pour transfer rate
+ }
+#if HDEBUG
+ printf("(buffer) start body!\n");
+#endif
+ } else { // mode test, ne pas passer en 1!!
+ back[i].status=0; // READY
+#if HTS_DEBUG_CLOSESOCK
+ DEBUG_W("back_wait(test ok): deletehttp\n");
+#endif
+ deletehttp(&back[i].r); back[i].r.soc=INVALID_SOCKET;
+ if (back[i].r.statuscode==200) {
+ strcpy(back[i].r.msg,"Test: OK");
+ back[i].r.statuscode=-10; // test réussi
+ }
+ else { // test a échoué, on ne change rien sauf que l'erreur est à titre indicatif
+ char tempo[1000];
+ strcpy(tempo,back[i].r.msg);
+ strcpy(back[i].r.msg,"Test: ");
+ strcat(back[i].r.msg,tempo);
+ }
+
+ }
+ }
+
+ }
+
+ /*}*/
+
+ } // si LF
+ } // r.size>2
+ } // si == 99
+
+ } // si pas d'erreurs
+#if BDEBUG==1
+ printf("bytes overall: %d\n",back[i].r.size);
+#endif
+ } // données dispo
+
+ // en cas d'erreur cl, supprimer éventuel fichier sur disque
+#if HTS_REMOVE_BAD_FILES
+ if (back[i].status<0) {
+ if (!back[i].testmode) { // pas en test
+ remove(back[i].url_sav); // éliminer fichier (endommagé)
+ //printf("&& %s\n",back[i].url_sav);
+ }
+ }
+#endif
+
+ /* funny log for commandline users */
+ //if (!opt->quiet) {
+ // petite animation
+ if (opt->verbosedisplay==1) {
+ if (back[i].status==0) {
+ if (back[i].r.statuscode==200)
+ printf("* %s%s ("LLintP" bytes) - OK"VT_CLREOL"\r",back[i].url_adr,back[i].url_fil,back[i].r.size);
+ else
+ printf("* %s%s ("LLintP" bytes) - %d"VT_CLREOL"\r",back[i].url_adr,back[i].url_fil,back[i].r.size,back[i].r.statuscode);
+ fflush(stdout);
+ }
+ }
+ //}
+
+
+ } // status>0
+ } // for
+
+ // vérifier timeouts
+ if (gestion_timeout) {
+ TStamp act;
+ act=time_local(); // temps en secondes
+ for(i=0;i<back_max;i++) {
+ if (back[i].status>0) { // réception/connexion/..
+ if (back[i].timeout>0) {
+ //printf("time check %d\n",((int) (act-back[i].timeout_refresh))-back[i].timeout);
+ if (((int) (act-back[i].timeout_refresh))>=back[i].timeout) {
+ if (back[i].r.soc!=INVALID_SOCKET) {
+#if HTS_DEBUG_CLOSESOCK
+ DEBUG_W("back_wait(timeout): deletehttp\n");
+#endif
+ deletehttp(&back[i].r);
+ }
+ back[i].r.soc=INVALID_SOCKET;
+ back[i].r.statuscode=-2;
+ if (back[i].status==100)
+ strcpy(back[i].r.msg,"Connect Time Out");
+ else if (back[i].status==101)
+ strcpy(back[i].r.msg,"DNS Time Out");
+ else
+ strcpy(back[i].r.msg,"Receive Time Out");
+ back[i].status=0; // terminé
+ } else if ((back[i].rateout>0) && (back[i].status<99)) {
+ if (((int) (act-back[i].rateout_time))>=HTS_WATCHRATE) { // checker au bout de 15s
+ if ( (int) ((back[i].r.size)/(act-back[i].rateout_time)) < back[i].rateout ) { // trop lent
+ back[i].status=0; // terminé
+ if (back[i].r.soc!=INVALID_SOCKET) {
+#if HTS_DEBUG_CLOSESOCK
+ DEBUG_W("back_wait(rateout): deletehttp\n");
+#endif
+ deletehttp(&back[i].r);
+ }
+ back[i].r.soc=INVALID_SOCKET;
+ back[i].r.statuscode=-3;
+ strcpy(back[i].r.msg,"Transfer Rate Too Low");
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ max_loop--;
+#if HTS_ANALYSTE
+ max_loop_chk++;
+#endif
+ } while((busy_state) && (busy_recv) && (max_loop>0));
+#if HTS_ANALYSTE
+ if ((!busy_recv) && (!busy_state)) {
+ if (max_loop_chk>=1) {
+ Sleep(10); // un tite pause pour éviter les lag..
+ }
+ }
+#endif
+}
+
+int back_checksize(httrackp* opt,lien_back* eback,int check_only_totalsize) {
+ LLint size_to_test;
+ if (check_only_totalsize)
+ size_to_test=eback->r.totalsize;
+ else
+ size_to_test=max(eback->r.totalsize,eback->r.size);
+ if (size_to_test>=0) {
+
+ /* Interdiction taille par le wizard? */
+ if (hts_testlinksize(opt,eback->url_adr,eback->url_fil,(eback->r.totalsize+1023)/1024)==-1) {
+ return 0; /* interdit */
+ }
+
+ /* vérifier taille classique (heml et non html) */
+ if ((istoobig(size_to_test,eback->maxfile_html,eback->maxfile_nonhtml,eback->r.contenttype))) {
+ return 0; /* interdit */
+ }
+ }
+ return 1;
+}
+
+
+// octets transférés + add
+LLint back_transfered(LLint nb,lien_back* back,int back_max) {
+ int i;
+ // ajouter octets en instance
+ for(i=0;i<back_max;i++)
+ if ((back[i].status>0) && (back[i].status<99))
+ nb+=back[i].r.size;
+ return nb;
+}
+
+// infos backing
+// j: 1 afficher sockets 2 afficher autres 3 tout afficher
+void back_info(lien_back* back,int i,int j,FILE* fp) {
+ if (back[i].status>=0) {
+ char s[256];
+ s[0]='\0';
+ back_infostr(back,i,j,s);
+ strcat(s,LF);
+ fprintf(fp,"%s",s);
+ }
+}
+
+// infos backing
+// j: 1 afficher sockets 2 afficher autres 3 tout afficher
+void back_infostr(lien_back* back,int i,int j,char* s) {
+ if (back[i].status>=0) {
+ int aff=0;
+ if (j & 1) {
+ if (back[i].status==100) {
+ strcat(s,"CONNECT ");
+ } else if (back[i].status==99) {
+ strcat(s,"INFOS ");
+ aff=1;
+ } else if (back[i].status==98) {
+ strcat(s,"INFOSC"); // infos chunk
+ aff=1;
+ }
+ else if (back[i].status>0) {
+#if HTS_ANALYSTE==2
+ strcat(s,"WAIT ");
+#else
+ strcat(s,"RECEIVE ");
+#endif
+ aff=1;
+ }
+ }
+ if (j & 2) {
+ if (back[i].status==0) {
+ switch (back[i].r.statuscode) {
+ case 200:
+ strcat(s,"READY ");
+ aff=1;
+ break;
+#if HTS_ANALYSTE==2
+ default:
+ strcat(s,"ERROR ");
+ break;
+#else
+ case -1:
+ strcat(s,"ERROR ");
+ aff=1;
+ break;
+ case -2:
+ strcat(s,"TIMEOUT ");
+ aff=1;
+ break;
+ case -3:
+ strcat(s,"TOOSLOW ");
+ aff=1;
+ break;
+ case 400:
+ strcat(s,"BADREQUEST ");
+ aff=1;
+ break;
+ case 401: case 403:
+ strcat(s,"FORBIDDEN ");
+ aff=1;
+ break;
+ case 404:
+ strcat(s,"NOT FOUND ");
+ aff=1;
+ break;
+ case 500:
+ strcat(s,"SERVERROR ");
+ aff=1;
+ break;
+ default:
+ {
+ char s2[256];
+ sprintf(s2,"ERROR(%d)",back[i].r.statuscode);
+ strcat(s,s2);
+ }
+ aff=1;
+#endif
+ }
+ }
+ }
+
+ if (aff) {
+ {
+ char s2[1024];
+ sprintf(s2,"\"%s",back[i].url_adr); strcat(s,s2);
+
+ if (back[i].url_fil[0]!='/') strcat(s,"/");
+ sprintf(s2,"%s\" ",back[i].url_fil); strcat(s,s2);
+ sprintf(s,LLintP" "LLintP" ",back[i].r.size,back[i].r.totalsize); strcat(s,s2);
+ }
+ }
+ }
+}
+
+// -- backing --
+
+#undef test_flush