⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 httpdav.c

📁 站点映像程序
💻 C
📖 第 1 页 / 共 5 页
字号:
/*    sitecopy, for managing remote web sites. WebDAV client routines.   Copyright (C) 1998-99, Joe Orton <joe@orton.demon.co.uk>                                                                        This program is free software; you can redistribute it and/or modify   it under the terms of the GNU General Public License as published by   the Free Software Foundation; either version 2 of the License, or   (at your option) any later version.     This program is distributed in the hope that it will be useful,   but WITHOUT ANY WARRANTY; without even the implied warranty of   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the   GNU General Public License for more details.     You should have received a copy of the GNU General Public License   along with this program; if not, write to the Free Software   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.   $Id: httpdav.c,v 1.46.2.18 1999/08/27 10:03:49 joe Exp $*//* This file is a collection of routines to implement a basic WebDAV * client, including an HTTP/1.1 client.  * Transparently supports basic and digest authentication. *//* HTTP method implementation: *   Call, in this order: *     1.  http_request_init()  - set up the request *     2.  http_request()       - make the request *     3.  http_request_end()   - clean up the request */#include <config.h>#include <sys/types.h>#include <sys/stat.h>#ifdef __EMX__#include <sys/select.h>#endif#include <netinet/in.h>#include <ctype.h>#include <errno.h>#include <fcntl.h>#include <stdio.h>#ifdef HAVE_STRING_H#include <string.h>#endif#ifdef HAVE_STRINGS_H#include <strings.h>#endif #ifdef HAVE_STDLIB_H#include <stdlib.h>#endif /* HAVE_STDLIB_H */#ifdef HAVE_UNISTD_H#include <unistd.h>#endif /* HAVE_UNISTD_H */#ifndef HAVE_SNPRINTF#include <snprintf.h>#endif#include <dates.h>#include <basename.h>#include <dirname.h>#include <strsplit.h>#include "frontend.h"#include "protocol.h"#include "httpauth.h"#include "httpdav.h"#include "common.h"#include "socket.h"#include "base64.h"/* Connection information... */int http_sock;bool http_connected;bool http_disable_expect = false;/* Time in seconds to wait for the server to give us a 100 Continue * after submitting a PUT with an "Expect: 100-continue" header. */#define HTTP_EXPECT_TIMEOUT 15/* 100-continue only used if size > HTTP_EXPECT_MINSIZ */#define HTTP_EXPECT_MINSIZE 512/* Whether the current server respects the Expect: 100-continue header */int http_expect_works; /* == 0 if it we don't know, 1 if it does,			    -1 if it doesn't support 100-continue */bool http_webdav_server = false;bool http_init_checks = true;bool http_conn_limit = false;/* Warning given out on spoof attack */const char *http_warn_spoof =   "The server has switched to using basic authentication from digest\n"  "authenticaion, which may be an attempt to discover your password.\n"  "Basic auth will NOT be used.";bool http_can_authenticate;http_auth_session_t http_server_auth, http_proxy_auth;unsigned int http_version_major, http_version_minor;const char *http_quotes = "\"'";const char *http_whitespace = " \r\n\t";#ifdef HAVE_LIBEXPAT/* WebDAV Fetch mode */#include <xmlparse.h>typedef enum {    dav_xml_multistatus = 0,    dav_xml_response,    dav_xml_responsedescription,    dav_xml_href,    dav_xml_propstat,    dav_xml_prop,    dav_xml_status,    dav_xml_getlastmodified,    dav_xml_getcontentlength,    dav_xml_resourcetype,    dav_xml_collection,    dav_xml_unknown,    dav_xml_root} dav_xml_tag_t;/* We get the tag_t from the names array below */const char *dav_xml_tagnames[] = {    "DAV:multistatus",    "DAV:response",    "DAV:responsedescription",    "DAV:href",    "DAV:propstat",    "DAV:prop",    "DAV:status",    "DAV:getlastmodified",    "DAV:getcontentlength",    "DAV:resourcetype",    "DAV:collection",    NULL, /* end-of-list marker */    "@<root>@" /* filler, so we can index this by dav_xml_tag_t */};typedef struct dav_xml_ns_s dav_xml_ns;/* Linked list of namespace scopes */struct dav_xml_ns_s {    char *name;    char *value;    dav_xml_ns *next;};struct dav_xml_state {    dav_xml_tag_t tag;    char *tag_name; /* The full tag name */    char *default_ns; /* The current default namespace */    dav_xml_ns *nspaces; /* List of other namespace scopes */    struct dav_xml_state *parent; /* The parent in the tree */};/* We pass around a dav_xmldoc as the userdata using expat.  This * maintains the current state of the parse and various other bits and * bobs. Within the parse, we store the current branch of the tree, * i.e., the current element and all its parents, but nothing other * than that.  * * The files list is filled as we go (by dav_fetch_gotresource), but * always kept in sorted order, since no ordering of resources in the * PROPFIND response is given.  */struct dav_xmldoc {    /* Points to the root of the document */    struct dav_xml_state *root;    /* Points to the current element in the document */    struct dav_xml_state *current;    /* Whether we want to collect CDATA at the moment or not */    bool want_cdata;    /* The cdata we've collected so far - grows as we collect     * more. */    char *cdata;    /* How big the buffer is atm */    size_t cdata_buflen;    /* How much cdata we've collected so far */    size_t cdata_len;     /* Is it valid? */    bool valid;    /* Temporary store of file info */    struct proto_file_t *file;    /* The complete fetch list */    struct proto_file_t *files;    /* The collection we did a PROPFIND on */    const char *fetch_root;};/* The initial size of the cdata buffer, and the minimum size by which * it grows each time we overflow it. For the PROPFIND requests * we do for sitecopy, the only cdata segments we collect are very small. */#define CDATABUFSIZ 128/* If the cdata buffer size is larger than this when we've finished * with its contents, then we reallocate it. Otherwise, we just * zero it out. Reallocation -> a free() and a malloc(). No realloc ->  * just a memset(). */#define CDATASHRINK 128/* Prototypes */static void dav_xml_startelm( void *userdata, const char *tag, const char **atts );static void dav_xml_endelm( void *userdata, const char *tag );static void dav_xml_cdata( void *userdata, const char *cdata, int len );static bool dav_xml_parsetag( struct dav_xml_state *state,		       const char *tag, const char **atts );static int dav_fetch_getdepth( const char *href );static bool dav_fetch_parse_href( struct dav_xmldoc *doc );static void dav_fetch_gotresource( struct dav_xmldoc *doc );static void dav_xml_parsebody( void *userdata, const char *buffer, const size_t len );static void http_get_content_charset( const char *name, const char *value );char *http_content_charset;#endif /* HAVE_LIBEXPAT *//* Handy macro to free things. */#define DOFREE(x) if( x!=NULL ) free( x )#define EOL "\r\n"#define HTTP_PORT 80const char *http_useragent = PACKAGE "/" VERSION;/* We need to remember the remote host and port even after connecting * the socket e.g. so we can form the Destination: header */struct proto_host_t http_server_host, http_proxy_host;/* This, to store the address of the server we CONNECT to - i.e., * the proxy if we have one, else the server */struct in_addr http_remoteaddr;int http_remoteport;bool http_use_proxy; /* whether we are using the proxy or not */int http_mkdir_works;static int http_response_read( http_req_t *req, char *buffer, size_t buflen );/* Sets up the body size for the given request */static int http_req_bodysize( http_req_t *req );/* The callback for GET requests (to allow signalling progress to the FE) */static void http_get_callback( void *user, const char *buffer, const size_t len );/* Do a dummy MKDIR with PUT */static int http_mkdir_with_put( const char *realdir );/* Holds the error message to be passed up */char http_error[BUFSIZ];/* Put the fixed headers into the request */static void http_req_fixedheaders( http_req_t *req );/* Concatenates the remote server name with :port if port!=80 on to  * the end of str. */static void http_strcat_hostname( struct proto_host_t *host, char *str );/* Header parser for OPTIONS requests. */static void http_options_parsehdr( const char *name, const char *value );static int http_parse_status( http_req_t *req, char *status_line );/* Sends the body down the wire */static int http_req_sendbody( http_req_t *req );/* Encodes the absPath sectionf of a URI using %<hex><hex> encoding */static char *uri_abspath_encode( const char *abs_path );/* Decodes a URI */static char *uri_decode( const char *uri );/* Opens the connection to the remote server. * Returns: *  PROTO_OK       on success *  PROTO_CONNECT  if the socket couldn't be connected */static int http_open( void );/* Closes the connection. * Always returns PROTO_OK */static int http_close( void );/* This doesn't really connect to the server. * Returns *  PROTO_LOOKUP if the hostname of the server could not be resolved *  PROTO_LOCALHOST if the hostname of the local machine could not be *    found *  PROTO_CONNECT if the socket could not be connected *  PROTO_OK if the connection was made successfully. */int http_init( const char *remote_root,	       struct proto_host_t *server, struct proto_host_t *proxy ) {    int ret;    /* Take a copy of the server information */    memcpy( &http_server_host, server, sizeof(struct proto_host_t) );    fe_connection( fe_namelookup );    if( proxy != NULL ) {	memcpy( &http_proxy_host, proxy, sizeof(struct proto_host_t) );	http_remoteport = proxy->port;	http_use_proxy = true;	if( host_lookup( http_proxy_host.hostname, &http_remoteaddr ) )	    return PROTO_LOOKUP;    } else {	http_remoteport = server->port;	http_use_proxy = false;	if( host_lookup( http_server_host.hostname, &http_remoteaddr ) )	    return PROTO_LOOKUP;    }    http_connected = false;    http_expect_works = http_disable_expect?-1:0; /* we don't know yet */    /* temporary workaround */    http_expect_works = -1;    http_mkdir_works = 0; /* we don't know yet */    http_auth_init( &http_server_auth, server->username, server->password );    if( http_use_proxy ) {	/* TODO: Implement properly *//*	http_auth_init( &http_proxy_auth, proxy->username, proxy->password ); */    }    http_can_authenticate = false;    ret = http_open();    /* Drop out if that failed, or they don't want the OPTIONS */    if( (!http_init_checks) || (ret != PROTO_OK) )	return ret;    /* Capability discovery... we don't care whether this     * actually works or not, we just want http_webdav_server     * set appropriately. */    (void) http_options( remote_root );    return PROTO_OK;}int http_finish( void ) {    DEBUG( DEBUG_HTTP, "http_finish called.\n" );    http_auth_finish( &http_server_auth );    if( http_connected ) http_close( );    return PROTO_OK;}/* Parse the HTTP-Version and Status-Code segments of the * given status_line. Sets http_version_* and req->class,status. * Returns: PROTO_OK on success, PROTO_ERROR otherwise */int http_parse_status( http_req_t *req, char *status_line ) {    char *part;    DEBUG( DEBUG_HTTP, "HTTP response line: %s", status_line );    /* Save the line for error codes later */    memset( http_error, 0, BUFSIZ );    strncpy( http_error, status_line, BUFSIZ );    /* Strip off the CRLF for the status line */    if( (part = strchr( http_error, '\r' )) != NULL )	*part = '\0';    /* Check they're speaking the right language */    if( strncmp( status_line, "HTTP/", 5 ) != 0 )	return PROTO_ERROR;    /* And find out which dialect of this peculiar language     * they can talk... */    http_version_major = 0;    http_version_minor = 0;     /* Note, we're good children, and accept leading zero's on the     * version numbers */    for( part = status_line + 5; *part != '\0' && isdigit(*part); part++ ) {	http_version_major += http_version_major*10 + (*part-'0');    }    if( *part != '.' ) return PROTO_ERROR;    for( part++ ; *part != '\0' && isdigit(*part); part++ ) {	http_version_minor += http_version_minor*10 + (*part-'0');    }    DEBUG( DEBUG_HTTP, "HTTP Version Major: %d, Minor: %d\n", 	   http_version_major, http_version_minor );    if( *part != ' ' ) return PROTO_ERROR;    /* Now for the Status-Code. part now points at the space     * between "HTTP/x.x YYY". We want YYY... could use atoi, but     * probably quicker this way. */    req->status = 100*(part[1]-'0') + 10*(part[2]-'0') + (part[3]-'0');    req->class = part[1]-'0';    /* And we can ignore the Reason-Phrase */    DEBUG( DEBUG_HTTP, "HTTP status code: %d\n", req->status );    return PROTO_OK;}/* Sends the body down the socket. * Returns PROTO_OK on success, PROTO_ERROR otherwise */int http_req_sendbody( http_req_t *req ) {    int ret;    switch( req->body ) {    case http_body_file:	ret = transfer( fileno(req->body_file), http_sock, req->body_size );	DEBUG( DEBUG_HTTP, "Sent %d bytes.\n", ret );	rewind( req->body_file ); /* since we may have to send it again */	break;    case http_body_buffer:	DEBUG( DEBUG_HTTP, "Sending body:\n%s\n", req->body_buffer );	ret = send_string( http_sock, req->body_buffer );	break;    default:	DEBUG( DEBUG_HTTP, "Argh in http_req_sendbody!" );	ret = -1;    }    if( ret == -1 ) { 	/* transfer failed */	return PROTO_ERROR;    } else {	return PROTO_OK;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -