1 /* Forms viewing/manipulation handling */
8 #define _GNU_SOURCE /* XXX: we want memrchr() ! */
15 #include <sys/types.h>
20 #include <fcntl.h> /* OS/2 needs this after sys/types.h */
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"
58 /* TODO: Some of these (particulary those encoding routines) would feel better
59 * in viewer/common/. --pasky */
61 struct submitted_value
*
62 init_submitted_value(unsigned char *name
, unsigned char *value
, enum form_type type
,
63 struct form_control
*fc
, int position
)
65 struct submitted_value
*sv
;
67 sv
= mem_alloc(sizeof(*sv
));
70 sv
->value
= stracpy(value
);
71 if (!sv
->value
) { mem_free(sv
); return NULL
; }
73 sv
->name
= stracpy(name
);
74 if (!sv
->name
) { mem_free(sv
->value
); mem_free(sv
); return NULL
; }
77 sv
->form_control
= fc
;
78 sv
->position
= position
;
84 done_submitted_value(struct submitted_value
*sv
)
87 mem_free_if(sv
->value
);
88 mem_free_if(sv
->name
);
93 fixup_select_state(struct form_control
*fc
, struct form_state
*fs
)
98 if_assert_failed
return;
101 && fs
->state
< fc
->nvalues
102 && !strcmp(fc
->values
[fs
->state
], fs
->value
))
105 for (i
= 0; i
< fc
->nvalues
; i
++)
106 if (!strcmp(fc
->values
[i
], fs
->value
)) {
113 mem_free_set(&fs
->value
, stracpy(fc
->nvalues
115 : (unsigned char *) ""));
120 selected_item(struct terminal
*term
, void *item_
, void *ses_
)
122 struct session
*ses
= ses_
;
123 int item
= (long) item_
;
124 struct document_view
*doc_view
;
126 struct form_state
*fs
;
127 struct form_control
*fc
;
130 if_assert_failed
return;
131 doc_view
= current_frame(ses
);
133 assert(doc_view
&& doc_view
->vs
&& doc_view
->document
);
134 if_assert_failed
return;
136 link
= get_current_link(doc_view
);
137 if (!link
|| link
->type
!= LINK_SELECT
) return;
139 fc
= get_link_form_control(link
);
140 fs
= find_form_state(doc_view
, fc
);
142 if (item
>= 0 && item
< fc
->nvalues
) {
144 mem_free_set(&fs
->value
, stracpy(fc
->values
[item
]));
146 fixup_select_state(fc
, fs
);
149 refresh_view(ses
, doc_view
, 0);
153 init_form_state(struct form_control
*fc
, struct form_state
*fs
)
156 if_assert_failed
return;
158 mem_free_set(&fs
->value
, NULL
);
163 #endif /* CONFIG_UTF_8 */
168 fs
->value
= stracpy(fc
->default_value
);
169 fs
->state
= strlen(fc
->default_value
);
172 fs
->utf8_pos
= strlen_utf8(&text
);
173 #endif /* CONFIG_UTF_8 */
177 fs
->value
= stracpy("");
181 #endif /* CONFIG_UTF_8 */
185 fs
->value
= stracpy(fc
->default_value
);
186 fs
->state
= fc
->default_state
;
187 fixup_select_state(fc
, fs
);
191 fs
->state
= fc
->default_state
;
198 fs
->value
= stracpy(fc
->default_value
);
205 find_form_state(struct document_view
*doc_view
, struct form_control
*fc
)
207 struct view_state
*vs
;
208 struct form_state
*fs
;
211 assert(doc_view
&& doc_view
->vs
&& fc
);
212 if_assert_failed
return NULL
;
217 if (n
>= vs
->form_info_len
) {
220 fs
= mem_align_alloc(&vs
->form_info
, vs
->form_info_len
, nn
, 0);
221 if (!fs
) return NULL
;
223 vs
->form_info_len
= nn
;
225 fs
= &vs
->form_info
[n
];
227 if (fs
->form_view
&& fs
->form_view
->form_num
== fc
->form
->form_num
228 && fs
->g_ctrl_num
== fc
->g_ctrl_num
229 && fs
->position
== fc
->position
230 && fs
->type
== fc
->type
)
233 mem_free_if(fs
->value
);
234 memset(fs
, 0, sizeof(*fs
));
235 fs
->form_view
= find_form_view(doc_view
, fc
->form
);
236 fs
->g_ctrl_num
= fc
->g_ctrl_num
;
237 fs
->position
= fc
->position
;
239 init_form_state(fc
, fs
);
244 struct form_control
*
245 find_form_control(struct document
*document
, struct form_state
*fs
)
247 struct form
*form
= find_form_by_form_view(document
, fs
->form_view
);
248 struct form_control
*fc
;
250 foreach (fc
, form
->items
) {
251 if (fs
->g_ctrl_num
== fc
->g_ctrl_num
252 && fs
->position
== fc
->position
253 && fs
->type
== fc
->type
)
261 find_form_view_in_vs(struct view_state
*vs
, int form_num
)
263 struct form_view
*fv
;
267 foreach (fv
, vs
->forms
)
268 if (fv
->form_num
== form_num
)
271 fv
= mem_calloc(1, sizeof(*fv
));
272 fv
->form_num
= form_num
;
273 add_to_list(vs
->forms
, fv
);
278 find_form_view(struct document_view
*doc_view
, struct form
*form
)
280 return find_form_view_in_vs(doc_view
->vs
, form
->form_num
);
284 find_form_by_form_view(struct document
*document
, struct form_view
*fv
)
288 foreach (form
, document
->forms
) {
289 if (form
->form_num
== fv
->form_num
)
297 get_current_state(struct session
*ses
)
299 struct document_view
*doc_view
;
301 struct form_state
*fs
;
304 if_assert_failed
return -1;
305 doc_view
= current_frame(ses
);
307 assert(doc_view
&& doc_view
->vs
&& doc_view
->document
);
308 if_assert_failed
return -1;
310 link
= get_current_link(doc_view
);
311 if (!link
|| link
->type
!= LINK_SELECT
) return -1;
313 fs
= find_form_state(doc_view
, get_link_form_control(link
));
314 if (fs
) return fs
->state
;
319 draw_form_entry(struct terminal
*term
, struct document_view
*doc_view
,
322 struct form_state
*fs
;
323 struct form_control
*fc
;
324 struct view_state
*vs
;
328 assert(term
&& doc_view
&& doc_view
->document
&& doc_view
->vs
&& link
);
329 if_assert_failed
return;
331 fc
= get_link_form_control(link
);
332 assertm(fc
, "link %d has no form control", (int) (link
- doc_view
->document
->links
));
333 if_assert_failed
return;
335 fs
= find_form_state(doc_view
, fc
);
338 box
= &doc_view
->box
;
345 unsigned char *text
, *end
;
346 #endif /* CONFIG_UTF_8 */
354 if (term
->utf8
) goto utf_8
;
355 #endif /* CONFIG_UTF_8 */
356 int_bounds(&fs
->vpos
, fs
->state
- fc
->size
+ 1, fs
->state
);
357 if (!link
->npoints
) break;
359 y
= link
->points
[0].y
+ dy
;
360 if (!row_is_in_box(box
, y
))
363 len
= strlen(fs
->value
) - fs
->vpos
;
364 x
= link
->points
[0].x
+ dx
;
366 for (i
= 0; i
< fc
->size
; i
++, x
++) {
369 if (!col_is_in_box(box
, x
)) continue;
371 if (fs
->value
&& i
>= -fs
->vpos
&& i
< len
)
372 data
= fc
->type
!= FC_PASSWORD
373 ? fs
->value
[i
+ fs
->vpos
] : '*';
377 draw_char_data(term
, x
, y
, data
);
383 end
= strchr(text
, '\0');
384 int_bounds(&fs
->vpos
, fs
->utf8_pos
- fc
->size
+ 1, fs
->utf8_pos
);
385 if (!link
->npoints
) break;
387 y
= link
->points
[0].y
+ dy
;
388 if (!row_is_in_box(box
, y
))
390 for (i
= 0; i
< fs
->vpos
; i
++) {
391 utf_8_to_unicode(&text
, end
);
394 len
= strlen_utf8(&s
);
395 x
= link
->points
[0].x
+ dx
;
397 for (i
= 0; i
< fc
->size
; i
++, x
++) {
400 if (!col_is_in_box(box
, x
)) continue;
402 if (fs
->value
&& i
>= -fs
->vpos
&& i
< len
)
403 data
= fc
->type
!= FC_PASSWORD
404 ? utf_8_to_unicode(&text
, end
) : '*';
408 draw_char_data(term
, x
, y
, data
);
411 #endif /* CONFIG_UTF_8 */
413 draw_textarea(term
, fs
, doc_view
, link
);
417 if (link
->npoints
< 2) break;
418 x
= link
->points
[1].x
+ dx
;
419 y
= link
->points
[1].y
+ dy
;
420 if (is_in_box(box
, x
, y
))
421 draw_char_data(term
, x
, y
, fs
->state
? 'X' : ' ');
424 fixup_select_state(fc
, fs
);
425 if (fs
->state
< fc
->nvalues
)
426 s
= fc
->labels
[fs
->state
];
428 /* XXX: when can this happen? --pasky */
431 if (term
->utf8
) goto utf_8_select
;
432 #endif /* CONFIG_UTF_8 */
433 len
= s
? strlen(s
) : 0;
434 for (i
= 0; i
< link
->npoints
; i
++) {
435 x
= link
->points
[i
].x
+ dx
;
436 y
= link
->points
[i
].y
+ dy
;
437 if (is_in_box(box
, x
, y
))
438 draw_char_data(term
, x
, y
, i
< len
? s
[i
] : '_');
444 end
= strchr(s
, '\0');
445 len
= strlen_utf8(&text
);
446 for (i
= 0; i
< link
->npoints
; i
++) {
447 x
= link
->points
[i
].x
+ dx
;
448 y
= link
->points
[i
].y
+ dy
;
449 if (is_in_box(box
, x
, y
))
450 draw_char_data(term
, x
, y
, i
< len
451 ? utf_8_to_unicode(&s
, end
) : '_');
454 #endif /* CONFIG_UTF_8 */
465 draw_forms(struct terminal
*term
, struct document_view
*doc_view
)
467 struct link
*l1
, *l2
;
469 assert(term
&& doc_view
);
470 if_assert_failed
return;
472 l1
= get_first_link(doc_view
);
473 l2
= get_last_link(doc_view
);
476 assertm(!l1
&& !l2
, "get_first_link == %p, get_last_link == %p", l1
, l2
);
477 /* Return path :-). */
481 struct form_control
*fc
= get_link_form_control(l1
);
484 #ifdef CONFIG_FORMHIST
485 if (fc
->type
== FC_TEXT
|| fc
->type
== FC_PASSWORD
) {
486 unsigned char *value
;
489 value
= get_form_history_value(fc
->form
->action
, fc
->name
);
492 mem_free_set(&fc
->default_value
,
495 #endif /* CONFIG_FORMHIST */
496 draw_form_entry(term
, doc_view
, l1
);
503 done_submitted_value_list(struct list_head
*list
)
505 struct submitted_value
*sv
, *svtmp
;
508 if_assert_failed
return;
510 foreach (sv
, *list
) {
513 del_from_list(svtmp
);
514 done_submitted_value(svtmp
);
519 add_submitted_value_to_list(struct form_control
*fc
,
520 struct form_state
*fs
,
521 struct list_head
*list
)
523 struct submitted_value
*sub
;
528 assert(fc
&& fs
&& list
);
531 position
= fc
->position
;
539 sub
= init_submitted_value(name
, fs
->value
, type
, fc
, position
);
540 if (sub
) add_to_list(*list
, sub
);
545 if (!fs
->state
) break;
552 sub
= init_submitted_value(name
, fs
->value
, type
, fc
,
554 if (sub
) add_to_list(*list
, sub
);
558 if (!fc
->nvalues
) break;
560 fixup_select_state(fc
, fs
);
561 sub
= init_submitted_value(name
, fs
->value
, type
, fc
, position
);
562 if (sub
) add_to_list(*list
, sub
);
566 name
= straconcat(fc
->name
, ".x", NULL
);
568 sub
= init_submitted_value(name
, "0", type
, fc
, position
);
570 if (sub
) add_to_list(*list
, sub
);
572 name
= straconcat(fc
->name
, ".y", NULL
);
574 sub
= init_submitted_value(name
, "0", type
, fc
, position
);
576 if (sub
) add_to_list(*list
, sub
);
583 sort_submitted_values(struct list_head
*list
)
586 struct submitted_value
*sub
;
589 foreach (sub
, *list
) if (list_has_next(*list
, sub
))
590 if (sub
->next
->position
< sub
->position
) {
591 struct submitted_value
*next
= sub
->next
;
594 add_at_pos(next
, sub
);
599 foreachback (sub
, *list
) if (list_has_next(*list
, sub
))
600 if (sub
->next
->position
< sub
->position
) {
601 struct submitted_value
*next
= sub
->next
;
604 add_at_pos(next
, sub
);
614 get_successful_controls(struct document_view
*doc_view
,
615 struct form_control
*fc
, struct list_head
*list
)
617 struct form_control
*fc2
;
619 assert(doc_view
&& fc
&& fc
->form
&& list
);
620 if_assert_failed
return;
622 foreach (fc2
, fc
->form
->items
) {
623 if (((fc2
->type
!= FC_SUBMIT
&&
624 fc2
->type
!= FC_IMAGE
&&
625 fc2
->type
!= FC_RESET
&&
626 fc2
->type
!= FC_BUTTON
) || fc2
== fc
)
627 && fc2
->name
&& fc2
->name
[0]) {
628 struct form_state
*fs
= find_form_state(doc_view
, fc2
);
632 add_submitted_value_to_list(fc2
, fs
, list
);
636 sort_submitted_values(list
);
640 encode_controls(struct list_head
*l
, struct string
*data
,
641 int cp_from
, int cp_to
)
643 struct submitted_value
*sv
;
644 struct conv_table
*convert_table
= NULL
;
648 if_assert_failed
return;
651 unsigned char *p2
= NULL
;
654 add_char_to_string(data
, '&');
658 encode_uri_string(data
, sv
->name
, strlen(sv
->name
), 1);
659 add_char_to_string(data
, '=');
661 /* Convert back to original encoding (see html_form_control()
662 * for the original recoding). */
663 if (sv
->type
== FC_TEXTAREA
) {
666 p
= encode_textarea(sv
);
669 convert_table
= get_translation_table(cp_from
, cp_to
);
671 p2
= convert_string(convert_table
, p
,
672 strlen(p
), -1, CSM_FORM
, NULL
, NULL
, NULL
);
675 } else if (sv
->type
== FC_TEXT
||
676 sv
->type
== FC_PASSWORD
) {
678 convert_table
= get_translation_table(cp_from
, cp_to
);
680 p2
= convert_string(convert_table
, sv
->value
,
681 strlen(sv
->value
), -1, CSM_FORM
, NULL
, NULL
, NULL
);
683 p2
= stracpy(sv
->value
);
687 encode_uri_string(data
, p2
, strlen(p2
), 1);
695 #define BOUNDARY_LENGTH 32
696 #define realloc_bound_ptrs(bptrs, bptrs_size) \
697 mem_align_alloc(bptrs, bptrs_size, bptrs_size + 1, 0xFF)
699 struct boundary_info
{
702 unsigned char string
[BOUNDARY_LENGTH
];
706 init_boundary(struct boundary_info
*boundary
)
708 memset(boundary
, 0, sizeof(*boundary
));
709 memset(boundary
->string
, '0', BOUNDARY_LENGTH
);
712 /* Add boundary to string and save the offset */
714 add_boundary(struct string
*data
, struct boundary_info
*boundary
)
716 add_to_string(data
, "--");
718 if (realloc_bound_ptrs(&boundary
->offsets
, boundary
->count
))
719 boundary
->offsets
[boundary
->count
++] = data
->length
;
721 add_bytes_to_string(data
, boundary
->string
, BOUNDARY_LENGTH
);
724 static inline unsigned char *
725 increment_boundary_counter(struct boundary_info
*boundary
)
729 /* This is just a decimal string incrementation */
730 for (j
= BOUNDARY_LENGTH
- 1; j
>= 0; j
--) {
731 if (boundary
->string
[j
]++ < '9')
732 return boundary
->string
;
734 boundary
->string
[j
] = '0';
737 INTERNAL("Form data boundary counter overflow");
743 check_boundary(struct string
*data
, struct boundary_info
*boundary
)
745 unsigned char *bound
= boundary
->string
;
748 /* Search between all boundaries. There is a starting and an ending
749 * boundary so only check the range of chars after the current offset
750 * and before the next offset. If some string in the form data matches
751 * the boundary string it is changed. */
752 for (i
= 0; i
< boundary
->count
- 1; i
++) {
753 /* Start after the boundary string and also jump past the
754 * "\r\nContent-Disposition: form-data; name=\"" string added
755 * before any form data. */
756 int start_offset
= boundary
->offsets
[i
] + BOUNDARY_LENGTH
+ 40;
758 /* End so that there is atleast BOUNDARY_LENGTH chars to
759 * compare. Subtract 2 char because there is no need to also
760 * compare the '--' prefix that is part of the boundary. */
761 int end_offset
= boundary
->offsets
[i
+ 1] - BOUNDARY_LENGTH
- 2;
762 unsigned char *pos
= data
->source
+ start_offset
;
763 unsigned char *end
= data
->source
+ end_offset
;
765 for (; pos
<= end
; pos
++) {
766 if (memcmp(pos
, bound
, BOUNDARY_LENGTH
))
769 /* If incrementing causes overflow bail out. There is
770 * no need to reset the boundary string with '0' since
771 * that is already done when incrementing. */
772 if (!increment_boundary_counter(boundary
))
775 /* Else start checking all boundaries using the new
782 /* Now update all the boundaries with the unique boundary string */
783 for (i
= 0; i
< boundary
->count
; i
++)
784 memcpy(data
->source
+ boundary
->offsets
[i
], bound
, BOUNDARY_LENGTH
);
787 /* FIXME: shouldn't we encode data at send time (in http.c) ? --Zas */
789 encode_multipart(struct session
*ses
, struct list_head
*l
, struct string
*data
,
790 struct boundary_info
*boundary
, int cp_from
, int cp_to
)
792 struct conv_table
*convert_table
= NULL
;
793 struct submitted_value
*sv
;
795 assert(ses
&& l
&& data
&& boundary
);
796 if_assert_failed
return;
798 init_boundary(boundary
);
801 add_boundary(data
, boundary
);
802 add_crlf_to_string(data
);
804 /* FIXME: name is not encoded.
806 * multipart/form-data contains a series of parts.
807 * Each part is expected to contain a content-disposition
808 * header where the value is "form-data" and a name attribute
809 * specifies the field name within the form,
810 * e.g., 'content-disposition: form-data; name="xxxxx"',
811 * where xxxxx is the field name corresponding to that field.
812 * Field names originally in non-ASCII character sets may be
813 * encoded using the method outlined in RFC 1522. */
814 add_to_string(data
, "Content-Disposition: form-data; name=\"");
815 add_to_string(data
, sv
->name
);
816 add_char_to_string(data
, '"');
818 if (sv
->type
== FC_FILE
) {
819 #define F_BUFLEN 1024
821 unsigned char buffer
[F_BUFLEN
];
822 unsigned char *extension
;
824 add_to_string(data
, "; filename=\"");
825 add_to_string(data
, get_filename_position(sv
->value
));
826 /* It sends bad data if the file name contains ", but
827 Netscape does the same */
828 /* FIXME: We should follow RFCs 1522, 1867,
829 * 2047 (updated by rfc 2231), to provide correct support
830 * for non-ASCII and special characters in values. --Zas */
831 add_char_to_string(data
, '"');
833 /* Add a Content-Type header if the type is configured */
834 extension
= strrchr(sv
->value
, '.');
836 unsigned char *type
= get_extension_content_type(extension
);
839 add_crlf_to_string(data
);
840 add_to_string(data
, "Content-Type: ");
841 add_to_string(data
, type
);
846 add_crlf_to_string(data
);
847 add_crlf_to_string(data
);
850 unsigned char *filename
;
852 if (get_cmd_opt_bool("anonymous")) {
857 /* FIXME: DO NOT COPY FILE IN MEMORY !! --Zas */
858 filename
= expand_tilde(sv
->value
);
859 if (!filename
) goto encode_error
;
861 fh
= open(filename
, O_RDONLY
);
864 if (fh
== -1) goto encode_error
;
867 ssize_t rd
= safe_read(fh
, buffer
, F_BUFLEN
);
875 add_bytes_to_string(data
, buffer
, rd
);
885 add_crlf_to_string(data
);
886 add_crlf_to_string(data
);
888 /* Convert back to original encoding (see
889 * html_form_control() for the original recoding). */
890 if (sv
->type
== FC_TEXT
|| sv
->type
== FC_PASSWORD
||
891 sv
->type
== FC_TEXTAREA
) {
895 convert_table
= get_translation_table(cp_from
,
898 p
= convert_string(convert_table
, sv
->value
,
899 strlen(sv
->value
), -1, CSM_FORM
, NULL
,
902 add_to_string(data
, p
);
906 add_to_string(data
, sv
->value
);
910 add_crlf_to_string(data
);
914 add_boundary(data
, boundary
);
915 add_to_string(data
, "--\r\n");
917 check_boundary(data
, boundary
);
919 mem_free_if(boundary
->offsets
);
923 mem_free_if(boundary
->offsets
);
926 /* XXX: This error message should move elsewhere. --Zas */
927 info_box(ses
->tab
->term
, MSGBOX_FREE_TEXT
,
928 N_("Error while posting form"), ALIGN_CENTER
,
929 msg_text(ses
->tab
->term
, N_("Could not load file %s: %s"),
930 sv
->value
, strerror(errno
)));
934 encode_newlines(struct string
*string
, unsigned char *data
)
936 for (; *data
; data
++) {
937 if (*data
== '\n' || *data
== '\r') {
938 unsigned char buffer
[3];
942 buffer
[1] = hx((((int) *data
) & 0xF0) >> 4);
943 buffer
[2] = hx(((int) *data
) & 0xF);
944 add_bytes_to_string(string
, buffer
, 3);
946 add_char_to_string(string
, *data
);
952 encode_text_plain(struct list_head
*l
, struct string
*data
,
953 int cp_from
, int cp_to
)
955 struct submitted_value
*sv
;
956 struct conv_table
*convert_table
= get_translation_table(cp_from
, cp_to
);
959 if_assert_failed
return;
962 unsigned char *area51
= NULL
;
963 unsigned char *value
= sv
->value
;
965 add_to_string(data
, sv
->name
);
966 add_char_to_string(data
, '=');
970 value
= area51
= encode_textarea(sv
);
975 /* Convert back to original encoding (see
976 * html_form_control() for the original recoding). */
977 value
= convert_string(convert_table
, value
,
978 strlen(value
), -1, CSM_FORM
,
981 /* Falling right through to free that textarea stuff */
984 /* Did the conversion fail? */
987 encode_newlines(data
, value
);
989 /* Free if we did convert something */
990 if (value
!= sv
->value
) mem_free(value
);
993 add_crlf_to_string(data
);
998 do_reset_form(struct document_view
*doc_view
, struct form
*form
)
1000 struct form_control
*fc
;
1002 assert(doc_view
&& doc_view
->document
);
1003 if_assert_failed
return;
1005 foreach (fc
, form
->items
) {
1006 struct form_state
*fs
= find_form_state(doc_view
, fc
);
1008 if (fs
) init_form_state(fc
, fs
);
1012 enum frame_event_status
1013 reset_form(struct session
*ses
, struct document_view
*doc_view
, int a
)
1015 struct link
*link
= get_current_link(doc_view
);
1017 if (!link
) return FRAME_EVENT_OK
;
1019 do_reset_form(doc_view
, get_link_form_control(link
)->form
);
1020 draw_forms(ses
->tab
->term
, doc_view
);
1022 /* Could be the refresh return value and then ditch the draw_forms()
1024 return FRAME_EVENT_OK
;
1028 get_form_uri(struct session
*ses
, struct document_view
*doc_view
,
1029 struct form_control
*fc
)
1031 struct boundary_info boundary
;
1032 INIT_LIST_HEAD(submit
);
1039 assert(ses
&& ses
->tab
&& ses
->tab
->term
);
1040 if_assert_failed
return NULL
;
1041 assert(doc_view
&& doc_view
->document
&& fc
&& fc
->form
);
1042 if_assert_failed
return NULL
;
1046 if (fc
->type
== FC_RESET
) {
1047 do_reset_form(doc_view
, form
);
1052 || !init_string(&data
))
1055 get_successful_controls(doc_view
, fc
, &submit
);
1057 cp_from
= get_opt_codepage_tree(ses
->tab
->term
->spec
, "charset");
1058 cp_to
= doc_view
->document
->cp
;
1059 switch (form
->method
) {
1060 case FORM_METHOD_GET
:
1061 case FORM_METHOD_POST
:
1062 encode_controls(&submit
, &data
, cp_from
, cp_to
);
1065 case FORM_METHOD_POST_MP
:
1066 encode_multipart(ses
, &submit
, &data
, &boundary
, cp_from
, cp_to
);
1069 case FORM_METHOD_POST_TEXT_PLAIN
:
1070 encode_text_plain(&submit
, &data
, cp_from
, cp_to
);
1073 #ifdef CONFIG_FORMHIST
1074 /* XXX: We check data.source here because a NULL value can indicate
1075 * not only a memory allocation failure, but also an error reading
1076 * a file that is to be uploaded. TODO: Distinguish between
1077 * these two classes of errors (is it worth it?). -- Miciah */
1079 && get_opt_bool("document.browse.forms.show_formhist"))
1080 memorize_form(ses
, &submit
, form
);
1083 done_submitted_value_list(&submit
);
1086 || !init_string(&go
)) {
1091 switch (form
->method
) {
1092 case FORM_METHOD_GET
:
1094 unsigned char *pos
= strchr(form
->action
, '#');
1097 add_bytes_to_string(&go
, form
->action
, pos
- form
->action
);
1099 add_to_string(&go
, form
->action
);
1102 if (strchr(go
.source
, '?'))
1103 add_char_to_string(&go
, '&');
1105 add_char_to_string(&go
, '?');
1107 add_string_to_string(&go
, &data
);
1109 if (pos
) add_to_string(&go
, pos
);
1112 case FORM_METHOD_POST
:
1113 case FORM_METHOD_POST_MP
:
1114 case FORM_METHOD_POST_TEXT_PLAIN
:
1116 /* Note that we end content type here by a simple '\n',
1117 * replaced later by correct '\r\n' in http_send_header(). */
1120 add_to_string(&go
, form
->action
);
1121 add_char_to_string(&go
, POST_CHAR
);
1122 if (form
->method
== FORM_METHOD_POST
) {
1123 add_to_string(&go
, "application/x-www-form-urlencoded\n");
1125 } else if (form
->method
== FORM_METHOD_POST_TEXT_PLAIN
) {
1126 /* Dunno about this one but we don't want the full
1127 * hextcat thingy. --jonas */
1128 add_to_string(&go
, "text/plain\n");
1129 add_to_string(&go
, data
.source
);
1133 add_to_string(&go
, "multipart/form-data; boundary=");
1134 add_bytes_to_string(&go
, boundary
.string
, BOUNDARY_LENGTH
);
1135 add_char_to_string(&go
, '\n');
1138 for (i
= 0; i
< data
.length
; i
++) {
1141 ulonghexcat(p
, NULL
, (int) data
.source
[i
], 2, '0', 0);
1142 add_to_string(&go
, p
);
1149 uri
= get_uri(go
.source
, 0);
1151 if (uri
) uri
->form
= 1;
1156 #undef BOUNDARY_LENGTH
1159 enum frame_event_status
1160 submit_form(struct session
*ses
, struct document_view
*doc_view
, int do_reload
)
1162 goto_current_link(ses
, doc_view
, do_reload
);
1163 return FRAME_EVENT_OK
;
1167 submit_given_form(struct session
*ses
, struct document_view
*doc_view
,
1168 struct form
*form
, int do_reload
)
1170 /* Added support for submitting forms in hidden
1171 * links in 1.285, commented code can safely be removed once we have made sure the new
1172 * code does the right thing. */
1175 struct document
*document
= doc_view
->document
;
1178 for (link
= 0; link
< document
->nlinks
; link
++) {
1179 struct form_control
*fc
= get_link_form_control(&document
->links
[link
]);
1181 if (fc
&& fc
->form
== form
) {
1182 doc_view
->vs
->current_link
= link
;
1183 submit_form(ses
, doc_view
, 0);
1188 if (!list_empty(form
->items
)) {
1189 struct form_control
*fc
= (struct form_control
*)form
->items
.next
;
1191 enum cache_mode mode
= do_reload
? CACHE_MODE_FORCE_RELOAD
: CACHE_MODE_NORMAL
;
1194 uri
= get_form_uri(ses
, doc_view
, fc
);
1196 goto_uri_frame(ses
, uri
, form
->target
, mode
);
1202 auto_submit_form(struct session
*ses
)
1204 struct document
*document
= ses
->doc_view
->document
;
1206 if (!list_empty(document
->forms
))
1207 submit_given_form(ses
, ses
->doc_view
, document
->forms
.next
, 0);
1213 set_file_form_state(struct terminal
*term
, void *filename_
, void *fs_
)
1215 unsigned char *filename
= filename_
;
1216 struct form_state
*fs
= fs_
;
1218 /* The menu code doesn't free the filename data */
1219 mem_free_set(&fs
->value
, filename
);
1220 fs
->state
= strlen(filename
);
1221 redraw_terminal(term
);
1226 file_form_menu(struct terminal
*term
, void *path_
, void *fs_
)
1228 unsigned char *path
= path_
;
1229 struct form_state
*fs
= fs_
;
1231 /* FIXME: It doesn't work for ../../ */
1233 int valuelen
= strlen(fs
->value
);
1234 int pathlen
= strlen(path
);
1235 int no_elevator
= 0;
1237 /* Don't add elevators for subdirs menus */
1238 /* It is not perfect at all because fs->value is not updated for each
1239 * newly opened file menu. Maybe it should be dropped. */
1240 for (; valuelen
< pathlen
; valuelen
++) {
1241 if (dir_sep(path
[valuelen
- 1])) {
1248 auto_complete_file(term
, 0 /* no_elevator */, path
,
1249 set_file_form_state
,
1250 file_form_menu
, fs
);
1254 enum frame_event_status
1255 field_op(struct session
*ses
, struct document_view
*doc_view
,
1256 struct link
*link
, struct term_event
*ev
)
1258 struct form_control
*fc
;
1259 struct form_state
*fs
;
1260 enum edit_action action_id
;
1261 unsigned char *text
;
1263 enum frame_event_status status
= FRAME_EVENT_REFRESH
;
1265 int utf8
= ses
->tab
->term
->utf8
;
1266 #endif /* CONFIG_UTF_8 */
1268 assert(ses
&& doc_view
&& link
&& ev
);
1269 if_assert_failed
return FRAME_EVENT_OK
;
1271 fc
= get_link_form_control(link
);
1272 assertm(fc
, "link has no form control");
1273 if_assert_failed
return FRAME_EVENT_OK
;
1275 if (fc
->mode
== FORM_MODE_DISABLED
|| ev
->ev
!= EVENT_KBD
1276 || ses
->insert_mode
== INSERT_MODE_OFF
)
1277 return FRAME_EVENT_IGNORED
;
1279 action_id
= kbd_action(KEYMAP_EDIT
, ev
, NULL
);
1281 fs
= find_form_state(doc_view
, fc
);
1282 if (!fs
|| !fs
->value
) return FRAME_EVENT_OK
;
1284 switch (action_id
) {
1288 unsigned char *text
= fs
->value
;
1289 unsigned char *end
= fs
->value
+ fs
->state
- 1;
1290 int old
= fs
->state
;
1292 while (utf_8_to_unicode(&text
, end
) != UCS_NO_CHAR
);
1293 fs
->state
= (int)(text
- fs
->value
);
1294 if (old
!= fs
->state
) fs
->utf8_pos
--;
1296 #endif /* CONFIG_UTF_8 */
1297 fs
->state
= int_max(fs
->state
- 1, 0);
1299 case ACT_EDIT_RIGHT
:
1302 unsigned char *text
= fs
->value
+ fs
->state
;
1303 unsigned char *end
= strchr(text
, '\0');
1304 int old
= fs
->state
;
1306 utf_8_to_unicode(&text
, end
);
1307 fs
->state
= (int)(text
- fs
->value
);
1308 if (old
!= fs
->state
) fs
->utf8_pos
++;
1310 #endif /* CONFIG_UTF_8 */
1311 fs
->state
= int_min(fs
->state
+ 1, strlen(fs
->value
));
1314 if (fc
->type
== FC_TEXTAREA
) {
1316 status
= textarea_op_home(fs
, fc
, utf8
);
1318 status
= textarea_op_home(fs
, fc
);
1319 #endif /* CONFIG_UTF_8 */
1324 #endif /* CONFIG_UTF_8 */
1328 if (fc
->type
!= FC_TEXTAREA
)
1329 status
= FRAME_EVENT_IGNORED
;
1332 status
= textarea_op_up(fs
, fc
, utf8
);
1334 status
= textarea_op_up(fs
, fc
);
1335 #endif /* CONFIG_UTF_8 */
1338 if (fc
->type
!= FC_TEXTAREA
)
1339 status
= FRAME_EVENT_IGNORED
;
1342 status
= textarea_op_down(fs
, fc
, utf8
);
1344 status
= textarea_op_down(fs
, fc
);
1345 #endif /* CONFIG_UTF_8 */
1348 if (fc
->type
== FC_TEXTAREA
) {
1350 status
= textarea_op_end(fs
, fc
, utf8
);
1352 status
= textarea_op_end(fs
, fc
);
1353 #endif /* CONFIG_UTF_8 */
1355 fs
->state
= strlen(fs
->value
);
1358 unsigned char *text
= fs
->value
;
1360 fs
->utf8_pos
= strlen_utf8(&text
);
1362 #endif /* CONFIG_UTF_8 */
1365 case ACT_EDIT_BEGINNING_OF_BUFFER
:
1366 if (fc
->type
== FC_TEXTAREA
) {
1368 status
= textarea_op_bob(fs
, fc
, utf8
);
1370 status
= textarea_op_bob(fs
, fc
);
1371 #endif /* CONFIG_UTF_8 */
1376 #endif /* CONFIG_UTF_8 */
1379 case ACT_EDIT_END_OF_BUFFER
:
1380 if (fc
->type
== FC_TEXTAREA
) {
1382 status
= textarea_op_eob(fs
, fc
, utf8
);
1384 status
= textarea_op_eob(fs
, fc
);
1385 #endif /* CONFIG_UTF_8 */
1387 fs
->state
= strlen(fs
->value
);
1390 unsigned char *text
= fs
->value
;
1392 fs
->utf8_pos
= strlen_utf8(&text
);
1394 #endif /* CONFIG_UTF_8 */
1397 case ACT_EDIT_OPEN_EXTERNAL
:
1398 if (form_field_is_readonly(fc
))
1399 status
= FRAME_EVENT_IGNORED
;
1400 else if (fc
->type
== FC_TEXTAREA
)
1401 textarea_edit(0, ses
->tab
->term
, fs
, doc_view
, link
);
1403 case ACT_EDIT_COPY_CLIPBOARD
:
1404 set_clipboard_text(fs
->value
);
1405 status
= FRAME_EVENT_OK
;
1407 case ACT_EDIT_CUT_CLIPBOARD
:
1408 set_clipboard_text(fs
->value
);
1409 if (!form_field_is_readonly(fc
))
1414 #endif /* CONFIG_UTF_8 */
1416 case ACT_EDIT_PASTE_CLIPBOARD
:
1417 if (form_field_is_readonly(fc
)) break;
1419 text
= get_clipboard_text();
1422 length
= strlen(text
);
1423 if (length
<= fc
->maxlength
) {
1424 unsigned char *v
= mem_realloc(fs
->value
, length
+ 1);
1428 memmove(v
, text
, length
+ 1);
1429 fs
->state
= strlen(fs
->value
);
1431 if (utf8
&& fc
->type
!= FC_TEXTAREA
) {
1432 unsigned char *text
= fs
->value
;
1434 fs
->utf8_pos
= strlen_utf8(&text
);
1436 #endif /* CONFIG_UTF_8 */
1441 case ACT_EDIT_ENTER
:
1442 if (fc
->type
== FC_TEXTAREA
) {
1444 status
= textarea_op_enter(fs
, fc
, utf8
);
1446 status
= textarea_op_enter(fs
, fc
);
1447 #endif /* CONFIG_UTF_8 */
1451 /* Set status to ok if either it is not possible to
1452 * submit the form or the posting fails. */
1453 /* FIXME: We should maybe have ACT_EDIT_ENTER_RELOAD */
1454 if ((has_form_submit(fc
->form
)
1455 && !get_opt_bool("document.browse.forms.auto_submit"))
1456 || goto_current_link(ses
, doc_view
, 0)) {
1457 if (ses
->insert_mode
== INSERT_MODE_ON
)
1458 ses
->insert_mode
= INSERT_MODE_OFF
;
1459 status
= FRAME_EVENT_OK
;
1462 case ACT_EDIT_BACKSPACE
:
1463 if (form_field_is_readonly(fc
)) {
1464 status
= FRAME_EVENT_IGNORED
;
1469 status
= FRAME_EVENT_OK
;
1475 unsigned char *text
= fs
->value
;
1476 unsigned char *end
= fs
->value
+ fs
->state
;
1478 for (i
= 0; i
< fs
->utf8_pos
- 1; i
++)
1479 utf_8_to_unicode(&text
, end
);
1480 length
= strlen(end
) + 1;
1481 memmove(text
, end
, length
);
1482 fs
->state
= (int)(text
- fs
->value
);
1486 #endif /* CONFIG_UTF_8 */
1487 length
= strlen(fs
->value
+ fs
->state
) + 1;
1488 text
= fs
->value
+ fs
->state
;
1490 memmove(text
- 1, text
, length
);
1493 case ACT_EDIT_DELETE
:
1494 if (form_field_is_readonly(fc
)) {
1495 status
= FRAME_EVENT_IGNORED
;
1499 length
= strlen(fs
->value
);
1500 if (fs
->state
>= length
) {
1501 status
= FRAME_EVENT_OK
;
1506 unsigned char *end
= fs
->value
+ length
;
1507 unsigned char *text
= fs
->value
+ fs
->state
;
1508 unsigned char *old
= text
;
1510 utf_8_to_unicode(&text
, end
);
1513 (int)(end
- text
) + 1);
1517 #endif /* CONFIG_UTF_8 */
1518 text
= fs
->value
+ fs
->state
;
1520 memmove(text
, text
+ 1, length
- fs
->state
);
1522 case ACT_EDIT_KILL_TO_BOL
:
1523 if (form_field_is_readonly(fc
)) {
1524 status
= FRAME_EVENT_IGNORED
;
1528 if (fs
->state
<= 0) {
1529 status
= FRAME_EVENT_OK
;
1533 text
= memrchr(fs
->value
, ASCII_LF
, fs
->state
);
1535 /* Leave the new-line character if it does not
1536 * immediately precede the cursor. */
1537 if (text
!= &fs
->value
[fs
->state
- 1])
1543 length
= strlen(fs
->value
+ fs
->state
) + 1;
1544 memmove(text
, fs
->value
+ fs
->state
, length
);
1546 fs
->state
= (int) (text
- fs
->value
);
1548 if (utf8
&& fc
->type
!= FC_TEXTAREA
) {
1549 unsigned char *text
= fs
->value
;
1551 fs
->utf8_pos
= strlen_utf8(&text
);
1553 #endif /* CONFIG_UTF_8 */
1555 case ACT_EDIT_KILL_TO_EOL
:
1556 if (form_field_is_readonly(fc
)) {
1557 status
= FRAME_EVENT_IGNORED
;
1561 if (!fs
->value
[fs
->state
]) {
1562 status
= FRAME_EVENT_OK
;
1566 text
= strchr(fs
->value
+ fs
->state
, ASCII_LF
);
1568 fs
->value
[fs
->state
] = '\0';
1572 if (fs
->value
[fs
->state
] == ASCII_LF
)
1575 memmove(fs
->value
+ fs
->state
, text
, strlen(text
) + 1);
1578 case ACT_EDIT_AUTO_COMPLETE
:
1579 if (fc
->type
!= FC_FILE
1580 || form_field_is_readonly(fc
)) {
1581 status
= FRAME_EVENT_IGNORED
;
1585 file_form_menu(ses
->tab
->term
, fs
->value
, fs
);
1588 case ACT_EDIT_CANCEL
:
1589 if (ses
->insert_mode
== INSERT_MODE_ON
)
1590 ses
->insert_mode
= INSERT_MODE_OFF
;
1592 status
= FRAME_EVENT_IGNORED
;
1595 case ACT_EDIT_REDRAW
:
1596 redraw_terminal_cls(ses
->tab
->term
);
1597 status
= FRAME_EVENT_OK
;
1601 if (!check_kbd_textinput_key(ev
)) {
1602 status
= FRAME_EVENT_IGNORED
;
1606 if (form_field_is_readonly(fc
)
1607 || strlen(fs
->value
) >= fc
->maxlength
1608 #ifndef CONFIG_UTF_8
1609 || !insert_in_string(&fs
->value
, fs
->state
, "?", 1)
1610 #endif /* CONFIG_UTF_8 */
1613 status
= FRAME_EVENT_OK
;
1618 static unsigned char buf
[7];
1624 buf
[i
++] = get_kbd_key(ev
);
1626 data
= utf_8_to_unicode(&t
, buf
+ i
);
1627 if (data
!= UCS_NO_CHAR
) {
1628 if (!insert_in_string(&fs
->value
, fs
->state
, buf
, i
)) {
1630 return FRAME_EVENT_OK
;
1641 return FRAME_EVENT_OK
;
1644 if (!insert_in_string(&fs
->value
, fs
->state
, "?", 1))
1645 return FRAME_EVENT_OK
;
1646 fs
->value
[fs
->state
++] = get_kbd_key(ev
);
1649 fs
->value
[fs
->state
++] = get_kbd_key(ev
);
1650 #endif /* CONFIG_UTF_8 */
1658 get_form_label(struct form_control
*fc
)
1663 return N_("Reset form");
1665 return N_("Harmless button");
1670 if (!fc
->form
->action
) return NULL
;
1672 if (fc
->form
->method
== FORM_METHOD_GET
)
1673 return N_("Submit form to");
1674 return N_("Post form to");
1676 return N_("Radio button");
1678 return N_("Checkbox");
1680 return N_("Select field");
1682 return N_("Text field");
1684 return N_("Text area");
1686 return N_("File upload");
1688 return N_("Password field");
1695 add_form_attr_to_string(struct string
*string
, struct terminal
*term
,
1696 unsigned char *name
, unsigned char *value
)
1698 add_to_string(string
, ", ");
1699 add_to_string(string
, _(name
, term
));
1701 add_char_to_string(string
, ' ');
1702 add_to_string(string
, value
);
1707 get_form_info(struct session
*ses
, struct document_view
*doc_view
)
1709 struct terminal
*term
= ses
->tab
->term
;
1710 struct link
*link
= get_current_link(doc_view
);
1711 struct form_control
*fc
;
1712 unsigned char *label
, *key
;
1717 fc
= get_link_form_control(link
);
1718 label
= get_form_label(fc
);
1719 if (!label
) return NULL
;
1721 if (!init_string(&str
)) return NULL
;
1723 add_to_string(&str
, _(label
, term
));
1725 if (link
->type
!= LINK_BUTTON
&& fc
->name
&& fc
->name
[0]) {
1726 add_form_attr_to_string(&str
, term
, N_("name"), fc
->name
);
1733 struct form_state
*fs
= find_form_state(doc_view
, fc
);
1735 if (!fs
->value
|| !fs
->value
[0])
1738 add_form_attr_to_string(&str
, term
, N_("value"), fs
->value
);
1748 unsigned char *uristring
;
1750 if (form_field_is_readonly(fc
)) {
1751 add_form_attr_to_string(&str
, term
, N_("read only"), NULL
);
1754 /* Should we add info about entering insert mode or add info
1755 * about submitting the form? */
1756 if (ses
->insert_mode
== INSERT_MODE_OFF
) {
1757 key
= get_keystroke(ACT_EDIT_ENTER
, KEYMAP_EDIT
);
1761 if (form_field_is_readonly(fc
))
1762 label
= N_("press %s to navigate");
1764 label
= N_("press %s to edit");
1766 add_to_string(&str
, " (");
1767 add_format_to_string(&str
, _(label
, term
), key
);
1768 add_char_to_string(&str
, ')');
1774 if (fc
->type
== FC_TEXTAREA
)
1779 if (!fc
->form
->action
1780 || (has_form_submit(fc
->form
)
1781 && !get_opt_bool("document.browse.forms.auto_submit")))
1784 uri
= get_uri(fc
->form
->action
, 0);
1787 /* Add the uri with password and post info stripped */
1788 uristring
= get_uri_string(uri
, URI_PUBLIC
);
1791 if (!uristring
) break;
1793 key
= get_keystroke(ACT_EDIT_ENTER
, KEYMAP_EDIT
);
1795 mem_free(uristring
);
1799 if (fc
->form
->method
== FORM_METHOD_GET
)
1800 label
= N_("press %s to submit to %s");
1802 label
= N_("press %s to post to %s");
1804 add_to_string(&str
, " (");
1805 add_format_to_string(&str
, _(label
, term
), key
, uristring
);
1806 mem_free(uristring
);
1809 add_char_to_string(&str
, ')');
1814 add_char_to_string(&str
, ' ');
1817 /* Add the uri with password and post info stripped */
1818 add_string_uri_to_string(&str
, fc
->form
->action
, URI_PUBLIC
);
1829 && get_opt_bool("document.browse.accesskey.display")) {
1830 add_to_string(&str
, " (");
1831 add_accesskey_to_string(&str
, link
->accesskey
);
1832 add_char_to_string(&str
, ')');
1839 link_form_menu_func(struct terminal
*term
, void *link_number_
, void *ses_
)
1841 struct session
*ses
= ses_
;
1842 struct document_view
*doc_view
;
1843 int link_number
= *(int *) link_number_
;
1845 mem_free(link_number_
);
1847 assert(term
&& ses
);
1848 if_assert_failed
return;
1850 doc_view
= current_frame(ses
);
1851 if (!doc_view
) return;
1853 assert(doc_view
->vs
&& doc_view
->document
);
1854 if_assert_failed
return;
1856 jump_to_link_number(ses
, doc_view
, link_number
);
1857 refresh_view(ses
, doc_view
, 0);
1861 link_form_menu(struct session
*ses
)
1863 struct document_view
*doc_view
;
1865 struct menu_item
*mi
;
1866 struct form_control
*fc
;
1870 if_assert_failed
return;
1872 doc_view
= current_frame(ses
);
1873 if (!doc_view
) return;
1875 assert(doc_view
->vs
&& doc_view
->document
);
1876 if_assert_failed
return;
1878 link
= get_current_link(doc_view
);
1881 assert(link_is_form(link
));
1883 fc
= get_link_form_control(link
);
1888 mi
= new_menu(FREE_LIST
| FREE_TEXT
| NO_INTL
);
1891 foreach (fc
, form
->items
) {
1892 unsigned char *text
;
1893 unsigned char *rtext
;
1904 text
= N_("Useless button");
1906 text
= N_("Submit button");
1910 text
= get_form_label(fc
);
1913 link_number
= get_form_control_link(doc_view
->document
, fc
);
1915 || !init_string(&str
))
1919 add_to_string(&str
, _(text
, ses
->tab
->term
));
1922 if (!rtext
) rtext
= fc
->alt
;
1924 add_to_menu(&mi
, str
.source
, rtext
, ACT_MAIN_NONE
,
1925 link_form_menu_func
, intdup(link_number
),
1929 do_menu(ses
->tab
->term
, mi
, ses
, 1);