/* * Receive n bytes from a socket */ int recvdata(SOCKET sock, char *ptr, int nbytes) { int nleft, nread; nleft = nbytes; while (nleft > 0) { D_PRINTF( "In recvdata(%d, %d)\n", sock, nleft ); nread = NETREAD(sock, ptr, nleft); D_PRINTF( "NETREAD() returned %d\n", nread ); if (BADSOCKET(nread) || nread == 0) { /* return error value NETWRITE */ D_PRINTF( "Error in recvdata(): %s\n",neterrstr() ); return(nread); } D_PRINTF( "NETREAD() data: \"%.*s\"\n", nread, ptr); nleft -= nread; ptr += nread; } /* end while */ /* return >= 0 */ return(nbytes - nleft); } /* end recvdata */
/* Stream the data to an ouput file as binary */ PUBLIC int HTOutputBinary ARGS2( int, input, FILE *, output) { do { int status = NETREAD(input, input_buffer, INPUT_BUFFER_SIZE); if (status <= 0) { if (status == 0) return 0; #ifndef DISABLE_TRACE if (www2Trace) fprintf(stderr, "HTFormat: File read error %d\n", status); #endif return 2; /* Error */ } fwrite(input_buffer, sizeof(char), status, output); } while (YES); }
static int echo_client(char *hostname, const int fd) { /* * WRITE TEXT FROM FILE DESCRIPTOR INTO STDOUT */ char buf[BUFSIZ]; int cc; D_PRINTF( "echo_client running\n" ); while (getppid() != 1) { cc = NETREAD(fd, buf, sizeof(buf)); if (cc > 0) { write(STDOUT_FILENO, buf, cc); } } D_PRINTF( "Exiting echo_client\n" ); NETCLOSE(fd); }
PUBLIC int HTCopy ARGS3(int, file_number, HTStream*, sink, int, bytes_already_read) { HTStreamClass targetClass; char line[256]; char *msg; int bytes = bytes_already_read; extern int twirl_increment; int next_twirl = twirl_increment; int rv = 0; int left = -1, total_read = bytes_already_read, hdr_len = 0; /* if(loading_length != -1) left = loading_length;*/ HTClearActiveIcon(); /* Push the data down the stream ** */ targetClass = *(sink->isa); /* Copy pointers to procedures */ hdr_len = HTMIME_get_header_length(sink); /* Push binary from socket down sink */ for(;;) { int status, intr; if (bytes > next_twirl) { intr = HTCheckActiveIcon(1); next_twirl += twirl_increment; } else { intr = HTCheckActiveIcon(0); } if (intr) { #ifdef SWP_HACK loading_length=(-1); #endif HTProgress ("Data transfer interrupted."); noLength=0; HTMeter(100,NULL); noLength=1; (*targetClass.handle_interrupt)(sink); rv = -1; goto ready_to_leave; } if(loading_length == -1) { left = -1; status = NETREAD(file_number, input_buffer, INPUT_BUFFER_SIZE); } else { left = (loading_length+hdr_len)-total_read; if(left>0) status = NETREAD(file_number, input_buffer, (left>INPUT_BUFFER_SIZE? INPUT_BUFFER_SIZE:left)); else status=0; } if (status > 0) total_read += status; /* fprintf(stderr,"ll = %d status = %d left = %d hdr = %d tr = %d\n", loading_length,status,left,hdr_len,total_read); */ /* status = NETREAD(file_number, input_buffer, INPUT_BUFFER_SIZE); */ if (status <= 0) { if (status == 0) break; if (status == HT_INTERRUPTED) { #ifdef SWP_HACK loading_length=(-1); #endif HTProgress ("Data transfer interrupted."); noLength=0; HTMeter(100,NULL); noLength=1; (*targetClass.handle_interrupt)(sink); rv = -1; goto ready_to_leave; } if (errno == ENOTCONN || errno == ECONNRESET || errno == EPIPE) { /* Arrrrgh, HTTP 0/1 compability problem, maybe. */ rv = -2; goto ready_to_leave; } break; } #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTCopy: put_block on input_buffer '%s'\n", input_buffer); #endif (*targetClass.put_block)(sink, input_buffer, status); if (ftpKludge) { hdr_len=0; } else { hdr_len = HTMIME_get_header_length(sink); } /* left = loading_length - total_read;*/ bytes += status; /* moved msg stuff here as loading_length may change midstream -bjs*/ if (loading_length == -1){ msg = (loading_inlined_images ? "Read %d bytes of inlined image data." : "Read %d bytes of data."); sprintf (line, msg, bytes); /* HTMeter(0,NULL);*/ }else{ msg = (loading_inlined_images ? "Read %d of %d bytes of inlined image data." : "Read %d of %d bytes of data."); sprintf (line, msg, bytes, loading_length+hdr_len); HTMeter((bytes*100)/(loading_length+hdr_len),NULL); } HTProgress (line); if((loading_length != -1) && (total_read>=(loading_length+hdr_len))) { /* fprintf(stderr,"done\n");*/ break; } } /* next bufferload */ /* HTProgress (loading_inlined_images ? "Data transfer complete." : "Data transfer complete."); */ HTProgress("Data transfer complete."); noLength=0; HTMeter(100,NULL); noLength=1; /* fprintf(stderr,"HTFormat: KeepAlive Exit\n");*/ /* NETCLOSE (file_number); */ /* Success. */ rv = 0; ready_to_leave: /* Reset ourselves so we don't get confused. */ loading_length = -1; return rv; }
/* PUBLIC HTAA_getUnfoldedLine() * READ AN UNFOLDED HEADER LINE FROM SOCKET * ON ENTRY: * HTAA_setupReader must absolutely be called before * this function to set up internal buffer. * * ON EXIT: * returns a newly-allocated character string representing * the read line. The line is unfolded, i.e. * lines that begin with whitespace are appended * to current line. E.g. * * Field-Name: Blaa-Blaa * This-Is-A-Continuation-Line * Here-Is_Another * * is seen by the caller as: * * Field-Name: Blaa-Blaa This-Is-A-Continuation-Line Here-Is_Another * */ char *HTAA_getUnfoldedLine(void) { char *line = NULL; char *cur; int count; BOOL peek_for_folding = NO; if (in_soc < 0) { CTRACE((tfp, "%s %s\n", "HTAA_getUnfoldedLine: buffer not initialized", "with function HTAA_setupReader()")); return NULL; } for (;;) { /* Reading from socket */ if (start_pointer >= end_pointer) { /*Read the next block and continue */ #ifdef USE_SSL if (SSL_handle) count = SSL_read(SSL_handle, buffer, BUFFER_SIZE); else count = NETREAD(in_soc, buffer, BUFFER_SIZE); #else count = NETREAD(in_soc, buffer, BUFFER_SIZE); #endif /* USE_SSL */ if (count <= 0) { in_soc = -1; return line; } if (count > (int) buffer_length) count = (int) buffer_length; start_pointer = buffer; end_pointer = buffer + count; *end_pointer = '\0'; #ifdef NOT_ASCII cur = start_pointer; while (cur < end_pointer) { *cur = TOASCII(*cur); cur++; } #endif /*NOT_ASCII */ } cur = start_pointer; /* Unfolding */ if (peek_for_folding) { if (*cur != ' ' && *cur != '\t') return line; /* Ok, no continuation line */ else /* So this is a continuation line, continue */ peek_for_folding = NO; } /* Finding end-of-line */ while (cur < end_pointer && *cur != '\n') /* Find the end-of-line */ cur++; /* (or end-of-buffer). */ /* Terminating line */ if (cur < end_pointer) { /* So *cur==LF, terminate line */ *cur = '\0'; /* Overwrite LF */ if (*(cur - 1) == '\r') *(cur - 1) = '\0'; /* Overwrite CR */ peek_for_folding = YES; /* Check for a continuation line */ } /* Copying the result */ if (line) StrAllocCat(line, start_pointer); /* Append */ else StrAllocCopy(line, start_pointer); /* A new line */ start_pointer = cur + 1; /* Skip the read line */ } /* forever */ }
/* Push data from a socket down a stream ** ------------------------------------- ** ** This routine is responsible for creating and PRESENTING any ** graphic (or other) objects described by the file. ** ** The file number given is assumed to be a TELNET stream, i.e., containing ** CRLF at the end of lines which need to be stripped to LF for unix ** when the format is textual. ** ** State of socket and target stream on entry: ** socket (file_number) assumed open, ** target (sink) assumed valid. ** ** Return values: ** HT_INTERRUPTED Interruption or error after some data received. ** -2 Unexpected disconnect before any data received. ** -1 Interruption or error before any data received, or ** (UNIX) other read error before any data received, or ** download cancelled. ** HT_LOADED Normal close of socket (end of file indication ** received), or ** unexpected disconnect after some data received, or ** other read error after some data received, or ** (not UNIX) other read error before any data received. ** ** State of socket and target stream on return depends on return value: ** HT_INTERRUPTED socket still open, target aborted. ** -2 socket still open, target stream still valid. ** -1 socket still open, target aborted. ** otherwise socket closed, target stream still valid. */ PUBLIC int HTCopy ARGS4( HTParentAnchor *, anchor, int, file_number, void*, handle GCC_UNUSED, HTStream*, sink) { HTStreamClass targetClass; BOOL suppress_readprogress = NO; int bytes; int rv = 0; /* Push the data down the stream */ targetClass = *(sink->isa); /* Copy pointers to procedures */ /* Push binary from socket down sink ** ** This operation could be put into a main event loop */ HTReadProgress(bytes = 0, 0); for (;;) { int status; if (LYCancelDownload) { LYCancelDownload = FALSE; (*targetClass._abort)(sink, NULL); rv = -1; goto finished; } if (HTCheckForInterrupt()) { _HTProgress (TRANSFER_INTERRUPTED); (*targetClass._abort)(sink, NULL); if (bytes) rv = HT_INTERRUPTED; else rv = -1; goto finished; } #ifdef USE_SSL if (handle) status = SSL_read((SSL *)handle, input_buffer, INPUT_BUFFER_SIZE); else status = NETREAD(file_number, input_buffer, INPUT_BUFFER_SIZE); #else status = NETREAD(file_number, input_buffer, INPUT_BUFFER_SIZE); #endif /* USE_SSL */ if (status <= 0) { if (status == 0) { break; } else if (status == HT_INTERRUPTED) { _HTProgress (TRANSFER_INTERRUPTED); (*targetClass._abort)(sink, NULL); if (bytes) rv = HT_INTERRUPTED; else rv = -1; goto finished; } else if (SOCKET_ERRNO == ENOTCONN || #ifdef _WINDOWS /* 1997/11/10 (Mon) 16:57:18 */ SOCKET_ERRNO == ETIMEDOUT || #endif SOCKET_ERRNO == ECONNRESET || SOCKET_ERRNO == EPIPE) { /* * Arrrrgh, HTTP 0/1 compatibility problem, maybe. */ if (bytes <= 0) { /* * Don't have any data, so let the calling * function decide what to do about it. - FM */ rv = -2; goto finished; } else { #ifdef UNIX /* * Treat what we've received already as the complete * transmission, but not without giving the user * an alert. I don't know about all the different * TCP stacks for VMS etc., so this is currently * only for UNIX. - kw */ HTInetStatus("NETREAD"); HTAlert("Unexpected server disconnect."); CTRACE((tfp, "HTCopy: Unexpected server disconnect. Treating as completed.\n")); status = 0; break; #else /* !UNIX */ /* * Treat what we've gotten already * as the complete transmission. - FM */ CTRACE((tfp, "HTCopy: Unexpected server disconnect. Treating as completed.\n")); status = 0; break; #endif /* UNIX */ } #ifdef UNIX } else { /* status < 0 and other errno */ /* * Treat what we've received already as the complete * transmission, but not without giving the user * an alert. I don't know about all the different * TCP stacks for VMS etc., so this is currently * only for UNIX. - kw */ HTInetStatus("NETREAD"); HTAlert("Unexpected read error."); if (bytes) { (void)NETCLOSE(file_number); rv = HT_LOADED; } else { (*targetClass._abort)(sink, NULL); rv = -1; } goto finished; #endif } break; } /* * Suppress ReadProgress messages when collecting a redirection * message, at least initially (unless/until anchor->content_type * gets changed, probably by the MIME message parser). That way * messages put up by the HTTP module or elsewhere can linger in * the statusline for a while. - kw */ suppress_readprogress = (anchor && anchor->content_type && !strcmp(anchor->content_type, "message/x-http-redirection")); #ifdef NOT_ASCII { char * p; for (p = input_buffer; p < input_buffer+status; p++) { *p = FROMASCII(*p); } } #endif /* NOT_ASCII */ (*targetClass.put_block)(sink, input_buffer, status); bytes += status; if (!suppress_readprogress) HTReadProgress(bytes, anchor ? anchor->content_length : 0); HTDisplayPartial(); } /* next bufferload */ _HTProgress(TRANSFER_COMPLETE); (void)NETCLOSE(file_number); rv = HT_LOADED; finished: HTFinishDisplayPartial(); return(rv); }
SOCKET connecttomaster(char *str) { char *tempch; SOCKET sock; char msg[100]; char ConnectStr[100]; /* Fix to handle multiple threads */ int tries; strcpy(ConnectStr, str); /* * BREAK UP THE connectstr INTO A HOSTNAME/HOST-IP AND A PORT NUMBER. */ if((tempch = strpbrk(ConnectStr,":")) == 0) { /* * INCORRECT FORMAT OF ConnectStr. CORRECT FORMAT IS * HOSTNAME:PORT OR HOST-IP:PORT */ D_PRINTF( "Incorrect format %s: use hostname:port or ip_addr:port\n", ConnectStr ); return(returnerr("Incorrect format %s: use host:port or ip_addr:port\n", ConnectStr)); } /* * ZERO OUT THE COLON SO WE HAVE TWO STRINGS, THE HOSTNAME AND THE PORT */ *tempch = '\0'; tempch++; /* loop here to connect to webmaster - TCP/IP allows no more than 5 * connection requests outstanding at once and thus the webmaster may * reject a connection if there are a lot of client processes */ #define MAXTRIES 30 #define TRYDELAY_SEC 1 for (tries = 0; tries < MAXTRIES; tries++) { sock = connectsock(ConnectStr,(NETPORT)atoi(tempch),"tcp"); if (!BADSOCKET(sock)) break; sleep(TRYDELAY_SEC); } if (BADSOCKET(sock)) { /* ERROR CONNECTING TO MASTER PROCESS */ return(returnerr("Could not connect to master process\n")); } /* * SIGNAL THE MASTER THAT WE ARE READY TO PROCEED. WHEN ALL * CHILD PROCESSES HAVE CONNECTED AND SENT THIS SIGNAL, * THE MASTER WILL ISSUE US A GO SIGNAL. */ if(NETWRITE(sock,READYSTR,READYSTRLEN) != READYSTRLEN) { return(returnerr("Error sending READY message to master")); } memset(msg,0,GOSTRLEN+1); if(NETREAD(sock,msg,GOSTRLEN) != GOSTRLEN) { D_PRINTF( "Error receiving GO message from master: %s\n", neterrstr() ); return(returnerr("Error receiving GO message from master\n")); } if(strncmp(GOSTR,msg,GOSTRLEN)) { /* * WE RECEIVED A MESSAGE OTHER THAN GO. PRINT IT OUT AND RETURN ERROR */ return(returnerr("Received non-GO message %s\n",msg)); } return(sock); } /* END connecttomaster() */
PUBLIC int HTLoadHTTP ARGS4 ( char *, arg, HTParentAnchor *, anAnchor, HTFormat, format_out, HTStream*, sink) { int s; /* Socket number for returned data */ char *command; /* 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 */ char *line_buffer; char *line_kept_clean; BOOL extensions; /* Assume good HTTP server */ int compressed; char line[2048]; /* bumped up to cover Kerb huge headers */ int length, doing_redirect, rv; int already_retrying = 0; int return_nothing; int i; int keepingalive = 0; char *p; /*SWP*/ int statusError=0; if (!arg) { status = -3; HTProgress ("Bad request."); goto done; } if (!*arg) { status = -2; HTProgress ("Bad request."); goto done; } 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; bytes_already_read = 0; length = 0; doing_redirect = 0; compressed = 0; target = NULL; line_buffer = NULL; line_kept_clean = NULL; return_nothing = 0; /* okay... addr looks like http://hagbard.ncsa.uiuc.edu/blah/etc.html lets crop it at the 3rd '/' */ for(p = arg,i=0;*p && i!=3;p++) if(*p=='/') i++; if(i==3) i = p-arg; /* i = length not counting last '/' */ else i = 0; if((lsocket != -1) && i && addr && !strncmp(addr,arg,i)){ /* keepalive is active and addresses match -- try the old socket */ s = lsocket; keepingalive = 1; /* flag in case of network error due to server timeout*/ lsocket = -1; /* prevent looping on failure */ #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: Keep-Alive reusing '%s'\n",addr); #endif /* if (addr && *addr) { sprintf(tmpbuf,"Reusing socket from %s.",addr); HTProgress(tmpbuf); } */ } else { if(addr) free(addr); /* save the address for next time around */ addr = malloc(i+1); strncpy(addr,arg,i); *(addr+i)=0; keepingalive = 0; /* just normal opening of the socket */ if(lsocket != -1) NETCLOSE(lsocket); /* no socket leaks here */ lsocket = -1; /*dont assign until we know the server says okay */ #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: Keep-Alive saving '%s'\n",addr); #endif /* if (addr && *addr) { sprintf(tmpbuf,"Saving %s for possible socket reuse.",addr); HTProgress(tmpbuf); } */ } if (!keepingalive) { status = HTDoConnect (arg, "HTTP", TCP_PORT, &s); if (status == HT_INTERRUPTED){ /* Interrupt cleanly. */ #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: Interrupted on connect; recovering cleanly.\n"); #endif HTProgress ("Connection interrupted."); /* status already == HT_INTERRUPTED */ goto done; } if (status < 0) { #ifndef DISABLE_TRACE if (www2Trace) fprintf(stderr, "HTTP: Unable to connect to remote host for `%s' (errno = %d).\n", arg, errno); #endif HTProgress ("Unable to connect to remote host."); status = HT_NO_DATA; goto done; } } /* Ask that node for the document, ** omitting the host name & anchor */ { char * p1 = HTParse(arg, "", PARSE_PATH|PARSE_PUNCTUATION); command = malloc(5 + strlen(p1)+ 2 + 31); if (do_post && !do_put) strcpy(command, "POST "); else if (do_post && do_put) strcpy(command, "PUT "); else if (do_head) strcpy(command, "HEAD "); else if (do_meta) strcpy(command, "META "); else strcpy(command, "GET "); /* * For a gateway, the beginning '/' on the request must * be stripped before appending to the gateway address. */ if ((using_gateway)||(using_proxy)) { strcat(command, p1+1); } else strcat(command, p1); free(p1); } if (extensions) { strcat(command, " "); strcat(command, HTTP_VERSION); } strcat(command, crlf); /* CR LF, as in rfc 977 */ if (extensions) { #ifdef SAM_NOT_YET /* SAM This produces an absolutely huge Accept: line. While there is probably a better way to turn this off, just compiling it out works for now. */ int n, i; if (!HTPresentations) HTFormatInit(); n = HTList_count(HTPresentations); begin_ptr=command+strlen(command); env_length=0; sprintf(line, "Accept:"); env_length+=strlen(line); StrAllocCat(command, line); /* KCMS Accept Header - swp */ if (KCMS_Return_Format==JPEG) { sprintf(line," image/x-pcd-jpeg,"); StrAllocCat(command, line); env_length+=strlen(line); } else if (KCMS_Return_Format==JYCC) { sprintf(line," image/x-pcd-jycc,"); StrAllocCat(command, line); env_length+=strlen(line); } else if (KCMS_Return_Format==GIF) { sprintf(line," image/x-pcd-gif,"); StrAllocCat(command, line); env_length+=strlen(line); } for(i=0; i<n; i++) { HTPresentation * pres = HTList_objectAt(HTPresentations, i); if (pres->rep_out == WWW_PRESENT) { sprintf(line, " %s,",HTAtom_name(pres->rep)); env_length+=strlen(line); StrAllocCat(command, line); if (env_length>200) { if ((tmp_ptr=strrchr(command,','))!=NULL) { *tmp_ptr='\0'; } sprintf(line, "%c%c",CR,LF); StrAllocCat(command, line); begin_ptr=command+strlen(command); sprintf(line, "Accept:"); env_length=strlen(line); StrAllocCat(command, line); } } } /* This gets rid of the last comma. */ if ((tmp_ptr=strrchr(command,','))!=NULL) { *tmp_ptr='\0'; sprintf(line, "%c%c",CR,LF); StrAllocCat(command, line); } else { /* No accept stuff...get rid of "Accept:" */ begin_ptr='\0'; } #endif /* if reloading, send no-cache pragma to proxy servers. --swp */ /* original patch from Ari L. <*****@*****.**> */ if (reloading) { sprintf(line, "Pragma: no-cache%c%c", CR, LF); StrAllocCat(command, line); } /*This is just used for "not" sending this header on a proxy request*/ if (useKeepAlive) { sprintf(line, "Connection: keep-alive%c%c", CR, LF); StrAllocCat(command, line); } if (sendAgent) { sprintf(line, "User-Agent: %s%c%c",agent[selectedAgent],CR,LF); /* sprintf(line, "User-Agent: %s/%s libwww/%s%c%c", HTAppName ? HTAppName : "unknown", HTAppVersion ? HTAppVersion : "0.0", HTLibraryVersion, CR, LF); */ StrAllocCat(command, line); } if (sendReferer) { /* HTTP Referer field, specifies back-link URL - amb */ if (HTReferer) { sprintf(line, "Referer: %s%c%c", HTReferer, CR, LF); StrAllocCat(command, line); HTReferer = NULL; } } { char *tmp,*startPtr,*endPtr; /* addr is always in URL form */ if (addr && !using_proxy && !using_gateway) { tmp=strdup(addr); startPtr=strchr(tmp,'/'); if (startPtr) { startPtr+=2; /*now at begining of hostname*/ if (*startPtr) { endPtr=strchr(startPtr,':'); if (!endPtr) { endPtr=strchr(startPtr,'/'); if (endPtr && *endPtr) { *endPtr='\0'; } } else { *endPtr='\0'; } sprintf(line, "Host: %s%c%c", startPtr, CR, LF); StrAllocCat(command, line); free(tmp); tmp=startPtr=endPtr=NULL; } } } else if (using_proxy || using_gateway) { sprintf(line, "Host: %s%c%c", proxy_host_fix, CR, LF); StrAllocCat(command, line); } } /* SWP -- 7/10/95 */ /* HTTP Extension headers */ /* Domain Restriction */ sprintf(line,"Extension: Notify-Domain-Restriction%c%c",CR,LF); StrAllocCat(command,line); /* BJS -- 12/05/95 -- allow arbitrary headers sent from browser */ if(extra_headers){ int h; for(h=0;extra_headers[h];h++){ sprintf(line,"%s%c%c",extra_headers[h],CR,LF); StrAllocCat(command,line); } } { char *docname; char *hostname; char *colon; int portnumber; char *auth; 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 portnumber = 80; if (NULL!=(auth=HTAA_composeAuth(hostname, portnumber, docname))) { sprintf(line, "%s%c%c", auth, CR, LF); StrAllocCat(command, line); } #ifndef DISABLE_TRACE if (www2Trace) { if (auth) fprintf(stderr, "HTTP: Sending authorization: %s\n", auth); else fprintf(stderr, "HTTP: Not sending authorization (yet)\n"); } #endif FREE(hostname); FREE(docname); } } if (do_post && !do_put) { #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: Doing post, content-type '%s'\n", post_content_type); #endif sprintf (line, "Content-type: %s%c%c", post_content_type ? post_content_type : "lose", CR, LF); StrAllocCat(command, line); { int content_length; if (!post_data) content_length = 4; /* 4 == "lose" :-) */ else content_length = strlen (post_data); sprintf (line, "Content-length: %d%c%c", content_length, CR, LF); StrAllocCat(command, line); } StrAllocCat(command, crlf); /* Blank line means "end" */ if (post_data) StrAllocCat(command, post_data); else StrAllocCat(command, "lose"); } else if (do_post && do_put) { sprintf (line, "Content-length: %d%c%c", put_file_size, CR, LF); StrAllocCat(command, line); StrAllocCat(command, crlf); /* Blank line means "end" */ } else { StrAllocCat(command, crlf); /* Blank line means "end" */ } #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "Writing:\n%s----------------------------------\n", command); #endif /* HTProgress ("Sending HTTP request."); */ status = NETWRITE(s, command, (int)strlen(command)); if (do_post && do_put) { char buf[BUFSIZ]; int upcnt=0,n; while (status>0) { n=fread(buf,1,BUFSIZ-1,put_fp); upcnt+= status = NETWRITE(s, buf, n); #ifndef DISABLE_TRACE if (www2Trace) { fprintf(stderr,"[%d](%d) %s",status,n,buf); } #endif if (feof(put_fp)) { break; } } if (status<0 || !feof(put_fp) || upcnt!=put_file_size) { char tmpbuf[BUFSIZ]; sprintf(tmpbuf,"Status: %d -- EOF: %d -- UpCnt/FileSize: %d/%d\n\nThe server you connected to either does not support\nthe PUT method, or an error occurred.\n\nYour upload was corrupted! Please try again!",status,(feof(put_fp)?1:0),upcnt,put_file_size); application_error(tmpbuf,"Upload Error!"); } } /* Twirl on each request to make things look nicer -- SWP */ HTCheckActiveIcon(1); #ifndef DISABLE_TRACE if (httpTrace) { fprintf(stderr,"%s",command); } #endif free (command); if (status <= 0) { if (status == 0) { #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: Got status 0 in initial write\n"); #endif /* Do nothing. */ } else if ((errno == ENOTCONN || errno == ECONNRESET || errno == EPIPE) && !already_retrying && /* Don't retry if we're posting. */ !do_post) { /* Arrrrgh, HTTP 0/1 compability problem, maybe. */ #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: BONZO ON WRITE Trying again with HTTP0 request.\n"); #endif /* HTProgress ("Retrying as HTTP0 request."); */ NETCLOSE(s); // SAM extensions = NO; already_retrying = 1; goto try_again; } else { if(keepingalive){ #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: Timeout on Keep-Alive. Retrying.\n"); #endif HTProgress("Server Timeout: Reconnecting"); goto try_again; } #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: Hit unexpected network WRITE error; aborting connection.\n"); #endif NETCLOSE (s); status = -1; HTProgress ("Unexpected network write error; connection aborted."); goto done; } } #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: WRITE delivered OK\n"); #endif HTProgress ("Done sending HTTP request; 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 = (char *) malloc(buffer_length * sizeof(char)); 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)); } #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: Trying to read %d\n", buffer_length - length - 1); #endif status = NETREAD(s, line_buffer + length, buffer_length - length - 1); #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: Read %d\n", status); #endif if (status <= 0) { /* Retry if we get nothing back too; bomb out if we get nothing twice. */ if (status == HT_INTERRUPTED) { #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: Interrupted initial read.\n"); #endif HTProgress ("Connection interrupted."); status = HT_INTERRUPTED; NETCLOSE (s); goto clean_up; } else if (status < 0 && (errno == ENOTCONN || errno == ECONNRESET || errno == EPIPE) && !already_retrying && !do_post) { /* Arrrrgh, HTTP 0/1 compability problem, maybe. */ #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: BONZO Trying again with HTTP0 request.\n"); #endif NETCLOSE(s); if (line_buffer) free(line_buffer); if (line_kept_clean) free(line_kept_clean); extensions = NO; already_retrying = 1; HTProgress ("Retrying as HTTP0 request."); goto try_again; } else { if(keepingalive){ #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: Timeout on Keep-Alive. Retrying.\n"); #endif HTProgress("Server Timeout: Reconnecting"); goto try_again; } #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: Hit unexpected network read error; aborting connection; status %d.\n", status); #endif HTProgress ("Unexpected network read error; connection aborted."); NETCLOSE (s); status = -1; goto clean_up; } } bytes_already_read += status; { char line[256]; sprintf (line, "Read %d bytes of data.", bytes_already_read); HTProgress (line); } if (status == 0) { end_of_file = YES; break; } line_buffer[length+status] = 0; if (line_buffer) { if (line_kept_clean) free (line_kept_clean); line_kept_clean = (char *)malloc (buffer_length * sizeof (char)); /* bcopy (line_buffer, line_kept_clean, buffer_length); */ memcpy (line_kept_clean, line_buffer, buffer_length); } 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 */ /* We now have a terminated unfolded line. Parse it. ** ------------------------------------------------- */ #ifndef DISABLE_TRACE if (www2Trace) fprintf(stderr, "HTTP: Rx: %s\n", line_buffer); #endif { int fields; char server_version[VERSION_LENGTH+1]; int server_status; /*SWP*/ statusError=0; server_version[0] = 0; fields = sscanf(line_buffer, "%20s %d", server_version, &server_status); #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: Scanned %d fields from line_buffer\n", fields); #endif #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: line_buffer '%s'\n", line_buffer); #endif /* Rule out HTTP/1.0 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] != '.') { /* HTTP0 reply */ HTAtom * encoding; #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "--- Talking HTTP0.\n"); #endif format_in = HTFileFormat(arg, &encoding, WWW_HTML, &compressed); start_of_data = line_kept_clean; } else { /* Decode full HTTP response */ format_in = HTAtom_for("www/mime"); /* 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 in this situation. */ start_of_data = eol ? eol + 1 : ""; length = eol ? length - (start_of_data - line_buffer) : 0; #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "--- Talking HTTP1.\n"); #endif switch (server_status / 100) { case 3: /* Various forms of redirection */ /* We now support this in the parser, at least. */ doing_redirect = 1; break; case 4: /* "I think I goofed" */ switch (server_status) { case 403: statusError=1; /* 403 is "forbidden"; display returned text. */ break; case 401: /* length -= start_of_data - text_buffer; */ if (HTAA_shouldRetryWithAuth(start_of_data, length, s)) { (void)NETCLOSE(s); lsocket = -1; if (line_buffer) free(line_buffer); if (line_kept_clean) free(line_kept_clean); #ifndef DISABLE_TRACE if (www2Trace) fprintf(stderr, "%s %d %s\n", "HTTP: close socket", s, "to retry with Access Authorization"); #endif HTProgress ("Retrying with access authorization information."); goto try_again; break; } else { statusError=1; /* Fall through. */ } default: statusError=1; break; } /* case 4 switch */ break; case 5: /* I think you goofed */ statusError=1; break; case 2: /* Good: Got MIME object */ switch (server_status) { case 204: return_nothing = 1; format_in = HTAtom_for("text/html"); break; case 200: if (do_head) { if (!start_of_data || !*start_of_data) { headData=NULL; } else { char *ptr; headData=strdup(start_of_data); ptr=strchr(headData,'\n'); *ptr='\0'; } } break; default: break; } break; default: /* bad number */ statusError=1; HTAlert("Unknown status reply from server!"); break; } /* Switch on server_status/100 */ } /* Full HTTP reply */ } /* scope of fields */ /* Set up the stream stack to handle the body of the message */ target = HTStreamStack(format_in, format_out, compressed, sink, anAnchor); if (!target) { char buffer[1024]; /* @@@@@@@@ */ sprintf(buffer, "Sorry, no known way of converting %s to %s.", HTAtom_name(format_in), HTAtom_name(format_out)); HTProgress (buffer); status = -1; NETCLOSE (s); lsocket = -1; goto clean_up; } if (!return_nothing) { #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: Doing put_block, '%s'\n", start_of_data); #endif /* BJS: parse start_of_data...*/ for(p=start_of_data;*p;p++){ /* if(*p=='C' && !strncmp("Content-length: ",p,16)){ i = 0; p+=16; while(*p && isdigit(*p)){ i = i*10 + *p-'0'; p++; } p--; #ifndef DISABLE_TRACE if(www2Trace) fprintf(stderr, "HTTP: Content Length is %d\n",i); #endif } */ if(*p=='K' && !strncmp("Keep-Alive:",p,11)){ #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: Server Agrees to Keep-Alive\n"); #endif lsocket = s; p+=10; } } #ifndef DISABLE_TRACE if (www2Trace && lsocket == -1) fprintf (stderr, "HTTP: Server does not agree to Keep-Alive\n"); #endif /* 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. */ /* if we dont use length, header length is wrong due to the discarded first line - bjs*/ rv = HTCopy(s, target, length /*bytes_already_read*/); if (rv == -1) { (*target->isa->handle_interrupt) (target); status = HT_INTERRUPTED; NETCLOSE (s); lsocket = -1; goto clean_up; } if (rv == -2 && !already_retrying && !do_post) { /* Aw hell. */ #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "HTTP: Trying again with HTTP0 request.\n"); #endif /* May as well consider it an interrupt -- right? */ (*target->isa->handle_interrupt) (target); NETCLOSE(s); if (line_buffer) free(line_buffer); if (line_kept_clean) free(line_kept_clean); extensions = NO; already_retrying = 1; HTProgress ("Retrying as HTTP0 request."); goto try_again; } } else { /* return_nothing is high. */ (*target->isa->put_string) (target, "<mosaic-access-override>\n"); HTProgress ("And silence filled the night."); } (*target->isa->end_document)(target); /* Close socket before doing free. */ if(lsocket == -1){ NETCLOSE(s); #ifndef DISABLE_TRACE if(www2Trace) fprintf(stderr,"HTTP: Closing connection\n"); #endif } else { HTProgress("Leaving Server Connection Open"); #ifndef DISABLE_TRACE if(www2Trace) fprintf(stderr,"HTTP: Keeping connection alive\n"); #endif } /* else { NETCLOSE(s); #ifndef DISABLE_TRACE if(www2Trace) fprintf(stderr,"HTTP: Closing connection\n"); #endif } */ (*target->isa->free)(target); if (doing_redirect) { /* 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 = HT_LOADED; } /* Clean up */ clean_up: if (line_buffer) free(line_buffer); if (line_kept_clean) free(line_kept_clean); done: /* Clear out on exit, just in case. */ do_post = 0; if (statusError) { securityType=HTAA_NONE; #ifndef DISABLE_TRACE if (www2Trace) fprintf(stderr,"Resetting security type to NONE.\n"); #endif } return status; }
/* echo stdout/stderr from clients */ void echo_client(void *stream) { SOCKET *sockarr; FILE *outfile; int which_stream = (int) stream; char buf[BUFSIZ]; int i, len, rv; fd_set readfds; /* This code which handles the timeout may need to be ifdef'ed for WIN32 */ struct timeval timeout; timeout.tv_sec = (long)5; timeout.tv_usec = (long)0; if (which_stream) { sockarr = sockIO; outfile = stdout; } else { sockarr = sockErr; outfile = stderr; } D_PRINTF( "echo_client running\n" ); signal( SIGINT, SIG_DFL); /* restore default behavior for SIGINT */ while (1) { FD_ZERO(&readfds); for (i = 0; i < num_rexecs; i++) if (sockarr[i] != BADSOCKET_VALUE) FD_SET(sockarr[i], &readfds); rv = select(num_rexecs, &readfds, 0, 0, &timeout); if ( rv == 0) continue; if (rv < 0 && WSAGetLastError() == WSANOTINITIALISED) return; if (rv < 0) errexit("Error in echo_client(): select() returns %d: %s\n", rv, neterrstr()); /* loop over the sockets that are ready with data */ for (i = 0; i < num_rexecs; i++) { if (sockarr[i] != BADSOCKET_VALUE && FD_ISSET(sockarr[i], &readfds)) { len = NETREAD(sockarr[i], buf, sizeof(buf)); if (len <= 0) { /* mark connection closed */ sockarr[i] = BADSOCKET_VALUE; if (len < 0 && WSAGetLastError() == WSANOTINITIALISED) return; if (len < 0) fprintf(stderr, "Error in echo_client() after NETREAD(): %s\n", neterrstr()); continue; } /* copy to stdout or stderr */ fwrite(buf, sizeof(char), len, outfile); } } } D_PRINTF( "Exiting echo_client\n" ); }
/* Handle one message ** ------------------ ** ** On entry, ** soc A file descriptor for input and output. ** On exit, ** returns >0 Channel is still open. ** 0 End of file was found, please close file ** <0 Error found, please close file. */ PUBLIC int HTHandle ARGS1(int, soc) { #define COMMAND_SIZE 2048 /* @@@@ WAIS queries can be big! */ #define MAX_LINES 10000 /* Maximum number of lines returned */ char command[COMMAND_SIZE+1]; char *keywords; /* pointer to keywords in address */ int status; char * arg; /* Pointer to the argument string */ time_t theTime; /* A long integer giving the datetime in secs */ for (;;) { int allocated = COMMAND_SIZE; command = (char *)malloc(allocated+1); if (!command) { fprintf(stderr, "Daemon: insufficient memrory!\n"); exit(-5); } for(;;) { if (allocated - bytes_read < MINIMUM_READ) { allocated = allocated + ALLOCATION_UNIT; command = (char *)realloc(command, allocated+1); if (!command) { fprintf(stderr, "Daemon: No memory to reallocate command!!\n"); exit(-6); } } status = NETREAD(soc, command + bytes_read, allocated - bytes_read); if (TRACE) fprintf(stderr, "NETREAD returned %d, errono=%d\n", status, errno); if (status<=0) { free(command); return status; /* Return and close file */ } { char * p= command + bytes_read; for(p=command + bytes_read; p < command + bytes_read + status; p++) { if (!*p) { free(command); return -1; /* No LF */ } if (*p==10) { break; } } bytes_read = bytes_read + status; if (p=command+bytes_read)/* No terminators yet */ continue; } break;/* Terminator found */ } command[bytes_read]=0; /* terminate the string */ #ifdef NOT_ASCII { char * p; for (p=command; *p; p++) { *p = FROMASCII(*p); if (*p == '\n') *p = ' '; } } #endif /* Log the call: */ if (logfile) { time(&theTime); fprintf(logfile, "%24.24s %s %s", ctime(&theTime), HTClientHost, command); fflush(logfile); /* Actually update it on disk */ if (TRACE) printf("Log: %24.24s %s %s", ctime(&theTime), HTClientHost, command); } arg=strchr(command,' '); if (arg) { int stat; *arg++ = 0; /* Terminate command */ arg = strip(arg); /* Strip space */ if (0==strcmp("GET", command)) { /* Get a file */ keywords=strchr(arg, '?'); if (keywords) *keywords++=0; /* Split into two */ stat = HTRetrieve(arg, keywords, soc); free(command); return stat; } } /* Space found supplied */ HTWriteASCII(soc, "<h1>HTTP Server v1.02</h1><h2>Test response</h2>\n"); HTWriteASCII(soc, "\n\nThe (unrecognised) command used was `"); HTWriteASCII(soc, command); HTWriteASCII(soc, "'.\n\n"); if (TRACE) printf("Return test string written back'\n"); free(command); return 0; /* End of file - please close socket */ } /* for */ } /* handle */