Explicit cast in strstr for C++
[elinks.git] / src / protocol / user.c
blobfda527046922cd4c6b20b4f55ec716cc5985f375
1 /* Internal "mailto", "telnet", "tn3270" and misc. protocol implementation */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <ctype.h>
8 #include <stdlib.h>
9 #include <stdio.h>
10 #ifdef HAVE_UNISTD_H
11 #include <unistd.h>
12 #endif
14 #include "elinks.h"
16 #include "bfu/dialog.h"
17 #include "config/options.h"
18 #include "intl/gettext/libintl.h"
19 #include "main/module.h"
20 #include "osdep/osdep.h"
21 #include "protocol/uri.h"
22 #include "protocol/user.h"
23 #include "session/download.h"
24 #include "session/session.h"
25 #include "terminal/terminal.h"
26 #include "terminal/window.h"
27 #include "util/conv.h"
28 #include "util/file.h"
29 #include "util/memory.h"
30 #include "util/string.h"
33 static union option_info user_protocol_options[] = {
34 INIT_OPT_TREE("protocol", N_("User protocols"),
35 "user", OPT_AUTOCREATE,
36 N_("User protocols. Options in this tree specify external "
37 "handlers for the appropriate protocols. Ie. "
38 "protocol.user.mailto.unix.")),
40 /* FIXME: Poorly designed options structure. Ought to be able to specify
41 * need_slashes, free_form and similar options as well :-(. --pasky */
43 /* Basically, it looks like protocol.user.mailto.win32 = "blah" */
45 INIT_OPT_TREE("protocol.user", NULL,
46 "_template_", OPT_AUTOCREATE,
47 N_("Handler (external program) for this protocol. Name the "
48 "options in this tree after your system (ie. unix, "
49 "unix-xwin).")),
51 INIT_OPT_STRING("protocol.user._template_", NULL,
52 "_template_", 0, "",
53 N_("Handler (external program) for this protocol and system.\n"
54 "%f in the string means file name to include form data from\n"
55 "%h in the string means hostname (or email address)\n"
56 "%p in the string means port\n"
57 "%d in the string means path (everything after the port)\n"
58 "%s in the string means subject (?subject=<this>)\n"
59 "%u in the string means the whole URL")),
61 #define INIT_OPT_USER_PROTOCOL(scheme, system, cmd) \
62 INIT_OPT_STRING("protocol.user." scheme, NULL, system, 0, cmd, NULL)
64 #ifndef CONFIG_GOPHER
65 INIT_OPT_USER_PROTOCOL("gopher", "unix", DEFAULT_AC_OPT_GOPHER),
66 INIT_OPT_USER_PROTOCOL("gopher", "unix-xwin", DEFAULT_AC_OPT_GOPHER),
67 #endif
68 INIT_OPT_USER_PROTOCOL("irc", "unix", DEFAULT_AC_OPT_IRC),
69 INIT_OPT_USER_PROTOCOL("irc", "unix-xwin", DEFAULT_AC_OPT_IRC),
70 INIT_OPT_USER_PROTOCOL("mailto", "unix", DEFAULT_AC_OPT_MAILTO),
71 INIT_OPT_USER_PROTOCOL("mailto", "unix-xwin", DEFAULT_AC_OPT_MAILTO),
72 #ifndef CONFIG_NNTP
73 INIT_OPT_USER_PROTOCOL("news", "unix", DEFAULT_AC_OPT_NEWS),
74 INIT_OPT_USER_PROTOCOL("news", "unix-xwin", DEFAULT_AC_OPT_NEWS),
75 #endif
76 INIT_OPT_USER_PROTOCOL("telnet", "unix", DEFAULT_AC_OPT_TELNET),
77 INIT_OPT_USER_PROTOCOL("telnet", "unix-xwin", DEFAULT_AC_OPT_TELNET),
78 INIT_OPT_USER_PROTOCOL("tn3270", "unix", DEFAULT_AC_OPT_TN3270),
79 INIT_OPT_USER_PROTOCOL("tn3270", "unix-xwin", DEFAULT_AC_OPT_TN3270),
81 NULL_OPTION_INFO,
84 struct module user_protocol_module = struct_module(
85 /* name: */ N_("User protocols"),
86 /* options: */ user_protocol_options,
87 /* hooks: */ NULL,
88 /* submodules: */ NULL,
89 /* data: */ NULL,
90 /* init: */ NULL,
91 /* done: */ NULL
95 unsigned char *
96 get_user_program(struct terminal *term, unsigned char *progid, int progidlen)
98 struct option *opt;
99 int xwin = term ? term->environment & ENV_XWIN : 0;
100 struct string name;
102 if (!init_string(&name)) return NULL;
104 add_to_string(&name, "protocol.user.");
106 /* Now add lowercased progid part. Delicious. */
107 add_bytes_to_string(&name, progid, progidlen);
108 convert_to_lowercase_locale_indep(&name.source[sizeof("protocol.user.") - 1], progidlen);
110 add_char_to_string(&name, '.');
111 add_to_string(&name, get_system_str(xwin));
113 opt = get_opt_rec_real(config_options, name.source);
115 done_string(&name);
116 return (unsigned char *) (opt ? opt->value.string : NULL);
120 static unsigned char *
121 subst_cmd(unsigned char *cmd, struct uri *uri, unsigned char *subj,
122 unsigned char *formfile)
124 struct string string;
126 if (!init_string(&string)) return NULL;
128 while (*cmd) {
129 int p;
131 for (p = 0; cmd[p] && cmd[p] != '%'; p++);
133 add_bytes_to_string(&string, cmd, p);
134 cmd += p;
136 if (*cmd != '%') break;
138 cmd++;
139 /* TODO: Decode URI fragments before adding them. --jonas */
140 switch (*cmd) {
141 case 'u':
143 unsigned char *url = struri(uri);
144 int length = get_real_uri_length(uri);
146 add_shell_safe_to_string(&string, url, length);
147 break;
149 case 'h':
150 /* TODO For some user protocols it would be
151 * better if substitution of each uri
152 * field was completely configurable. Now
153 * @host contains both the uri username
154 * field, (password field) and hostname
155 * field because it is useful for mailto
156 * protocol handling. */
157 /* It would break a lot of configurations so I
158 * don't know. --jonas */
159 if (uri->userlen && uri->hostlen) {
160 int hostlen = uri->host + uri->hostlen - uri->user;
162 add_shell_safe_to_string(&string, uri->user,
163 hostlen);
164 } else if (uri->host) {
165 add_shell_safe_to_string(&string, uri->host,
166 uri->hostlen);
168 break;
169 case 'p':
170 if (uri->portlen)
171 add_shell_safe_to_string(&string, uri->port,
172 uri->portlen);
173 break;
174 case 'd':
175 if (uri->datalen)
176 add_shell_safe_to_string(&string, uri->data,
177 uri->datalen);
178 break;
179 case 's':
180 if (subj)
181 add_shell_safe_to_string(&string, subj,
182 strlen(subj));
183 break;
184 case 'f':
185 if (formfile)
186 add_to_string(&string, formfile);
187 break;
188 default:
189 add_bytes_to_string(&string, cmd - 1, 2);
190 break;
192 if (*cmd) cmd++;
195 return string.source;
198 /* Stay silent about complete RFC 2368 support or do it yourself! ;-).
199 * --pasky */
200 static unsigned char *
201 get_subject_from_query(unsigned char *query)
203 unsigned char *subject;
205 if (strncmp(query, "subject=", 8)) {
206 subject = strstr((const char *)query, "&subject=");
207 if (!subject) return NULL;
208 subject += 9;
209 } else {
210 subject = query + 8;
213 /* Return subject until next '&'-value or end of string */
214 return memacpy(subject, strcspn(subject, "&"));
217 static unsigned char *
218 save_form_data_to_file(struct uri *uri)
220 unsigned char *filename = get_tempdir_filename("elinks-XXXXXX");
221 int fd;
222 FILE *fp;
223 size_t nmemb, len;
224 unsigned char *formdata;
226 if (!filename) return NULL;
228 fd = safe_mkstemp(filename);
229 if (fd < 0) {
230 mem_free(filename);
231 return NULL;
234 if (!uri->post) return filename;
236 /* Jump the content type */
237 formdata = strchr((const char *)uri->post, '\n');
238 formdata = formdata ? formdata + 1 : uri->post;
239 len = strlen(formdata);
240 if (len == 0) return filename;
242 fp = fdopen(fd, "w");
243 if (!fp) {
245 error:
246 unlink(filename);
247 mem_free(filename);
248 close(fd);
249 return NULL;
252 nmemb = fwrite(formdata, len, 1, fp);
253 if (nmemb != 1) {
254 fclose(fp);
255 goto error;
258 if (fclose(fp) != 0)
259 goto error;
261 return filename;
264 void
265 user_protocol_handler(struct session *ses, struct uri *uri)
267 unsigned char *subj = NULL, *prog;
268 unsigned char *filename;
270 prog = get_user_program(ses->tab->term, struri(uri), uri->protocollen);
271 if (!prog || !*prog) {
272 unsigned char *protocol = memacpy(struri(uri), uri->protocollen);
274 /* Shouldn't ever happen, but be paranoid. */
275 /* Happens when you're in X11 and you've no handler for it. */
276 info_box(ses->tab->term, MSGBOX_FREE_TEXT,
277 N_("No program"), ALIGN_CENTER,
278 msg_text(ses->tab->term,
279 N_("No program specified for protocol %s."),
280 empty_string_or_(protocol)));
282 mem_free_if(protocol);
283 return;
286 if (uri->data && uri->datalen) {
287 /* Some mailto specific stuff follows... */
288 unsigned char *query = get_uri_string(uri, URI_QUERY);
290 if (query) {
291 subj = get_subject_from_query(query);
292 mem_free(query);
293 if (subj) decode_uri(subj);
297 filename = save_form_data_to_file(uri);
299 prog = subst_cmd(prog, uri, subj, filename);
300 mem_free_if(subj);
301 if (prog) {
302 unsigned char *delete_ = empty_string_or_(filename);
304 exec_on_terminal(ses->tab->term, prog, delete_, TERM_EXEC_FG);
305 mem_free(prog);
307 } else if (filename) {
308 unlink(filename);
311 mem_free_if(filename);