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 
41 struct redirect {
42     char *requri;
43     int valid; /* non-zero if .uri contains a redirect */
44     ne_uri uri;
45     ne_session *sess;
46 };
47 
48 static void
create(ne_request * req,void * session,const char * method,const char * uri)49 create(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 
post_send(ne_request * req,void * private,const ne_status * status)59 static 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 
free_redirect(void * cookie)110 static 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 
ne_redirect_register(ne_session * sess)119 void 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 
ne_redirect_location(ne_session * sess)132 const 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