fsp: Fixed a serious bug.
[elinks.git] / src / protocol / user.c
blobf0bcc5dbfddc41d69c644bcf48c743ee5f948d7a
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 struct 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\n"
37 "handlers for the appropriate protocols. Ie.\n"
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\n"
48 "options in this tree after your system (ie. unix, unix-xwin).")),
50 INIT_OPT_STRING("protocol.user._template_", NULL,
51 "_template_", 0, "",
52 N_("Handler (external program) for this protocol and system.\n"
53 "%f in the string means file name to include form data from\n"
54 "%h in the string means hostname (or email address)\n"
55 "%p in the string means port\n"
56 "%d in the string means path (everything after the port)\n"
57 "%s in the string means subject (?subject=<this>)\n"
58 "%u in the string means the whole URL")),
60 #define INIT_OPT_USER_PROTOCOL(scheme, system, cmd) \
61 INIT_OPT_STRING("protocol.user." scheme, NULL, system, 0, cmd, NULL)
63 #ifndef CONFIG_GOPHER
64 INIT_OPT_USER_PROTOCOL("gopher", "unix", DEFAULT_AC_OPT_GOPHER),
65 INIT_OPT_USER_PROTOCOL("gopher", "unix-xwin", DEFAULT_AC_OPT_GOPHER),
66 #endif
67 INIT_OPT_USER_PROTOCOL("irc", "unix", DEFAULT_AC_OPT_IRC),
68 INIT_OPT_USER_PROTOCOL("irc", "unix-xwin", DEFAULT_AC_OPT_IRC),
69 INIT_OPT_USER_PROTOCOL("mailto", "unix", DEFAULT_AC_OPT_MAILTO),
70 INIT_OPT_USER_PROTOCOL("mailto", "unix-xwin", DEFAULT_AC_OPT_MAILTO),
71 #ifndef CONFIG_NNTP
72 INIT_OPT_USER_PROTOCOL("news", "unix", DEFAULT_AC_OPT_NEWS),
73 INIT_OPT_USER_PROTOCOL("news", "unix-xwin", DEFAULT_AC_OPT_NEWS),
74 #endif
75 INIT_OPT_USER_PROTOCOL("telnet", "unix", DEFAULT_AC_OPT_TELNET),
76 INIT_OPT_USER_PROTOCOL("telnet", "unix-xwin", DEFAULT_AC_OPT_TELNET),
77 INIT_OPT_USER_PROTOCOL("tn3270", "unix", DEFAULT_AC_OPT_TN3270),
78 INIT_OPT_USER_PROTOCOL("tn3270", "unix-xwin", DEFAULT_AC_OPT_TN3270),
80 NULL_OPTION_INFO,
83 struct module user_protocol_module = struct_module(
84 /* name: */ N_("User protocols"),
85 /* options: */ user_protocol_options,
86 /* hooks: */ NULL,
87 /* submodules: */ NULL,
88 /* data: */ NULL,
89 /* init: */ NULL,
90 /* done: */ NULL
94 unsigned char *
95 get_user_program(struct terminal *term, unsigned char *progid, int progidlen)
97 struct option *opt;
98 int xwin = term ? term->environment & ENV_XWIN : 0;
99 struct string name;
101 if (!init_string(&name)) return NULL;
103 add_to_string(&name, "protocol.user.");
105 /* Now add lowercased progid part. Delicious. */
106 add_bytes_to_string(&name, progid, progidlen);
107 convert_to_lowercase(&name.source[sizeof("protocol.user.") - 1], progidlen);
109 add_char_to_string(&name, '.');
110 add_to_string(&name, get_system_str(xwin));
112 opt = get_opt_rec_real(config_options, name.source);
114 done_string(&name);
115 return (unsigned char *) (opt ? opt->value.string : NULL);
119 static unsigned char *
120 subst_cmd(unsigned char *cmd, struct uri *uri, unsigned char *subj,
121 unsigned char *formfile)
123 struct string string;
125 if (!init_string(&string)) return NULL;
127 while (*cmd) {
128 int p;
130 for (p = 0; cmd[p] && cmd[p] != '%'; p++);
132 add_bytes_to_string(&string, cmd, p);
133 cmd += p;
135 if (*cmd != '%') break;
137 cmd++;
138 /* TODO: Decode URI fragments before adding them. --jonas */
139 switch (*cmd) {
140 case 'u':
142 unsigned char *url = struri(uri);
143 int length = get_real_uri_length(uri);
145 add_shell_safe_to_string(&string, url, length);
146 break;
148 case 'h':
149 /* TODO For some user protocols it would be
150 * better if substitution of each uri
151 * field was completely configurable. Now
152 * @host contains both the uri username
153 * field, (password field) and hostname
154 * field because it is useful for mailto
155 * protocol handling. */
156 /* It would break a lot of configurations so I
157 * don't know. --jonas */
158 if (uri->userlen && uri->hostlen) {
159 int hostlen = uri->host + uri->hostlen - uri->user;
161 add_shell_safe_to_string(&string, uri->user,
162 hostlen);
163 } else if (uri->host) {
164 add_shell_safe_to_string(&string, uri->host,
165 uri->hostlen);
167 break;
168 case 'p':
169 if (uri->portlen)
170 add_shell_safe_to_string(&string, uri->port,
171 uri->portlen);
172 break;
173 case 'd':
174 if (uri->datalen)
175 add_shell_safe_to_string(&string, uri->data,
176 uri->datalen);
177 break;
178 case 's':
179 if (subj)
180 add_shell_safe_to_string(&string, subj,
181 strlen(subj));
182 break;
183 case 'f':
184 if (formfile)
185 add_to_string(&string, formfile);
186 break;
187 default:
188 add_bytes_to_string(&string, cmd - 1, 2);
189 break;
191 if (*cmd) cmd++;
194 return string.source;
197 /* Stay silent about complete RFC 2368 support or do it yourself! ;-).
198 * --pasky */
199 static unsigned char *
200 get_subject_from_query(unsigned char *query)
202 unsigned char *subject;
204 if (strncmp(query, "subject=", 8)) {
205 subject = strstr(query, "&subject=");
206 if (!subject) return NULL;
207 subject += 9;
208 } else {
209 subject = query + 8;
212 /* Return subject until next '&'-value or end of string */
213 return memacpy(subject, strcspn(subject, "&"));
216 static unsigned char *
217 save_form_data_to_file(struct uri *uri)
219 unsigned char *filename = get_tempdir_filename("elinks-XXXXXX");
220 int fd;
221 FILE *fp;
222 size_t nmemb, len;
223 unsigned char *formdata;
225 if (!filename) return NULL;
227 fd = safe_mkstemp(filename);
228 if (fd < 0) {
229 mem_free(filename);
230 return NULL;
233 if (!uri->post) return filename;
235 /* Jump the content type */
236 formdata = strchr(uri->post, '\n');
237 formdata = formdata ? formdata + 1 : uri->post;
238 len = strlen(formdata);
239 if (len == 0) return filename;
241 fp = fdopen(fd, "w");
242 if (!fp) {
244 error:
245 unlink(filename);
246 mem_free(filename);
247 close(fd);
248 return NULL;
251 nmemb = fwrite(formdata, len, 1, fp);
252 if (nmemb != 1) {
253 fclose(fp);
254 goto error;
257 if (fclose(fp) != 0)
258 goto error;
260 return filename;
263 void
264 user_protocol_handler(struct session *ses, struct uri *uri)
266 unsigned char *subj = NULL, *prog;
267 unsigned char *filename;
269 prog = get_user_program(ses->tab->term, struri(uri), uri->protocollen);
270 if (!prog || !*prog) {
271 unsigned char *protocol = memacpy(struri(uri), uri->protocollen);
273 /* Shouldn't ever happen, but be paranoid. */
274 /* Happens when you're in X11 and you've no handler for it. */
275 info_box(ses->tab->term, MSGBOX_FREE_TEXT,
276 N_("No program"), ALIGN_CENTER,
277 msg_text(ses->tab->term,
278 N_("No program specified for protocol %s."),
279 empty_string_or_(protocol)));
281 mem_free_if(protocol);
282 return;
285 if (uri->data && uri->datalen) {
286 /* Some mailto specific stuff follows... */
287 unsigned char *query = get_uri_string(uri, URI_QUERY);
289 if (query) {
290 subj = get_subject_from_query(query);
291 mem_free(query);
292 if (subj) decode_uri(subj);
296 filename = save_form_data_to_file(uri);
298 prog = subst_cmd(prog, uri, subj, filename);
299 mem_free_if(subj);
300 if (prog) {
301 unsigned char *delete = empty_string_or_(filename);
303 exec_on_terminal(ses->tab->term, prog, delete, TERM_EXEC_FG);
304 mem_free(prog);
306 } else if (filename) {
307 unlink(filename);
310 mem_free_if(filename);