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/html/parser.h"
33 #include "document/view.h"
34 #include "ecmascript/ecmascript.h"
35 #include "intl/gettext/libintl.h"
36 #include "formhist/formhist.h"
37 #include "mime/mime.h"
38 #include "osdep/ascii.h"
39 #include "osdep/osdep.h"
40 #include "protocol/uri.h"
41 #include "session/session.h"
42 #include "session/task.h"
43 #include "terminal/kbd.h"
44 #include "terminal/terminal.h"
45 #include "terminal/window.h"
46 #include "util/conv.h"
47 #include "util/error.h"
48 #include "util/file.h"
49 #include "util/memory.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 */
63 /** @relates submitted_value */
64 struct submitted_value
*
65 init_submitted_value(unsigned char *name
, unsigned char *value
, enum form_type type
,
66 struct form_control
*fc
, int position
)
68 struct submitted_value
*sv
;
70 sv
= mem_alloc(sizeof(*sv
));
73 sv
->value
= stracpy(value
);
74 if (!sv
->value
) { mem_free(sv
); return NULL
; }
76 sv
->name
= stracpy(name
);
77 if (!sv
->name
) { mem_free(sv
->value
); mem_free(sv
); return NULL
; }
80 sv
->form_control
= fc
;
81 sv
->position
= position
;
86 /** @relates submitted_value */
88 done_submitted_value(struct submitted_value
*sv
)
91 mem_free_if(sv
->value
);
92 mem_free_if(sv
->name
);
97 fixup_select_state(struct form_control
*fc
, struct form_state
*fs
)
102 if_assert_failed
return;
105 && fs
->state
< fc
->nvalues
106 && !strcmp(fc
->values
[fs
->state
], fs
->value
))
109 for (i
= 0; i
< fc
->nvalues
; i
++)
110 if (!strcmp(fc
->values
[i
], fs
->value
)) {
117 mem_free_set(&fs
->value
, stracpy(fc
->nvalues
119 : (unsigned char *) ""));
124 selected_item(struct terminal
*term
, void *item_
, void *ses_
)
126 struct session
*ses
= ses_
;
127 int item
= (long) item_
;
128 struct document_view
*doc_view
;
130 struct form_state
*fs
;
131 struct form_control
*fc
;
134 if_assert_failed
return;
135 doc_view
= current_frame(ses
);
137 assert(doc_view
&& doc_view
->vs
&& doc_view
->document
);
138 if_assert_failed
return;
140 link
= get_current_link(doc_view
);
141 if (!link
|| link
->type
!= LINK_SELECT
) return;
143 fc
= get_link_form_control(link
);
144 fs
= find_form_state(doc_view
, fc
);
146 if (item
>= 0 && item
< fc
->nvalues
) {
148 mem_free_set(&fs
->value
, stracpy(fc
->values
[item
]));
150 fixup_select_state(fc
, fs
);
153 refresh_view(ses
, doc_view
, 0);
157 init_form_state(struct document_view
*doc_view
,
158 struct form_control
*fc
, struct form_state
*fs
)
160 struct terminal
*term
;
161 int doc_cp
, viewer_cp
;
164 if_assert_failed
return;
166 doc_cp
= doc_view
->document
->cp
;
167 term
= doc_view
->session
->tab
->term
;
168 viewer_cp
= get_terminal_codepage(term
);
170 mem_free_set(&fs
->value
, NULL
);
175 #ifdef CONFIG_FORMHIST
176 fs
->value
= null_or_stracpy(
177 get_form_history_value(
178 fc
->form
->action
, fc
->name
));
179 #endif /* CONFIG_FORMHIST */
182 if (fs
->value
== NULL
) {
183 fs
->value
= convert_string(
184 get_translation_table(doc_cp
, viewer_cp
),
186 strlen(fc
->default_value
),
188 &fs
->state
, NULL
, NULL
);
190 fs
->state
= fs
->value
? strlen(fs
->value
) : 0;
192 if (fc
->type
== FC_TEXTAREA
)
194 #endif /* CONFIG_UTF8 */
198 fs
->value
= stracpy("");
203 fs
->value
= convert_string(
204 get_translation_table(doc_cp
, viewer_cp
),
206 strlen(fc
->default_value
),
208 &fs
->state
, NULL
, NULL
);
209 fs
->state
= fc
->default_state
;
210 fixup_select_state(fc
, fs
);
214 fs
->state
= fc
->default_state
;
221 /* We don't want to recode hidden fields. */
222 fs
->value
= stracpy(fc
->default_value
);
229 find_form_state(struct document_view
*doc_view
, struct form_control
*fc
)
231 struct view_state
*vs
;
232 struct form_state
*fs
;
235 assert(doc_view
&& doc_view
->vs
&& fc
);
236 if_assert_failed
return NULL
;
241 if (n
>= vs
->form_info_len
) {
243 #ifdef CONFIG_ECMASCRIPT
244 const struct form_state
*const old_form_info
= vs
->form_info
;
247 fs
= mem_align_alloc(&vs
->form_info
, vs
->form_info_len
, nn
, 0);
248 if (!fs
) return NULL
;
250 vs
->form_info_len
= nn
;
252 #ifdef CONFIG_ECMASCRIPT
253 /* TODO: Standard C does not allow this comparison;
254 * if the memory to which old_form_info pointed has
255 * been freed, then the value of the pointer itself is
256 * indeterminate. Fixing this would require changing
257 * mem_align_alloc to tell the caller whether it did
259 if (vs
->form_info
!= old_form_info
) {
260 /* vs->form_info[] was moved to a different address.
261 * Update all the ECMAScript objects that have
262 * pointers to its elements. */
263 for (nn
= 0; nn
< vs
->form_info_len
; nn
++)
264 ecmascript_moved_form_state(&vs
->form_info
[nn
]);
266 #endif /* CONFIG_ECMASCRIPT */
268 fs
= &vs
->form_info
[n
];
270 if (fs
->form_view
&& fs
->form_view
->form_num
== fc
->form
->form_num
271 && fs
->g_ctrl_num
== fc
->g_ctrl_num
272 && fs
->position
== fc
->position
273 && fs
->type
== fc
->type
)
276 mem_free_if(fs
->value
);
277 memset(fs
, 0, sizeof(*fs
));
278 fs
->form_view
= find_form_view(doc_view
, fc
->form
);
279 fs
->g_ctrl_num
= fc
->g_ctrl_num
;
280 fs
->position
= fc
->position
;
282 init_form_state(doc_view
, fc
, fs
);
287 struct form_control
*
288 find_form_control(struct document
*document
, struct form_state
*fs
)
290 struct form
*form
= find_form_by_form_view(document
, fs
->form_view
);
291 struct form_control
*fc
;
293 foreach (fc
, form
->items
) {
294 if (fs
->g_ctrl_num
== fc
->g_ctrl_num
295 && fs
->position
== fc
->position
296 && fs
->type
== fc
->type
)
304 find_form_view_in_vs(struct view_state
*vs
, int form_num
)
306 struct form_view
*fv
;
310 foreach (fv
, vs
->forms
)
311 if (fv
->form_num
== form_num
)
314 fv
= mem_calloc(1, sizeof(*fv
));
315 fv
->form_num
= form_num
;
316 add_to_list(vs
->forms
, fv
);
321 find_form_view(struct document_view
*doc_view
, struct form
*form
)
323 return find_form_view_in_vs(doc_view
->vs
, form
->form_num
);
327 find_form_by_form_view(struct document
*document
, struct form_view
*fv
)
331 foreach (form
, document
->forms
) {
332 if (form
->form_num
== fv
->form_num
)
338 /** Free any data owned by @a fs, but not the struct form_state
339 * itself, because that is normally allocated as part of an array.
340 * @relates form_state */
342 done_form_state(struct form_state
*fs
)
344 #ifdef CONFIG_ECMASCRIPT
345 ecmascript_detach_form_state(fs
);
347 mem_free_if(fs
->value
);
350 /** Free @a fv and any data owned by it. This does not call
351 * del_from_list(fv), so the caller must usually do that first.
352 * @relates form_view */
354 done_form_view(struct form_view
*fv
)
356 #ifdef CONFIG_ECMASCRIPT
357 ecmascript_detach_form_view(fv
);
363 get_current_state(struct session
*ses
)
365 struct document_view
*doc_view
;
367 struct form_state
*fs
;
370 if_assert_failed
return -1;
371 doc_view
= current_frame(ses
);
373 assert(doc_view
&& doc_view
->vs
&& doc_view
->document
);
374 if_assert_failed
return -1;
376 link
= get_current_link(doc_view
);
377 if (!link
|| link
->type
!= LINK_SELECT
) return -1;
379 fs
= find_form_state(doc_view
, get_link_form_control(link
));
380 if (fs
) return fs
->state
;
385 draw_form_entry(struct terminal
*term
, struct document_view
*doc_view
,
388 struct form_state
*fs
;
389 struct form_control
*fc
;
390 struct view_state
*vs
;
394 assert(term
&& doc_view
&& doc_view
->document
&& doc_view
->vs
&& link
);
395 if_assert_failed
return;
397 fc
= get_link_form_control(link
);
398 assertm(fc
!= NULL
, "link %d has no form control", (int) (link
- doc_view
->document
->links
));
399 if_assert_failed
return;
401 fs
= find_form_state(doc_view
, fc
);
404 box
= &doc_view
->box
;
411 unsigned char *text
, *end
, *last_in_view
;
413 #endif /* CONFIG_UTF8 */
420 if (!link
->npoints
) break;
422 y
= link
->points
[0].y
+ dy
;
423 if (!row_is_in_box(box
, y
))
426 x
= link
->points
[0].x
+ dx
;
428 if (term
->utf8_cp
) goto utf8
;
429 #endif /* CONFIG_UTF8 */
430 int_bounds(&fs
->vpos
, fs
->state
- fc
->size
+ 1, fs
->state
);
431 len
= strlen(fs
->value
) - fs
->vpos
;
433 for (i
= 0; i
< fc
->size
; i
++, x
++) {
436 if (!col_is_in_box(box
, x
)) continue;
438 if (fs
->value
&& i
>= -fs
->vpos
&& i
< len
)
439 data
= fc
->type
!= FC_PASSWORD
440 ? fs
->value
[i
+ fs
->vpos
] : '*';
444 draw_char_data(term
, x
, y
, data
);
453 if (!text
) text
= "";
455 int_bounds(&fs
->state
, 0, len
);
456 int_bounds(&fs
->vpos
, 0, fs
->state
);
461 for (i
= 0; i
< fc
->size
; ) {
464 unsigned char *maybe_in_view
= text
;
466 data
= utf8_to_unicode(&text
, end
);
467 if (data
== UCS_NO_CHAR
) /* end of string */
469 else if (fc
->type
== FC_PASSWORD
)
472 cells
= unicode_to_cell(data
);
473 if (i
+ cells
<= fc
->size
) {
474 last_in_view
= maybe_in_view
;
475 if (colspan_is_in_box(box
, x
+ i
, cells
)) {
476 /* The character fits completely.
477 * Draw the character, and mark any
478 * further cells with UCS_NO_CHAR. */
479 draw_char_data(term
, x
+ i
, y
, data
);
480 for (cell
= 1; cell
< cells
; cell
++)
481 draw_char_data(term
, x
+ i
+ cell
,
487 /* The character does not fit completely.
488 * Write UCS_ORPHAN_CELL to the cells that
490 for (cell
= 0; cell
< cells
; cell
++) {
491 if (col_is_in_box(box
, x
+ i
+ cell
)
492 && i
+ cell
< fc
->size
)
502 /* The int_bounds calls above ensured that the
503 * insertion point cannot be at the left side
504 * of the scrolled-visible part of the text.
505 * However it can still be at the right side.
506 * Check whether we need to change fs->vpos.
508 * This implementation attempts to follow
510 * - If the insertion point is at the end of
511 * the string, leave at least one empty cell
512 * so that there is a place for the cursor.
513 * - If a character follows the insertion
514 * point, make that character fully visible;
515 * note the character may be double-width.
516 * - If fc->size < 2, it is not possible to
517 * make a double-width character fully
518 * visible. In this case, it is OK if the
519 * output is ugly, but ELinks must not fall
520 * into an infinite loop or crash.
521 * - The length of the string should not affect
522 * how long this function takes. The width
523 * of the widget naturally will.
524 * - Optimize the case where fields are drawn
525 * several times without being modified.
528 * - If the "for i" loop above hit UCS_NO_CHAR,
529 * then there is no need to scroll.
530 * - When the string ends with a double-width
531 * character that fits in only partially,
532 * then text==end, but the field may have
534 if (fs
->value
&& last_in_view
535 && last_in_view
< fs
->value
+ fs
->state
) {
536 unsigned char *ptr
= fs
->value
+ fs
->state
;
537 int cells
= fc
->size
;
538 enum utf8_step how
= (fc
->type
== FC_PASSWORD
)
539 ? UTF8_STEP_CHARACTERS
540 : UTF8_STEP_CELLS_FEWER
;
542 /* The insertion point is at the right
543 * side of the scrolled-visible part
544 * of the text. Decide a new fs->vpos
545 * by counting cells backwards from
546 * @ptr. But first advance @ptr past
547 * the character that follows the
548 * insertion point, so that it will be
549 * fully displayed. If there is no
550 * such character, reserve one cell
551 * for the cursor anyway. */
552 if (utf8_to_unicode(&ptr
, end
) == UCS_NO_CHAR
)
554 ptr
= utf8_step_backward(ptr
, fs
->value
,
557 if (fs
->vpos
!= ptr
- fs
->value
) {
558 fs
->vpos
= ptr
- fs
->value
;
560 goto retry_after_scroll
;
564 #endif /* CONFIG_UTF8 */
566 draw_textarea(term
, fs
, doc_view
, link
);
570 if (link
->npoints
< 2) break;
571 x
= link
->points
[1].x
+ dx
;
572 y
= link
->points
[1].y
+ dy
;
573 if (is_in_box(box
, x
, y
))
574 draw_char_data(term
, x
, y
, fs
->state
? 'X' : ' ');
577 fixup_select_state(fc
, fs
);
578 if (fs
->state
< fc
->nvalues
)
579 s
= fc
->labels
[fs
->state
];
581 /* XXX: when can this happen? --pasky */
584 if (term
->utf8_cp
) goto utf8_select
;
585 #endif /* CONFIG_UTF8 */
586 len
= s
? strlen(s
) : 0;
587 for (i
= 0; i
< link
->npoints
; i
++) {
588 x
= link
->points
[i
].x
+ dx
;
589 y
= link
->points
[i
].y
+ dy
;
590 if (is_in_box(box
, x
, y
))
591 draw_char_data(term
, x
, y
, i
< len
? s
[i
] : '_');
597 end
= strchr(s
, '\0');
598 len
= utf8_ptr2cells(text
, end
);
599 for (i
= 0; i
< link
->npoints
; i
++) {
600 x
= link
->points
[i
].x
+ dx
;
601 y
= link
->points
[i
].y
+ dy
;
602 if (is_in_box(box
, x
, y
)) {
607 data
= utf8_to_unicode(&s
, end
);
608 cell
= unicode_to_cell(data
);
609 if (i
+ 1 < len
&& cell
== 2) {
610 draw_char_data(term
, x
++, y
, data
);
614 } else if (cell
== 2) {
615 data
= UCS_ORPHAN_CELL
;
619 draw_char_data(term
, x
, y
, data
);
623 #endif /* CONFIG_UTF8 */
634 draw_forms(struct terminal
*term
, struct document_view
*doc_view
)
636 struct link
*l1
, *l2
;
638 assert(term
&& doc_view
);
639 if_assert_failed
return;
641 l1
= get_first_link(doc_view
);
642 l2
= get_last_link(doc_view
);
645 assertm(!l1
&& !l2
, "get_first_link == %p, get_last_link == %p", l1
, l2
);
646 /* Return path :-). */
650 struct form_control
*fc
= get_link_form_control(l1
);
653 draw_form_entry(term
, doc_view
, l1
);
659 /** @relates submitted_value */
661 done_submitted_value_list(LIST_OF(struct submitted_value
) *list
)
663 struct submitted_value
*sv
, *svtmp
;
666 if_assert_failed
return;
668 foreach (sv
, *list
) {
671 del_from_list(svtmp
);
672 done_submitted_value(svtmp
);
677 add_submitted_value_to_list(struct form_control
*fc
,
678 struct form_state
*fs
,
679 LIST_OF(struct submitted_value
) *list
)
681 struct submitted_value
*sub
;
686 assert(fc
&& fs
&& list
);
689 position
= fc
->position
;
697 sub
= init_submitted_value(name
, fs
->value
, type
, fc
, position
);
698 if (sub
) add_to_list(*list
, sub
);
703 if (!fs
->state
) break;
710 sub
= init_submitted_value(name
, fs
->value
, type
, fc
,
712 if (sub
) add_to_list(*list
, sub
);
716 if (!fc
->nvalues
) break;
718 fixup_select_state(fc
, fs
);
719 sub
= init_submitted_value(name
, fs
->value
, type
, fc
, position
);
720 if (sub
) add_to_list(*list
, sub
);
724 name
= straconcat(fc
->name
, ".x", (unsigned char *) NULL
);
726 sub
= init_submitted_value(name
, "0", type
, fc
, position
);
728 if (sub
) add_to_list(*list
, sub
);
730 name
= straconcat(fc
->name
, ".y", (unsigned char *) NULL
);
732 sub
= init_submitted_value(name
, "0", type
, fc
, position
);
734 if (sub
) add_to_list(*list
, sub
);
741 sort_submitted_values(LIST_OF(struct submitted_value
) *list
)
744 struct submitted_value
*sub
;
747 foreach (sub
, *list
) if (list_has_next(*list
, sub
))
748 if (sub
->next
->position
< sub
->position
) {
749 struct submitted_value
*next
= sub
->next
;
752 add_at_pos(next
, sub
);
757 foreachback (sub
, *list
) if (list_has_next(*list
, sub
))
758 if (sub
->next
->position
< sub
->position
) {
759 struct submitted_value
*next
= sub
->next
;
762 add_at_pos(next
, sub
);
772 get_successful_controls(struct document_view
*doc_view
,
773 struct form_control
*fc
,
774 LIST_OF(struct submitted_value
) *list
)
776 struct form_control
*fc2
;
778 assert(doc_view
&& fc
&& fc
->form
&& list
);
779 if_assert_failed
return;
781 foreach (fc2
, fc
->form
->items
) {
782 if (((fc2
->type
!= FC_SUBMIT
&&
783 fc2
->type
!= FC_IMAGE
&&
784 fc2
->type
!= FC_RESET
&&
785 fc2
->type
!= FC_BUTTON
) || fc2
== fc
)
786 && fc2
->name
&& fc2
->name
[0]) {
787 struct form_state
*fs
= find_form_state(doc_view
, fc2
);
791 add_submitted_value_to_list(fc2
, fs
, list
);
795 sort_submitted_values(list
);
799 encode_crlf(struct submitted_value
*sv
)
801 struct string newtext
;
804 assert(sv
&& sv
->value
);
805 if_assert_failed
return NULL
;
807 if (!init_string(&newtext
)) return NULL
;
809 for (i
= 0; sv
->value
[i
]; i
++) {
810 if (sv
->value
[i
] == '\r') {
811 if (sv
->value
[i
+1] != '\n')
812 add_crlf_to_string(&newtext
);
813 } else if (sv
->value
[i
] == '\n')
814 add_crlf_to_string(&newtext
);
816 add_char_to_string(&newtext
, sv
->value
[i
]);
819 return newtext
.source
;
823 encode_controls(LIST_OF(struct submitted_value
) *l
, struct string
*data
,
824 int cp_from
, int cp_to
)
826 struct submitted_value
*sv
;
827 struct conv_table
*convert_table
= NULL
;
831 if_assert_failed
return;
834 unsigned char *p2
= NULL
;
837 add_char_to_string(data
, '&');
841 encode_uri_string(data
, sv
->name
, strlen(sv
->name
), 1);
842 add_char_to_string(data
, '=');
844 /* Convert back to original encoding (see html_form_control()
845 * for the original recoding). */
846 if (sv
->type
== FC_TEXTAREA
) {
849 p
= encode_textarea(sv
);
852 convert_table
= get_translation_table(cp_from
, cp_to
);
854 p2
= convert_string(convert_table
, p
,
855 strlen(p
), -1, CSM_FORM
, NULL
, NULL
, NULL
);
858 } else if (sv
->type
== FC_TEXT
||
859 sv
->type
== FC_PASSWORD
) {
861 convert_table
= get_translation_table(cp_from
, cp_to
);
863 p2
= convert_string(convert_table
, sv
->value
,
864 strlen(sv
->value
), -1, CSM_FORM
, NULL
, NULL
, NULL
);
865 } else if (sv
->type
== FC_HIDDEN
) {
866 p2
= encode_crlf(sv
);
868 p2
= stracpy(sv
->value
);
872 encode_uri_string(data
, p2
, strlen(p2
), 1);
880 #define BOUNDARY_LENGTH 32
881 #define realloc_bound_ptrs(bptrs, bptrs_size) \
882 mem_align_alloc(bptrs, bptrs_size, bptrs_size + 1, 0xFF)
884 struct boundary_info
{
887 unsigned char string
[BOUNDARY_LENGTH
];
890 /** @relates boundary_info */
892 init_boundary(struct boundary_info
*boundary
)
894 memset(boundary
, 0, sizeof(*boundary
));
895 memset(boundary
->string
, '0', BOUNDARY_LENGTH
);
898 /** Add boundary to string and save the offset
899 * @relates boundary_info */
901 add_boundary(struct string
*data
, struct boundary_info
*boundary
)
903 add_to_string(data
, "--");
905 if (realloc_bound_ptrs(&boundary
->offsets
, boundary
->count
))
906 boundary
->offsets
[boundary
->count
++] = data
->length
;
908 add_bytes_to_string(data
, boundary
->string
, BOUNDARY_LENGTH
);
911 /** @relates boundary_info */
912 static inline unsigned char *
913 increment_boundary_counter(struct boundary_info
*boundary
)
917 /* This is just a decimal string incrementation */
918 for (j
= BOUNDARY_LENGTH
- 1; j
>= 0; j
--) {
919 if (boundary
->string
[j
]++ < '9')
920 return boundary
->string
;
922 boundary
->string
[j
] = '0';
925 INTERNAL("Form data boundary counter overflow");
930 /** @relates boundary_info */
932 check_boundary(struct string
*data
, struct boundary_info
*boundary
)
934 unsigned char *bound
= boundary
->string
;
937 /* Search between all boundaries. There is a starting and an ending
938 * boundary so only check the range of chars after the current offset
939 * and before the next offset. If some string in the form data matches
940 * the boundary string it is changed. */
941 for (i
= 0; i
< boundary
->count
- 1; i
++) {
942 /* Start after the boundary string and also jump past the
943 * "\r\nContent-Disposition: form-data; name=\"" string added
944 * before any form data. */
945 int start_offset
= boundary
->offsets
[i
] + BOUNDARY_LENGTH
+ 40;
947 /* End so that there is atleast BOUNDARY_LENGTH chars to
948 * compare. Subtract 2 char because there is no need to also
949 * compare the '--' prefix that is part of the boundary. */
950 int end_offset
= boundary
->offsets
[i
+ 1] - BOUNDARY_LENGTH
- 2;
951 unsigned char *pos
= data
->source
+ start_offset
;
952 unsigned char *end
= data
->source
+ end_offset
;
954 for (; pos
<= end
; pos
++) {
955 if (memcmp(pos
, bound
, BOUNDARY_LENGTH
))
958 /* If incrementing causes overflow bail out. There is
959 * no need to reset the boundary string with '0' since
960 * that is already done when incrementing. */
961 if (!increment_boundary_counter(boundary
))
964 /* Else start checking all boundaries using the new
971 /* Now update all the boundaries with the unique boundary string */
972 for (i
= 0; i
< boundary
->count
; i
++)
973 memcpy(data
->source
+ boundary
->offsets
[i
], bound
, BOUNDARY_LENGTH
);
976 /** @todo FIXME: shouldn't we encode data at send time (in http.c) ? --Zas */
978 encode_multipart(struct session
*ses
, LIST_OF(struct submitted_value
) *l
,
980 struct boundary_info
*boundary
, int cp_from
, int cp_to
)
982 struct conv_table
*convert_table
= NULL
;
983 struct submitted_value
*sv
;
985 assert(ses
&& l
&& data
&& boundary
);
986 if_assert_failed
return;
988 init_boundary(boundary
);
991 add_boundary(data
, boundary
);
992 add_crlf_to_string(data
);
994 /** @bug FIXME: name is not encoded.
996 * multipart/form-data contains a series of parts.
997 * Each part is expected to contain a content-disposition
998 * header where the value is "form-data" and a name attribute
999 * specifies the field name within the form,
1000 * e.g., 'content-disposition: form-data; name="xxxxx"',
1001 * where xxxxx is the field name corresponding to that field.
1002 * Field names originally in non-ASCII character sets may be
1003 * encoded using the method outlined in RFC 1522. */
1004 add_to_string(data
, "Content-Disposition: form-data; name=\"");
1005 add_to_string(data
, sv
->name
);
1006 add_char_to_string(data
, '"');
1008 if (sv
->type
== FC_FILE
) {
1009 #define F_BUFLEN 1024
1011 unsigned char buffer
[F_BUFLEN
];
1012 unsigned char *extension
;
1014 add_to_string(data
, "; filename=\"");
1015 add_to_string(data
, get_filename_position(sv
->value
));
1016 /* It sends bad data if the file name contains ", but
1017 Netscape does the same */
1018 /* FIXME: We should follow RFCs 1522, 1867,
1019 * 2047 (updated by rfc 2231), to provide correct support
1020 * for non-ASCII and special characters in values. --Zas */
1021 add_char_to_string(data
, '"');
1023 /* Add a Content-Type header if the type is configured */
1024 extension
= strrchr(sv
->value
, '.');
1026 unsigned char *type
= get_extension_content_type(extension
);
1029 add_crlf_to_string(data
);
1030 add_to_string(data
, "Content-Type: ");
1031 add_to_string(data
, type
);
1036 add_crlf_to_string(data
);
1037 add_crlf_to_string(data
);
1040 unsigned char *filename
;
1042 if (get_cmd_opt_bool("anonymous")) {
1047 /* FIXME: DO NOT COPY FILE IN MEMORY !! --Zas */
1048 filename
= expand_tilde(sv
->value
);
1049 if (!filename
) goto encode_error
;
1051 fh
= open(filename
, O_RDONLY
);
1054 if (fh
== -1) goto encode_error
;
1057 ssize_t rd
= safe_read(fh
, buffer
, F_BUFLEN
);
1065 add_bytes_to_string(data
, buffer
, rd
);
1075 add_crlf_to_string(data
);
1076 add_crlf_to_string(data
);
1078 /* Convert back to original encoding (see
1079 * html_form_control() for the original recoding). */
1080 if (sv
->type
== FC_TEXT
|| sv
->type
== FC_PASSWORD
||
1081 sv
->type
== FC_TEXTAREA
) {
1085 convert_table
= get_translation_table(cp_from
,
1088 p
= convert_string(convert_table
, sv
->value
,
1089 strlen(sv
->value
), -1, CSM_FORM
, NULL
,
1092 add_to_string(data
, p
);
1096 add_to_string(data
, sv
->value
);
1100 add_crlf_to_string(data
);
1104 add_boundary(data
, boundary
);
1105 add_to_string(data
, "--\r\n");
1107 check_boundary(data
, boundary
);
1109 mem_free_if(boundary
->offsets
);
1113 mem_free_if(boundary
->offsets
);
1116 /* XXX: This error message should move elsewhere. --Zas */
1117 info_box(ses
->tab
->term
, MSGBOX_FREE_TEXT
,
1118 N_("Error while posting form"), ALIGN_CENTER
,
1119 msg_text(ses
->tab
->term
, N_("Could not load file %s: %s"),
1120 sv
->value
, strerror(errno
)));
1124 encode_newlines(struct string
*string
, unsigned char *data
)
1126 for (; *data
; data
++) {
1127 if (*data
== '\n' || *data
== '\r') {
1128 unsigned char buffer
[3];
1132 buffer
[1] = hx((((int) *data
) & 0xF0) >> 4);
1133 buffer
[2] = hx(((int) *data
) & 0xF);
1134 add_bytes_to_string(string
, buffer
, 3);
1136 add_char_to_string(string
, *data
);
1142 encode_text_plain(LIST_OF(struct submitted_value
) *l
, struct string
*data
,
1143 int cp_from
, int cp_to
)
1145 struct submitted_value
*sv
;
1146 struct conv_table
*convert_table
= get_translation_table(cp_from
, cp_to
);
1149 if_assert_failed
return;
1152 unsigned char *area51
= NULL
;
1153 unsigned char *value
= sv
->value
;
1155 add_to_string(data
, sv
->name
);
1156 add_char_to_string(data
, '=');
1160 value
= area51
= encode_textarea(sv
);
1164 if (!area51
) value
= area51
= encode_crlf(sv
);
1169 /* Convert back to original encoding (see
1170 * html_form_control() for the original recoding). */
1171 value
= convert_string(convert_table
, value
,
1172 strlen(value
), -1, CSM_FORM
,
1175 /* Falling right through to free that textarea stuff */
1176 mem_free_if(area51
);
1178 /* Did the conversion fail? */
1181 encode_newlines(data
, value
);
1183 /* Free if we did convert something */
1184 if (value
!= sv
->value
) mem_free(value
);
1187 add_crlf_to_string(data
);
1192 do_reset_form(struct document_view
*doc_view
, struct form
*form
)
1194 struct form_control
*fc
;
1196 assert(doc_view
&& doc_view
->document
);
1197 if_assert_failed
return;
1199 foreach (fc
, form
->items
) {
1200 struct form_state
*fs
= find_form_state(doc_view
, fc
);
1202 if (fs
) init_form_state(doc_view
, fc
, fs
);
1206 enum frame_event_status
1207 reset_form(struct session
*ses
, struct document_view
*doc_view
, int a
)
1209 struct link
*link
= get_current_link(doc_view
);
1211 if (!link
) return FRAME_EVENT_OK
;
1213 do_reset_form(doc_view
, get_link_form_control(link
)->form
);
1214 draw_forms(ses
->tab
->term
, doc_view
);
1216 /* Could be the refresh return value and then ditch the draw_forms()
1218 return FRAME_EVENT_OK
;
1222 get_form_uri(struct session
*ses
, struct document_view
*doc_view
,
1223 struct form_control
*fc
)
1225 struct boundary_info boundary
;
1226 INIT_LIST_OF(struct submitted_value
, submit
);
1233 assert(ses
&& ses
->tab
&& ses
->tab
->term
);
1234 if_assert_failed
return NULL
;
1235 assert(doc_view
&& doc_view
->document
&& fc
&& fc
->form
);
1236 if_assert_failed
return NULL
;
1240 if (fc
->type
== FC_RESET
) {
1241 do_reset_form(doc_view
, form
);
1246 || !init_string(&data
))
1249 get_successful_controls(doc_view
, fc
, &submit
);
1251 cp_from
= get_terminal_codepage(ses
->tab
->term
);
1252 cp_to
= doc_view
->document
->cp
;
1253 switch (form
->method
) {
1254 case FORM_METHOD_GET
:
1255 case FORM_METHOD_POST
:
1256 encode_controls(&submit
, &data
, cp_from
, cp_to
);
1259 case FORM_METHOD_POST_MP
:
1260 encode_multipart(ses
, &submit
, &data
, &boundary
, cp_from
, cp_to
);
1263 case FORM_METHOD_POST_TEXT_PLAIN
:
1264 encode_text_plain(&submit
, &data
, cp_from
, cp_to
);
1267 #ifdef CONFIG_FORMHIST
1268 /* XXX: We check data.source here because a NULL value can indicate
1269 * not only a memory allocation failure, but also an error reading
1270 * a file that is to be uploaded. TODO: Distinguish between
1271 * these two classes of errors (is it worth it?). -- Miciah */
1273 && get_opt_bool("document.browse.forms.show_formhist"))
1274 memorize_form(ses
, &submit
, form
);
1277 done_submitted_value_list(&submit
);
1280 || !init_string(&go
)) {
1285 switch (form
->method
) {
1286 case FORM_METHOD_GET
:
1288 unsigned char *pos
= strchr(form
->action
, '#');
1291 add_bytes_to_string(&go
, form
->action
, pos
- form
->action
);
1293 add_to_string(&go
, form
->action
);
1296 if (strchr(go
.source
, '?'))
1297 add_char_to_string(&go
, '&');
1299 add_char_to_string(&go
, '?');
1301 add_string_to_string(&go
, &data
);
1303 if (pos
) add_to_string(&go
, pos
);
1306 case FORM_METHOD_POST
:
1307 case FORM_METHOD_POST_MP
:
1308 case FORM_METHOD_POST_TEXT_PLAIN
:
1310 /* Note that we end content type here by a simple '\n',
1311 * replaced later by correct '\r\n' in http_send_header(). */
1314 add_to_string(&go
, form
->action
);
1315 add_char_to_string(&go
, POST_CHAR
);
1316 if (form
->method
== FORM_METHOD_POST
) {
1317 add_to_string(&go
, "application/x-www-form-urlencoded\n");
1319 } else if (form
->method
== FORM_METHOD_POST_TEXT_PLAIN
) {
1320 /* Dunno about this one but we don't want the full
1321 * hextcat thingy. --jonas */
1322 add_to_string(&go
, "text/plain\n");
1323 add_to_string(&go
, data
.source
);
1327 add_to_string(&go
, "multipart/form-data; boundary=");
1328 add_bytes_to_string(&go
, boundary
.string
, BOUNDARY_LENGTH
);
1329 add_char_to_string(&go
, '\n');
1332 for (i
= 0; i
< data
.length
; i
++) {
1335 ulonghexcat(p
, NULL
, (int) data
.source
[i
], 2, '0', 0);
1336 add_to_string(&go
, p
);
1343 uri
= get_uri(go
.source
, 0);
1345 if (uri
) uri
->form
= 1;
1350 #undef BOUNDARY_LENGTH
1353 enum frame_event_status
1354 submit_form(struct session
*ses
, struct document_view
*doc_view
, int do_reload
)
1356 goto_current_link(ses
, doc_view
, do_reload
);
1357 return FRAME_EVENT_OK
;
1361 submit_given_form(struct session
*ses
, struct document_view
*doc_view
,
1362 struct form
*form
, int do_reload
)
1364 /* Added support for submitting forms in hidden
1365 * links in 1.285, commented code can safely be removed once we have made sure the new
1366 * code does the right thing. */
1369 struct document
*document
= doc_view
->document
;
1372 for (link
= 0; link
< document
->nlinks
; link
++) {
1373 struct form_control
*fc
= get_link_form_control(&document
->links
[link
]);
1375 if (fc
&& fc
->form
== form
) {
1376 doc_view
->vs
->current_link
= link
;
1377 submit_form(ses
, doc_view
, 0);
1382 if (!list_empty(form
->items
)) {
1383 struct form_control
*fc
= (struct form_control
*)form
->items
.next
;
1385 enum cache_mode mode
= do_reload
? CACHE_MODE_FORCE_RELOAD
: CACHE_MODE_NORMAL
;
1388 uri
= get_form_uri(ses
, doc_view
, fc
);
1390 goto_uri_frame(ses
, uri
, form
->target
, mode
);
1396 auto_submit_form(struct session
*ses
)
1398 struct document
*document
= ses
->doc_view
->document
;
1400 if (!list_empty(document
->forms
))
1401 submit_given_form(ses
, ses
->doc_view
, document
->forms
.next
, 0);
1407 set_file_form_state(struct terminal
*term
, void *filename_
, void *fs_
)
1409 unsigned char *filename
= filename_
;
1410 struct form_state
*fs
= fs_
;
1412 /* The menu code doesn't free the filename data */
1413 mem_free_set(&fs
->value
, filename
);
1414 fs
->state
= strlen(filename
);
1415 redraw_terminal(term
);
1420 file_form_menu(struct terminal
*term
, void *path_
, void *fs_
)
1422 unsigned char *path
= path_
;
1423 struct form_state
*fs
= fs_
;
1425 /* FIXME: It doesn't work for ../../ */
1427 int valuelen
= strlen(fs
->value
);
1428 int pathlen
= strlen(path
);
1429 int no_elevator
= 0;
1431 /* Don't add elevators for subdirs menus */
1432 /* It is not perfect at all because fs->value is not updated for each
1433 * newly opened file menu. Maybe it should be dropped. */
1434 for (; valuelen
< pathlen
; valuelen
++) {
1435 if (dir_sep(path
[valuelen
- 1])) {
1442 auto_complete_file(term
, 0 /* no_elevator */, path
,
1443 set_file_form_state
,
1444 file_form_menu
, fs
);
1448 enum frame_event_status
1449 field_op(struct session
*ses
, struct document_view
*doc_view
,
1450 struct link
*link
, struct term_event
*ev
)
1452 struct form_control
*fc
;
1453 struct form_state
*fs
;
1454 enum edit_action action_id
;
1455 unsigned char *text
;
1457 enum frame_event_status status
= FRAME_EVENT_REFRESH
;
1459 const unsigned char *ctext
;
1460 int utf8
= ses
->tab
->term
->utf8_cp
;
1461 #endif /* CONFIG_UTF8 */
1463 assert(ses
&& doc_view
&& link
&& ev
);
1464 if_assert_failed
return FRAME_EVENT_OK
;
1466 fc
= get_link_form_control(link
);
1467 assertm(fc
!= NULL
, "link has no form control");
1468 if_assert_failed
return FRAME_EVENT_OK
;
1470 if (fc
->mode
== FORM_MODE_DISABLED
|| ev
->ev
!= EVENT_KBD
1471 || ses
->insert_mode
== INSERT_MODE_OFF
)
1472 return FRAME_EVENT_IGNORED
;
1474 action_id
= kbd_action(KEYMAP_EDIT
, ev
, NULL
);
1476 fs
= find_form_state(doc_view
, fc
);
1477 if (!fs
|| !fs
->value
) return FRAME_EVENT_OK
;
1479 switch (action_id
) {
1482 if (fc
->type
== FC_TEXTAREA
) {
1483 status
= textarea_op_left(fs
, fc
, utf8
);
1487 unsigned char *new_value
;
1489 new_value
= utf8_prevchar(fs
->value
+ fs
->state
, 1, fs
->value
);
1490 fs
->state
= new_value
- fs
->value
;
1492 #endif /* CONFIG_UTF8 */
1493 fs
->state
= int_max(fs
->state
- 1, 0);
1495 case ACT_EDIT_RIGHT
:
1497 if (fc
->type
== FC_TEXTAREA
) {
1498 status
= textarea_op_right(fs
, fc
, utf8
);
1502 unsigned char *text
= fs
->value
+ fs
->state
;
1503 unsigned char *end
= strchr(text
, '\0');
1505 utf8_to_unicode(&text
, end
);
1506 fs
->state
= (int)(text
- fs
->value
);
1508 #endif /* CONFIG_UTF8 */
1509 fs
->state
= int_min(fs
->state
+ 1, strlen(fs
->value
));
1513 if (fc
->type
== FC_TEXTAREA
) {
1514 status
= textarea_op_home(fs
, fc
, utf8
);
1519 if (fc
->type
== FC_TEXTAREA
) {
1520 status
= textarea_op_home(fs
, fc
);
1525 #endif /* CONFIG_UTF8 */
1528 if (fc
->type
!= FC_TEXTAREA
)
1529 status
= FRAME_EVENT_IGNORED
;
1532 status
= textarea_op_up(fs
, fc
, utf8
);
1534 status
= textarea_op_up(fs
, fc
);
1535 #endif /* CONFIG_UTF8 */
1538 if (fc
->type
!= FC_TEXTAREA
)
1539 status
= FRAME_EVENT_IGNORED
;
1542 status
= textarea_op_down(fs
, fc
, utf8
);
1544 status
= textarea_op_down(fs
, fc
);
1545 #endif /* CONFIG_UTF8 */
1548 if (fc
->type
== FC_TEXTAREA
) {
1550 status
= textarea_op_end(fs
, fc
, utf8
);
1552 status
= textarea_op_end(fs
, fc
);
1553 #endif /* CONFIG_UTF8 */
1555 fs
->state
= strlen(fs
->value
);
1558 case ACT_EDIT_BEGINNING_OF_BUFFER
:
1559 if (fc
->type
== FC_TEXTAREA
) {
1561 status
= textarea_op_bob(fs
, fc
, utf8
);
1564 status
= textarea_op_bob(fs
, fc
);
1565 #endif /* CONFIG_UTF8 */
1570 case ACT_EDIT_END_OF_BUFFER
:
1571 if (fc
->type
== FC_TEXTAREA
) {
1573 status
= textarea_op_eob(fs
, fc
, utf8
);
1575 status
= textarea_op_eob(fs
, fc
);
1576 #endif /* CONFIG_UTF8 */
1578 fs
->state
= strlen(fs
->value
);
1581 case ACT_EDIT_OPEN_EXTERNAL
:
1582 if (form_field_is_readonly(fc
))
1583 status
= FRAME_EVENT_IGNORED
;
1584 else if (fc
->type
== FC_TEXTAREA
)
1585 textarea_edit(0, ses
->tab
->term
, fs
, doc_view
, link
);
1587 case ACT_EDIT_COPY_CLIPBOARD
:
1588 set_clipboard_text(fs
->value
);
1589 status
= FRAME_EVENT_OK
;
1591 case ACT_EDIT_CUT_CLIPBOARD
:
1592 set_clipboard_text(fs
->value
);
1593 if (!form_field_is_readonly(fc
))
1597 if (fc
->type
== FC_TEXTAREA
)
1599 #endif /* CONFIG_UTF8 */
1601 case ACT_EDIT_PASTE_CLIPBOARD
:
1602 if (form_field_is_readonly(fc
)) break;
1604 text
= get_clipboard_text();
1607 length
= strlen(text
);
1608 if (length
<= fc
->maxlength
) {
1609 unsigned char *v
= mem_realloc(fs
->value
, length
+ 1);
1613 memmove(v
, text
, length
+ 1);
1614 fs
->state
= strlen(fs
->value
);
1616 if (utf8
&& fc
->type
== FC_TEXTAREA
)
1618 #endif /* CONFIG_UTF8 */
1623 case ACT_EDIT_ENTER
:
1624 if (fc
->type
== FC_TEXTAREA
) {
1626 status
= textarea_op_enter(fs
, fc
, utf8
);
1628 status
= textarea_op_enter(fs
, fc
);
1629 #endif /* CONFIG_UTF8 */
1633 /* Set status to ok if either it is not possible to
1634 * submit the form or the posting fails. */
1635 /* FIXME: We should maybe have ACT_EDIT_ENTER_RELOAD */
1636 if ((has_form_submit(fc
->form
)
1637 && !get_opt_bool("document.browse.forms.auto_submit"))
1638 || goto_current_link(ses
, doc_view
, 0)) {
1639 if (ses
->insert_mode
== INSERT_MODE_ON
)
1640 ses
->insert_mode
= INSERT_MODE_OFF
;
1641 status
= FRAME_EVENT_OK
;
1644 case ACT_EDIT_BACKSPACE
:
1645 if (form_field_is_readonly(fc
)) {
1646 status
= FRAME_EVENT_IGNORED
;
1651 status
= FRAME_EVENT_OK
;
1656 int old_state
= fs
->state
;
1657 unsigned char *new_value
;
1659 new_value
= utf8_prevchar(fs
->value
+ fs
->state
, 1, fs
->value
);
1660 fs
->state
= new_value
- fs
->value
;
1662 if (old_state
!= fs
->state
) {
1663 if (fc
->type
== FC_TEXTAREA
)
1665 length
= strlen(fs
->value
+ old_state
) + 1;
1666 memmove(new_value
, fs
->value
+ old_state
, length
);
1669 #endif /* CONFIG_UTF8 */
1671 length
= strlen(fs
->value
+ fs
->state
) + 1;
1672 text
= fs
->value
+ fs
->state
;
1674 memmove(text
- 1, text
, length
);
1678 case ACT_EDIT_DELETE
:
1679 if (form_field_is_readonly(fc
)) {
1680 status
= FRAME_EVENT_IGNORED
;
1684 length
= strlen(fs
->value
);
1685 if (fs
->state
>= length
) {
1686 status
= FRAME_EVENT_OK
;
1691 unsigned char *end
= fs
->value
+ length
;
1692 unsigned char *text
= fs
->value
+ fs
->state
;
1693 unsigned char *old
= text
;
1695 utf8_to_unicode(&text
, end
);
1698 (int)(end
- text
) + 1);
1702 #endif /* CONFIG_UTF8 */
1703 text
= fs
->value
+ fs
->state
;
1705 memmove(text
, text
+ 1, length
- fs
->state
);
1707 case ACT_EDIT_KILL_TO_BOL
:
1708 if (form_field_is_readonly(fc
)) {
1709 status
= FRAME_EVENT_IGNORED
;
1713 if (fs
->state
<= 0) {
1714 status
= FRAME_EVENT_OK
;
1718 text
= memrchr(fs
->value
, ASCII_LF
, fs
->state
);
1720 /* Leave the new-line character if it does not
1721 * immediately precede the cursor. */
1722 if (text
!= &fs
->value
[fs
->state
- 1])
1728 length
= strlen(fs
->value
+ fs
->state
) + 1;
1729 memmove(text
, fs
->value
+ fs
->state
, length
);
1731 fs
->state
= (int) (text
- fs
->value
);
1734 if (fc
->type
== FC_TEXTAREA
)
1737 #endif /* CONFIG_UTF8 */
1739 case ACT_EDIT_KILL_TO_EOL
:
1740 if (form_field_is_readonly(fc
)) {
1741 status
= FRAME_EVENT_IGNORED
;
1745 if (!fs
->value
[fs
->state
]) {
1746 status
= FRAME_EVENT_OK
;
1750 text
= strchr(fs
->value
+ fs
->state
, ASCII_LF
);
1752 fs
->value
[fs
->state
] = '\0';
1756 if (fs
->value
[fs
->state
] == ASCII_LF
)
1759 memmove(fs
->value
+ fs
->state
, text
, strlen(text
) + 1);
1762 case ACT_EDIT_KILL_WORD_BACK
:
1763 if (form_field_is_readonly(fc
)) {
1764 status
= FRAME_EVENT_IGNORED
;
1768 if (fs
->state
<= 0) {
1769 status
= FRAME_EVENT_OK
;
1773 text
= &fs
->value
[fs
->state
];
1774 while (text
> fs
->value
&& isspace(*(text
- 1)))
1776 while (text
> fs
->value
&& !isspace(*(text
- 1)))
1778 if (*text
== ASCII_LF
1779 && text
!= &fs
->value
[fs
->state
- 1])
1782 length
= strlen(fs
->value
+ fs
->state
) + 1;
1783 memmove(text
, fs
->value
+ fs
->state
, length
);
1785 fs
->state
= (int) (text
- fs
->value
);
1788 case ACT_EDIT_MOVE_BACKWARD_WORD
:
1789 while (fs
->state
> 0
1790 && isspace(fs
->value
[fs
->state
- 1]))
1792 while (fs
->state
> 0
1793 && !isspace(fs
->value
[fs
->state
- 1]))
1797 case ACT_EDIT_MOVE_FORWARD_WORD
:
1798 while (isspace(fs
->value
[fs
->state
]))
1800 while (fs
->value
[fs
->state
]
1801 && !isspace(fs
->value
[fs
->state
]))
1803 while (isspace(fs
->value
[fs
->state
]))
1807 case ACT_EDIT_AUTO_COMPLETE
:
1808 if (fc
->type
!= FC_FILE
1809 || form_field_is_readonly(fc
)) {
1810 status
= FRAME_EVENT_IGNORED
;
1814 file_form_menu(ses
->tab
->term
, fs
->value
, fs
);
1817 case ACT_EDIT_CANCEL
:
1818 if (ses
->insert_mode
== INSERT_MODE_ON
)
1819 ses
->insert_mode
= INSERT_MODE_OFF
;
1821 status
= FRAME_EVENT_IGNORED
;
1824 case ACT_EDIT_REDRAW
:
1825 redraw_terminal_cls(ses
->tab
->term
);
1826 status
= FRAME_EVENT_OK
;
1830 if (!check_kbd_textinput_key(ev
)) {
1831 status
= FRAME_EVENT_IGNORED
;
1835 if (form_field_is_readonly(fc
)
1837 || strlen(fs
->value
) >= fc
->maxlength
1838 || !insert_in_string(&fs
->value
, fs
->state
, "?", 1)
1839 #endif /* CONFIG_UTF8 */
1842 status
= FRAME_EVENT_OK
;
1847 /* fs->value is in the charset of the terminal. */
1848 ctext
= u2cp_no_nbsp(get_kbd_key(ev
),
1849 get_terminal_codepage(ses
->tab
->term
));
1850 length
= strlen(ctext
);
1852 if (strlen(fs
->value
) + length
> fc
->maxlength
1853 || !insert_in_string(&fs
->value
, fs
->state
, ctext
, length
)) {
1854 status
= FRAME_EVENT_OK
;
1858 fs
->state
+= length
;
1859 if (fc
->type
== FC_TEXTAREA
)
1862 fs
->value
[fs
->state
++] = get_kbd_key(ev
);
1863 #endif /* CONFIG_UTF8 */
1870 static unsigned char *
1871 get_form_label(struct form_control
*fc
)
1876 return N_("Reset form");
1878 return N_("Harmless button");
1883 if (!fc
->form
->action
) return NULL
;
1885 if (fc
->form
->method
== FORM_METHOD_GET
)
1886 return N_("Submit form to");
1887 return N_("Post form to");
1889 return N_("Radio button");
1891 return N_("Checkbox");
1893 return N_("Select field");
1895 return N_("Text field");
1897 return N_("Text area");
1899 return N_("File upload");
1901 return N_("Password field");
1908 add_form_attr_to_string(struct string
*string
, struct terminal
*term
,
1909 unsigned char *name
, unsigned char *value
)
1911 add_to_string(string
, ", ");
1912 add_to_string(string
, _(name
, term
));
1914 add_char_to_string(string
, ' ');
1915 add_to_string(string
, value
);
1920 get_form_info(struct session
*ses
, struct document_view
*doc_view
)
1922 struct terminal
*term
= ses
->tab
->term
;
1923 struct link
*link
= get_current_link(doc_view
);
1924 struct form_control
*fc
;
1925 unsigned char *label
, *key
;
1930 fc
= get_link_form_control(link
);
1931 label
= get_form_label(fc
);
1932 if (!label
) return NULL
;
1934 if (!init_string(&str
)) return NULL
;
1936 add_to_string(&str
, _(label
, term
));
1938 if (link
->type
!= LINK_BUTTON
&& fc
->name
&& fc
->name
[0]) {
1939 add_form_attr_to_string(&str
, term
, N_("name"), fc
->name
);
1946 struct form_state
*fs
= find_form_state(doc_view
, fc
);
1948 if (!fs
->value
|| !fs
->value
[0])
1951 add_form_attr_to_string(&str
, term
, N_("value"), fs
->value
);
1961 unsigned char *uristring
;
1963 if (form_field_is_readonly(fc
)) {
1964 add_form_attr_to_string(&str
, term
, N_("read only"), NULL
);
1967 /* Should we add info about entering insert mode or add info
1968 * about submitting the form? */
1969 if (ses
->insert_mode
== INSERT_MODE_OFF
) {
1970 key
= get_keystroke(ACT_EDIT_ENTER
, KEYMAP_EDIT
);
1974 if (form_field_is_readonly(fc
))
1975 label
= N_("press %s to navigate");
1977 label
= N_("press %s to edit");
1979 add_to_string(&str
, " (");
1980 add_format_to_string(&str
, _(label
, term
), key
);
1981 add_char_to_string(&str
, ')');
1987 if (fc
->type
== FC_TEXTAREA
)
1992 if (!fc
->form
->action
1993 || (has_form_submit(fc
->form
)
1994 && !get_opt_bool("document.browse.forms.auto_submit")))
1997 uri
= get_uri(fc
->form
->action
, 0);
2000 /* Add the uri with password and post info stripped */
2001 uristring
= get_uri_string(uri
, URI_PUBLIC
);
2004 if (!uristring
) break;
2006 key
= get_keystroke(ACT_EDIT_ENTER
, KEYMAP_EDIT
);
2008 mem_free(uristring
);
2012 if (fc
->form
->method
== FORM_METHOD_GET
)
2013 label
= N_("press %s to submit to %s");
2015 label
= N_("press %s to post to %s");
2017 add_to_string(&str
, " (");
2018 add_format_to_string(&str
, _(label
, term
), key
, uristring
);
2019 mem_free(uristring
);
2022 add_char_to_string(&str
, ')');
2027 add_char_to_string(&str
, ' ');
2030 /* Add the uri with password and post info stripped */
2031 add_string_uri_to_string(&str
, fc
->form
->action
, URI_PUBLIC
);
2042 && get_opt_bool("document.browse.accesskey.display")) {
2043 add_to_string(&str
, " (");
2044 add_accesskey_to_string(&str
, link
->accesskey
);
2045 add_char_to_string(&str
, ')');
2052 link_form_menu_func(struct terminal
*term
, void *link_number_
, void *ses_
)
2054 struct session
*ses
= ses_
;
2055 struct document_view
*doc_view
;
2056 int link_number
= *(int *) link_number_
;
2058 mem_free(link_number_
);
2060 assert(term
&& ses
);
2061 if_assert_failed
return;
2063 doc_view
= current_frame(ses
);
2064 if (!doc_view
) return;
2066 assert(doc_view
->vs
&& doc_view
->document
);
2067 if_assert_failed
return;
2069 jump_to_link_number(ses
, doc_view
, link_number
);
2070 refresh_view(ses
, doc_view
, 0);
2074 link_form_menu(struct session
*ses
)
2076 struct document_view
*doc_view
;
2078 struct menu_item
*mi
;
2079 struct form_control
*fc
;
2083 if_assert_failed
return;
2085 doc_view
= current_frame(ses
);
2086 if (!doc_view
) return;
2088 assert(doc_view
->vs
&& doc_view
->document
);
2089 if_assert_failed
return;
2091 link
= get_current_link(doc_view
);
2094 assert(link_is_form(link
));
2096 fc
= get_link_form_control(link
);
2101 mi
= new_menu(FREE_LIST
| FREE_TEXT
| NO_INTL
);
2104 foreach (fc
, form
->items
) {
2105 unsigned char *text
;
2106 unsigned char *rtext
;
2117 text
= N_("Useless button");
2119 text
= N_("Submit button");
2123 text
= get_form_label(fc
);
2126 link_number
= get_form_control_link(doc_view
->document
, fc
);
2128 || !init_string(&str
))
2132 add_to_string(&str
, _(text
, ses
->tab
->term
));
2135 if (!rtext
) rtext
= fc
->alt
;
2137 add_to_menu(&mi
, str
.source
, rtext
, ACT_MAIN_NONE
,
2138 link_form_menu_func
, intdup(link_number
),
2142 do_menu(ses
->tab
->term
, mi
, ses
, 1);