1070 lines
24 KiB
C
1070 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 (l == NULL)
|
|
break;
|
|
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
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|