1/*
2   HTTP-redirect support
3   Copyright (C) 1999-2007, 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_session.h"
32#include "ne_request.h"
33#include "ne_alloc.h"
34#include "ne_uri.h"
35#include "ne_redirect.h"
36#include "ne_internal.h"
37#include "ne_string.h"
38
39#define REDIRECT_ID "http://www.webdav.org/neon/hooks/http-redirect"
40
41struct redirect {
42    char *requri;
43    int valid; /* non-zero if .uri contains a redirect */
44    ne_uri uri;
45    ne_session *sess;
46};
47
48static void
49create(ne_request *req, void *session, const char *method, const char *uri)
50{
51    struct redirect *red = session;
52    if (red->requri) ne_free(red->requri);
53    red->requri = ne_strdup(uri);
54}
55
56#define REDIR(n) ((n) == 301 || (n) == 302 || (n) == 303 || \
57		  (n) == 307)
58
59static int post_send(ne_request *req, void *private, const ne_status *status)
60{
61    struct redirect *red = private;
62    const char *location = ne_get_response_header(req, "Location");
63    ne_buffer *path = NULL;
64    int ret;
65
66    /* Don't do anything for non-redirect status or no Location header. */
67    if (!REDIR(status->code) || location == NULL)
68	return NE_OK;
69
70    if (strstr(location, "://") == NULL && location[0] != '/') {
71	char *pnt;
72
73	path = ne_buffer_create();
74	ne_buffer_zappend(path, red->requri);
75	pnt = strrchr(path->data, '/');
76
77	if (pnt && pnt[1] != '\0') {
78	    /* Chop off last path segment. */
79	    pnt[1] = '\0';
80	    ne_buffer_altered(path);
81	}
82	ne_buffer_zappend(path, location);
83	location = path->data;
84    }
85
86    /* free last uri. */
87    ne_uri_free(&red->uri);
88
89    /* Parse the Location header */
90    if (ne_uri_parse(location, &red->uri) || red->uri.path == NULL) {
91        red->valid = 0;
92	ne_set_error(red->sess, _("Could not parse redirect destination URL"));
93        ret = NE_ERROR;
94    } else {
95        /* got a valid redirect. */
96        red->valid = 1;
97        ret = NE_REDIRECT;
98
99        if (!red->uri.host) {
100            /* Not an absoluteURI: breaks 2616 but everybody does it. */
101            ne_fill_server_uri(red->sess, &red->uri);
102        }
103    }
104
105    if (path) ne_buffer_destroy(path);
106
107    return ret;
108}
109
110static void free_redirect(void *cookie)
111{
112    struct redirect *red = cookie;
113    ne_uri_free(&red->uri);
114    if (red->requri)
115        ne_free(red->requri);
116    ne_free(red);
117}
118
119void ne_redirect_register(ne_session *sess)
120{
121    struct redirect *red = ne_calloc(sizeof *red);
122
123    red->sess = sess;
124
125    ne_hook_create_request(sess, create, red);
126    ne_hook_post_send(sess, post_send, red);
127    ne_hook_destroy_session(sess, free_redirect, red);
128
129    ne_set_session_private(sess, REDIRECT_ID, red);
130}
131
132const ne_uri *ne_redirect_location(ne_session *sess)
133{
134    struct redirect *red = ne_get_session_private(sess, REDIRECT_ID);
135
136    if (red && red->valid)
137        return &red->uri;
138    else
139        return NULL;
140}
141
142