📄 httpdav.c
字号:
DEBUG( DEBUG_XML, "Parse error - not good context for this tag.\n" ); return; } /* Now actually do something about it */ switch( s->tag ) { case dav_xml_response: /* new file information */ DEBUG( DEBUG_XML, "New file context.\n" ); memset( doc->file, 0, sizeof(struct proto_file_t) ); break; case dav_xml_href: case dav_xml_getcontentlength: case dav_xml_resourcetype: case dav_xml_getlastmodified: /* For these elements, collect the CDATA */ DEBUG( DEBUG_XML, "Collecting CDATA...\n" ); doc->want_cdata = true; break; case dav_xml_prop: case dav_xml_propstat: case dav_xml_collection: default: /* donothing */ break; } if( doc->want_cdata ) { /* Ready the cdata buffer for new cdata */ if( doc->cdata_buflen == -1 ) { /* Create a buffer */ DEBUG( DEBUG_XML, "Allocating new cdata buffer.\n" ); doc->cdata = malloc( CDATABUFSIZ ); doc->cdata_buflen = CDATABUFSIZ; } /* Now zero-out the buffer. */ memset( doc->cdata, 0, doc->cdata_buflen ); /* And we've got nothing in it */ doc->cdata_len = 0; }}/* Returns the depth of the file in the directory heirarchy, * i.e. 1 for /foo.html, 2 for /bar/norm.html, 4 for /a/b/c/d.html */static int dav_fetch_getdepth( const char *href ) { const char *pnt; int count = 0; for( pnt=href; *pnt != '\0'; pnt++ ) /* oneliner */ if( *pnt == '/' ) count++; return count;}static bool dav_fetch_parse_href( struct dav_xmldoc *doc ) { const char *tmp; char *dec; bool ret; size_t rootlen; DEBUG( DEBUG_HTTP, "Parsing href [%s]\n", doc->cdata ); if( strncmp( doc->cdata, "http://", 7 ) == 0 ) { /* Absolute URI. * Look for the path bit */ DEBUG( DEBUG_HTTP, "Got absolute URI.\n" ); tmp = strchr( doc->cdata+7, '/' ); if( tmp == NULL ) { DEBUG( DEBUG_HTTP, "No path segment found.\n" ); return false; } } else { tmp = doc->cdata; } DEBUG( DEBUG_HTTP, "Using abspath segment: %s\n", tmp ); dec = uri_decode( tmp ); DEBUG( DEBUG_HTTP, "Decoded is: [%s]. Root is [%s]\n", tmp, doc->fetch_root ); /* Now, dec points to the absPath section of the URI. * We check whether this resource is actually in the * collection we have done the PROPFIND against. */ rootlen = strlen( doc->fetch_root ); if( strncmp( dec, doc->fetch_root, rootlen ) != 0 ) { DEBUG( DEBUG_HTTP, "parse_href failed: root collection not matched." ); ret = false; } else { /* We're in the right place */ DEBUG( DEBUG_HTTP, "parse_href: Got [%s]\n", dec + rootlen ); /* Filename must be the basename */ doc->file->filename = strdup( base_name( dec + rootlen ) ); /* And fill in the directory while we're here */ doc->file->directory = dir_name( dec + rootlen ); ret = true; DEBUG( DEBUG_HTTP, "parse_href: Filename [%s], Directory [%s]\n", doc->file->filename, doc->file->directory ); } free( dec ); return ret;}static void dav_fetch_gotresource( struct dav_xmldoc *doc ) { struct proto_file_t *current, *previous; DEBUG( DEBUG_HTTP, "Got resource:\n" "filename = [%s] isdir = %s size = %d mtime = %s\n", doc->file->filename, doc->file->isdir?"true":"false", doc->file->size, rfc1123_date( doc->file->modtime ) ); if( (strlen( doc->file->directory ) == 0) && (strlen( doc->file->filename ) == 0 ) ) { DEBUG( DEBUG_HTTP, "Resource is root collection, ignoring.\n" ); return; } if( doc->file->isdir ) { doc->file->directory[strlen(doc->file->directory)-1] = '\0'; } DEBUG( DEBUG_HTTP, "Filename is really: %s\n", doc->file->filename ); /* Depth in the hierarchy - i.e., how many directories deep the * resource is. */ doc->file->depth = dav_fetch_getdepth( doc->file->directory ); DEBUG( DEBUG_HTTP, "File is at depth: %d\n", doc->file->depth ); /* Insert it into the list, keeping the list sorted by depth */ for( current=doc->files, previous=NULL; current!=NULL; previous=current, current=current->next ) /* one-liner */ if( current->depth > doc->file->depth ) break; doc->file->next = current; if( previous == NULL ) { doc->files = doc->file; } else { previous->next = doc->file; } /* Create a new file, ready to be filled in */ doc->file = malloc( sizeof( struct proto_file_t ) ); memset( doc->file, 0, sizeof( struct proto_file_t ) );}/* End-of-element handler */static void dav_xml_endelm( void *userdata, const char *tag ) { struct dav_xmldoc *doc = (struct dav_xmldoc *)userdata; struct dav_xml_state *s; dav_xml_ns *this_ns, *next_ns; if( !doc->valid ) { /* We've stopped parsing */ DEBUG( DEBUG_XML, "Parse died. Ignoring end of element: %s\n", tag ); return; } s = doc->current; DEBUG( DEBUG_XML, "End of element %s.\n", tag); switch( s->tag ) { case dav_xml_href: doc->valid = dav_fetch_parse_href( doc ); break; case dav_xml_getlastmodified: DEBUG( DEBUG_HTTP, "Parsing date [%s]\n", doc->cdata ); doc->file->modtime = rfc1123_parse( doc->cdata ); if( doc->file->modtime == (time_t)-1 ) { DEBUG( DEBUG_HTTP, "Date is not in RFC1123 format.\n" ); doc->valid = false; } break; case dav_xml_getcontentlength: doc->file->size = atoi( doc->cdata ); break; case dav_xml_collection: doc->file->isdir = true; break; case dav_xml_response: dav_fetch_gotresource( doc ); break; default: break; } /* Move the current pointer up the chain */ doc->current = s->parent; DEBUG( DEBUG_XML, "Back in tag: %s\n", doc->current->tag_name ); if( doc->want_cdata ) { /* Free the cdata buffer if it's grown to big for its boots. */ if( doc->cdata_buflen > CDATASHRINK ) { DEBUG( DEBUG_XML, "cdata buffer overgrown, freeing.\n" ); free( doc->cdata ); doc->cdata_buflen = -1; } /* And we've stopped collecting it now, thanks */ doc->want_cdata = false; } if( s->default_ns!=NULL ) free( s->default_ns ); /* Free the namespaces */ this_ns = s->nspaces; while( this_ns != NULL ) { next_ns = this_ns->next; free( this_ns ); this_ns = next_ns; }; free( s->tag_name ); free( s ); DEBUG( DEBUG_XML, "Cleanup okay.\n" );}/* CDATA handler. We keep the entire cdata for each element in * doc->cdata, and expand the buffer as necessary. */static void dav_xml_cdata( void *userdata, const char *cdata, int len ) { struct dav_xmldoc *doc = (struct dav_xmldoc *)userdata; size_t newlen; if( !doc->want_cdata ) return; /* First, if this is the beginning of the CDATA, skip all * leading whitespace, we don't want it. */ if( doc->cdata_buflen < 0 ) { DEBUG( DEBUG_XML, "ALERT: Shouldn't be collecting.\n" ); return; } DEBUG( DEBUG_XML, "Given %d bytes of cdata.\n", len ); if( doc->cdata_len == 0 ) { size_t wslen = 0; /* Ignore any leading whitespace */ while( wslen < len && ( cdata[wslen] == ' ' || cdata[wslen] == '\r' || cdata[wslen] == '\n' || cdata[wslen] == '\t' ) ) { wslen++; } cdata += wslen; len -= wslen; DEBUG( DEBUG_XML, "Skipped %d bytes of leading whitespace.\n", wslen ); if( len == 0 ) { DEBUG( DEBUG_XML, "Zero bytes of content.\n" ); return; } } /* Work out whether we need to expand the cdata buffer to * include the new incoming cdata. We always keep one more * byte in the buffer than we need, for the null-terminator */ for( newlen = doc->cdata_buflen; newlen < (doc->cdata_len + len + 1); newlen += CDATABUFSIZ ) /* nullop */ ; if( newlen > doc->cdata_buflen ) { size_t oldbuflen = doc->cdata_buflen; /* Reallocate bigger buffer */ DEBUG( DEBUG_XML, "Growing CDATA buffer from %d to %d.\n", oldbuflen, newlen ); doc->cdata = realloc( doc->cdata, newlen ); doc->cdata_buflen = newlen; /* Zero-out the new bit of buffer */ memset( doc->cdata+oldbuflen, 0, newlen-oldbuflen ); } /* Now simply copy the new cdata onto the end of the buffer */ memcpy( doc->cdata+doc->cdata_len, cdata, len ); doc->cdata_len += len; DEBUG( DEBUG_XML, "Collected %d bytes of cdata, buffer now:\n%s\n", len, doc->cdata );}static void http_get_content_charset( const char *name, const char *value ) { char **pairs; int n; if( strcasecmp( name, "Content-Type" ) == 0 ) { /* Get the charset so we can pass it on to expat */ pairs = strpairs( value, ';', '=', http_quotes, http_whitespace ); for( n = 0; pairs[n] != NULL; n+=2 ) { if( (strcasecmp( pairs[n], "charset") == 0) && pairs[n+1] != NULL ) { DOFREE( http_content_charset ); /* Strip off the quotes */ http_content_charset = strstrip( pairs[n+1], '\"' ); DEBUG( DEBUG_HTTP, "Got content type charset: %s\n", http_content_charset ); } } strpairs_free( pairs ); } return;}static void dav_xml_parsebody( void *userdata, const char *buffer, const size_t len ) { struct dav_xmldoc *doc = userdata; int ret; /* duck out if it's broken */ if( !doc->valid ) { DEBUG( DEBUG_XML, "Not parsing %d bytes!\n", len ); return; } if( len == 0 ) { DEBUG( DEBUG_XML, "Got 0-length buffer, end of response.\n" ); ret = XML_Parse( dav_xml_parser, "", 0, -1 ); } else { DEBUG( DEBUG_XML, "Got %d length buffer.\n", len ); ret = XML_Parse( dav_xml_parser, buffer, len, 0 ); } DEBUG( DEBUG_XML, "XML_Parse returned %d\n", ret ); if( ret == 0 ) { doc->valid = false; }}/* WebDAV fetch mode handler. */int dav_fetch( const char *dirname, struct proto_file_t **files ) { http_req_t req; struct dav_xmldoc doc = {0}; int ret; const char *propfind_body = "<?xml version=\"1.0\"?>" EOL /* should we use encoding=? */ "<propfind xmlns=\"DAV:\">" EOL " <prop>" EOL " <getcontentlength/>" EOL " <getlastmodified/>" EOL " <resourcetype/>" EOL " </prop>" EOL "</propfind>" EOL; const char *myheaders = "Content-Type: text/xml" EOL /* should we use charset=? */ "Depth: infinity" EOL; dav_xml_parser = XML_ParserCreate( NULL ); XML_SetElementHandler( dav_xml_parser, dav_xml_startelm, dav_xml_endelm ); XML_SetCharacterDataHandler( dav_xml_parser, dav_xml_cdata ); XML_SetUserData( dav_xml_parser, (void *) &doc ); /* Create a dummy state to act as the root element in the * tree. Just makes things a little bit easier since we can * then always presume we have a ->parent element. */ doc.root = malloc( sizeof( struct dav_xml_state ) ); memset( doc.root, 0, sizeof( struct dav_xml_state ) ); doc.root->tag = dav_xml_root; doc.root->tag_name = "@root@"; /* And set it to the current element */ doc.current = doc.root; /* Init the document */ doc.files = NULL; doc.fetch_root = dirname; doc.valid = true; /* so far... */ doc.file = malloc( sizeof( struct proto_file_t ) ); memset( doc.file, 0, sizeof( struct proto_file_t ) ); http_request_init( &req, "PROPFIND", dirname ); req.body_callback = dav_xml_parsebody; req.body_callback_userdata = &doc; req.body = http_body_buffer; req.body_buffer = propfind_body; /* Add in the content type header */ strcat( req.headers, myheaders ); ret = http_request( &req ); XML_ParserFree( dav_xml_parser ); free( doc.root ); if( ret == PROTO_OK && req.class == 2 && doc.valid ) { *files = doc.files; ret = PROTO_OK; } else { *files = NULL; ret = PROTO_ERROR; } http_request_end( &req ); return ret;}#endif /* HAVE_LIBEXPAT */int http_head( const char *directory ) { http_req_t req; int ret; http_request_init( &req, "HEAD", directory ); ret = http_request( &req ); if( ret == PROTO_OK && req.class != 2 ) ret = PROTO_ERROR; http_request_end( &req ); return ret;}static void http_options_parsehdr( const char *name, const char *value ) { char **classes, **class; if( strcasecmp( name, "DAV" ) == 0 ) { DEBUG( DEBUG_HTTP, "Got OPTIONS header with value: %s\n", value ); classes = strsplit( value, ',', http_quotes, http_whitespace ); for( class = classes; *class!=NULL; class++ ) { DEBUG( DEBUG_HTTP, "Got compliance class: [%s]\n", *class ); if( strncmp( *class, "1", 1 ) == 0 ) { DEBUG( DEBUG_HTTP, "Class 1 compliant server.\n" ); http_webdav_server = true; } } strsplit_free( classes ); }}/* Performs an OPTIONS request. * Sets http_webdav_server appropriately. */int http_options( const char *directory ) { http_req_t req; int ret; http_webdav_server = false; http_request_init( &req, "OPTIONS", directory ); req.hdrs_callback = http_options_parsehdr; ret = http_request( &req ); if( ret == PROTO_OK && req.class != 2 ) ret = PROTO_ERROR; http_request_end( &req ); return ret;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -