diff options
Diffstat (limited to 'src/htsparse.c')
-rw-r--r-- | src/htsparse.c | 2377 |
1 files changed, 2377 insertions, 0 deletions
diff --git a/src/htsparse.c b/src/htsparse.c new file mode 100644 index 0000000..b012a8d --- /dev/null +++ b/src/htsparse.c @@ -0,0 +1,2377 @@ +/* ------------------------------------------------------------ */ +/* +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: Main source */ +/* DIRECT INCLUDE TO httrack.c */ +/* Author: Xavier Roche */ +/* ------------------------------------------------------------ */ + + +#if HTS_ANALYSTE +if (hts_htmlcheck(r.adr,(int)r.size,urladr,urlfil)) { +#endif + FILE* fp=NULL; // fichier écrit localement + char* adr=r.adr; // pointeur (on parcourt) + char* lastsaved; // adresse du dernier octet sauvé + 1 + if ( (opt.debug>1) && (opt.log!=NULL) ) { + fspc(opt.log,"debug"); fprintf(opt.log,"scan file.."LF); test_flush; + } + + + // Indexing! +#if HTS_MAKE_KEYWORD_INDEX + if (opt.kindex) { + if (index_keyword(r.adr,r.size,r.contenttype,savename,opt.path_html)) { + if ( (opt.debug>1) && (opt.log!=NULL) ) { + fspc(opt.log,"debug"); fprintf(opt.log,"indexing file..done"LF); test_flush; + } + } else { + if ( (opt.debug>1) && (opt.log!=NULL) ) { + fspc(opt.log,"debug"); fprintf(opt.log,"indexing file..error!"LF); test_flush; + } + } + } +#endif + + // Now, parsing + if ((opt.getmode & 1) && (ptr>0)) { // récupérer les html sur disque + // créer le fichier html local + HT_ADD_FOP; // écrire peu à peu le fichier + } + + if (!error) { + int detect_title=0; // détection du title + // + char* in_media=NULL; // in other media type (real media and so..) + int intag=0; // on est dans un tag + int incomment=0; // dans un <!-- + int inscript=0; // dans un scipt pour applets javascript) + int inscript_tag=0; // on est dans un <body onLoad="... terminé par > + char inscript_tag_lastc='\0'; + // terminaison (" ou ') du "<body onLoad=.." + int inscriptgen=0; // on est dans un code générant, ex après obj.write(".. + char scriptgen_q='\0'; // caractère faisant office de guillemet (' ou ") + int no_esc_utf=0; // ne pas echapper chars > 127 + int nofollow=0; // ne pas scanner + // + int parseall_lastc='\0'; // dernier caractère parsé pour parseall + int parseall_incomment=0; // dans un /* */ (exemple: a = /* URL */ "img.gif";) + // + char* intag_start=adr; + char* intag_startattr=NULL; + int intag_start_valid=0; + HT_ADD_START; // débuter + + + /* statistics */ + if ((opt.getmode & 1) && (ptr>0)) { + /* + HTS_STAT.stat_files++; + HTS_STAT.stat_bytes+=r.size; + */ + } + + /* Primary list or URLs */ + if (ptr == 0) { + intag=1; + intag_start_valid=0; + } + /* Check is the file is a .js file */ + else if ( + (strfield2(r.contenttype,"application/x-javascript")!=0) + || (strfield2(r.contenttype,"text/css")!=0) + ) { /* JavaScript js file */ + inscript=1; + intag=1; // because après <script> on y est .. - pas utile + intag_start_valid=0; // OUI car nous sommes dans du code, plus dans du "vrai" tag + if ((opt.debug>1) && (opt.log!=NULL)) { + fspc(opt.log,"debug"); fprintf(opt.log,"note: this file is a javascript file"LF); test_flush; + } + } + /* Or a real audio */ + else if (strfield2(r.contenttype,"audio/x-pn-realaudio")!=0) { /* realaudio link file */ + inscript=intag=1; + intag_start_valid=0; + in_media="RAM"; // real media! + } + // Detect UTF8 format + if (is_unicode_utf8((unsigned char*) r.adr, (unsigned int) r.size) == 1) { + no_esc_utf=1; + } else { + no_esc_utf=0; + } + // Hack to prevent any problems with ram files of other files + * ( r.adr + r.size ) = '\0'; + + + // ------------------------------------------------------------ + // analyser ce qu'il y a en mémoire (fichier html) + // on scanne les balises + // ------------------------------------------------------------ +#if HTS_ANALYSTE + _hts_in_html_done=0; // 0% scannés + _hts_cancel=0; // pas de cancel + _hts_in_html_parsing=1; // flag pour indiquer un parsing +#endif + base[0]='\0'; // effacer base-href + lastsaved=adr; + do { + int p=0; + int valid_p=0; // force to take p even if == 0 + int ending_p='\0'; // ending quote? + error=0; + + /* Hack to avoid NULL char problems with C syntax */ + /* Yes, some bogus HTML pages can embed null chars + and therefore can not be properly handled if this hack is not done + */ + if ( ! (*adr) ) { + if ( ((int) (adr - r.adr)) < r.size) + *adr=' '; + } + + + + /* + index.html built here + */ + // Construction index.html (sommaire) + // Avant de tester les a href, + // Ici on teste si l'on doit construire l'index vers le(s) site(s) miroir(s) + if (!makeindex_done) { // autoriation d'écrire un index + if (!detect_title) { + if (opt.depth == liens[ptr]->depth) { // on note toujours les premiers liens + if (!in_media) { + if (opt.makeindex && (ptr>0)) { + if (opt.getmode & 1) { // autorisation d'écrire + p=strfield(adr,"title"); + if (p) { + if (*(adr-1)=='/') p=0; // /title + } else { + if (strfield(adr,"/html")) + p=-1; // noter, mais sans titre + else if (strfield(adr,"body")) + p=-1; // noter, mais sans titre + else if ( ((int) (adr - r.adr) ) >= (r.size-1) ) + p=-1; // noter, mais sans titre + else if ( (int) (adr - r.adr) >= r.size - 2) // we got to hurry + p=-1; // xxc xxc xxc + } + } else + p=0; + + if (p) { // ok center + if (makeindex_fp==NULL) { + verif_backblue(opt.path_html); // générer gif + makeindex_fp=filecreate(fconcat(opt.path_html,"index.html")); + if (makeindex_fp!=NULL) { + + // Header + fprintf(makeindex_fp,template_header, + "<!-- Mirror and index made by HTTrack Website Copier/"HTTRACK_VERSION" "HTTRACK_AFF_AUTHORS" -->" + ); + + } else makeindex_done=-1; // fait, erreur + } + + if (makeindex_fp!=NULL) { + char tempo[HTS_URLMAXSIZE*2]; + char s[HTS_URLMAXSIZE*2]; + char* a=NULL; + char* b=NULL; + s[0]='\0'; + if (p>0) { + a=strchr(adr,'>'); + if (a!=NULL) { + a++; + while(is_space(*a)) a++; // sauter espaces & co + b=strchr(a,'<'); // prochain tag + } + } + if (lienrelatif(tempo,liens[ptr]->sav,concat(opt.path_html,"index.html"))==0) { + detect_title=1; // ok détecté pour cette page! + makeindex_links++; // un de plus + strcpy(makeindex_firstlink,tempo); + // + if ((b==a) || (a==NULL) || (b==NULL)) { // pas de titre + strcpy(s,tempo); + } else if ((b-a)<256) { + b--; + while(is_space(*b)) b--; + strncpy(s,a,b-a+1); + *(s+(b-a)+1)='\0'; + } + + // Body + fprintf(makeindex_fp,template_body, + tempo, + s + ); + + } + } + } + } + } + + } else if (liens[ptr]->depth<opt.depth) { // on a sauté level1+1 et level1 + HT_INDEX_END; + } + } // if (opt.makeindex) + } + // FIN Construction index.html (sommaire) + /* + end -- index.html built here + */ + + + + /* Parse */ + if ( + (*adr=='<') /* No starting tag */ + && (!inscript) /* Not in (java)script */ + && (!incomment) /* Not in comment (<!--) */ + ) { + intag=1; + parseall_incomment=0; + //inquote=0; // effacer quote + intag_start=adr; intag_start_valid=1; + codebase[0]='\0'; // effacer éventuel codebase + + if (opt.getmode & 1) { // sauver html + p=strfield(adr,"</html"); + if (p==0) p=strfield(adr,"<head>"); + // if (p==0) p=strfield(adr,"<doctype"); + if (p) { + if (strnotempty(opt.footer)) { + char tempo[1024+HTS_URLMAXSIZE*2]; + char gmttime[256]; + char* eol="\n"; + tempo[0]='\0'; + if (strchr(r.adr,'\r')) + eol="\r\n"; + time_gmt_rfc822(gmttime); + strcat(tempo,eol); + sprintf(tempo+strlen(tempo),opt.footer,jump_identification(urladr),urlfil,gmttime,"","","","","","","",""); + strcat(tempo,eol); + //fwrite(tempo,1,strlen(tempo),fp); + HT_ADD(tempo); + } + } + } + + // éliminer les <!-- (commentaires) : intag dévalidé + if (*(adr+1)=='!') + if (*(adr+2)=='-') + if (*(adr+3)=='-') { + intag=0; + incomment=1; + intag_start_valid=0; + } + + } + else if ( + (*adr=='>') /* ending tag */ + && ( (!inscript) || (inscript_tag) ) /* and in tag (or in script) */ + ) { + if (inscript_tag) { + inscript_tag=inscript=0; + intag=0; + incomment=0; + intag_start_valid=0; + } else if (!incomment) { + intag=0; //inquote=0; + + // entrée dans du javascript? + // on parse ICI car il se peut qu'on ait eu a parser les src=.. dedans + //if (!inscript) { // sinon on est dans un obj.write(".. + if ((intag_start_valid) && + ( + check_tag(intag_start,"script") + || + check_tag(intag_start,"style") + ) + ) { + char* a=intag_start; // < + // ** while(is_realspace(*(--a))); + if (*a=='<') { // sûr que c'est un tag? + inscript=1; + intag=1; // because après <script> on y est .. - pas utile + intag_start_valid=0; // OUI car nous sommes dans du code, plus dans du "vrai" tag + } + } + } else { /* end of comment? */ + // vérifier fermeture correcte + if ( (*(adr-1)=='-') && (*(adr-2)=='-') ) { + intag=0; + incomment=0; + intag_start_valid=0; + } +#if GT_ENDS_COMMENT + /* wrong comment ending */ + else { + /* check if correct ending does not exists + <!-- foo > example <!-- bar > is sometimes accepted by browsers + when no --> is used somewhere else.. darn those browsers are dirty + */ + if (!strstr(adr,"-->")) { + intag=0; + incomment=0; + intag_start_valid=0; + } + } +#endif + } + //} + } + //else if (*adr==34) { + // inquote=(inquote?0:1); + //} + else if (intag || inscript) { // nous sommes dans un tag/commentaire, tester si on recoit un tag + int p_type=0; + int p_nocatch=0; + int p_searchMETAURL=0; // chercher ..URL=<url> + int add_class=0; // ajouter .class + int add_class_dots_to_patch=0; // number of '.' in code="x.y.z<realname>" + char* p_flush=NULL; + + + // ------------------------------------------------------------ + // parsing évolé + // ------------------------------------------------------------ + if (((isalpha((unsigned char)*adr)) || (*adr=='/') || (inscript) || (inscriptgen))) { // sinon pas la peine de tester.. + + + /* caractère de terminaison pour "miniparsing" javascript=.. ? + (ex: <a href="javascript:()" action="foo"> ) */ + if (inscript_tag) { + if (inscript_tag_lastc) { + if (*adr == inscript_tag_lastc) { + /* sortir */ + inscript_tag=inscript=0; + incomment=0; + } + } + } + + + // Note: + // Certaines pages ne respectent pas le html + // notamment les guillements ne sont pas fixés + // Nous sommes dans un tag, donc on peut faire un test plus + // large pour pouvoi prendre en compte ces particularités + + // à vérifier: ACTION, CODEBASE, VRML + + if (in_media) { + if (strcmp(in_media,"RAM")==0) { // real media + p=0; + valid_p=1; + } + } else if (ptr>0) { /* pas première page 0 (primary) */ + p=0; // saut pour le nom de fichier: adresse nom fichier=adr+p + + // ------------------------------ + // détection d'écriture JavaScript. + // osons les obj.write et les obj.href=.. ! osons! + // note: inscript==1 donc on sautera après les \" + if (inscript) { + if (inscriptgen) { // on est déja dans un objet générant.. + if (*adr==scriptgen_q) { // fermeture des " ou ' + if (*(adr-1)!='\\') { // non + inscriptgen=0; // ok parsing terminé + } + } + } else { + char* a=NULL; + char check_this_fking_line=0; // parsing code javascript.. + char must_be_terminated=0; // caractère obligatoire de terminaison! + int token_size; + if (!(token_size=strfield(adr,".writeln"))) // détection ...objet.write[ln]("code html")... + token_size=strfield(adr,".write"); + if (token_size) { + a=adr+token_size; + while(is_realspace(*a)) a++; // sauter espaces + if (*a=='(') { // début parenthèse + check_this_fking_line=2; // à parser! + must_be_terminated=')'; + a++; // sauter ( + } + } + // euhh ??? ??? + /* else if (strfield(adr,".href")) { // détection ...objet.href="... + a=adr+5; + while(is_realspace(*a)) a++; // sauter espaces + if (*a=='=') { // ohh un égal + check_this_fking_line=1; // à noter! + must_be_terminated=';'; // et si t'as oublié le ; tu sais pas coder + a++; // sauter = + } + + }*/ + + // on a un truc du genre instruction"code généré" dont on parse le code + if (check_this_fking_line) { + while(is_realspace(*a)) a++; + if ((*a=='\'') || (*a=='"')) { // départ de '' ou "" + char *b; + int ex=0; + scriptgen_q=*a; // quote + b=a+1; // départ de la chaîne + // vérifier forme ("code") et pas ("code"+var), ingérable + do { + a++; // caractère suivant + if (*a==scriptgen_q) if (*(a-1)!='\\') // quote non slash + ex=1; // sortie + if ((*a==10) || (*a==13)) + ex=1; + } while(!ex); + if (*a==scriptgen_q) { // fin du quote + a++; + while(is_realspace(*a)) a++; + if (*a==must_be_terminated) { // parenthèse fermante: ("..") + + // bon, on doit parser une ligne javascript + // 1) si check.. ==1 alors c'est un nom de fichier direct, donc + // on fixe p sur le saut nécessaire pour atteindre le nom du fichier + // et le moteur se débrouillera ensuite tout seul comme un grand + // 2) si check==2 c'est un peu plus tordu car là on génére du + // code html au sein de code javascript au sein de code html + // dans ce cas on doit fixer un flag à un puis ensuite dans la boucle + // on devra parser les instructions standard comme <a href etc + // NOTE: le code javascript autogénéré n'est pas pris en compte!! + // (et ne marche pas dans 50% des cas de toute facon!) + if (check_this_fking_line==1) { + p=(int) (b - adr); // calculer saut! + } else { + inscriptgen=1; // SCRIPTGEN actif + adr=b; // jump + } + + if ((opt.debug>1) && (opt.log!=NULL)) { + char str[512]; + str[0]='\0'; + strncat(str,b,minimum((int) (a - b + 1), 32)); + fspc(opt.log,"debug"); fprintf(opt.log,"active code (%s) detected in javascript: %s"LF,(check_this_fking_line==2)?"parse":"pickup",str); test_flush; + } + } + + } + + } + + + } + } + } + // fin detection code générant javascript vers html + // ------------------------------ + + + // analyse proprement dite, A HREF=.. etc.. + if (!p) { + // si dans un tag, et pas dans un script - sauf si on analyse un obj.write(".. + if ((intag && (!inscript)) || inscriptgen) { + if ( (*(adr-1)=='<') || (is_space(*(adr-1))) ) { // <tag < tag etc + // <A HREF=.. pour les liens HTML + p=rech_tageq(adr,"href"); + if (p) { // href.. tester si c'est une bas href! + if ((intag_start_valid) && check_tag(intag_start,"base")) { // oui! + // ** note: base href et codebase ne font pas bon ménage.. + p_type=2; // c'est un chemin + } + } + + /* Tags supplémentaires à vérifier (<img src=..> etc) */ + if (p==0) { + int i=0; + while( (p==0) && (strnotempty(hts_detect[i])) ) { + p=rech_tageq(adr,hts_detect[i]); + i++; + } + } + + /* Tags supplémentaires en début à vérifier (<object .. hotspot1=..> etc) */ + if (p==0) { + int i=0; + while( (p==0) && (strnotempty(hts_detectbeg[i])) ) { + p=rech_tageqbegdigits(adr,hts_detectbeg[i]); + i++; + } + } + + /* Tags supplémentaires à vérifier : URL=.. */ + if (p==0) { + int i=0; + while( (p==0) && (strnotempty(hts_detectURL[i])) ) { + p=rech_tageq(adr,hts_detectURL[i]); + i++; + } + if (p) + p_searchMETAURL=1; + } + + /* Tags supplémentaires à vérifier, mais à ne pas capturer */ + if (p==0) { + int i=0; + while( (p==0) && (strnotempty(hts_detectandleave[i])) ) { + p=rech_tageq(adr,hts_detectandleave[i]); + i++; + } + if (p) + p_nocatch=1; /* ne pas rechercher */ + } + + /* Evénements */ + if (p==0) { + int i=0; + /* détection onLoad etc */ + while( (p==0) && (strnotempty(hts_detect_js[i])) ) { + p=rech_tageq(adr,hts_detect_js[i]); + i++; + } + /* non détecté - détecter également les onXxxxx= */ + if (p==0) { + if ( (*adr=='o') && (*(adr+1)=='n') && isUpperLetter(*(adr+2)) ) { + p=0; + while(isalpha((unsigned char)adr[p]) && (p<64) ) p++; + if (p<64) { + while(is_space(adr[p])) p++; + if (adr[p]=='=') + p++; + else p=0; + } else p=0; + } + } + /* OK, événement repéré */ + if (p) { + inscript_tag_lastc=*(adr+p); /* à attendre à la fin */ + adr+=p; /* saut */ + /* + On est désormais dans du code javascript + */ + inscript_tag=inscript=1; + } + p=0; /* quoi qu'il arrive, ne rien démarrer ici */ + } + + // <APPLET CODE=.. pour les applet java.. [CODEBASE (chemin..) à faire] + if (p==0) { + p=rech_tageq(adr,"code"); + if (p) { + if ((intag_start_valid) && check_tag(intag_start,"applet")) { // dans un <applet ! + p_type=-1; // juste le nom de fichier+dossier, écire avant codebase + add_class=1; // ajouter .class au besoin + + // vérifier qu'il n'y a pas de codebase APRES + // sinon on swappe les deux. + // pas très propre mais c'est ce qu'il y a de plus simple à faire!! + + { + char *a; + a=adr; + while((*a) && (*a!='>') && (!rech_tageq(a,"codebase"))) a++; + if (rech_tageq(a,"codebase")) { // banzai! codebase= + char* b; + b=strchr(a,'>'); + if (b) { + if (((int) (b - adr)) < 1000) { // au total < 1Ko + char tempo[HTS_URLMAXSIZE*2]; + tempo[0]='\0'; + strncat(tempo,a,(int) (b - a) ); + strcat( tempo," "); + strncat(tempo,adr,(int) (a - adr - 1)); + // éventuellement remplire par des espaces pour avoir juste la taille + while((int) strlen(tempo)<((int) (b - adr))) + strcat(tempo," "); + // pas d'erreur? + if ((int) strlen(tempo) == ((int) (b - adr) )) { + strncpy(adr,tempo,strlen(tempo)); // PAS d'octet nul à la fin! + p=0; // DEVALIDER!! + p_type=0; + add_class=0; + } + } + } + } + } + + } + } + } + + // liens à patcher mais pas à charger (ex: codebase) + if (p==0) { // note: si non chargé (ex: ignorer .class) patché tout de même + p=rech_tageq(adr,"codebase"); + if (p) { + if ((intag_start_valid) && check_tag(intag_start,"applet")) { // dans un <applet ! + p_type=-2; + } else p=-1; // ne plus chercher + } + } + + + // Meta tags pour robots + if (p==0) { + if (opt.robots) { + if ((intag_start_valid) && check_tag(intag_start,"meta")) { + if (rech_tageq(adr,"name")) { // name=robots.txt + char tempo[1100]; + char* a; + tempo[0]='\0'; + a=strchr(adr,'>'); +#if DEBUG_ROBOTS + printf("robots.txt meta tag detected\n"); +#endif + if (a) { + if (((int) (a - adr)) < 999 ) { + strncat(tempo,adr,(int) (a - adr)); + if (strstrcase(tempo,"content")) { + if (strstrcase(tempo,"robots")) { + if (strstrcase(tempo,"nofollow")) { +#if DEBUG_ROBOTS + printf("robots.txt meta tag: nofollow in %s%s\n",urladr,urlfil); +#endif + nofollow=1; // NE PLUS suivre liens dans cette page + if (opt.errlog) { + fspc(opt.errlog,"warning"); fprintf(opt.errlog,"Link %s%s not scanned (follow robots meta tag)"LF,urladr,urlfil); + test_flush; + } + } + } + } + } + } + } + } + } + } + + // entrée dans une applet javascript + /*if (!inscript) { // sinon on est dans un obj.write(".. + if (p==0) + if (rech_sampletag(adr,"script")) + if (check_tag(intag_start,"script")) { + inscript=1; + } + }*/ + + // Ici on procède à une analyse du code javascript pour tenter de récupérer + // certains fichiers évidents. + // C'est devenu obligatoire vu le nombre de pages qui intègrent + // des images réactives par exemple + } + } else if (inscript) { + if ( + ( + (strfield(adr,"/script")) + || + (strfield(adr,"/style")) + ) + ) { + char* a=adr; + //while(is_realspace(*(--a))); + while( is_realspace(*a) ) a--; + a--; + if (*a=='<') { // sûr que c'est un tag? + inscript=0; + } + } else { + /* + Script Analyzing - different types supported: + foo="url" + foo("url") or foo(url) + foo "url" + */ + int nc; + char expected = '='; // caractère attendu après + char* expected_end = ";"; + int can_avoid_quotes=0; + char quotes_replacement='\0'; + if (inscript_tag) + expected_end=";\"\'"; // voir a href="javascript:doc.location='foo'" + nc = strfield(adr,".src"); // nom.src="image"; + if (!nc) nc = strfield(adr,".location"); // document.location="doc" + if (!nc) nc = strfield(adr,".href"); // document.location="doc" + if (!nc) if ( (nc = strfield(adr,".open")) ) { // window.open("doc",.. + expected='('; // parenthèse + expected_end="),"; // fin: virgule ou parenthèse + } + if (!nc) if ( (nc = strfield(adr,".replace")) ) { // window.replace("url") + expected='('; // parenthèse + expected_end=")"; // fin: parenthèse + } + if (!nc) if ( (nc = strfield(adr,".link")) ) { // window.link("url") + expected='('; // parenthèse + expected_end=")"; // fin: parenthèse + } + if (!nc) if ( (nc = strfield(adr,"url")) ) { // url(url) + expected='('; // parenthèse + expected_end=")"; // fin: parenthèse + can_avoid_quotes=1; + quotes_replacement=')'; + } + if (!nc) if ( (nc = strfield(adr,"import")) ) { // import "url" + if (is_space(*(adr+nc))) { + expected=0; // no char expected + } else + nc=0; + } + if (nc) { + char *a; + a=adr+nc; + while(is_realspace(*a)) a++; + if ((*a == expected) || (!expected)) { + if (expected) + a++; + while(is_realspace(*a)) a++; + if ((*a==34) || (*a=='\'') || (can_avoid_quotes)) { + char *b,*c; + int ndelim=1; + if ((*a==34) || (*a=='\'')) + a++; + else + ndelim=0; + b=a; + if (ndelim) { + while((*b!=34) && (*b!='\'') && (*b!='\0')) b++; + } + else { + while((*b != quotes_replacement) && (*b!='\0')) b++; + } + c=b--; c+=ndelim; + while(*c==' ') c++; + if ((strchr(expected_end,*c)) || (*c=='\n') || (*c=='\r')) { + c-=(ndelim+1); + if ((int) (c - a + 1)) { + if ((opt.debug>1) && (opt.log!=NULL)) { + char str[512]; + str[0]='\0'; + strncat(str,a,minimum((int) (c - a + 1),32)); + fspc(opt.log,"debug"); fprintf(opt.log,"link detected in javascript: %s"LF,str); test_flush; + } + p=(int) (a - adr); // p non nul: TRAITER CHAINE COMME FICHIER + if (can_avoid_quotes) { + ending_p=quotes_replacement; + } + } + } + + + } + } + } + + } + } + } + + } else { // ptr == 0 + //p=rech_tageq(adr,"primary"); // lien primaire, yeah + p=0; // No stupid tag anymore, raw link + valid_p=1; // Valid even if p==0 + while ((adr[p] == '\r') || (adr[p] == '\n')) + p++; + //can_avoid_quotes=1; + ending_p='\r'; + } + + } else if (isspace((unsigned char)*adr)) { + intag_startattr=adr+1; // attribute in tag (for dirty parsing) + } + + + // ------------------------------------------------------------ + // dernier recours - parsing "sale" : détection systématique des .gif, etc. + // risque: générer de faux fichiers parazites + // fix: ne parse plus dans les commentaires + // ------------------------------------------------------------ + if ( (opt.parseall) && (ptr>0) && (!in_media) ) { // option parsing "brut" + int incomment_justquit=0; + if (!is_realspace(*adr)) { + int noparse=0; + + // Gestion des /* */ + if (inscript) { + if (parseall_incomment) { + if ((*adr=='/') && (*(adr-1)=='*')) + parseall_incomment=0; + incomment_justquit=1; // ne pas noter dernier caractère + } else { + if ((*adr=='/') && (*(adr+1)=='*')) + parseall_incomment=1; + } + } else + parseall_incomment=0; + + /* vérifier que l'on est pas dans un <!-- --> pur */ + if ( (!intag) && (incomment) && (!inscript)) + noparse=1; /* commentaire */ + + // recherche d'URLs + if ((!parseall_incomment) && (!noparse)) { + if (!p) { // non déja trouvé + if (adr != r.adr) { // >1 caractère + // scanner les chaines + if ((*adr == '\"') || (*adr=='\'')) { // "xx.gif" 'xx.gif' + if (strchr("=(,",parseall_lastc)) { // exemple: a="img.gif.. + char *a=adr; + char stop=*adr; // " ou ' + int count=0; + + // sauter caractères + a++; + // copier + while((*a) && (*a!='\'') && (*a!='\"') && (count<HTS_URLMAXSIZE)) { count++; a++; } + + // ok chaine terminée par " ou ' + if ((*a == stop) && (count<HTS_URLMAXSIZE) && (count>0)) { + char c; + char* aend; + // + aend=a; // sauver début + a++; + while(is_taborspace(*a)) a++; + c=*a; + if (strchr("),;>/+\r\n",c)) { // exemple: ..img.gif"; + // le / est pour funct("img.gif" /* URL */); + char tempo[HTS_URLMAXSIZE*2]; + char type[256]; + int url_ok=0; // url valide? + tempo[0]='\0'; type[0]='\0'; + // + strncat(tempo,adr+1,count); + // + if ((!strchr(tempo,' ')) || inscript) { // espace dedans: méfiance! (sauf dans code javascript) + int invalid_url=0; + + // escape + unescape_amp(tempo); + + // Couper au # ou ? éventuel + { + char* a=strchr(tempo,'#'); + if (a) + *a='\0'; + a=strchr(tempo,'?'); + if (a) + *a='\0'; + } + + // vérifier qu'il n'y a pas de caractères spéciaux + if (!strnotempty(tempo)) + invalid_url=1; + else if (strchr(tempo,'*') + || strchr(tempo,'<') + || strchr(tempo,'>')) + invalid_url=1; + + /* non invalide? */ + if (!invalid_url) { + // Un plus à la fin? Alors ne pas prendre sauf si extension ("/toto.html#"+tag) + if (c!='+') { // PAS de plus à la fin + char* a; + // "Comparisons of scheme names MUST be case-insensitive" (RFC2616) + //if ((strncmp(tempo,"http://",7)==0) || (strncmp(tempo,"ftp://",6)==0)) // ok pas de problème + if ( + (strfield(tempo,"http:")) + || (strfield(tempo,"ftp:")) +#if HTS_USEOPENSSL + || (strfield(tempo,"https:")) +#endif + ) // ok pas de problème + url_ok=1; + else if (tempo[strlen(tempo)-1]=='/') { // un slash: ok.. + if (inscript) // sinon si pas javascript, méfiance (répertoire style base?) + url_ok=1; + } else if ((a=strchr(tempo,'/'))) { // un slash: ok.. + if (inscript) { // sinon si pas javascript, méfiance (style "text/css") + if (strchr(a+1,'/')) // un seul / : abandon (STYLE type='text/css') + url_ok=1; + } + } + } + // Prendre si extension reconnue + if (!url_ok) { + get_httptype(type,tempo,0); + if (strnotempty(type)) // type reconnu! + url_ok=1; + else if (is_dyntype(get_ext(tempo))) // reconnu php,cgi,asp.. + url_ok=1; + // MAIS pas les foobar@aol.com !! + if (strchr(tempo,'@')) + url_ok=0; + } + // + // Ok, cela pourrait être une URL + if (url_ok) { + + // Check if not fodbidden tag (id,name..) + if (intag_start_valid) { + if (intag_start) + if (intag_startattr) + if (intag) + if (!inscript) + if (!incomment) { + int i=0,nop=0; + while( (nop==0) && (strnotempty(hts_nodetect[i])) ) { + nop=rech_tageq(intag_startattr,hts_nodetect[i]); + i++; + } + // Forbidden tag + if (nop) { + url_ok=0; + if ((opt.debug>1) && (opt.log!=NULL)) { + fspc(opt.log,"debug"); fprintf(opt.log,"dirty parsing: bad tag avoided: %s"LF,hts_nodetect[i-1]); test_flush; + } + } + } + } + + + // Accepter URL, on la traitera comme une URL normale!! + if (url_ok) + p=1; + + } + } + } + } + } + } + } + } + } // p == 0 + + // plus dans un commentaire + if (!incomment_justquit) + parseall_lastc=*adr; // caractère avant le prochain + + } // not in comment + + } // if realspace + } // if parseall + + + // ------------------------------------------------------------ + // p!=0 : on a repéré un éventuel lien + // ------------------------------------------------------------ + // + if ((p>0) || (valid_p)) { // on a repéré un lien + //int lien_valide=0; + char* eadr=NULL; /* fin de l'URL */ + char* quote_adr=NULL; /* adresse du ? dans l'adresse */ + int ok=1; + char quote='\0'; + + // si nofollow ou un stop a été déclenché, réécrire tous les liens en externe + if ((nofollow) || (opt.state.stop)) + p_nocatch=1; + + // écrire codebase avant, flusher avant code + if ((p_type==-1) || (p_type==-2)) { + if ((opt.getmode & 1) && (ptr>0)) { + HT_ADD_ADR; // refresh + } + lastsaved=adr; // dernier écrit+1 + } + + // sauter espaces + adr+=p; + while((is_space(*adr)) && (quote=='\0')) { + if (!quote) + if ((*adr=='\"') || (*adr=='\'')) + quote=*adr; // on doit attendre cela à la fin + // puis quitter + adr++; // sauter les espaces, "" et cie + } + + /* Stop at \n (LF) if primary links*/ + if (ptr == 0) + quote='\n'; + /* s'arrêter que ce soit un ' ou un " : pour document.write('<img src="foo'+a); par exemple! */ + else if (inscript) + quote='\0'; + + // sauter éventuel \" ou \' javascript + if (inscript) { // on est dans un obj.write(".. + if (*adr=='\\') { + if ((*(adr+1)=='\'') || (*(adr+1)=='"')) { // \" ou \' + adr+=2; // sauter + } + } + } + + // sauter content="1;URL=http://.. + if (p_searchMETAURL) { + int l=0; + while( + (adr + l + 4 < r.adr + r.size) + && (!strfield(adr+l,"URL=")) + && (l<128) ) l++; + if (!strfield(adr+l,"URL=")) + ok=-1; + else + adr+=(l+4); + } + + /* éviter les javascript:document.location=.. : les parser, plutôt */ + if (ok!=-1) { + if (strfield(adr,"javascript:")) { + ok=-1; + /* + On est désormais dans du code javascript + */ + inscript_tag=inscript=1; + inscript_tag_lastc=quote; /* à attendre à la fin */ + } + } + + if (p_type==1) { + if (*adr=='#') { + adr++; // sauter # pour usemap etc + } + } + eadr=adr; + + // ne pas flusher après code si on doit écrire le codebase avant! + if ((p_type!=-1) && (p_type!=2) && (p_type!=-2)) { + if ((opt.getmode & 1) && (ptr>0)) { + HT_ADD_ADR; // refresh + } + lastsaved=adr; // dernier écrit+1 + // après on écrira soit les données initiales, + // soir une URL/lien modifié! + } else if (p_type==-1) p_flush=adr; // flusher jusqu'à adr ensuite + + if (ok!=-1) { // continuer + // découper le lien + do { + if ((* (unsigned char*) eadr)<32) { // caractère de contrôle (ou \0) + if (!is_space(*eadr)) + ok=0; + } + if ( ( ((int) (eadr - adr)) ) > HTS_URLMAXSIZE) // ** trop long, >HTS_URLMAXSIZE caractères (on prévoit HTS_URLMAXSIZE autres pour path) + ok=-1; // ne pas traiter ce lien + + if (ok > 0) { + //if (*eadr!=' ') { + if (is_space(*eadr)) { // guillemets,CR, etc + if ((!quote) || (*eadr==quote)) // si pas d'attente de quote spéciale ou si quote atteinte + ok=0; + } else if (ending_p && (*eadr==ending_p)) + ok=0; + else { + switch(*eadr) { + case '>': + if (!quote) { + if (!inscript) { + intag=0; // PLUS dans un tag! + intag_start_valid=0; + } + ok=0; + } + break; + /*case '<':*/ + case '#': + if (*(eadr-1) != '&') // ( + ok=0; + break; + // case '?': non! + case '\\': if (inscript) ok=0; break; // \" ou \' point d'arrêt + case '?': quote_adr=adr; break; // noter position query + } + } + //} + } + eadr++; + } while(ok==1); + + // Empty link detected + if ( (((int) (eadr - adr))) <= 1) { // link empty + ok=-1; // No + if (*adr != '#') { // Not empty+unique # + if ( (((int) (eadr - adr)) == 1)) { // 1=link empty with delim (end_adr-start_adr) + if (quote) { + if ((opt.getmode & 1) && (ptr>0)) { + HT_ADD("#"); // We add this for a <href=""> + } + } + } + } + } + + } + + if (ok==0) { // tester un lien + char lien[HTS_URLMAXSIZE*2]; + int meme_adresse=0; // 0 par défaut pour primary + //char *copie_de_adr=adr; + //char* p; + + // construire lien (découpage) + if ( (((int) (eadr - adr))-1) < HTS_URLMAXSIZE ) { // pas trop long? + strncpy(lien,adr,((int) (eadr - adr))-1); + *(lien+ (((int) (eadr - adr)))-1 )='\0'; + //printf("link: %s\n",lien); + // supprimer les espaces + while((lien[strlen(lien)-1]==' ') && (strnotempty(lien))) lien[strlen(lien)-1]='\0'; + + +#if HTS_STRIP_DOUBLE_SLASH + // supprimer les // en / (sauf pour http://) + { + char *a,*p,*q; + int done=0; + a=strchr(lien,':'); // http:// + if (a) { + a++; + while(*a=='/') a++; // position après http:// + } else { + a=lien; // début + while(*a=='/') a++; // position après http:// + } + q=strchr(a,'?'); // ne pas traiter après '?' + if (!q) + q=a+strlen(a)-1; + while(( p=strstr(a,"//")) && (!done) ) { // remplacer // par / + if ((int) p>(int) q) { // après le ? (toto.cgi?param=1//2.3) + done=1; // stopper + } else { + char tempo[HTS_URLMAXSIZE*2]; + tempo[0]='\0'; + strncat(tempo,a,(int) p - (int) a); + strcat (tempo,p+1); + strcpy(a,tempo); // recopier + } + } + } +#endif + + } else + lien[0]='\0'; // erreur + + // ------------------------------------------------------ + // Lien repéré et extrait + if (strnotempty(lien)>0) { // construction du lien + char adr[HTS_URLMAXSIZE*2],fil[HTS_URLMAXSIZE*2]; // ATTENTION adr cache le "vrai" adr + int forbidden_url=-1; // lien non interdit (mais non autorisé..) + int just_test_it=0; // mode de test des liens + int set_prio_to=0; // pour capture de page isolée + int import_done=0; // lien importé (ne pas scanner ensuite *à priori*) + // + adr[0]='\0'; fil[0]='\0'; + // + // 0: autorisé + // 1: interdit (patcher tout de même adresse) + + if ((opt.debug>1) && (opt.log!=NULL)) { + fspc(opt.log,"debug"); fprintf(opt.log,"link detected in html: %s"LF,lien); test_flush; + } + + // external check +#if HTS_ANALYSTE + if (!hts_htmlcheck_linkdetected(lien)) { + error=1; // erreur + if (opt.errlog) { + fspc(opt.errlog,"error"); fprintf(opt.errlog,"Link %s refused by external wrapper"LF,lien); + test_flush; + } + } +#endif + + // purger espaces de début et fin, CR,LF résiduels + // (IMG SRC="foo.<\n>gif") + { + char* a; + while (is_realspace(lien[0])) { + char tempo[HTS_URLMAXSIZE*2]; + tempo[0]='\0'; + strcpy(tempo,lien+1); + strcpy(lien,tempo); + } + while(strnotempty(lien) + && (is_realspace(lien[max(0,(int)(strlen(lien))-1)])) ) { + lien[strlen(lien)-1]='\0'; + } + while ((a=strchr(lien,'\n'))) { + char tempo[HTS_URLMAXSIZE*2]; + tempo[0]='\0'; + strncat(tempo,lien,(int) (a - lien)); + strcat(tempo,a+1); + strcpy(lien,tempo); + } + while ((a=strchr(lien,'\r'))) { + char tempo[HTS_URLMAXSIZE*2]; + tempo[0]='\0'; + strncat(tempo,lien,(int) (a - lien)); + strcat(tempo,a+1); + strcpy(lien,tempo); + } + } + + /* Unescape/escape %20 and other */ + { + char query[HTS_URLMAXSIZE*2]; + char* a=strchr(lien,'?'); + if (a) { + strcpy(query,a); + *a='\0'; + } else + query[0]='\0'; + // conversion & -> & et autres joyeusetés + unescape_amp(lien); + unescape_amp(query); + // décoder l'inutile (%2E par exemple) et coder espaces + // XXXXXXXXXXXXXXXXX strcpy(lien,unescape_http(lien)); + strcpy(lien,unescape_http_unharm(lien, (no_esc_utf)?0:1)); + escape_spc_url(lien); + strcat(lien,query); /* restore */ + } + + // convertir les éventuels \ en des / pour éviter des problèmes de reconnaissance! + { + char* a=jump_identification(lien); + while( (a=strchr(a,'\\')) ) *a='/'; + } + + // supprimer le(s) ./ + while ((lien[0]=='.') && (lien[1]=='/')) { + char tempo[HTS_URLMAXSIZE*2]; + strcpy(tempo,lien+2); + strcpy(lien,tempo); + } + if (strnotempty(lien)==0) // sauf si plus de nom de fichier + strcpy(lien,"./"); + + // vérifie les /~machin -> /~machin/ + // supposition dangereuse? + // OUI!! +#if HTS_TILDE_SLASH + if (lien[strlen(lien)-1]!='/') { + char *a=lien+strlen(lien)-1; + // éviter aussi index~1.html + while (((int) a>(int) lien) && (*a!='~') && (*a!='/') && (*a!='.')) a--; + if (*a=='~') { + strcat(lien,"/"); // ajouter slash + } + } +#endif + + // APPLET CODE="mixer.MixerApplet.class" --> APPLET CODE="mixer/MixerApplet.class" + // yes, this is dirty + // but I'm so lazzy.. + // and besides the java "code" convention is really a pain in html code + if (p_type==-1) { + char* a=strrchr(lien,'.'); + add_class_dots_to_patch=0; + if (a) { + char* b; + do { + b=strchr(lien,'.'); + if ((b != a) && (b)) { + add_class_dots_to_patch++; + *b='/'; + } + } while((b != a) && (b)); + } + } + + // éliminer les éventuels :80 (port par défaut!) + if (link_has_authority(lien)) { + char * a; + a=strstr(lien,"//"); // "//" authority + if (a) + a+=2; + else + a=lien; + // while((*a) && (*a!='/') && (*a!=':')) a++; + a=jump_toport(a); + if (a) { // port + int port=0; + int defport=80; + char* b=a+1; +#if HTS_USEOPENSSL + // FIXME + //if (strfield(adr, "https:")) { + //} +#endif + while(isdigit((unsigned char)*b)) { port*=10; port+=(int) (*b-'0'); b++; } + if (port==defport) { // port 80, default - c'est débile + char tempo[HTS_URLMAXSIZE*2]; + tempo[0]='\0'; + strncat(tempo,lien,(int) (a - lien)); + strcat(tempo,a+3); // sauter :80 + strcpy(lien,tempo); + } + } + } + + // filtrer les parazites (mailto & cie) + /* + if (strfield(lien,"mailto:")) { // ne pas traiter + error=1; + } else if (strfield(lien,"news:")) { // ne pas traiter + error=1; + } + */ + + // vérifier que l'on ne doit pas ajouter de .class + if (!error) { + if (add_class) { + char *a = lien+strlen(lien)-1; + while(( a > lien) && (*a!='/') && (*a!='.')) a--; + if (*a != '.') + strcat(lien,".class"); // ajouter .class + else if (!strfield2(a,".class")) + strcat(lien,".class"); // idem + } + } + + // si c'est un chemin, alors vérifier (toto/toto.html -> http://www/toto/) + if (!error) { + if ((opt.debug>1) && (opt.log!=NULL)) { + fspc(opt.log,"debug"); fprintf(opt.log,"position link check %s"LF,lien); test_flush; + } + + if ((p_type==2) || (p_type==-2)) { // code ou codebase + // Vérifier les codebase=applet (au lieu de applet/) + if (p_type==-2) { // codebase + if (strnotempty(lien)) { + if (fil[strlen(lien)-1]!='/') { // pas répertoire + strcat(lien,"/"); + } + } + } + /* only one ending / (bug on some pages) */ + if ((int)strlen(lien)>2) { + while( (lien[strlen(lien)-2]=='/') && ((int)strlen(lien)>2) ) /* double // (bug) */ + lien[strlen(lien)-1]='\0'; + } + // copier nom host si besoin est + if (!link_has_authority(lien)) { // pas de http:// + char adr2[HTS_URLMAXSIZE*2],fil2[HTS_URLMAXSIZE*2]; // ** euh ident_url_relatif?? + if (ident_url_relatif(lien,urladr,urlfil,adr2,fil2)<0) { + error=1; + } else { + strcpy(lien,"http://"); + strcat(lien,adr2); + if (*fil2!='/') + strcat(lien,"/"); + strcat(lien,fil2); + { + char* a; + a=lien+strlen(lien)-1; + while((*a) && (*a!='/') && ( a> lien)) a--; + if (*a=='/') { + *(a+1)='\0'; + } + } + //char tempo[HTS_URLMAXSIZE*2]; + //strcpy(tempo,"http://"); + //strcat(tempo,urladr); // host + //if (*lien!='/') + // strcat(tempo,"/"); + //strcat(tempo,lien); + //strcpy(lien,tempo); + } + } + + if (!error) { // pas d'erreur? + if (p_type==2) { // code ET PAS codebase + char* a=lien+strlen(lien)-1; + while( (a > lien) && (*a) && (*a!='/')) a--; + if (*a=='/') // ok on a repéré le dernier / + *(a+1)='\0'; // couper + else { + *lien='\0'; // éliminer + error=1; // erreur, ne pas poursuivre + } + } + + // stocker base ou codebase? + switch(p_type) { + case 2: { + //if (*lien!='/') strcat(base,"/"); + strcpy(base,lien); + } + break; // base + case -2: { + //if (*lien!='/') strcat(codebase,"/"); + strcpy(codebase,lien); + } + break; // base + } + + if ((opt.debug>1) && (opt.log!=NULL)) { + fspc(opt.log,"debug"); fprintf(opt.log,"code/codebase link %s base %s"LF,lien,base); test_flush; + } + //printf("base code: %s - %s\n",lien,base); + } + + } else { + char* _base; + if (p_type==-1) // code (applet) + _base=codebase; + else + _base=base; + + + // ajouter chemin de base href.. + if (strnotempty(_base)) { // considérer base + if (!link_has_authority(lien)) { // non absolue + //if (*lien!='/') { // non absolu sur le site (/) + if ( ((int) strlen(_base)+(int) strlen(lien))<HTS_URLMAXSIZE) { + // mailto: and co: do NOT add base + if (ident_url_relatif(lien,urladr,urlfil,adr,fil)>=0) { + char tempo[HTS_URLMAXSIZE*2]; + // base est absolue + strcpy(tempo,_base); + strcat(tempo,lien + ((*lien=='/')?1:0) ); + strcpy(lien,tempo); // patcher en considérant base + // ** vérifier que ../ fonctionne (ne doit pas arriver mais bon..) + + if ((opt.debug>1) && (opt.log!=NULL)) { + fspc(opt.log,"debug"); fprintf(opt.log,"link modified with code/codebase %s"LF,lien); test_flush; + } + } + } else { + error=1; // erreur + if (opt.errlog) { + fspc(opt.errlog,"error"); fprintf(opt.errlog,"Link %s too long with base href"LF,lien); + test_flush; + } + } + //} + } + } + + + } + } + + + // transformer lien quelconque (http, relatif, etc) en une adresse + // et un chemin+fichier (adr,fil) + if (!error) { + int reponse; + if ((opt.debug>1) && (opt.log!=NULL)) { + fspc(opt.log,"debug"); fprintf(opt.log,"build relative link %s with %s%s"LF,lien,urladr,urlfil); test_flush; + } + if ((reponse=ident_url_relatif(lien,urladr,urlfil,adr,fil))<0) { + adr[0]='\0'; // erreur + if (reponse==-2) { + if (opt.errlog) { + fspc(opt.errlog,"warning"); fprintf(opt.errlog,"Link %s not caught (unknown ftp:// protocol)"LF,lien); + test_flush; + } + } else { + if ((opt.debug>1) && (opt.errlog!=NULL)) { + fspc(opt.errlog,"debug"); fprintf(opt.errlog,"ident_url_relatif failed for %s with %s%s"LF,lien,urladr,urlfil); test_flush; + } + } + } + } else { + if ((opt.debug>1) && (opt.log!=NULL)) { + fspc(opt.log,"debug"); fprintf(opt.log,"link %s not build, error detected before"LF,lien); test_flush; + } + adr[0]='\0'; + } + +#if HTS_CHECK_STRANGEDIR + // !ATTENTION! + // Ici on teste les exotiques du genre www.truc.fr/machin (sans slash à la fin) + // je n'ai pas encore trouvé le moyen de faire la différence entre un répertoire + // et un fichier en http A PRIORI : je fais donc un test + // En cas de moved xxx, on recalcule adr et fil, tout simplement + // DEFAUT: test effectué plusieurs fois! à revoir!!! + if ((adr[0]!='\0') && (strcmp(adr,"file://") && (p_type!=2) && (p_type!=-2)) { + //## if ((adr[0]!='\0') && (adr[0]!=lOCAL_CHAR) && (p_type!=2) && (p_type!=-2)) { + if (fil[strlen(fil)-1]!='/') { // pas répertoire + if (ishtml(fil)==-2) { // pas d'extension + char loc[HTS_URLMAXSIZE*2]; // éventuelle nouvelle position + loc[0]='\0'; + if ((opt.debug>1) && (opt.log!=NULL)) { + fspc(opt.log,"debug"); fprintf(opt.log,"link-check-directory: %s%s"LF,adr,fil); + test_flush; + } + + // tester éventuelle nouvelle position + switch (http_location(adr,fil,loc).statuscode) { + case 200: // ok au final + if (strnotempty(loc)) { // a changé d'adresse + if (opt.errlog) { + fspc(opt.errlog,"warning"); fprintf(opt.errlog,"Link %s%s has moved to %s for %s%s"LF,adr,fil,loc,urladr,urlfil); + test_flush; + } + + // recalculer adr et fil! + if (ident_url_absolute(loc,adr,fil)==-1) { + adr[0]='\0'; // cancel + if ((opt.debug>1) && (opt.log!=NULL)) { + fspc(opt.log,"debug"); fprintf(opt.log,"link-check-dir: %s%s"LF,adr,fil); + test_flush; + } + } + + } + break; + case -2: case -3: // timeout ou erreur grave + if (opt.errlog) { + fspc(opt.errlog,"warning"); fprintf(opt.errlog,"Connection too slow for testing link %s%s (from %s%s)"LF,adr,fil,urladr,urlfil); + test_flush; + } + + break; + } + + } + } + } +#endif + + // Le lien doit juste être réécrit, mais ne doit pas générer un lien + // exemple: <FORM ACTION="url_cgi"> + if (p_nocatch) { + forbidden_url=1; // interdire récupération du lien + if ((opt.debug>1) && (opt.log!=NULL)) { + fspc(opt.log,"debug"); fprintf(opt.log,"link forced external at %s%s"LF,adr,fil); + test_flush; + } + } + + // Tester si un lien doit être accepté ou refusé (wizard) + // forbidden_url=1 : lien refusé + // forbidden_url=0 : lien accepté + //if ((ptr>0) && (p_type!=2) && (p_type!=-2)) { // tester autorisations? + if ((p_type!=2) && (p_type!=-2)) { // tester autorisations? + if (!p_nocatch) { + if (adr[0]!='\0') { + if ((opt.debug>1) && (opt.log!=NULL)) { + fspc(opt.log,"debug"); fprintf(opt.log,"wizard link test at %s%s.."LF,adr,fil); + test_flush; + } + forbidden_url=hts_acceptlink(&opt,ptr,lien_tot,liens, + adr,fil, + &filters,&filptr,opt.maxfilter, + &robots, + &set_prio_to, + &just_test_it); + if ((opt.debug>1) && (opt.log!=NULL)) { + fspc(opt.log,"debug"); fprintf(opt.log,"result for wizard link test: %d"LF,forbidden_url); + test_flush; + } + } + } + } + + // calculer meme_adresse + meme_adresse=strfield2(jump_identification(adr),jump_identification(urladr)); + + + + // Début partie sauvegarde + + // ici on forme le nom du fichier à sauver, et on patche l'URL + if (adr[0]!='\0') { + // savename: simplifier les ../ et autres joyeusetés + char save[HTS_URLMAXSIZE*2]; + int r_sv=0; + // En cas de moved, adresse première + char former_adr[HTS_URLMAXSIZE*2]; + char former_fil[HTS_URLMAXSIZE*2]; + // + save[0]='\0'; former_adr[0]='\0'; former_fil[0]='\0'; + // + + // nom du chemin à sauver si on doit le calculer + // note: url_savename peut décider de tester le lien si il le trouve + // suspect, et modifier alors adr et fil + // dans ce cas on aura une référence directe au lieu des traditionnels + // moved en cascade (impossible à reproduire à priori en local, lorsque des fichiers + // gif sont impliqués par exemple) + if ((p_type!=2) && (p_type!=-2)) { // pas base href ou codebase + if (forbidden_url!=1) { + char last_adr[HTS_URLMAXSIZE*2]; + last_adr[0]='\0'; + //char last_fil[HTS_URLMAXSIZE*2]=""; + strcpy(last_adr,adr); // ancienne adresse + //strcpy(last_fil,fil); // ancien chemin + r_sv=url_savename(adr,fil,save,former_adr,former_fil,liens[ptr]->adr,liens[ptr]->fil,&opt,liens,lien_tot,back,back_max,&cache,&hash,ptr,numero_passe); + if (strcmp(jump_identification(last_adr),jump_identification(adr)) != 0) { // a changé + + // 2e test si moved + + // Tester si un lien doit être accepté ou refusé (wizard) + // forbidden_url=1 : lien refusé + // forbidden_url=0 : lien accepté + if ((ptr>0) && (p_type!=2) && (p_type!=-2)) { // tester autorisations? + if (!p_nocatch) { + if (adr[0]!='\0') { + if ((opt.debug>1) && (opt.log!=NULL)) { + fspc(opt.log,"debug"); fprintf(opt.log,"wizard moved link retest at %s%s.."LF,adr,fil); + test_flush; + } + forbidden_url=hts_acceptlink(&opt,ptr,lien_tot,liens, + adr,fil, + &filters,&filptr,opt.maxfilter, + &robots, + &set_prio_to, + &just_test_it); + if ((opt.debug>1) && (opt.log!=NULL)) { + fspc(opt.log,"debug"); fprintf(opt.log,"result for wizard moved link retest: %d"LF,forbidden_url); + test_flush; + } + } + } + } + + //import_done=1; // c'est un import! + meme_adresse=0; // on a changé + } + } else { + strcpy(save,""); // dummy + } + } + if (r_sv!=-1) { // pas d'erreur, on continue + /* log */ + if ((opt.debug>1) && (opt.log!=NULL)) { + fspc(opt.log,"debug"); + if (forbidden_url!=1) { // le lien va être chargé + if ((p_type==2) || (p_type==-2)) { // base href ou codebase, pas un lien + fprintf(opt.log,"Code/Codebase: %s%s"LF,adr,fil); + } else if ((opt.getmode & 4)==0) { + fprintf(opt.log,"Record: %s%s -> %s"LF,adr,fil,save); + } else { + if (!ishtml(fil)) + fprintf(opt.log,"Record after: %s%s -> %s"LF,adr,fil,save); + else + fprintf(opt.log,"Record: %s%s -> %s"LF,adr,fil,save); + } + } else + fprintf(opt.log,"External: %s%s"LF,adr,fil); + test_flush; + } + /* FIN log */ + + // écrire lien + if ((p_type==2) || (p_type==-2)) { // base href ou codebase, sauter + lastsaved=eadr-1+1; // sauter " + } + /* */ + else if (opt.urlmode==0) { // URL absolue dans tous les cas + if ((opt.getmode & 1) && (ptr>0)) { // ecrire les html + if (!link_has_authority(adr)) { + HT_ADD("http://"); + } else { + char* aut = strstr(adr, "//"); + if (aut) { + char tmp[256]; + tmp[0]='\0'; + strncat(tmp, adr, (int) (aut - adr)); // scheme + HT_ADD(tmp); // Protocol + HT_ADD("//"); + } + } + + if (!opt.passprivacy) { + HT_ADD(jump_protocol(adr)); // Password + } else { + HT_ADD(jump_identification(adr)); // No Password + } + if (*fil!='/') + HT_ADD("/"); + HT_ADD(fil); + } + lastsaved=eadr-1; // dernier écrit+1 (enfin euh apres on fait un ++ alors hein) + /* */ + } else if (opt.urlmode >= 4) { // ne rien faire dans tous les cas! + /* */ + /* leave the link 'as is' */ + /* Sinon, dépend de interne/externe */ + } else if (forbidden_url==1) { // le lien ne sera pas chargé, référence externe! + if ((opt.getmode & 1) && (ptr>0)) { + if (p_type!=-1) { // pas que le nom de fichier (pas classe java) + if (!opt.external) { + if (!link_has_authority(adr)) { + HT_ADD("http://"); + if (!opt.passprivacy) { + HT_ADD(adr); // Password + } else { + HT_ADD(jump_identification(adr)); // No Password + } + if (*fil!='/') + HT_ADD("/"); + HT_ADD(fil); + } else { + char* aut = strstr(adr, "//"); + if (aut) { + char tmp[256]; + tmp[0]='\0'; + strncat(tmp, adr, (int) (aut - adr)); // scheme + HT_ADD(tmp); // Protocol + HT_ADD("//"); + if (!opt.passprivacy) { + HT_ADD(jump_protocol(adr)); // Password + } else { + HT_ADD(jump_identification(adr)); // No Password + } + if (*fil!='/') + HT_ADD("/"); + HT_ADD(fil); + } + } + // + } else { // fichier/page externe, mais on veut générer une erreur + // + int patch_it=0; + int add_url=0; + char* cat_name=NULL; + char* cat_data=NULL; + int cat_nb=0; + int cat_data_len=0; + + // ajouter lien external + switch ( (link_has_authority(adr)) ? 1 : ( (fil[strlen(fil)-1]=='/')?1:(ishtml(fil)) ) ) { + case 1: case -2: // html ou répertoire + if (opt.getmode & 1) { // sauver html + patch_it=1; // redirect + add_url=1; // avec link? + cat_name="external.html"; + cat_nb=0; + cat_data=HTS_DATA_UNKNOWN_HTML; + cat_data_len=HTS_DATA_UNKNOWN_HTML_LEN; + } + break; + default: // inconnu + // asp, cgi.. + if (is_dyntype(get_ext(fil))) { + patch_it=1; // redirect + add_url=1; // avec link? + cat_name="external.html"; + cat_nb=0; + cat_data=HTS_DATA_UNKNOWN_HTML; + cat_data_len=HTS_DATA_UNKNOWN_HTML_LEN; + } else if ( (strfield2(fil+max(0,(int)strlen(fil)-4),".gif")) + || (strfield2(fil+max(0,(int)strlen(fil)-4),".jpg")) + || (strfield2(fil+max(0,(int)strlen(fil)-4),".xbm")) + || (ishtml(fil)!=0) ) { + patch_it=1; // redirect + add_url=1; // avec link aussi + cat_name="external.gif"; + cat_nb=1; + cat_data=HTS_DATA_UNKNOWN_GIF; + cat_data_len=HTS_DATA_UNKNOWN_GIF_LEN; + } + break; + }// html,gif + + if (patch_it) { + char save[HTS_URLMAXSIZE*2]; + char tempo[HTS_URLMAXSIZE*2]; + strcpy(save,opt.path_html); + strcat(save,cat_name); + if (lienrelatif(tempo,save,savename)==0) { + if (!no_esc_utf) + escape_uri(tempo); // escape with %xx + else + escape_uri_utf(tempo); // escape with %xx + HT_ADD(tempo); // page externe + if (add_url) { + HT_ADD("?link="); // page externe + + // same as above + if (!link_has_authority(adr)) { + HT_ADD("http://"); + if (!opt.passprivacy) { + HT_ADD(adr); // Password + } else { + HT_ADD(jump_identification(adr)); // No Password + } + if (*fil!='/') + HT_ADD("/"); + HT_ADD(fil); + } else { + char* aut = strstr(adr, "//"); + if (aut) { + char tmp[256]; + tmp[0]='\0'; + strncat(tmp, adr, (int) (aut - adr) + 2); // scheme + HT_ADD(tmp); + if (!opt.passprivacy) { + HT_ADD(jump_protocol(adr)); // Password + } else { + HT_ADD(jump_identification(adr)); // No Password + } + if (*fil!='/') + HT_ADD("/"); + HT_ADD(fil); + } + } + // + + } + } + + // écrire fichier? + if (verif_external(cat_nb,1)) { + //if (!fexist(fconcat(opt.path_html,cat_name))) { + FILE* fp = filecreate(fconcat(opt.path_html,cat_name)); + if (fp) { + if (cat_data_len==0) { // texte + verif_backblue(opt.path_html); + fprintf(fp,"%s%s","<!-- Created by HTTrack Website Copier/"HTTRACK_VERSION" "HTTRACK_AFF_AUTHORS" -->"LF,cat_data); + } else { // data + fwrite(cat_data,cat_data_len,1,fp); + } + fclose(fp); + usercommand(0,NULL,fconcat(opt.path_html,cat_name)); + } + } + } else { // écrire normalement le nom de fichier + HT_ADD("http://"); + if (!opt.passprivacy) { + HT_ADD(adr); // Password + } else { + HT_ADD(jump_identification(adr)); // No Password + } + if (*fil!='/') + HT_ADD("/"); + HT_ADD(fil); + }// patcher? + } // external + } else { // que le nom de fichier (classe java) + // en gros recopie de plus bas: copier codebase et base + if (p_flush) { + char tempo[HTS_URLMAXSIZE*2]; // <-- ajouté + char tempo_pat[HTS_URLMAXSIZE*2]; + + // Calculer chemin + tempo_pat[0]='\0'; + strcpy(tempo,fil); // <-- ajouté + { + char* a=strrchr(tempo,'/'); + + // Example: we converted code="x.y.z.foo.class" into "x/y/z/foo.class" + // we have to do the contrary now + if (add_class_dots_to_patch>0) { + while( (add_class_dots_to_patch>0) && (a) ) { + *a='.'; // convert "false" java / into . + add_class_dots_to_patch--; + a=strrchr(tempo,'/'); + } + // if add_class_dots_to_patch, this is because there is a problem!! + if (add_class_dots_to_patch) { + if (opt.errlog) { + fspc(opt.errlog,"warning"); fprintf(opt.errlog,"Error: can not rewind java path %s, check html code"LF,tempo); + test_flush; + } + } + } + + // Cut path/filename + if (a) { + char tempo2[HTS_URLMAXSIZE*2]; + strcpy(tempo2,a+1); // FICHIER + strncat(tempo_pat,tempo,(int) (a - tempo)+1); // chemin + strcpy(tempo,tempo2); // fichier + } + } + + // érire codebase="chemin" + if ((opt.getmode & 1) && (ptr>0)) { + char tempo4[HTS_URLMAXSIZE*2]; + tempo4[0]='\0'; + + if (strnotempty(tempo_pat)) { + HT_ADD("codebase=\"http://"); + if (!opt.passprivacy) { + HT_ADD(adr); // Password + } else { + HT_ADD(jump_identification(adr)); // No Password + } + if (*tempo_pat!='/') HT_ADD("/"); + HT_ADD(tempo_pat); + HT_ADD("\" "); + } + + strncat(tempo4,lastsaved,(int) (p_flush - lastsaved)); + HT_ADD(tempo4); // refresh code=" + HT_ADD(tempo); + } + } + } + } + lastsaved=eadr-1; + } + /* + else if (opt.urlmode==1) { // ABSOLU, c'est le cas le moins courant + // NE FONCTIONNE PAS!! (et est inutile) + if ((opt.getmode & 1) && (ptr>0)) { // ecrire les html + // écrire le lien modifié, absolu + HT_ADD("file:"); + if (*save=='/') + HT_ADD(save+1) + else + HT_ADD(save) + } + lastsaved=eadr-1; // dernier écrit+1 (enfin euh apres on fait un ++ alors hein) + } + */ + else if (opt.urlmode==3) { // URI absolue / + if ((opt.getmode & 1) && (ptr>0)) { // ecrire les html + HT_ADD(fil); + } + lastsaved=eadr-1; // dernier écrit+1 (enfin euh apres on fait un ++ alors hein) + } + else if (opt.urlmode==2) { // RELATIF + char tempo[HTS_URLMAXSIZE*2]; + tempo[0]='\0'; + // calculer le lien relatif + + if (lienrelatif(tempo,save,savename)==0) { + if (!no_esc_utf) + escape_uri(tempo); // escape with %xx + else + escape_uri_utf(tempo); // escape with %xx + if ((opt.debug>1) && (opt.log!=NULL)) { + fspc(opt.log,"debug"); fprintf(opt.log,"relative link at %s build with %s and %s: %s"LF,adr,save,savename,tempo); + test_flush; + } + + // lien applet (code) - il faut placer un codebase avant + if (p_type==-1) { // que le nom de fichier + + if (p_flush) { + char tempo_pat[HTS_URLMAXSIZE*2]; + tempo_pat[0]='\0'; + { + char* a=strrchr(tempo,'/'); + + // Example: we converted code="x.y.z.foo.class" into "x/y/z/foo.class" + // we have to do the contrary now + if (add_class_dots_to_patch>0) { + while( (add_class_dots_to_patch>0) && (a) ) { + *a='.'; // convert "false" java / into . + add_class_dots_to_patch--; + a=strrchr(tempo,'/'); + } + // if add_class_dots_to_patch, this is because there is a problem!! + if (add_class_dots_to_patch) { + if (opt.errlog) { + fspc(opt.errlog,"warning"); fprintf(opt.errlog,"Error: can not rewind java path %s, check html code"LF,tempo); + test_flush; + } + } + } + + if (a) { + char tempo2[HTS_URLMAXSIZE*2]; + strcpy(tempo2,a+1); + strncat(tempo_pat,tempo,(int) (a - tempo)+1); // chemin + strcpy(tempo,tempo2); // fichier + } + } + + // érire codebase="chemin" + if ((opt.getmode & 1) && (ptr>0)) { + char tempo4[HTS_URLMAXSIZE*2]; + tempo4[0]='\0'; + + if (strnotempty(tempo_pat)) { + HT_ADD("codebase=\""); + HT_ADD(tempo_pat); + HT_ADD("\" "); + } + + strncat(tempo4,lastsaved,(int) (p_flush - lastsaved)); + HT_ADD(tempo4); // refresh code=" + } + } + //lastsaved=adr; // dernier écrit+1 + } + + if ((opt.getmode & 1) && (ptr>0)) { + // écrire le lien modifié, relatif + HT_ADD(tempo); + + // Add query-string, for informational purpose only + // Useless, because all parameters-pages are saved into different targets + if (opt.includequery) { + char* a=strchr(lien,'?'); + if (a) { + HT_ADD(a); + } + } + } + lastsaved=eadr-1; // dernier écrit+1 (enfin euh apres on fait un ++ alors hein) + } else { + if (opt.errlog) { + fprintf(opt.errlog,"Error building relative link %s and %s"LF,save,savename); + test_flush; + } + } + } // sinon le lien sera écrit normalement + + +#if 0 + if (fexist(save)) { // le fichier existe.. + adr[0]='\0'; + //if ((opt.debug>0) && (opt.log!=NULL)) { + if (opt.errlog) { + fspc(opt.errlog,"warning"); fprintf(opt.errlog,"Link has already been written on disk, cancelled: %s"LF,save); + test_flush; + } + } +#endif + + /* Security check */ + if (strlen(save) >= HTS_URLMAXSIZE) { + adr[0]='\0'; + if (opt.errlog) { + fspc(opt.errlog,"warning"); fprintf(opt.errlog,"Link is too long: %s"LF,save); + test_flush; + } + } + + if ((adr[0]!='\0') && (p_type!=2) && (p_type!=-2) && ( (forbidden_url!=1) || (just_test_it))) { // si le fichier n'existe pas, ajouter à la liste + // n'y a-t-il pas trop de liens? + if (lien_tot+1 >= lien_max-4) { // trop de liens! + printf("PANIC! : Too many URLs : >%d [%d]\n",lien_tot,__LINE__); + if (opt.errlog) { + fprintf(opt.errlog,LF"Too many URLs, giving up..(>%d)"LF,lien_max); + fprintf(opt.errlog,"To avoid that: use #L option for more links (example: -#L1000000)"LF); + test_flush; + } + if ((opt.getmode & 1) && (ptr>0)) { if (fp) { fclose(fp); fp=NULL; } } + XH_uninit; // désallocation mémoire & buffers + return 0; + + } else { // noter le lien sur la listes des liens à charger + int pass_fix,dejafait=0; + + // Calculer la priorité de ce lien + if ((opt.getmode & 4)==0) { // traiter html après + pass_fix=0; + } else { // vérifier que ce n'est pas un !html + if (!ishtml(fil)) + pass_fix=1; // priorité inférieure (traiter après) + else + pass_fix=max(0,numero_passe); // priorité normale + } + + /* If the file seems to be an html file, get depth-1 */ + /* + if (strnotempty(save)) { + if (ishtml(save) == 1) { + // descore_prio = 2; + } else { + // descore_prio = 1; + } + } + */ + + // vérifier que le lien n'a pas déja été noté + // si c'est le cas, alors il faut s'assurer que la priorité associée + // au fichier est la plus grande des deux priorités + // + // On part de la fin et on essaye de se presser (économise temps machine) +#if HTS_HASH + { + int i=hash_read(&hash,save,"",0); // lecture type 0 (sav) + if (i>=0) { + liens[i]->depth=maximum(liens[i]->depth,liens[ptr]->depth - 1); + dejafait=1; + } + } +#else + { + int l; + int i; + l=strlen(save); // opti + for(i=lien_tot-1;(i>=0) && (dejafait==0);i--) { + if (liens[i]->sav_len==l) { // même taille de chaîne + if (strcmp(liens[i]->sav,save)==0) { // existe déja + liens[i]->depth=maximum(liens[i]->depth,liens[ptr]->depth - 1); + dejafait=1; + } + } + } + } +#endif + + // le lien n'a jamais été créé. + // cette fois ci, on le crée! + if (!dejafait) { + // + // >>>> CREER LE LIEN <<<< + // + // enregistrer lien à charger + //liens[lien_tot]->adr[0]=liens[lien_tot]->fil[0]=liens[lien_tot]->sav[0]='\0'; + // même adresse: l'objet père est l'objet père de l'actuel + + // DEBUT ROBOTS.TXT AJOUT + if (!just_test_it) { + if ( + (!strfield(adr,"ftp://")) // non ftp + && (!strfield(adr,"file://")) ) { // non file + if (opt.robots) { // récupérer robots + if (ishtml(fil)!=0) { // pas la peine pour des fichiers isolés + if (checkrobots(&robots,adr,"") != -1) { // robots.txt ? + checkrobots_set(&robots,adr,""); // ajouter entrée vide + if (checkrobots(&robots,adr,"") == -1) { // robots.txt ? + // enregistrer robots.txt (MACRO) + liens_record(adr,"/robots.txt","","",""); + if (liens[lien_tot]==NULL) { // erreur, pas de place réservée + printf("PANIC! : Not enough memory [%d]\n",__LINE__); + if (opt.errlog) { + fprintf(opt.errlog,"Not enough memory, can not re-allocate %d bytes"LF,(int)((add_tab_alloc+1)*sizeof(lien_url))); + test_flush; + } + if ((opt.getmode & 1) && (ptr>0)) { if (fp) { fclose(fp); fp=NULL; } } + XH_uninit; // désallocation mémoire & buffers + return 0; + } + liens[lien_tot]->testmode=0; // pas mode test + liens[lien_tot]->link_import=0; // pas mode import + liens[lien_tot]->premier=lien_tot; + liens[lien_tot]->precedent=ptr; + liens[lien_tot]->depth=0; + liens[lien_tot]->pass2=max(0,numero_passe); + liens[lien_tot]->retry=0; + lien_tot++; // UN LIEN DE PLUS +#if DEBUG_ROBOTS + printf("robots.txt: added file robots.txt for %s\n",adr); +#endif + if ((opt.debug>1) && (opt.log!=NULL)) { + fspc(opt.log,"debug"); fprintf(opt.log,"robots.txt added at %s"LF,adr); + test_flush; + } + } else { + if (opt.errlog) { + fprintf(opt.errlog,"Unexpected robots.txt error at %d"LF,__LINE__); + test_flush; + } + } + } + } + } + } + } + // FIN ROBOTS.TXT AJOUT + + // enregistrer (MACRO) + liens_record(adr,fil,save,former_adr,former_fil); + if (liens[lien_tot]==NULL) { // erreur, pas de place réservée + printf("PANIC! : Not enough memory [%d]\n",__LINE__); + if (opt.errlog) { + fprintf(opt.errlog,"Not enough memory, can not re-allocate %d bytes"LF,(int)((add_tab_alloc+1)*sizeof(lien_url))); + test_flush; + } + if ((opt.getmode & 1) && (ptr>0)) { if (fp) { fclose(fp); fp=NULL; } } + XH_uninit; // désallocation mémoire & buffers + return 0; + } + + // mode test? + if (!just_test_it) + liens[lien_tot]->testmode=0; // pas mode test + else + liens[lien_tot]->testmode=1; // mode test + if (!import_done) + liens[lien_tot]->link_import=0; // pas mode import + else + liens[lien_tot]->link_import=1; // mode import + // écrire autres paramètres de la structure-lien + if ((meme_adresse) && (!import_done) && (liens[ptr]->premier != 0)) + liens[lien_tot]->premier=liens[ptr]->premier; + else // sinon l'objet père est le précédent lui même + liens[lien_tot]->premier=lien_tot; + // liens[lien_tot]->premier=ptr; + + liens[lien_tot]->precedent=ptr; + // noter la priorité + if (!set_prio_to) + liens[lien_tot]->depth=liens[ptr]->depth - 1; + else + liens[lien_tot]->depth=max(0,min(liens[ptr]->depth-1,set_prio_to-1)); // PRIORITE NULLE (catch page) + // noter pass + liens[lien_tot]->pass2=pass_fix; + liens[lien_tot]->retry=opt.retry; + + //strcpy(liens[lien_tot]->adr,adr); + //strcpy(liens[lien_tot]->fil,fil); + //strcpy(liens[lien_tot]->sav,save); + if ((opt.debug>1) && (opt.log!=NULL)) { + if (!just_test_it) { + fspc(opt.log,"debug"); fprintf(opt.log,"OK, NOTE: %s%s -> %s"LF,liens[lien_tot]->adr,liens[lien_tot]->fil,liens[lien_tot]->sav); + } else { + fspc(opt.log,"debug"); fprintf(opt.log,"OK, TEST: %s%s"LF,liens[lien_tot]->adr,liens[lien_tot]->fil); + } + test_flush; + } + + lien_tot++; // UN LIEN DE PLUS + } else { // if !dejafait + if ((opt.debug>1) && (opt.log!=NULL)) { + fspc(opt.log,"debug"); fprintf(opt.log,"link has already been recorded, cancelled: %s"LF,save); + test_flush; + } + + } + + + } // si pas trop de liens + } // si adr[0]!='\0' + + + } // if adr[0]!='\0' + + } // if adr[0]!='\0' + + } // if strlen(lien)>0 + + } // if ok==0 + + adr=eadr-1; // ** sauter + + } // if (p) + + } // si '<' ou '>' + + // plus loin + adr++; + + + /* Otimization: if we are scanning in HTML data (not in tag or script), + then jump to the next starting tag */ + if (ptr>0) { + if ( (!intag) /* Not in tag */ + && (!inscript) /* Not in (java)script */ + && (!incomment) /* Not in comment (<!--) */ + && (!inscript_tag) /* Not in tag with script inside */ + ) + { + /* Not at the end */ + if (( ((int) (adr - r.adr)) ) < r.size) { + /* Not on a starting tag yet */ + if (*adr != '<') { + char* adr_next = strchr(adr,'<'); + /* Jump to near end (index hack) */ + if (!adr_next) { + if ( + ( (int)(adr - r.adr) < (r.size - 4)) + && + (r.size > 4) + ) { + adr = r.adr + r.size - 2; + } + } else { + adr = adr_next; + } + } + } + } + } + + // ---------- + // écrire peu à peu + if ((opt.getmode & 1) && (ptr>0)) HT_ADD_ADR; + lastsaved=adr; // dernier écrit+1 + // ---------- + + // pour les stats du shell si parsing trop long +#if HTS_ANALYSTE + if (r.size) + _hts_in_html_done=(100 * ((int) (adr - r.adr)) ) / (int)(r.size); + if (_hts_in_html_poll) { + _hts_in_html_poll=0; + // temps à attendre, et remplir autant que l'on peut le cache (backing) + back_wait(back,back_max,&opt,&cache,HTS_STAT.stat_timestart); + back_fillmax(back,back_max,&opt,&cache,liens,ptr,numero_passe,lien_tot); + + // Transfer rate + engine_stats(); + + // Refresh various stats + HTS_STAT.stat_nsocket=back_nsoc(back,back_max); + HTS_STAT.stat_errors=fspc(NULL,"error"); + HTS_STAT.stat_warnings=fspc(NULL,"warning"); + HTS_STAT.stat_infos=fspc(NULL,"info"); + HTS_STAT.nbk=backlinks_done(liens,lien_tot,ptr); + HTS_STAT.nb=back_transfered(HTS_STAT.stat_bytes,back,back_max); + + if (!hts_htmlcheck_loop(back,back_max,0,ptr,lien_tot,(int) (time_local()-HTS_STAT.stat_timestart),&HTS_STAT)) { + if (opt.errlog) { + fspc(opt.errlog,"info"); fprintf(opt.errlog,"Exit requested by shell or user"LF); + test_flush; + } + exit_xh=1; // exit requested + XH_uninit; + return 0; + //adr = r.adr + r.size; // exit + } else if (_hts_cancel==1) { + // adr = r.adr + r.size; // exit + nofollow=1; // moins violent + _hts_cancel=0; + } + } + + // refresh the backing system each 2 seconds + if (engine_stats()) { + back_wait(back,back_max,&opt,&cache,HTS_STAT.stat_timestart); + back_fillmax(back,back_max,&opt,&cache,liens,ptr,numero_passe,lien_tot); + } +#endif + } while(( ((int) (adr - r.adr)) ) < r.size); +#if HTS_ANALYSTE + _hts_in_html_parsing=0; // flag + _hts_cancel=0; // pas de cancel +#endif + if ((opt.getmode & 1) && (ptr>0)) { + HT_ADD_END; // achever + } + // + // + // + } // if !error + + + if (opt.getmode & 1) { if (fp) { fclose(fp); fp=NULL; } } + // sauver fichier + //structcheck(savename); + //filesave(r.adr,r.size,savename); + +#if HTS_ANALYSTE + } // analyse OK +#endif + |