Back to index

avfs  1.0.1
http_redirect.c
Go to the documentation of this file.
00001 /* 
00002    HTTP-redirect support
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 #ifdef HAVE_STRING_H
00028 #include <string.h>
00029 #endif
00030 
00031 #include "http_request.h"
00032 #include "ne_alloc.h"
00033 #include "http_private.h"
00034 #include "http_redirect.h"
00035 #include "uri.h"
00036 #include "neon_i18n.h"
00037 
00038 struct redirect {
00039     char *location;
00040     http_req *req;
00041     http_redirect_confirm confirm;
00042     http_redirect_notify notify;
00043     void *userdata;
00044 };
00045 
00046 static void *create(void *session, http_req *req, 
00047                   const char *method, const char *uri);
00048 static int post_send(void *private, const http_status *status);
00049 static void destroy(void *private);
00050 
00051 http_request_hooks redirect_hooks = {
00052     "http://www.webdav.org/neon/hooks/http-redirect",
00053     create,
00054     NULL,
00055     NULL,
00056     post_send,
00057     destroy
00058 };
00059 
00060 static void *
00061 create(void *session, http_req *req, const char *method, const char *uri)
00062 {
00063     struct redirect *red = session;
00064     
00065     /* for handling 3xx redirects */
00066     http_add_response_header_handler(req, "Location",
00067                                  http_duplicate_header, &red->location);
00068 
00069     red->req = req;
00070 
00071     return red;
00072 }
00073 
00074 /* 2616 says we can't auto-redirect if the method is not GET or HEAD.
00075  * We extend this to PROPFIND too, which violates a 2616 MUST, but
00076  * is following the spirit of the spec, I think. */
00077 static int auto_redirect(struct redirect *red)
00078 {
00079     return (red->req->method_is_head ||
00080            strcasecmp(red->req->method, "GET") == 0 || 
00081            strcasecmp(red->req->method, "PROPFIND") == 0);
00082 }
00083 
00084 static int post_send(void *private, const http_status *status)
00085 {
00086     struct redirect *red = private;
00087     struct uri uri;
00088 
00089     if ((status->code != 302 && status->code != 301) ||
00090        red->location == NULL) {
00091        /* Nothing to do. */
00092        return HTTP_OK;
00093     }
00094     
00095     if (uri_parse(red->location, &uri, NULL)) {
00096        /* Couldn't parse the URI */
00097        http_set_error(red->req->session, 
00098                      _("Could not parse redirect location."));
00099        return HTTP_ERROR;
00100     }
00101     
00102     
00103     if (auto_redirect(red)) {
00104        if (red->notify != NULL) {
00105            (*red->notify)(red->userdata, red->req->abs_path, uri.path);
00106        }
00107     } else {
00108        /* Need user-confirmation to follow the redirect */
00109        if (red->confirm == NULL || 
00110            !(*red->confirm)(red->userdata, red->req->abs_path, uri.path)) {
00111            return HTTP_OK;
00112        }
00113     }
00114     
00115     red->req->abs_path = ne_strdup(uri.path);
00116 
00117     /* Set red->req->uri and new host/port for the session, if necessary */
00118     
00119     HTTP_FREE(red->req->uri);
00120     if (red->req->use_proxy)
00121        red->req->uri = ne_strdup(red->location);
00122     else
00123        red->req->uri = ne_strdup(red->req->abs_path);
00124     
00125     if (uri.host != NULL && 
00126        strcasecmp(uri.host, red->req->session->server.hostname) != 0) {
00127        /* Handle redirecting to another host. */
00128        
00129        if (uri.port <= 0)
00130            uri.port = HTTP_PORT;
00131        
00132        /* FIXME: shouldn't do this here.  Let the caller control
00133            this.  */
00134        http_session_server(red->req->session, uri.host, uri.port);
00135     }
00136 
00137     /* FIXME: need to uri_free here (the above http_session_server()
00138      * call prevents this at the moment). */
00139     
00140     return HTTP_RETRY;
00141 }
00142 
00143 static void destroy(void *private)
00144 {
00145     struct redirect *red = private;
00146     HTTP_FREE(red->location);
00147 }
00148 
00149 void http_redirect_register(http_session *sess, 
00150                          http_redirect_confirm confirm,
00151                          http_redirect_notify notify,
00152                          void *userdata)
00153 {
00154     struct redirect *red = ne_calloc(sizeof *red);
00155     
00156     red->confirm = confirm;
00157     red->notify = notify;
00158     red->userdata = userdata;
00159     
00160     http_add_hooks(sess, &redirect_hooks, red);
00161 }