Back to index

avfs  1.0.1
dav_basic.c
Go to the documentation of this file.
00001 /* 
00002    WebDAV Class 1 namespace operations and 207 error handling
00003    Copyright (C) 1999-2001, Joe Orton <joe@light.plus.com>
00004 
00005    This library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Library General Public
00007    License as published by the Free Software Foundation; either
00008    version 2 of the License, or (at your option) any later version.
00009    
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public
00016    License along with this library; if not, write to the Free
00017    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
00018    MA 02111-1307, USA
00019 
00020 */
00021 
00022 #include "config.h"
00023 
00024 #ifdef HAVE_STDLIB_H
00025 #include <stdlib.h>
00026 #endif
00027 
00028 #ifdef HAVE_STRING_H
00029 #include <string.h>
00030 #endif
00031 
00032 #include "http_request.h"
00033 
00034 #include "dav_basic.h"
00035 #include "uri.h" /* for uri_has_trailing_slash */
00036 #include "http_basic.h" /* for http_content_type */
00037 #include "string_utils.h" /* for sbuffer */
00038 #include "dav_207.h"
00039 #include "ne_alloc.h"
00040 
00041 #ifdef USE_DAV_LOCKS
00042 #include "dav_locks.h"
00043 #endif
00044 
00045 /* Handling of 207 errors: we keep a string buffer, and append
00046  * messages to it as they come down.
00047  *
00048  * Note, 424 means it would have worked but something else went wrong.
00049  * We will have had the error for "something else", so we display
00050  * that, and skip 424 errors. */
00051 
00052 /* This is passed as userdata to the 207 code. */
00053 struct context {
00054     char *href;
00055     sbuffer buf;
00056     unsigned int is_error;
00057 };
00058 
00059 static void *start_response(void *userdata, const char *href)
00060 {
00061     struct context *ctx = userdata;
00062     HTTP_FREE(ctx->href);
00063     ctx->href = ne_strdup(href);
00064     return NULL;
00065 }
00066 
00067 static void handle_error(struct context *ctx,
00068                       const char *status_line, const http_status *status,
00069                       const char *description)
00070 {
00071     if (status && status->klass != 2) {
00072        if (status->code != 424) {
00073            ctx->is_error = 1;
00074            sbuffer_concat(ctx->buf, ctx->href, ": ", status_line, "\n", NULL);
00075            if (description != NULL) {
00076               /* TODO: these can be multi-line. Would be good to
00077                * word-wrap this at col 80. */
00078               sbuffer_concat(ctx->buf, " -> ", description, "\n", NULL);
00079            }
00080        }
00081     }
00082 
00083 }
00084 
00085 static void end_response(void *userdata, void *response, const char *status_line,
00086                       const http_status *status, const char *description)
00087 {
00088     struct context *ctx = userdata;
00089     handle_error(ctx, status_line, status, description);
00090 }
00091 
00092 static void 
00093 end_propstat(void *userdata, void *propstat, const char *status_line,
00094             const http_status *status, const char *description)
00095 {
00096     struct context *ctx = userdata;
00097     handle_error(ctx, status_line, status, description);
00098 }
00099 
00100 void dav_add_depth_header(http_req *req, int depth)
00101 {
00102     const char *value;
00103     switch(depth) {
00104     case DAV_DEPTH_ZERO:
00105        value = "0";
00106        break;
00107     case DAV_DEPTH_ONE:
00108        value = "1";
00109        break;
00110     default:
00111        value = "infinity";
00112        break;
00113     }
00114     http_add_request_header(req, "Depth", value);
00115 }
00116 
00117 /* Dispatch a DAV request and handle a 207 error response appropriately */
00118 int dav_simple_request(http_session *sess, http_req *req)
00119 {
00120     int ret;
00121     http_content_type ctype = {0};
00122     struct context ctx = {0};
00123     dav_207_parser *p207;
00124     hip_xml_parser *p;
00125     
00126     p = hip_xml_create();
00127     p207 = dav_207_create(p, &ctx);
00128     /* The error string is progressively written into the
00129      * sbuffer by the element callbacks */
00130     ctx.buf = sbuffer_create();
00131 
00132     dav_207_set_response_handlers(p207, start_response, end_response);
00133     dav_207_set_propstat_handlers(p207, NULL, end_propstat);
00134     
00135     http_add_response_body_reader(req, dav_accept_207, hip_xml_parse_v, p);
00136     http_add_response_header_handler(req, "Content-Type", 
00137                                   http_content_type_handler, &ctype);
00138 
00139     dav_207_ignore_unknown(p207);
00140 
00141     ret = http_request_dispatch(req);
00142 
00143     if (ret == HTTP_OK) {
00144        if (http_get_status(req)->code == 207) {
00145            if (!hip_xml_valid(p)) { 
00146               /* The parse was invalid */
00147               http_set_error(sess, hip_xml_get_error(p));
00148               ret = HTTP_ERROR;
00149            } else if (ctx.is_error) {
00150               /* If we've actually got any error information
00151                * from the 207, then set that as the error */
00152               http_set_error(sess, sbuffer_data(ctx.buf));
00153               ret = HTTP_ERROR;
00154            }
00155        } else if (http_get_status(req)->klass != 2) {
00156            ret = HTTP_ERROR;
00157        }
00158     }
00159 
00160     HTTP_FREE(ctype.value);
00161     dav_207_destroy(p207);
00162     hip_xml_destroy(p);
00163     sbuffer_destroy(ctx.buf);
00164     HTTP_FREE(ctx.href);
00165 
00166     http_request_destroy(req);
00167 
00168     return ret;
00169 }
00170     
00171 static int copy_or_move(http_session *sess, int is_move, int overwrite,
00172                      const char *src, const char *dest ) 
00173 {
00174     http_req *req = http_request_create( sess, is_move?"MOVE":"COPY", src );
00175 
00176 #ifdef USE_DAV_LOCKS
00177     if (is_move) {
00178        dav_lock_using_resource(req, src, DAV_DEPTH_INFINITE);
00179     }
00180     dav_lock_using_resource(req, dest, DAV_DEPTH_INFINITE);
00181     /* And we need to be able to add members to the destination's parent */
00182     dav_lock_using_parent(req, dest);
00183 #endif
00184 
00185     http_print_request_header(req, "Destination", "%s://%s%s", 
00186                            http_get_scheme(sess), 
00187                            http_get_server_hostport(sess), dest);
00188     
00189     http_add_request_header(req, "Overwrite", overwrite?"T":"F");
00190 
00191     return dav_simple_request(sess, req);
00192 }
00193 
00194 int dav_copy(http_session *sess, int overwrite, 
00195             const char *src, const char *dest) 
00196 {
00197     return copy_or_move(sess, 0, overwrite, src, dest);
00198 }
00199 
00200 int dav_move(http_session *sess, int overwrite,
00201             const char *src, const char *dest) 
00202 {
00203     return copy_or_move(sess, 1, overwrite, src, dest);
00204 }
00205 
00206 /* Deletes the specified resource. (and in only two lines of code!) */
00207 int dav_delete(http_session *sess, const char *uri) 
00208 {
00209     http_req *req = http_request_create(sess, "DELETE", uri);
00210 
00211 #ifdef USE_DAV_LOCKS
00212     dav_lock_using_resource(req, uri, DAV_DEPTH_INFINITE);
00213     dav_lock_using_parent(req, uri);
00214 #endif
00215     
00216     /* joe: I asked on the DAV WG list about whether we might get a
00217      * 207 error back from a DELETE... conclusion, you shouldn't if
00218      * you don't send the Depth header, since we might be an HTTP/1.1
00219      * client and a 2xx response indicates success to them.  But
00220      * it's all a bit unclear. In any case, DAV servers today do
00221      * return 207 to DELETE even if we don't send the Depth header.
00222      * So we handle 207 errors appropriately. */
00223 
00224     return dav_simple_request(sess, req);
00225 }
00226 
00227 int dav_mkcol(http_session *sess, const char *uri) 
00228 {
00229     http_req *req;
00230     char *real_uri;
00231     int ret;
00232 
00233     if (uri_has_trailing_slash(uri)) {
00234        real_uri = ne_strdup(uri);
00235     } else {
00236        CONCAT2(real_uri, uri, "/");
00237     }
00238 
00239     req = http_request_create(sess, "MKCOL", real_uri);
00240 
00241 #ifdef USE_DAV_LOCKS
00242     dav_lock_using_resource(req, real_uri, 0);
00243     dav_lock_using_parent(req, real_uri);
00244 #endif
00245     
00246     ret = dav_simple_request(sess, req);
00247 
00248     free(real_uri);
00249 
00250     return ret;
00251 }