1 /** Forms viewing/manipulation handling
9 #define _GNU_SOURCE /* XXX: we want memrchr() ! */
16 #include <sys/types.h>
21 #include <fcntl.h> /* OS/2 needs this after sys/types.h */
26 #include "bfu/listmenu.h"
27 #include "bfu/dialog.h"
28 #include "config/kbdbind.h"
29 #include "dialogs/menu.h"
30 #include "document/document.h"
31 #include "document/forms.h"
32 #include "document/view.h"
33 #include "ecmascript/ecmascript.h"
34 #include "intl/gettext/libintl.h"
35 #include "formhist/formhist.h"
36 #include "mime/mime.h"
37 #include "osdep/ascii.h"
38 #include "osdep/osdep.h"
39 #include "protocol/uri.h"
40 #include "session/session.h"
41 #include "session/task.h"
42 #include "terminal/kbd.h"
43 #include "terminal/terminal.h"
44 #include "terminal/window.h"
45 #include "util/conv.h"
46 #include "util/error.h"
47 #include "util/file.h"
48 #include "util/memory.h"
49 #include "util/random.h"
50 #include "util/string.h"
51 #include "viewer/action.h"
52 #include "viewer/text/draw.h"
53 #include "viewer/text/form.h"
54 #include "viewer/text/link.h"
55 #include "viewer/text/textarea.h"
56 #include "viewer/text/view.h"
57 #include "viewer/text/vs.h"
60 /* TODO: Some of these (particulary those encoding routines) would feel better
61 * in viewer/common/. --pasky */
64 LIST_HEAD(struct files_offset
);
65 /* offset of the filename in the data generated by encode_multipart.
66 * data[begin] is the FILE_CHAR, data + begin + 1 is the filename. */
68 /* end of filename. data[end] is the FILE_CHAR. In normal strings
69 * it would be a nul char. */
74 /** @relates submitted_value */
76 struct submitted_value
*
77 init_submitted_value(unsigned char *name
, unsigned char *value
, enum form_type type
,
78 struct form_control
*fc
, int position
)
80 struct submitted_value
*sv
;
82 sv
= mem_alloc(sizeof(*sv
));
85 sv
->value
= stracpy(value
);
86 if (!sv
->value
) { mem_free(sv
); return NULL
; }
88 sv
->name
= stracpy(name
);
89 if (!sv
->name
) { mem_free(sv
->value
); mem_free(sv
); return NULL
; }
92 sv
->form_control
= fc
;
93 sv
->position
= position
;
98 /** @relates submitted_value */
100 done_submitted_value(struct submitted_value
*sv
)
103 mem_free_if(sv
->value
);
104 mem_free_if(sv
->name
);
109 fixup_select_state(struct form_control
*fc
, struct form_state
*fs
)
114 if_assert_failed
return;
117 && fs
->state
< fc
->nvalues
118 && !strcmp(fc
->values
[fs
->state
], fs
->value
))
121 for (i
= 0; i
< fc
->nvalues
; i
++)
122 if (!strcmp(fc
->values
[i
], fs
->value
)) {
129 mem_free_set(&fs
->value
, stracpy(fc
->nvalues
131 : (unsigned char *) ""));
136 selected_item(struct terminal
*term
, void *item_
, void *ses_
)
138 struct session
*ses
= ses_
;
139 int item
= (long) item_
;
140 struct document_view
*doc_view
;
142 struct form_state
*fs
;
143 struct form_control
*fc
;
146 if_assert_failed
return;
147 doc_view
= current_frame(ses
);
149 assert(doc_view
&& doc_view
->vs
&& doc_view
->document
);
150 if_assert_failed
return;
152 link
= get_current_link(doc_view
);
153 if (!link
|| link
->type
!= LINK_SELECT
) return;
155 fc
= get_link_form_control(link
);
156 fs
= find_form_state(doc_view
, fc
);
158 if (item
>= 0 && item
< fc
->nvalues
) {
160 mem_free_set(&fs
->value
, stracpy(fc
->values
[item
]));
162 fixup_select_state(fc
, fs
);
165 refresh_view(ses
, doc_view
, 0);
169 init_form_state(struct document_view
*doc_view
,
170 struct form_control
*fc
, struct form_state
*fs
)
172 struct terminal
*term
;
173 int doc_cp
, viewer_cp
;
176 if_assert_failed
return;
178 doc_cp
= doc_view
->document
->cp
;
179 term
= doc_view
->session
->tab
->term
;
180 viewer_cp
= get_terminal_codepage(term
);
182 mem_free_set(&fs
->value
, NULL
);
187 #ifdef CONFIG_FORMHIST
188 fs
->value
= null_or_stracpy(
189 get_form_history_value(
190 fc
->form
->action
, fc
->name
));
191 #endif /* CONFIG_FORMHIST */
194 if (fs
->value
== NULL
) {
195 fs
->value
= convert_string(
196 get_translation_table(doc_cp
, viewer_cp
),
198 strlen(fc
->default_value
),
200 &fs
->state
, NULL
, NULL
);
202 fs
->state
= fs
->value
? strlen(fs
->value
) : 0;
204 if (fc
->type
== FC_TEXTAREA
)
206 #endif /* CONFIG_UTF8 */
210 fs
->value
= stracpy("");
215 fs
->value
= convert_string(
216 get_translation_table(doc_cp
, viewer_cp
),
218 strlen(fc
->default_value
),
220 &fs
->state
, NULL
, NULL
);
221 fs
->state
= fc
->default_state
;
222 fixup_select_state(fc
, fs
);
226 fs
->state
= fc
->default_state
;
233 /* We don't want to recode hidden fields. */
234 fs
->value
= stracpy(fc
->default_value
);
241 find_form_state(struct document_view
*doc_view
, struct form_control
*fc
)
243 struct view_state
*vs
;
244 struct form_state
*fs
;
247 assert(doc_view
&& doc_view
->vs
&& fc
);
248 if_assert_failed
return NULL
;
253 if (n
>= vs
->form_info_len
) {
255 #ifdef CONFIG_ECMASCRIPT
256 const struct form_state
*const old_form_info
= vs
->form_info
;
259 fs
= mem_align_alloc(&vs
->form_info
, vs
->form_info_len
, nn
, 0);
260 if (!fs
) return NULL
;
262 vs
->form_info_len
= nn
;
264 #ifdef CONFIG_ECMASCRIPT
265 /* TODO: Standard C does not allow this comparison;
266 * if the memory to which old_form_info pointed has
267 * been freed, then the value of the pointer itself is
268 * indeterminate. Fixing this would require changing
269 * mem_align_alloc to tell the caller whether it did
271 if (vs
->form_info
!= old_form_info
) {
272 /* vs->form_info[] was moved to a different address.
273 * Update all the ECMAScript objects that have
274 * pointers to its elements. */
275 for (nn
= 0; nn
< vs
->form_info_len
; nn
++)
276 ecmascript_moved_form_state(&vs
->form_info
[nn
]);
278 #endif /* CONFIG_ECMASCRIPT */
280 fs
= &vs
->form_info
[n
];
282 if (fs
->form_view
&& fs
->form_view
->form_num
== fc
->form
->form_num
283 && fs
->g_ctrl_num
== fc
->g_ctrl_num
284 && fs
->position
== fc
->position
285 && fs
->type
== fc
->type
)
288 mem_free_if(fs
->value
);
289 memset(fs
, 0, sizeof(*fs
));
290 fs
->form_view
= find_form_view(doc_view
, fc
->form
);
291 fs
->g_ctrl_num
= fc
->g_ctrl_num
;
292 fs
->position
= fc
->position
;
294 init_form_state(doc_view
, fc
, fs
);
299 struct form_control
*
300 find_form_control(struct document
*document
, struct form_state
*fs
)
302 struct form
*form
= find_form_by_form_view(document
, fs
->form_view
);
303 struct form_control
*fc
;
305 foreach (fc
, form
->items
) {
306 if (fs
->g_ctrl_num
== fc
->g_ctrl_num
307 && fs
->position
== fc
->position
308 && fs
->type
== fc
->type
)
316 find_form_view_in_vs(struct view_state
*vs
, int form_num
)
318 struct form_view
*fv
;
322 foreach (fv
, vs
->forms
)
323 if (fv
->form_num
== form_num
)
326 fv
= mem_calloc(1, sizeof(*fv
));
327 fv
->form_num
= form_num
;
328 add_to_list(vs
->forms
, fv
);
333 find_form_view(struct document_view
*doc_view
, struct form
*form
)
335 return find_form_view_in_vs(doc_view
->vs
, form
->form_num
);
339 find_form_by_form_view(struct document
*document
, struct form_view
*fv
)
343 foreach (form
, document
->forms
) {
344 if (form
->form_num
== fv
->form_num
)
350 /** Free any data owned by @a fs, but not the struct form_state
351 * itself, because that is normally allocated as part of an array.
352 * @relates form_state */
354 done_form_state(struct form_state
*fs
)
356 #ifdef CONFIG_ECMASCRIPT
357 ecmascript_detach_form_state(fs
);
359 mem_free_if(fs
->value
);
362 /** Free @a fv and any data owned by it. This does not call
363 * del_from_list(fv), so the caller must usually do that first.
364 * @relates form_view */
366 done_form_view(struct form_view
*fv
)
368 #ifdef CONFIG_ECMASCRIPT
369 ecmascript_detach_form_view(fv
);
375 get_current_state(struct session
*ses
)
377 struct document_view
*doc_view
;
379 struct form_state
*fs
;
382 if_assert_failed
return -1;
383 doc_view
= current_frame(ses
);
385 assert(doc_view
&& doc_view
->vs
&& doc_view
->document
);
386 if_assert_failed
return -1;
388 link
= get_current_link(doc_view
);
389 if (!link
|| link
->type
!= LINK_SELECT
) return -1;
391 fs
= find_form_state(doc_view
, get_link_form_control(link
));
392 if (fs
) return fs
->state
;
397 draw_form_entry(struct terminal
*term
, struct document_view
*doc_view
,
400 struct form_state
*fs
;
401 struct form_control
*fc
;
402 struct view_state
*vs
;
406 assert(term
&& doc_view
&& doc_view
->document
&& doc_view
->vs
&& link
);
407 if_assert_failed
return;
409 fc
= get_link_form_control(link
);
410 assertm(fc
!= NULL
, "link %d has no form control", (int) (link
- doc_view
->document
->links
));
411 if_assert_failed
return;
413 fs
= find_form_state(doc_view
, fc
);
416 box
= &doc_view
->box
;
423 unsigned char *text
, *end
, *last_in_view
;
424 #endif /* CONFIG_UTF8 */
431 if (!link
->npoints
) break;
433 y
= link
->points
[0].y
+ dy
;
434 if (!row_is_in_box(box
, y
))
437 x
= link
->points
[0].x
+ dx
;
439 if (term
->utf8_cp
) goto utf8
;
440 #endif /* CONFIG_UTF8 */
441 int_bounds(&fs
->vpos
, fs
->state
- fc
->size
+ 1, fs
->state
);
442 len
= strlen(fs
->value
) - fs
->vpos
;
444 for (i
= 0; i
< fc
->size
; i
++, x
++) {
447 if (!col_is_in_box(box
, x
)) continue;
449 if (fs
->value
&& i
>= -fs
->vpos
&& i
< len
)
450 data
= fc
->type
!= FC_PASSWORD
451 ? fs
->value
[i
+ fs
->vpos
] : '*';
455 draw_char_data(term
, x
, y
, data
);
463 if (!text
) text
= "";
465 int_bounds(&fs
->state
, 0, len
);
466 int_bounds(&fs
->vpos
, 0, fs
->state
);
471 for (i
= 0; i
< fc
->size
; ) {
474 unsigned char *maybe_in_view
= text
;
476 data
= utf8_to_unicode(&text
, end
);
477 if (data
== UCS_NO_CHAR
) /* end of string */
479 else if (fc
->type
== FC_PASSWORD
)
482 cells
= unicode_to_cell(data
);
483 if (i
+ cells
<= fc
->size
) {
484 last_in_view
= maybe_in_view
;
485 if (colspan_is_in_box(box
, x
+ i
, cells
)) {
486 /* The character fits completely.
487 * Draw the character, and mark any
488 * further cells with UCS_NO_CHAR. */
489 draw_char_data(term
, x
+ i
, y
, data
);
490 for (cell
= 1; cell
< cells
; cell
++)
491 draw_char_data(term
, x
+ i
+ cell
,
497 /* The character does not fit completely.
498 * Write UCS_ORPHAN_CELL to the cells that
500 for (cell
= 0; cell
< cells
; cell
++) {
501 if (col_is_in_box(box
, x
+ i
+ cell
)
502 && i
+ cell
< fc
->size
)
512 /* The int_bounds calls above ensured that the
513 * insertion point cannot be at the left side
514 * of the scrolled-visible part of the text.
515 * However it can still be at the right side.
516 * Check whether we need to change fs->vpos.
518 * This implementation attempts to follow
520 * - If the insertion point is at the end of
521 * the string, leave at least one empty cell
522 * so that there is a place for the cursor.
523 * - If a character follows the insertion
524 * point, make that character fully visible;
525 * note the character may be double-width.
526 * - If fc->size < 2, it is not possible to
527 * make a double-width character fully
528 * visible. In this case, it is OK if the
529 * output is ugly, but ELinks must not fall
530 * into an infinite loop or crash.
531 * - The length of the string should not affect
532 * how long this function takes. The width
533 * of the widget naturally will.
534 * - Optimize the case where fields are drawn
535 * several times without being modified.
538 * - If the "for i" loop above hit UCS_NO_CHAR,
539 * then there is no need to scroll.
540 * - When the string ends with a double-width
541 * character that fits in only partially,
542 * then text==end, but the field may have
544 if (fs
->value
&& last_in_view
545 && last_in_view
< fs
->value
+ fs
->state
) {
546 unsigned char *ptr
= fs
->value
+ fs
->state
;
547 int cells
= fc
->size
;
548 enum utf8_step how
= (fc
->type
== FC_PASSWORD
)
549 ? UTF8_STEP_CHARACTERS
550 : UTF8_STEP_CELLS_FEWER
;
552 /* The insertion point is at the right
553 * side of the scrolled-visible part
554 * of the text. Decide a new fs->vpos
555 * by counting cells backwards from
556 * @ptr. But first advance @ptr past
557 * the character that follows the
558 * insertion point, so that it will be
559 * fully displayed. If there is no
560 * such character, reserve one cell
561 * for the cursor anyway. */
562 if (utf8_to_unicode(&ptr
, end
) == UCS_NO_CHAR
)
564 ptr
= utf8_step_backward(ptr
, fs
->value
,
567 if (fs
->vpos
!= ptr
- fs
->value
) {
568 fs
->vpos
= ptr
- fs
->value
;
569 goto retry_after_scroll
;
573 #endif /* CONFIG_UTF8 */
575 draw_textarea(term
, fs
, doc_view
, link
);
579 if (link
->npoints
< 2) break;
580 x
= link
->points
[1].x
+ dx
;
581 y
= link
->points
[1].y
+ dy
;
582 if (is_in_box(box
, x
, y
))
583 draw_char_data(term
, x
, y
, fs
->state
? 'X' : ' ');
586 fixup_select_state(fc
, fs
);
587 if (fs
->state
< fc
->nvalues
)
588 s
= fc
->labels
[fs
->state
];
590 /* XXX: when can this happen? --pasky */
593 if (term
->utf8_cp
) goto utf8_select
;
594 #endif /* CONFIG_UTF8 */
595 len
= s
? strlen(s
) : 0;
596 for (i
= 0; i
< link
->npoints
; i
++) {
597 x
= link
->points
[i
].x
+ dx
;
598 y
= link
->points
[i
].y
+ dy
;
599 if (is_in_box(box
, x
, y
))
600 draw_char_data(term
, x
, y
, i
< len
? s
[i
] : '_');
606 end
= strchr((const char *)s
, '\0');
607 len
= utf8_ptr2cells(text
, end
);
608 for (i
= 0; i
< link
->npoints
; i
++) {
609 x
= link
->points
[i
].x
+ dx
;
610 y
= link
->points
[i
].y
+ dy
;
611 if (is_in_box(box
, x
, y
)) {
616 data
= utf8_to_unicode(&s
, end
);
617 cell
= unicode_to_cell(data
);
618 if (i
+ 1 < len
&& cell
== 2) {
619 draw_char_data(term
, x
++, y
, data
);
623 } else if (cell
== 2) {
624 data
= UCS_ORPHAN_CELL
;
628 draw_char_data(term
, x
, y
, data
);
632 #endif /* CONFIG_UTF8 */
643 draw_forms(struct terminal
*term
, struct document_view
*doc_view
)
645 struct link
*l1
, *l2
;
647 assert(term
&& doc_view
);
648 if_assert_failed
return;
650 l1
= get_first_link(doc_view
);
651 l2
= get_last_link(doc_view
);
654 assertm(!l1
&& !l2
, "get_first_link == %p, get_last_link == %p", l1
, l2
);
655 /* Return path :-). */
659 struct form_control
*fc
= get_link_form_control(l1
);
662 draw_form_entry(term
, doc_view
, l1
);
668 /** @relates submitted_value */
670 done_submitted_value_list(LIST_OF(struct submitted_value
) *list
)
672 struct submitted_value
*sv
, *svtmp
;
675 if_assert_failed
return;
677 foreach (sv
, *list
) {
680 del_from_list(svtmp
);
681 done_submitted_value(svtmp
);
686 add_submitted_value_to_list(struct form_control
*fc
,
687 struct form_state
*fs
,
688 LIST_OF(struct submitted_value
) *list
)
690 struct submitted_value
*sub
;
695 assert(fc
&& fs
&& list
);
698 position
= fc
->position
;
706 sub
= init_submitted_value(name
, fs
->value
, type
, fc
, position
);
707 if (sub
) add_to_list(*list
, sub
);
712 if (!fs
->state
) break;
719 sub
= init_submitted_value(name
, fs
->value
, type
, fc
,
721 if (sub
) add_to_list(*list
, sub
);
725 if (!fc
->nvalues
) break;
727 fixup_select_state(fc
, fs
);
728 sub
= init_submitted_value(name
, fs
->value
, type
, fc
, position
);
729 if (sub
) add_to_list(*list
, sub
);
733 name
= straconcat(fc
->name
, ".x", (unsigned char *) NULL
);
735 sub
= init_submitted_value(name
, "0", type
, fc
, position
);
737 if (sub
) add_to_list(*list
, sub
);
739 name
= straconcat(fc
->name
, ".y", (unsigned char *) NULL
);
741 sub
= init_submitted_value(name
, "0", type
, fc
, position
);
743 if (sub
) add_to_list(*list
, sub
);
750 sort_submitted_values(LIST_OF(struct submitted_value
) *list
)
753 struct submitted_value
*sub
;
756 foreach (sub
, *list
) if (list_has_next(*list
, sub
))
757 if (sub
->next
->position
< sub
->position
) {
758 struct submitted_value
*next
= sub
->next
;
761 add_at_pos(next
, sub
);
766 foreachback (sub
, *list
) if (list_has_next(*list
, sub
))
767 if (sub
->next
->position
< sub
->position
) {
768 struct submitted_value
*next
= sub
->next
;
771 add_at_pos(next
, sub
);
781 get_successful_controls(struct document_view
*doc_view
,
782 struct form_control
*fc
,
783 LIST_OF(struct submitted_value
) *list
)
785 struct form_control
*fc2
;
787 assert(doc_view
&& fc
&& fc
->form
&& list
);
788 if_assert_failed
return;
790 foreach (fc2
, fc
->form
->items
) {
791 if (((fc2
->type
!= FC_SUBMIT
&&
792 fc2
->type
!= FC_IMAGE
&&
793 fc2
->type
!= FC_RESET
&&
794 fc2
->type
!= FC_BUTTON
) || fc2
== fc
)
795 && fc2
->name
&& fc2
->name
[0]) {
796 struct form_state
*fs
= find_form_state(doc_view
, fc2
);
800 add_submitted_value_to_list(fc2
, fs
, list
);
804 sort_submitted_values(list
);
808 encode_crlf(struct submitted_value
*sv
)
810 struct string newtext
;
813 assert(sv
&& sv
->value
);
814 if_assert_failed
return NULL
;
816 if (!init_string(&newtext
)) return NULL
;
818 for (i
= 0; sv
->value
[i
]; i
++) {
819 if (sv
->value
[i
] == '\r') {
820 if (sv
->value
[i
+1] != '\n')
821 add_crlf_to_string(&newtext
);
822 } else if (sv
->value
[i
] == '\n')
823 add_crlf_to_string(&newtext
);
825 add_char_to_string(&newtext
, sv
->value
[i
]);
828 return newtext
.source
;
832 encode_controls(LIST_OF(struct submitted_value
) *l
, struct string
*data
,
833 int cp_from
, int cp_to
)
835 struct submitted_value
*sv
;
836 struct conv_table
*convert_table
= NULL
;
840 if_assert_failed
return;
843 unsigned char *p2
= NULL
;
846 add_char_to_string(data
, '&');
850 encode_uri_string(data
, sv
->name
, strlen(sv
->name
), 1);
851 add_char_to_string(data
, '=');
853 /* Convert back to original encoding (see html_form_control()
854 * for the original recoding). */
855 if (sv
->type
== FC_TEXTAREA
) {
858 p
= encode_textarea(sv
);
861 convert_table
= get_translation_table(cp_from
, cp_to
);
863 p2
= convert_string(convert_table
, p
,
864 strlen(p
), -1, CSM_FORM
, NULL
, NULL
, NULL
);
867 } else if (sv
->type
== FC_TEXT
||
868 sv
->type
== FC_PASSWORD
) {
870 convert_table
= get_translation_table(cp_from
, cp_to
);
872 p2
= convert_string(convert_table
, sv
->value
,
873 strlen(sv
->value
), -1, CSM_FORM
, NULL
, NULL
, NULL
);
874 } else if (sv
->type
== FC_HIDDEN
) {
875 p2
= encode_crlf(sv
);
877 p2
= stracpy(sv
->value
);
881 encode_uri_string(data
, p2
, strlen(p2
), 1);
889 #define BOUNDARY_LENGTH 32
890 #define realloc_bound_ptrs(bptrs, bptrs_size) \
891 mem_align_alloc(bptrs, bptrs_size, bptrs_size + 1, 0xFF)
893 struct boundary_info
{
896 unsigned char string
[BOUNDARY_LENGTH
];
899 /** @relates boundary_info */
901 randomize_boundary(unsigned char *data
, int length
)
905 random_nonce(data
, length
);
906 for (i
= 0; i
< length
; i
++) {
907 /* Only [0-9A-Za-z]. */
908 data
[i
] = data
[i
] & 63;
909 if (data
[i
] < 10) data
[i
] += '0';
910 else if (data
[i
] < 36) data
[i
] = data
[i
] - 10 + 'A';
911 else if (data
[i
] < 62) data
[i
] = data
[i
] - 36 + 'a';
916 /** @relates boundary_info */
918 init_boundary(struct boundary_info
*boundary
)
920 memset(boundary
, 0, sizeof(*boundary
));
921 randomize_boundary(boundary
->string
, BOUNDARY_LENGTH
);
924 /** Add boundary to string and save the offset
925 * @relates boundary_info */
927 add_boundary(struct string
*data
, struct boundary_info
*boundary
)
929 add_to_string(data
, "--");
931 if (realloc_bound_ptrs(&boundary
->offsets
, boundary
->count
))
932 boundary
->offsets
[boundary
->count
++] = data
->length
;
934 add_bytes_to_string(data
, boundary
->string
, BOUNDARY_LENGTH
);
938 /** Format a multipart/form-data body for a POST request.
941 * Display an info_box() in the terminal of this session if an error
944 * List of values to be sent to the server.
946 * Append the body here. This is in the same format as uri.post,
947 * except this never has a Content-Type at the beginning, the
948 * literal parts are not encoded in hexadecimal, and the file names
949 * are not percent-encoded. Therefore the result would be ambiguous
951 * @param[out] boundary
952 * A random boundary %string, and a list of offsets where the
953 * boundary was used, so that the caller can in principle change the
954 * %string and update all of its uses if the original one conflicts
955 * with some of the submitted values. However, the caller does not
956 * do that nowadays because reading through the attached files would
957 * be too expensive. It just assumes the boundary is random enough.
959 * List of offsets of names of files to be uploaded. This is how
960 * the caller knows which occurrences of ::FILE_CHAR in @a data
961 * should be encoded and which ones should not.
963 * Codepage of the submitted-value strings in @a l.
965 * Codepage wanted by the server.
967 * @todo FIXME: shouldn't we encode data at send time (in http.c) ? --Zas */
969 encode_multipart(struct session
*ses
, LIST_OF(struct submitted_value
) *l
,
970 struct string
*data
, struct boundary_info
*boundary
,
971 LIST_OF(struct files_offset
) *bfs
, int cp_from
, int cp_to
)
973 struct conv_table
*convert_table
= NULL
;
974 struct submitted_value
*sv
;
976 assert(ses
&& l
&& data
&& boundary
);
977 if_assert_failed
return;
979 init_boundary(boundary
);
982 add_boundary(data
, boundary
);
983 add_crlf_to_string(data
);
985 /** @bug FIXME: name is not encoded.
987 * multipart/form-data contains a series of parts.
988 * Each %part is expected to contain a content-disposition
989 * header where the value is "form-data" and a name attribute
990 * specifies the field name within the form,
991 * e.g., 'content-disposition: form-data; name="xxxxx"',
992 * where xxxxx is the field name corresponding to that field.
993 * Field names originally in non-ASCII character sets may be
994 * encoded using the method outlined in RFC 1522. */
995 add_to_string(data
, "Content-Disposition: form-data; name=\"");
996 add_to_string(data
, sv
->name
);
997 add_char_to_string(data
, '"');
999 if (sv
->type
== FC_FILE
) {
1000 unsigned char *extension
;
1002 add_to_string(data
, "; filename=\"");
1003 add_to_string(data
, get_filename_position(sv
->value
));
1004 /* It sends bad data if the file name contains ", but
1005 Netscape does the same */
1006 /* FIXME: We should follow RFCs 1522, 1867,
1007 * 2047 (updated by rfc 2231), to provide correct support
1008 * for non-ASCII and special characters in values. --Zas */
1009 add_char_to_string(data
, '"');
1011 /* Add a Content-Type header if the type is configured */
1012 extension
= strrchr((const char *)sv
->value
, '.');
1014 unsigned char *type
= get_extension_content_type(extension
);
1017 add_crlf_to_string(data
);
1018 add_to_string(data
, "Content-Type: ");
1019 add_to_string(data
, type
);
1024 add_crlf_to_string(data
);
1025 add_crlf_to_string(data
);
1028 unsigned char *filename
;
1029 struct files_offset
*bfs_new
;
1031 if (get_cmd_opt_bool("anonymous")) {
1036 filename
= expand_tilde(sv
->value
);
1037 if (!filename
) goto encode_error
;
1039 if (access(filename
, R_OK
)) {
1044 bfs_new
= mem_calloc(1, sizeof(*bfs_new
));
1049 bfs_new
->begin
= data
->length
;
1050 add_char_to_string(data
, FILE_CHAR
);
1051 add_to_string(data
, filename
);
1052 add_char_to_string(data
, FILE_CHAR
);
1053 bfs_new
->end
= data
->length
;
1054 add_to_list_end(*bfs
, bfs_new
);
1058 add_crlf_to_string(data
);
1059 add_crlf_to_string(data
);
1061 /* Convert back to original encoding (see
1062 * html_special_form_control() for the original
1064 if (sv
->type
== FC_TEXT
|| sv
->type
== FC_PASSWORD
||
1065 sv
->type
== FC_TEXTAREA
) {
1069 convert_table
= get_translation_table(cp_from
,
1072 p
= convert_string(convert_table
, sv
->value
,
1073 strlen(sv
->value
), -1, CSM_FORM
, NULL
,
1076 add_to_string(data
, p
);
1080 add_to_string(data
, sv
->value
);
1084 add_crlf_to_string(data
);
1088 add_boundary(data
, boundary
);
1089 add_to_string(data
, "--\r\n");
1091 mem_free_if(boundary
->offsets
);
1096 mem_free_if(boundary
->offsets
);
1099 /* XXX: This error message should move elsewhere. --Zas */
1100 info_box(ses
->tab
->term
, MSGBOX_FREE_TEXT
,
1101 N_("Error while posting form"), ALIGN_CENTER
,
1102 msg_text(ses
->tab
->term
, N_("Could not load file %s: %s"),
1103 sv
->value
, strerror(errno
)));
1107 encode_newlines(struct string
*string
, unsigned char *data
)
1109 for (; *data
; data
++) {
1110 if (*data
== '\n' || *data
== '\r') {
1111 unsigned char buffer
[3];
1115 buffer
[1] = hx((((int) *data
) & 0xF0) >> 4);
1116 buffer
[2] = hx(((int) *data
) & 0xF);
1117 add_bytes_to_string(string
, buffer
, 3);
1119 add_char_to_string(string
, *data
);
1125 encode_text_plain(LIST_OF(struct submitted_value
) *l
, struct string
*data
,
1126 int cp_from
, int cp_to
)
1128 struct submitted_value
*sv
;
1129 struct conv_table
*convert_table
= get_translation_table(cp_from
, cp_to
);
1132 if_assert_failed
return;
1135 unsigned char *area51
= NULL
;
1136 unsigned char *value
= sv
->value
;
1138 add_to_string(data
, sv
->name
);
1139 add_char_to_string(data
, '=');
1143 value
= area51
= encode_textarea(sv
);
1147 if (!area51
) value
= area51
= encode_crlf(sv
);
1152 /* Convert back to original encoding (see
1153 * html_form_control() for the original recoding). */
1154 value
= convert_string(convert_table
, value
,
1155 strlen(value
), -1, CSM_FORM
,
1158 /* Falling right through to free that textarea stuff */
1159 mem_free_if(area51
);
1161 /* Did the conversion fail? */
1164 encode_newlines(data
, value
);
1166 /* Free if we did convert something */
1167 if (value
!= sv
->value
) mem_free(value
);
1170 add_crlf_to_string(data
);
1175 do_reset_form(struct document_view
*doc_view
, struct form
*form
)
1177 struct form_control
*fc
;
1179 assert(doc_view
&& doc_view
->document
);
1180 if_assert_failed
return;
1182 foreach (fc
, form
->items
) {
1183 struct form_state
*fs
= find_form_state(doc_view
, fc
);
1185 if (fs
) init_form_state(doc_view
, fc
, fs
);
1189 enum frame_event_status
1190 reset_form(struct session
*ses
, struct document_view
*doc_view
, int a
)
1192 struct link
*link
= get_current_link(doc_view
);
1194 if (!link
) return FRAME_EVENT_OK
;
1196 do_reset_form(doc_view
, get_link_form_control(link
)->form
);
1197 draw_forms(ses
->tab
->term
, doc_view
);
1199 /* Could be the refresh return value and then ditch the draw_forms()
1201 return FRAME_EVENT_OK
;
1205 get_form_uri(struct session
*ses
, struct document_view
*doc_view
,
1206 struct form_control
*fc
)
1208 struct boundary_info boundary
;
1209 INIT_LIST_OF(struct submitted_value
, submit
);
1210 INIT_LIST_OF(struct files_offset
, bfs
);
1217 assert(ses
&& ses
->tab
&& ses
->tab
->term
);
1218 if_assert_failed
return NULL
;
1219 assert(doc_view
&& doc_view
->document
&& fc
&& fc
->form
);
1220 if_assert_failed
return NULL
;
1224 if (fc
->type
== FC_RESET
) {
1225 do_reset_form(doc_view
, form
);
1230 || !init_string(&data
))
1233 get_successful_controls(doc_view
, fc
, &submit
);
1235 cp_from
= get_terminal_codepage(ses
->tab
->term
);
1236 cp_to
= doc_view
->document
->cp
;
1237 switch (form
->method
) {
1238 case FORM_METHOD_GET
:
1239 case FORM_METHOD_POST
:
1240 encode_controls(&submit
, &data
, cp_from
, cp_to
);
1243 case FORM_METHOD_POST_MP
:
1244 encode_multipart(ses
, &submit
, &data
, &boundary
,
1245 &bfs
, cp_from
, cp_to
);
1248 case FORM_METHOD_POST_TEXT_PLAIN
:
1249 encode_text_plain(&submit
, &data
, cp_from
, cp_to
);
1252 #ifdef CONFIG_FORMHIST
1253 /* XXX: We check data.source here because a NULL value can indicate
1254 * not only a memory allocation failure, but also an error reading
1255 * a file that is to be uploaded. TODO: Distinguish between
1256 * these two classes of errors (is it worth it?). -- Miciah */
1258 && get_opt_bool("document.browse.forms.show_formhist", ses
))
1259 memorize_form(ses
, &submit
, form
);
1262 done_submitted_value_list(&submit
);
1265 || !init_string(&go
)) {
1270 switch (form
->method
) {
1271 case FORM_METHOD_GET
:
1273 unsigned char *pos
= strchr((const char *)form
->action
, '#');
1276 add_bytes_to_string(&go
, form
->action
, pos
- form
->action
);
1278 add_to_string(&go
, form
->action
);
1281 if (strchr((const char *)go
.source
, '?'))
1282 add_char_to_string(&go
, '&');
1284 add_char_to_string(&go
, '?');
1286 add_string_to_string(&go
, &data
);
1288 if (pos
) add_to_string(&go
, pos
);
1291 case FORM_METHOD_POST
:
1292 case FORM_METHOD_POST_MP
:
1293 case FORM_METHOD_POST_TEXT_PLAIN
:
1295 /* Note that we end content type here by a simple '\n',
1296 * replaced later by correct '\r\n' in http_send_header(). */
1298 add_to_string(&go
, form
->action
);
1299 add_char_to_string(&go
, POST_CHAR
);
1300 if (form
->method
== FORM_METHOD_POST
) {
1301 add_to_string(&go
, "application/x-www-form-urlencoded\n");
1303 } else if (form
->method
== FORM_METHOD_POST_TEXT_PLAIN
) {
1304 /* Dunno about this one but we don't want the full
1305 * hextcat thingy. --jonas */
1306 add_to_string(&go
, "text/plain\n");
1307 add_to_string(&go
, data
.source
);
1311 add_to_string(&go
, "multipart/form-data; boundary=");
1312 add_bytes_to_string(&go
, boundary
.string
, BOUNDARY_LENGTH
);
1313 add_char_to_string(&go
, '\n');
1316 if (list_empty(bfs
)) {
1319 for (i
= 0; i
< data
.length
; i
++) {
1322 ulonghexcat(p
, NULL
, (int) data
.source
[i
], 2, '0', 0);
1323 add_to_string(&go
, p
);
1326 struct files_offset
*b
;
1330 for (; i
< b
->begin
; i
++) {
1333 ulonghexcat(p
, NULL
, (int) data
.source
[i
], 2, '0', 0);
1334 add_to_string(&go
, p
);
1336 assert(i
== b
->begin
);
1337 assert(b
->end
- b
->begin
>= 2);
1338 assert(data
.source
[b
->begin
] == FILE_CHAR
);
1339 assert(data
.source
[b
->end
- 1] == FILE_CHAR
);
1340 add_char_to_string(&go
, FILE_CHAR
);
1341 encode_uri_string(&go
, data
.source
+ i
+ 1,
1342 b
->end
- b
->begin
- 2, 0);
1343 add_char_to_string(&go
, FILE_CHAR
);
1346 for (; i
< data
.length
; i
++) {
1349 ulonghexcat(p
, NULL
, (int) data
.source
[i
], 2, '0', 0);
1350 add_to_string(&go
, p
);
1358 uri
= get_uri(go
.source
, 0);
1368 #undef BOUNDARY_LENGTH
1371 enum frame_event_status
1372 submit_form(struct session
*ses
, struct document_view
*doc_view
, int do_reload
)
1374 goto_current_link(ses
, doc_view
, do_reload
);
1375 return FRAME_EVENT_OK
;
1379 submit_given_form(struct session
*ses
, struct document_view
*doc_view
,
1380 struct form
*form
, int do_reload
)
1382 /* Added support for submitting forms in hidden
1383 * links in 1.285, commented code can safely be removed once we have made sure the new
1384 * code does the right thing. */
1387 struct document
*document
= doc_view
->document
;
1390 for (link
= 0; link
< document
->nlinks
; link
++) {
1391 struct form_control
*fc
= get_link_form_control(&document
->links
[link
]);
1393 if (fc
&& fc
->form
== form
) {
1394 doc_view
->vs
->current_link
= link
;
1395 submit_form(ses
, doc_view
, 0);
1400 if (!list_empty(form
->items
)) {
1401 struct form_control
*fc
= (struct form_control
*)form
->items
.next
;
1403 enum cache_mode mode
= do_reload
? CACHE_MODE_FORCE_RELOAD
: CACHE_MODE_NORMAL
;
1406 uri
= get_form_uri(ses
, doc_view
, fc
);
1408 goto_uri_frame(ses
, uri
, form
->target
, mode
);
1414 auto_submit_form(struct session
*ses
)
1416 struct document
*document
= ses
->doc_view
->document
;
1418 if (!list_empty(document
->forms
))
1419 submit_given_form(ses
, ses
->doc_view
, document
->forms
.next
, 0);
1425 set_file_form_state(struct terminal
*term
, void *filename_
, void *fs_
)
1427 unsigned char *filename
= filename_
;
1428 struct form_state
*fs
= fs_
;
1430 /* The menu code doesn't free the filename data */
1431 mem_free_set(&fs
->value
, filename
);
1432 fs
->state
= strlen(filename
);
1433 redraw_terminal(term
);
1438 file_form_menu(struct terminal
*term
, void *path_
, void *fs_
)
1440 unsigned char *path
= path_
;
1441 struct form_state
*fs
= fs_
;
1443 /* FIXME: It doesn't work for ../../ */
1445 int valuelen
= strlen(fs
->value
);
1446 int pathlen
= strlen(path
);
1447 int no_elevator
= 0;
1449 /* Don't add elevators for subdirs menus */
1450 /* It is not perfect at all because fs->value is not updated for each
1451 * newly opened file menu. Maybe it should be dropped. */
1452 for (; valuelen
< pathlen
; valuelen
++) {
1453 if (dir_sep(path
[valuelen
- 1])) {
1460 auto_complete_file(term
, 0 /* no_elevator */, path
,
1461 set_file_form_state
,
1462 file_form_menu
, fs
);
1466 enum frame_event_status
1467 field_op(struct session
*ses
, struct document_view
*doc_view
,
1468 struct link
*link
, struct term_event
*ev
)
1470 struct form_control
*fc
;
1471 struct form_state
*fs
;
1472 enum edit_action action_id
;
1473 unsigned char *text
;
1475 enum frame_event_status status
= FRAME_EVENT_REFRESH
;
1477 const unsigned char *ctext
;
1478 int utf8
= ses
->tab
->term
->utf8_cp
;
1479 #endif /* CONFIG_UTF8 */
1481 assert(ses
&& doc_view
&& link
&& ev
);
1482 if_assert_failed
return FRAME_EVENT_OK
;
1484 fc
= get_link_form_control(link
);
1485 assertm(fc
!= NULL
, "link has no form control");
1486 if_assert_failed
return FRAME_EVENT_OK
;
1488 if (fc
->mode
== FORM_MODE_DISABLED
|| ev
->ev
!= EVENT_KBD
1489 || (ses
->insert_mode
== INSERT_MODE_OFF
1490 && !(get_kbd_modifier(ev
) & KBD_MOD_PASTE
)))
1491 return FRAME_EVENT_IGNORED
;
1493 action_id
= kbd_action(KEYMAP_EDIT
, ev
, NULL
);
1495 fs
= find_form_state(doc_view
, fc
);
1496 if (!fs
|| !fs
->value
) return FRAME_EVENT_OK
;
1498 switch (action_id
) {
1501 if (fc
->type
== FC_TEXTAREA
) {
1502 status
= textarea_op_left(fs
, fc
, utf8
);
1506 unsigned char *new_value
;
1508 new_value
= utf8_prevchar(fs
->value
+ fs
->state
, 1, fs
->value
);
1509 fs
->state
= new_value
- fs
->value
;
1511 #endif /* CONFIG_UTF8 */
1512 fs
->state
= int_max(fs
->state
- 1, 0);
1514 case ACT_EDIT_RIGHT
:
1516 if (fc
->type
== FC_TEXTAREA
) {
1517 status
= textarea_op_right(fs
, fc
, utf8
);
1521 unsigned char *text
= fs
->value
+ fs
->state
;
1522 unsigned char *end
= strchr((const char *)text
, '\0');
1524 utf8_to_unicode(&text
, end
);
1525 fs
->state
= (int)(text
- fs
->value
);
1527 #endif /* CONFIG_UTF8 */
1528 fs
->state
= int_min(fs
->state
+ 1, strlen(fs
->value
));
1532 if (fc
->type
== FC_TEXTAREA
) {
1533 status
= textarea_op_home(fs
, fc
, utf8
);
1538 if (fc
->type
== FC_TEXTAREA
) {
1539 status
= textarea_op_home(fs
, fc
);
1544 #endif /* CONFIG_UTF8 */
1547 if (fc
->type
!= FC_TEXTAREA
)
1548 status
= FRAME_EVENT_IGNORED
;
1551 status
= textarea_op_up(fs
, fc
, utf8
);
1553 status
= textarea_op_up(fs
, fc
);
1554 #endif /* CONFIG_UTF8 */
1557 if (fc
->type
!= FC_TEXTAREA
)
1558 status
= FRAME_EVENT_IGNORED
;
1561 status
= textarea_op_down(fs
, fc
, utf8
);
1563 status
= textarea_op_down(fs
, fc
);
1564 #endif /* CONFIG_UTF8 */
1567 if (fc
->type
== FC_TEXTAREA
) {
1569 status
= textarea_op_end(fs
, fc
, utf8
);
1571 status
= textarea_op_end(fs
, fc
);
1572 #endif /* CONFIG_UTF8 */
1574 fs
->state
= strlen(fs
->value
);
1577 case ACT_EDIT_BEGINNING_OF_BUFFER
:
1578 if (fc
->type
== FC_TEXTAREA
) {
1580 status
= textarea_op_bob(fs
, fc
, utf8
);
1583 status
= textarea_op_bob(fs
, fc
);
1584 #endif /* CONFIG_UTF8 */
1589 case ACT_EDIT_END_OF_BUFFER
:
1590 if (fc
->type
== FC_TEXTAREA
) {
1592 status
= textarea_op_eob(fs
, fc
, utf8
);
1594 status
= textarea_op_eob(fs
, fc
);
1595 #endif /* CONFIG_UTF8 */
1597 fs
->state
= strlen(fs
->value
);
1600 case ACT_EDIT_OPEN_EXTERNAL
:
1601 if (form_field_is_readonly(fc
))
1602 status
= FRAME_EVENT_IGNORED
;
1603 else if (fc
->type
== FC_TEXTAREA
)
1604 textarea_edit(0, ses
->tab
->term
, fs
, doc_view
, link
);
1606 case ACT_EDIT_COPY_CLIPBOARD
:
1607 set_clipboard_text(fs
->value
);
1608 status
= FRAME_EVENT_OK
;
1610 case ACT_EDIT_CUT_CLIPBOARD
:
1611 set_clipboard_text(fs
->value
);
1612 if (!form_field_is_readonly(fc
))
1616 if (fc
->type
== FC_TEXTAREA
)
1618 #endif /* CONFIG_UTF8 */
1620 case ACT_EDIT_PASTE_CLIPBOARD
:
1621 if (form_field_is_readonly(fc
)) break;
1623 text
= get_clipboard_text();
1626 length
= strlen(text
);
1627 if (length
<= fc
->maxlength
) {
1628 unsigned char *v
= mem_realloc(fs
->value
, length
+ 1);
1632 memmove(v
, text
, length
+ 1);
1633 fs
->state
= strlen(fs
->value
);
1635 if (utf8
&& fc
->type
== FC_TEXTAREA
)
1637 #endif /* CONFIG_UTF8 */
1642 case ACT_EDIT_ENTER
:
1643 if (fc
->type
== FC_TEXTAREA
) {
1645 status
= textarea_op_enter(fs
, fc
, utf8
);
1647 status
= textarea_op_enter(fs
, fc
);
1648 #endif /* CONFIG_UTF8 */
1652 /* Set status to ok if either it is not possible to
1653 * submit the form or the posting fails. */
1654 /* FIXME: We should maybe have ACT_EDIT_ENTER_RELOAD */
1655 if ((has_form_submit(fc
->form
)
1656 && !get_opt_bool("document.browse.forms.auto_submit", ses
))
1657 || goto_link(ses
, doc_view
, link
, 0)) {
1658 if (ses
->insert_mode
== INSERT_MODE_ON
)
1659 ses
->insert_mode
= INSERT_MODE_OFF
;
1660 status
= FRAME_EVENT_OK
;
1663 case ACT_EDIT_BACKSPACE
:
1664 if (form_field_is_readonly(fc
)) {
1665 status
= FRAME_EVENT_IGNORED
;
1670 status
= FRAME_EVENT_OK
;
1675 int old_state
= fs
->state
;
1676 unsigned char *new_value
;
1678 new_value
= utf8_prevchar(fs
->value
+ fs
->state
, 1, fs
->value
);
1679 fs
->state
= new_value
- fs
->value
;
1681 if (old_state
!= fs
->state
) {
1682 if (fc
->type
== FC_TEXTAREA
)
1684 length
= strlen(fs
->value
+ old_state
) + 1;
1685 memmove(new_value
, fs
->value
+ old_state
, length
);
1688 #endif /* CONFIG_UTF8 */
1690 length
= strlen(fs
->value
+ fs
->state
) + 1;
1691 text
= fs
->value
+ fs
->state
;
1693 memmove(text
- 1, text
, length
);
1697 case ACT_EDIT_DELETE
:
1698 if (form_field_is_readonly(fc
)) {
1699 status
= FRAME_EVENT_IGNORED
;
1703 length
= strlen(fs
->value
);
1704 if (fs
->state
>= length
) {
1705 status
= FRAME_EVENT_OK
;
1710 unsigned char *end
= fs
->value
+ length
;
1711 unsigned char *text
= fs
->value
+ fs
->state
;
1712 unsigned char *old
= text
;
1714 utf8_to_unicode(&text
, end
);
1717 (int)(end
- text
) + 1);
1721 #endif /* CONFIG_UTF8 */
1722 text
= fs
->value
+ fs
->state
;
1724 memmove(text
, text
+ 1, length
- fs
->state
);
1726 case ACT_EDIT_KILL_TO_BOL
:
1727 if (form_field_is_readonly(fc
)) {
1728 status
= FRAME_EVENT_IGNORED
;
1732 if (fs
->state
<= 0) {
1733 status
= FRAME_EVENT_OK
;
1737 text
= memrchr(fs
->value
, ASCII_LF
, fs
->state
);
1739 /* Leave the new-line character if it does not
1740 * immediately precede the cursor. */
1741 if (text
!= &fs
->value
[fs
->state
- 1])
1747 length
= strlen(fs
->value
+ fs
->state
) + 1;
1748 memmove(text
, fs
->value
+ fs
->state
, length
);
1750 fs
->state
= (int) (text
- fs
->value
);
1753 if (fc
->type
== FC_TEXTAREA
)
1756 #endif /* CONFIG_UTF8 */
1758 case ACT_EDIT_KILL_TO_EOL
:
1759 if (form_field_is_readonly(fc
)) {
1760 status
= FRAME_EVENT_IGNORED
;
1764 if (!fs
->value
[fs
->state
]) {
1765 status
= FRAME_EVENT_OK
;
1769 text
= strchr((const char *)(fs
->value
+ fs
->state
), ASCII_LF
);
1771 fs
->value
[fs
->state
] = '\0';
1775 if (fs
->value
[fs
->state
] == ASCII_LF
)
1778 memmove(fs
->value
+ fs
->state
, text
, strlen(text
) + 1);
1781 case ACT_EDIT_KILL_WORD_BACK
:
1782 if (form_field_is_readonly(fc
)) {
1783 status
= FRAME_EVENT_IGNORED
;
1787 if (fs
->state
<= 0) {
1788 status
= FRAME_EVENT_OK
;
1792 text
= &fs
->value
[fs
->state
];
1793 while (text
> fs
->value
&& isspace(*(text
- 1)))
1795 while (text
> fs
->value
&& !isspace(*(text
- 1)))
1797 if (*text
== ASCII_LF
1798 && text
!= &fs
->value
[fs
->state
- 1])
1801 length
= strlen(fs
->value
+ fs
->state
) + 1;
1802 memmove(text
, fs
->value
+ fs
->state
, length
);
1804 fs
->state
= (int) (text
- fs
->value
);
1807 case ACT_EDIT_MOVE_BACKWARD_WORD
:
1808 while (fs
->state
> 0
1809 && isspace(fs
->value
[fs
->state
- 1]))
1811 while (fs
->state
> 0
1812 && !isspace(fs
->value
[fs
->state
- 1]))
1816 case ACT_EDIT_MOVE_FORWARD_WORD
:
1817 while (isspace(fs
->value
[fs
->state
]))
1819 while (fs
->value
[fs
->state
]
1820 && !isspace(fs
->value
[fs
->state
]))
1822 while (isspace(fs
->value
[fs
->state
]))
1826 case ACT_EDIT_AUTO_COMPLETE
:
1827 if (fc
->type
!= FC_FILE
1828 || form_field_is_readonly(fc
)) {
1829 status
= FRAME_EVENT_IGNORED
;
1833 file_form_menu(ses
->tab
->term
, fs
->value
, fs
);
1836 case ACT_EDIT_CANCEL
:
1837 if (ses
->insert_mode
== INSERT_MODE_ON
)
1838 ses
->insert_mode
= INSERT_MODE_OFF
;
1840 status
= FRAME_EVENT_IGNORED
;
1843 case ACT_EDIT_REDRAW
:
1844 redraw_terminal_cls(ses
->tab
->term
);
1845 status
= FRAME_EVENT_OK
;
1849 if (!check_kbd_textinput_key(ev
)) {
1850 status
= FRAME_EVENT_IGNORED
;
1854 if (form_field_is_readonly(fc
)
1856 || strlen(fs
->value
) >= fc
->maxlength
1857 || !insert_in_string(&fs
->value
, fs
->state
, "?", 1)
1858 #endif /* CONFIG_UTF8 */
1861 status
= FRAME_EVENT_OK
;
1866 /* fs->value is in the charset of the terminal. */
1867 ctext
= u2cp_no_nbsp(get_kbd_key(ev
),
1868 get_terminal_codepage(ses
->tab
->term
));
1869 length
= strlen(ctext
);
1871 if (strlen(fs
->value
) + length
> fc
->maxlength
1872 || !insert_in_string(&fs
->value
, fs
->state
, ctext
, length
)) {
1873 status
= FRAME_EVENT_OK
;
1877 fs
->state
+= length
;
1878 if (fc
->type
== FC_TEXTAREA
)
1881 fs
->value
[fs
->state
++] = get_kbd_key(ev
);
1882 #endif /* CONFIG_UTF8 */
1889 static unsigned char *
1890 get_form_label(struct form_control
*fc
)
1895 return N_("Reset form");
1897 return N_("Harmless button");
1902 if (!fc
->form
->action
) return NULL
;
1904 if (fc
->form
->method
== FORM_METHOD_GET
)
1905 return N_("Submit form to");
1906 return N_("Post form to");
1908 return N_("Radio button");
1910 return N_("Checkbox");
1912 return N_("Select field");
1914 return N_("Text field");
1916 return N_("Text area");
1918 return N_("File upload");
1920 return N_("Password field");
1927 add_form_attr_to_string(struct string
*string
, struct terminal
*term
,
1928 unsigned char *name
, unsigned char *value
)
1930 add_to_string(string
, ", ");
1931 add_to_string(string
, _(name
, term
));
1933 add_char_to_string(string
, ' ');
1934 add_to_string(string
, value
);
1939 get_form_info(struct session
*ses
, struct document_view
*doc_view
)
1941 struct terminal
*term
= ses
->tab
->term
;
1942 struct link
*link
= get_current_link(doc_view
);
1943 struct form_control
*fc
;
1944 unsigned char *label
, *key
;
1949 fc
= get_link_form_control(link
);
1950 label
= get_form_label(fc
);
1951 if (!label
) return NULL
;
1953 if (!init_string(&str
)) return NULL
;
1955 add_to_string(&str
, _(label
, term
));
1957 if (link
->type
!= LINK_BUTTON
&& fc
->name
&& fc
->name
[0]) {
1958 add_form_attr_to_string(&str
, term
, N_("name"), fc
->name
);
1965 struct form_state
*fs
= find_form_state(doc_view
, fc
);
1967 if (!fs
->value
|| !fs
->value
[0])
1970 add_form_attr_to_string(&str
, term
, N_("value"), fs
->value
);
1980 unsigned char *uristring
;
1982 if (form_field_is_readonly(fc
)) {
1983 add_form_attr_to_string(&str
, term
, N_("read only"), NULL
);
1986 /* Should we add info about entering insert mode or add info
1987 * about submitting the form? */
1988 if (ses
->insert_mode
== INSERT_MODE_OFF
) {
1989 key
= get_keystroke(ACT_EDIT_ENTER
, KEYMAP_EDIT
);
1993 if (form_field_is_readonly(fc
))
1994 label
= N_("press %s to navigate");
1996 label
= N_("press %s to edit");
1998 add_to_string(&str
, " (");
1999 add_format_to_string(&str
, _(label
, term
), key
);
2000 add_char_to_string(&str
, ')');
2006 if (fc
->type
== FC_TEXTAREA
)
2011 if (!fc
->form
->action
2012 || (has_form_submit(fc
->form
)
2013 && !get_opt_bool("document.browse.forms.auto_submit",
2017 uri
= get_uri(fc
->form
->action
, 0);
2020 /* Add the uri with password and post info stripped */
2021 uristring
= get_uri_string(uri
, URI_PUBLIC
);
2024 if (!uristring
) break;
2026 key
= get_keystroke(ACT_EDIT_ENTER
, KEYMAP_EDIT
);
2028 mem_free(uristring
);
2032 if (fc
->form
->method
== FORM_METHOD_GET
)
2033 label
= N_("press %s to submit to %s");
2035 label
= N_("press %s to post to %s");
2037 add_to_string(&str
, " (");
2038 add_format_to_string(&str
, _(label
, term
), key
, uristring
);
2039 mem_free(uristring
);
2042 add_char_to_string(&str
, ')');
2047 add_char_to_string(&str
, ' ');
2050 /* Add the uri with password and post info stripped */
2051 add_string_uri_to_string(&str
, fc
->form
->action
, URI_PUBLIC
);
2062 && get_opt_bool("document.browse.accesskey.display", ses
)) {
2063 add_to_string(&str
, " (");
2064 add_accesskey_to_string(&str
, link
->accesskey
);
2065 add_char_to_string(&str
, ')');
2072 link_form_menu_func(struct terminal
*term
, void *link_number_
, void *ses_
)
2074 struct session
*ses
= ses_
;
2075 struct document_view
*doc_view
;
2076 int link_number
= *(int *) link_number_
;
2078 mem_free(link_number_
);
2080 assert(term
&& ses
);
2081 if_assert_failed
return;
2083 doc_view
= current_frame(ses
);
2084 if (!doc_view
) return;
2086 assert(doc_view
->vs
&& doc_view
->document
);
2087 if_assert_failed
return;
2089 jump_to_link_number(ses
, doc_view
, link_number
);
2090 refresh_view(ses
, doc_view
, 0);
2094 link_form_menu(struct session
*ses
)
2096 struct document_view
*doc_view
;
2098 struct menu_item
*mi
;
2099 struct form_control
*fc
;
2103 if_assert_failed
return;
2105 doc_view
= current_frame(ses
);
2106 if (!doc_view
) return;
2108 assert(doc_view
->vs
&& doc_view
->document
);
2109 if_assert_failed
return;
2111 link
= get_current_link(doc_view
);
2114 assert(link_is_form(link
));
2116 fc
= get_link_form_control(link
);
2121 mi
= new_menu(FREE_LIST
| FREE_TEXT
| NO_INTL
);
2124 foreach (fc
, form
->items
) {
2125 unsigned char *text
;
2126 unsigned char *rtext
;
2137 text
= N_("Useless button");
2139 text
= N_("Submit button");
2143 text
= get_form_label(fc
);
2146 link_number
= get_form_control_link(doc_view
->document
, fc
);
2148 || !init_string(&str
))
2152 add_to_string(&str
, _(text
, ses
->tab
->term
));
2155 if (!rtext
) rtext
= fc
->alt
;
2157 add_to_menu(&mi
, str
.source
, rtext
, ACT_MAIN_NONE
,
2158 link_form_menu_func
, intdup(link_number
),
2162 do_menu(ses
->tab
->term
, mi
, ses
, 1);