iconv: Bail out of the loop when an illegal sequence of bytes occurs.
[elinks/elinks-j605.git] / src / protocol / protocol.c
blobaecfd098a37b46d69c8f5944ce91c0427717863c
1 /* Protocol implementation manager. */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <stdlib.h>
8 #include <string.h>
10 #include "elinks.h"
12 #include "bfu/dialog.h"
13 #include "document/view.h"
14 #include "ecmascript/ecmascript.h"
15 #include "intl/gettext/libintl.h"
16 #include "main/module.h"
17 #include "network/connection.h"
18 #include "protocol/protocol.h"
19 #include "protocol/uri.h"
20 #include "session/session.h"
21 #include "terminal/terminal.h"
22 #include "terminal/window.h"
23 #include "util/memory.h"
24 #include "util/string.h"
26 /* Backends dynamic area: */
28 #include "protocol/about.h"
29 #include "protocol/auth/auth.h"
30 #include "protocol/bittorrent/bittorrent.h"
31 #include "protocol/bittorrent/connection.h"
32 #include "protocol/data.h"
33 #include "protocol/file/cgi.h"
34 #include "protocol/file/file.h"
35 #include "protocol/file/mailcap.h"
36 #include "protocol/finger/finger.h"
37 #include "protocol/fsp/fsp.h"
38 #include "protocol/ftp/ftp.h"
39 #include "protocol/gopher/gopher.h"
40 #include "protocol/http/http.h"
41 #include "protocol/nntp/connection.h"
42 #include "protocol/nntp/nntp.h"
43 #include "protocol/rewrite/rewrite.h"
44 #include "protocol/smb/smb.h"
45 #include "protocol/user.h"
48 struct protocol_backend {
49 unsigned char *name;
50 int port;
51 protocol_handler_T *handler;
52 unsigned int need_slashes:1;
53 unsigned int need_slash_after_host:1;
54 unsigned int free_syntax:1;
55 unsigned int need_ssl:1;
56 unsigned int keep_double_slashes:1;
59 static const struct protocol_backend protocol_backends[] = {
60 { "about", 0, about_protocol_handler, 0, 0, 1, 0, 1 },
61 { "bittorrent", 0, bittorrent_protocol_handler, 0, 0, 1, 0, 1 },
62 { "bittorrent-peer",0,bittorrent_peer_protocol_handler, 1, 1, 0, 0, 1 },
63 { "data", 0, data_protocol_handler, 0, 0, 1, 0, 1 },
64 { "file", 0, file_protocol_handler, 1, 0, 0, 0, 0 },
65 { "finger", 79, finger_protocol_handler, 1, 1, 0, 0, 1 },
66 { "fsp", 21, fsp_protocol_handler, 1, 1, 0, 0, 1 },
67 { "ftp", 21, ftp_protocol_handler, 1, 1, 0, 0, 0 },
68 { "gopher", 70, gopher_protocol_handler, 1, 1, 0, 0, 1 },
69 { "http", 80, http_protocol_handler, 1, 1, 0, 0, 1 },
70 { "https", 443, https_protocol_handler, 1, 1, 0, 1, 1 },
71 { "javascript", 0, NULL, 0, 0, 1, 0, 1 },
72 { "mailcap", 0, mailcap_protocol_handler, 0, 0, 1, 0, 0 },
73 { "news", 0, news_protocol_handler, 0, 0, 1, 0, 1 },
74 { "nntp", 119, nntp_protocol_handler, 1, 1, 0, 0, 0 },
75 { "nntps", 563, nntp_protocol_handler, 1, 1, 0, 1, 0 },
76 { "proxy", 3128, proxy_protocol_handler, 1, 1, 0, 0, 1 },
77 { "smb", 139, smb_protocol_handler, 1, 1, 0, 0, 1 },
78 { "snews", 0, news_protocol_handler, 0, 0, 1, 0, 1 },
80 /* Keep these last! */
81 { NULL, 0, NULL, 0, 0, 1, 0, 1 },
83 { "user", 0, NULL, 0, 0, 0, 0, 1 },
84 /* Internal protocol for mapping to protocol.user.* handlers. Placed
85 * last because it's checked first and else should be ignored. */
86 { "custom", 0, NULL, 0, 0, 1, 0, 1 },
90 /* This function gets called quite a lot these days. With incremental rendering
91 * and all I counted 4400 calls alone when loading fm. With the old linear
92 * comparison this would lead to 30800 comparison against protocol names. The
93 * binary search used currently reduces it to 4400 (meaning fm only has HTTP
94 * links). */
96 enum protocol
97 get_protocol(unsigned char *name, int namelen)
99 /* These are really enum protocol values but can take on negative
100 * values and since 0 <= -1 for enum values it's better to use clean
101 * integer type. */
102 int start, end;
103 enum protocol protocol;
105 /* Almost dichotomic search is used here */
106 /* Starting at the HTTP entry which is the most common that will make
107 * file and NNTP the next entries checked and amongst the third checks
108 * are proxy and FTP. */
109 start = 0;
110 end = PROTOCOL_UNKNOWN - 1;
111 protocol = PROTOCOL_HTTP;
113 assert(start <= protocol && protocol <= end);
115 while (start <= end) {
116 unsigned char *pname = protocol_backends[protocol].name;
117 int pnamelen = strlen(pname);
118 int minlen = int_min(pnamelen, namelen);
119 int compare = c_strncasecmp(pname, name, minlen);
121 if (compare == 0) {
122 if (pnamelen == namelen)
123 return protocol;
125 /* If the current protocol name is longer than the
126 * protocol name being searched for move @end else move
127 * @start. */
128 compare = pnamelen > namelen ? 1 : -1;
131 if (compare > 0)
132 end = protocol - 1;
133 else
134 start = protocol + 1;
136 protocol = (start + end) / 2;
138 /* Custom (protocol.user) protocol has higher precedence than builtin
139 * handlers, but we will check for it when following a link.
140 * Calling get_user_program for every link is too expensive. --witekfl */
141 /* TODO: In order to fully give higher precedence to user chosen
142 * protocols we have to get some terminal to pass along. */
144 if (get_user_program(NULL, name, namelen))
145 return PROTOCOL_USER;
147 return PROTOCOL_UNKNOWN;
151 #define VALID_PROTOCOL(p) (0 <= (p) && (p) < PROTOCOL_BACKENDS)
154 get_protocol_port(enum protocol protocol)
156 assert(VALID_PROTOCOL(protocol));
157 if_assert_failed return 0;
159 assert(uri_port_is_valid(protocol_backends[protocol].port));
160 if_assert_failed return 0;
162 return protocol_backends[protocol].port;
166 get_protocol_need_slashes(enum protocol protocol)
168 assert(VALID_PROTOCOL(protocol));
169 if_assert_failed return 0;
170 return protocol_backends[protocol].need_slashes;
174 get_protocol_need_slash_after_host(enum protocol protocol)
176 assert(VALID_PROTOCOL(protocol));
177 if_assert_failed return 0;
178 return protocol_backends[protocol].need_slash_after_host;
182 get_protocol_keep_double_slashes(enum protocol protocol)
184 assert(VALID_PROTOCOL(protocol));
185 if_assert_failed return 0;
186 return protocol_backends[protocol].keep_double_slashes;
190 get_protocol_free_syntax(enum protocol protocol)
192 assert(VALID_PROTOCOL(protocol));
193 if_assert_failed return 0;
194 return protocol_backends[protocol].free_syntax;
198 get_protocol_need_ssl(enum protocol protocol)
200 assert(VALID_PROTOCOL(protocol));
201 if_assert_failed return 0;
202 return protocol_backends[protocol].need_ssl;
205 protocol_handler_T *
206 get_protocol_handler(enum protocol protocol)
208 assert(VALID_PROTOCOL(protocol));
209 if_assert_failed return NULL;
210 return protocol_backends[protocol].handler;
214 static void
215 generic_external_protocol_handler(struct session *ses, struct uri *uri)
217 /* [gettext_accelerator_context(generic_external_protocol_handler)] */
218 struct connection_state state;
220 switch (uri->protocol) {
221 case PROTOCOL_JAVASCRIPT:
222 #ifdef CONFIG_ECMASCRIPT
223 ecmascript_protocol_handler(ses, uri);
224 return;
225 #else
226 state = connection_state(S_NO_JAVASCRIPT);
227 #endif
228 break;
230 case PROTOCOL_UNKNOWN:
231 state = connection_state(S_UNKNOWN_PROTOCOL);
232 break;
234 default:
235 #ifndef CONFIG_SSL
236 if (get_protocol_need_ssl(uri->protocol)) {
237 state = connection_state(S_SSL_ERROR);
238 break;
240 #endif
241 msg_box(ses->tab->term, NULL, MSGBOX_FREE_TEXT,
242 N_("Error"), ALIGN_CENTER,
243 msg_text(ses->tab->term,
244 N_("This version of ELinks does not contain "
245 "%s protocol support"),
246 protocol_backends[uri->protocol].name),
247 ses, 1,
248 MSG_BOX_BUTTON(N_("~OK"), NULL, B_ENTER | B_ESC));
249 return;
252 print_error_dialog(ses, state, uri, PRI_CANCEL);
255 protocol_external_handler_T *
256 get_protocol_external_handler(struct terminal *term, struct uri *uri)
258 unsigned char *prog;
260 assert(uri && VALID_PROTOCOL(uri->protocol));
261 if_assert_failed return NULL;
263 prog = get_user_program(term, struri(uri), uri->protocollen);
264 if (prog && *prog)
265 return user_protocol_handler;
267 if (!protocol_backends[uri->protocol].handler)
268 return generic_external_protocol_handler;
270 return NULL;
274 static union option_info protocol_options[] = {
275 INIT_OPT_TREE("", N_("Protocols"),
276 "protocol", OPT_SORT,
277 N_("Protocol specific options.")),
279 INIT_OPT_STRING("protocol", N_("No-proxy domains"),
280 "no_proxy", 0, "",
281 N_("Comma separated list of domains for which the proxy "
282 "(HTTP/FTP) should be disabled. Optionally, a port can be "
283 "specified for some domains as well. If it's blank, "
284 "NO_PROXY environment variable is checked as well.")),
286 NULL_OPTION_INFO,
288 static struct module *protocol_submodules[] = {
289 &auth_module,
290 #ifdef CONFIG_BITTORRENT
291 &bittorrent_protocol_module,
292 #endif
293 &file_protocol_module,
294 #ifdef CONFIG_CGI
295 &cgi_protocol_module,
296 #endif
297 #ifdef CONFIG_FINGER
298 &finger_protocol_module,
299 #endif
300 #ifdef CONFIG_FSP
301 &fsp_protocol_module,
302 #endif
303 #ifdef CONFIG_FTP
304 &ftp_protocol_module,
305 #endif
306 #ifdef CONFIG_GOPHER
307 &gopher_protocol_module,
308 #endif
309 &http_protocol_module,
310 #ifdef CONFIG_NNTP
311 &nntp_protocol_module,
312 #endif
313 #ifdef CONFIG_SMB
314 &smb_protocol_module,
315 #endif
316 #ifdef CONFIG_URI_REWRITE
317 &uri_rewrite_module,
318 #endif
319 &user_protocol_module,
320 NULL,
323 struct module protocol_module = struct_module(
324 /* name: */ N_("Protocol"),
325 /* options: */ protocol_options,
326 /* hooks: */ NULL,
327 /* submodules: */ protocol_submodules,
328 /* data: */ NULL,
329 /* init: */ NULL,
330 /* done: */ NULL