1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293 |
- /* HyperText Tranfer Protocol - Client implementation HTTP.c
- * ==========================
- * Modified:
- * 27 Jan 1994 PDM Added Ari Luotonen's Fix for Reload when using proxy
- * servers.
- * 28 Apr 1997 AJL,FM Do Proxy Authorisation.
- */
- #include <HTUtils.h>
- #include <HTTP.h>
- #include <LYUtils.h>
- #ifdef USE_SSL
- #include <HTNews.h>
- #endif
- #define HTTP_VERSION "HTTP/1.0"
- #define HTTP_PORT 80
- #define HTTPS_PORT 443
- #define SNEWS_PORT 563
- #define INIT_LINE_SIZE 1536 /* Start with line buffer this big */
- #define LINE_EXTEND_THRESH 256 /* Minimum read size */
- #define VERSION_LENGTH 20 /* for returned protocol version */
- #include <HTParse.h>
- #include <HTTCP.h>
- #include <HTFormat.h>
- #include <HTFile.h>
- #include <HTAlert.h>
- #include <HTMIME.h>
- #include <HTML.h>
- #include <HTInit.h>
- #include <HTAABrow.h>
- #include <HTAccess.h> /* Are we using an HTTP gateway? */
- #include <LYCookie.h>
- #include <LYGlobalDefs.h>
- #include <GridText.h>
- #include <LYStrings.h>
- #include <LYrcFile.h>
- #include <LYLeaks.h>
- struct _HTStream {
- HTStreamClass *isa;
- };
- BOOL reloading = FALSE; /* Reloading => send no-cache pragma to proxy */
- char *redirecting_url = NULL; /* Location: value. */
- BOOL permanent_redirection = FALSE; /* Got 301 status? */
- BOOL redirect_post_content = FALSE; /* Don't convert to GET? */
- #ifdef USE_SSL
- SSL_CTX *ssl_ctx = NULL; /* SSL ctx */
- SSL *SSL_handle = NULL;
- static int ssl_okay;
- static void free_ssl_ctx(void)
- {
- if (ssl_ctx != NULL)
- SSL_CTX_free(ssl_ctx);
- }
- static int HTSSLCallback(int preverify_ok, X509_STORE_CTX * x509_ctx GCC_UNUSED)
- {
- char *msg = NULL;
- int result = 1;
- if (!(preverify_ok || ssl_okay || ssl_noprompt)) {
- #ifdef USE_X509_SUPPORT
- HTSprintf0(&msg, SSL_FORCED_PROMPT,
- X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509_ctx)));
- if (HTForcedPrompt(ssl_noprompt, msg, YES))
- ssl_okay = 1;
- else
- result = 0;
- #endif
- FREE(msg);
- }
- return result;
- }
- SSL *HTGetSSLHandle(void)
- {
- #ifdef USE_GNUTLS_INCL
- static char *certfile = NULL;
- #endif
- if (ssl_ctx == NULL) {
- /*
- * First time only.
- */
- #if SSLEAY_VERSION_NUMBER < 0x0800
- ssl_ctx = SSL_CTX_new();
- X509_set_default_verify_paths(ssl_ctx->cert);
- #else
- SSLeay_add_ssl_algorithms();
- ssl_ctx = SSL_CTX_new(SSLv23_client_method());
- SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL);
- SSL_CTX_set_default_verify_paths(ssl_ctx);
- SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, HTSSLCallback);
- #endif /* SSLEAY_VERSION_NUMBER < 0x0800 */
- #ifdef USE_GNUTLS_INCL
- if ((certfile = LYGetEnv("SSL_CERT_FILE")) != NULL) {
- CTRACE((tfp,
- "HTGetSSLHandle: certfile is set to %s by SSL_CERT_FILE\n",
- certfile));
- }
- #endif
- atexit(free_ssl_ctx);
- }
- #ifdef USE_GNUTLS_INCL
- ssl_ctx->certfile = certfile;
- ssl_ctx->certfile_type = GNUTLS_X509_FMT_PEM;
- #endif
- ssl_okay = 0;
- return (SSL_new(ssl_ctx));
- }
- void HTSSLInitPRNG(void)
- {
- #if SSLEAY_VERSION_NUMBER >= 0x00905100
- if (RAND_status() == 0) {
- char rand_file[256];
- time_t t;
- int pid;
- long l, seed;
- t = time(NULL);
- pid = getpid();
- RAND_file_name(rand_file, 256);
- CTRACE((tfp, "HTTP: Seeding PRNG\n"));
- if (rand_file != NULL) {
- /* Seed as much as 1024 bytes from RAND_file_name */
- RAND_load_file(rand_file, 1024);
- }
- /* Seed in time (mod_ssl does this) */
- RAND_seed((unsigned char *) &t, sizeof(time_t));
- /* Seed in pid (mod_ssl does this) */
- RAND_seed((unsigned char *) &pid, sizeof(pid));
- /* Initialize system's random number generator */
- RAND_bytes((unsigned char *) &seed, sizeof(long));
- lynx_srand(seed);
- while (RAND_status() == 0) {
- /* Repeatedly seed the PRNG using the system's random number generator until it has been seeded with enough data */
- l = lynx_rand();
- RAND_seed((unsigned char *) &l, sizeof(long));
- }
- if (rand_file != NULL) {
- /* Write a rand_file */
- RAND_write_file(rand_file);
- }
- }
- #endif /* SSLEAY_VERSION_NUMBER >= 0x00905100 */
- return;
- }
- #define HTTP_NETREAD(sock, buff, size, handle) \
- (handle ? SSL_read(handle, buff, size) : NETREAD(sock, buff, size))
- #define HTTP_NETWRITE(sock, buff, size, handle) \
- (handle ? SSL_write(handle, buff, size) : NETWRITE(sock, buff, size))
- #define HTTP_NETCLOSE(sock, handle) \
- { (void)NETCLOSE(sock); if (handle) SSL_free(handle); SSL_handle = handle = NULL; }
- #else
- #define HTTP_NETREAD(a, b, c, d) NETREAD(a, b, c)
- #define HTTP_NETWRITE(a, b, c, d) NETWRITE(a, b, c)
- #define HTTP_NETCLOSE(a, b) (void)NETCLOSE(a)
- #endif /* USE_SSL */
- #ifdef _WINDOWS /* 1997/11/06 (Thu) 13:00:08 */
- #define BOX_TITLE "Lynx " __FILE__
- #define BOX_FLAG (MB_ICONINFORMATION | MB_SETFOREGROUND)
- typedef struct {
- int fd;
- char *buf;
- int len;
- } recv_data_t;
- int ws_read_per_sec = 0;
- static int ws_errno = 0;
- static DWORD g_total_times = 0;
- static DWORD g_total_bytes = 0;
- char *str_speed(void)
- {
- static char buff[32];
- if (ws_read_per_sec > 1000)
- sprintf(buff, "%d.%03dkB", ws_read_per_sec / 1000,
- (ws_read_per_sec % 1000));
- else
- sprintf(buff, "%3d", ws_read_per_sec);
- return buff;
- }
- /* The same like read, but takes care of EINTR and uses select to
- timeout the stale connections. */
- static int ws_read(int fd, char *buf, int len)
- {
- int res;
- int retry = 3;
- do {
- res = recv(fd, buf, len, 0);
- if (WSAEWOULDBLOCK == WSAGetLastError()) {
- Sleep(100);
- if (retry-- > 0)
- continue;
- }
- } while (res == SOCKET_ERROR && SOCKET_ERRNO == EINTR);
- return res;
- }
- #define DWORD_ERR ((DWORD)-1)
- static DWORD __stdcall _thread_func(void *p)
- {
- DWORD result;
- int i, val;
- recv_data_t *q = (recv_data_t *) p;
- i = 0;
- i++;
- val = ws_read(q->fd, q->buf, q->len);
- if (val == SOCKET_ERROR) {
- ws_errno = WSAGetLastError();
- #if 0
- char buff[256];
- sprintf(buff, "Thread read: %d, error (%ld), fd = %d, len = %d",
- i, ws_errno, q->fd, q->len);
- MessageBox(NULL, buff, BOX_TITLE, BOX_FLAG);
- #endif
- result = DWORD_ERR;
- } else {
- result = val;
- }
- return result;
- }
- /* The same like read, but takes care of EINTR and uses select to
- timeout the stale connections. */
- int ws_netread(int fd, char *buf, int len)
- {
- int i;
- char buff[256];
- /* 1998/03/30 (Mon) 09:01:21 */
- HANDLE hThread;
- DWORD dwThreadID;
- DWORD exitcode = 0;
- DWORD ret_val = DWORD_ERR;
- DWORD val, process_time, now_TickCount, save_TickCount;
- static recv_data_t para;
- extern int win32_check_interrupt(void); /* LYUtil.c */
- extern int lynx_timeout; /* LYMain.c */
- extern CRITICAL_SECTION critSec_READ; /* LYMain.c */
- #define TICK 5
- #define STACK_SIZE 0x2000uL
- InitializeCriticalSection(&critSec_READ);
- para.fd = fd;
- para.buf = buf;
- para.len = len;
- ws_read_per_sec = 0;
- save_TickCount = GetTickCount();
- hThread = CreateThread(NULL, STACK_SIZE,
- _thread_func,
- (void *) ¶, 0UL, &dwThreadID);
- if (hThread == 0) {
- HTInfoMsg("CreateThread Failed (read)");
- goto read_exit;
- }
- i = 0;
- while (1) {
- val = WaitForSingleObject(hThread, 1000 / TICK);
- i++;
- if (val == WAIT_FAILED) {
- HTInfoMsg("Wait Failed");
- ret_val = DWORD_ERR;
- break;
- } else if (val == WAIT_TIMEOUT) {
- i++;
- if (i / TICK > (AlertSecs + 2)) {
- sprintf(buff, "Read Waiting (%2d.%01d) for %d Bytes",
- i / TICK, (i % TICK) * 10 / TICK, len);
- SetConsoleTitle(buff);
- }
- if (win32_check_interrupt() || ((i / TICK) > lynx_timeout)) {
- if (CloseHandle(hThread) == FALSE) {
- HTInfoMsg("Thread terminate Failed");
- }
- WSASetLastError(ETIMEDOUT);
- ret_val = HT_INTERRUPTED;
- break;
- }
- } else if (val == WAIT_OBJECT_0) {
- if (GetExitCodeThread(hThread, &exitcode) == FALSE) {
- exitcode = DWORD_ERR;
- }
- if (CloseHandle(hThread) == FALSE) {
- HTInfoMsg("Thread terminate Failed");
- }
- now_TickCount = GetTickCount();
- if (now_TickCount >= save_TickCount)
- process_time = now_TickCount - save_TickCount;
- else
- process_time = now_TickCount + (0xffffffff - save_TickCount);
- if (process_time == 0)
- process_time = 1;
- g_total_times += process_time;
- /*
- * DWORD is unsigned, and could be an error code which is signed.
- */
- if ((long) exitcode > 0)
- g_total_bytes += exitcode;
- ws_read_per_sec = g_total_bytes;
- if (ws_read_per_sec > 2000000) {
- if (g_total_times > 1000)
- ws_read_per_sec /= (g_total_times / 1000);
- } else {
- ws_read_per_sec *= 1000;
- ws_read_per_sec /= g_total_times;
- }
- ret_val = exitcode;
- break;
- }
- } /* end while(1) */
- read_exit:
- LeaveCriticalSection(&critSec_READ);
- return ret_val;
- }
- #endif /* _WINDOWS */
- /*
- * Strip any username from the given string so we retain only the host.
- */
- static void strip_userid(char *host)
- {
- char *p1 = host;
- char *p2 = strchr(host, '@');
- char *fake;
- if (p2 != 0) {
- *p2++ = '\0';
- if ((fake = HTParse(host, "", PARSE_HOST)) != NULL) {
- char *msg = NULL;
- CTRACE((tfp, "parsed:%s\n", fake));
- HTSprintf0(&msg, gettext("Address contains a username: %s"), host);
- HTAlert(msg);
- FREE(msg);
- }
- while ((*p1++ = *p2++) != '\0') {
- ;
- }
- }
- }
- /*
- * Check if the user's options specified to use the given encoding. Normally
- * all encodings with compiled-in support are specified (encodingALL).
- */
- static BOOL acceptEncoding(int code)
- {
- BOOL result = FALSE;
- if ((code & LYAcceptEncoding) != 0) {
- const char *program = 0;
- switch (code) {
- case encodingGZIP:
- program = HTGetProgramPath(ppGZIP);
- break;
- case encodingDEFLATE:
- program = HTGetProgramPath(ppINFLATE);
- break;
- case encodingCOMPRESS:
- program = HTGetProgramPath(ppCOMPRESS);
- break;
- case encodingBZIP2:
- program = HTGetProgramPath(ppBZIP2);
- break;
- default:
- break;
- }
- /*
- * FIXME: if lynx did not rely upon external programs to decompress
- * files for external viewers, this check could be relaxed.
- */
- result = (program != 0);
- }
- return result;
- }
- /* Load Document from HTTP Server HTLoadHTTP()
- * ==============================
- *
- * Given a hypertext address, this routine loads a document.
- *
- *
- * On entry,
- * arg is the hypertext reference of the article to be loaded.
- *
- * On exit,
- * returns >=0 If no error, a good socket number
- * <0 Error.
- *
- * The socket must be closed by the caller after the document has been
- * read.
- *
- */
- static int HTLoadHTTP(const char *arg,
- HTParentAnchor *anAnchor,
- HTFormat format_out,
- HTStream *sink)
- {
- static char *empty = "";
- int s; /* Socket number for returned data */
- const char *url = arg; /* The URL which get_physical() returned */
- bstring *command = NULL; /* The whole command */
- char *eol; /* End of line if found */
- char *start_of_data; /* Start of body of reply */
- int status; /* tcp return */
- int bytes_already_read;
- char crlf[3]; /* A CR LF equivalent string */
- HTStream *target; /* Unconverted data */
- HTFormat format_in; /* Format arriving in the message */
- BOOL do_head = FALSE; /* Whether or not we should do a head */
- BOOL do_post = FALSE; /* ARE WE posting ? */
- const char *METHOD;
- BOOL had_header; /* Have we had at least one header? */
- char *line_buffer;
- char *line_kept_clean;
- int real_length_of_line = 0;
- BOOL extensions; /* Assume good HTTP server */
- char *linebuf = NULL;
- char temp[80];
- BOOL first_Accept = TRUE;
- BOOL show_401 = FALSE;
- BOOL show_407 = FALSE;
- BOOL auth_proxy = NO; /* Generate a proxy authorization. - AJL */
- int length, rawlength, rv;
- int server_status;
- BOOL doing_redirect, already_retrying = FALSE;
- int len = 0;
- #ifdef USE_SSL
- BOOL do_connect = FALSE; /* ARE WE going to use a proxy tunnel ? */
- BOOL did_connect = FALSE; /* ARE WE actually using a proxy tunnel ? */
- const char *connect_url = NULL; /* The URL being proxied */
- char *connect_host = NULL; /* The host being proxied */
- SSL *handle = NULL; /* The SSL handle */
- char ssl_dn[1024];
- char *cert_host;
- char *ssl_host;
- char *p;
- char *msg = NULL;
- int status_sslcertcheck;
- char *ssl_dn_start;
- char *ssl_all_cns;
- #ifdef USE_GNUTLS_INCL
- int ret;
- unsigned tls_status;
- #endif
- #if SSLEAY_VERSION_NUMBER >= 0x0900
- BOOL try_tls = TRUE;
- #endif /* SSLEAY_VERSION_NUMBER >= 0x0900 */
- SSL_handle = NULL;
- #else
- void *handle = NULL;
- #endif /* USE_SSL */
- if (anAnchor->isHEAD)
- do_head = TRUE;
- else if (anAnchor->post_data)
- do_post = TRUE;
- if (!url) {
- status = -3;
- _HTProgress(BAD_REQUEST);
- goto done;
- }
- if (!*url) {
- status = -2;
- _HTProgress(BAD_REQUEST);
- goto done;
- }
- #ifdef USE_SSL
- if (using_proxy && !strncmp(url, "http://", 7)) {
- if ((connect_url = strstr((url + 7), "https://"))) {
- do_connect = TRUE;
- connect_host = HTParse(connect_url, "https", PARSE_HOST);
- if (!strchr(connect_host, ':')) {
- sprintf(temp, ":%d", HTTPS_PORT);
- StrAllocCat(connect_host, temp);
- }
- CTRACE((tfp, "HTTP: connect_url = '%s'\n", connect_url));
- CTRACE((tfp, "HTTP: connect_host = '%s'\n", connect_host));
- } else if ((connect_url = strstr((url + 7), "snews://"))) {
- do_connect = TRUE;
- connect_host = HTParse(connect_url, "snews", PARSE_HOST);
- if (!strchr(connect_host, ':')) {
- sprintf(temp, ":%d", SNEWS_PORT);
- StrAllocCat(connect_host, temp);
- }
- CTRACE((tfp, "HTTP: connect_url = '%s'\n", connect_url));
- CTRACE((tfp, "HTTP: connect_host = '%s'\n", connect_host));
- }
- }
- #endif /* USE_SSL */
- sprintf(crlf, "%c%c", CR, LF);
- /*
- * At this point, we're talking HTTP/1.0.
- */
- extensions = YES;
- try_again:
- /*
- * All initializations are moved down here from up above, so we can start
- * over here...
- */
- eol = 0;
- had_header = NO;
- length = 0;
- doing_redirect = FALSE;
- permanent_redirection = FALSE;
- redirect_post_content = FALSE;
- target = NULL;
- line_buffer = NULL;
- line_kept_clean = NULL;
- #ifdef USE_SSL
- if (!strncmp(url, "https", 5))
- status = HTDoConnect(url, "HTTPS", HTTPS_PORT, &s);
- else
- status = HTDoConnect(url, "HTTP", HTTP_PORT, &s);
- #else
- if (!strncmp(url, "https", 5)) {
- HTAlert(gettext("This client does not contain support for HTTPS URLs."));
- status = HT_NOT_LOADED;
- goto done;
- }
- status = HTDoConnect(arg, "HTTP", HTTP_PORT, &s);
- #endif /* USE_SSL */
- if (status == HT_INTERRUPTED) {
- /*
- * Interrupt cleanly.
- */
- CTRACE((tfp, "HTTP: Interrupted on connect; recovering cleanly.\n"));
- _HTProgress(CONNECTION_INTERRUPTED);
- status = HT_NOT_LOADED;
- goto done;
- }
- if (status < 0) {
- #ifdef _WINDOWS
- CTRACE((tfp, "HTTP: Unable to connect to remote host for `%s'\n"
- " (status = %d, sock_errno = %d).\n",
- url, status, SOCKET_ERRNO));
- #else
- CTRACE((tfp,
- "HTTP: Unable to connect to remote host for `%s' (errno = %d).\n",
- url, SOCKET_ERRNO));
- #endif
- HTAlert(gettext("Unable to connect to remote host."));
- status = HT_NOT_LOADED;
- goto done;
- }
- #ifdef USE_SSL
- use_tunnel:
- /*
- * If this is an https document, then do the SSL stuff here.
- */
- if (did_connect || !strncmp(url, "https", 5)) {
- SSL_handle = handle = HTGetSSLHandle();
- SSL_set_fd(handle, s);
- #if SSLEAY_VERSION_NUMBER >= 0x0900
- if (!try_tls)
- handle->options |= SSL_OP_NO_TLSv1;
- #endif /* SSLEAY_VERSION_NUMBER >= 0x0900 */
- HTSSLInitPRNG();
- status = SSL_connect(handle);
- if (status <= 0) {
- #if SSLEAY_VERSION_NUMBER >= 0x0900
- if (try_tls) {
- _HTProgress(gettext("Retrying connection without TLS."));
- try_tls = FALSE;
- if (did_connect)
- HTTP_NETCLOSE(s, handle);
- goto try_again;
- } else {
- unsigned long SSLerror;
- CTRACE((tfp,
- "HTTP: Unable to complete SSL handshake for '%s', SSL_connect=%d, SSL error stack dump follows\n",
- url, status));
- SSL_load_error_strings();
- while ((SSLerror = ERR_get_error()) != 0) {
- CTRACE((tfp, "HTTP: SSL: %s\n", ERR_error_string(SSLerror, NULL)));
- }
- HTAlert("Unable to make secure connection to remote host.");
- if (did_connect)
- HTTP_NETCLOSE(s, handle);
- status = HT_NOT_LOADED;
- goto done;
- }
- #else
- unsigned long SSLerror;
- CTRACE((tfp,
- "HTTP: Unable to complete SSL handshake for '%s', SSL_connect=%d, SSL error stack dump follows\n",
- url, status));
- SSL_load_error_strings();
- while ((SSLerror = ERR_get_error()) != 0) {
- CTRACE((tfp, "HTTP: SSL: %s\n", ERR_error_string(SSLerror, NULL)));
- }
- HTAlert("Unable to make secure connection to remote host.");
- if (did_connect)
- HTTP_NETCLOSE(s, handle);
- status = HT_NOT_LOADED;
- goto done;
- #endif /* SSLEAY_VERSION_NUMBER >= 0x0900 */
- }
- #ifdef USE_GNUTLS_INCL
- ret = gnutls_certificate_verify_peers2(handle->gnutls_state, &tls_status);
- if ((ret < 0) || tls_status) {
- int flag_continue = 1;
- char *msg2;
- if (tls_status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
- msg2 = gettext("no issuer was found");
- } else if (tls_status & GNUTLS_CERT_SIGNER_NOT_CA) {
- msg2 = gettext("issuer is not a CA");
- } else if (tls_status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
- msg2 = gettext("the certificate has no known issuer");
- } else if (tls_status & GNUTLS_CERT_REVOKED) {
- msg2 = gettext("the certificate has been revoked");
- } else {
- msg2 = gettext("the certificate is not trusted");
- }
- HTSprintf0(&msg, SSL_FORCED_PROMPT, msg2);
- CTRACE((tfp, "HTLoadHTTP: %s\n", msg));
- if (!ssl_noprompt) {
- if (!HTForcedPrompt(ssl_noprompt, msg, YES)) {
- flag_continue = 0;
- }
- } else if (ssl_noprompt == FORCE_PROMPT_NO) {
- flag_continue = 0;
- }
- FREE(msg);
- if (flag_continue == 0) {
- status = HT_NOT_LOADED;
- FREE(msg);
- goto done;
- }
- }
- #endif
- X509_NAME_oneline(X509_get_subject_name(SSL_get_peer_certificate(handle)),
- #ifndef USE_GNUTLS_INCL
- ssl_dn, sizeof(ssl_dn));
- #else
- ssl_dn + 1, sizeof(ssl_dn) - 1);
- /* Iterate over DN in incompatible GnuTLS format to bring it into OpenSSL format */
- ssl_dn[0] = '/';
- ssl_dn_start = ssl_dn;
- while (*ssl_dn_start) {
- if ((*ssl_dn_start == ',') && (*(ssl_dn_start + 1) == ' ')) {
- *ssl_dn_start++ = '/';
- if (*(p = ssl_dn_start) != 0) {
- while ((p[0] = p[1]) != 0)
- ++p;
- }
- } else {
- ssl_dn_start++;
- }
- }
- #endif
- /*
- * X.509 DN validation taking ALL CN fields into account
- * (c) 2006 Thorsten Glaser <tg@mirbsd.de>
- */
- /* initialise status information */
- status_sslcertcheck = 0; /* 0 = no CN found in DN */
- ssl_dn_start = ssl_dn;
- ssl_all_cns = NULL;
- /* get host we're connecting to */
- ssl_host = HTParse(url, "", PARSE_HOST);
- /* strip port number */
- if ((p = strchr(ssl_host, ':')) != NULL)
- *p = '\0';
- /* validate all CNs found in DN */
- while ((cert_host = strstr(ssl_dn_start, "/CN=")) != NULL) {
- status_sslcertcheck = 1; /* 1 = could not verify CN */
- /* start of CommonName */
- cert_host += 4;
- /* find next part of DistinguishedName */
- if ((p = strchr(cert_host, '/')) != NULL) {
- *p = '\0';
- ssl_dn_start = p; /* yes this points to the NUL byte */
- } else
- ssl_dn_start = NULL;
- /* strip port number */
- if ((p = strchr(cert_host, ':')) != NULL)
- *p = '\0';
- /* verify this CN */
- if (!strcasecomp_asterisk(ssl_host, cert_host)) {
- status_sslcertcheck = 2; /* 2 = verified peer */
- /* I think this is cool to have in the logs --mirabilos */
- HTSprintf0(&msg,
- gettext("Verified connection to %s (cert=%s)"),
- ssl_host, cert_host);
- _HTProgress(msg);
- FREE(msg);
- /* no need to continue the verification loop */
- break;
- }
- /* add this CN to list of failed CNs */
- if (ssl_all_cns == NULL) {
- StrAllocCopy(ssl_all_cns, cert_host);
- } else {
- StrAllocCat(ssl_all_cns, ":");
- StrAllocCat(ssl_all_cns, cert_host);
- }
- /* if we cannot retry, don't try it */
- if (ssl_dn_start == NULL)
- break;
- /* now retry next CN found in DN */
- *ssl_dn_start = '/'; /* formerly NUL byte */
- }
- /* if an error occurred, format the appropriate message */
- if (status_sslcertcheck == 0) {
- HTSprintf0(&msg, SSL_FORCED_PROMPT,
- gettext("Can't find common name in certificate"));
- } else if (status_sslcertcheck == 1) {
- HTSprintf0(&msg,
- gettext("SSL error:host(%s)!=cert(%s)-Continue?"),
- ssl_host, ssl_all_cns);
- }
- /* if an error occurred, let the user decide how much he trusts */
- if (status_sslcertcheck < 2) {
- if (!HTForcedPrompt(ssl_noprompt, msg, YES)) {
- status = HT_NOT_LOADED;
- FREE(msg);
- FREE(ssl_all_cns);
- goto done;
- }
- }
- HTSprintf0(&msg,
- gettext("Secure %d-bit %s (%s) HTTP connection"),
- SSL_get_cipher_bits(handle, NULL),
- SSL_get_cipher_version(handle),
- SSL_get_cipher(handle));
- _HTProgress(msg);
- FREE(msg);
- }
- #endif /* USE_SSL */
- /* Ask that node for the document, omitting the host name & anchor
- */
- {
- char *p1 = (HTParse(url, "", PARSE_PATH | PARSE_PUNCTUATION));
- #ifdef USE_SSL
- if (do_connect) {
- METHOD = "CONNECT";
- BStrCopy0(command, "CONNECT ");
- } else
- #endif /* USE_SSL */
- if (do_post) {
- METHOD = "POST";
- BStrCopy0(command, "POST ");
- } else if (do_head) {
- METHOD = "HEAD";
- BStrCopy0(command, "HEAD ");
- } else {
- METHOD = "GET";
- BStrCopy0(command, "GET ");
- }
- /*
- * If we are using a proxy gateway don't copy in the first slash of
- * say: /gopher://a;lkdjfl;ajdf;lkj/;aldk/adflj so that just
- * gopher://.... is sent.
- */
- #ifdef USE_SSL
- if (using_proxy && !did_connect) {
- if (do_connect)
- BStrCat0(command, connect_host);
- else
- BStrCat0(command, p1 + 1);
- }
- #else
- if (using_proxy)
- BStrCat0(command, p1 + 1);
- #endif /* USE_SSL */
- else
- BStrCat0(command, p1);
- FREE(p1);
- }
- if (extensions) {
- BStrCat0(command, " ");
- BStrCat0(command, HTTP_VERSION);
- }
- BStrCat0(command, crlf); /* CR LF, as in rfc 977 */
- if (extensions) {
- int n, i;
- char *host = NULL;
- if ((host = HTParse(anAnchor->address, "", PARSE_HOST)) != NULL) {
- strip_userid(host);
- HTBprintf(&command, "Host: %s%c%c", host, CR, LF);
- FREE(host);
- }
- if (!HTPresentations)
- HTFormatInit();
- n = HTList_count(HTPresentations);
- first_Accept = TRUE;
- len = 0;
- for (i = 0; i < n; i++) {
- HTPresentation *pres =
- (HTPresentation *) HTList_objectAt(HTPresentations, i);
- if (pres->get_accept) {
- if (pres->quality < 1.0) {
- if (pres->maxbytes > 0) {
- sprintf(temp, ";q=%4.3f;mxb=%ld",
- pres->quality, pres->maxbytes);
- } else {
- sprintf(temp, ";q=%4.3f", pres->quality);
- }
- } else if (pres->maxbytes > 0) {
- sprintf(temp, ";mxb=%ld", pres->maxbytes);
- } else {
- temp[0] = '\0';
- }
- HTSprintf0(&linebuf, "%s%s%s",
- (first_Accept ?
- "Accept: " : ", "),
- HTAtom_name(pres->rep),
- temp);
- len += strlen(linebuf);
- if (len > 252 && !first_Accept) {
- BStrCat0(command, crlf);
- HTSprintf0(&linebuf, "Accept: %s%s",
- HTAtom_name(pres->rep),
- temp);
- len = strlen(linebuf);
- }
- BStrCat0(command, linebuf);
- first_Accept = FALSE;
- }
- }
- HTBprintf(&command, "%s*/*;q=0.01%c%c",
- (first_Accept ?
- "Accept: " : ", "), CR, LF);
- first_Accept = FALSE;
- len = 0;
- /*
- * FIXME: suppressing the "Accept-Encoding" in this case is done to
- * work around limitations of the presentation logic used for the
- * command-line "-base" option. The remote site may transmit the
- * document gzip'd, but the ensuing logic in HTSaveToFile() would see
- * the mime-type as gzip rather than text/html, and not prepend the
- * base URL. This is less efficient than accepting the compressed data
- * and uncompressing it, adding the base URL but is simpler than
- * augmenting the dump's presentation logic -TD
- */
- if (LYPrependBaseToSource && dump_output_immediately) {
- CTRACE((tfp,
- "omit Accept-Encoding to work-around interaction with -source\n"));
- } else {
- char *list = 0;
- int j, k;
- for (j = 1; j < encodingALL; j <<= 1) {
- if (acceptEncoding(j)) {
- for (k = 0; tbl_preferred_encoding[k].name != 0; ++k) {
- if (tbl_preferred_encoding[k].value == j) {
- if (list != 0)
- StrAllocCat(list, ", ");
- StrAllocCat(list, tbl_preferred_encoding[k].name);
- break;
- }
- }
- }
- }
- if (list != 0) {
- HTBprintf(&command, "Accept-Encoding: %s%c%c", list, CR, LF);
- free(list);
- }
- }
- if (language && *language) {
- HTBprintf(&command, "Accept-Language: %s%c%c", language, CR, LF);
- }
- if (pref_charset && *pref_charset) {
- BStrCat0(command, "Accept-Charset: ");
- StrAllocCopy(linebuf, pref_charset);
- if (linebuf[strlen(linebuf) - 1] == ',')
- linebuf[strlen(linebuf) - 1] = '\0';
- LYLowerCase(linebuf);
- if (strstr(linebuf, "iso-8859-1") == NULL)
- StrAllocCat(linebuf, ", iso-8859-1;q=0.01");
- if (strstr(linebuf, "us-ascii") == NULL)
- StrAllocCat(linebuf, ", us-ascii;q=0.01");
- BStrCat0(command, linebuf);
- HTBprintf(&command, "%c%c", CR, LF);
- }
- #if 0
- /*
- * Promote 300 (Multiple Choices) replies, if supported, over 406 (Not
- * Acceptable) replies. - FM
- *
- * This used to be done in versions 2.7 and 2.8*, but violates the
- * specs for transparent content negotiation and has the effect that
- * servers supporting those specs will send 300 (Multiple Choices)
- * instead of a normal response (e.g. 200 OK), since they will assume
- * that the client wants to make the choice. It is not clear whether
- * there are any servers or sites for which sending this header really
- * improves anything.
- *
- * If there ever is a need to send "Negotiate: trans" and really mean
- * it, we should send "Negotiate: trans,trans" or similar, since that
- * is semantically equivalent and some servers may ignore "Negotiate:
- * trans" as a special case when it comes from Lynx (to work around the
- * old faulty behavior). - kw
- *
- * References:
- * RFC 2295 (see also RFC 2296), and mail to lynx-dev and
- * new-httpd@apache.org from Koen Holtman, Jan 1999.
- */
- if (!do_post) {
- HTBprintf(&command, "Negotiate: trans%c%c", CR, LF);
- }
- #endif /* 0 */
- /*
- * When reloading give no-cache pragma to proxy server to make it
- * refresh its cache. -- Ari L. <luotonen@dxcern.cern.ch>
- *
- * Also send it as a Cache-Control header for HTTP/1.1. - FM
- */
- if (reloading) {
- HTBprintf(&command, "Pragma: no-cache%c%c", CR, LF);
- HTBprintf(&command, "Cache-Control: no-cache%c%c", CR, LF);
- }
- if (LYUserAgent && *LYUserAgent) {
- char *cp = LYSkipBlanks(LYUserAgent);
- /* Won't send it at all if all blank - kw */
- if (*cp != '\0')
- HTBprintf(&command, "User-Agent: %.*s%c%c",
- INIT_LINE_SIZE - 15, LYUserAgent, CR, LF);
- } else {
- HTBprintf(&command, "User-Agent: %s/%s libwww-FM/%s%c%c",
- HTAppName ? HTAppName : "unknown",
- HTAppVersion ? HTAppVersion : "0.0",
- HTLibraryVersion, CR, LF);
- }
- if (personal_mail_address && !LYNoFromHeader) {
- HTBprintf(&command, "From: %s%c%c", personal_mail_address, CR, LF);
- }
- if (!(LYUserSpecifiedURL ||
- LYNoRefererHeader || LYNoRefererForThis) &&
- strcmp(HTLoadedDocumentURL(), "")) {
- const char *cp = LYRequestReferer;
- if (!cp)
- cp = HTLoadedDocumentURL(); /* @@@ Try both? - kw */
- BStrCat0(command, "Referer: ");
- if (isLYNXIMGMAP(cp)) {
- char *pound = findPoundSelector(cp);
- int nn = (pound ? (int) (pound - cp) : (int) strlen(cp));
- HTSABCat(&command, cp + LEN_LYNXIMGMAP, nn);
- } else {
- BStrCat0(command, cp);
- }
- HTBprintf(&command, "%c%c", CR, LF);
- } {
- char *abspath;
- char *docname;
- char *hostname;
- char *colon;
- int portnumber;
- char *auth, *cookie = NULL;
- BOOL secure = (BOOL) (strncmp(anAnchor->address, "https", 5) ?
- FALSE : TRUE);
- abspath = HTParse(arg, "", PARSE_PATH | PARSE_PUNCTUATION);
- docname = HTParse(arg, "", PARSE_PATH);
- hostname = HTParse(arg, "", PARSE_HOST);
- if (hostname &&
- NULL != (colon = strchr(hostname, ':'))) {
- *(colon++) = '\0'; /* Chop off port number */
- portnumber = atoi(colon);
- } else if (!strncmp(arg, "https", 5)) {
- portnumber = HTTPS_PORT;
- } else {
- portnumber = HTTP_PORT;
- }
- /*
- * Add Authorization, Proxy-Authorization, and/or Cookie headers,
- * if applicable.
- */
- if (using_proxy) {
- /*
- * If we are using a proxy, first determine if we should
- * include an Authorization header and/or Cookie header for the
- * ultimate target of this request. - FM & AJL
- */
- char *host2 = NULL, *path2 = NULL;
- int port2 = (strncmp(docname, "https", 5) ?
- HTTP_PORT : HTTPS_PORT);
- host2 = HTParse(docname, "", PARSE_HOST);
- path2 = HTParse(docname, "", PARSE_PATH | PARSE_PUNCTUATION);
- if (host2) {
- if ((colon = strchr(host2, ':')) != NULL) {
- /* Use non-default port number */
- *colon = '\0';
- colon++;
- port2 = atoi(colon);
- }
- }
- /*
- * This composeAuth() does file access, i.e., for the ultimate
- * target of the request. - AJL
- */
- auth_proxy = NO;
- if ((auth = HTAA_composeAuth(host2, port2, path2,
- auth_proxy)) != NULL &&
- *auth != '\0') {
- /*
- * If auth is not NULL nor zero-length, it's an
- * Authorization header to be included. - FM
- */
- HTBprintf(&command, "%s%c%c", auth, CR, LF);
- CTRACE((tfp, "HTTP: Sending authorization: %s\n", auth));
- } else if (auth && *auth == '\0') {
- /*
- * If auth is a zero-length string, the user either
- * cancelled or goofed at the username and password prompt.
- * - FM
- */
- if (!(traversal || dump_output_immediately) &&
- HTConfirm(CONFIRM_WO_PASSWORD)) {
- show_401 = TRUE;
- } else {
- if (traversal || dump_output_immediately)
- HTAlert(FAILED_NEED_PASSWD);
- #ifdef USE_SSL
- if (did_connect)
- HTTP_NETCLOSE(s, handle);
- #endif /* USE_SSL */
- BStrFree(command);
- FREE(hostname);
- FREE(docname);
- FREE(abspath);
- FREE(host2);
- FREE(path2);
- status = HT_NOT_LOADED;
- goto done;
- }
- } else {
- CTRACE((tfp, "HTTP: Not sending authorization (yet).\n"));
- }
- /*
- * Add 'Cookie:' header, if it's HTTP or HTTPS document being
- * proxied.
- */
- if (!strncmp(docname, "http", 4)) {
- cookie = LYAddCookieHeader(host2, path2, port2, secure);
- }
- FREE(host2);
- FREE(path2);
- /*
- * The next composeAuth() will be for the proxy. - AJL
- */
- auth_proxy = YES;
- } else {
- /*
- * Add cookie for a non-proxied request. - FM
- */
- cookie = LYAddCookieHeader(hostname, abspath, portnumber, secure);
- auth_proxy = NO;
- }
- /*
- * If we do have a cookie set, add it to the request buffer. - FM
- */
- if (cookie != NULL) {
- if (*cookie != '$') {
- /*
- * It's a historical cookie, so signal to the server that
- * we support modern cookies. - FM
- */
- BStrCat0(command, "Cookie2: $Version=\"1\"");
- BStrCat0(command, crlf);
- CTRACE((tfp, "HTTP: Sending Cookie2: $Version =\"1\"\n"));
- }
- if (*cookie != '\0') {
- /*
- * It's not a zero-length string, so add the header. Note
- * that any folding of long strings has been done already
- * in LYCookie.c. - FM
- */
- BStrCat0(command, "Cookie: ");
- BStrCat0(command, cookie);
- BStrCat0(command, crlf);
- CTRACE((tfp, "HTTP: Sending Cookie: %s\n", cookie));
- }
- FREE(cookie);
- }
- FREE(abspath);
- /*
- * If we are using a proxy, auth_proxy should be YES, and we check
- * here whether we want a Proxy-Authorization header for it. If we
- * are not using a proxy, auth_proxy should still be NO, and we
- * check here for whether we want an Authorization header. - FM &
- * AJL
- */
- if ((auth = HTAA_composeAuth(hostname,
- portnumber,
- docname,
- auth_proxy)) != NULL &&
- *auth != '\0') {
- /*
- * If auth is not NULL nor zero-length, it's an Authorization
- * or Proxy-Authorization header to be included. - FM
- */
- HTBprintf(&command, "%s%c%c", auth, CR, LF);
- CTRACE((tfp, (auth_proxy ?
- "HTTP: Sending proxy authorization: %s\n" :
- "HTTP: Sending authorization: %s\n"),
- auth));
- } else if (auth && *auth == '\0') {
- /*
- * If auth is a zero-length string, the user either cancelled
- * or goofed at the username and password prompt. - FM
- */
- if (!(traversal || dump_output_immediately) && HTConfirm(CONFIRM_WO_PASSWORD)) {
- if (auth_proxy == TRUE) {
- show_407 = TRUE;
- } else {
- show_401 = TRUE;
- }
- } else {
- if (traversal || dump_output_immediately)
- HTAlert(FAILED_NEED_PASSWD);
- BStrFree(command);
- FREE(hostname);
- FREE(docname);
- status = HT_NOT_LOADED;
- goto done;
- }
- } else {
- CTRACE((tfp, (auth_proxy ?
- "HTTP: Not sending proxy authorization (yet).\n" :
- "HTTP: Not sending authorization (yet).\n")));
- }
- FREE(hostname);
- FREE(docname);
- }
- auth_proxy = NO;
- }
- if (
- #ifdef USE_SSL
- !do_connect &&
- #endif /* USE_SSL */
- do_post) {
- CTRACE((tfp, "HTTP: Doing post, content-type '%s'\n",
- anAnchor->post_content_type
- ? anAnchor->post_content_type
- : "lose"));
- HTBprintf(&command, "Content-type: %s%c%c",
- anAnchor->post_content_type
- ? anAnchor->post_content_type
- : "lose",
- CR, LF);
- HTBprintf(&command, "Content-length: %d%c%c",
- !isBEmpty(anAnchor->post_data)
- ? BStrLen(anAnchor->post_data)
- : 0,
- CR, LF);
- BStrCat0(command, crlf); /* Blank line means "end" of headers */
- BStrCat(command, anAnchor->post_data);
- } else
- BStrCat0(command, crlf); /* Blank line means "end" of headers */
- if (TRACE) {
- CTRACE((tfp, "Writing:\n"));
- trace_bstring(command);
- #ifdef USE_SSL
- CTRACE((tfp, "%s",
- (anAnchor->post_data && !do_connect ? crlf : "")));
- #else
- CTRACE((tfp, "%s",
- (anAnchor->post_data ? crlf : "")));
- #endif /* USE_SSL */
- CTRACE((tfp, "----------------------------------\n"));
- }
- _HTProgress(gettext("Sending HTTP request."));
- #ifdef NOT_ASCII /* S/390 -- gil -- 0548 */
- {
- char *p2;
- for (p2 = BStrData(command);
- p2 < BStrData(command) + BStrLen(command);
- p2++)
- *p2 = TOASCII(*p2);
- }
- #endif /* NOT_ASCII */
- status = HTTP_NETWRITE(s, BStrData(command), BStrLen(command), handle);
- BStrFree(command);
- FREE(linebuf);
- if (status <= 0) {
- if (status == 0) {
- CTRACE((tfp, "HTTP: Got status 0 in initial write\n"));
- /* Do nothing. */
- } else if ((SOCKET_ERRNO == ENOTCONN ||
- SOCKET_ERRNO == ECONNRESET ||
- SOCKET_ERRNO == EPIPE) &&
- !already_retrying &&
- /* Don't retry if we're posting. */ !do_post) {
- /*
- * Arrrrgh, HTTP 0/1 compatibility problem, maybe.
- */
- CTRACE((tfp,
- "HTTP: BONZO ON WRITE Trying again with HTTP0 request.\n"));
- _HTProgress(RETRYING_AS_HTTP0);
- HTTP_NETCLOSE(s, handle);
- extensions = NO;
- already_retrying = TRUE;
- goto try_again;
- } else {
- CTRACE((tfp,
- "HTTP: Hit unexpected network WRITE error; aborting connection.\n"));
- HTTP_NETCLOSE(s, handle);
- status = -1;
- HTAlert(gettext("Unexpected network write error; connection aborted."));
- goto done;
- }
- }
- CTRACE((tfp, "HTTP: WRITE delivered OK\n"));
- _HTProgress(gettext("HTTP request sent; waiting for response."));
- /* Read the first line of the response
- * -----------------------------------
- */
- {
- /* Get numeric status etc */
- BOOL end_of_file = NO;
- int buffer_length = INIT_LINE_SIZE;
- line_buffer = typecallocn(char, buffer_length);
- if (line_buffer == NULL)
- outofmem(__FILE__, "HTLoadHTTP");
- HTReadProgress(bytes_already_read = 0, 0);
- do { /* Loop to read in the first line */
- /*
- * Extend line buffer if necessary for those crazy WAIS URLs ;-)
- */
- if (buffer_length - length < LINE_EXTEND_THRESH) {
- buffer_length = buffer_length + buffer_length;
- line_buffer =
- (char *) realloc(line_buffer, (buffer_length * sizeof(char)));
- if (line_buffer == NULL)
- outofmem(__FILE__, "HTLoadHTTP");
- }
- CTRACE((tfp, "HTTP: Trying to read %d\n", buffer_length - length - 1));
- status = HTTP_NETREAD(s, line_buffer + length,
- buffer_length - length - 1, handle);
- CTRACE((tfp, "HTTP: Read %d\n", status));
- if (status <= 0) {
- /*
- * Retry if we get nothing back too.
- * Bomb out if we get nothing twice.
- */
- if (status == HT_INTERRUPTED) {
- CTRACE((tfp, "HTTP: Interrupted initial read.\n"));
- _HTProgress(CONNECTION_INTERRUPTED);
- HTTP_NETCLOSE(s, handle);
- status = HT_NO_DATA;
- goto clean_up;
- } else if (status < 0 &&
- (SOCKET_ERRNO == ENOTCONN ||
- #ifdef _WINDOWS /* 1997/11/09 (Sun) 16:59:58 */
- SOCKET_ERRNO == ETIMEDOUT ||
- #endif
- SOCKET_ERRNO == ECONNRESET ||
- SOCKET_ERRNO == EPIPE) &&
- !already_retrying && !do_post) {
- /*
- * Arrrrgh, HTTP 0/1 compability problem, maybe.
- */
- CTRACE((tfp,
- "HTTP: BONZO Trying again with HTTP0 request.\n"));
- HTTP_NETCLOSE(s, handle);
- FREE(line_buffer);
- FREE(line_kept_clean);
- extensions = NO;
- already_retrying = TRUE;
- _HTProgress(RETRYING_AS_HTTP0);
- goto try_again;
- } else {
- CTRACE((tfp,
- "HTTP: Hit unexpected network read error; aborting connection; status %d.\n",
- status));
- HTAlert(gettext("Unexpected network read error; connection aborted."));
- HTTP_NETCLOSE(s, handle);
- status = -1;
- goto clean_up;
- }
- }
- #ifdef NOT_ASCII /* S/390 -- gil -- 0564 */
- {
- char *p2;
- for (p2 = line_buffer + length;
- p2 < line_buffer + length + status;
- p2++)
- *p2 = FROMASCII(*p2);
- }
- #endif /* NOT_ASCII */
- bytes_already_read += status;
- HTReadProgress(bytes_already_read, 0);
- #ifdef UCX /* UCX returns -1 on EOF */
- if (status == 0 || status == -1)
- #else
- if (status == 0)
- #endif
- {
- end_of_file = YES;
- break;
- }
- line_buffer[length + status] = 0;
- if (line_buffer) {
- FREE(line_kept_clean);
- line_kept_clean = (char *) malloc(buffer_length * sizeof(char));
- if (line_kept_clean == NULL)
- outofmem(__FILE__, "HTLoadHTTP");
- memcpy(line_kept_clean, line_buffer, buffer_length);
- real_length_of_line = length + status;
- }
- eol = strchr(line_buffer + length, LF);
- /* Do we *really* want to do this? */
- if (eol && eol != line_buffer && *(eol - 1) == CR)
- *(eol - 1) = ' ';
- length = length + status;
- /* Do we really want to do *this*? */
- if (eol)
- *eol = 0; /* Terminate the line */
- }
- /* All we need is the first line of the response. If it's a HTTP/1.0
- * response, then the first line will be absurdly short and therefore
- * we can safely gate the number of bytes read through this code (as
- * opposed to below) to ~1000.
- *
- * Well, let's try 100.
- */
- while (!eol && !end_of_file && bytes_already_read < 100);
- } /* Scope of loop variables */
- /* save total length, in case we decide later to show it all - kw */
- rawlength = length;
- /* We now have a terminated unfolded line. Parse it.
- * --------------------------------------------------
- */
- CTRACE((tfp, "HTTP: Rx: %s\n", line_buffer));
- /*
- * Kludge to work with old buggy servers and the VMS Help gateway. They
- * can't handle the third word, so we try again without it.
- */
- if (extensions && /* Old buggy server or Help gateway? */
- (0 == strncmp(line_buffer, "<TITLE>Bad File Request</TITLE>", 31) ||
- 0 == strncmp(line_buffer, "Address should begin with", 25) ||
- 0 == strncmp(line_buffer, "<TITLE>Help ", 12) ||
- 0 == strcmp(line_buffer,
- "Document address invalid or access not authorised"))) {
- FREE(line_buffer);
- FREE(line_kept_clean);
- extensions = NO;
- already_retrying = TRUE;
- CTRACE((tfp, "HTTP: close socket %d to retry with HTTP0\n", s));
- HTTP_NETCLOSE(s, handle);
- /* print a progress message */
- _HTProgress(RETRYING_AS_HTTP0);
- goto try_again;
- } {
- int fields;
- char server_version[VERSION_LENGTH + 1];
- server_version[0] = 0;
- fields = sscanf(line_buffer, "%20s %d",
- server_version,
- &server_status);
- CTRACE((tfp, "HTTP: Scanned %d fields from line_buffer\n", fields));
- if (http_error_file) { /* Make the status code externally available */
- FILE *error_file;
- #ifdef SERVER_STATUS_ONLY
- error_file = fopen(http_error_file, TXT_W);
- if (error_file) { /* Managed to open the file */
- fprintf(error_file, "error=%d\n", server_status);
- fclose(error_file);
- }
- #else
- error_file = fopen(http_error_file, TXT_A);
- if (error_file) { /* Managed to open the file */
- fprintf(error_file, " URL=%s (%s)\n", url, METHOD);
- fprintf(error_file, "STATUS=%s\n", line_buffer);
- fclose(error_file);
- }
- #endif /* SERVER_STATUS_ONLY */
- }
- /*
- * Rule out a non-HTTP/1.n reply as best we can.
- */
- if (fields < 2 || !server_version[0] || server_version[0] != 'H' ||
- server_version[1] != 'T' || server_version[2] != 'T' ||
- server_version[3] != 'P' || server_version[4] != '/' ||
- server_version[6] != '.') {
- /*
- * Ugh! An HTTP0 reply,
- */
- HTAtom *encoding;
- CTRACE((tfp, "--- Talking HTTP0.\n"));
- format_in = HTFileFormat(url, &encoding, NULL);
- /*
- * Treat all plain text as HTML. This sucks but its the only
- * solution without without looking at content.
- */
- if (!strncmp(HTAtom_name(format_in), "text/plain", 10)) {
- CTRACE((tfp, "HTTP: format_in being changed to text/HTML\n"));
- format_in = WWW_HTML;
- }
- if (!IsUnityEnc(encoding)) {
- /*
- * Change the format to that for "www/compressed".
- */
- CTRACE((tfp, "HTTP: format_in is '%s',\n", HTAtom_name(format_in)));
- StrAllocCopy(anAnchor->content_type, HTAtom_name(format_in));
- StrAllocCopy(anAnchor->content_encoding, HTAtom_name(encoding));
- format_in = HTAtom_for("www/compressed");
- CTRACE((tfp, " Treating as '%s' with encoding '%s'\n",
- "www/compressed", HTAtom_name(encoding)));
- }
- start_of_data = line_kept_clean;
- } else {
- /*
- * Set up to decode full HTTP/1.n response. - FM
- */
- format_in = HTAtom_for("www/mime");
- CTRACE((tfp, "--- Talking HTTP1.\n"));
- /*
- * We set start_of_data to "" when !eol here because there will be
- * a put_block done below; we do *not* use the value of
- * start_of_data (as a pointer) in the computation of length (or
- * anything else) when !eol. Otherwise, set the value of length to
- * what we have beyond eol (i.e., beyond the status line). - FM
- */
- start_of_data = eol ? eol + 1 : empty;
- length = eol ? length - (start_of_data - line_buffer) : 0;
- /*
- * Trim trailing spaces in line_buffer so that we can use it in
- * messages which include the status line. - FM
- */
- while (line_buffer[strlen(line_buffer) - 1] == ' ')
- line_buffer[strlen(line_buffer) - 1] = '\0';
- /*
- * Take appropriate actions based on the status. - FM
- */
- switch (server_status / 100) {
- case 1:
- /*
- * HTTP/1.1 Informational statuses.
- * 100 Continue.
- * 101 Switching Protocols.
- * > 101 is unknown.
- * We should never get these, and they have only the status
- * line and possibly other headers, so we'll deal with them by
- * showing the full header to the user as text/plain. - FM
- */
- HTAlert(gettext("Got unexpected Informational Status."));
- do_head = TRUE;
- break;
- case 2:
- /*
- * Good: Got MIME object! (Successful) - FM
- */
- if (do_head) {
- /*
- * If HEAD was requested, show headers (and possibly bogus
- * body) for all 2xx status codes as text/plain - KW
- */
- HTProgress(line_buffer);
- break;
- }
- switch (server_status) {
- case 204:
- /*
- * No Content.
- */
- HTAlert(line_buffer);
- HTTP_NETCLOSE(s, handle);
- HTNoDataOK = 1;
- status = HT_NO_DATA;
- goto clean_up;
- case 205:
- /*
- * Reset Content. The server has fulfilled the request but
- * nothing is returned and we should reset any form
- * content. We'll instruct the user to do that, and
- * restore the current document. - FM
- */
- HTAlert(gettext("Request fulfilled. Reset Content."));
- HTTP_NETCLOSE(s, handle);
- status = HT_NO_DATA;
- goto clean_up;
- case 206:
- /*
- * Partial Content. We didn't send a Range so something
- * went wrong somewhere. Show the status message and
- * restore the current document. - FM
- */
- HTAlert(line_buffer);
- HTTP_NETCLOSE(s, handle);
- status = HT_NO_DATA;
- goto clean_up;
- default:
- /*
- * 200 OK.
- * 201 Created.
- * 202 Accepted.
- * 203 Non-Authoritative Information.
- * > 206 is unknown.
- * All should return something to display.
- */
- #if defined(USE_SSL) && !defined(DISABLE_NEWS)
- if (do_connect) {
- CTRACE((tfp,
- "HTTP: Proxy tunnel to '%s' established.\n",
- connect_host));
- do_connect = FALSE;
- url = connect_url;
- FREE(line_buffer);
- FREE(line_kept_clean);
- if (!strncmp(connect_url, "snews", 5)) {
- CTRACE((tfp,
- " Will attempt handshake and snews connection.\n"));
- status = HTNewsProxyConnect(s, url, anAnchor,
- format_out, sink);
- goto done;
- }
- did_connect = TRUE;
- already_retrying = TRUE;
- eol = 0;
- bytes_already_read = 0;
- had_header = NO;
- length = 0;
- doing_redirect = FALSE;
- permanent_redirection = FALSE;
- target = NULL;
- CTRACE((tfp,
- " Will attempt handshake and resubmit headers.\n"));
- goto use_tunnel;
- }
- #endif /* USE_SSL */
- HTProgress(line_buffer);
- } /* case 2 switch */
- break;
- case 3:
- /*
- * Various forms of Redirection. - FM
- * 300 Multiple Choices.
- * 301 Moved Permanently.
- * 302 Found (temporary; we can, and do, use GET).
- * 303 See Other (temporary; always use GET).
- * 304 Not Modified.
- * 305 Use Proxy.
- * 306 Set Proxy.
- * 307 Temporary Redirect with method retained.
- * > 308 is unknown.
- */
- if (no_url_redirection || do_head || keep_mime_headers) {
- /*
- * If any of these flags are set, we do not redirect, but
- * instead show what was returned to the user as
- * text/plain. - FM
- */
- HTProgress(line_buffer);
- break;
- }
- if (server_status == 300) { /* Multiple Choices */
- /*
- * For client driven content negotiation. The server
- * should be sending some way for the user-agent to make a
- * selection, so we'll show the user whatever the server
- * returns. There might be a Location: header with the
- * server's preference present, but the choice should be up
- * to the user, someday based on an Alternates: header,
- * and a body always should be present with descriptions
- * and links for the choices (i.e., we use the latter, for
- * now). - FM
- */
- HTAlert(line_buffer);
- if (traversal) {
- HTTP_NETCLOSE(s, handle);
- status = -1;
- goto clean_up;
- }
- if (!dump_output_immediately &&
- format_out == HTAtom_for("www/download")) {
- /*
- * Convert a download request to a presentation request
- * for interactive users. - FM
- */
- format_out = WWW_PRESENT;
- }
- break;
- }
- if (server_status == 304) { /* Not Modified */
- /*
- * We didn't send an "If-Modified-Since" header, so this
- * status is inappropriate. We'll deal with it by showing
- * the full header to the user as text/plain. - FM
- */
- HTAlert(gettext("Got unexpected 304 Not Modified status."));
- do_head = TRUE;
- break;
- }
- if (server_status == 305 ||
- server_status == 306 ||
- server_status > 307) {
- /*
- * Show user the content, if any, for 305, 306, or unknown
- * status. - FM
- */
- HTAlert(line_buffer);
- if (traversal) {
- HTTP_NETCLOSE(s, handle);
- status = -1;
- goto clean_up;
- }
- if (!dump_output_immediately &&
- format_out == HTAtom_for("www/download")) {
- /*
- * Convert a download request to a presentation request
- * for interactive users. - FM
- */
- format_out = WWW_PRESENT;
- }
- break;
- }
- /*
- * We do not load the file, but read the headers for the
- * "Location:", check out that redirecting_url and if it's
- * acceptible (e.g., not a telnet URL when we have that
- * disabled), initiate a new fetch. If that's another
- * redirecting_url, we'll repeat the checks, and fetch
- * initiations if acceptible, until we reach the actual URL, or
- * the redirection limit set in HTAccess.c is exceeded. If the
- * status was 301 indicating that the relocation is permanent,
- * we set the permanent_redirection flag to make it permanent
- * for the current anchor tree (i.e., will persist until the
- * tree is freed or the client exits). If the redirection
- * would include POST content, we seek confirmation from an
- * interactive user, with option to use 303 for 301 (but not
- * for 307), and otherwise refuse the redirection. We also
- * don't allow permanent redirection if we keep POST content.
- * If we don't find the Location header or it's value is
- * zero-length, we display whatever the server returned, and
- * the user should RELOAD that to try again, or make a
- * selection from it if it contains links, or Left-Arrow to the
- * previous document. - FM
- */
- {
- if ((dump_output_immediately || traversal) &&
- do_post &&
- server_status != 303 &&
- server_status != 302 &&
- server_status != 301) {
- /*
- * Don't redirect POST content without approval from an
- * interactive user. - FM
- */
- HTTP_NETCLOSE(s, handle);
- status = -1;
- HTAlert(gettext("Redirection of POST content requires user approval."));
- if (traversal)
- HTProgress(line_buffer);
- goto clean_up;
- }
- HTProgress(line_buffer);
- if (server_status == 301) { /* Moved Permanently */
- if (do_post) {
- /*
- * Don't make the redirection permanent if we have
- * POST content. - FM
- */
- CTRACE((tfp,
- "HTTP: Have POST content. Treating 301 (Permanent) as Temporary.\n"));
- HTAlert(gettext("Have POST content. Treating Permanent Redirection as Temporary.\n"));
- } else {
- permanent_redirection = TRUE;
- }
- }
- doing_redirect = TRUE;
- break;
- }
- case 4:
- /*
- * "I think I goofed!" (Client Error) - FM
- */
- switch (server_status) {
- case 401: /* Unauthorized */
- /*
- * Authorization for orgin server required. If show_401 is
- * set, proceed to showing the 401 body. Otherwise, if we
- * can set up authorization based on the WWW-Authenticate
- * header, and the user provides a username and password,
- * try again. Otherwise, check whether to show the 401
- * body or restore the current document. - FM
- */
- if (show_401)
- break;
- if (HTAA_shouldRetryWithAuth(start_of_data, length, s, NO)) {
- HTTP_NETCLOSE(s, handle);
- if (dump_output_immediately && !authentication_info[0]) {
- fprintf(stderr,
- "HTTP: Access authorization required.\n");
- fprintf(stderr,
- " Use the -auth=id:pw parameter.\n");
- status = HT_NO_DATA;
- goto clean_up;
- }
- CTRACE((tfp, "%s %d %s\n",
- "HTTP: close socket", s,
- "to retry with Access Authorization"));
- _HTProgress(gettext("Retrying with access authorization information."));
- FREE(line_buffer);
- FREE(line_kept_clean);
- #ifdef USE_SSL
- if (using_proxy && !strncmp(url, "https://", 8)) {
- url = arg;
- do_connect = TRUE;
- did_connect = FALSE;
- }
- #endif /* USE_SSL */
- goto try_again;
- } else if (!(traversal || dump_output_immediately) &&
- HTConfirm(gettext("Show the 401 message body?"))) {
- break;
- } else {
- if (traversal || dump_output_immediately)
- HTAlert(FAILED_RETRY_WITH_AUTH);
- HTTP_NETCLOSE(s, handle);
- status = -1;
- goto clean_up;
- }
- case 407:
- /*
- * Authorization for proxy server required. If we are not
- * in fact using a proxy, or show_407 is set, proceed to
- * showing the 407 body. Otherwise, if we can set up
- * authorization based on the Proxy-Authenticate header,
- * and the user provides a username and password, try
- * again. Otherwise, check whether to show the 401 body or
- * restore the current document. - FM & AJL
- */
- if (!using_proxy || show_407)
- break;
- if (HTAA_shouldRetryWithAuth(start_of_data, length, s, YES)) {
- HTTP_NETCLOSE(s, handle);
- if (dump_output_immediately && !proxyauth_info[0]) {
- fprintf(stderr,
- "HTTP: Proxy authorization required.\n");
- fprintf(stderr,
- " Use the -pauth=id:pw parameter.\n");
- status = HT_NO_DATA;
- goto clean_up;
- }
- CTRACE((tfp, "%s %d %s\n",
- "HTTP: close socket", s,
- "to retry with Proxy Authorization"));
- _HTProgress(HTTP_RETRY_WITH_PROXY);
- FREE(line_buffer);
- FREE(line_kept_clean);
- goto try_again;
- } else if (!(traversal || dump_output_immediately) &&
- HTConfirm(gettext("Show the 407 message body?"))) {
- if (!dump_output_immediately &&
- format_out == HTAtom_for("www/download")) {
- /*
- * Convert a download request to a presentation
- * request for interactive users. - FM
- */
- format_out = WWW_PRESENT;
- }
- break;
- } else {
- if (traversal || dump_output_immediately)
- HTAlert(FAILED_RETRY_WITH_PROXY);
- HTTP_NETCLOSE(s, handle);
- status = -1;
- goto clean_up;
- }
- case 408:
- /*
- * Request Timeout. Show the status message and restore
- * the current document. - FM
- */
- HTAlert(line_buffer);
- HTTP_NETCLOSE(s, handle);
- status = HT_NO_DATA;
- goto done;
- default:
- /*
- * 400 Bad Request.
- * 402 Payment Required.
- * 403 Forbidden.
- * 404 Not Found.
- * 405 Method Not Allowed.
- * 406 Not Acceptable.
- * 409 Conflict.
- * 410 Gone.
- * 411 Length Required.
- * 412 Precondition Failed.
- * 413 Request Entity Too Large.
- * 414 Request-URI Too Long.
- * 415 Unsupported Media Type.
- * 416 List Response (for content negotiation).
- * > 416 is unknown.
- * Show the status message, and display the returned text
- * if we are not doing a traversal. - FM
- */
- HTAlert(line_buffer);
- if (traversal) {
- HTTP_NETCLOSE(s, handle);
- status = -1;
- goto clean_up;
- }
- if (!dump_output_immediately &&
- format_out == HTAtom_for("www/download")) {
- /*
- * Convert a download request to a presentation request
- * for interactive users. - FM
- */
- format_out = WWW_PRESENT;
- }
- break;
- } /* case 4 switch */
- break;
- case 5:
- /*
- * "I think YOU goofed!" (server error)
- * 500 Internal Server Error
- * 501 Not Implemented
- * 502 Bad Gateway
- * 503 Service Unavailable
- * 504 Gateway Timeout
- * 505 HTTP Version Not Supported
- * > 505 is unknown.
- * Should always include a message, which we always should
- * display. - FM
- */
- HTAlert(line_buffer);
- if (traversal) {
- HTTP_NETCLOSE(s, handle);
- status = -1;
- goto clean_up;
- }
- if (!dump_output_immediately &&
- format_out == HTAtom_for("www/download")) {
- /*
- * Convert a download request to a presentation request for
- * interactive users. - FM
- */
- format_out = WWW_PRESENT;
- }
- break;
- default:
- /*
- * Bad or unknown server_status number. Take a chance and hope
- * there is something to display. - FM
- */
- HTAlert(gettext("Unknown status reply from server!"));
- HTAlert(line_buffer);
- if (traversal) {
- HTTP_NETCLOSE(s, handle);
- status = -1;
- goto clean_up;
- }
- if (!dump_output_immediately &&
- format_out == HTAtom_for("www/download")) {
- /*
- * Convert a download request to a presentation request for
- * interactive users. - FM
- */
- format_out = WWW_PRESENT;
- }
- break;
- } /* Switch on server_status/100 */
- } /* Full HTTP reply */
- } /* scope of fields */
- /*
- * The user may have pressed the 'z'ap key during the pause caused by one
- * of the HTAlerts above if the server reported an error, to skip loading
- * of the error response page. Checking here before setting up the stream
- * stack and feeding it data avoids doing unnecessary work, it also can
- * avoid unnecessarily pushing a loaded document out of the cache to make
- * room for the unwanted error page. - kw
- */
- if (HTCheckForInterrupt()) {
- HTTP_NETCLOSE(s, handle);
- if (doing_redirect) {
- /*
- * Impatient user. - FM
- */
- CTRACE((tfp, "HTTP: Interrupted followup read.\n"));
- _HTProgress(CONNECTION_INTERRUPTED);
- }
- status = HT_INTERRUPTED;
- goto clean_up;
- }
- /*
- * Set up the stream stack to handle the body of the message.
- */
- if (do_head || keep_mime_headers) {
- /*
- * It was a HEAD request, or we want the headers and source.
- */
- start_of_data = line_kept_clean;
- #ifdef SH_EX /* FIX BUG by kaz@maczuka.hitachi.ibaraki.jp */
- /* GIF file contains \0, so strlen does not return the data length */
- length = real_length_of_line;
- #else
- length = rawlength;
- #endif
- format_in = HTAtom_for("text/plain");
- } else if (doing_redirect) {
- format_in = HTAtom_for("message/x-http-redirection");
- StrAllocCopy(anAnchor->content_type, HTAtom_name(format_in));
- if (traversal) {
- format_out = WWW_DEBUG;
- if (!sink)
- sink = HTErrorStream();
- } else if (!dump_output_immediately &&
- format_out == HTAtom_for("www/download")) {
- /*
- * Convert a download request to a presentation request for
- * interactive users. - FM
- */
- format_out = WWW_PRESENT;
- }
- }
- target = HTStreamStack(format_in,
- format_out,
- sink, anAnchor);
- if (target == NULL) {
- char *buffer = NULL;
- HTTP_NETCLOSE(s, handle);
- HTSprintf0(&buffer, CANNOT_CONVERT_I_TO_O,
- HTAtom_name(format_in), HTAtom_name(format_out));
- _HTProgress(buffer);
- FREE(buffer);
- status = -1;
- goto clean_up;
- }
- /*
- * Recycle the first chunk of data, in all cases.
- */
- (*target->isa->put_block) (target, start_of_data, length);
- /*
- * Go pull the bulk of the data down.
- */
- rv = HTCopy(anAnchor, s, (void *) handle, target);
- /*
- * If we get here with doing_redirect set, it means that we were looking
- * for a Location header. We either have got it now in redirecting_url -
- * in that case the stream should not have loaded any data. Or we didn't
- * get it, in that case the stream may have presented the message body
- * normally. - kw
- */
- if (rv == -1) {
- /*
- * Intentional interrupt before data were received, not an error
- */
- /* (*target->isa->_abort)(target, NULL); *//* already done in HTCopy */
- if (doing_redirect && traversal)
- status = -1;
- else
- status = HT_INTERRUPTED;
- HTTP_NETCLOSE(s, handle);
- goto clean_up;
- }
- if (rv == -2) {
- /*
- * Aw hell, a REAL error, maybe cuz it's a dumb HTTP0 server
- */
- (*target->isa->_abort) (target, NULL);
- if (doing_redirect && redirecting_url) {
- /*
- * Got a location before the error occurred? Then consider it an
- * interrupt but proceed below as normal. - kw
- */
- /* do nothing here */
- } else {
- HTTP_NETCLOSE(s, handle);
- if (!doing_redirect && !already_retrying && !do_post) {
- CTRACE((tfp, "HTTP: Trying again with HTTP0 request.\n"));
- /*
- * May as well consider it an interrupt -- right?
- */
- FREE(line_buffer);
- FREE(line_kept_clean);
- extensions = NO;
- already_retrying = TRUE;
- _HTProgress(RETRYING_AS_HTTP0);
- goto try_again;
- } else {
- status = HT_NOT_LOADED;
- goto clean_up;
- }
- }
- }
- /*
- * Free if complete transmission (socket was closed before return). Close
- * socket if partial transmission (was freed on abort).
- */
- if (rv != HT_INTERRUPTED && rv != -2) {
- (*target->isa->_free) (target);
- } else {
- HTTP_NETCLOSE(s, handle);
- }
- if (doing_redirect) {
- if (redirecting_url) {
- /*
- * Set up for checking redirecting_url in LYGetFile.c for
- * restrictions before we seek the document at that Location. - FM
- */
- CTRACE((tfp, "HTTP: Picked up location '%s'\n",
- redirecting_url));
- if (rv == HT_INTERRUPTED) {
- /*
- * Intentional interrupt after data were received, not an error
- * (probably). We take it as a user request to abandon the
- * redirection chain.
- *
- * This could reasonably be changed (by just removing this
- * block), it would make sense if there are redirecting
- * resources that "hang" after sending the headers. - kw
- */
- FREE(redirecting_url);
- CTRACE((tfp, "HTTP: Interrupted followup read.\n"));
- status = HT_INTERRUPTED;
- goto clean_up;
- }
- HTProgress(line_buffer);
- if (server_status == 305) { /* Use Proxy */
- /*
- * Make sure the proxy field ends with a slash. - FM
- */
- if (redirecting_url[strlen(redirecting_url) - 1]
- != '/')
- StrAllocCat(redirecting_url, "/");
- /*
- * Append our URL. - FM
- */
- StrAllocCat(redirecting_url, anAnchor->address);
- CTRACE((tfp, "HTTP: Proxy URL is '%s'\n",
- redirecting_url));
- }
- if (!do_post ||
- server_status == 303 ||
- server_status == 302) {
- /*
- * We don't have POST content (nor support PUT or DELETE), or
- * the status is "See Other" or "General Redirection" and we
- * can convert to GET, so go back and check out the new URL. -
- * FM
- */
- status = HT_REDIRECTING;
- goto clean_up;
- }
- /*
- * Make sure the user wants to redirect the POST content, or treat
- * as GET - FM & DK
- */
- switch (HTConfirmPostRedirect(redirecting_url,
- server_status)) {
- /*
- * User failed to confirm. Abort the fetch.
- */
- case 0:
- doing_redirect = FALSE;
- FREE(redirecting_url);
- status = HT_NO_DATA;
- goto clean_up;
- /*
- * User wants to treat as GET with no content. Go back to
- * check out the URL.
- */
- case 303:
- break;
- /*
- * Set the flag to retain the POST content and go back to check
- * out the URL. - FM
- */
- default:
- redirect_post_content = TRUE;
- }
- /* Lou's old comment: - FM */
- /* OK, now we've got the redirection URL temporarily stored
- in external variable redirecting_url, exported from HTMIME.c,
- since there's no straightforward way to do this in the library
- currently. Do the right thing. */
- status = HT_REDIRECTING;
- } else {
- status = traversal ? -1 : HT_LOADED;
- }
- } else {
- /*
- * If any data were received, treat as a complete transmission
- */
- status = HT_LOADED;
- }
- /*
- * Clean up
- */
- clean_up:
- FREE(line_buffer);
- FREE(line_kept_clean);
- done:
- /*
- * Clear out on exit, just in case.
- */
- do_head = FALSE;
- do_post = FALSE;
- reloading = FALSE;
- #ifdef USE_SSL
- do_connect = FALSE;
- did_connect = FALSE;
- FREE(connect_host);
- if (handle) {
- SSL_free(handle);
- SSL_handle = handle = NULL;
- }
- #endif /* USE_SSL */
- return status;
- }
- /* Protocol descriptor
- */
- #ifdef GLOBALDEF_IS_MACRO
- #define _HTTP_C_GLOBALDEF_1_INIT { "http", HTLoadHTTP, 0}
- GLOBALDEF(HTProtocol, HTTP, _HTTP_C_GLOBALDEF_1_INIT);
- #define _HTTP_C_GLOBALDEF_2_INIT { "https", HTLoadHTTP, 0}
- GLOBALDEF(HTProtocol, HTTPS, _HTTP_C_GLOBALDEF_2_INIT);
- #else
- GLOBALDEF HTProtocol HTTP =
- {"http", HTLoadHTTP, 0};
- GLOBALDEF HTProtocol HTTPS =
- {"https", HTLoadHTTP, 0};
- #endif /* GLOBALDEF_IS_MACRO */
|