When encountering a <form ...> tag that contains these values: method="get" enctype="multipart/form-data" Do not transform the method into POST to accomodate enctype. Instead behave in the compatible way that all other browsers behave in this instance and ignore the enctype parameter (treating it as the default application/x-www-form-urlencoded) and perform a "GET" just as the method parameter requests. This behavior produces far more compatible results than gratuitously changing the "get" into a "post" which can result in unexpected "405 Method Not Allowed" errors. Signed-off-by: Kyle J. McKay <mackyle@gmail.com>
1068 lines
24 KiB
C
1068 lines
24 KiB
C
/* $Id: form.c,v 1.35 2010/07/18 13:48:48 htrb Exp $ */
|
|
/*
|
|
* HTML forms
|
|
*/
|
|
#include "fm.h"
|
|
#include "parsetag.h"
|
|
#include "parsetagx.h"
|
|
#include "myctype.h"
|
|
#include "local.h"
|
|
#include "regex.h"
|
|
|
|
extern Str *textarea_str;
|
|
extern int max_textarea;
|
|
#ifdef MENU_SELECT
|
|
extern FormSelectOption *select_option;
|
|
extern int max_select;
|
|
#include "menu.h"
|
|
#endif /* MENU_SELECT */
|
|
|
|
/* *INDENT-OFF* */
|
|
struct {
|
|
char *action;
|
|
void (*rout)(struct parsed_tagarg *);
|
|
} internal_action[] = {
|
|
{"map", follow_map},
|
|
{"option", panel_set_option},
|
|
#ifdef USE_COOKIE
|
|
{"cookie", set_cookie_flag},
|
|
#endif /* USE_COOKIE */
|
|
{"download", download_action},
|
|
#ifdef USE_M17N
|
|
{ "charset", change_charset },
|
|
#endif
|
|
{"none", NULL},
|
|
{NULL, NULL},
|
|
};
|
|
/* *INDENT-ON* */
|
|
|
|
struct form_list *
|
|
newFormList(char *action, char *method, char *charset, char *enctype,
|
|
char *target, char *name, struct form_list *_next)
|
|
{
|
|
struct form_list *l;
|
|
Str a = Strnew_charp(action);
|
|
int m = FORM_METHOD_GET;
|
|
int e = FORM_ENCTYPE_URLENCODED;
|
|
#ifdef USE_M17N
|
|
wc_ces c = 0;
|
|
#endif
|
|
|
|
if (method == NULL || !strcasecmp(method, "get"))
|
|
m = FORM_METHOD_GET;
|
|
else if (!strcasecmp(method, "post"))
|
|
m = FORM_METHOD_POST;
|
|
else if (!strcasecmp(method, "internal"))
|
|
m = FORM_METHOD_INTERNAL;
|
|
/* unknown method is regarded as 'get' */
|
|
|
|
if (m != FORM_METHOD_GET && enctype != NULL &&
|
|
!strcasecmp(enctype, "multipart/form-data")) {
|
|
e = FORM_ENCTYPE_MULTIPART;
|
|
}
|
|
|
|
#ifdef USE_M17N
|
|
if (charset != NULL)
|
|
c = wc_guess_charset(charset, 0);
|
|
#endif
|
|
|
|
l = New(struct form_list);
|
|
l->item = l->lastitem = NULL;
|
|
l->action = a;
|
|
l->method = m;
|
|
#ifdef USE_M17N
|
|
l->charset = c;
|
|
#endif
|
|
l->enctype = e;
|
|
l->target = target;
|
|
l->name = name;
|
|
l->next = _next;
|
|
l->nitems = 0;
|
|
l->body = NULL;
|
|
l->length = 0;
|
|
return l;
|
|
}
|
|
|
|
/*
|
|
* add <input> element to form_list
|
|
*/
|
|
struct form_item_list *
|
|
formList_addInput(struct form_list *fl, struct parsed_tag *tag)
|
|
{
|
|
struct form_item_list *item;
|
|
char *p;
|
|
int i;
|
|
|
|
/* if not in <form>..</form> environment, just ignore <input> tag */
|
|
if (fl == NULL)
|
|
return NULL;
|
|
|
|
item = New(struct form_item_list);
|
|
item->type = FORM_UNKNOWN;
|
|
item->size = -1;
|
|
item->rows = 0;
|
|
item->checked = item->init_checked = 0;
|
|
item->accept = 0;
|
|
item->name = NULL;
|
|
item->value = item->init_value = NULL;
|
|
item->readonly = 0;
|
|
if (parsedtag_get_value(tag, ATTR_TYPE, &p)) {
|
|
item->type = formtype(p);
|
|
if (item->size < 0 &&
|
|
(item->type == FORM_INPUT_TEXT ||
|
|
item->type == FORM_INPUT_FILE ||
|
|
item->type == FORM_INPUT_PASSWORD))
|
|
item->size = FORM_I_TEXT_DEFAULT_SIZE;
|
|
}
|
|
if (parsedtag_get_value(tag, ATTR_NAME, &p))
|
|
item->name = Strnew_charp(p);
|
|
if (parsedtag_get_value(tag, ATTR_VALUE, &p))
|
|
item->value = item->init_value = Strnew_charp(p);
|
|
item->checked = item->init_checked = parsedtag_exists(tag, ATTR_CHECKED);
|
|
item->accept = parsedtag_exists(tag, ATTR_ACCEPT);
|
|
parsedtag_get_value(tag, ATTR_SIZE, &item->size);
|
|
parsedtag_get_value(tag, ATTR_MAXLENGTH, &item->maxlength);
|
|
item->readonly = parsedtag_exists(tag, ATTR_READONLY);
|
|
if (parsedtag_get_value(tag, ATTR_TEXTAREANUMBER, &i)
|
|
&& i >= 0 && i < max_textarea)
|
|
item->value = item->init_value = textarea_str[i];
|
|
#ifdef MENU_SELECT
|
|
if (parsedtag_get_value(tag, ATTR_SELECTNUMBER, &i)
|
|
&& i >= 0 && i < max_select)
|
|
item->select_option = select_option[i].first;
|
|
#endif /* MENU_SELECT */
|
|
if (parsedtag_get_value(tag, ATTR_ROWS, &p))
|
|
item->rows = atoi(p);
|
|
if (item->type == FORM_UNKNOWN) {
|
|
/* type attribute is missing. Ignore the tag. */
|
|
return NULL;
|
|
}
|
|
#ifdef MENU_SELECT
|
|
if (item->type == FORM_SELECT) {
|
|
chooseSelectOption(item, item->select_option);
|
|
item->init_selected = item->selected;
|
|
item->init_value = item->value;
|
|
item->init_label = item->label;
|
|
}
|
|
#endif /* MENU_SELECT */
|
|
if (item->type == FORM_INPUT_FILE && item->value && item->value->length) {
|
|
/* security hole ! */
|
|
return NULL;
|
|
}
|
|
item->parent = fl;
|
|
item->next = NULL;
|
|
if (fl->item == NULL) {
|
|
fl->item = fl->lastitem = item;
|
|
}
|
|
else {
|
|
fl->lastitem->next = item;
|
|
fl->lastitem = item;
|
|
}
|
|
if (item->type == FORM_INPUT_HIDDEN)
|
|
return NULL;
|
|
fl->nitems++;
|
|
return item;
|
|
}
|
|
|
|
static char *_formtypetbl[] = {
|
|
"text", "password", "checkbox", "radio", "submit", "reset", "hidden",
|
|
"image", "select", "textarea", "button", "file", NULL
|
|
};
|
|
|
|
static char *_formmethodtbl[] = {
|
|
"GET", "POST", "INTERNAL", "HEAD"
|
|
};
|
|
|
|
char *
|
|
form2str(FormItemList *fi)
|
|
{
|
|
Str tmp = Strnew();
|
|
|
|
if (fi->type != FORM_SELECT && fi->type != FORM_TEXTAREA)
|
|
Strcat_charp(tmp, "input type=");
|
|
Strcat_charp(tmp, _formtypetbl[fi->type]);
|
|
if (fi->name && fi->name->length)
|
|
Strcat_m_charp(tmp, " name=\"", fi->name->ptr, "\"", NULL);
|
|
if ((fi->type == FORM_INPUT_RADIO || fi->type == FORM_INPUT_CHECKBOX ||
|
|
fi->type == FORM_SELECT) && fi->value)
|
|
Strcat_m_charp(tmp, " value=\"", fi->value->ptr, "\"", NULL);
|
|
Strcat_m_charp(tmp, " (", _formmethodtbl[fi->parent->method], " ",
|
|
fi->parent->action->ptr, ")", NULL);
|
|
return tmp->ptr;
|
|
}
|
|
|
|
int
|
|
formtype(char *typestr)
|
|
{
|
|
int i;
|
|
for (i = 0; _formtypetbl[i]; i++) {
|
|
if (!strcasecmp(typestr, _formtypetbl[i]))
|
|
return i;
|
|
}
|
|
return FORM_INPUT_TEXT;
|
|
}
|
|
|
|
void
|
|
formRecheckRadio(Anchor *a, Buffer *buf, FormItemList *fi)
|
|
{
|
|
int i;
|
|
Anchor *a2;
|
|
FormItemList *f2;
|
|
|
|
for (i = 0; i < buf->formitem->nanchor; i++) {
|
|
a2 = &buf->formitem->anchors[i];
|
|
f2 = (FormItemList *)a2->url;
|
|
if (f2->parent == fi->parent && f2 != fi &&
|
|
f2->type == FORM_INPUT_RADIO && Strcmp(f2->name, fi->name) == 0) {
|
|
f2->checked = 0;
|
|
formUpdateBuffer(a2, buf, f2);
|
|
}
|
|
}
|
|
fi->checked = 1;
|
|
formUpdateBuffer(a, buf, fi);
|
|
}
|
|
|
|
void
|
|
formResetBuffer(Buffer *buf, AnchorList *formitem)
|
|
{
|
|
int i;
|
|
Anchor *a;
|
|
FormItemList *f1, *f2;
|
|
|
|
if (buf == NULL || buf->formitem == NULL || formitem == NULL)
|
|
return;
|
|
for (i = 0; i < buf->formitem->nanchor && i < formitem->nanchor; i++) {
|
|
a = &buf->formitem->anchors[i];
|
|
if (a->y != a->start.line)
|
|
continue;
|
|
f1 = (FormItemList *)a->url;
|
|
f2 = (FormItemList *)formitem->anchors[i].url;
|
|
if (f1->type != f2->type ||
|
|
strcmp(((f1->name == NULL) ? "" : f1->name->ptr),
|
|
((f2->name == NULL) ? "" : f2->name->ptr)))
|
|
break; /* What's happening */
|
|
switch (f1->type) {
|
|
case FORM_INPUT_TEXT:
|
|
case FORM_INPUT_PASSWORD:
|
|
case FORM_INPUT_FILE:
|
|
case FORM_TEXTAREA:
|
|
f1->value = f2->value;
|
|
f1->init_value = f2->init_value;
|
|
break;
|
|
case FORM_INPUT_CHECKBOX:
|
|
case FORM_INPUT_RADIO:
|
|
f1->checked = f2->checked;
|
|
f1->init_checked = f2->init_checked;
|
|
break;
|
|
case FORM_SELECT:
|
|
#ifdef MENU_SELECT
|
|
f1->select_option = f2->select_option;
|
|
f1->value = f2->value;
|
|
f1->label = f2->label;
|
|
f1->selected = f2->selected;
|
|
f1->init_value = f2->init_value;
|
|
f1->init_label = f2->init_label;
|
|
f1->init_selected = f2->init_selected;
|
|
#endif /* MENU_SELECT */
|
|
break;
|
|
default:
|
|
continue;
|
|
}
|
|
formUpdateBuffer(a, buf, f1);
|
|
}
|
|
}
|
|
|
|
static int
|
|
form_update_line(Line *line, char **str, int spos, int epos, int width,
|
|
int newline, int password)
|
|
{
|
|
int c_len = 1, c_width = 1, w, i, len, pos;
|
|
char *p, *buf;
|
|
Lineprop c_type, effect, *prop;
|
|
|
|
for (p = *str, w = 0, pos = 0; *p && w < width;) {
|
|
c_type = get_mctype((unsigned char *)p);
|
|
#ifdef USE_M17N
|
|
c_len = get_mclen(p);
|
|
c_width = get_mcwidth(p);
|
|
#endif
|
|
if (c_type == PC_CTRL) {
|
|
if (newline && *p == '\n')
|
|
break;
|
|
if (*p != '\r') {
|
|
w++;
|
|
pos++;
|
|
}
|
|
}
|
|
else if (password) {
|
|
#ifdef USE_M17N
|
|
if (w + c_width > width)
|
|
break;
|
|
#endif
|
|
w += c_width;
|
|
pos += c_width;
|
|
#ifdef USE_M17N
|
|
}
|
|
else if (c_type & PC_UNKNOWN) {
|
|
w++;
|
|
pos++;
|
|
}
|
|
else {
|
|
if (w + c_width > width)
|
|
break;
|
|
#endif
|
|
w += c_width;
|
|
pos += c_len;
|
|
}
|
|
p += c_len;
|
|
}
|
|
pos += width - w;
|
|
|
|
len = line->len + pos + spos - epos;
|
|
buf = New_N(char, len + 1);
|
|
buf[len] = '\0';
|
|
prop = New_N(Lineprop, len);
|
|
bcopy((void *)line->lineBuf, (void *)buf, spos * sizeof(char));
|
|
bcopy((void *)line->propBuf, (void *)prop, spos * sizeof(Lineprop));
|
|
|
|
effect = CharEffect(line->propBuf[spos]);
|
|
for (p = *str, w = 0, pos = spos; *p && w < width;) {
|
|
c_type = get_mctype((unsigned char *)p);
|
|
#ifdef USE_M17N
|
|
c_len = get_mclen(p);
|
|
c_width = get_mcwidth(p);
|
|
#endif
|
|
if (c_type == PC_CTRL) {
|
|
if (newline && *p == '\n')
|
|
break;
|
|
if (*p != '\r') {
|
|
buf[pos] = password ? '*' : ' ';
|
|
prop[pos] = effect | PC_ASCII;
|
|
pos++;
|
|
w++;
|
|
}
|
|
}
|
|
else if (password) {
|
|
#ifdef USE_M17N
|
|
if (w + c_width > width)
|
|
break;
|
|
#endif
|
|
for (i = 0; i < c_width; i++) {
|
|
buf[pos] = '*';
|
|
prop[pos] = effect | PC_ASCII;
|
|
pos++;
|
|
w++;
|
|
}
|
|
#ifdef USE_M17N
|
|
}
|
|
else if (c_type & PC_UNKNOWN) {
|
|
buf[pos] = ' ';
|
|
prop[pos] = effect | PC_ASCII;
|
|
pos++;
|
|
w++;
|
|
}
|
|
else {
|
|
if (w + c_width > width)
|
|
break;
|
|
#else
|
|
}
|
|
else {
|
|
#endif
|
|
buf[pos] = *p;
|
|
prop[pos] = effect | c_type;
|
|
pos++;
|
|
#ifdef USE_M17N
|
|
c_type = (c_type & ~PC_WCHAR1) | PC_WCHAR2;
|
|
for (i = 1; i < c_len; i++) {
|
|
buf[pos] = p[i];
|
|
prop[pos] = effect | c_type;
|
|
pos++;
|
|
}
|
|
#endif
|
|
w += c_width;
|
|
}
|
|
p += c_len;
|
|
}
|
|
for (; w < width; w++) {
|
|
buf[pos] = ' ';
|
|
prop[pos] = effect | PC_ASCII;
|
|
pos++;
|
|
}
|
|
if (newline) {
|
|
if (!FoldTextarea) {
|
|
while (*p && *p != '\r' && *p != '\n')
|
|
p++;
|
|
}
|
|
if (*p == '\r')
|
|
p++;
|
|
if (*p == '\n')
|
|
p++;
|
|
}
|
|
*str = p;
|
|
|
|
bcopy((void *)&line->lineBuf[epos], (void *)&buf[pos],
|
|
(line->len - epos) * sizeof(char));
|
|
bcopy((void *)&line->propBuf[epos], (void *)&prop[pos],
|
|
(line->len - epos) * sizeof(Lineprop));
|
|
line->lineBuf = buf;
|
|
line->propBuf = prop;
|
|
line->len = len;
|
|
line->size = len;
|
|
|
|
return pos;
|
|
}
|
|
|
|
void
|
|
formUpdateBuffer(Anchor *a, Buffer *buf, FormItemList *form)
|
|
{
|
|
Buffer save;
|
|
char *p;
|
|
int spos, epos, rows, c_rows, pos, col = 0;
|
|
Line *l;
|
|
|
|
copyBuffer(&save, buf);
|
|
gotoLine(buf, a->start.line);
|
|
switch (form->type) {
|
|
case FORM_TEXTAREA:
|
|
case FORM_INPUT_TEXT:
|
|
case FORM_INPUT_FILE:
|
|
case FORM_INPUT_PASSWORD:
|
|
case FORM_INPUT_CHECKBOX:
|
|
case FORM_INPUT_RADIO:
|
|
#ifdef MENU_SELECT
|
|
case FORM_SELECT:
|
|
#endif /* MENU_SELECT */
|
|
spos = a->start.pos;
|
|
epos = a->end.pos;
|
|
break;
|
|
default:
|
|
spos = a->start.pos + 1;
|
|
epos = a->end.pos - 1;
|
|
}
|
|
switch (form->type) {
|
|
case FORM_INPUT_CHECKBOX:
|
|
case FORM_INPUT_RADIO:
|
|
if (buf->currentLine == NULL ||
|
|
spos >= buf->currentLine->len || spos < 0)
|
|
break;
|
|
if (form->checked)
|
|
buf->currentLine->lineBuf[spos] = '*';
|
|
else
|
|
buf->currentLine->lineBuf[spos] = ' ';
|
|
break;
|
|
case FORM_INPUT_TEXT:
|
|
case FORM_INPUT_FILE:
|
|
case FORM_INPUT_PASSWORD:
|
|
case FORM_TEXTAREA:
|
|
#ifdef MENU_SELECT
|
|
case FORM_SELECT:
|
|
if (form->type == FORM_SELECT) {
|
|
p = form->label->ptr;
|
|
updateSelectOption(form, form->select_option);
|
|
}
|
|
else
|
|
#endif /* MENU_SELECT */
|
|
{
|
|
if (!form->value)
|
|
break;
|
|
p = form->value->ptr;
|
|
}
|
|
l = buf->currentLine;
|
|
if (!l)
|
|
break;
|
|
if (form->type == FORM_TEXTAREA) {
|
|
int n = a->y - buf->currentLine->linenumber;
|
|
if (n > 0)
|
|
for (; l && n; l = l->prev, n--) ;
|
|
else if (n < 0)
|
|
for (; l && n; l = l->prev, n++) ;
|
|
if (!l)
|
|
break;
|
|
}
|
|
rows = form->rows ? form->rows : 1;
|
|
col = COLPOS(l, a->start.pos);
|
|
for (c_rows = 0; c_rows < rows; c_rows++, l = l->next) {
|
|
if (rows > 1) {
|
|
pos = columnPos(l, col);
|
|
a = retrieveAnchor(buf->formitem, l->linenumber, pos);
|
|
if (a == NULL)
|
|
break;
|
|
spos = a->start.pos;
|
|
epos = a->end.pos;
|
|
}
|
|
if (a->start.line != a->end.line || spos > epos || epos >= l->len ||
|
|
spos < 0 || epos < 0 || COLPOS(l, epos) < col)
|
|
break;
|
|
pos = form_update_line(l, &p, spos, epos, COLPOS(l, epos) - col,
|
|
rows > 1,
|
|
form->type == FORM_INPUT_PASSWORD);
|
|
if (pos != epos) {
|
|
shiftAnchorPosition(buf->href, buf->hmarklist,
|
|
a->start.line, spos, pos - epos);
|
|
shiftAnchorPosition(buf->name, buf->hmarklist,
|
|
a->start.line, spos, pos - epos);
|
|
shiftAnchorPosition(buf->img, buf->hmarklist,
|
|
a->start.line, spos, pos - epos);
|
|
shiftAnchorPosition(buf->formitem, buf->hmarklist,
|
|
a->start.line, spos, pos - epos);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
copyBuffer(buf, &save);
|
|
arrangeLine(buf);
|
|
}
|
|
|
|
|
|
Str
|
|
textfieldrep(Str s, int width)
|
|
{
|
|
Lineprop c_type;
|
|
Str n = Strnew_size(width + 2);
|
|
int i, j, k, c_len;
|
|
|
|
j = 0;
|
|
for (i = 0; i < s->length; i += c_len) {
|
|
c_type = get_mctype((unsigned char *)&s->ptr[i]);
|
|
c_len = get_mclen(&s->ptr[i]);
|
|
if (s->ptr[i] == '\r')
|
|
continue;
|
|
k = j + get_mcwidth(&s->ptr[i]);
|
|
if (k > width)
|
|
break;
|
|
if (c_type == PC_CTRL)
|
|
Strcat_char(n, ' ');
|
|
#ifdef USE_M17N
|
|
else if (c_type & PC_UNKNOWN)
|
|
Strcat_char(n, ' ');
|
|
#endif
|
|
else if (s->ptr[i] == '&')
|
|
Strcat_charp(n, "&");
|
|
else if (s->ptr[i] == '<')
|
|
Strcat_charp(n, "<");
|
|
else if (s->ptr[i] == '>')
|
|
Strcat_charp(n, ">");
|
|
else
|
|
Strcat_charp_n(n, &s->ptr[i], c_len);
|
|
j = k;
|
|
}
|
|
for (; j < width; j++)
|
|
Strcat_char(n, ' ');
|
|
return n;
|
|
}
|
|
|
|
static void
|
|
form_fputs_decode(Str s, FILE * f)
|
|
{
|
|
char *p;
|
|
Str z = Strnew();
|
|
|
|
for (p = s->ptr; *p;) {
|
|
switch (*p) {
|
|
#if !defined( __CYGWIN__ ) && !defined( __EMX__ )
|
|
case '\r':
|
|
if (*(p + 1) == '\n')
|
|
p++;
|
|
/* continue to the next label */
|
|
#endif /* !defined( __CYGWIN__ ) && !defined( __EMX__
|
|
* ) */
|
|
default:
|
|
Strcat_char(z, *p);
|
|
p++;
|
|
break;
|
|
}
|
|
}
|
|
#ifdef USE_M17N
|
|
z = wc_Str_conv_strict(z, InnerCharset, DisplayCharset);
|
|
#endif
|
|
Strfputs(z, f);
|
|
}
|
|
|
|
|
|
void
|
|
input_textarea(FormItemList *fi)
|
|
{
|
|
char *tmpf = tmpfname(TMPF_DFL, NULL)->ptr;
|
|
Str tmp;
|
|
FILE *f;
|
|
#ifdef USE_M17N
|
|
wc_ces charset = DisplayCharset;
|
|
wc_uint8 auto_detect;
|
|
#endif
|
|
|
|
f = fopen(tmpf, "w");
|
|
if (f == NULL) {
|
|
/* FIXME: gettextize? */
|
|
disp_err_message("Can't open temporary file", FALSE);
|
|
return;
|
|
}
|
|
if (fi->value)
|
|
form_fputs_decode(fi->value, f);
|
|
fclose(f);
|
|
|
|
fmTerm();
|
|
system(myEditor(Editor, tmpf, 1)->ptr);
|
|
fmInit();
|
|
|
|
if (fi->readonly)
|
|
goto input_end;
|
|
f = fopen(tmpf, "r");
|
|
if (f == NULL) {
|
|
/* FIXME: gettextize? */
|
|
disp_err_message("Can't open temporary file", FALSE);
|
|
goto input_end;
|
|
}
|
|
fi->value = Strnew();
|
|
#ifdef USE_M17N
|
|
auto_detect = WcOption.auto_detect;
|
|
WcOption.auto_detect = WC_OPT_DETECT_ON;
|
|
#endif
|
|
while (tmp = Strfgets(f), tmp->length > 0) {
|
|
if (tmp->length == 1 && tmp->ptr[tmp->length - 1] == '\n') {
|
|
/* null line with bare LF */
|
|
tmp = Strnew_charp("\r\n");
|
|
}
|
|
else if (tmp->length > 1 && tmp->ptr[tmp->length - 1] == '\n' &&
|
|
tmp->ptr[tmp->length - 2] != '\r') {
|
|
Strshrink(tmp, 1);
|
|
Strcat_charp(tmp, "\r\n");
|
|
}
|
|
tmp = convertLine(NULL, tmp, RAW_MODE, &charset, DisplayCharset);
|
|
Strcat(fi->value, tmp);
|
|
}
|
|
#ifdef USE_M17N
|
|
WcOption.auto_detect = auto_detect;
|
|
#endif
|
|
fclose(f);
|
|
input_end:
|
|
unlink(tmpf);
|
|
}
|
|
|
|
void
|
|
do_internal(char *action, char *data)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; internal_action[i].action; i++) {
|
|
if (strcasecmp(internal_action[i].action, action) == 0) {
|
|
if (internal_action[i].rout)
|
|
internal_action[i].rout(cgistr2tagarg(data));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef MENU_SELECT
|
|
void
|
|
addSelectOption(FormSelectOption *fso, Str value, Str label, int chk)
|
|
{
|
|
FormSelectOptionItem *o;
|
|
o = New(FormSelectOptionItem);
|
|
if (value == NULL)
|
|
value = label;
|
|
o->value = value;
|
|
Strremovefirstspaces(label);
|
|
Strremovetrailingspaces(label);
|
|
o->label = label;
|
|
o->checked = chk;
|
|
o->next = NULL;
|
|
if (fso->first == NULL)
|
|
fso->first = fso->last = o;
|
|
else {
|
|
fso->last->next = o;
|
|
fso->last = o;
|
|
}
|
|
}
|
|
|
|
void
|
|
chooseSelectOption(FormItemList *fi, FormSelectOptionItem *item)
|
|
{
|
|
FormSelectOptionItem *opt;
|
|
int i;
|
|
|
|
fi->selected = 0;
|
|
if (item == NULL) {
|
|
fi->value = Strnew_size(0);
|
|
fi->label = Strnew_size(0);
|
|
return;
|
|
}
|
|
fi->value = item->value;
|
|
fi->label = item->label;
|
|
for (i = 0, opt = item; opt != NULL; i++, opt = opt->next) {
|
|
if (opt->checked) {
|
|
fi->value = opt->value;
|
|
fi->label = opt->label;
|
|
fi->selected = i;
|
|
break;
|
|
}
|
|
}
|
|
updateSelectOption(fi, item);
|
|
}
|
|
|
|
void
|
|
updateSelectOption(FormItemList *fi, FormSelectOptionItem *item)
|
|
{
|
|
int i;
|
|
|
|
if (fi == NULL || item == NULL)
|
|
return;
|
|
for (i = 0; item != NULL; i++, item = item->next) {
|
|
if (i == fi->selected)
|
|
item->checked = TRUE;
|
|
else
|
|
item->checked = FALSE;
|
|
}
|
|
}
|
|
|
|
int
|
|
formChooseOptionByMenu(struct form_item_list *fi, int x, int y)
|
|
{
|
|
int i, n, selected = -1, init_select = fi->selected;
|
|
FormSelectOptionItem *opt;
|
|
char **label;
|
|
|
|
for (n = 0, opt = fi->select_option; opt != NULL; n++, opt = opt->next) ;
|
|
label = New_N(char *, n + 1);
|
|
for (i = 0, opt = fi->select_option; opt != NULL; i++, opt = opt->next)
|
|
label[i] = opt->label->ptr;
|
|
label[n] = NULL;
|
|
|
|
optionMenu(x, y, label, &selected, init_select, NULL);
|
|
|
|
if (selected < 0)
|
|
return 0;
|
|
for (i = 0, opt = fi->select_option; opt != NULL; i++, opt = opt->next) {
|
|
if (i == selected) {
|
|
fi->selected = selected;
|
|
fi->value = opt->value;
|
|
fi->label = opt->label;
|
|
break;
|
|
}
|
|
}
|
|
updateSelectOption(fi, fi->select_option);
|
|
return 1;
|
|
}
|
|
#endif /* MENU_SELECT */
|
|
|
|
void
|
|
form_write_data(FILE * f, char *boundary, char *name, char *value)
|
|
{
|
|
fprintf(f, "--%s\r\n", boundary);
|
|
fprintf(f, "Content-Disposition: form-data; name=\"%s\"\r\n\r\n", name);
|
|
fprintf(f, "%s\r\n", value);
|
|
}
|
|
|
|
void
|
|
form_write_from_file(FILE * f, char *boundary, char *name, char *filename,
|
|
char *file)
|
|
{
|
|
FILE *fd;
|
|
struct stat st;
|
|
int c;
|
|
char *type;
|
|
|
|
fprintf(f, "--%s\r\n", boundary);
|
|
fprintf(f,
|
|
"Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\n",
|
|
name, mybasename(filename));
|
|
type = guessContentType(file);
|
|
fprintf(f, "Content-Type: %s\r\n\r\n",
|
|
type ? type : "application/octet-stream");
|
|
|
|
if (lstat(file, &st) < 0)
|
|
goto write_end;
|
|
if (S_ISDIR(st.st_mode))
|
|
goto write_end;
|
|
fd = fopen(file, "r");
|
|
if (fd != NULL) {
|
|
while ((c = fgetc(fd)) != EOF)
|
|
fputc(c, f);
|
|
fclose(fd);
|
|
}
|
|
write_end:
|
|
fprintf(f, "\r\n");
|
|
}
|
|
|
|
struct pre_form_item {
|
|
int type;
|
|
char *name;
|
|
char *value;
|
|
int checked;
|
|
struct pre_form_item *next;
|
|
};
|
|
|
|
struct pre_form {
|
|
char *url;
|
|
Regex *re_url;
|
|
char *name;
|
|
char *action;
|
|
struct pre_form_item *item;
|
|
struct pre_form *next;
|
|
};
|
|
|
|
static struct pre_form *PreForm = NULL;
|
|
|
|
static struct pre_form *
|
|
add_pre_form(struct pre_form *prev, char *url, Regex *re_url, char *name, char *action)
|
|
{
|
|
ParsedURL pu;
|
|
struct pre_form *new;
|
|
|
|
if (prev)
|
|
new = prev->next = New(struct pre_form);
|
|
else
|
|
new = PreForm = New(struct pre_form);
|
|
if (url && !re_url) {
|
|
parseURL2(url, &pu, NULL);
|
|
new->url = parsedURL2Str(&pu)->ptr;
|
|
}
|
|
else
|
|
new->url = url;
|
|
new->re_url = re_url;
|
|
new->name = (name && *name) ? name : NULL;
|
|
new->action = (action && *action) ? action : NULL;
|
|
new->item = NULL;
|
|
new->next = NULL;
|
|
return new;
|
|
}
|
|
|
|
static struct pre_form_item *
|
|
add_pre_form_item(struct pre_form *pf, struct pre_form_item *prev, int type,
|
|
char *name, char *value, char *checked)
|
|
{
|
|
struct pre_form_item *new;
|
|
|
|
if (!pf)
|
|
return NULL;
|
|
if (prev)
|
|
new = prev->next = New(struct pre_form_item);
|
|
else
|
|
new = pf->item = New(struct pre_form_item);
|
|
new->type = type;
|
|
new->name = name;
|
|
new->value = value;
|
|
if (checked && *checked && (!strcmp(checked, "0") ||
|
|
!strcasecmp(checked, "off")
|
|
|| !strcasecmp(checked, "no")))
|
|
new->checked = 0;
|
|
else
|
|
new->checked = 1;
|
|
new->next = NULL;
|
|
return new;
|
|
}
|
|
|
|
/*
|
|
* url <url>|/<re-url>/
|
|
* form [<name>] <action>
|
|
* text <name> <value>
|
|
* file <name> <value>
|
|
* passwd <name> <value>
|
|
* checkbox <name> <value> [<checked>]
|
|
* radio <name> <value>
|
|
* select <name> <value>
|
|
* submit [<name> [<value>]]
|
|
* image [<name> [<value>]]
|
|
* textarea <name>
|
|
* <value>
|
|
* /textarea
|
|
*/
|
|
|
|
void
|
|
loadPreForm(void)
|
|
{
|
|
FILE *fp;
|
|
Str line = NULL, textarea = NULL;
|
|
struct pre_form *pf = NULL;
|
|
struct pre_form_item *pi = NULL;
|
|
int type = -1;
|
|
char *name = NULL;
|
|
|
|
PreForm = NULL;
|
|
fp = openSecretFile(pre_form_file);
|
|
if (fp == NULL)
|
|
return;
|
|
while (1) {
|
|
char *p, *s, *arg;
|
|
Regex *re_arg;
|
|
|
|
line = Strfgets(fp);
|
|
if (line->length == 0)
|
|
break;
|
|
if (textarea && !(!strncmp(line->ptr, "/textarea", 9) &&
|
|
IS_SPACE(line->ptr[9]))) {
|
|
Strcat(textarea, line);
|
|
continue;
|
|
}
|
|
Strchop(line);
|
|
Strremovefirstspaces(line);
|
|
p = line->ptr;
|
|
if (*p == '#' || *p == '\0')
|
|
continue; /* comment or empty line */
|
|
s = getWord(&p);
|
|
|
|
if (!strcmp(s, "url")) {
|
|
arg = getRegexWord((const char **)&p, &re_arg);
|
|
if (!arg || !*arg)
|
|
continue;
|
|
p = getQWord(&p);
|
|
pf = add_pre_form(pf, arg, re_arg, NULL, p);
|
|
pi = pf->item;
|
|
continue;
|
|
}
|
|
if (!pf)
|
|
continue;
|
|
|
|
arg = getWord(&p);
|
|
if (!strcmp(s, "form")) {
|
|
if (!arg || !*arg)
|
|
continue;
|
|
s = getQWord(&p);
|
|
p = getQWord(&p);
|
|
if (!p || !*p) {
|
|
p = s;
|
|
s = NULL;
|
|
}
|
|
if (pf->item) {
|
|
struct pre_form *prev = pf;
|
|
pf = add_pre_form(prev, "", NULL, s, p);
|
|
/* copy previous URL */
|
|
pf->url = prev->url;
|
|
pf->re_url = prev->re_url;
|
|
}
|
|
else {
|
|
pf->name = s;
|
|
pf->action = (p && *p) ? p : NULL;
|
|
}
|
|
pi = pf->item;
|
|
continue;
|
|
}
|
|
if (!strcmp(s, "text"))
|
|
type = FORM_INPUT_TEXT;
|
|
else if (!strcmp(s, "file"))
|
|
type = FORM_INPUT_FILE;
|
|
else if (!strcmp(s, "passwd") || !strcmp(s, "password"))
|
|
type = FORM_INPUT_PASSWORD;
|
|
else if (!strcmp(s, "checkbox"))
|
|
type = FORM_INPUT_CHECKBOX;
|
|
else if (!strcmp(s, "radio"))
|
|
type = FORM_INPUT_RADIO;
|
|
else if (!strcmp(s, "submit"))
|
|
type = FORM_INPUT_SUBMIT;
|
|
else if (!strcmp(s, "image"))
|
|
type = FORM_INPUT_IMAGE;
|
|
else if (!strcmp(s, "select"))
|
|
type = FORM_SELECT;
|
|
else if (!strcmp(s, "textarea")) {
|
|
type = FORM_TEXTAREA;
|
|
name = Strnew_charp(arg)->ptr;
|
|
textarea = Strnew();
|
|
continue;
|
|
}
|
|
else if (textarea && name && !strcmp(s, "/textarea")) {
|
|
pi = add_pre_form_item(pf, pi, type, name, textarea->ptr, NULL);
|
|
textarea = NULL;
|
|
name = NULL;
|
|
continue;
|
|
}
|
|
else
|
|
continue;
|
|
s = getQWord(&p);
|
|
pi = add_pre_form_item(pf, pi, type, arg, s, getQWord(&p));
|
|
}
|
|
fclose(fp);
|
|
}
|
|
|
|
void
|
|
preFormUpdateBuffer(Buffer *buf)
|
|
{
|
|
struct pre_form *pf;
|
|
struct pre_form_item *pi;
|
|
int i;
|
|
Anchor *a;
|
|
FormList *fl;
|
|
FormItemList *fi;
|
|
#ifdef MENU_SELECT
|
|
FormSelectOptionItem *opt;
|
|
int j;
|
|
#endif
|
|
|
|
if (!buf || !buf->formitem || !PreForm)
|
|
return;
|
|
|
|
for (pf = PreForm; pf; pf = pf->next) {
|
|
if (pf->re_url) {
|
|
Str url = parsedURL2Str(&buf->currentURL);
|
|
if (!RegexMatch(pf->re_url, url->ptr, url->length, 1))
|
|
continue;
|
|
}
|
|
else if (pf->url) {
|
|
if (Strcmp_charp(parsedURL2Str(&buf->currentURL), pf->url))
|
|
continue;
|
|
}
|
|
else
|
|
continue;
|
|
for (i = 0; i < buf->formitem->nanchor; i++) {
|
|
a = &buf->formitem->anchors[i];
|
|
fi = (FormItemList *)a->url;
|
|
fl = fi->parent;
|
|
if (pf->name && (!fl->name || strcmp(fl->name, pf->name)))
|
|
continue;
|
|
if (pf->action
|
|
&& (!fl->action || Strcmp_charp(fl->action, pf->action)))
|
|
continue;
|
|
for (pi = pf->item; pi; pi = pi->next) {
|
|
if (pi->type != fi->type)
|
|
continue;
|
|
if (pi->type == FORM_INPUT_SUBMIT ||
|
|
pi->type == FORM_INPUT_IMAGE) {
|
|
if ((!pi->name || !*pi->name ||
|
|
(fi->name && !Strcmp_charp(fi->name, pi->name))) &&
|
|
(!pi->value || !*pi->value ||
|
|
(fi->value && !Strcmp_charp(fi->value, pi->value))))
|
|
buf->submit = a;
|
|
continue;
|
|
}
|
|
if (!pi->name || !fi->name || Strcmp_charp(fi->name, pi->name))
|
|
continue;
|
|
switch (pi->type) {
|
|
case FORM_INPUT_TEXT:
|
|
case FORM_INPUT_FILE:
|
|
case FORM_INPUT_PASSWORD:
|
|
case FORM_TEXTAREA:
|
|
fi->value = Strnew_charp(pi->value);
|
|
formUpdateBuffer(a, buf, fi);
|
|
break;
|
|
case FORM_INPUT_CHECKBOX:
|
|
if (pi->value && fi->value &&
|
|
!Strcmp_charp(fi->value, pi->value)) {
|
|
fi->checked = pi->checked;
|
|
formUpdateBuffer(a, buf, fi);
|
|
}
|
|
break;
|
|
case FORM_INPUT_RADIO:
|
|
if (pi->value && fi->value &&
|
|
!Strcmp_charp(fi->value, pi->value))
|
|
formRecheckRadio(a, buf, fi);
|
|
break;
|
|
#ifdef MENU_SELECT
|
|
case FORM_SELECT:
|
|
for (j = 0, opt = fi->select_option; opt != NULL;
|
|
j++, opt = opt->next) {
|
|
if (pi->value && opt->value &&
|
|
!Strcmp_charp(opt->value, pi->value)) {
|
|
fi->selected = j;
|
|
fi->value = opt->value;
|
|
fi->label = opt->label;
|
|
updateSelectOption(fi, fi->select_option);
|
|
formUpdateBuffer(a, buf, fi);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|