📄 httpdav.c
字号:
}}/* Deal with the body size */int http_req_bodysize( http_req_t *req ) { struct stat bodyst; /* Do extra stuff if we have a body */ switch( req->body ) { case http_body_file: /* Get file length */ if( fstat( fileno(req->body_file), &bodyst ) < 0 ) { /* Stat failed */ DEBUG( DEBUG_HTTP, "Stat failed: %s\n", strerror( errno ) ); return PROTO_ERROR; } req->body_size = bodyst.st_size; break; case http_body_buffer: req->body_size = strlen( req->body_buffer ); break; default: /* No body, so no size. */ return PROTO_OK; } if( req->body != http_body_none ) { char tmp[BUFSIZ]; /* Add the body length header */ snprintf( tmp, BUFSIZ, "Content-Length: %d" EOL, req->body_size ); strcat( req->headers, tmp ); } return PROTO_OK;}void http_strcat_hostname( struct proto_host_t *host, char *str ) { strcat( str, host->hostname ); /* Only add the port if it isn't 80 */ if( host->port != HTTP_PORT ) { static char buffer[128]; snprintf( buffer, 128, ":%d", host->port ); strcat( str, buffer ); }}/* Lob the User-Agent, connection and host headers in to the request * headers */void http_req_fixedheaders( http_req_t *req ) { strcat( req->headers, "User-Agent: " ); strcat( req->headers, http_useragent ); strcat( req->headers, EOL "Connection: Keep-Alive" EOL "Host: " ); http_strcat_hostname( &http_server_host, req->headers ); strcat( req->headers, EOL );}/* Decodes a URI */char *uri_decode( const char *uri ) { const char *pnt; char *ret, *retpos, buf[5] = { "0x00\0" }; retpos = ret = malloc( strlen( uri ) + 1 ); for( pnt = uri; *pnt != '\0'; pnt++ ) { if( *pnt == '%' ) { if( !isxdigit((unsigned char) pnt[1]) || !isxdigit((unsigned char) pnt[2]) ) { /* Invalid URI */ return NULL; } buf[2] = *++pnt; buf[3] = *++pnt; /* bit faster than memcpy */ *retpos++ = strtol( buf, NULL, 16 ); } else { *retpos++ = *pnt; } } *retpos = '\0'; return ret;}/* RFC2396 spake: * "Data must be escaped if it does not have a representation * using an unreserved character". * ...where... * unreserved = alphanum | mark * mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" * * We need also to skip reserved characters * reserved = ";" | "/" | "?" | ":" | "@" | "&" | * "=" | "+" | "$" | "," *//* Lookup table: * 1 marks an RESERVED character. 2 marks a UNRESERVED character. * 0 marks everything else. */#define RE 1#define UN 2 const short uri_chars[128] = {/* 0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 16 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 32 */ 0, UN, 0, 0, RE, 0, RE, UN, UN, UN, UN, RE, RE, UN, UN, RE,/* 48 */ UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, RE, RE, 0, RE, 0, RE,/* 64 */ RE, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN,/* 80 */ UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, 0, 0, 0, 0, UN,/* 96 */ 0, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN,/* 112 */ UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, UN, 0, 0, 0, UN, 0 };#undef RE#undef UN/* Encodes the abspath segment of a URI. * TODO: Make this parse a complete URI */char *uri_abspath_encode( const char *abs_path ) { const char *pnt; char *ret, *retpos; /* Rather than mess about growing the buffer, allocate as much as * the URI could possibly need, i.e. every character gets %XX * escaped. Hence 3 times input size. */ retpos = ret = malloc( strlen( abs_path ) * 3 + 1 ); for( pnt = abs_path; *pnt != '\0'; pnt++ ) { /* Escape it: * - if it isn't 7-bit * - if it is a reserved character (but ignore '/') * - otherwise, if it is not an unreserved character * (note, there are many characters that are neither reserved * nor unreserved) */ if( *pnt<0 || (uri_chars[(int) *pnt] < 2 && *pnt!='/' )) { /* Escape it - %<hex><hex> */ sprintf( retpos, "%%%02x", (unsigned char) *pnt ); retpos += 3; } else { /* It's cool */ *retpos++ = *pnt; } } *retpos = '\0'; return ret;}#ifdef URITESTvoid fe_transfer_progress( size_t progress, size_t total ) {}int main( int argc, char *argv[] ) { char *tmp; if( argc!=2 ) { printf( "doh. usage:\nuritest a_uri_abspath_segment\n" "e.g. uritest \"/this/is/a silly<filename>/but/hey\"\n" ); exit(-1); } printf( "Input URI: %s\n", argv[1] ); tmp = uri_abspath_encode( argv[1] ); printf( "Encoded: %s\n", tmp ); printf( "Decoded: %s\n", uri_decode( tmp ) ); return 0;}#endif /* URITEST *//* Initializes the request with given method and URI. * URI must be abs_path - i.e., NO scheme+hostname. It will BREAK * otherwise. */void http_request_init( http_req_t *req, const char *method, const char *uri ) { /* Clear it out */ memset( req, 0, sizeof( http_req_t ) ); DEBUG( DEBUG_HTTP, "Request starts.\n" ); /* Add in the fixed headers */ http_req_fixedheaders( req ); /* Set the standard stuff */ req->method = method; req->uri = uri_abspath_encode( uri ); req->body_callback = NULL; req->body = http_body_none; }void http_request_end( http_req_t *req ) { if( req->uri != NULL ) { free( req->uri ); } DEBUG( DEBUG_HTTP, "Request ends.\n" );}/* Reads a block of the response into buffer, which is of size buflen. * Returns number of bytes read, 0 on end-of-response, or -1 on error. */int http_response_read( http_req_t *req, char *buffer, size_t buflen ) { int willread, readlen; if( req->resp_te==http_te_chunked ) { /* We are doing a chunked transfer-encoding. * It goes: `SIZE CRLF CHUNK CRLF SIZE CRLF CHUNK CRLF ...' * ended by a `CHUNK CRLF 0 CRLF', a 0-sized chunk. * The slight complication is that we have to cope with * partial reads of chunks. * For this reason, resp_chunk_left contains the number of * bytes left to read in the current chunk. */ if( req->resp_chunk_left == 0 ) { /* We are at the start of a new chunk. */ DEBUG( DEBUG_HTTP, "New chunk.\n" ); if( read_line( http_sock, buffer, buflen ) < 0 ) { DEBUG( DEBUG_HTTP, "Could not read chunk size.\n" ); return -1; } DEBUG( DEBUG_HTTP, "[Chunk Size] < %s", buffer ); if( sscanf( buffer, "%x", &req->resp_chunk_left ) != 1 ) { DEBUG( DEBUG_HTTP, "Couldn't read chunk size.\n" ); return -1; } DEBUG( DEBUG_HTTP, "Got chunk size: %d\n", req->resp_chunk_left ); if( req->resp_chunk_left == 0 ) { /* Zero-size chunk */ DEBUG( DEBUG_HTTP, "Zero-size chunk.\n" ); return 0; } } willread = min( buflen - 1, req->resp_chunk_left ); } else if( req->resp_length > 0 ) { /* Have we finished reading the body? */ if( req->resp_left == 0 ) return 0; willread = min( buflen - 1, req->resp_left ); } else { /* Read until socket-close */ willread = buflen - 1; } DEBUG( DEBUG_HTTP, "Reading %d bytes of response body.\n", willread ); readlen = sock_read( http_sock, buffer, willread ); DEBUG( DEBUG_HTTP, "Got %d bytes.\n", readlen ); if( readlen < 0 ) { /* It broke */ DEBUG( DEBUG_HTTP, "Could not read block.\n" ); return -1; } else if( (readlen == 0) && ( (req->resp_length > 0) || (req->resp_te==http_te_chunked) )) { /* Premature close before read all of body, or during chunk read. */ DEBUG( DEBUG_HTTP, "Socket closed before end of body.\n" ); return -1; } buffer[readlen] = '\0'; DEBUG( DEBUG_HTTP, "Read block:\n%s\n", buffer ); if( req->resp_te==http_te_chunked ) { req->resp_chunk_left -= readlen; if( req->resp_chunk_left == 0 ) { char crlfbuf[2]; /* If we've read a whole chunk, read a CRLF */ if( read_data( http_sock, crlfbuf, 2 ) == 0 ) { DEBUG( DEBUG_HTTP, "Read CRLF bytes.\n" ); if( strncmp( crlfbuf, EOL, 2 ) != 0 ) { DEBUG( DEBUG_HTTP, "CRLF bytes didn't contain CRLF!\n" ); return -1; } } else { return -1; } } } else if( req->resp_length > 0 ) { req->resp_left -= readlen; } return readlen;}/* The HTTP/1.x request/response mechanism * * Returns: * PROTO_OK if the request was made (not related to status code) * PROTO_ERROR if the request could not be made * The STATUS CODE is placed in req->status. The error string is * placed in http_error. * * TODO: This should be chopped up into smaller chunks, and get rid of * the horrid goto's. */int http_request( http_req_t *req ) { char buffer[BUFSIZ]; /* For reading from the socket */ int ret, attempt, con_attempt; bool using_expect, /* whether we have sent a Expect: 100 header */ close_connection, dead_connection, wants_body; /* whether the caller wants the response body * callbacks */#ifdef USE_BROKEN_PROPFIND bool is_propfind = (strcmp( req->method, "PROPFIND" ) == 0);#endif#define HTTP_FATAL_ERROR(a) { \ DEBUG( DEBUG_HTTP, a ); \ ret = PROTO_ERROR; \ close_connection = true; \ goto http_request_finish_proc; \ } /* Initialization... */ DEBUG( DEBUG_HTTP, "Request started...\n" ); strcpy( http_error, "Unknown error." ); ret = PROTO_OK; if( http_req_bodysize( req ) != PROTO_OK ) return PROTO_ERROR; /* I shall try this only twice... * First time, with default authentication stuff (either, what we * did last time, or none at all), then with up-to-the-minute * what-the-server-requested authentication stuff. */ attempt = con_attempt = 1; http_auth_new_request( &http_server_auth, req->method, req->uri, req->body_buffer, req->body_file ); do { char request[REQSIZ], *authinfo = NULL; /* Add in the Request-Line */ strcpy( request, req->method ); if( http_use_proxy ) { strcat( request, " http://" ); http_strcat_hostname( &http_server_host, request ); } else { strcat( request, " " ); } strcat( request, req->uri ); strcat( request, " HTTP/1.1" EOL ); /* The caller-supplied headers */ strcat( request, req->headers ); if( http_can_authenticate ) { /* Add the authorization headers in */ char *val = http_auth_request( &http_server_auth ); if( val != NULL ) { strcat( request, "Authorization: " ); strcat( request, val ); free( val ); } else { DEBUG( DEBUG_HTTP, "auth_request returned NULL.\n" ); } } /* Now handle the body. */ using_expect = false; if( req->body!=http_body_none ) { if( (http_expect_works > -1) && (req->body_size > HTTP_EXPECT_MINSIZE) #ifdef USE_BROKEN_PROPFIND /* ... definitely NOT if we're doing PROPFIND */ && (!is_propfind)#endif /* USE_BROKEN_PROPFIND */ ) { /* Add Expect: 100-continue. */ strcat( request, "Expect: 100-continue" EOL ); using_expect = true; } } /* Final CRLF */ strcat( request, EOL ); /* Now send the request */ /* Open the connection if necessary */ if( !http_connected ) { if( (ret = http_open()) != PROTO_OK ) return ret; } dead_connection = false;#ifdef USE_BROKEN_PROPFIND if( !is_propfind ) {#endif /* Send the headers */#ifdef DEBUGGING if( (256&debug_mask) == 256 ) { /* Display everything mode */ DEBUG( DEBUG_HTTP, "Sending request headers:\n%s", request ); } else { /* Blank out the Authorization paramaters */ char reqdebug[REQSIZ], *pnt; strcpy( reqdebug, request ); pnt = strstr( reqdebug, "Authorization: " ); if( pnt != NULL ) { for( pnt += 15; *pnt != '\r' && *pnt != '\0'; pnt++ ) { *pnt = 'x'; } } DEBUG( DEBUG_HTTP, "Sending request headers:\n%s", reqdebug ); }#endif DBEUGGING if( send_string( http_sock, request ) < 0 ) { dead_connection = true; HTTP_FATAL_ERROR( "Could not send request!\n" ); } DEBUG( DEBUG_HTTP, "Request sent.\n" ); #ifdef USE_BROKEN_PROPFIND }#endif /* USE_BROKEN_PROPFIND */ /* Now, if we are doing a Expect: 100, hang around for a short * amount of time, to see if the server actually cares about the * Expect and sends us a 100 Continue response if the request * is valid, else an error code if it's not. This saves sending * big files to the server when they will be rejected. */ if( using_expect ) { DEBUG( DEBUG_HTTP, "Waiting for response...\n" ); ret = sock_block( http_sock, HTTP_EXPECT_TIMEOUT ); switch( ret ) { case -1: /* error */ HTTP_FATAL_ERROR( "Wait (select) failed.\n" );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -