1 /* Forms viewing/manipulation handling */
2 
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
6 
7 #ifndef _GNU_SOURCE
8 #define _GNU_SOURCE /* XXX: we want memrchr() ! */
9 #endif
10 
11 #include <errno.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <sys/types.h>
16 #ifdef HAVE_UNISTD_H
17 #include <unistd.h>
18 #endif
19 #ifdef HAVE_FCNTL_H
20 #include <fcntl.h> /* OS/2 needs this after sys/types.h */
21 #endif
22 
23 #include "elinks.h"
24 
25 #include "bfu/listmenu.h"
26 #include "bfu/dialog.h"
27 #include "config/kbdbind.h"
28 #include "dialogs/menu.h"
29 #include "document/document.h"
30 #include "document/forms.h"
31 #include "document/html/parser.h"
32 #include "document/view.h"
33 #include "intl/gettext/libintl.h"
34 #include "formhist/formhist.h"
35 #include "mime/mime.h"
36 #include "osdep/ascii.h"
37 #include "osdep/osdep.h"
38 #include "protocol/uri.h"
39 #include "session/session.h"
40 #include "session/task.h"
41 #include "terminal/kbd.h"
42 #include "terminal/terminal.h"
43 #include "terminal/window.h"
44 #include "util/conv.h"
45 #include "util/error.h"
46 #include "util/file.h"
47 #include "util/memory.h"
48 #include "util/string.h"
49 #include "viewer/action.h"
50 #include "viewer/text/draw.h"
51 #include "viewer/text/form.h"
52 #include "viewer/text/link.h"
53 #include "viewer/text/textarea.h"
54 #include "viewer/text/view.h"
55 #include "viewer/text/vs.h"
56 
57 
58 /* TODO: Some of these (particulary those encoding routines) would feel better
59  * in viewer/common/. --pasky */
60 
61 struct submitted_value *
init_submitted_value(unsigned char * name,unsigned char * value,enum form_type type,struct form_control * fc,int position)62 init_submitted_value(unsigned char *name, unsigned char *value, enum form_type type,
63 		     struct form_control *fc, int position)
64 {
65 	struct submitted_value *sv;
66 
67 	sv = mem_alloc(sizeof(*sv));
68 	if (!sv) return NULL;
69 
70 	sv->value = stracpy(value);
71 	if (!sv->value) { mem_free(sv); return NULL; }
72 
73 	sv->name = stracpy(name);
74 	if (!sv->name) { mem_free(sv->value); mem_free(sv); return NULL; }
75 
76 	sv->type = type;
77 	sv->form_control = fc;
78 	sv->position = position;
79 
80 	return sv;
81 }
82 
83 void
done_submitted_value(struct submitted_value * sv)84 done_submitted_value(struct submitted_value *sv)
85 {
86 	if (!sv) return;
87 	mem_free_if(sv->value);
88 	mem_free_if(sv->name);
89 	mem_free(sv);
90 }
91 
92 static void
fixup_select_state(struct form_control * fc,struct form_state * fs)93 fixup_select_state(struct form_control *fc, struct form_state *fs)
94 {
95 	int i;
96 
97 	assert(fc && fs);
98 	if_assert_failed return;
99 
100 	if (fs->state >= 0
101 	    && fs->state < fc->nvalues
102 	    && !strcmp(fc->values[fs->state], fs->value))
103 		return;
104 
105 	for (i = 0; i < fc->nvalues; i++)
106 		if (!strcmp(fc->values[i], fs->value)) {
107 			fs->state = i;
108 			return;
109 		}
110 
111 	fs->state = 0;
112 
113 	mem_free_set(&fs->value, stracpy(fc->nvalues
114 					 ? fc->values[0]
115 					 : (unsigned char *) ""));
116 }
117 
118 /* menu_func_T */
119 void
selected_item(struct terminal * term,void * item_,void * ses_)120 selected_item(struct terminal *term, void *item_, void *ses_)
121 {
122 	struct session *ses = ses_;
123 	int item = (long) item_;
124 	struct document_view *doc_view;
125 	struct link *link;
126 	struct form_state *fs;
127 	struct form_control *fc;
128 
129 	assert(term && ses);
130 	if_assert_failed return;
131 	doc_view = current_frame(ses);
132 
133 	assert(doc_view && doc_view->vs && doc_view->document);
134 	if_assert_failed return;
135 
136 	link = get_current_link(doc_view);
137 	if (!link || link->type != LINK_SELECT) return;
138 
139 	fc = get_link_form_control(link);
140 	fs = find_form_state(doc_view, fc);
141 	if (fs) {
142 		if (item >= 0 && item < fc->nvalues) {
143 			fs->state = item;
144 			mem_free_set(&fs->value, stracpy(fc->values[item]));
145 		}
146 		fixup_select_state(fc, fs);
147 	}
148 
149 	refresh_view(ses, doc_view, 0);
150 }
151 
152 static void
init_form_state(struct form_control * fc,struct form_state * fs)153 init_form_state(struct form_control *fc, struct form_state *fs)
154 {
155 	assert(fc && fs);
156 	if_assert_failed return;
157 
158 	mem_free_set(&fs->value, NULL);
159 
160 	switch (fc->type) {
161 		case FC_TEXT:
162 		case FC_PASSWORD:
163 		case FC_TEXTAREA:
164 			fs->value = stracpy(fc->default_value);
165 			fs->state = strlen(fc->default_value);
166 			fs->vpos = 0;
167 			break;
168 		case FC_FILE:
169 			fs->value = stracpy("");
170 			fs->state = 0;
171 			fs->vpos = 0;
172 			break;
173 		case FC_SELECT:
174 			fs->value = stracpy(fc->default_value);
175 			fs->state = fc->default_state;
176 			fixup_select_state(fc, fs);
177 			break;
178 		case FC_CHECKBOX:
179 		case FC_RADIO:
180 			fs->state = fc->default_state;
181 			/* Fall-through */
182 		case FC_SUBMIT:
183 		case FC_IMAGE:
184 		case FC_RESET:
185 		case FC_BUTTON:
186 		case FC_HIDDEN:
187 			fs->value = stracpy(fc->default_value);
188 			break;
189 	}
190 }
191 
192 
193 struct form_state *
find_form_state(struct document_view * doc_view,struct form_control * fc)194 find_form_state(struct document_view *doc_view, struct form_control *fc)
195 {
196 	struct view_state *vs;
197 	struct form_state *fs;
198 	int n;
199 
200 	assert(doc_view && doc_view->vs && fc);
201 	if_assert_failed return NULL;
202 
203 	vs = doc_view->vs;
204 	n = fc->g_ctrl_num;
205 
206 	if (n >= vs->form_info_len) {
207 		int nn = n + 1;
208 
209 		fs = mem_realloc(vs->form_info, nn * sizeof(*fs));
210 		if (!fs) return NULL;
211 		memset(fs + vs->form_info_len, 0,
212 		       (nn - vs->form_info_len) * sizeof(*fs));
213 		vs->form_info = fs;
214 		vs->form_info_len = nn;
215 	}
216 	fs = &vs->form_info[n];
217 
218 	if (fs->form_view && fs->form_view->form_num == fc->form->form_num
219 	    && fs->g_ctrl_num == fc->g_ctrl_num
220 	    && fs->position == fc->position
221 	    && fs->type == fc->type)
222 		return fs;
223 
224 	mem_free_if(fs->value);
225 	memset(fs, 0, sizeof(*fs));
226 	fs->form_view = find_form_view(doc_view, fc->form);
227 	fs->g_ctrl_num = fc->g_ctrl_num;
228 	fs->position = fc->position;
229 	fs->type = fc->type;
230 	init_form_state(fc, fs);
231 
232 	return fs;
233 }
234 
235 struct form_control *
find_form_control(struct document * document,struct form_state * fs)236 find_form_control(struct document *document, struct form_state *fs)
237 {
238 	struct form *form = find_form_by_form_view(document, fs->form_view);
239 	struct form_control *fc;
240 
241 	foreach (fc, form->items) {
242 		if (fs->g_ctrl_num == fc->g_ctrl_num
243 		    && fs->position == fc->position
244 		    && fs->type == fc->type)
245 			return fc;
246 	}
247 
248 	return NULL;
249 }
250 
251 struct form_view *
find_form_view_in_vs(struct view_state * vs,int form_num)252 find_form_view_in_vs(struct view_state *vs, int form_num)
253 {
254 	struct form_view *fv;
255 
256 	assert(vs);
257 
258 	foreach (fv, vs->forms)
259 		if (fv->form_num == form_num)
260 			return fv;
261 
262 	fv = mem_calloc(1, sizeof(*fv));
263 	fv->form_num = form_num;
264 	add_to_list(vs->forms, fv);
265 	return fv;
266 }
267 
268 struct form_view *
find_form_view(struct document_view * doc_view,struct form * form)269 find_form_view(struct document_view *doc_view, struct form *form)
270 {
271 	return find_form_view_in_vs(doc_view->vs, form->form_num);
272 }
273 
274 struct form *
find_form_by_form_view(struct document * document,struct form_view * fv)275 find_form_by_form_view(struct document *document, struct form_view *fv)
276 {
277 	struct form *form;
278 
279 	foreach (form, document->forms) {
280 		if (form->form_num == fv->form_num)
281 			return form;
282 	}
283 	return NULL;
284 }
285 
286 
287 int
get_current_state(struct session * ses)288 get_current_state(struct session *ses)
289 {
290 	struct document_view *doc_view;
291 	struct link *link;
292 	struct form_state *fs;
293 
294 	assert(ses);
295 	if_assert_failed return -1;
296 	doc_view = current_frame(ses);
297 
298 	assert(doc_view && doc_view->vs && doc_view->document);
299 	if_assert_failed return -1;
300 
301 	link = get_current_link(doc_view);
302 	if (!link || link->type != LINK_SELECT) return -1;
303 
304 	fs = find_form_state(doc_view, get_link_form_control(link));
305 	if (fs) return fs->state;
306 	return -1;
307 }
308 
309 void
draw_form_entry(struct terminal * term,struct document_view * doc_view,struct link * link)310 draw_form_entry(struct terminal *term, struct document_view *doc_view,
311 		struct link *link)
312 {
313 	struct form_state *fs;
314 	struct form_control *fc;
315 	struct view_state *vs;
316 	struct box *box;
317 	int dx, dy;
318 
319 	assert(term && doc_view && doc_view->document && doc_view->vs && link);
320 	if_assert_failed return;
321 
322 	fc = get_link_form_control(link);
323 	assertm(fc, "link %d has no form control", (int) (link - doc_view->document->links));
324 	if_assert_failed return;
325 
326 	fs = find_form_state(doc_view, fc);
327 	if (!fs) return;
328 
329 	box = &doc_view->box;
330 	vs = doc_view->vs;
331 	dx = box->x - vs->x;
332 	dy = box->y - vs->y;
333 	switch (fc->type) {
334 		unsigned char *s;
335 		int len;
336 		int i, x, y;
337 
338 		case FC_TEXT:
339 		case FC_PASSWORD:
340 		case FC_FILE:
341 			int_bounds(&fs->vpos, fs->state - fc->size + 1, fs->state);
342 			if (!link->npoints) break;
343 
344 			y = link->points[0].y + dy;
345 			if (!row_is_in_box(box, y))
346 				break;
347 
348 			len = strlen(fs->value) - fs->vpos;
349 			x = link->points[0].x + dx;
350 
351 			for (i = 0; i < fc->size; i++, x++) {
352 				unsigned char data;
353 
354 				if (!col_is_in_box(box, x)) continue;
355 
356 				if (fs->value && i >= -fs->vpos && i < len)
357 					data = fc->type != FC_PASSWORD
358 					     ? fs->value[i + fs->vpos] : '*';
359 				else
360 					data = '_';
361 
362 				draw_char_data(term, x, y, data);
363 			}
364 			break;
365 		case FC_TEXTAREA:
366 			draw_textarea(term, fs, doc_view, link);
367 			break;
368 		case FC_CHECKBOX:
369 		case FC_RADIO:
370 			if (link->npoints < 2) break;
371 			x = link->points[1].x + dx;
372 			y = link->points[1].y + dy;
373 			if (is_in_box(box, x, y))
374 				draw_char_data(term, x, y, fs->state ? 'X' : ' ');
375 			break;
376 		case FC_SELECT:
377 			fixup_select_state(fc, fs);
378 			if (fs->state < fc->nvalues)
379 				s = fc->labels[fs->state];
380 			else
381 				/* XXX: when can this happen? --pasky */
382 				s = "";
383 			len = s ? strlen(s) : 0;
384 			for (i = 0; i < link->npoints; i++) {
385 				x = link->points[i].x + dx;
386 				y = link->points[i].y + dy;
387 				if (is_in_box(box, x, y))
388 					draw_char_data(term, x, y, i < len ? s[i] : '_');
389 			}
390 			break;
391 		case FC_SUBMIT:
392 		case FC_IMAGE:
393 		case FC_RESET:
394 		case FC_BUTTON:
395 		case FC_HIDDEN:
396 			break;
397 	}
398 }
399 
400 void
draw_forms(struct terminal * term,struct document_view * doc_view)401 draw_forms(struct terminal *term, struct document_view *doc_view)
402 {
403 	struct link *l1, *l2;
404 
405 	assert(term && doc_view);
406 	if_assert_failed return;
407 
408 	l1 = get_first_link(doc_view);
409 	l2 = get_last_link(doc_view);
410 
411 	if (!l1 || !l2) {
412 		assertm(!l1 && !l2, "get_first_link == %p, get_last_link == %p", l1, l2);
413 		/* Return path :-). */
414 		return;
415 	}
416 	do {
417 		struct form_control *fc = get_link_form_control(l1);
418 
419 		if (!fc) continue;
420 #ifdef CONFIG_FORMHIST
421 		if (fc->type == FC_TEXT || fc->type == FC_PASSWORD) {
422 			unsigned char *value;
423 
424 			assert(fc->form);
425 			value = get_form_history_value(fc->form->action, fc->name);
426 
427 			if (value)
428 				mem_free_set(&fc->default_value,
429 					     stracpy(value));
430 		}
431 #endif /* CONFIG_FORMHIST */
432 		draw_form_entry(term, doc_view, l1);
433 
434 	} while (l1++ < l2);
435 }
436 
437 
438 void
done_submitted_value_list(struct list_head * list)439 done_submitted_value_list(struct list_head *list)
440 {
441 	struct submitted_value *sv, *svtmp;
442 
443 	assert(list);
444 	if_assert_failed return;
445 
446 	foreach (sv, *list) {
447 		svtmp = sv;
448 		sv = sv->prev;
449 		del_from_list(svtmp);
450 		done_submitted_value(svtmp);
451 	}
452 }
453 
454 static void
add_submitted_value_to_list(struct form_control * fc,struct form_state * fs,struct list_head * list)455 add_submitted_value_to_list(struct form_control *fc,
456 		            struct form_state *fs,
457 		            struct list_head *list)
458 {
459 	struct submitted_value *sub;
460 	unsigned char *name;
461 	enum form_type type;
462 	int position;
463 
464 	assert(fc && fs && list);
465 
466 	name = fc->name;
467 	position = fc->position;
468 	type = fc->type;
469 
470 	switch (fc->type) {
471 	case FC_TEXT:
472 	case FC_PASSWORD:
473 	case FC_FILE:
474 	case FC_TEXTAREA:
475 		sub = init_submitted_value(name, fs->value, type, fc, position);
476 		if (sub) add_to_list(*list, sub);
477 		break;
478 
479 	case FC_CHECKBOX:
480 	case FC_RADIO:
481 		if (!fs->state) break;
482 		/* fall through */
483 
484 	case FC_SUBMIT:
485 	case FC_HIDDEN:
486 	case FC_RESET:
487 	case FC_BUTTON:
488 		sub = init_submitted_value(name, fs->value, type, fc,
489 					   position);
490 		if (sub) add_to_list(*list, sub);
491 		break;
492 
493 	case FC_SELECT:
494 		if (!fc->nvalues) break;
495 
496 		fixup_select_state(fc, fs);
497 		sub = init_submitted_value(name, fs->value, type, fc, position);
498 		if (sub) add_to_list(*list, sub);
499 		break;
500 
501 	case FC_IMAGE:
502 	        name = straconcat(fc->name, ".x", NULL);
503 		if (!name) break;
504 		sub = init_submitted_value(name, "0", type, fc, position);
505 		mem_free(name);
506 		if (sub) add_to_list(*list, sub);
507 
508 		name = straconcat(fc->name, ".y", NULL);
509 		if (!name) break;
510 		sub = init_submitted_value(name, "0", type, fc, position);
511 		mem_free(name);
512 		if (sub) add_to_list(*list, sub);
513 
514 		break;
515 	}
516 }
517 
518 static void
sort_submitted_values(struct list_head * list)519 sort_submitted_values(struct list_head *list)
520 {
521 	while (1) {
522 		struct submitted_value *sub;
523 		int changed = 0;
524 
525 		foreach (sub, *list) if (list_has_next(*list, sub))
526 			if (sub->next->position < sub->position) {
527 				struct submitted_value *next = sub->next;
528 
529 				del_from_list(sub);
530 				add_at_pos(next, sub);
531 				sub = next;
532 				changed = 1;
533 			}
534 
535 		foreachback (sub, *list) if (list_has_next(*list, sub))
536 			if (sub->next->position < sub->position) {
537 				struct submitted_value *next = sub->next;
538 
539 				del_from_list(sub);
540 				add_at_pos(next, sub);
541 				sub = next;
542 				changed = 1;
543 			}
544 
545 		if (!changed) break;
546 	};
547 }
548 
549 static void
get_successful_controls(struct document_view * doc_view,struct form_control * fc,struct list_head * list)550 get_successful_controls(struct document_view *doc_view,
551 			struct form_control *fc, struct list_head *list)
552 {
553 	struct form_control *fc2;
554 
555 	assert(doc_view && fc && fc->form && list);
556 	if_assert_failed return;
557 
558 	foreach (fc2, fc->form->items) {
559 		if (((fc2->type != FC_SUBMIT &&
560 			 fc2->type != FC_IMAGE &&
561 			 fc2->type != FC_RESET &&
562 			 fc2->type != FC_BUTTON) || fc2 == fc)
563 		    && fc2->name && fc2->name[0]) {
564 			struct form_state *fs = find_form_state(doc_view, fc2);
565 
566 			if (!fs) continue;
567 
568 			add_submitted_value_to_list(fc2, fs, list);
569 		}
570 	}
571 
572 	sort_submitted_values(list);
573 }
574 
575 static void
encode_controls(struct list_head * l,struct string * data,int cp_from,int cp_to)576 encode_controls(struct list_head *l, struct string *data,
577 		int cp_from, int cp_to)
578 {
579 	struct submitted_value *sv;
580 	struct conv_table *convert_table = NULL;
581 	int lst = 0;
582 
583 	assert(l && data);
584 	if_assert_failed return;
585 
586 	foreach (sv, *l) {
587 		unsigned char *p2 = NULL;
588 
589 		if (lst)
590 			add_char_to_string(data, '&');
591 		else
592 			lst = 1;
593 
594 		encode_uri_string(data, sv->name, strlen(sv->name), 1);
595 		add_char_to_string(data, '=');
596 
597 		/* Convert back to original encoding (see html_form_control()
598 		 * for the original recoding). */
599 		if (sv->type == FC_TEXTAREA) {
600 			unsigned char *p;
601 
602 			p = encode_textarea(sv);
603 			if (p) {
604 				if (!convert_table)
605 					convert_table = get_translation_table(cp_from, cp_to);
606 
607 				p2 = convert_string(convert_table, p,
608 						    strlen(p), -1, CSM_FORM, NULL, NULL, NULL);
609 				mem_free(p);
610 			}
611 		} else if (sv->type == FC_TEXT ||
612 			   sv->type == FC_PASSWORD) {
613 			if (!convert_table)
614 				convert_table = get_translation_table(cp_from, cp_to);
615 
616 			p2 = convert_string(convert_table, sv->value,
617 					    strlen(sv->value), -1, CSM_FORM, NULL, NULL, NULL);
618 		} else {
619 			p2 = stracpy(sv->value);
620 		}
621 
622 		if (p2) {
623 			encode_uri_string(data, p2, strlen(p2), 1);
624 			mem_free(p2);
625 		}
626 	}
627 }
628 
629 
630 
631 #define BOUNDARY_LENGTH	32
632 #define realloc_bound_ptrs(bptrs, bptrs_size) \
633 	mem_align_alloc(bptrs, bptrs_size, bptrs_size + 1, int, 0xFF)
634 
635 struct boundary_info {
636 	int count;
637 	int *offsets;
638 	unsigned char string[BOUNDARY_LENGTH];
639 };
640 
641 static inline void
init_boundary(struct boundary_info * boundary)642 init_boundary(struct boundary_info *boundary)
643 {
644 	memset(boundary, 0, sizeof(*boundary));
645 	memset(boundary->string, '0', BOUNDARY_LENGTH);
646 }
647 
648 /* Add boundary to string and save the offset */
649 static inline void
add_boundary(struct string * data,struct boundary_info * boundary)650 add_boundary(struct string *data, struct boundary_info *boundary)
651 {
652 	add_to_string(data, "--");
653 
654 	if (realloc_bound_ptrs(&boundary->offsets, boundary->count))
655 		boundary->offsets[boundary->count++] = data->length;
656 
657 	add_bytes_to_string(data, boundary->string, BOUNDARY_LENGTH);
658 }
659 
660 static inline unsigned char *
increment_boundary_counter(struct boundary_info * boundary)661 increment_boundary_counter(struct boundary_info *boundary)
662 {
663 	int j;
664 
665 	/* This is just a decimal string incrementation */
666 	for (j = BOUNDARY_LENGTH - 1; j >= 0; j--) {
667 		if (boundary->string[j]++ < '9')
668 			return boundary->string;
669 
670 		boundary->string[j] = '0';
671 	}
672 
673 	INTERNAL("Form data boundary counter overflow");
674 
675 	return NULL;
676 }
677 
678 static inline void
check_boundary(struct string * data,struct boundary_info * boundary)679 check_boundary(struct string *data, struct boundary_info *boundary)
680 {
681 	unsigned char *bound = boundary->string;
682 	int i;
683 
684 	/* Search between all boundaries. There is a starting and an ending
685 	 * boundary so only check the range of chars after the current offset
686 	 * and before the next offset. If some string in the form data matches
687 	 * the boundary string it is changed. */
688 	for (i = 0; i < boundary->count - 1; i++) {
689 		/* Start after the boundary string and also jump past the
690 		 * "\r\nContent-Disposition: form-data; name=\"" string added
691 		 * before any form data. */
692 		int start_offset = boundary->offsets[i] + BOUNDARY_LENGTH + 40;
693 
694 		/* End so that there is atleast BOUNDARY_LENGTH chars to
695 		 * compare. Subtract 2 char because there is no need to also
696 		 * compare the '--' prefix that is part of the boundary. */
697 		int end_offset = boundary->offsets[i + 1] - BOUNDARY_LENGTH - 2;
698 		unsigned char *pos = data->source + start_offset;
699 		unsigned char *end = data->source + end_offset;
700 
701 		for (; pos <= end; pos++) {
702 			if (memcmp(pos, bound, BOUNDARY_LENGTH))
703 				continue;
704 
705 			/* If incrementing causes overflow bail out. There is
706 			 * no need to reset the boundary string with '0' since
707 			 * that is already done when incrementing. */
708 			if (!increment_boundary_counter(boundary))
709 				return;
710 
711 			/* Else start checking all boundaries using the new
712 			 * boundary string */
713 			i = 0;
714 			break;
715 		}
716 	}
717 
718 	/* Now update all the boundaries with the unique boundary string */
719 	for (i = 0; i < boundary->count; i++)
720 		memcpy(data->source + boundary->offsets[i], bound, BOUNDARY_LENGTH);
721 }
722 
723 /* FIXME: shouldn't we encode data at send time (in http.c) ? --Zas */
724 static void
encode_multipart(struct session * ses,struct list_head * l,struct string * data,struct boundary_info * boundary,int cp_from,int cp_to)725 encode_multipart(struct session *ses, struct list_head *l, struct string *data,
726 		 struct boundary_info *boundary, int cp_from, int cp_to)
727 {
728 	struct conv_table *convert_table = NULL;
729 	struct submitted_value *sv;
730 
731 	assert(ses && l && data && boundary);
732 	if_assert_failed return;
733 
734 	init_boundary(boundary);
735 
736 	foreach (sv, *l) {
737 		add_boundary(data, boundary);
738 		add_crlf_to_string(data);
739 
740 		/* FIXME: name is not encoded.
741 		 * from RFC 1867:
742 		 * multipart/form-data contains a series of parts.
743 		 * Each part is expected to contain a content-disposition
744 		 * header where the value is "form-data" and a name attribute
745 		 * specifies the field name within the form,
746 		 * e.g., 'content-disposition: form-data; name="xxxxx"',
747 		 * where xxxxx is the field name corresponding to that field.
748 		 * Field names originally in non-ASCII character sets may be
749 		 * encoded using the method outlined in RFC 1522. */
750 		add_to_string(data, "Content-Disposition: form-data; name=\"");
751 		add_to_string(data, sv->name);
752 		add_char_to_string(data, '"');
753 
754 		if (sv->type == FC_FILE) {
755 #define F_BUFLEN 1024
756 			int fh;
757 			unsigned char buffer[F_BUFLEN];
758 			unsigned char *extension;
759 
760 			add_to_string(data, "; filename=\"");
761 			add_to_string(data, get_filename_position(sv->value));
762 			/* It sends bad data if the file name contains ", but
763 			   Netscape does the same */
764 			/* FIXME: We should follow RFCs 1522, 1867,
765 			 * 2047 (updated by rfc 2231), to provide correct support
766 			 * for non-ASCII and special characters in values. --Zas */
767 			add_char_to_string(data, '"');
768 
769 			/* Add a Content-Type header if the type is configured */
770 			extension = strrchr(sv->value, '.');
771 			if (extension) {
772 				unsigned char *type = get_extension_content_type(extension);
773 
774 				if (type) {
775 					add_crlf_to_string(data);
776 					add_to_string(data, "Content-Type: ");
777 					add_to_string(data, type);
778 					mem_free(type);
779 				}
780 			}
781 
782 			add_crlf_to_string(data);
783 			add_crlf_to_string(data);
784 
785 			if (*sv->value) {
786 				unsigned char *filename;
787 
788 				if (get_cmd_opt_bool("anonymous")) {
789 					errno = EPERM;
790 					goto encode_error;
791 				}
792 
793 				/* FIXME: DO NOT COPY FILE IN MEMORY !! --Zas */
794 				filename = expand_tilde(sv->value);
795 				if (!filename) goto encode_error;
796 
797 				fh = open(filename, O_RDONLY);
798 				mem_free(filename);
799 
800 				if (fh == -1) goto encode_error;
801 				set_bin(fh);
802 				while (1) {
803 					ssize_t rd = safe_read(fh, buffer, F_BUFLEN);
804 
805 					if (rd) {
806 						if (rd == -1) {
807 							close(fh);
808 							goto encode_error;
809 						}
810 
811 						add_bytes_to_string(data, buffer, rd);
812 
813 					} else {
814 						break;
815 					}
816 				};
817 				close(fh);
818 			}
819 #undef F_BUFLEN
820 		} else {
821 			add_crlf_to_string(data);
822 			add_crlf_to_string(data);
823 
824 			/* Convert back to original encoding (see
825 			 * html_form_control() for the original recoding). */
826 			if (sv->type == FC_TEXT || sv->type == FC_PASSWORD ||
827 			    sv->type == FC_TEXTAREA) {
828 				unsigned char *p;
829 
830 				if (!convert_table)
831 					convert_table = get_translation_table(cp_from,
832 									      cp_to);
833 
834 				p = convert_string(convert_table, sv->value,
835 						   strlen(sv->value), -1, CSM_FORM, NULL,
836 						   NULL, NULL);
837 				if (p) {
838 					add_to_string(data, p);
839 					mem_free(p);
840 				}
841 			} else {
842 				add_to_string(data, sv->value);
843 			}
844 		}
845 
846 		add_crlf_to_string(data);
847 	}
848 
849 	/* End-boundary */
850 	add_boundary(data, boundary);
851 	add_to_string(data, "--\r\n");
852 
853 	check_boundary(data, boundary);
854 
855 	mem_free_if(boundary->offsets);
856 	return;
857 
858 encode_error:
859 	mem_free_if(boundary->offsets);
860 	done_string(data);
861 
862 	/* XXX: This error message should move elsewhere. --Zas */
863 	info_box(ses->tab->term, MSGBOX_FREE_TEXT,
864 		 N_("Error while posting form"), ALIGN_CENTER,
865 		 msg_text(ses->tab->term, N_("Could not load file %s: %s"),
866 			  sv->value, strerror(errno)));
867 }
868 
869 static void
encode_newlines(struct string * string,unsigned char * data)870 encode_newlines(struct string *string, unsigned char *data)
871 {
872 	for (; *data; data++) {
873 		if (*data == '\n' || *data == '\r') {
874 			unsigned char buffer[3];
875 
876 			/* Hex it. */
877 			buffer[0] = '%';
878 			buffer[1] = hx((((int) *data) & 0xF0) >> 4);
879 			buffer[2] = hx(((int) *data) & 0xF);
880 			add_bytes_to_string(string, buffer, 3);
881 		} else {
882 			add_char_to_string(string, *data);
883 		}
884 	}
885 }
886 
887 static void
encode_text_plain(struct list_head * l,struct string * data,int cp_from,int cp_to)888 encode_text_plain(struct list_head *l, struct string *data,
889 		  int cp_from, int cp_to)
890 {
891 	struct submitted_value *sv;
892 	struct conv_table *convert_table = get_translation_table(cp_from, cp_to);
893 
894 	assert(l && data);
895 	if_assert_failed return;
896 
897 	foreach (sv, *l) {
898 		unsigned char *area51 = NULL;
899 		unsigned char *value = sv->value;
900 
901 		add_to_string(data, sv->name);
902 		add_char_to_string(data, '=');
903 
904 		switch (sv->type) {
905 		case FC_TEXTAREA:
906 			value = area51 = encode_textarea(sv);
907 			if (!area51) break;
908 			/* Fall through */
909 		case FC_TEXT:
910 		case FC_PASSWORD:
911 			/* Convert back to original encoding (see
912 			 * html_form_control() for the original recoding). */
913 			value = convert_string(convert_table, value,
914 			                       strlen(value), -1, CSM_FORM,
915 			                       NULL, NULL, NULL);
916 		default:
917 			/* Falling right through to free that textarea stuff */
918 			mem_free_if(area51);
919 
920 			/* Did the conversion fail? */
921 			if (!value) break;
922 
923 			encode_newlines(data, value);
924 
925 			/* Free if we did convert something */
926 			if (value != sv->value) mem_free(value);
927 		}
928 
929 		add_crlf_to_string(data);
930 	}
931 }
932 
933 void
do_reset_form(struct document_view * doc_view,struct form * form)934 do_reset_form(struct document_view *doc_view, struct form *form)
935 {
936 	struct form_control *fc;
937 
938 	assert(doc_view && doc_view->document);
939 	if_assert_failed return;
940 
941 	foreach (fc, form->items) {
942 		struct form_state *fs = find_form_state(doc_view, fc);
943 
944 		if (fs) init_form_state(fc, fs);
945 	}
946 }
947 
948 enum frame_event_status
reset_form(struct session * ses,struct document_view * doc_view,int a)949 reset_form(struct session *ses, struct document_view *doc_view, int a)
950 {
951 	struct link *link = get_current_link(doc_view);
952 
953 	if (!link) return FRAME_EVENT_OK;
954 
955 	do_reset_form(doc_view, get_link_form_control(link)->form);
956 	draw_forms(ses->tab->term, doc_view);
957 
958 	/* Could be the refresh return value and then ditch the draw_forms()
959 	 * call. */
960 	return FRAME_EVENT_OK;
961 }
962 
963 struct uri *
get_form_uri(struct session * ses,struct document_view * doc_view,struct form_control * fc)964 get_form_uri(struct session *ses, struct document_view *doc_view,
965 	     struct form_control *fc)
966 {
967 	struct boundary_info boundary;
968 	INIT_LIST_HEAD(submit);
969 	struct string data;
970 	struct string go;
971 	int cp_from, cp_to;
972 	struct uri *uri;
973 	struct form *form;
974 
975 	assert(ses && ses->tab && ses->tab->term);
976 	if_assert_failed return NULL;
977 	assert(doc_view && doc_view->document && fc && fc->form);
978 	if_assert_failed return NULL;
979 
980 	form = fc->form;
981 
982 	if (fc->type == FC_RESET) {
983 		do_reset_form(doc_view, form);
984 		return NULL;
985 	} else if (fc->type == FC_BUTTON) {
986 		return NULL;
987 	}
988 
989 	if (!form->action
990 	    || !init_string(&data))
991 		return NULL;
992 
993 	get_successful_controls(doc_view, fc, &submit);
994 
995 	cp_from = get_opt_codepage_tree(ses->tab->term->spec, "charset");
996 	cp_to = doc_view->document->cp;
997 	switch (form->method) {
998 	case FORM_METHOD_GET:
999 	case FORM_METHOD_POST:
1000 		encode_controls(&submit, &data, cp_from, cp_to);
1001 		break;
1002 
1003 	case FORM_METHOD_POST_MP:
1004 		encode_multipart(ses, &submit, &data, &boundary, cp_from, cp_to);
1005 		break;
1006 
1007 	case FORM_METHOD_POST_TEXT_PLAIN:
1008 		encode_text_plain(&submit, &data, cp_from, cp_to);
1009 	}
1010 
1011 #ifdef CONFIG_FORMHIST
1012 	/* XXX: We check data.source here because a NULL value can indicate
1013 	 * not only a memory allocation failure, but also an error reading
1014 	 * a file that is to be uploaded. TODO: Distinguish between
1015 	 * these two classes of errors (is it worth it?). -- Miciah */
1016 	if (data.source
1017 	    && get_opt_bool("document.browse.forms.show_formhist"))
1018 		memorize_form(ses, &submit, form);
1019 #endif
1020 
1021 	done_submitted_value_list(&submit);
1022 
1023 	if (!data.source
1024 	    || !init_string(&go)) {
1025 		done_string(&data);
1026 		return NULL;
1027 	}
1028 
1029 	switch (form->method) {
1030 	case FORM_METHOD_GET:
1031 	{
1032 		unsigned char *pos = strchr(form->action, '#');
1033 
1034 		if (pos) {
1035 			add_bytes_to_string(&go, form->action, pos - form->action);
1036 		} else {
1037 			add_to_string(&go, form->action);
1038 		}
1039 
1040 		if (strchr(go.source, '?'))
1041 			add_char_to_string(&go, '&');
1042 		else
1043 			add_char_to_string(&go, '?');
1044 
1045 		add_string_to_string(&go, &data);
1046 
1047 		if (pos) add_to_string(&go, pos);
1048 		break;
1049 	}
1050 	case FORM_METHOD_POST:
1051 	case FORM_METHOD_POST_MP:
1052 	case FORM_METHOD_POST_TEXT_PLAIN:
1053 	{
1054 		/* Note that we end content type here by a simple '\n',
1055 		 * replaced later by correct '\r\n' in http_send_header(). */
1056 		int i;
1057 
1058 		add_to_string(&go, form->action);
1059 		add_char_to_string(&go, POST_CHAR);
1060 		if (form->method == FORM_METHOD_POST) {
1061 			add_to_string(&go, "application/x-www-form-urlencoded\n");
1062 
1063 		} else if (form->method == FORM_METHOD_POST_TEXT_PLAIN) {
1064 			/* Dunno about this one but we don't want the full
1065 			 * hextcat thingy. --jonas */
1066 			add_to_string(&go, "text/plain\n");
1067 			add_to_string(&go, data.source);
1068 			break;
1069 
1070 		} else {
1071 			add_to_string(&go, "multipart/form-data; boundary=");
1072 			add_bytes_to_string(&go, boundary.string, BOUNDARY_LENGTH);
1073 			add_char_to_string(&go, '\n');
1074 		}
1075 
1076 		for (i = 0; i < data.length; i++) {
1077 			unsigned char p[3];
1078 
1079 			ulonghexcat(p, NULL, (int) data.source[i], 2, '0', 0);
1080 			add_to_string(&go, p);
1081 		}
1082 	}
1083 	}
1084 
1085 	done_string(&data);
1086 
1087 	uri = get_uri(go.source, 0);
1088 	done_string(&go);
1089 	if (uri) uri->form = 1;
1090 
1091 	return uri;
1092 }
1093 
1094 #undef BOUNDARY_LENGTH
1095 
1096 
1097 enum frame_event_status
submit_form(struct session * ses,struct document_view * doc_view,int do_reload)1098 submit_form(struct session *ses, struct document_view *doc_view, int do_reload)
1099 {
1100 	goto_current_link(ses, doc_view, do_reload);
1101 	return FRAME_EVENT_OK;
1102 }
1103 
1104 void
submit_given_form(struct session * ses,struct document_view * doc_view,struct form * form)1105 submit_given_form(struct session *ses, struct document_view *doc_view, struct form *form)
1106 {
1107 /* Added support for submitting forms in hidden
1108  * links in 1.285, commented code can safely be removed once we have made sure the new
1109  * code does the right thing. */
1110 #if 0
1111 
1112 	struct document *document = doc_view->document;
1113 	int link;
1114 
1115 	for (link = 0; link < document->nlinks; link++) {
1116 		struct form_control *fc = get_link_form_control(&document->links[link]);
1117 
1118 		if (fc && fc->form == form) {
1119 			doc_view->vs->current_link = link;
1120 			submit_form(ses, doc_view, 0);
1121 			return;
1122 		}
1123 	}
1124 #endif
1125 	if (!list_empty(form->items)) {
1126 		struct form_control *fc = (struct form_control *)form->items.next;
1127 		struct uri *uri;
1128 
1129 		if (!fc) return;
1130 		uri = get_form_uri(ses, doc_view, fc);
1131 		if (!uri) return;
1132 		goto_uri_frame(ses, uri, form->target, CACHE_MODE_NORMAL);
1133 		done_uri(uri);
1134 	}
1135 }
1136 
1137 void
auto_submit_form(struct session * ses)1138 auto_submit_form(struct session *ses)
1139 {
1140 	struct document *document = ses->doc_view->document;
1141 
1142 	if (!list_empty(document->forms))
1143 		submit_given_form(ses, ses->doc_view, document->forms.next);
1144 }
1145 
1146 
1147 /* menu_func_T */
1148 static void
set_file_form_state(struct terminal * term,void * filename_,void * fs_)1149 set_file_form_state(struct terminal *term, void *filename_, void *fs_)
1150 {
1151 	unsigned char *filename = filename_;
1152 	struct form_state *fs = fs_;
1153 
1154 	/* The menu code doesn't free the filename data */
1155 	mem_free_set(&fs->value, filename);
1156 	fs->state = strlen(filename);
1157 	redraw_terminal(term);
1158 }
1159 
1160 /* menu_func_T */
1161 static void
file_form_menu(struct terminal * term,void * path_,void * fs_)1162 file_form_menu(struct terminal *term, void *path_, void *fs_)
1163 {
1164 	unsigned char *path = path_;
1165 	struct form_state *fs = fs_;
1166 
1167 	/* FIXME: It doesn't work for ../../ */
1168 #if 0
1169 	int valuelen = strlen(fs->value);
1170 	int pathlen = strlen(path);
1171 	int no_elevator = 0;
1172 
1173 	/* Don't add elevators for subdirs menus */
1174 	/* It is not perfect at all because fs->value is not updated for each
1175 	 * newly opened file menu. Maybe it should be dropped. */
1176 	for (; valuelen < pathlen; valuelen++) {
1177 		if (dir_sep(path[valuelen - 1])) {
1178 			no_elevator = 1;
1179 			break;
1180 		}
1181 	}
1182 #endif
1183 
1184 	auto_complete_file(term, 0 /* no_elevator */, path,
1185 			   set_file_form_state,
1186 			   file_form_menu, fs);
1187 }
1188 
1189 
1190 enum frame_event_status
field_op(struct session * ses,struct document_view * doc_view,struct link * link,struct term_event * ev)1191 field_op(struct session *ses, struct document_view *doc_view,
1192 	 struct link *link, struct term_event *ev)
1193 {
1194 	struct form_control *fc;
1195 	struct form_state *fs;
1196 	enum edit_action action_id;
1197 	unsigned char *text;
1198 	int length;
1199 	enum frame_event_status status = FRAME_EVENT_REFRESH;
1200 
1201 	assert(ses && doc_view && link && ev);
1202 	if_assert_failed return FRAME_EVENT_OK;
1203 
1204 	fc = get_link_form_control(link);
1205 	assertm(fc, "link has no form control");
1206 	if_assert_failed return FRAME_EVENT_OK;
1207 
1208 	if (fc->mode == FORM_MODE_DISABLED || ev->ev != EVENT_KBD
1209 	    || ses->insert_mode == INSERT_MODE_OFF)
1210 		return FRAME_EVENT_IGNORED;
1211 
1212 	action_id = kbd_action(KEYMAP_EDIT, ev, NULL);
1213 
1214 	fs = find_form_state(doc_view, fc);
1215 	if (!fs || !fs->value) return FRAME_EVENT_OK;
1216 
1217 	switch (action_id) {
1218 		case ACT_EDIT_LEFT:
1219 			fs->state = int_max(fs->state - 1, 0);
1220 			break;
1221 		case ACT_EDIT_RIGHT:
1222 			fs->state = int_min(fs->state + 1, strlen(fs->value));
1223 			break;
1224 		case ACT_EDIT_HOME:
1225 			if (fc->type == FC_TEXTAREA) {
1226 				status = textarea_op_home(fs, fc);
1227 			} else {
1228 				fs->state = 0;
1229 			}
1230 			break;
1231 		case ACT_EDIT_UP:
1232 			if (fc->type != FC_TEXTAREA)
1233 				status = FRAME_EVENT_IGNORED;
1234 			else
1235 				status = textarea_op_up(fs, fc);
1236 			break;
1237 		case ACT_EDIT_DOWN:
1238 			if (fc->type != FC_TEXTAREA)
1239 				status = FRAME_EVENT_IGNORED;
1240 			else
1241 				status = textarea_op_down(fs, fc);
1242 			break;
1243 		case ACT_EDIT_END:
1244 			if (fc->type == FC_TEXTAREA) {
1245 				status = textarea_op_end(fs, fc);
1246 			} else {
1247 				fs->state = strlen(fs->value);
1248 			}
1249 			break;
1250 		case ACT_EDIT_BEGINNING_OF_BUFFER:
1251 			if (fc->type == FC_TEXTAREA) {
1252 				status = textarea_op_bob(fs, fc);
1253 			} else {
1254 				fs->state = 0;
1255 			}
1256 			break;
1257 		case ACT_EDIT_END_OF_BUFFER:
1258 			if (fc->type == FC_TEXTAREA) {
1259 				status = textarea_op_eob(fs, fc);
1260 			} else {
1261 				fs->state = strlen(fs->value);
1262 			}
1263 			break;
1264 		case ACT_EDIT_OPEN_EXTERNAL:
1265 			if (form_field_is_readonly(fc))
1266 				status = FRAME_EVENT_IGNORED;
1267 			else if (fc->type == FC_TEXTAREA)
1268 				textarea_edit(0, ses->tab->term, fs, doc_view, link);
1269 			break;
1270 		case ACT_EDIT_COPY_CLIPBOARD:
1271 			set_clipboard_text(fs->value);
1272 			status = FRAME_EVENT_OK;
1273 			break;
1274 		case ACT_EDIT_CUT_CLIPBOARD:
1275 			set_clipboard_text(fs->value);
1276 			if (!form_field_is_readonly(fc))
1277 				fs->value[0] = 0;
1278 			fs->state = 0;
1279 			break;
1280 		case ACT_EDIT_PASTE_CLIPBOARD:
1281 			if (form_field_is_readonly(fc)) break;
1282 
1283 			text = get_clipboard_text();
1284 			if (!text) break;
1285 
1286 			length = strlen(text);
1287 			if (length <= fc->maxlength) {
1288 				unsigned char *v = mem_realloc(fs->value, length + 1);
1289 
1290 				if (v) {
1291 					fs->value = v;
1292 					memmove(v, text, length + 1);
1293 					fs->state = strlen(fs->value);
1294 				}
1295 			}
1296 			mem_free(text);
1297 			break;
1298 		case ACT_EDIT_ENTER:
1299 			if (fc->type == FC_TEXTAREA) {
1300 				status = textarea_op_enter(fs, fc);
1301 				break;
1302 			}
1303 
1304 			/* Set status to ok if either it is not possible to
1305 			 * submit the form or the posting fails. */
1306 			/* FIXME: We should maybe have ACT_EDIT_ENTER_RELOAD */
1307 			if ((has_form_submit(fc->form)
1308 			      && !get_opt_bool("document.browse.forms.auto_submit"))
1309 			    || goto_current_link(ses, doc_view, 0)) {
1310 				if (ses->insert_mode == INSERT_MODE_ON)
1311 					ses->insert_mode = INSERT_MODE_OFF;
1312 				status = FRAME_EVENT_OK;
1313 			}
1314 			break;
1315 		case ACT_EDIT_BACKSPACE:
1316 			if (form_field_is_readonly(fc)) {
1317 				status = FRAME_EVENT_IGNORED;
1318 				break;
1319 			}
1320 
1321 			if (!fs->state) {
1322 				status = FRAME_EVENT_OK;
1323 				break;
1324 			}
1325 
1326 			length = strlen(fs->value + fs->state) + 1;
1327 			text = fs->value + fs->state;
1328 
1329 			memmove(text - 1, text, length);
1330 			fs->state--;
1331 			break;
1332 		case ACT_EDIT_DELETE:
1333 			if (form_field_is_readonly(fc)) {
1334 				status = FRAME_EVENT_IGNORED;
1335 				break;
1336 			}
1337 
1338 			length = strlen(fs->value);
1339 			if (fs->state >= length) {
1340 				status = FRAME_EVENT_OK;
1341 				break;
1342 			}
1343 
1344 			text = fs->value + fs->state;
1345 
1346 			memmove(text, text + 1, length - fs->state);
1347 			break;
1348 		case ACT_EDIT_KILL_TO_BOL:
1349 			if (form_field_is_readonly(fc)) {
1350 				status = FRAME_EVENT_IGNORED;
1351 				break;
1352 			}
1353 
1354 			if (fs->state <= 0) {
1355 				status = FRAME_EVENT_OK;
1356 				break;
1357 			}
1358 
1359 			text = memrchr(fs->value, ASCII_LF, fs->state);
1360 			if (text) {
1361 				/* Leave the new-line character if it does not
1362 				 * immediately precede the cursor. */
1363 				if (text != &fs->value[fs->state - 1])
1364 					text++;
1365 			} else {
1366 				text = fs->value;
1367 			}
1368 
1369 			length = strlen(fs->value + fs->state) + 1;
1370 			memmove(text, fs->value + fs->state, length);
1371 
1372 			fs->state = (int) (text - fs->value);
1373 			break;
1374 		case ACT_EDIT_KILL_TO_EOL:
1375 			if (form_field_is_readonly(fc)) {
1376 				status = FRAME_EVENT_IGNORED;
1377 				break;
1378 			}
1379 
1380 			if (!fs->value[fs->state]) {
1381 				status = FRAME_EVENT_OK;
1382 				break;
1383 			}
1384 
1385 			text = strchr(fs->value + fs->state, ASCII_LF);
1386 			if (!text) {
1387 				fs->value[fs->state] = '\0';
1388 				break;
1389 			}
1390 
1391 			if (fs->value[fs->state] == ASCII_LF)
1392 				++text;
1393 
1394 			memmove(fs->value + fs->state, text, strlen(text) + 1);
1395 			break;
1396 
1397 		case ACT_EDIT_AUTO_COMPLETE:
1398 			if (fc->type != FC_FILE
1399 			    || form_field_is_readonly(fc)) {
1400 				status = FRAME_EVENT_IGNORED;
1401 				break;
1402 			}
1403 
1404 			file_form_menu(ses->tab->term, fs->value, fs);
1405 			break;
1406 
1407 		case ACT_EDIT_CANCEL:
1408 			if (ses->insert_mode == INSERT_MODE_ON)
1409 				ses->insert_mode = INSERT_MODE_OFF;
1410 			else
1411 				status = FRAME_EVENT_IGNORED;
1412 			break;
1413 
1414 		case ACT_EDIT_REDRAW:
1415 			redraw_terminal_cls(ses->tab->term);
1416 			status = FRAME_EVENT_OK;
1417 			break;
1418 
1419 		default:
1420 			if (!check_kbd_textinput_key(ev)) {
1421 				status = FRAME_EVENT_IGNORED;
1422 				break;
1423 			}
1424 
1425 			if (form_field_is_readonly(fc)
1426 			    || strlen(fs->value) >= fc->maxlength
1427 			    || !insert_in_string(&fs->value, fs->state, "?", 1)) {
1428 				status = FRAME_EVENT_OK;
1429 				break;
1430 			}
1431 
1432 			fs->value[fs->state++] = get_kbd_key(ev);
1433 			break;
1434 	}
1435 
1436 	return status;
1437 }
1438 
1439 unsigned char *
get_form_label(struct form_control * fc)1440 get_form_label(struct form_control *fc)
1441 {
1442 	assert(fc->form);
1443 	switch (fc->type) {
1444 	case FC_RESET:
1445 		return N_("Reset form");
1446 	case FC_BUTTON:
1447 		return N_("Harmless button");
1448 	case FC_HIDDEN:
1449 		return NULL;
1450 	case FC_SUBMIT:
1451 	case FC_IMAGE:
1452 		if (!fc->form->action) return NULL;
1453 
1454 		if (fc->form->method == FORM_METHOD_GET)
1455 			return N_("Submit form to");
1456 		return N_("Post form to");
1457 	case FC_RADIO:
1458 		return N_("Radio button");
1459 	case FC_CHECKBOX:
1460 		return N_("Checkbox");
1461 	case FC_SELECT:
1462 		return N_("Select field");
1463 	case FC_TEXT:
1464 		return N_("Text field");
1465 	case FC_TEXTAREA:
1466 		return N_("Text area");
1467 	case FC_FILE:
1468 		return N_("File upload");
1469 	case FC_PASSWORD:
1470 		return N_("Password field");
1471 	}
1472 
1473 	return NULL;
1474 }
1475 
1476 static inline void
add_form_attr_to_string(struct string * string,struct terminal * term,unsigned char * name,unsigned char * value)1477 add_form_attr_to_string(struct string *string, struct terminal *term,
1478 			unsigned char *name, unsigned char *value)
1479 {
1480 	add_to_string(string, ", ");
1481 	add_to_string(string, _(name, term));
1482 	if (value) {
1483 		add_char_to_string(string, ' ');
1484 		add_to_string(string, value);
1485 	}
1486 }
1487 
1488 unsigned char *
get_form_info(struct session * ses,struct document_view * doc_view)1489 get_form_info(struct session *ses, struct document_view *doc_view)
1490 {
1491 	struct terminal *term = ses->tab->term;
1492 	struct link *link = get_current_link(doc_view);
1493 	struct form_control *fc;
1494 	unsigned char *label, *key;
1495 	struct string str;
1496 
1497 	assert(link);
1498 
1499 	fc = get_link_form_control(link);
1500 	label = get_form_label(fc);
1501 	if (!label) return NULL;
1502 
1503 	if (!init_string(&str)) return NULL;
1504 
1505 	add_to_string(&str, _(label, term));
1506 
1507 	if (link->type != LINK_BUTTON && fc->name && fc->name[0]) {
1508 		add_form_attr_to_string(&str, term, N_("name"), fc->name);
1509 	}
1510 
1511 	switch (fc->type) {
1512 	case FC_CHECKBOX:
1513 	case FC_RADIO:
1514 	{
1515 		struct form_state *fs = find_form_state(doc_view, fc);
1516 
1517 		if (!fs->value || !fs->value[0])
1518 			break;
1519 
1520 		add_form_attr_to_string(&str, term, N_("value"), fs->value);
1521 		break;
1522 	}
1523 
1524 	case FC_TEXT:
1525 	case FC_PASSWORD:
1526 	case FC_FILE:
1527 	case FC_TEXTAREA:
1528 	{
1529 		struct uri *uri;
1530 		unsigned char *uristring;
1531 
1532 		if (form_field_is_readonly(fc)) {
1533 			add_form_attr_to_string(&str, term, N_("read only"), NULL);
1534 		}
1535 
1536 		/* Should we add info about entering insert mode or add info
1537 		 * about submitting the form? */
1538 		if (ses->insert_mode == INSERT_MODE_OFF) {
1539 			key = get_keystroke(ACT_EDIT_ENTER, KEYMAP_EDIT);
1540 
1541 			if (!key) break;
1542 
1543 			if (form_field_is_readonly(fc))
1544 				label = N_("press %s to navigate");
1545 			else
1546 				label = N_("press %s to edit");
1547 
1548 			add_to_string(&str, " (");
1549 			add_format_to_string(&str, _(label, term), key);
1550 			add_char_to_string(&str, ')');
1551 			mem_free(key);
1552 			break;
1553 
1554 		}
1555 
1556 		if (fc->type == FC_TEXTAREA)
1557 			break;
1558 
1559 		assert(fc->form);
1560 
1561 		if (!fc->form->action
1562 		    || (has_form_submit(fc->form)
1563 		        && !get_opt_bool("document.browse.forms.auto_submit")))
1564 			break;
1565 
1566 		uri = get_uri(fc->form->action, 0);
1567 		if (!uri) break;
1568 
1569 		/* Add the uri with password and post info stripped */
1570 		uristring = get_uri_string(uri, URI_PUBLIC);
1571 		done_uri(uri);
1572 
1573 		if (!uristring) break;
1574 
1575 		key = get_keystroke(ACT_EDIT_ENTER, KEYMAP_EDIT);
1576 		if (!key) {
1577 			mem_free(uristring);
1578 			break;
1579 		}
1580 
1581 		if (fc->form->method == FORM_METHOD_GET)
1582 			label = N_("press %s to submit to %s");
1583 		else
1584 			label = N_("press %s to post to %s");
1585 
1586 		add_to_string(&str, " (");
1587 		add_format_to_string(&str, _(label, term), key, uristring);
1588 		mem_free(uristring);
1589 		mem_free(key);
1590 
1591 		add_char_to_string(&str, ')');
1592 		break;
1593 	}
1594 	case FC_SUBMIT:
1595 	case FC_IMAGE:
1596 		add_char_to_string(&str, ' ');
1597 
1598 		assert(fc->form);
1599 		/* Add the uri with password and post info stripped */
1600 		add_string_uri_to_string(&str, fc->form->action, URI_PUBLIC);
1601 		break;
1602 
1603 	case FC_HIDDEN:
1604 	case FC_RESET:
1605 	case FC_BUTTON:
1606 	case FC_SELECT:
1607 		break;
1608 	}
1609 
1610 	if (link->accesskey
1611 	    && get_opt_bool("document.browse.accesskey.display")) {
1612 		add_to_string(&str, " (");
1613 		add_accesskey_to_string(&str, link->accesskey);
1614 		add_char_to_string(&str, ')');
1615 	}
1616 
1617 	return str.source;
1618 }
1619 
1620 static void
link_form_menu_func(struct terminal * term,void * link_number_,void * ses_)1621 link_form_menu_func(struct terminal *term, void *link_number_, void *ses_)
1622 {
1623 	struct session *ses = ses_;
1624 	struct document_view *doc_view;
1625 	int link_number = *(int *) link_number_;
1626 
1627 	mem_free(link_number_);
1628 
1629 	assert(term && ses);
1630 	if_assert_failed return;
1631 
1632 	doc_view = current_frame(ses);
1633 	if (!doc_view) return;
1634 
1635 	assert(doc_view->vs && doc_view->document);
1636 	if_assert_failed return;
1637 
1638 	jump_to_link_number(ses, doc_view, link_number);
1639 	refresh_view(ses, doc_view, 0);
1640 }
1641 
1642 void
link_form_menu(struct session * ses)1643 link_form_menu(struct session *ses)
1644 {
1645 	struct document_view *doc_view;
1646 	struct link *link;
1647 	struct menu_item *mi;
1648 	struct form_control *fc;
1649 	struct form *form;
1650 
1651 	assert(ses);
1652 	if_assert_failed return;
1653 
1654 	doc_view = current_frame(ses);
1655 	if (!doc_view) return;
1656 
1657 	assert(doc_view->vs && doc_view->document);
1658 	if_assert_failed return;
1659 
1660 	link = get_current_link(doc_view);
1661 	if (!link) return;
1662 
1663 	assert(link_is_form(link));
1664 
1665 	fc = get_link_form_control(link);
1666 	if (!fc) return;
1667 
1668 	form = fc->form;
1669 
1670 	mi = new_menu(FREE_LIST | FREE_TEXT | NO_INTL);
1671 	if (!mi) return;
1672 
1673 	foreach (fc, form->items) {
1674 		unsigned char *text;
1675 		unsigned char *rtext;
1676 		int link_number;
1677 		struct string str;
1678 
1679 		switch (fc->type) {
1680 		case FC_HIDDEN:
1681 			continue;
1682 
1683 		case FC_SUBMIT:
1684 		case FC_IMAGE:
1685 			if (!form->action)
1686 				text = N_("Useless button");
1687 			else
1688 				text = N_("Submit button");
1689 			break;
1690 
1691 		default:
1692 			text = get_form_label(fc);
1693 		}
1694 
1695 		link_number = get_form_control_link(doc_view->document, fc);
1696 		if (link_number < 0
1697 		    || !init_string(&str))
1698 			continue;
1699 
1700 		assert(text);
1701 		add_to_string(&str, _(text, ses->tab->term));
1702 
1703 		rtext = fc->name;
1704 		if (!rtext) rtext = fc->alt;
1705 
1706 		add_to_menu(&mi, str.source, rtext, ACT_MAIN_NONE,
1707 		            link_form_menu_func, intdup(link_number),
1708 		            FREE_DATA);
1709 	}
1710 
1711 	do_menu(ses->tab->term, mi, ses, 1);
1712 }
1713