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
);
164 fs
->value
= stracpy(fc
->default_value
);
165 fs
->state
= strlen(fc
->default_value
);
169 fs
->value
= stracpy("");
174 fs
->value
= stracpy(fc
->default_value
);
175 fs
->state
= fc
->default_state
;
176 fixup_select_state(fc
, fs
);
180 fs
->state
= fc
->default_state
;
187 fs
->value
= stracpy(fc
->default_value
);
194 find_form_state(struct document_view
*doc_view
, struct form_control
*fc
)
196 struct view_state
*vs
;
197 struct form_state
*fs
;
200 assert(doc_view
&& doc_view
->vs
&& fc
);
201 if_assert_failed
return NULL
;
206 if (n
>= vs
->form_info_len
) {
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
));
214 vs
->form_info_len
= nn
;
216 fs
= &vs
->form_info
[n
];
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
)
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
;
230 init_form_state(fc
, fs
);
235 struct form_control
*
236 find_form_control(struct document
*document
, struct form_state
*fs
)
238 struct form
*form
= find_form_by_form_view(document
, fs
->form_view
);
239 struct form_control
*fc
;
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
)
252 find_form_view_in_vs(struct view_state
*vs
, int form_num
)
254 struct form_view
*fv
;
258 foreach (fv
, vs
->forms
)
259 if (fv
->form_num
== form_num
)
262 fv
= mem_calloc(1, sizeof(*fv
));
263 fv
->form_num
= form_num
;
264 add_to_list(vs
->forms
, fv
);
269 find_form_view(struct document_view
*doc_view
, struct form
*form
)
271 return find_form_view_in_vs(doc_view
->vs
, form
->form_num
);
275 find_form_by_form_view(struct document
*document
, struct form_view
*fv
)
279 foreach (form
, document
->forms
) {
280 if (form
->form_num
== fv
->form_num
)
288 get_current_state(struct session
*ses
)
290 struct document_view
*doc_view
;
292 struct form_state
*fs
;
295 if_assert_failed
return -1;
296 doc_view
= current_frame(ses
);
298 assert(doc_view
&& doc_view
->vs
&& doc_view
->document
);
299 if_assert_failed
return -1;
301 link
= get_current_link(doc_view
);
302 if (!link
|| link
->type
!= LINK_SELECT
) return -1;
304 fs
= find_form_state(doc_view
, get_link_form_control(link
));
305 if (fs
) return fs
->state
;
310 draw_form_entry(struct terminal
*term
, struct document_view
*doc_view
,
313 struct form_state
*fs
;
314 struct form_control
*fc
;
315 struct view_state
*vs
;
319 assert(term
&& doc_view
&& doc_view
->document
&& doc_view
->vs
&& link
);
320 if_assert_failed
return;
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;
326 fs
= find_form_state(doc_view
, fc
);
329 box
= &doc_view
->box
;
341 int_bounds(&fs
->vpos
, fs
->state
- fc
->size
+ 1, fs
->state
);
342 if (!link
->npoints
) break;
344 y
= link
->points
[0].y
+ dy
;
345 if (!row_is_in_box(box
, y
))
348 len
= strlen(fs
->value
) - fs
->vpos
;
349 x
= link
->points
[0].x
+ dx
;
351 for (i
= 0; i
< fc
->size
; i
++, x
++) {
354 if (!col_is_in_box(box
, x
)) continue;
356 if (fs
->value
&& i
>= -fs
->vpos
&& i
< len
)
357 data
= fc
->type
!= FC_PASSWORD
358 ? fs
->value
[i
+ fs
->vpos
] : '*';
362 draw_char_data(term
, x
, y
, data
);
366 draw_textarea(term
, fs
, doc_view
, link
);
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' : ' ');
377 fixup_select_state(fc
, fs
);
378 if (fs
->state
< fc
->nvalues
)
379 s
= fc
->labels
[fs
->state
];
381 /* XXX: when can this happen? --pasky */
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
] : '_');
401 draw_forms(struct terminal
*term
, struct document_view
*doc_view
)
403 struct link
*l1
, *l2
;
405 assert(term
&& doc_view
);
406 if_assert_failed
return;
408 l1
= get_first_link(doc_view
);
409 l2
= get_last_link(doc_view
);
412 assertm(!l1
&& !l2
, "get_first_link == %p, get_last_link == %p", l1
, l2
);
413 /* Return path :-). */
417 struct form_control
*fc
= get_link_form_control(l1
);
420 #ifdef CONFIG_FORMHIST
421 if (fc
->type
== FC_TEXT
|| fc
->type
== FC_PASSWORD
) {
422 unsigned char *value
;
425 value
= get_form_history_value(fc
->form
->action
, fc
->name
);
428 mem_free_set(&fc
->default_value
,
431 #endif /* CONFIG_FORMHIST */
432 draw_form_entry(term
, doc_view
, l1
);
439 done_submitted_value_list(struct list_head
*list
)
441 struct submitted_value
*sv
, *svtmp
;
444 if_assert_failed
return;
446 foreach (sv
, *list
) {
449 del_from_list(svtmp
);
450 done_submitted_value(svtmp
);
455 add_submitted_value_to_list(struct form_control
*fc
,
456 struct form_state
*fs
,
457 struct list_head
*list
)
459 struct submitted_value
*sub
;
464 assert(fc
&& fs
&& list
);
467 position
= fc
->position
;
475 sub
= init_submitted_value(name
, fs
->value
, type
, fc
, position
);
476 if (sub
) add_to_list(*list
, sub
);
481 if (!fs
->state
) break;
488 sub
= init_submitted_value(name
, fs
->value
, type
, fc
,
490 if (sub
) add_to_list(*list
, sub
);
494 if (!fc
->nvalues
) break;
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
);
502 name
= straconcat(fc
->name
, ".x", NULL
);
504 sub
= init_submitted_value(name
, "0", type
, fc
, position
);
506 if (sub
) add_to_list(*list
, sub
);
508 name
= straconcat(fc
->name
, ".y", NULL
);
510 sub
= init_submitted_value(name
, "0", type
, fc
, position
);
512 if (sub
) add_to_list(*list
, sub
);
519 sort_submitted_values(struct list_head
*list
)
522 struct submitted_value
*sub
;
525 foreach (sub
, *list
) if (list_has_next(*list
, sub
))
526 if (sub
->next
->position
< sub
->position
) {
527 struct submitted_value
*next
= sub
->next
;
530 add_at_pos(next
, sub
);
535 foreachback (sub
, *list
) if (list_has_next(*list
, sub
))
536 if (sub
->next
->position
< sub
->position
) {
537 struct submitted_value
*next
= sub
->next
;
540 add_at_pos(next
, sub
);
550 get_successful_controls(struct document_view
*doc_view
,
551 struct form_control
*fc
, struct list_head
*list
)
553 struct form_control
*fc2
;
555 assert(doc_view
&& fc
&& fc
->form
&& list
);
556 if_assert_failed
return;
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
);
568 add_submitted_value_to_list(fc2
, fs
, list
);
572 sort_submitted_values(list
);
576 encode_controls(struct list_head
*l
, struct string
*data
,
577 int cp_from
, int cp_to
)
579 struct submitted_value
*sv
;
580 struct conv_table
*convert_table
= NULL
;
584 if_assert_failed
return;
587 unsigned char *p2
= NULL
;
590 add_char_to_string(data
, '&');
594 encode_uri_string(data
, sv
->name
, strlen(sv
->name
), 1);
595 add_char_to_string(data
, '=');
597 /* Convert back to original encoding (see html_form_control()
598 * for the original recoding). */
599 if (sv
->type
== FC_TEXTAREA
) {
602 p
= encode_textarea(sv
);
605 convert_table
= get_translation_table(cp_from
, cp_to
);
607 p2
= convert_string(convert_table
, p
,
608 strlen(p
), -1, CSM_FORM
, NULL
, NULL
, NULL
);
611 } else if (sv
->type
== FC_TEXT
||
612 sv
->type
== FC_PASSWORD
) {
614 convert_table
= get_translation_table(cp_from
, cp_to
);
616 p2
= convert_string(convert_table
, sv
->value
,
617 strlen(sv
->value
), -1, CSM_FORM
, NULL
, NULL
, NULL
);
619 p2
= stracpy(sv
->value
);
623 encode_uri_string(data
, p2
, strlen(p2
), 1);
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)
635 struct boundary_info
{
638 unsigned char string
[BOUNDARY_LENGTH
];
642 init_boundary(struct boundary_info
*boundary
)
644 memset(boundary
, 0, sizeof(*boundary
));
645 memset(boundary
->string
, '0', BOUNDARY_LENGTH
);
648 /* Add boundary to string and save the offset */
650 add_boundary(struct string
*data
, struct boundary_info
*boundary
)
652 add_to_string(data
, "--");
654 if (realloc_bound_ptrs(&boundary
->offsets
, boundary
->count
))
655 boundary
->offsets
[boundary
->count
++] = data
->length
;
657 add_bytes_to_string(data
, boundary
->string
, BOUNDARY_LENGTH
);
660 static inline unsigned char *
661 increment_boundary_counter(struct boundary_info
*boundary
)
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
;
670 boundary
->string
[j
] = '0';
673 INTERNAL("Form data boundary counter overflow");
679 check_boundary(struct string
*data
, struct boundary_info
*boundary
)
681 unsigned char *bound
= boundary
->string
;
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;
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
;
701 for (; pos
<= end
; pos
++) {
702 if (memcmp(pos
, bound
, BOUNDARY_LENGTH
))
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
))
711 /* Else start checking all boundaries using the new
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
);
723 /* FIXME: shouldn't we encode data at send time (in http.c) ? --Zas */
725 encode_multipart(struct session
*ses
, struct list_head
*l
, struct string
*data
,
726 struct boundary_info
*boundary
, int cp_from
, int cp_to
)
728 struct conv_table
*convert_table
= NULL
;
729 struct submitted_value
*sv
;
731 assert(ses
&& l
&& data
&& boundary
);
732 if_assert_failed
return;
734 init_boundary(boundary
);
737 add_boundary(data
, boundary
);
738 add_crlf_to_string(data
);
740 /* FIXME: name is not encoded.
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
, '"');
754 if (sv
->type
== FC_FILE
) {
755 #define F_BUFLEN 1024
757 unsigned char buffer
[F_BUFLEN
];
758 unsigned char *extension
;
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
, '"');
769 /* Add a Content-Type header if the type is configured */
770 extension
= strrchr(sv
->value
, '.');
772 unsigned char *type
= get_extension_content_type(extension
);
775 add_crlf_to_string(data
);
776 add_to_string(data
, "Content-Type: ");
777 add_to_string(data
, type
);
782 add_crlf_to_string(data
);
783 add_crlf_to_string(data
);
786 unsigned char *filename
;
788 if (get_cmd_opt_bool("anonymous")) {
793 /* FIXME: DO NOT COPY FILE IN MEMORY !! --Zas */
794 filename
= expand_tilde(sv
->value
);
795 if (!filename
) goto encode_error
;
797 fh
= open(filename
, O_RDONLY
);
800 if (fh
== -1) goto encode_error
;
803 ssize_t rd
= safe_read(fh
, buffer
, F_BUFLEN
);
811 add_bytes_to_string(data
, buffer
, rd
);
821 add_crlf_to_string(data
);
822 add_crlf_to_string(data
);
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
) {
831 convert_table
= get_translation_table(cp_from
,
834 p
= convert_string(convert_table
, sv
->value
,
835 strlen(sv
->value
), -1, CSM_FORM
, NULL
,
838 add_to_string(data
, p
);
842 add_to_string(data
, sv
->value
);
846 add_crlf_to_string(data
);
850 add_boundary(data
, boundary
);
851 add_to_string(data
, "--\r\n");
853 check_boundary(data
, boundary
);
855 mem_free_if(boundary
->offsets
);
859 mem_free_if(boundary
->offsets
);
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
)));
870 encode_newlines(struct string
*string
, unsigned char *data
)
872 for (; *data
; data
++) {
873 if (*data
== '\n' || *data
== '\r') {
874 unsigned char buffer
[3];
878 buffer
[1] = hx((((int) *data
) & 0xF0) >> 4);
879 buffer
[2] = hx(((int) *data
) & 0xF);
880 add_bytes_to_string(string
, buffer
, 3);
882 add_char_to_string(string
, *data
);
888 encode_text_plain(struct list_head
*l
, struct string
*data
,
889 int cp_from
, int cp_to
)
891 struct submitted_value
*sv
;
892 struct conv_table
*convert_table
= get_translation_table(cp_from
, cp_to
);
895 if_assert_failed
return;
898 unsigned char *area51
= NULL
;
899 unsigned char *value
= sv
->value
;
901 add_to_string(data
, sv
->name
);
902 add_char_to_string(data
, '=');
906 value
= area51
= encode_textarea(sv
);
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
,
917 /* Falling right through to free that textarea stuff */
920 /* Did the conversion fail? */
923 encode_newlines(data
, value
);
925 /* Free if we did convert something */
926 if (value
!= sv
->value
) mem_free(value
);
929 add_crlf_to_string(data
);
934 do_reset_form(struct document_view
*doc_view
, struct form
*form
)
936 struct form_control
*fc
;
938 assert(doc_view
&& doc_view
->document
);
939 if_assert_failed
return;
941 foreach (fc
, form
->items
) {
942 struct form_state
*fs
= find_form_state(doc_view
, fc
);
944 if (fs
) init_form_state(fc
, fs
);
948 enum frame_event_status
949 reset_form(struct session
*ses
, struct document_view
*doc_view
, int a
)
951 struct link
*link
= get_current_link(doc_view
);
953 if (!link
) return FRAME_EVENT_OK
;
955 do_reset_form(doc_view
, get_link_form_control(link
)->form
);
956 draw_forms(ses
->tab
->term
, doc_view
);
958 /* Could be the refresh return value and then ditch the draw_forms()
960 return FRAME_EVENT_OK
;
964 get_form_uri(struct session
*ses
, struct document_view
*doc_view
,
965 struct form_control
*fc
)
967 struct boundary_info boundary
;
968 INIT_LIST_HEAD(submit
);
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
;
982 if (fc
->type
== FC_RESET
) {
983 do_reset_form(doc_view
, form
);
985 } else if (fc
->type
== FC_BUTTON
) {
990 || !init_string(&data
))
993 get_successful_controls(doc_view
, fc
, &submit
);
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
);
1003 case FORM_METHOD_POST_MP
:
1004 encode_multipart(ses
, &submit
, &data
, &boundary
, cp_from
, cp_to
);
1007 case FORM_METHOD_POST_TEXT_PLAIN
:
1008 encode_text_plain(&submit
, &data
, cp_from
, cp_to
);
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 */
1017 && get_opt_bool("document.browse.forms.show_formhist"))
1018 memorize_form(ses
, &submit
, form
);
1021 done_submitted_value_list(&submit
);
1024 || !init_string(&go
)) {
1029 switch (form
->method
) {
1030 case FORM_METHOD_GET
:
1032 unsigned char *pos
= strchr(form
->action
, '#');
1035 add_bytes_to_string(&go
, form
->action
, pos
- form
->action
);
1037 add_to_string(&go
, form
->action
);
1040 if (strchr(go
.source
, '?'))
1041 add_char_to_string(&go
, '&');
1043 add_char_to_string(&go
, '?');
1045 add_string_to_string(&go
, &data
);
1047 if (pos
) add_to_string(&go
, pos
);
1050 case FORM_METHOD_POST
:
1051 case FORM_METHOD_POST_MP
:
1052 case FORM_METHOD_POST_TEXT_PLAIN
:
1054 /* Note that we end content type here by a simple '\n',
1055 * replaced later by correct '\r\n' in http_send_header(). */
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");
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
);
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');
1076 for (i
= 0; i
< data
.length
; i
++) {
1079 ulonghexcat(p
, NULL
, (int) data
.source
[i
], 2, '0', 0);
1080 add_to_string(&go
, p
);
1087 uri
= get_uri(go
.source
, 0);
1089 if (uri
) uri
->form
= 1;
1094 #undef BOUNDARY_LENGTH
1097 enum frame_event_status
1098 submit_form(struct session
*ses
, struct document_view
*doc_view
, int do_reload
)
1100 goto_current_link(ses
, doc_view
, do_reload
);
1101 return FRAME_EVENT_OK
;
1105 submit_given_form(struct session
*ses
, struct document_view
*doc_view
,
1106 struct form
*form
, int do_reload
)
1108 /* Added support for submitting forms in hidden
1109 * links in 1.285, commented code can safely be removed once we have made sure the new
1110 * code does the right thing. */
1113 struct document
*document
= doc_view
->document
;
1116 for (link
= 0; link
< document
->nlinks
; link
++) {
1117 struct form_control
*fc
= get_link_form_control(&document
->links
[link
]);
1119 if (fc
&& fc
->form
== form
) {
1120 doc_view
->vs
->current_link
= link
;
1121 submit_form(ses
, doc_view
, 0);
1126 if (!list_empty(form
->items
)) {
1127 struct form_control
*fc
= (struct form_control
*)form
->items
.next
;
1129 enum cache_mode mode
= do_reload
? CACHE_MODE_FORCE_RELOAD
: CACHE_MODE_NORMAL
;
1132 uri
= get_form_uri(ses
, doc_view
, fc
);
1134 goto_uri_frame(ses
, uri
, form
->target
, mode
);
1140 auto_submit_form(struct session
*ses
)
1142 struct document
*document
= ses
->doc_view
->document
;
1144 if (!list_empty(document
->forms
))
1145 submit_given_form(ses
, ses
->doc_view
, document
->forms
.next
, 0);
1151 set_file_form_state(struct terminal
*term
, void *filename_
, void *fs_
)
1153 unsigned char *filename
= filename_
;
1154 struct form_state
*fs
= fs_
;
1156 /* The menu code doesn't free the filename data */
1157 mem_free_set(&fs
->value
, filename
);
1158 fs
->state
= strlen(filename
);
1159 redraw_terminal(term
);
1164 file_form_menu(struct terminal
*term
, void *path_
, void *fs_
)
1166 unsigned char *path
= path_
;
1167 struct form_state
*fs
= fs_
;
1169 /* FIXME: It doesn't work for ../../ */
1171 int valuelen
= strlen(fs
->value
);
1172 int pathlen
= strlen(path
);
1173 int no_elevator
= 0;
1175 /* Don't add elevators for subdirs menus */
1176 /* It is not perfect at all because fs->value is not updated for each
1177 * newly opened file menu. Maybe it should be dropped. */
1178 for (; valuelen
< pathlen
; valuelen
++) {
1179 if (dir_sep(path
[valuelen
- 1])) {
1186 auto_complete_file(term
, 0 /* no_elevator */, path
,
1187 set_file_form_state
,
1188 file_form_menu
, fs
);
1192 enum frame_event_status
1193 field_op(struct session
*ses
, struct document_view
*doc_view
,
1194 struct link
*link
, struct term_event
*ev
)
1196 struct form_control
*fc
;
1197 struct form_state
*fs
;
1198 enum edit_action action_id
;
1199 unsigned char *text
;
1201 enum frame_event_status status
= FRAME_EVENT_REFRESH
;
1203 assert(ses
&& doc_view
&& link
&& ev
);
1204 if_assert_failed
return FRAME_EVENT_OK
;
1206 fc
= get_link_form_control(link
);
1207 assertm(fc
, "link has no form control");
1208 if_assert_failed
return FRAME_EVENT_OK
;
1210 if (fc
->mode
== FORM_MODE_DISABLED
|| ev
->ev
!= EVENT_KBD
1211 || ses
->insert_mode
== INSERT_MODE_OFF
)
1212 return FRAME_EVENT_IGNORED
;
1214 action_id
= kbd_action(KEYMAP_EDIT
, ev
, NULL
);
1216 fs
= find_form_state(doc_view
, fc
);
1217 if (!fs
|| !fs
->value
) return FRAME_EVENT_OK
;
1219 switch (action_id
) {
1221 fs
->state
= int_max(fs
->state
- 1, 0);
1223 case ACT_EDIT_RIGHT
:
1224 fs
->state
= int_min(fs
->state
+ 1, strlen(fs
->value
));
1227 if (fc
->type
== FC_TEXTAREA
) {
1228 status
= textarea_op_home(fs
, fc
);
1234 if (fc
->type
!= FC_TEXTAREA
)
1235 status
= FRAME_EVENT_IGNORED
;
1237 status
= textarea_op_up(fs
, fc
);
1240 if (fc
->type
!= FC_TEXTAREA
)
1241 status
= FRAME_EVENT_IGNORED
;
1243 status
= textarea_op_down(fs
, fc
);
1246 if (fc
->type
== FC_TEXTAREA
) {
1247 status
= textarea_op_end(fs
, fc
);
1249 fs
->state
= strlen(fs
->value
);
1252 case ACT_EDIT_BEGINNING_OF_BUFFER
:
1253 if (fc
->type
== FC_TEXTAREA
) {
1254 status
= textarea_op_bob(fs
, fc
);
1259 case ACT_EDIT_END_OF_BUFFER
:
1260 if (fc
->type
== FC_TEXTAREA
) {
1261 status
= textarea_op_eob(fs
, fc
);
1263 fs
->state
= strlen(fs
->value
);
1266 case ACT_EDIT_OPEN_EXTERNAL
:
1267 if (form_field_is_readonly(fc
))
1268 status
= FRAME_EVENT_IGNORED
;
1269 else if (fc
->type
== FC_TEXTAREA
)
1270 textarea_edit(0, ses
->tab
->term
, fs
, doc_view
, link
);
1272 case ACT_EDIT_COPY_CLIPBOARD
:
1273 set_clipboard_text(fs
->value
);
1274 status
= FRAME_EVENT_OK
;
1276 case ACT_EDIT_CUT_CLIPBOARD
:
1277 set_clipboard_text(fs
->value
);
1278 if (!form_field_is_readonly(fc
))
1282 case ACT_EDIT_PASTE_CLIPBOARD
:
1283 if (form_field_is_readonly(fc
)) break;
1285 text
= get_clipboard_text();
1288 length
= strlen(text
);
1289 if (length
<= fc
->maxlength
) {
1290 unsigned char *v
= mem_realloc(fs
->value
, length
+ 1);
1294 memmove(v
, text
, length
+ 1);
1295 fs
->state
= strlen(fs
->value
);
1300 case ACT_EDIT_ENTER
:
1301 if (fc
->type
== FC_TEXTAREA
) {
1302 status
= textarea_op_enter(fs
, fc
);
1306 /* Set status to ok if either it is not possible to
1307 * submit the form or the posting fails. */
1308 /* FIXME: We should maybe have ACT_EDIT_ENTER_RELOAD */
1309 if ((has_form_submit(fc
->form
)
1310 && !get_opt_bool("document.browse.forms.auto_submit"))
1311 || goto_current_link(ses
, doc_view
, 0)) {
1312 if (ses
->insert_mode
== INSERT_MODE_ON
)
1313 ses
->insert_mode
= INSERT_MODE_OFF
;
1314 status
= FRAME_EVENT_OK
;
1317 case ACT_EDIT_BACKSPACE
:
1318 if (form_field_is_readonly(fc
)) {
1319 status
= FRAME_EVENT_IGNORED
;
1324 status
= FRAME_EVENT_OK
;
1328 length
= strlen(fs
->value
+ fs
->state
) + 1;
1329 text
= fs
->value
+ fs
->state
;
1331 memmove(text
- 1, text
, length
);
1334 case ACT_EDIT_DELETE
:
1335 if (form_field_is_readonly(fc
)) {
1336 status
= FRAME_EVENT_IGNORED
;
1340 length
= strlen(fs
->value
);
1341 if (fs
->state
>= length
) {
1342 status
= FRAME_EVENT_OK
;
1346 text
= fs
->value
+ fs
->state
;
1348 memmove(text
, text
+ 1, length
- fs
->state
);
1350 case ACT_EDIT_KILL_TO_BOL
:
1351 if (form_field_is_readonly(fc
)) {
1352 status
= FRAME_EVENT_IGNORED
;
1356 if (fs
->state
<= 0) {
1357 status
= FRAME_EVENT_OK
;
1361 text
= memrchr(fs
->value
, ASCII_LF
, fs
->state
);
1363 /* Leave the new-line character if it does not
1364 * immediately precede the cursor. */
1365 if (text
!= &fs
->value
[fs
->state
- 1])
1371 length
= strlen(fs
->value
+ fs
->state
) + 1;
1372 memmove(text
, fs
->value
+ fs
->state
, length
);
1374 fs
->state
= (int) (text
- fs
->value
);
1376 case ACT_EDIT_KILL_TO_EOL
:
1377 if (form_field_is_readonly(fc
)) {
1378 status
= FRAME_EVENT_IGNORED
;
1382 if (!fs
->value
[fs
->state
]) {
1383 status
= FRAME_EVENT_OK
;
1387 text
= strchr(fs
->value
+ fs
->state
, ASCII_LF
);
1389 fs
->value
[fs
->state
] = '\0';
1393 if (fs
->value
[fs
->state
] == ASCII_LF
)
1396 memmove(fs
->value
+ fs
->state
, text
, strlen(text
) + 1);
1399 case ACT_EDIT_AUTO_COMPLETE
:
1400 if (fc
->type
!= FC_FILE
1401 || form_field_is_readonly(fc
)) {
1402 status
= FRAME_EVENT_IGNORED
;
1406 file_form_menu(ses
->tab
->term
, fs
->value
, fs
);
1409 case ACT_EDIT_CANCEL
:
1410 if (ses
->insert_mode
== INSERT_MODE_ON
)
1411 ses
->insert_mode
= INSERT_MODE_OFF
;
1413 status
= FRAME_EVENT_IGNORED
;
1416 case ACT_EDIT_REDRAW
:
1417 redraw_terminal_cls(ses
->tab
->term
);
1418 status
= FRAME_EVENT_OK
;
1422 if (!check_kbd_textinput_key(ev
)) {
1423 status
= FRAME_EVENT_IGNORED
;
1427 if (form_field_is_readonly(fc
)
1428 || strlen(fs
->value
) >= fc
->maxlength
1429 || !insert_in_string(&fs
->value
, fs
->state
, "?", 1)) {
1430 status
= FRAME_EVENT_OK
;
1434 fs
->value
[fs
->state
++] = get_kbd_key(ev
);
1442 get_form_label(struct form_control
*fc
)
1447 return N_("Reset form");
1449 return N_("Harmless button");
1454 if (!fc
->form
->action
) return NULL
;
1456 if (fc
->form
->method
== FORM_METHOD_GET
)
1457 return N_("Submit form to");
1458 return N_("Post form to");
1460 return N_("Radio button");
1462 return N_("Checkbox");
1464 return N_("Select field");
1466 return N_("Text field");
1468 return N_("Text area");
1470 return N_("File upload");
1472 return N_("Password field");
1479 add_form_attr_to_string(struct string
*string
, struct terminal
*term
,
1480 unsigned char *name
, unsigned char *value
)
1482 add_to_string(string
, ", ");
1483 add_to_string(string
, _(name
, term
));
1485 add_char_to_string(string
, ' ');
1486 add_to_string(string
, value
);
1491 get_form_info(struct session
*ses
, struct document_view
*doc_view
)
1493 struct terminal
*term
= ses
->tab
->term
;
1494 struct link
*link
= get_current_link(doc_view
);
1495 struct form_control
*fc
;
1496 unsigned char *label
, *key
;
1501 fc
= get_link_form_control(link
);
1502 label
= get_form_label(fc
);
1503 if (!label
) return NULL
;
1505 if (!init_string(&str
)) return NULL
;
1507 add_to_string(&str
, _(label
, term
));
1509 if (link
->type
!= LINK_BUTTON
&& fc
->name
&& fc
->name
[0]) {
1510 add_form_attr_to_string(&str
, term
, N_("name"), fc
->name
);
1517 struct form_state
*fs
= find_form_state(doc_view
, fc
);
1519 if (!fs
->value
|| !fs
->value
[0])
1522 add_form_attr_to_string(&str
, term
, N_("value"), fs
->value
);
1532 unsigned char *uristring
;
1534 if (form_field_is_readonly(fc
)) {
1535 add_form_attr_to_string(&str
, term
, N_("read only"), NULL
);
1538 /* Should we add info about entering insert mode or add info
1539 * about submitting the form? */
1540 if (ses
->insert_mode
== INSERT_MODE_OFF
) {
1541 key
= get_keystroke(ACT_EDIT_ENTER
, KEYMAP_EDIT
);
1545 if (form_field_is_readonly(fc
))
1546 label
= N_("press %s to navigate");
1548 label
= N_("press %s to edit");
1550 add_to_string(&str
, " (");
1551 add_format_to_string(&str
, _(label
, term
), key
);
1552 add_char_to_string(&str
, ')');
1558 if (fc
->type
== FC_TEXTAREA
)
1563 if (!fc
->form
->action
1564 || (has_form_submit(fc
->form
)
1565 && !get_opt_bool("document.browse.forms.auto_submit")))
1568 uri
= get_uri(fc
->form
->action
, 0);
1571 /* Add the uri with password and post info stripped */
1572 uristring
= get_uri_string(uri
, URI_PUBLIC
);
1575 if (!uristring
) break;
1577 key
= get_keystroke(ACT_EDIT_ENTER
, KEYMAP_EDIT
);
1579 mem_free(uristring
);
1583 if (fc
->form
->method
== FORM_METHOD_GET
)
1584 label
= N_("press %s to submit to %s");
1586 label
= N_("press %s to post to %s");
1588 add_to_string(&str
, " (");
1589 add_format_to_string(&str
, _(label
, term
), key
, uristring
);
1590 mem_free(uristring
);
1593 add_char_to_string(&str
, ')');
1598 add_char_to_string(&str
, ' ');
1601 /* Add the uri with password and post info stripped */
1602 add_string_uri_to_string(&str
, fc
->form
->action
, URI_PUBLIC
);
1613 && get_opt_bool("document.browse.accesskey.display")) {
1614 add_to_string(&str
, " (");
1615 add_accesskey_to_string(&str
, link
->accesskey
);
1616 add_char_to_string(&str
, ')');
1623 link_form_menu_func(struct terminal
*term
, void *link_number_
, void *ses_
)
1625 struct session
*ses
= ses_
;
1626 struct document_view
*doc_view
;
1627 int link_number
= *(int *) link_number_
;
1629 mem_free(link_number_
);
1631 assert(term
&& ses
);
1632 if_assert_failed
return;
1634 doc_view
= current_frame(ses
);
1635 if (!doc_view
) return;
1637 assert(doc_view
->vs
&& doc_view
->document
);
1638 if_assert_failed
return;
1640 jump_to_link_number(ses
, doc_view
, link_number
);
1641 refresh_view(ses
, doc_view
, 0);
1645 link_form_menu(struct session
*ses
)
1647 struct document_view
*doc_view
;
1649 struct menu_item
*mi
;
1650 struct form_control
*fc
;
1654 if_assert_failed
return;
1656 doc_view
= current_frame(ses
);
1657 if (!doc_view
) return;
1659 assert(doc_view
->vs
&& doc_view
->document
);
1660 if_assert_failed
return;
1662 link
= get_current_link(doc_view
);
1665 assert(link_is_form(link
));
1667 fc
= get_link_form_control(link
);
1672 mi
= new_menu(FREE_LIST
| FREE_TEXT
| NO_INTL
);
1675 foreach (fc
, form
->items
) {
1676 unsigned char *text
;
1677 unsigned char *rtext
;
1688 text
= N_("Useless button");
1690 text
= N_("Submit button");
1694 text
= get_form_label(fc
);
1697 link_number
= get_form_control_link(doc_view
->document
, fc
);
1699 || !init_string(&str
))
1703 add_to_string(&str
, _(text
, ses
->tab
->term
));
1706 if (!rtext
) rtext
= fc
->alt
;
1708 add_to_menu(&mi
, str
.source
, rtext
, ACT_MAIN_NONE
,
1709 link_form_menu_func
, intdup(link_number
),
1713 do_menu(ses
->tab
->term
, mi
, ses
, 1);