1 /* HTML forms parser */
14 #include "bfu/listmenu.h"
16 #include "document/html/parser/forms.h"
17 #include "document/html/parser/link.h"
18 #include "document/html/parser/stack.h"
19 #include "document/html/parser/parse.h"
20 #include "document/html/parser.h"
21 #include "document/html/renderer.h"
22 #include "document/forms.h"
23 #include "intl/charsets.h"
24 #include "protocol/protocol.h"
25 #include "protocol/uri.h"
26 #include "util/conv.h"
27 #include "util/error.h"
28 #include "util/memdebug.h"
29 #include "util/memlist.h"
30 #include "util/memory.h"
31 #include "util/string.h"
34 #include "document/html/internal.h"
39 html_form(struct html_context
*html_context
, unsigned char *a
,
40 unsigned char *xxx3
, unsigned char *xxx4
, unsigned char **xxx5
)
45 html_context
->was_br
= 1;
50 form
->method
= FORM_METHOD_GET
;
51 form
->form_num
= a
- html_context
->startf
;
53 al
= get_attr_val(a
, "method", html_context
->options
);
55 if (!strcasecmp(al
, "post")) {
56 unsigned char *enctype
;
58 enctype
= get_attr_val(a
, "enctype",
59 html_context
->options
);
61 form
->method
= FORM_METHOD_POST
;
63 if (!strcasecmp(enctype
, "multipart/form-data"))
64 form
->method
= FORM_METHOD_POST_MP
;
65 else if (!strcasecmp(enctype
, "text/plain"))
66 form
->method
= FORM_METHOD_POST_TEXT_PLAIN
;
72 form
->onsubmit
= get_attr_val(a
, "onsubmit", html_context
->options
);
73 al
= get_attr_val(a
, "name", html_context
->options
);
74 if (al
) form
->name
= al
;
76 al
= get_attr_val(a
, "action", html_context
->options
);
77 /* The HTML specification at
78 * http://www.w3.org/TR/REC-html40/interact/forms.html#h-17.3 states
79 * that the behavior of an empty action attribute should be undefined.
80 * Mozilla handles action="" as action="<current-URI>" which seems
81 * reasonable. (bug 615) */
83 form
->action
= join_urls(html_context
->base_href
, trim_chars(al
, ' ', NULL
));
87 enum uri_component components
= URI_ORIGINAL
;
91 /* We have to do following for GET method, because we would end
92 * up with two '?' otherwise. */
93 if (form
->method
== FORM_METHOD_GET
)
94 components
= URI_FORM_GET
;
96 form
->action
= get_uri_string(html_context
->base_href
, components
);
98 /* No action URI should contain post data */
99 assert(!form
->action
|| !strchr(form
->action
, POST_CHAR
));
101 /* GET method URIs should not have '?'. */
103 || form
->method
!= FORM_METHOD_GET
104 || !strchr(form
->action
, '?'));
107 al
= get_target(html_context
->options
, a
);
108 form
->target
= al
? al
: stracpy(html_context
->base_target
);
110 html_context
->special_f(html_context
, SP_FORM
, form
);
115 get_form_mode(struct html_context
*html_context
, unsigned char *attr
)
117 if (has_attr(attr
, "disabled", html_context
->options
))
118 return FORM_MODE_DISABLED
;
120 if (has_attr(attr
, "readonly", html_context
->options
))
121 return FORM_MODE_READONLY
;
123 return FORM_MODE_NORMAL
;
126 static struct form_control
*
127 init_form_control(enum form_type type
, unsigned char *attr
,
128 struct html_context
*html_context
)
130 struct form_control
*fc
;
132 fc
= mem_calloc(1, sizeof(*fc
));
133 if (!fc
) return NULL
;
136 fc
->position
= attr
- html_context
->startf
;
137 fc
->mode
= get_form_mode(html_context
, attr
);
143 html_button(struct html_context
*html_context
, unsigned char *a
,
144 unsigned char *xxx3
, unsigned char *xxx4
, unsigned char **xxx5
)
147 struct form_control
*fc
;
148 enum form_type type
= FC_SUBMIT
;
150 html_focusable(html_context
, a
);
152 al
= get_attr_val(a
, "type", html_context
->options
);
153 if (!al
) goto no_type_attr
;
155 if (!strcasecmp(al
, "button")) {
157 } else if (!strcasecmp(al
, "reset")) {
159 } else if (strcasecmp(al
, "submit")) {
167 fc
= init_form_control(type
, a
, html_context
);
170 fc
->name
= get_attr_val(a
, "name", html_context
->options
);
171 fc
->default_value
= get_attr_val(a
, "value", html_context
->options
);
172 if (!fc
->default_value
) {
173 if (fc
->type
== FC_SUBMIT
)
174 fc
->default_value
= stracpy("Submit");
175 else if (fc
->type
== FC_RESET
)
176 fc
->default_value
= stracpy("Reset");
177 else if (fc
->type
== FC_BUTTON
)
178 fc
->default_value
= stracpy("Button");
180 if (!fc
->default_value
)
181 fc
->default_value
= stracpy("");
183 html_context
->special_f(html_context
, SP_CONTROL
, fc
);
185 format
.style
.attr
|= AT_BOLD
;
189 html_input_format(struct html_context
*html_context
, unsigned char *a
,
190 struct form_control
*fc
)
192 put_chrs(html_context
, " ", 1);
193 html_stack_dup(html_context
, ELEMENT_KILLABLE
);
194 html_focusable(html_context
, a
);
196 if (format
.title
) mem_free(format
.title
);
197 format
.title
= get_attr_val(a
, "title", html_context
->options
);
205 format
.style
.attr
|= AT_BOLD
;
206 for (i
= 0; i
< fc
->size
; i
++)
207 put_chrs(html_context
, "_", 1);
211 format
.style
.attr
|= AT_BOLD
;
212 put_chrs(html_context
, "[ ]", 8);
215 format
.style
.attr
|= AT_BOLD
;
216 put_chrs(html_context
, "( )", 8);
222 mem_free_set(&format
.image
, NULL
);
223 al
= get_url_val(a
, "src", html_context
->options
);
225 al
= get_url_val(a
, "dynsrc",
226 html_context
->options
);
228 format
.image
= join_urls(html_context
->base_href
, al
);
231 format
.style
.attr
|= AT_BOLD
;
232 put_chrs(html_context
, "[ ", 7);
234 put_chrs(html_context
, fc
->alt
, strlen(fc
->alt
));
236 put_chrs(html_context
, fc
->name
, strlen(fc
->name
));
238 put_chrs(html_context
, "Submit", 6);
240 put_chrs(html_context
, " ]", 7);
246 format
.style
.attr
|= AT_BOLD
;
247 put_chrs(html_context
, "[ ", 7);
248 if (fc
->default_value
)
249 put_chrs(html_context
, fc
->default_value
, strlen(fc
->default_value
));
250 put_chrs(html_context
, " ]", 7);
255 INTERNAL("bad control type");
257 pop_html_element(html_context
);
258 put_chrs(html_context
, " ", 1);
262 html_input(struct html_context
*html_context
, unsigned char *a
,
263 unsigned char *xxx3
, unsigned char *xxx4
, unsigned char **xxx5
)
266 struct form_control
*fc
;
268 fc
= init_form_control(FC_TEXT
, a
, html_context
);
271 al
= get_attr_val(a
, "type", html_context
->options
);
273 if (!strcasecmp(al
, "text")) fc
->type
= FC_TEXT
;
274 else if (!strcasecmp(al
, "hidden")) fc
->type
= FC_HIDDEN
;
275 else if (!strcasecmp(al
, "button")) fc
->type
= FC_BUTTON
;
276 else if (!strcasecmp(al
, "checkbox")) fc
->type
= FC_CHECKBOX
;
277 else if (!strcasecmp(al
, "radio")) fc
->type
= FC_RADIO
;
278 else if (!strcasecmp(al
, "password")) fc
->type
= FC_PASSWORD
;
279 else if (!strcasecmp(al
, "submit")) fc
->type
= FC_SUBMIT
;
280 else if (!strcasecmp(al
, "reset")) fc
->type
= FC_RESET
;
281 else if (!strcasecmp(al
, "file")) fc
->type
= FC_FILE
;
282 else if (!strcasecmp(al
, "image")) fc
->type
= FC_IMAGE
;
283 /* else unknown type, let it default to FC_TEXT. */
287 if (fc
->type
!= FC_FILE
)
288 fc
->default_value
= get_attr_val(a
, "value",
289 html_context
->options
);
290 if (!fc
->default_value
) {
291 if (fc
->type
== FC_CHECKBOX
)
292 fc
->default_value
= stracpy("on");
293 else if (fc
->type
== FC_SUBMIT
)
294 fc
->default_value
= stracpy("Submit");
295 else if (fc
->type
== FC_RESET
)
296 fc
->default_value
= stracpy("Reset");
297 else if (fc
->type
== FC_BUTTON
)
298 fc
->default_value
= stracpy("Button");
300 if (!fc
->default_value
)
301 fc
->default_value
= stracpy("");
303 fc
->name
= get_attr_val(a
, "name", html_context
->options
);
305 fc
->size
= get_num(a
, "size", html_context
->options
);
307 fc
->size
= html_context
->options
->default_form_input_size
;
309 if (fc
->size
> html_context
->options
->box
.width
)
310 fc
->size
= html_context
->options
->box
.width
;
311 fc
->maxlength
= get_num(a
, "maxlength", html_context
->options
);
312 if (fc
->maxlength
== -1) fc
->maxlength
= INT_MAX
;
313 if (fc
->type
== FC_CHECKBOX
|| fc
->type
== FC_RADIO
)
314 fc
->default_state
= has_attr(a
, "checked",
315 html_context
->options
);
316 if (fc
->type
== FC_IMAGE
)
317 fc
->alt
= get_attr_val(a
, "alt", html_context
->options
);
319 if (fc
->type
!= FC_HIDDEN
) {
320 html_input_format(html_context
, a
, fc
);
323 html_context
->special_f(html_context
, SP_CONTROL
, fc
);
326 static struct list_menu lnk_menu
;
329 do_html_select(unsigned char *attr
, unsigned char *html
,
330 unsigned char *eof
, unsigned char **end
,
331 struct html_context
*html_context
)
333 struct conv_table
*ct
= html_context
->special_f(html_context
, SP_TABLE
, NULL
);
334 struct form_control
*fc
;
335 struct string lbl
= NULL_STRING
, orig_lbl
= NULL_STRING
;
336 unsigned char **values
= NULL
;
337 unsigned char **labels
;
338 unsigned char *name
, *t_attr
, *en
;
347 html_focusable(html_context
, attr
);
348 init_menu(&lnk_menu
);
355 while (html
< eof
&& *html
!= '<') html
++;
361 if (lbl
.source
) done_string(&lbl
);
362 if (orig_lbl
.source
) done_string(&orig_lbl
);
366 for (j
= 0; j
< order
; j
++)
367 mem_free_if(values
[j
]);
370 destroy_menu(&lnk_menu
);
376 unsigned char *q
, *s
= en
;
379 while (l
&& isspace(s
[0])) s
++, l
--;
380 while (l
&& isspace(s
[l
-1])) l
--;
381 q
= convert_string(ct
, s
, l
,
382 html_context
->options
->cp
,
383 CSM_DEFAULT
, NULL
, NULL
, NULL
);
384 if (q
) add_to_string(&lbl
, q
), mem_free(q
);
385 add_bytes_to_string(&orig_lbl
, s
, l
);
388 if (html
+ 2 <= eof
&& (html
[1] == '!' || html
[1] == '?')) {
389 html
= skip_comment(html
, eof
);
393 if (parse_element(html
, eof
, &name
, &namelen
, &t_attr
, &en
)) {
398 if (!namelen
) goto see
;
400 if (name
[0] == '/') {
402 if (!namelen
) goto see
;
409 if (closing_tag
&& !strlcasecmp(name
, namelen
, "SELECT", 6)) {
410 add_select_item(&lnk_menu
, &lbl
, &orig_lbl
, values
, order
, nnmi
);
414 if (!strlcasecmp(name
, namelen
, "OPTION", 6)) {
415 add_select_item(&lnk_menu
, &lbl
, &orig_lbl
, values
, order
, nnmi
);
418 unsigned char *value
, *label
;
420 if (has_attr(t_attr
, "disabled", html_context
->options
))
423 && has_attr(t_attr
, "selected", html_context
->options
))
425 value
= get_attr_val(t_attr
, "value", html_context
->options
);
427 if (!mem_align_alloc(&values
, order
, order
+ 1, unsigned char *, 0xFF))
430 values
[order
++] = value
;
431 label
= get_attr_val(t_attr
, "label", html_context
->options
);
432 if (label
) new_menu_item(&lnk_menu
, label
, order
- 1, 0);
433 if (!value
|| !label
) {
435 init_string(&orig_lbl
);
443 if (!strlcasecmp(name
, namelen
, "OPTGROUP", 8)) {
444 add_select_item(&lnk_menu
, &lbl
, &orig_lbl
, values
, order
, nnmi
);
446 if (group
) new_menu_item(&lnk_menu
, NULL
, -1, 0), group
= 0;
449 unsigned char *label
;
451 label
= get_attr_val(t_attr
, "label", html_context
->options
);
455 if (!label
) goto see
;
457 new_menu_item(&lnk_menu
, label
, -1, 0);
467 if (!order
) goto abort
;
469 labels
= mem_calloc(order
, sizeof(unsigned char *));
470 if (!labels
) goto abort
;
472 fc
= init_form_control(FC_SELECT
, attr
, html_context
);
478 fc
->name
= get_attr_val(attr
, "name", html_context
->options
);
479 fc
->default_state
= preselect
< 0 ? 0 : preselect
;
480 fc
->default_value
= order
? stracpy(values
[fc
->default_state
]) : stracpy("");
483 fc
->menu
= detach_menu(&lnk_menu
);
486 menu_labels(fc
->menu
, "", labels
);
487 put_chrs(html_context
, "[", 1);
488 html_stack_dup(html_context
, ELEMENT_KILLABLE
);
490 format
.style
.attr
|= AT_BOLD
;
493 for (i
= 0; i
< order
; i
++) {
494 if (!labels
[i
]) continue;
495 int_lower_bound(&max_width
, strlen(labels
[i
]));
498 for (i
= 0; i
< max_width
; i
++)
499 put_chrs(html_context
, "_", 1);
501 pop_html_element(html_context
);
502 put_chrs(html_context
, "]", 1);
503 html_context
->special_f(html_context
, SP_CONTROL
, fc
);
508 do_html_select_multiple(struct html_context
*html_context
, unsigned char *a
,
509 unsigned char *html
, unsigned char *eof
,
512 unsigned char *al
= get_attr_val(a
, "name", html_context
->options
);
515 html_focusable(html_context
, a
);
516 html_top
->type
= ELEMENT_DONT_KILL
;
517 mem_free_set(&format
.select
, al
);
518 format
.select_disabled
= has_attr(a
, "disabled", html_context
->options
)
524 html_select(struct html_context
*html_context
, unsigned char *a
,
525 unsigned char *html
, unsigned char *eof
, unsigned char **end
)
527 if (has_attr(a
, "multiple", html_context
->options
))
528 do_html_select_multiple(html_context
, a
, html
, eof
, end
);
530 do_html_select(a
, html
, eof
, end
, html_context
);
535 html_option(struct html_context
*html_context
, unsigned char *a
,
536 unsigned char *xxx3
, unsigned char *xxx4
, unsigned char **xxx5
)
538 struct form_control
*fc
;
541 if (!format
.select
) return;
543 val
= get_attr_val(a
, "value", html_context
->options
);
546 unsigned char *p
, *r
;
550 for (p
= a
- 1; *p
!= '<'; p
--);
552 if (!init_string(&str
)) goto end_parse
;
553 if (parse_element(p
, html_context
->eoff
, NULL
, NULL
, NULL
, &p
)) {
554 INTERNAL("parse element failed");
560 while (p
< html_context
->eoff
&& isspace(*p
)) p
++;
561 while (p
< html_context
->eoff
&& !isspace(*p
) && *p
!= '<') {
564 add_char_to_string(&str
, *p
? *p
: ' '), p
++;
568 val
= str
.source
; /* Has to be before the possible 'goto end_parse' */
570 while (r
< html_context
->eoff
&& isspace(*r
)) r
++;
571 if (r
>= html_context
->eoff
) goto end_parse
;
572 if (r
- 2 <= html_context
->eoff
&& (r
[1] == '!' || r
[1] == '?')) {
573 p
= skip_comment(r
, html_context
->eoff
);
576 if (parse_element(r
, html_context
->eoff
, &name
, &namelen
, NULL
, &p
)) goto sp
;
578 if (namelen
< 6) goto se
;
579 if (name
[0] == '/') name
++, namelen
--;
581 if (strlcasecmp(name
, namelen
, "OPTION", 6)
582 && strlcasecmp(name
, namelen
, "SELECT", 6)
583 && strlcasecmp(name
, namelen
, "OPTGROUP", 8))
588 fc
= init_form_control(FC_CHECKBOX
, a
, html_context
);
594 fc
->name
= null_or_stracpy(format
.select
);
595 fc
->default_value
= val
;
596 fc
->default_state
= has_attr(a
, "selected", html_context
->options
);
597 fc
->mode
= has_attr(a
, "disabled", html_context
->options
)
599 : format
.select_disabled
;
601 put_chrs(html_context
, " ", 1);
602 html_stack_dup(html_context
, ELEMENT_KILLABLE
);
604 format
.style
.attr
|= AT_BOLD
;
605 put_chrs(html_context
, "[ ]", 3);
606 pop_html_element(html_context
);
607 put_chrs(html_context
, " ", 1);
608 html_context
->special_f(html_context
, SP_CONTROL
, fc
);
612 html_textarea(struct html_context
*html_context
, unsigned char *attr
,
613 unsigned char *html
, unsigned char *eof
, unsigned char **end
)
615 struct form_control
*fc
;
616 unsigned char *p
, *t_name
, *wrap_attr
;
621 html_focusable(html_context
, attr
);
622 while (html
< eof
&& (*html
== '\n' || *html
== '\r')) html
++;
624 while (p
< eof
&& *p
!= '<') {
633 if (parse_element(p
, eof
, &t_name
, &t_namelen
, NULL
, end
)) goto pp
;
634 if (strlcasecmp(t_name
, t_namelen
, "/TEXTAREA", 9)) goto pp
;
636 fc
= init_form_control(FC_TEXTAREA
, attr
, html_context
);
639 fc
->name
= get_attr_val(attr
, "name", html_context
->options
);
640 fc
->default_value
= memacpy(html
, p
- html
);
641 for (p
= fc
->default_value
; p
&& p
[0]; p
++) {
642 /* FIXME: We don't cope well with entities here. Bugzilla uses
643 * inside of textarea and we fail miserably upon that
647 || (p
> fc
->default_value
&& p
[-1] == '\n')) {
648 memcpy(p
, p
+ 1, strlen(p
));
656 cols
= get_num(attr
, "cols", html_context
->options
);
658 cols
= html_context
->options
->default_form_input_size
;
659 cols
++; /* Add 1 column, other browsers may have different
660 behavior here (mozilla adds 2) --Zas */
661 if (cols
> html_context
->options
->box
.width
)
662 cols
= html_context
->options
->box
.width
;
665 rows
= get_num(attr
, "rows", html_context
->options
);
666 if (rows
<= 0) rows
= 1;
667 if (rows
> html_context
->options
->box
.height
)
668 rows
= html_context
->options
->box
.height
;
670 html_context
->options
->needs_height
= 1;
672 wrap_attr
= get_attr_val(attr
, "wrap", html_context
->options
);
674 if (!strcasecmp(wrap_attr
, "hard")
675 || !strcasecmp(wrap_attr
, "physical")) {
676 fc
->wrap
= FORM_WRAP_HARD
;
677 } else if (!strcasecmp(wrap_attr
, "soft")
678 || !strcasecmp(wrap_attr
, "virtual")) {
679 fc
->wrap
= FORM_WRAP_SOFT
;
680 } else if (!strcasecmp(wrap_attr
, "none")
681 || !strcasecmp(wrap_attr
, "off")) {
682 fc
->wrap
= FORM_WRAP_NONE
;
686 } else if (has_attr(attr
, "nowrap", html_context
->options
)) {
687 fc
->wrap
= FORM_WRAP_NONE
;
690 fc
->wrap
= FORM_WRAP_SOFT
;
693 fc
->maxlength
= get_num(attr
, "maxlength", html_context
->options
);
694 if (fc
->maxlength
== -1) fc
->maxlength
= INT_MAX
;
696 if (rows
> 1) ln_break(html_context
, 1);
697 else put_chrs(html_context
, " ", 1);
699 html_stack_dup(html_context
, ELEMENT_KILLABLE
);
701 format
.style
.attr
|= AT_BOLD
;
703 for (i
= 0; i
< rows
; i
++) {
706 for (j
= 0; j
< cols
; j
++)
707 put_chrs(html_context
, "_", 1);
709 ln_break(html_context
, 1);
712 pop_html_element(html_context
);
714 ln_break(html_context
, 1);
716 put_chrs(html_context
, " ", 1);
717 html_context
->special_f(html_context
, SP_CONTROL
, fc
);