"Fossies" - the Fresh Open Source Software Archive

Member "sitecopy-0.16.6/lib/neon/ne_props.c" (30 Jan 2008, 18874 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_props.c" see the Fossies "Dox" file reference documentation.

    1 /* 
    2    WebDAV property manipulation
    3    Copyright (C) 2000-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 #ifdef HAVE_STDLIB_H
   25 #include <stdlib.h>
   26 #endif
   27 #ifdef HAVE_STRING_H
   28 #include <string.h>
   29 #endif
   30 
   31 #include "ne_alloc.h"
   32 #include "ne_xml.h"
   33 #include "ne_props.h"
   34 #include "ne_basic.h"
   35 #include "ne_locks.h"
   36 #include "ne_internal.h"
   37 
   38 /* don't store flat props with a value > 10K */
   39 #define MAX_FLATPROP_LEN (102400)
   40 
   41 struct ne_propfind_handler_s {
   42     ne_session *sess;
   43     ne_request *request;
   44 
   45     int has_props; /* whether we've already written some
   46             * props to the body. */
   47     ne_buffer *body;
   48     
   49     ne_207_parser *parser207;
   50     ne_xml_parser *parser;
   51 
   52     /* Creator/destructor callbacks. */
   53     ne_props_create_complex creator;
   54     ne_props_destroy_complex destructor;
   55     void *cd_userdata;
   56     
   57     /* Current propset, or NULL if none being processed. */
   58     ne_prop_result_set *current;
   59 
   60     ne_buffer *value; /* current flat property value */
   61     int depth; /* nesting depth within a flat property */
   62 
   63     ne_props_result callback;
   64     void *userdata;
   65 };
   66 
   67 #define ELM_flatprop (NE_207_STATE_TOP - 1)
   68 
   69 /* We build up the results of one 'response' element in memory. */
   70 struct prop {
   71     char *name, *nspace, *value, *lang;
   72     /* Store a ne_propname here too, for convienience.  pname.name =
   73      * name, pname.nspace = nspace, but they are const'ed in pname. */
   74     ne_propname pname;
   75 };
   76 
   77 #define NSPACE(x) ((x) ? (x) : "")
   78 
   79 struct propstat {
   80     struct prop *props;
   81     int numprops;
   82     ne_status status;
   83 };
   84 
   85 /* Results set. */
   86 struct ne_prop_result_set_s {
   87     struct propstat *pstats;
   88     int numpstats, counter;
   89     void *private;
   90     ne_uri uri;
   91 };
   92 
   93 #define MAX_PROP_COUNTER (1024)
   94 
   95 static int 
   96 startelm(void *userdata, int state, const char *name, const char *nspace,
   97      const char **atts);
   98 static int 
   99 endelm(void *userdata, int state, const char *name, const char *nspace);
  100 
  101 /* Handle character data; flat property value. */
  102 static int chardata(void *userdata, int state, const char *data, size_t len)
  103 {
  104     ne_propfind_handler *hdl = userdata;
  105 
  106     if (state == ELM_flatprop && hdl->value->length < MAX_FLATPROP_LEN)
  107         ne_buffer_append(hdl->value, data, len);
  108 
  109     return 0;
  110 }
  111 
  112 ne_xml_parser *ne_propfind_get_parser(ne_propfind_handler *handler)
  113 {
  114     return handler->parser;
  115 }
  116 
  117 ne_request *ne_propfind_get_request(ne_propfind_handler *handler)
  118 {
  119     return handler->request;
  120 }
  121 
  122 static int propfind(ne_propfind_handler *handler, 
  123             ne_props_result results, void *userdata)
  124 {
  125     int ret;
  126     ne_request *req = handler->request;
  127 
  128     /* Register the flat property handler to catch any properties 
  129      * which the user isn't handling as 'complex'. */
  130     ne_xml_push_handler(handler->parser, startelm, chardata, endelm, handler);
  131 
  132     handler->callback = results;
  133     handler->userdata = userdata;
  134 
  135     ne_set_request_body_buffer(req, handler->body->data,
  136                    ne_buffer_size(handler->body));
  137 
  138     ne_add_request_header(req, "Content-Type", NE_XML_MEDIA_TYPE);
  139     
  140     ne_add_response_body_reader(req, ne_accept_207, ne_xml_parse_v, 
  141                   handler->parser);
  142 
  143     ret = ne_request_dispatch(req);
  144 
  145     if (ret == NE_OK && ne_get_status(req)->klass != 2) {
  146     ret = NE_ERROR;
  147     } else if (ne_xml_failed(handler->parser)) {
  148     ne_set_error(handler->sess, "%s", ne_xml_get_error(handler->parser));
  149     ret = NE_ERROR;
  150     }
  151 
  152     return ret;
  153 }
  154 
  155 static void set_body(ne_propfind_handler *hdl, const ne_propname *names)
  156 {
  157     ne_buffer *body = hdl->body;
  158     int n;
  159     
  160     if (!hdl->has_props) {
  161     ne_buffer_czappend(body, "<prop>\n");
  162     hdl->has_props = 1;
  163     }
  164 
  165     for (n = 0; names[n].name != NULL; n++) {
  166     ne_buffer_concat(body, "<", names[n].name, " xmlns=\"", 
  167              NSPACE(names[n].nspace), "\"/>\n", NULL);
  168     }
  169 
  170 }
  171 
  172 int ne_propfind_allprop(ne_propfind_handler *handler, 
  173              ne_props_result results, void *userdata)
  174 {
  175     ne_buffer_czappend(handler->body, "<allprop/></propfind>\n");
  176     return propfind(handler, results, userdata);
  177 }
  178 
  179 int ne_propfind_named(ne_propfind_handler *handler, const ne_propname *props,
  180                ne_props_result results, void *userdata)
  181 {
  182     set_body(handler, props);
  183     ne_buffer_czappend(handler->body, "</prop></propfind>\n");
  184     return propfind(handler, results, userdata);
  185 }
  186 
  187 
  188 /* The easy one... PROPPATCH */
  189 int ne_proppatch(ne_session *sess, const char *uri, 
  190          const ne_proppatch_operation *items)
  191 {
  192     ne_request *req = ne_request_create(sess, "PROPPATCH", uri);
  193     ne_buffer *body = ne_buffer_create();
  194     int n, ret;
  195     
  196     /* Create the request body */
  197     ne_buffer_czappend(body, "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
  198                        "<D:propertyupdate xmlns:D=\"DAV:\">");
  199 
  200     for (n = 0; items[n].name != NULL; n++) {
  201     const char *elm = (items[n].type == ne_propset) ? "set" : "remove";
  202 
  203     /* <set><prop><prop-name>value</prop-name></prop></set> */
  204     ne_buffer_concat(body, "<D:", elm, "><D:prop>"
  205              "<", items[n].name->name, NULL);
  206     
  207     if (items[n].name->nspace) {
  208         ne_buffer_concat(body, " xmlns=\"", items[n].name->nspace, "\"", NULL);
  209     }
  210 
  211     if (items[n].type == ne_propset) {
  212         ne_buffer_concat(body, ">", items[n].value, NULL);
  213     } else {
  214         ne_buffer_append(body, ">", 1);
  215     }
  216 
  217     ne_buffer_concat(body, "</", items[n].name->name, "></D:prop></D:", elm, 
  218                          ">\n", NULL);
  219     }   
  220 
  221     ne_buffer_czappend(body, "</D:propertyupdate>\n");
  222 
  223     ne_set_request_body_buffer(req, body->data, ne_buffer_size(body));
  224     ne_add_request_header(req, "Content-Type", NE_XML_MEDIA_TYPE);
  225     
  226 #ifdef NE_HAVE_DAV
  227     ne_lock_using_resource(req, uri, NE_DEPTH_ZERO);
  228 #endif
  229 
  230     ret = ne_simple_request(sess, req);
  231     
  232     ne_buffer_destroy(body);
  233 
  234     return ret;
  235 }
  236 
  237 /* Compare two property names. */
  238 static int pnamecmp(const ne_propname *pn1, const ne_propname *pn2)
  239 {
  240     if (pn1->nspace == NULL && pn2->nspace != NULL) {
  241     return 1;
  242     } else if (pn1->nspace != NULL && pn2->nspace == NULL) {
  243     return -1;
  244     } else if (pn1->nspace == NULL) {
  245     return strcmp(pn1->name, pn2->name);
  246     } else {
  247     return (strcmp(pn1->nspace, pn2->nspace) ||
  248         strcmp(pn1->name, pn2->name));
  249     }
  250 }
  251 
  252 /* Find property in 'set' with name 'pname'.  If found, set pstat_ret
  253  * to the containing propstat, likewise prop_ret, and returns zero.
  254  * If not found, returns non-zero.  */
  255 static int findprop(const ne_prop_result_set *set, const ne_propname *pname,
  256             struct propstat **pstat_ret, struct prop **prop_ret)
  257 {
  258     
  259     int ps, p;
  260 
  261     for (ps = 0; ps < set->numpstats; ps++) {
  262     for (p = 0; p < set->pstats[ps].numprops; p++) {
  263         struct prop *prop = &set->pstats[ps].props[p];
  264 
  265         if (pnamecmp(&prop->pname, pname) == 0) {
  266         if (pstat_ret != NULL)
  267             *pstat_ret = &set->pstats[ps];
  268         if (prop_ret != NULL)
  269             *prop_ret = prop;
  270         return 0;
  271         }
  272     }
  273     }
  274 
  275     return -1;
  276 }
  277 
  278 const char *ne_propset_value(const ne_prop_result_set *set,
  279                   const ne_propname *pname)
  280 {
  281     struct prop *prop;
  282     
  283     if (findprop(set, pname, NULL, &prop)) {
  284     return NULL;
  285     } else {
  286     return prop->value;
  287     }
  288 }
  289 
  290 const char *ne_propset_lang(const ne_prop_result_set *set,
  291                  const ne_propname *pname)
  292 {
  293     struct prop *prop;
  294 
  295     if (findprop(set, pname, NULL, &prop)) {
  296     return NULL;
  297     } else {
  298     return prop->lang;
  299     }
  300 }
  301 
  302 void *ne_propfind_current_private(ne_propfind_handler *handler)
  303 {
  304     return handler->current ? handler->current->private : NULL;
  305 }
  306 
  307 void *ne_propset_private(const ne_prop_result_set *set)
  308 {
  309     return set->private;
  310 }
  311 
  312 int ne_propset_iterate(const ne_prop_result_set *set,
  313             ne_propset_iterator iterator, void *userdata)
  314 {
  315     int ps, p;
  316 
  317     for (ps = 0; ps < set->numpstats; ps++) {
  318     for (p = 0; p < set->pstats[ps].numprops; p++) {
  319         struct prop *prop = &set->pstats[ps].props[p];
  320         int ret = iterator(userdata, &prop->pname, prop->value, 
  321                    &set->pstats[ps].status);
  322         if (ret)
  323         return ret;
  324 
  325     }
  326     }
  327 
  328     return 0;
  329 }
  330 
  331 const ne_status *ne_propset_status(const ne_prop_result_set *set,
  332                       const ne_propname *pname)
  333 {
  334     struct propstat *pstat;
  335     
  336     if (findprop(set, pname, &pstat, NULL)) {
  337     /* TODO: it is tempting to return a dummy status object here
  338      * rather than NULL, which says "Property result was not given
  339      * by server."  but I'm not sure if this is best left to the
  340      * client.  */
  341     return NULL;
  342     } else {
  343     return &pstat->status;
  344     }
  345 }
  346 
  347 static void *start_response(void *userdata, const ne_uri *uri)
  348 {
  349     ne_prop_result_set *set = ne_calloc(sizeof(*set));
  350     ne_propfind_handler *hdl = userdata;
  351 
  352     ne_uri_copy(&set->uri, uri);
  353 
  354     if (hdl->creator) {
  355     set->private = hdl->creator(hdl->cd_userdata, &set->uri);
  356     }
  357 
  358     hdl->current = set;
  359 
  360     return set;
  361 }
  362 
  363 static void *start_propstat(void *userdata, void *response)
  364 {
  365     ne_prop_result_set *set = response;
  366     ne_propfind_handler *hdl = userdata;
  367     struct propstat *pstat;
  368     int n;
  369 
  370     if (++hdl->current->counter == MAX_PROP_COUNTER) {
  371         ne_xml_set_error(hdl->parser, _("Response exceeds maximum property count"));
  372         return NULL;
  373     }
  374     
  375     n = set->numpstats;
  376     set->pstats = ne_realloc(set->pstats, sizeof(struct propstat) * (n+1));
  377     set->numpstats = n+1;
  378 
  379     pstat = &set->pstats[n];
  380     memset(pstat, 0, sizeof(*pstat));
  381     
  382     /* And return this as the new pstat. */
  383     return &set->pstats[n];
  384 }
  385 
  386 static int startelm(void *userdata, int parent,
  387                     const char *nspace, const char *name, const char **atts)
  388 {
  389     ne_propfind_handler *hdl = userdata;
  390     struct propstat *pstat = ne_207_get_current_propstat(hdl->parser207);
  391     struct prop *prop;
  392     int n;
  393     const char *lang;
  394 
  395     /* Just handle all children of propstat and their descendants. */
  396     if ((parent != NE_207_STATE_PROP && parent != ELM_flatprop) 
  397         || pstat == NULL)
  398         return NE_XML_DECLINE;
  399 
  400     if (parent == ELM_flatprop) {
  401         /* collecting the flatprop value. */
  402         hdl->depth++;
  403         if (hdl->value->used < MAX_FLATPROP_LEN) {
  404             const char **a = atts;
  405 
  406             ne_buffer_concat(hdl->value, "<", nspace, name, NULL);
  407             
  408             while (a[0] && hdl->value->used < MAX_FLATPROP_LEN) {
  409                 const char *nsep = strchr(a[0], ':'), *pfx;
  410 
  411                 /* Resolve the attribute namespace prefix, if any.
  412                  * Ignore a failure to resolve the namespace prefix. */
  413                 pfx = nsep ? ne_xml_resolve_nspace(hdl->parser,
  414                                                    a[0], nsep - a[0]) : NULL;
  415                 
  416                 if (pfx) {
  417                     ne_buffer_concat(hdl->value, " ", pfx, nsep + 1, "='", 
  418                                      a[1], "'", NULL);
  419                 }
  420                 else {
  421                     ne_buffer_concat(hdl->value, " ", a[0], "='", a[1], "'", NULL);
  422                 }
  423                 a += 2;
  424             }
  425 
  426             ne_buffer_czappend(hdl->value, ">");
  427         }
  428 
  429         return ELM_flatprop;
  430     }        
  431 
  432     /* Enforce maximum number of properties per resource to prevent a
  433      * memory exhaustion attack by a hostile server. */
  434     if (++hdl->current->counter == MAX_PROP_COUNTER) {
  435         ne_xml_set_error(hdl->parser, _("Response exceeds maximum property count"));
  436         return NE_XML_ABORT;
  437     }
  438 
  439     /* Add a property to this propstat */
  440     n = pstat->numprops;
  441 
  442     pstat->props = ne_realloc(pstat->props, sizeof(struct prop) * (n + 1));
  443     pstat->numprops = n+1;
  444 
  445     /* Fill in the new property. */
  446     prop = &pstat->props[n];
  447 
  448     prop->pname.name = prop->name = ne_strdup(name);
  449     if (nspace[0] == '\0') {
  450     prop->pname.nspace = prop->nspace = NULL;
  451     } else {
  452     prop->pname.nspace = prop->nspace = ne_strdup(nspace);
  453     }
  454     prop->value = NULL;
  455 
  456     NE_DEBUG(NE_DBG_XML, "Got property #%d: {%s}%s.\n", n, 
  457          NSPACE(prop->nspace), prop->name);
  458 
  459     /* This is under discussion at time of writing (April '01), and it
  460      * looks like we need to retrieve the xml:lang property from any
  461      * element here or above.
  462      *
  463      * Also, I think we might need attribute namespace handling here.  */
  464     lang = ne_xml_get_attr(hdl->parser, atts, NULL, "xml:lang");
  465     if (lang != NULL) {
  466     prop->lang = ne_strdup(lang);
  467     NE_DEBUG(NE_DBG_XML, "Property language is %s\n", prop->lang);
  468     } else {
  469     prop->lang = NULL;
  470     }
  471 
  472     hdl->depth = 0;
  473 
  474     return ELM_flatprop;
  475 }
  476 
  477 static int endelm(void *userdata, int state,
  478                   const char *nspace, const char *name)
  479 {
  480     ne_propfind_handler *hdl = userdata;
  481     struct propstat *pstat = ne_207_get_current_propstat(hdl->parser207);
  482     int n;
  483 
  484     if (hdl->depth > 0) {
  485         /* nested. */
  486         if (hdl->value->used < MAX_FLATPROP_LEN)
  487             ne_buffer_concat(hdl->value, "</", nspace, name, ">", NULL);
  488         hdl->depth--;
  489     } else {
  490         /* end of the current property value */
  491         n = pstat->numprops - 1;
  492         pstat->props[n].value = ne_buffer_finish(hdl->value);
  493         hdl->value = ne_buffer_create();
  494     }
  495     return 0;
  496 }
  497 
  498 static void end_propstat(void *userdata, void *pstat_v, 
  499              const ne_status *status,
  500              const char *description)
  501 {
  502     struct propstat *pstat = pstat_v;
  503 
  504     /* Nothing to do if no status was given. */
  505     if (!status) return;
  506 
  507     /* If we get a non-2xx response back here, we wipe the value for
  508      * each of the properties in this propstat, so the caller knows to
  509      * look at the status instead. It's annoying, since for each prop
  510      * we will have done an unnecessary strdup("") above, but there is
  511      * no easy way round that given the fact that we don't know
  512      * whether we've got an error or not till after we get the
  513      * property element.
  514      *
  515      * Interestingly IIS breaks the 2518 DTD and puts the status
  516      * element first in the propstat. This is useful since then we
  517      * *do* know whether each subsequent empty prop element means, but
  518      * we can't rely on that here. */
  519     if (status->klass != 2) {
  520     int n;
  521     
  522     for (n = 0; n < pstat->numprops; n++) {
  523         ne_free(pstat->props[n].value);
  524         pstat->props[n].value = NULL;
  525     }
  526     }
  527 
  528     /* copy the status structure, and dup the reason phrase. */
  529     pstat->status = *status;
  530     pstat->status.reason_phrase = ne_strdup(status->reason_phrase);
  531 }
  532 
  533 /* Frees up a results set */
  534 static void free_propset(ne_propfind_handler *handler,
  535                          ne_prop_result_set *set)
  536 {
  537     int n;
  538     
  539     if (handler->destructor && set->private) {
  540         handler->destructor(handler->cd_userdata, set->private);
  541     }
  542 
  543     for (n = 0; n < set->numpstats; n++) {
  544     int m;
  545     struct propstat *p = &set->pstats[n];
  546 
  547     for (m = 0; m < p->numprops; m++) {
  548             if (p->props[m].nspace) ne_free(p->props[m].nspace);
  549             ne_free(p->props[m].name);
  550             if (p->props[m].lang) ne_free(p->props[m].lang);
  551             if (p->props[m].value) ne_free(p->props[m].value);
  552             p->props[m].nspace = p->props[m].lang = 
  553                 p->props[m].value = NULL;
  554     }
  555 
  556     if (p->status.reason_phrase)
  557         ne_free(p->status.reason_phrase);
  558     if (p->props)
  559         ne_free(p->props);
  560     }
  561 
  562     if (set->pstats)
  563     ne_free(set->pstats);
  564     ne_uri_free(&set->uri);
  565     ne_free(set);
  566 }
  567 
  568 static void end_response(void *userdata, void *resource,
  569              const ne_status *status,
  570              const char *description)
  571 {
  572     ne_propfind_handler *handler = userdata;
  573     ne_prop_result_set *set = resource;
  574 
  575     /* Pass back the results for this resource. */
  576     if (handler->callback && set->numpstats > 0)
  577     handler->callback(handler->userdata, &set->uri, set);
  578 
  579     /* Clean up the propset tree we've just built. */
  580     free_propset(handler, set);
  581     handler->current = NULL;
  582 }
  583 
  584 ne_propfind_handler *
  585 ne_propfind_create(ne_session *sess, const char *uri, int depth)
  586 {
  587     ne_propfind_handler *ret = ne_calloc(sizeof(ne_propfind_handler));
  588     ne_uri base = {0};
  589 
  590     ne_fill_server_uri(sess, &base);
  591     base.path = ne_strdup(uri);
  592 
  593     ret->parser = ne_xml_create();
  594     ret->parser207 = ne_207_create(ret->parser, &base, ret);
  595     ret->sess = sess;
  596     ret->body = ne_buffer_create();
  597     ret->request = ne_request_create(sess, "PROPFIND", uri);
  598     ret->value = ne_buffer_create();
  599 
  600     ne_add_depth_header(ret->request, depth);
  601 
  602     ne_207_set_response_handlers(ret->parser207, 
  603                   start_response, end_response);
  604 
  605     ne_207_set_propstat_handlers(ret->parser207, start_propstat,
  606                   end_propstat);
  607 
  608     /* The start of the request body is fixed: */
  609     ne_buffer_czappend(ret->body, 
  610                        "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" 
  611                        "<propfind xmlns=\"DAV:\">");
  612 
  613     ne_uri_free(&base);
  614 
  615     return ret;
  616 }
  617 
  618 /* Destroy a propfind handler */
  619 void ne_propfind_destroy(ne_propfind_handler *handler)
  620 {
  621     ne_buffer_destroy(handler->value);
  622     if (handler->current)
  623         free_propset(handler, handler->current);
  624     ne_207_destroy(handler->parser207);
  625     ne_xml_destroy(handler->parser);
  626     ne_buffer_destroy(handler->body);
  627     ne_request_destroy(handler->request);
  628     ne_free(handler);    
  629 }
  630 
  631 int ne_simple_propfind(ne_session *sess, const char *href, int depth,
  632             const ne_propname *props,
  633             ne_props_result results, void *userdata)
  634 {
  635     ne_propfind_handler *hdl;
  636     int ret;
  637 
  638     hdl = ne_propfind_create(sess, href, depth);
  639     if (props != NULL) {
  640     ret = ne_propfind_named(hdl, props, results, userdata);
  641     } else {
  642     ret = ne_propfind_allprop(hdl, results, userdata);
  643     }
  644     
  645     ne_propfind_destroy(hdl);
  646     
  647     return ret;
  648 }
  649 
  650 int ne_propnames(ne_session *sess, const char *href, int depth,
  651           ne_props_result results, void *userdata)
  652 {
  653     ne_propfind_handler *hdl;
  654     int ret;
  655 
  656     hdl = ne_propfind_create(sess, href, depth);
  657 
  658     ne_buffer_czappend(hdl->body, "<propname/></propfind>");
  659 
  660     ret = propfind(hdl, results, userdata);
  661 
  662     ne_propfind_destroy(hdl);
  663 
  664     return ret;
  665 }
  666 
  667 void ne_propfind_set_private(ne_propfind_handler *hdl,
  668                              ne_props_create_complex creator,
  669                              ne_props_destroy_complex destructor,
  670                              void *userdata)
  671 {
  672     hdl->creator = creator;
  673     hdl->destructor = destructor;
  674     hdl->cd_userdata = userdata;
  675 }