📄 httpdav.c
字号:
break; case 1: /* we got us a response! */ DEBUG( DEBUG_HTTP, "Wait got data.\n" ); http_expect_works = 1; /* it works - use it again */ break; case 0: /* Timed out - i.e. Expect: ignored. There is a danger * here that the server DOES respect the Expect: header, * but was going SO slowly that it didn't get time to * respond within HTTP_EXPECT_TIMEOUT. * TODO: while sending the body, check to see if the * server has sent anything back - if it HAS, then * stop sending - this is a spec compliance SHOULD */ DEBUG( DEBUG_HTTP, "Wait timed out.\n" ); http_expect_works = -1; /* don't try that again */ /* and give them the body */ if( http_req_sendbody( req ) != PROTO_OK ) HTTP_FATAL_ERROR( "Could not send body.\n" ); break; } } else if( req->body != http_body_none ) {#ifdef USE_BROKEN_PROPFIND if( is_propfind ) { char *pfbuf; DEBUG( DEBUG_HTTP, "Broken PROPFIND handler...\n" ); pfbuf = malloc( strlen(request) + req->body_size + 1 ); memset( pfbuf, 0, strlen(request) + req->body_size + 1 ); strcpy( pfbuf, request ); strcat( pfbuf, req->body_buffer ); DEBUG( DEBUG_HTTP, "Sending PROPFIND request...\n" ); if( send_string( http_sock, pfbuf ) < 0 ) { HTTP_FATAL_ERROR( "Could not send request.\n" ); } DEBUG( DEBUG_HTTP, "Sending newline.\n" ); if( send_line( http_sock, "" ) < 0 ) { HTTP_FATAL_ERROR( "Could not send newline.\n" ); } free( pfbuf ); } else { #endif /* USE_BROKEN_PROPFIND */ /* Just chuck the file down the socket */ DEBUG( DEBUG_HTTP, "Sending body...\n" ); if( http_req_sendbody( req ) == PROTO_ERROR ) HTTP_FATAL_ERROR( "Could not send body.\n" ); /* FIXME: This. Should it be here? */ DEBUG( DEBUG_HTTP, "Sending newline.\n" ); if( send_line( http_sock, "" ) < 0 ) { HTTP_FATAL_ERROR( "Could not send newline.\n" ); } DEBUG( DEBUG_HTTP, "Body sent.\n" );#ifdef USE_BROKEN_PROPFIND }#endif /* USE_BROKEN_PROPFIND */ } /* Now, we have either: * - Sent the header and body, or * - Sent the header incl. Expect: line, and got some response. * In any case, we get the status line of the response. */ /* HTTP/1.1 says we MUST be able to accept any number of * 100 (Continue) responses prior to the normal response. * So loop while we get them. */ do { if( read_line( http_sock, buffer, BUFSIZ ) < 0 ) { dead_connection = true; HTTP_FATAL_ERROR( "Could not read status line.\n" ); } DEBUG( DEBUG_HTTP, "[Status Line] < %s", buffer ); /* Got the status line - parse it */ if( http_parse_status( req, buffer ) == PROTO_ERROR ) HTTP_FATAL_ERROR( "Could not parse status line.\n" ); if( req->class == 1 ) { DEBUG( DEBUG_HTTP, "Got 1xx-class.\n" ); /* Skip any headers, we don't need them */ do { if( read_line( http_sock, buffer, BUFSIZ ) < 0 ) HTTP_FATAL_ERROR( "Could not read header.\n" ); DEBUG( DEBUG_HTTP, "[Ignored header] < %s", buffer ); } while( strcmp( buffer, EOL ) != 0 ); if( using_expect && (req->status == 100) ) { /* We are using Expect: 100, and we got a 100-continue * return code... send the request body */ DEBUG( DEBUG_HTTP, "Got continue... sending body now.\n" ); if( http_req_sendbody( req ) != PROTO_OK ) HTTP_FATAL_ERROR( "Could not send body.\n" ); DEBUG( DEBUG_HTTP, "Body sent.\n" ); } } } while( req->class == 1 ); /* We've got the real status line... now get the headers */ req->resp_length = -1; req->resp_te = http_te_unknown; close_connection = false; /* Now read the rest of the header... up to the next blank line */ while( read_line( http_sock, buffer, BUFSIZ ) > 0 ) { char extra[BUFSIZ], *pnt; DEBUG( DEBUG_HTTP, "[Header:%d] < %s", strlen(buffer), buffer ); if( strcmp( buffer, EOL ) == 0 ) { DEBUG( DEBUG_HTTP, "CRLF: End of headers.\n" ); break; } while(true) { /* Collect any extra lines into buffer */ ret = sock_recv( http_sock, extra, 1, MSG_PEEK); if( ret <= 0 ) { HTTP_FATAL_ERROR( "Couldn't peek at next line.\n" ); } if( extra[0] != ' ' && extra[0] != '\t' ) { /* No more headers */ break; } ret = read_line( http_sock, extra, BUFSIZ ); if( ret == -2 ) { /* No newline within BUFSIZ bytes. This is a * loooong header. */ DEBUG( DEBUG_HTTP, "Header line longer than buffer, skipped.\n" ); break; } else if( ret <= 0 ) { HTTP_FATAL_ERROR( "Couldn't read next header line.\n" ); } else { DEBUG( DEBUG_HTTP, "[Cont:%d] < %s", strlen(extra), extra); } /* Append a space to the end of the last header, in * place of the CRLF. */ pnt = strchr( buffer, '\r' ); pnt[0] = ' '; pnt[1] = '\0'; /* Skip leading whitespace off next line */ for( pnt = extra; *pnt!='\0' && ( *pnt == ' ' || *pnt =='\t' ); pnt++ ) /*oneliner*/; DEBUG( DEBUG_HTTP, "[Continued] < %s", pnt ); if( strlen(buffer) + strlen(pnt) >= BUFSIZ ) { DEBUG( DEBUG_HTTP, "Exceeded header buffer space.\n" ); /* Note, we don't break out of the loop here, cos * we need to collect all the continued lines */ } else { strcat( buffer, pnt ); } } /* Now parse the header line. This is all a bit noddy. */ pnt = strchr( buffer, ':' ); if( pnt != NULL ) { char *name, *value, *part; /* Null-term name at the : */ *pnt = '\0'; name = buffer; /* Strip leading whitespace from the value */ for( value = pnt+1; *value!='\0' && *value==' '; value++ ) /* nullop */; if( (part = strchr( value, '\r' )) != NULL ) *part = '\0'; if( (part = strchr( value, '\n' )) != NULL ) *part = '\0'; DEBUG( DEBUG_HTTP, "Header Name: [%s], Value: [%s]\n", name, value ); if( strcasecmp( name, "Content-Length" ) == 0 ) { /* TODO: 2068 says we MUST notify the user if this * is not a real number. */ req->resp_length = atoi( value ); } else if( strcasecmp( name, "Transfer-Encoding" ) == 0 ) { if( strcasecmp( value, "chunked" ) == 0 ) { req->resp_te = http_te_chunked; } else { req->resp_te = http_te_unknown; } } else if( strcasecmp( name, "Connection" ) == 0 ) { if( strcasecmp( value, "close" ) == 0 ) { close_connection = true; } } else if( strcasecmp( name, "WWW-Authenticate" ) == 0 ) { /* Parse the authentication challenge */ http_can_authenticate = http_auth_challenge( &http_server_auth, value ); } else if( strcasecmp( name, "Authentication-Info" ) == 0 ) { /* Remember on the authentication reponse */ authinfo = strdup( value ); } else if( req->hdrs_callback != NULL ) { (*req->hdrs_callback)( name, value ); } } } /* Body length calculation, bit icky. * Here, we set: * length==-1 if we DO NOT know the exact body length * length>=0 if we DO know the body length. * * RFC2068, section 4.3: * NO body is returned if the method is HEAD, or the resp status * is 204 or 304 */ if( (strcmp( req->method, "HEAD" ) == 0 ) || req->status==204 || req->status==304 ) { req->resp_length = 0; } else { /* RFC2068, section 4.4: if we have a transfer encoding * and a content-length, then ignore the content-length. */ if( (req->resp_length>-1) && (req->resp_te!=http_te_unknown) ) { req->resp_length = -1; } } /* The caller only wants the body if this request * was 2xx class. */ /* FIXME: Let the caller decide when they want it*/ wants_body = (req->class == 2); if( req->resp_length != 0 ) { /* Now, read the body */ int readlen; req->resp_left = req->resp_length; req->resp_chunk_left = 0; do { /* Read a block */ readlen = http_response_read( req, buffer, BUFSIZ ); DEBUG( DEBUG_HTTP, "Read %d bytes.\n", readlen ); if( readlen > -1 ) { /* What to do with the body block */ if( req->body_callback && wants_body ) (*req->body_callback)( req->body_callback_userdata, buffer, readlen ); http_auth_response_body( &http_server_auth, buffer, readlen ); } } while( readlen > 0 ); if( readlen < 0 ) { HTTP_FATAL_ERROR( "Block read error.\n" ); } else if( (readlen == 0) && (req->resp_te == http_te_chunked) ) { char *pnt; /* Read till CRLF - skip trailing headers */ do { if( read_line( http_sock, buffer, BUFSIZ ) < 0 ) { /* It broke */ HTTP_FATAL_ERROR( "Could not read trailer.\n" ); } DEBUG( DEBUG_HTTP, "[Chunk trailer] %s", buffer ); /* Now parse the header line. */ pnt = strchr( buffer, ':' ); if( pnt != NULL ) { char *name, *value, *part; /* Null-term name at the : */ *pnt = '\0'; name = buffer; /* Strip leading whitespace from the value */ for( value = pnt+1; *value!='\0' && *value==' '; value++ ) /* nullop */; if( (part = strchr( value, '\r' )) != NULL ) *part = '\0'; if( (part = strchr( value, '\n' )) != NULL ) *part = '\0'; DEBUG( DEBUG_HTTP, "Header Name: [%s], Value: [%s]\n", name, value ); if( strcasecmp( name, "Authentication-Info" ) == 0 ) { /* Save the authinfo value */ if( authinfo == NULL ) { authinfo = strdup( value ); } else { DEBUG( DEBUG_HTTP, "Recieved TWO auth-info headers... ignoring second.\n" ); } } } } while( strcmp( buffer, EOL ) != 0 ); } } /* Now, if we are doing authentication, check the * server's credentials. */ if( http_can_authenticate && (authinfo != NULL) ) { if( http_auth_verify_response( &http_server_auth, authinfo ) ) { ret = PROTO_OK; DEBUG( DEBUG_HTTP, "Response authenticated okay.\n" ); } else { DEBUG( DEBUG_HTTP, "Reponse authenticated as invalid.\n" ); ret = PROTO_ERROR; sprintf( http_error,"Server was not authenticated correctly."); } } else { ret = PROTO_OK; } DOFREE( authinfo ); http_request_finish_proc: /* Now, do we close the connection? */ if( close_connection ) { DEBUG( DEBUG_HTTP, "Forced connection close.\n" ); http_close( ); } else if( http_conn_limit ) { DEBUG( DEBUG_HTTP, "Limited connection close.\n" ); http_close( ); } /* Now, do that all *again* if it didn't work. * Otherwise, give up */ } while( (dead_connection && (++con_attempt<4)) || ((++attempt<3) && http_can_authenticate && (req->status == 401) )); DEBUG( DEBUG_HTTP, "Req ends, status %d class %dxx, status line:\n%s\n", req->status, req->class, http_error ); return ret;}/* Simple HTTP put. * local is the local filename. Remote is the destination URI (URI?) * Make it proper. * Returns: * PROTO_FILE if no local file * PROTO_ERROR if something general goes wrong * PROTO_OK if it all works fine */int http_put( const char *local, const char *remote, const bool ascii ) { http_req_t req; int ret; http_request_init( &req, "PUT", remote ); /* joe: ANSI C says the "b" will be ignored by platforms which * should ignore it... but, we'll play safe: */#if defined (__EMX__) || defined(__CYGWIN__) req.body_file = fopen( local, "rb" );#else req.body_file = fopen( local, "r" );#endif if( req.body_file == NULL ) { strcpy( http_error, "Could not open file." ); ret = PROTO_FILE; } else { req.body = http_body_file; ret = http_request( &req ); fclose( req.body_file ); if( ret == PROTO_OK && req.class != 2 ) ret = PROTO_ERROR; } http_request_end( &req ); return ret;}int http_get_fd;bool http_get_working;size_t http_get_total, http_get_progress;void http_get_callback( void *user, const char *buffer, const size_t len ) { if( !http_get_working ) return; DEBUG( DEBUG_HTTP, "Got progress: %d out of %d\n", len, http_get_total ); if( send_data( http_get_fd, buffer, len ) < 0 ) { http_get_working = false; } else { http_get_progress += len; fe_transfer_progress( http_get_progress, http_get_total ); }}int http_get( const char *local, const char *remote, const int remotesize, const bool ascii ) { http_req_t req; int ret;#if defined (__EMX__) || defined (__CYGWIN__) /* We have to set O_BINARY, thus need open(). Otherwise it should be equivalent to creat(). */ http_get_fd = open( local, O_WRONLY|O_TRUNC|O_CREAT|O_BINARY, 0644 );#else http_get_fd = creat( local, 0644 );#endif if( http_get_fd < 0 ) { snprintf( http_error, BUFSIZ, "Could not open local file: %s", strerror( errno ) ); return PROTO_ERROR; } http_request_init( &req, "GET", remote ); req.body_callback = http_get_callback; http_get_working = true; http_get_progress = 0; http_get_total = remotesize; DEBUG( DEBUG_HTTP, "Total remote size: %d\n", remotesize ); ret = http_request( &req ); if( close( http_get_fd ) < 0 ) { snprintf( http_error, BUFSIZ, "Error closing local file: %s", strerror( errno ) ); ret = PROTO_ERROR; } else if( ret == PROTO_OK && req.class != 2 ) { ret = PROTO_ERROR; } http_request_end( &req ); return ret;}/* Perform the file operations */int dav_move( const char *from, const char *to ) { http_req_t req; int ret; http_request_init( &req, "MOVE", from ); strcat( req.headers, "Destination: http://" ); http_strcat_hostname( &http_server_host, req.headers ); strcat( req.headers, to );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -