1 /* $Id: form.c,v 1.34 2004/02/05 17:23:07 ukai Exp $ */
12 extern Str *textarea_str;
14 extern FormSelectOption *select_option;
16 #endif /* MENU_SELECT */
21 void (*rout)(struct parsed_tagarg *);
22 } internal_action[] = {
24 {"option", panel_set_option},
26 {"cookie", set_cookie_flag},
27 #endif /* USE_COOKIE */
28 {"download", download_action},
30 { "charset", change_charset },
38 newFormList(char *action, char *method, char *charset, char *enctype,
39 char *target, char *name, struct form_list *_next)
42 Str a = Strnew_charp(action);
43 int m = FORM_METHOD_GET;
44 int e = FORM_ENCTYPE_URLENCODED;
49 if (method == NULL || !strcasecmp(method, "get"))
51 else if (!strcasecmp(method, "post"))
53 else if (!strcasecmp(method, "internal"))
54 m = FORM_METHOD_INTERNAL;
55 /* unknown method is regarded as 'get' */
57 if (enctype != NULL && !strcasecmp(enctype, "multipart/form-data")) {
58 e = FORM_ENCTYPE_MULTIPART;
59 if (m == FORM_METHOD_GET)
65 c = wc_guess_charset(charset, 0);
68 l = New(struct form_list);
69 l->item = l->lastitem = NULL;
86 * add <input> element to form_list
88 struct form_item_list *
89 formList_addInput(struct form_list *fl, struct parsed_tag *tag)
91 struct form_item_list *item;
95 /* if not in <form>..</form> environment, just ignore <input> tag */
99 item = New(struct form_item_list);
100 item->type = FORM_UNKNOWN;
103 item->checked = item->init_checked = 0;
106 item->value = item->init_value = NULL;
108 if (parsedtag_get_value(tag, ATTR_TYPE, &p)) {
109 item->type = formtype(p);
110 if (item->size < 0 &&
111 (item->type == FORM_INPUT_TEXT ||
112 item->type == FORM_INPUT_FILE ||
113 item->type == FORM_INPUT_PASSWORD))
114 item->size = FORM_I_TEXT_DEFAULT_SIZE;
116 if (parsedtag_get_value(tag, ATTR_NAME, &p))
117 item->name = Strnew_charp(p);
118 if (parsedtag_get_value(tag, ATTR_VALUE, &p))
119 item->value = item->init_value = Strnew_charp(p);
120 item->checked = item->init_checked = parsedtag_exists(tag, ATTR_CHECKED);
121 item->accept = parsedtag_exists(tag, ATTR_ACCEPT);
122 parsedtag_get_value(tag, ATTR_SIZE, &item->size);
123 parsedtag_get_value(tag, ATTR_MAXLENGTH, &item->maxlength);
124 item->readonly = parsedtag_exists(tag, ATTR_READONLY);
125 if (parsedtag_get_value(tag, ATTR_TEXTAREANUMBER, &i))
126 item->value = item->init_value = textarea_str[i];
128 if (parsedtag_get_value(tag, ATTR_SELECTNUMBER, &i))
129 item->select_option = select_option[i].first;
130 #endif /* MENU_SELECT */
131 if (parsedtag_get_value(tag, ATTR_ROWS, &p))
132 item->rows = atoi(p);
133 if (item->type == FORM_UNKNOWN) {
134 /* type attribute is missing. Ignore the tag. */
138 if (item->type == FORM_SELECT) {
139 chooseSelectOption(item, item->select_option);
140 item->init_selected = item->selected;
141 item->init_value = item->value;
142 item->init_label = item->label;
144 #endif /* MENU_SELECT */
145 if (item->type == FORM_INPUT_FILE && item->value && item->value->length) {
146 /* security hole ! */
151 if (fl->item == NULL) {
152 fl->item = fl->lastitem = item;
155 fl->lastitem->next = item;
158 if (item->type == FORM_INPUT_HIDDEN)
164 static char *_formtypetbl[] = {
165 "text", "password", "checkbox", "radio", "submit", "reset", "hidden",
166 "image", "select", "textarea", "button", "file", NULL
169 static char *_formmethodtbl[] = {
170 "GET", "POST", "INTERNAL", "HEAD"
174 form2str(FormItemList *fi)
178 if (fi->type != FORM_SELECT && fi->type != FORM_TEXTAREA)
179 Strcat_charp(tmp, "input type=");
180 Strcat_charp(tmp, _formtypetbl[fi->type]);
181 if (fi->name && fi->name->length)
182 Strcat_m_charp(tmp, " name=\"", fi->name->ptr, "\"", NULL);
183 if ((fi->type == FORM_INPUT_RADIO || fi->type == FORM_INPUT_CHECKBOX ||
184 fi->type == FORM_SELECT) && fi->value)
185 Strcat_m_charp(tmp, " value=\"", fi->value->ptr, "\"", NULL);
186 Strcat_m_charp(tmp, " (", _formmethodtbl[fi->parent->method], " ",
187 fi->parent->action->ptr, ")", NULL);
192 formtype(char *typestr)
195 for (i = 0; _formtypetbl[i]; i++) {
196 if (!strcasecmp(typestr, _formtypetbl[i]))
203 formRecheckRadio(Anchor *a, Buffer *buf, FormItemList *fi)
209 for (i = 0; i < buf->formitem->nanchor; i++) {
210 a2 = &buf->formitem->anchors[i];
211 f2 = (FormItemList *)a2->url;
212 if (f2->parent == fi->parent && f2 != fi &&
213 f2->type == FORM_INPUT_RADIO && Strcmp(f2->name, fi->name) == 0) {
215 formUpdateBuffer(a2, buf, f2);
219 formUpdateBuffer(a, buf, fi);
223 formResetBuffer(Buffer *buf, AnchorList *formitem)
227 FormItemList *f1, *f2;
229 if (buf == NULL || buf->formitem == NULL || formitem == NULL)
231 for (i = 0; i < buf->formitem->nanchor && i < formitem->nanchor; i++) {
232 a = &buf->formitem->anchors[i];
233 if (a->y != a->start.line)
235 f1 = (FormItemList *)a->url;
236 f2 = (FormItemList *)formitem->anchors[i].url;
237 if (f1->type != f2->type ||
238 strcmp(((f1->name == NULL) ? "" : f1->name->ptr),
239 ((f2->name == NULL) ? "" : f2->name->ptr)))
240 break; /* What's happening */
242 case FORM_INPUT_TEXT:
243 case FORM_INPUT_PASSWORD:
244 case FORM_INPUT_FILE:
246 f1->value = f2->value;
247 f1->init_value = f2->init_value;
249 case FORM_INPUT_CHECKBOX:
250 case FORM_INPUT_RADIO:
251 f1->checked = f2->checked;
252 f1->init_checked = f2->init_checked;
256 f1->select_option = f2->select_option;
257 f1->value = f2->value;
258 f1->label = f2->label;
259 f1->selected = f2->selected;
260 f1->init_value = f2->init_value;
261 f1->init_label = f2->init_label;
262 f1->init_selected = f2->init_selected;
263 #endif /* MENU_SELECT */
268 formUpdateBuffer(a, buf, f1);
273 form_update_line(Line *line, char **str, int spos, int epos, int width,
274 int newline, int password)
276 int c_len = 1, c_width = 1, w, i, len, pos;
278 Lineprop c_type, effect, *prop;
280 for (p = *str, w = 0, pos = 0; *p && w < width;) {
281 c_type = get_mctype((unsigned char *)p);
283 c_len = get_mclen(p);
284 c_width = get_mcwidth(p);
286 if (c_type == PC_CTRL) {
287 if (newline && *p == '\n')
296 if (w + c_width > width)
303 else if (c_type & PC_UNKNOWN) {
308 if (w + c_width > width)
318 len = line->len + pos + spos - epos;
319 buf = New_N(char, len);
320 prop = New_N(Lineprop, len);
321 bcopy((void *)line->lineBuf, (void *)buf, spos * sizeof(char));
322 bcopy((void *)line->propBuf, (void *)prop, spos * sizeof(Lineprop));
324 effect = CharEffect(line->propBuf[spos]);
325 for (p = *str, w = 0, pos = spos; *p && w < width;) {
326 c_type = get_mctype((unsigned char *)p);
328 c_len = get_mclen(p);
329 c_width = get_mcwidth(p);
331 if (c_type == PC_CTRL) {
332 if (newline && *p == '\n')
335 buf[pos] = password ? '*' : ' ';
336 prop[pos] = effect | PC_ASCII;
343 if (w + c_width > width)
346 for (i = 0; i < c_width; i++) {
348 prop[pos] = effect | PC_ASCII;
354 else if (c_type & PC_UNKNOWN) {
356 prop[pos] = effect | PC_ASCII;
361 if (w + c_width > width)
368 prop[pos] = effect | c_type;
371 c_type = (c_type & ~PC_WCHAR1) | PC_WCHAR2;
372 for (i = 1; i < c_len; i++) {
374 prop[pos] = effect | c_type;
382 for (; w < width; w++) {
384 prop[pos] = effect | PC_ASCII;
389 while (*p && *p != '\r' && *p != '\n')
399 bcopy((void *)&line->lineBuf[epos], (void *)&buf[pos],
400 (line->len - epos) * sizeof(char));
401 bcopy((void *)&line->propBuf[epos], (void *)&prop[pos],
402 (line->len - epos) * sizeof(Lineprop));
404 line->propBuf = prop;
411 formUpdateBuffer(Anchor *a, Buffer *buf, FormItemList *form)
415 int spos, epos, rows, c_rows, pos, col = 0;
418 copyBuffer(&save, buf);
419 gotoLine(buf, a->start.line);
420 switch (form->type) {
422 case FORM_INPUT_TEXT:
423 case FORM_INPUT_FILE:
424 case FORM_INPUT_PASSWORD:
425 case FORM_INPUT_CHECKBOX:
426 case FORM_INPUT_RADIO:
429 #endif /* MENU_SELECT */
434 spos = a->start.pos + 1;
435 epos = a->end.pos - 1;
437 switch (form->type) {
438 case FORM_INPUT_CHECKBOX:
439 case FORM_INPUT_RADIO:
441 buf->currentLine->lineBuf[spos] = '*';
443 buf->currentLine->lineBuf[spos] = ' ';
445 case FORM_INPUT_TEXT:
446 case FORM_INPUT_FILE:
447 case FORM_INPUT_PASSWORD:
451 if (form->type == FORM_SELECT) {
452 p = form->label->ptr;
453 updateSelectOption(form, form->select_option);
456 #endif /* MENU_SELECT */
457 p = form->value->ptr;
458 l = buf->currentLine;
459 if (form->type == FORM_TEXTAREA) {
460 int n = a->y - buf->currentLine->linenumber;
462 for (; l && n; l = l->prev, n--) ;
464 for (; l && n; l = l->prev, n++) ;
468 rows = form->rows ? form->rows : 1;
469 col = COLPOS(l, a->start.pos);
470 for (c_rows = 0; c_rows < rows; c_rows++, l = l->next) {
472 pos = columnPos(l, col);
473 a = retrieveAnchor(buf->formitem, l->linenumber, pos);
479 pos = form_update_line(l, &p, spos, epos, COLPOS(l, epos) - col,
481 form->type == FORM_INPUT_PASSWORD);
483 shiftAnchorPosition(buf->href, buf->hmarklist,
484 a->start.line, spos, pos - epos);
485 shiftAnchorPosition(buf->name, buf->hmarklist,
486 a->start.line, spos, pos - epos);
487 shiftAnchorPosition(buf->img, buf->hmarklist,
488 a->start.line, spos, pos - epos);
489 shiftAnchorPosition(buf->formitem, buf->hmarklist,
490 a->start.line, spos, pos - epos);
495 copyBuffer(buf, &save);
501 textfieldrep(Str s, int width)
504 Str n = Strnew_size(width + 2);
508 for (i = 0; i < s->length; i += c_len) {
509 c_type = get_mctype((unsigned char *)&s->ptr[i]);
510 c_len = get_mclen(&s->ptr[i]);
511 if (s->ptr[i] == '\r')
513 k = j + get_mcwidth(&s->ptr[i]);
516 if (c_type == PC_CTRL)
519 else if (c_type & PC_UNKNOWN)
522 else if (s->ptr[i] == '&')
523 Strcat_charp(n, "&");
524 else if (s->ptr[i] == '<')
525 Strcat_charp(n, "<");
526 else if (s->ptr[i] == '>')
527 Strcat_charp(n, ">");
529 Strcat_charp_n(n, &s->ptr[i], c_len);
532 for (; j < width; j++)
538 form_fputs_decode(Str s, FILE * f)
543 for (p = s->ptr; *p;) {
545 #if !defined( __CYGWIN__ ) && !defined( __EMX__ )
547 if (*(p + 1) == '\n')
549 /* continue to the next label */
550 #endif /* !defined( __CYGWIN__ ) && !defined( __EMX__
559 z = wc_Str_conv_strict(z, InnerCharset, DisplayCharset);
566 input_textarea(FormItemList *fi)
568 char *tmpf = tmpfname(TMPF_DFL, NULL)->ptr;
572 wc_ces charset = DisplayCharset;
573 wc_uint8 auto_detect;
576 f = fopen(tmpf, "w");
578 /* FIXME: gettextize? */
579 disp_err_message("Can't open temporary file", FALSE);
583 form_fputs_decode(fi->value, f);
587 system(myEditor(Editor, tmpf, 1)->ptr);
592 f = fopen(tmpf, "r");
594 /* FIXME: gettextize? */
595 disp_err_message("Can't open temporary file", FALSE);
598 fi->value = Strnew();
600 auto_detect = WcOption.auto_detect;
601 WcOption.auto_detect = WC_OPT_DETECT_ON;
603 while (tmp = Strfgets(f), tmp->length > 0) {
604 if (tmp->length == 1 && tmp->ptr[tmp->length - 1] == '\n') {
605 /* null line with bare LF */
606 tmp = Strnew_charp("\r\n");
608 else if (tmp->length > 1 && tmp->ptr[tmp->length - 1] == '\n' &&
609 tmp->ptr[tmp->length - 2] != '\r') {
611 Strcat_charp(tmp, "\r\n");
613 tmp = convertLine(NULL, tmp, RAW_MODE, &charset, DisplayCharset);
614 Strcat(fi->value, tmp);
617 WcOption.auto_detect = auto_detect;
625 do_internal(char *action, char *data)
629 for (i = 0; internal_action[i].action; i++) {
630 if (strcasecmp(internal_action[i].action, action) == 0) {
631 if (internal_action[i].rout)
632 internal_action[i].rout(cgistr2tagarg(data));
640 addSelectOption(FormSelectOption *fso, Str value, Str label, int chk)
642 FormSelectOptionItem *o;
643 o = New(FormSelectOptionItem);
647 Strremovefirstspaces(label);
648 Strremovetrailingspaces(label);
652 if (fso->first == NULL)
653 fso->first = fso->last = o;
661 chooseSelectOption(FormItemList *fi, FormSelectOptionItem *item)
663 FormSelectOptionItem *opt;
668 fi->value = Strnew_size(0);
669 fi->label = Strnew_size(0);
672 fi->value = item->value;
673 fi->label = item->label;
674 for (i = 0, opt = item; opt != NULL; i++, opt = opt->next) {
676 fi->value = opt->value;
677 fi->label = opt->label;
682 updateSelectOption(fi, item);
686 updateSelectOption(FormItemList *fi, FormSelectOptionItem *item)
690 if (fi == NULL || item == NULL)
692 for (i = 0; item != NULL; i++, item = item->next) {
693 if (i == fi->selected)
694 item->checked = TRUE;
696 item->checked = FALSE;
701 formChooseOptionByMenu(struct form_item_list *fi, int x, int y)
703 int i, n, selected = -1, init_select = fi->selected;
704 FormSelectOptionItem *opt;
707 for (n = 0, opt = fi->select_option; opt != NULL; n++, opt = opt->next) ;
708 label = New_N(char *, n + 1);
709 for (i = 0, opt = fi->select_option; opt != NULL; i++, opt = opt->next)
710 label[i] = opt->label->ptr;
713 optionMenu(x, y, label, &selected, init_select, NULL);
717 for (i = 0, opt = fi->select_option; opt != NULL; i++, opt = opt->next) {
719 fi->selected = selected;
720 fi->value = opt->value;
721 fi->label = opt->label;
725 updateSelectOption(fi, fi->select_option);
728 #endif /* MENU_SELECT */
731 form_write_data(FILE * f, char *boundary, char *name, char *value)
733 fprintf(f, "--%s\r\n", boundary);
734 fprintf(f, "Content-Disposition: form-data; name=\"%s\"\r\n\r\n", name);
735 fprintf(f, "%s\r\n", value);
739 form_write_from_file(FILE * f, char *boundary, char *name, char *filename,
747 fprintf(f, "--%s\r\n", boundary);
749 "Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\n",
750 name, mybasename(filename));
751 type = guessContentType(file);
752 fprintf(f, "Content-Type: %s\r\n\r\n",
753 type ? type : "application/octet-stream");
755 if (lstat(file, &st) < 0)
757 if (S_ISDIR(st.st_mode))
759 fd = fopen(file, "r");
761 while ((c = fgetc(fd)) != EOF)
769 struct pre_form_item {
774 struct pre_form_item *next;
782 struct pre_form_item *item;
783 struct pre_form *next;
786 static struct pre_form *PreForm = NULL;
788 static struct pre_form *
789 add_pre_form(struct pre_form *prev, char *url, char *name, char *action)
792 struct pre_form *new;
795 new = prev->next = New(struct pre_form);
797 new = PreForm = New(struct pre_form);
798 if (url && *url == '/') {
800 if (l > 1 && url[l - 1] == '/')
801 new->url = allocStr(url + 1, l - 2);
804 new->re_url = newRegex(new->url, FALSE, NULL, NULL);
809 parseURL2(url, &pu, NULL);
810 new->url = parsedURL2Str(&pu)->ptr;
813 new->name = (name && *name) ? name : NULL;
814 new->action = (action && *action) ? action : NULL;
820 static struct pre_form_item *
821 add_pre_form_item(struct pre_form *pf, struct pre_form_item *prev, int type,
822 char *name, char *value, char *checked)
824 struct pre_form_item *new;
829 new = prev->next = New(struct pre_form_item);
831 new = pf->item = New(struct pre_form_item);
835 if (checked && *checked && (!strcmp(checked, "0") ||
836 strcasecmp(checked, "off")
837 || !strcasecmp(checked, "no")))
846 * url <url>|/<re-url>/
847 * form [<name>] <action>
848 * text <name> <value>
849 * file <name> <value>
850 * passwd <name> <value>
851 * checkbox <name> <value> [<checked>]
852 * radio <name> <value>
853 * select <name> <value>
854 * submit [<name> [<value>]]
855 * image [<name> [<value>]]
865 Str line = NULL, textarea = NULL;
866 struct pre_form *pf = NULL;
867 struct pre_form_item *pi = NULL;
872 fp = openSecretFile(pre_form_file);
879 if (line->length == 0)
881 if (textarea && !(!strncmp(line->ptr, "/textarea", 9) &&
882 IS_SPACE(line->ptr[9]))) {
883 Strcat(textarea, line);
887 Strremovefirstspaces(line);
889 if (*p == '#' || *p == '\0')
890 continue; /* comment or empty line */
894 if (!strcmp(s, "url")) {
898 pf = add_pre_form(pf, arg, NULL, p);
904 if (!strcmp(s, "form")) {
914 struct pre_form *prev = pf;
915 pf = add_pre_form(prev, "", s, p);
916 /* copy previous URL */
918 pf->re_url = prev->re_url;
922 pf->action = (p && *p) ? p : NULL;
927 if (!strcmp(s, "text"))
928 type = FORM_INPUT_TEXT;
929 else if (!strcmp(s, "file"))
930 type = FORM_INPUT_FILE;
931 else if (!strcmp(s, "passwd") || !strcmp(s, "password"))
932 type = FORM_INPUT_PASSWORD;
933 else if (!strcmp(s, "checkbox"))
934 type = FORM_INPUT_CHECKBOX;
935 else if (!strcmp(s, "radio"))
936 type = FORM_INPUT_RADIO;
937 else if (!strcmp(s, "submit"))
938 type = FORM_INPUT_SUBMIT;
939 else if (!strcmp(s, "image"))
940 type = FORM_INPUT_IMAGE;
941 else if (!strcmp(s, "select"))
943 else if (!strcmp(s, "textarea")) {
944 type = FORM_TEXTAREA;
945 name = Strnew_charp(arg)->ptr;
949 else if (textarea && name && !strcmp(s, "/textarea")) {
950 pi = add_pre_form_item(pf, pi, type, name, textarea->ptr, NULL);
958 pi = add_pre_form_item(pf, pi, type, arg, s, getQWord(&p));
964 preFormUpdateBuffer(Buffer *buf)
967 struct pre_form_item *pi;
973 FormSelectOptionItem *opt;
977 if (!buf || !buf->formitem || !PreForm)
980 for (pf = PreForm; pf; pf = pf->next) {
982 Str url = parsedURL2Str(&buf->currentURL);
983 if (!RegexMatch(pf->re_url, url->ptr, url->length, 1))
987 if (Strcmp_charp(parsedURL2Str(&buf->currentURL), pf->url))
992 for (i = 0; i < buf->formitem->nanchor; i++) {
993 a = &buf->formitem->anchors[i];
994 fi = (FormItemList *)a->url;
996 if (pf->name && (!fl->name || strcmp(fl->name, pf->name)))
999 && (!fl->action || Strcmp_charp(fl->action, pf->action)))
1001 for (pi = pf->item; pi; pi = pi->next) {
1002 if (pi->type != fi->type)
1004 if (pi->type == FORM_INPUT_SUBMIT ||
1005 pi->type == FORM_INPUT_IMAGE) {
1006 if ((!pi->name || !*pi->name ||
1007 (fi->name && !Strcmp_charp(fi->name, pi->name))) &&
1008 (!pi->value || !*pi->value ||
1009 (fi->value && !Strcmp_charp(fi->value, pi->value))))
1013 if (!pi->name || !fi->name || Strcmp_charp(fi->name, pi->name))
1016 case FORM_INPUT_TEXT:
1017 case FORM_INPUT_FILE:
1018 case FORM_INPUT_PASSWORD:
1020 fi->value = Strnew_charp(pi->value);
1021 formUpdateBuffer(a, buf, fi);
1023 case FORM_INPUT_CHECKBOX:
1024 if (pi->value && fi->value &&
1025 !Strcmp_charp(fi->value, pi->value)) {
1026 fi->checked = pi->checked;
1027 formUpdateBuffer(a, buf, fi);
1030 case FORM_INPUT_RADIO:
1031 if (pi->value && fi->value &&
1032 !Strcmp_charp(fi->value, pi->value))
1033 formRecheckRadio(a, buf, fi);
1037 for (j = 0, opt = fi->select_option; opt != NULL;
1038 j++, opt = opt->next) {
1039 if (pi->value && opt->value &&
1040 !Strcmp_charp(opt->value, pi->value)) {
1042 fi->value = opt->value;
1043 fi->label = opt->label;
1044 updateSelectOption(fi, fi->select_option);
1045 formUpdateBuffer(a, buf, fi);