"Fossies" - the Fresh Open Source Software Archive

Member "sitecopy-0.16.6/lib/neon/ne_basic.c" (7 Feb 2008, 12847 Bytes) of archive /linux/www/sitecopy-0.16.6.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "ne_basic.c" see the Fossies "Dox" file reference documentation.

    1 /* 
    2    Basic HTTP and WebDAV methods
    3    Copyright (C) 1999-2008, Joe Orton <joe@manyfish.co.uk>
    4 
    5    This library is free software; you can redistribute it and/or
    6    modify it under the terms of the GNU Library General Public
    7    License as published by the Free Software Foundation; either
    8    version 2 of the License, or (at your option) any later version.
    9    
   10    This library is distributed in the hope that it will be useful,
   11    but WITHOUT ANY WARRANTY; without even the implied warranty of
   12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   13    Library General Public License for more details.
   14 
   15    You should have received a copy of the GNU Library General Public
   16    License along with this library; if not, write to the Free
   17    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
   18    MA 02111-1307, USA
   19 
   20 */
   21 
   22 #include "config.h"
   23 
   24 #include <sys/types.h>
   25 #include <sys/stat.h> /* for struct stat */
   26 
   27 #ifdef HAVE_STRING_H
   28 #include <string.h>
   29 #endif
   30 #ifdef HAVE_UNISTD_H
   31 #include <unistd.h>
   32 #endif
   33 #ifdef HAVE_STDLIB_H
   34 #include <stdlib.h>
   35 #endif
   36 
   37 #include <errno.h>
   38 
   39 #include "ne_request.h"
   40 #include "ne_alloc.h"
   41 #include "ne_utils.h"
   42 #include "ne_basic.h"
   43 #include "ne_207.h"
   44 
   45 #ifdef NE_HAVE_DAV
   46 #include "ne_uri.h"
   47 #include "ne_locks.h"
   48 #endif
   49 
   50 #include "ne_dates.h"
   51 #include "ne_internal.h"
   52 
   53 int ne_getmodtime(ne_session *sess, const char *uri, time_t *modtime) 
   54 {
   55     ne_request *req = ne_request_create(sess, "HEAD", uri);
   56     const char *value;
   57     int ret;
   58 
   59     ret = ne_request_dispatch(req);
   60 
   61     value = ne_get_response_header(req, "Last-Modified"); 
   62 
   63     if (ret == NE_OK && ne_get_status(req)->klass != 2) {
   64     *modtime = -1;
   65     ret = NE_ERROR;
   66     } 
   67     else if (value) {
   68         *modtime = ne_httpdate_parse(value);
   69     }
   70     else {
   71         *modtime = -1;
   72     }
   73 
   74     ne_request_destroy(req);
   75 
   76     return ret;
   77 }
   78 
   79 #ifdef NE_LFS
   80 #define ne_fstat fstat64
   81 typedef struct stat64 struct_stat;
   82 #else
   83 #define ne_fstat fstat
   84 typedef struct stat struct_stat;
   85 #endif
   86 
   87 /* PUT's from fd to URI */
   88 int ne_put(ne_session *sess, const char *uri, int fd) 
   89 {
   90     ne_request *req;
   91     struct_stat st;
   92     int ret;
   93 
   94     if (ne_fstat(fd, &st)) {
   95         int errnum = errno;
   96         char buf[200];
   97 
   98         ne_set_error(sess, _("Could not determine file size: %s"),
   99                      ne_strerror(errnum, buf, sizeof buf));
  100         return NE_ERROR;
  101     }
  102     
  103     req = ne_request_create(sess, "PUT", uri);
  104 
  105 #ifdef NE_HAVE_DAV
  106     ne_lock_using_resource(req, uri, 0);
  107     ne_lock_using_parent(req, uri);
  108 #endif
  109 
  110     ne_set_request_body_fd(req, fd, 0, st.st_size);
  111     
  112     ret = ne_request_dispatch(req);
  113     
  114     if (ret == NE_OK && ne_get_status(req)->klass != 2)
  115     ret = NE_ERROR;
  116 
  117     ne_request_destroy(req);
  118 
  119     return ret;
  120 }
  121 
  122 /* Dispatch a GET request REQ, writing the response body to FD fd.  If
  123  * RANGE is non-NULL, then it is the value of the Range request
  124  * header, e.g. "bytes=1-5".  Returns an NE_* error code. */
  125 static int dispatch_to_fd(ne_request *req, int fd, const char *range)
  126 {
  127     ne_session *const sess = ne_get_session(req);
  128     const ne_status *const st = ne_get_status(req);
  129     int ret;
  130     size_t rlen;
  131 
  132     /* length of bytespec after "bytes=" */
  133     rlen = range ? strlen(range + 6) : 0;
  134 
  135     do {
  136         const char *value;
  137         
  138         ret = ne_begin_request(req);
  139         if (ret != NE_OK) break;
  140 
  141         value = ne_get_response_header(req, "Content-Range");
  142 
  143         /* For a 206 response, check that a Content-Range header is
  144          * given which matches the Range request header. */
  145         if (range && st->code == 206 
  146             && (value == NULL || strncmp(value, "bytes ", 6) != 0
  147                 || strncmp(range + 6, value + 6, rlen)
  148                 || (range[5 + rlen] != '-' && value[6 + rlen] != '/'))) {
  149             ne_set_error(sess, _("Response did not include requested range"));
  150             return NE_ERROR;
  151         }
  152 
  153         if ((range && st->code == 206) || (!range && st->klass == 2)) {
  154             ret = ne_read_response_to_fd(req, fd);
  155         } else {
  156             ret = ne_discard_response(req);
  157         }
  158 
  159         if (ret == NE_OK) ret = ne_end_request(req);
  160     } while (ret == NE_RETRY);
  161 
  162     return ret;
  163 }
  164 
  165 static int get_range_common(ne_session *sess, const char *uri, 
  166                             const char *brange, int fd)
  167 
  168 {
  169     ne_request *req = ne_request_create(sess, "GET", uri);
  170     const ne_status *status;
  171     int ret;
  172 
  173     ne_add_request_header(req, "Range", brange);
  174     ne_add_request_header(req, "Accept-Ranges", "bytes");
  175 
  176     ret = dispatch_to_fd(req, fd, brange);
  177 
  178     status = ne_get_status(req);
  179 
  180     if (ret == NE_OK && status->code == 416) {
  181     /* connection is terminated too early with Apache/1.3, so we check
  182      * this even if ret == NE_ERROR... */
  183     ne_set_error(sess, _("Range is not satisfiable"));
  184     ret = NE_ERROR;
  185     }
  186     else if (ret == NE_OK) {
  187     if (status->klass == 2 && status->code != 206) {
  188         ne_set_error(sess, _("Resource does not support ranged GET requests"));
  189         ret = NE_ERROR;
  190     }
  191     else if (status->klass != 2) {
  192         ret = NE_ERROR;
  193     }
  194     } 
  195     
  196     ne_request_destroy(req);
  197 
  198     return ret;
  199 }
  200 
  201 int ne_get_range(ne_session *sess, const char *uri, 
  202          ne_content_range *range, int fd)
  203 {
  204     char brange[64];
  205 
  206     if (range->end == -1) {
  207         ne_snprintf(brange, sizeof brange, "bytes=%" FMT_NE_OFF_T "-", 
  208                     range->start);
  209     }
  210     else {
  211     ne_snprintf(brange, sizeof brange,
  212                     "bytes=%" FMT_NE_OFF_T "-%" FMT_NE_OFF_T,
  213                     range->start, range->end);
  214     }
  215 
  216     return get_range_common(sess, uri, brange, fd);
  217 }
  218 
  219 /* Get to given fd */
  220 int ne_get(ne_session *sess, const char *uri, int fd)
  221 {
  222     ne_request *req = ne_request_create(sess, "GET", uri);
  223     int ret;
  224 
  225     ret = dispatch_to_fd(req, fd, NULL);
  226     
  227     if (ret == NE_OK && ne_get_status(req)->klass != 2) {
  228     ret = NE_ERROR;
  229     }
  230 
  231     ne_request_destroy(req);
  232 
  233     return ret;
  234 }
  235 
  236 
  237 /* Get to given fd */
  238 int ne_post(ne_session *sess, const char *uri, int fd, const char *buffer)
  239 {
  240     ne_request *req = ne_request_create(sess, "POST", uri);
  241     int ret;
  242 
  243     ne_set_request_flag(req, NE_REQFLAG_IDEMPOTENT, 0);
  244 
  245     ne_set_request_body_buffer(req, buffer, strlen(buffer));
  246 
  247     ret = dispatch_to_fd(req, fd, NULL);
  248     
  249     if (ret == NE_OK && ne_get_status(req)->klass != 2) {
  250     ret = NE_ERROR;
  251     }
  252 
  253     ne_request_destroy(req);
  254 
  255     return ret;
  256 }
  257 
  258 int ne_get_content_type(ne_request *req, ne_content_type *ct)
  259 {
  260     const char *value;
  261     char *sep, *stype;
  262 
  263     value = ne_get_response_header(req, "Content-Type");
  264     if (value == NULL || strchr(value, '/') == NULL) {
  265         return -1;
  266     }
  267 
  268     ct->value = ne_strdup(value);
  269     
  270     stype = strchr(ct->value, '/');
  271 
  272     *stype++ = '\0';
  273     ct->type = ct->value;
  274     ct->charset = NULL;
  275     
  276     sep = strchr(stype, ';');
  277 
  278     if (sep) {
  279     char *tok;
  280     /* look for the charset parameter. TODO; probably better to
  281      * hand-carve a parser than use ne_token/strstr/shave here. */
  282     *sep++ = '\0';
  283     do {
  284         tok = ne_qtoken(&sep, ';', "\"\'");
  285         if (tok) {
  286         tok = strstr(tok, "charset=");
  287         if (tok)
  288             ct->charset = ne_shave(tok+8, "\"\'");
  289         } else {
  290         break;
  291         }
  292     } while (sep != NULL);
  293     }
  294 
  295     /* set subtype, losing any trailing whitespace */
  296     ct->subtype = ne_shave(stype, " \t");
  297     
  298     if (ct->charset == NULL && ne_strcasecmp(ct->type, "text") == 0) {
  299         /* 3280§3.1: text/xml without charset implies us-ascii. */
  300         if (ne_strcasecmp(ct->subtype, "xml") == 0)
  301             ct->charset = "us-ascii";
  302         /* 2616§3.7.1: subtypes of text/ default to charset ISO-8859-1. */
  303         else
  304             ct->charset = "ISO-8859-1";
  305     }
  306     
  307     return 0;
  308 }
  309 
  310 static const struct options_map {
  311     const char *name;
  312     unsigned int cap;
  313 } options_map[] = {
  314     { "1", NE_CAP_DAV_CLASS1 },
  315     { "2", NE_CAP_DAV_CLASS2 },
  316     { "3", NE_CAP_DAV_CLASS3 },
  317     { "<http://apache.org/dav/propset/fs/1>", NE_CAP_MODDAV_EXEC },
  318     { "access-control", NE_CAP_DAV_ACL },
  319     { "version-control", NE_CAP_VER_CONTROL },
  320     { "checkout-in-place", NE_CAP_CO_IN_PLACE },
  321     { "version-history", NE_CAP_VER_HISTORY },
  322     { "workspace", NE_CAP_WORKSPACE },
  323     { "update", NE_CAP_UPDATE },
  324     { "label", NE_CAP_LABEL },
  325     { "working-resource", NE_CAP_WORK_RESOURCE },
  326     { "merge", NE_CAP_MERGE },
  327     { "baseline", NE_CAP_BASELINE },
  328     { "version-controlled-collection", NE_CAP_VC_COLLECTION }
  329 };
  330 
  331 static void parse_dav_header(const char *value, unsigned int *caps)
  332 {
  333     char *tokens = ne_strdup(value), *pnt = tokens;
  334     
  335     *caps = 0;
  336 
  337     do {
  338         char *tok = ne_qtoken(&pnt, ',',  "\"'");
  339         unsigned n;
  340 
  341         if (!tok) break;
  342         
  343         tok = ne_shave(tok, " \r\t\n");
  344 
  345         for (n = 0; n < sizeof(options_map)/sizeof(options_map[0]); n++) {
  346             if (strcmp(tok, options_map[n].name) == 0) {
  347                 *caps |= options_map[n].cap;
  348             }
  349         }
  350     } while (pnt != NULL);
  351     
  352     ne_free(tokens);
  353 }
  354 
  355 int ne_options2(ne_session *sess, const char *uri, unsigned int *caps)
  356 {
  357     ne_request *req = ne_request_create(sess, "OPTIONS", uri);
  358     int ret = ne_request_dispatch(req);
  359     const char *header = ne_get_response_header(req, "DAV");
  360     
  361     if (header) parse_dav_header(header, caps);
  362  
  363     if (ret == NE_OK && ne_get_status(req)->klass != 2) {
  364     ret = NE_ERROR;
  365     }
  366     
  367     ne_request_destroy(req);
  368 
  369     return ret;
  370 }
  371 
  372 int ne_options(ne_session *sess, const char *path,
  373                ne_server_capabilities *caps)
  374 {
  375     int ret;
  376     unsigned int capmask = 0;
  377     
  378     memset(caps, 0, sizeof *caps);
  379 
  380     ret = ne_options2(sess, path, &capmask);
  381 
  382     caps->dav_class1 = capmask & NE_CAP_DAV_CLASS1 ? 1 : 0;
  383     caps->dav_class2 = capmask & NE_CAP_DAV_CLASS2 ? 1 : 0;
  384     caps->dav_executable = capmask & NE_CAP_MODDAV_EXEC ? 1 : 0;
  385     
  386     return ret;
  387 }
  388 
  389 #ifdef NE_HAVE_DAV
  390 
  391 void ne_add_depth_header(ne_request *req, int depth)
  392 {
  393     const char *value;
  394     switch(depth) {
  395     case NE_DEPTH_ZERO:
  396     value = "0";
  397     break;
  398     case NE_DEPTH_ONE:
  399     value = "1";
  400     break;
  401     default:
  402     value = "infinity";
  403     break;
  404     }
  405     ne_add_request_header(req, "Depth", value);
  406 }
  407 
  408 static int copy_or_move(ne_session *sess, int is_move, int overwrite,
  409             int depth, const char *src, const char *dest) 
  410 {
  411     ne_request *req = ne_request_create( sess, is_move?"MOVE":"COPY", src );
  412 
  413     /* 2518 S8.9.2 says only use Depth: infinity with MOVE. */
  414     if (!is_move) {
  415     ne_add_depth_header(req, depth);
  416     }
  417 
  418 #ifdef NE_HAVE_DAV
  419     if (is_move) {
  420     ne_lock_using_resource(req, src, NE_DEPTH_INFINITE);
  421     }
  422     ne_lock_using_resource(req, dest, NE_DEPTH_INFINITE);
  423     /* And we need to be able to add members to the destination's parent */
  424     ne_lock_using_parent(req, dest);
  425 #endif
  426 
  427     if (ne_get_session_flag(sess, NE_SESSFLAG_RFC4918)) {
  428         ne_add_request_header(req, "Destination", dest);
  429     }
  430     else {
  431         ne_print_request_header(req, "Destination", "%s://%s%s", 
  432                                 ne_get_scheme(sess), 
  433                                 ne_get_server_hostport(sess), dest);
  434     }
  435     
  436     ne_add_request_header(req, "Overwrite", overwrite?"T":"F");
  437 
  438     return ne_simple_request(sess, req);
  439 }
  440 
  441 int ne_copy(ne_session *sess, int overwrite, int depth,
  442          const char *src, const char *dest) 
  443 {
  444     return copy_or_move(sess, 0, overwrite, depth, src, dest);
  445 }
  446 
  447 int ne_move(ne_session *sess, int overwrite,
  448          const char *src, const char *dest) 
  449 {
  450     return copy_or_move(sess, 1, overwrite, 0, src, dest);
  451 }
  452 
  453 /* Deletes the specified resource. (and in only two lines of code!) */
  454 int ne_delete(ne_session *sess, const char *uri) 
  455 {
  456     ne_request *req = ne_request_create(sess, "DELETE", uri);
  457 
  458 #ifdef NE_HAVE_DAV
  459     ne_lock_using_resource(req, uri, NE_DEPTH_INFINITE);
  460     ne_lock_using_parent(req, uri);
  461 #endif
  462     
  463     /* joe: I asked on the DAV WG list about whether we might get a
  464      * 207 error back from a DELETE... conclusion, you shouldn't if
  465      * you don't send the Depth header, since we might be an HTTP/1.1
  466      * client and a 2xx response indicates success to them.  But
  467      * it's all a bit unclear. In any case, DAV servers today do
  468      * return 207 to DELETE even if we don't send the Depth header.
  469      * So we handle 207 errors appropriately. */
  470 
  471     return ne_simple_request(sess, req);
  472 }
  473 
  474 int ne_mkcol(ne_session *sess, const char *uri) 
  475 {
  476     ne_request *req;
  477     char *real_uri;
  478     int ret;
  479 
  480     if (ne_path_has_trailing_slash(uri)) {
  481     real_uri = ne_strdup(uri);
  482     } else {
  483     real_uri = ne_concat(uri, "/", NULL);
  484     }
  485 
  486     req = ne_request_create(sess, "MKCOL", real_uri);
  487 
  488 #ifdef NE_HAVE_DAV
  489     ne_lock_using_resource(req, real_uri, 0);
  490     ne_lock_using_parent(req, real_uri);
  491 #endif
  492     
  493     ret = ne_simple_request(sess, req);
  494 
  495     ne_free(real_uri);
  496 
  497     return ret;
  498 }
  499 
  500 #endif /* NE_HAVE_DAV */