Explicit cast to (const char *) in strcasestr for C++
[elinks.git] / src / protocol / proxy.c
blob96e4f0e71fbf712430e95eef35bb14d81918ac6c
1 /* Proxy handling */
3 #ifndef _GNU_SOURCE
4 #define _GNU_SOURCE /* XXX: we _WANT_ strcasestr() ! */
5 #endif
7 #ifdef HAVE_CONFIG_H
8 #include "config.h"
9 #endif
11 #include <stdlib.h>
12 #include <string.h>
14 #include "elinks.h"
16 #include "config/options.h"
17 #include "main/event.h"
18 #include "network/connection.h"
19 #include "network/state.h"
20 #include "protocol/protocol.h"
21 #include "protocol/proxy.h"
22 #include "protocol/uri.h"
23 #include "util/memory.h"
24 #include "util/string.h"
27 static int
28 proxy_probe_no_proxy(unsigned char *url, unsigned char *no_proxy)
30 unsigned char *slash = strchr((const char *)url, '/');
32 if (slash) *slash = '\0';
34 while (no_proxy && *no_proxy) {
35 unsigned char *jumper = strchr((const char *)no_proxy, ',');
37 skip_space(no_proxy);
38 if (jumper) *jumper = '\0';
40 if (c_strcasestr((const char *)url, (const char *)no_proxy)) {
41 if (jumper) *jumper = ',';
42 if (slash) *slash = '/';
43 return 1;
45 no_proxy = jumper;
46 if (jumper) {
47 *jumper = ',';
48 no_proxy++;
52 if (slash) *slash = '/';
53 return 0;
56 static struct uri *
57 proxy_uri(struct uri *uri, unsigned char *proxy,
58 struct connection_state *error_state)
60 struct string string;
62 if (init_string(&string)
63 && string_concat(&string, "proxy://", proxy, "/",
64 (unsigned char *) NULL)
65 && add_uri_to_string(&string, uri, URI_BASE)) {
66 /* There is no need to use URI_BASE when calling get_uri()
67 * because URI_BASE should not add any fragments in the first
68 * place. */
69 uri = get_uri(string.source, 0);
70 /* XXX: Assume the problem is due to @proxy having bad format.
71 * This is a lot faster easier than checking the format. */
72 if (!uri)
73 *error_state = connection_state(S_PROXY_ERROR);
74 } else {
75 uri = NULL;
76 *error_state = connection_state(S_OUT_OF_MEM);
79 done_string(&string);
80 return uri;
83 static unsigned char *
84 strip_proxy_protocol(unsigned char *proxy,
85 unsigned char *strip1, unsigned char *strip2)
87 assert(proxy && *proxy);
89 if (!c_strncasecmp(proxy, strip1, strlen(strip1)))
90 proxy += strlen(strip1);
91 else if (strip2 && !c_strncasecmp(proxy, strip2, strlen(strip2)))
92 proxy += strlen(strip2);
94 return proxy;
97 /* TODO: We could of course significantly simplify the calling convention by
98 * autogenerating most of the parameters from protocol name. Having a function
99 * exported by protocol/protocol.* dedicated to that would be nice too.
100 * --pasky */
101 static unsigned char *
102 get_protocol_proxy(unsigned char *opt,
103 unsigned char *env1, unsigned char *env2,
104 unsigned char *strip1, unsigned char *strip2)
106 unsigned char *proxy;
108 proxy = get_opt_str(opt, NULL);
109 if (!*proxy) proxy = getenv(env1);
110 if (!proxy || !*proxy) proxy = getenv(env2);
112 if (proxy && *proxy) {
113 proxy = strip_proxy_protocol(proxy, strip1, strip2);
116 return proxy;
119 static struct uri *
120 get_proxy_worker(struct uri *uri, unsigned char *proxy,
121 struct connection_state *error_state)
123 unsigned char *protocol_proxy = NULL;
125 if (proxy) {
126 if (*proxy) {
127 proxy = strip_proxy_protocol(proxy, "http://", "ftp://");
129 return proxy_uri(uri, proxy, error_state);
132 /* "" from script_hook_get_proxy() */
133 return get_composed_uri(uri, URI_BASE);
136 switch (uri->protocol) {
137 case PROTOCOL_HTTP:
138 protocol_proxy = get_protocol_proxy("protocol.http.proxy.host",
139 "HTTP_PROXY", "http_proxy",
140 "http://", NULL);
141 break;
143 case PROTOCOL_HTTPS:
144 /* As Timo Lindfors explains, the communication between ELinks
145 * and the proxy server is never encrypted, altho the proxy
146 * might be used to transfer encrypted data between Web client
147 * and Web server. (Some proxy servers might allow encrypted
148 * communication between the Web client and the proxy
149 * but ELinks does not support that.) */
150 /* So, don't check whether the URI for the proxy begins
151 * with "https://" but rather check for "http://".
152 * Maybe we should allow either -- ELinks uses HTTP
153 * to communicate with the proxy when we use it for FTP, but we
154 * check for "ftp://" below; and what about 'be liberal in what
155 * you accept' (altho that is usually applied to data received
156 * from remote systems, not to user input)? -- Miciah */
157 protocol_proxy = get_protocol_proxy("protocol.https.proxy.host",
158 "HTTPS_PROXY", "https_proxy",
159 "http://", NULL);
160 break;
162 case PROTOCOL_FTP:
163 protocol_proxy = get_protocol_proxy("protocol.ftp.proxy.host",
164 "FTP_PROXY", "ftp_proxy",
165 "ftp://", "http://");
166 break;
169 if (protocol_proxy && *protocol_proxy) {
170 unsigned char *no_proxy;
171 unsigned char *slash = strchr((const char *)protocol_proxy, '/');
173 if (slash) *slash = 0;
175 no_proxy = get_opt_str("protocol.no_proxy", NULL);
176 if (!*no_proxy) no_proxy = getenv("NO_PROXY");
177 if (!no_proxy || !*no_proxy) no_proxy = getenv("no_proxy");
179 if (!proxy_probe_no_proxy(uri->host, no_proxy))
180 return proxy_uri(uri, protocol_proxy, error_state);
183 return get_composed_uri(uri, URI_BASE);
186 struct uri *
187 get_proxy_uri(struct uri *uri, struct connection_state *error_state)
189 if (uri->protocol == PROTOCOL_PROXY) {
190 return get_composed_uri(uri, URI_BASE);
191 } else {
192 #ifdef CONFIG_SCRIPTING
193 unsigned char *tmp = NULL;
194 static int get_proxy_event_id = EVENT_NONE;
196 set_event_id(get_proxy_event_id, "get-proxy");
197 trigger_event(get_proxy_event_id, &tmp, struri(uri));
199 uri = get_proxy_worker(uri, tmp, error_state);
200 mem_free_if(tmp);
201 return uri;
202 #else
203 return get_proxy_worker(uri, NULL, error_state);
204 #endif
208 struct uri *
209 get_proxied_uri(struct uri *uri)
211 if (uri->protocol == PROTOCOL_PROXY)
212 return get_uri(uri->data, URI_BASE);
214 return get_composed_uri(uri, URI_BASE);