1 /* Protocol implementation manager. */
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/finger/finger.h"
36 #include "protocol/fsp/fsp.h"
37 #include "protocol/ftp/ftp.h"
38 #include "protocol/gopher/gopher.h"
39 #include "protocol/http/http.h"
40 #include "protocol/nntp/connection.h"
41 #include "protocol/nntp/nntp.h"
42 #include "protocol/rewrite/rewrite.h"
43 #include "protocol/smb/smb.h"
44 #include "protocol/user.h"
47 struct protocol_backend
{
50 protocol_handler_T
*handler
;
51 unsigned int need_slashes
:1;
52 unsigned int need_slash_after_host
:1;
53 unsigned int free_syntax
:1;
54 unsigned int need_ssl
:1;
55 unsigned int keep_double_slashes
:1;
58 static const struct protocol_backend protocol_backends
[] = {
59 { "about", 0, about_protocol_handler
, 0, 0, 1, 0, 1 },
60 { "bittorrent", 0, bittorrent_protocol_handler
, 0, 0, 1, 0, 1 },
61 { "bittorrent-peer",0,bittorrent_peer_protocol_handler
, 1, 1, 0, 0, 1 },
62 { "data", 0, data_protocol_handler
, 0, 0, 1, 0, 1 },
63 { "file", 0, file_protocol_handler
, 1, 0, 0, 0, 0 },
64 { "finger", 79, finger_protocol_handler
, 1, 1, 0, 0, 1 },
65 { "fsp", 21, fsp_protocol_handler
, 1, 1, 0, 0, 1 },
66 { "ftp", 21, ftp_protocol_handler
, 1, 1, 0, 0, 0 },
67 { "gopher", 70, gopher_protocol_handler
, 1, 1, 0, 0, 1 },
68 { "http", 80, http_protocol_handler
, 1, 1, 0, 0, 1 },
69 { "https", 443, https_protocol_handler
, 1, 1, 0, 1, 1 },
70 { "javascript", 0, NULL
, 0, 0, 1, 0, 1 },
71 { "news", 0, news_protocol_handler
, 0, 0, 1, 0, 1 },
72 { "nntp", 119, nntp_protocol_handler
, 1, 1, 0, 0, 0 },
73 { "nntps", 563, nntp_protocol_handler
, 1, 1, 0, 1, 0 },
74 { "proxy", 3128, proxy_protocol_handler
, 1, 1, 0, 0, 1 },
75 { "smb", 139, smb_protocol_handler
, 1, 1, 0, 0, 1 },
76 { "snews", 0, news_protocol_handler
, 0, 0, 1, 0, 1 },
78 /* Keep these last! */
79 { NULL
, 0, NULL
, 0, 0, 1, 0, 1 },
81 { "user", 0, NULL
, 0, 0, 0, 0, 1 },
82 /* Internal protocol for mapping to protocol.user.* handlers. Placed
83 * last because it's checked first and else should be ignored. */
84 { "custom", 0, NULL
, 0, 0, 1, 0, 1 },
88 /* This function gets called quite a lot these days. With incremental rendering
89 * and all I counted 4400 calls alone when loading fm. With the old linear
90 * comparison this would lead to 30800 comparison against protocol names. The
91 * binary search used currently reduces it to 4400 (meaning fm only has HTTP
95 get_protocol(unsigned char *name
, int namelen
)
97 /* These are really enum protocol values but can take on negative
98 * values and since 0 <= -1 for enum values it's better to use clean
101 enum protocol protocol
;
103 /* Almost dichotomic search is used here */
104 /* Starting at the HTTP entry which is the most common that will make
105 * file and NNTP the next entries checked and amongst the third checks
106 * are proxy and FTP. */
108 end
= PROTOCOL_UNKNOWN
- 1;
109 protocol
= PROTOCOL_HTTP
;
111 assert(start
<= protocol
&& protocol
<= end
);
113 while (start
<= end
) {
114 unsigned char *pname
= protocol_backends
[protocol
].name
;
115 int pnamelen
= strlen(pname
);
116 int minlen
= int_min(pnamelen
, namelen
);
117 int compare
= c_strncasecmp(pname
, name
, minlen
);
120 if (pnamelen
== namelen
)
123 /* If the current protocol name is longer than the
124 * protocol name being searched for move @end else move
126 compare
= pnamelen
> namelen
? 1 : -1;
132 start
= protocol
+ 1;
134 protocol
= (start
+ end
) / 2;
136 /* Custom (protocol.user) protocol has higher precedence than builtin
137 * handlers, but we will check for it when following a link.
138 * Calling get_user_program for every link is too expensive. --witekfl */
139 /* TODO: In order to fully give higher precedence to user chosen
140 * protocols we have to get some terminal to pass along. */
142 if (get_user_program(NULL
, name
, namelen
))
143 return PROTOCOL_USER
;
145 return PROTOCOL_UNKNOWN
;
149 #define VALID_PROTOCOL(p) (0 <= (p) && (p) < PROTOCOL_BACKENDS)
152 get_protocol_port(enum protocol protocol
)
154 assert(VALID_PROTOCOL(protocol
));
155 if_assert_failed
return 0;
157 assert(uri_port_is_valid(protocol_backends
[protocol
].port
));
158 if_assert_failed
return 0;
160 return protocol_backends
[protocol
].port
;
164 get_protocol_need_slashes(enum protocol protocol
)
166 assert(VALID_PROTOCOL(protocol
));
167 if_assert_failed
return 0;
168 return protocol_backends
[protocol
].need_slashes
;
172 get_protocol_need_slash_after_host(enum protocol protocol
)
174 assert(VALID_PROTOCOL(protocol
));
175 if_assert_failed
return 0;
176 return protocol_backends
[protocol
].need_slash_after_host
;
180 get_protocol_keep_double_slashes(enum protocol protocol
)
182 assert(VALID_PROTOCOL(protocol
));
183 if_assert_failed
return 0;
184 return protocol_backends
[protocol
].keep_double_slashes
;
188 get_protocol_free_syntax(enum protocol protocol
)
190 assert(VALID_PROTOCOL(protocol
));
191 if_assert_failed
return 0;
192 return protocol_backends
[protocol
].free_syntax
;
196 get_protocol_need_ssl(enum protocol protocol
)
198 assert(VALID_PROTOCOL(protocol
));
199 if_assert_failed
return 0;
200 return protocol_backends
[protocol
].need_ssl
;
204 get_protocol_handler(enum protocol protocol
)
206 assert(VALID_PROTOCOL(protocol
));
207 if_assert_failed
return NULL
;
208 return protocol_backends
[protocol
].handler
;
213 generic_external_protocol_handler(struct session
*ses
, struct uri
*uri
)
215 /* [gettext_accelerator_context(generic_external_protocol_handler)] */
216 struct connection_state state
;
218 switch (uri
->protocol
) {
219 case PROTOCOL_JAVASCRIPT
:
220 #ifdef CONFIG_ECMASCRIPT
221 ecmascript_protocol_handler(ses
, uri
);
224 state
= connection_state(S_NO_JAVASCRIPT
);
228 case PROTOCOL_UNKNOWN
:
229 state
= connection_state(S_UNKNOWN_PROTOCOL
);
234 if (get_protocol_need_ssl(uri
->protocol
)) {
235 state
= connection_state(S_SSL_ERROR
);
239 msg_box(ses
->tab
->term
, NULL
, MSGBOX_FREE_TEXT
,
240 N_("Error"), ALIGN_CENTER
,
241 msg_text(ses
->tab
->term
,
242 N_("This version of ELinks does not contain "
243 "%s protocol support"),
244 protocol_backends
[uri
->protocol
].name
),
246 MSG_BOX_BUTTON(N_("~OK"), NULL
, B_ENTER
| B_ESC
));
250 print_error_dialog(ses
, state
, uri
, PRI_CANCEL
);
253 protocol_external_handler_T
*
254 get_protocol_external_handler(struct terminal
*term
, struct uri
*uri
)
258 assert(uri
&& VALID_PROTOCOL(uri
->protocol
));
259 if_assert_failed
return NULL
;
261 prog
= get_user_program(term
, struri(uri
), uri
->protocollen
);
263 return user_protocol_handler
;
265 if (!protocol_backends
[uri
->protocol
].handler
)
266 return generic_external_protocol_handler
;
272 static struct option_info protocol_options
[] = {
273 INIT_OPT_TREE("", N_("Protocols"),
274 "protocol", OPT_SORT
,
275 N_("Protocol specific options.")),
277 INIT_OPT_STRING("protocol", N_("No-proxy domains"),
279 N_("Comma separated list of domains for which the proxy (HTTP/FTP)\n"
280 "should be disabled. Optionally, a port can be specified for some\n"
281 "domains as well. If it's blank, NO_PROXY environment variable is\n"
282 "checked as well.")),
286 static struct module
*protocol_submodules
[] = {
288 #ifdef CONFIG_BITTORRENT
289 &bittorrent_protocol_module
,
291 &file_protocol_module
,
293 &cgi_protocol_module
,
296 &finger_protocol_module
,
299 &fsp_protocol_module
,
302 &ftp_protocol_module
,
305 &gopher_protocol_module
,
307 &http_protocol_module
,
309 &nntp_protocol_module
,
312 &smb_protocol_module
,
314 #ifdef CONFIG_URI_REWRITE
317 &user_protocol_module
,
321 struct module protocol_module
= struct_module(
322 /* name: */ N_("Protocol"),
323 /* options: */ protocol_options
,
325 /* submodules: */ protocol_submodules
,