]> git.lizzy.rs Git - plan9front.git/blobdiff - sys/src/cmd/mothra/forms.c
exec(2): fix prototypes
[plan9front.git] / sys / src / cmd / mothra / forms.c
index 9ce604efb2f1d7a3b15519e4c1bf259b390250b9..90cfe4683699429d1df4bfdedab1b8b924e67485 100644 (file)
@@ -1,6 +1,3 @@
-/*
- * type=image is treated like submit
- */
 #include <u.h>
 #include <libc.h>
 #include <draw.h>
@@ -8,10 +5,12 @@
 #include <panel.h>
 #include "mothra.h"
 #include "html.h"
+
 typedef struct Field Field;
 typedef struct Option Option;
 struct Form{
        int method;
+       char *ctype;
        char *action;
        Field *fields, *efields;
        Form *next;
@@ -48,6 +47,7 @@ enum{
        TEXTWIN,
        HIDDEN,
        INDEX,
+       FILE,
 };
 struct Option{
        int selected;
@@ -56,23 +56,23 @@ struct Option{
        char *value;
        Option *next;
 };
+
+#define BOUNDARY "nAboJ9uN6ZXsqoVGzLAdjKq97TWDTGjo"
+
 void h_checkinput(Panel *, int, int);
 void h_radioinput(Panel *, int, int);
 void h_submitinput(Panel *, int);
 void h_buttoninput(Panel *, int);
+void h_fileinput(Panel *, int);
 void h_submittype(Panel *, char *);
 void h_submitindex(Panel *, char *);
 void h_resetinput(Panel *, int);
 void h_select(Panel *, int, int);
-void h_cut(Panel *, int);
-void h_paste(Panel *, int);
-void h_snarf(Panel *, int);
-void h_edit(Panel *);
 char *selgen(Panel *, int);
 char *nullgen(Panel *, int);
 Field *newfield(Form *form){
        Field *f;
-       f=emallocz(sizeof(Field), 1);
+       f=emalloc(sizeof(Field));
        if(form->efields==0)
                form->fields=f;
        else
@@ -99,11 +99,11 @@ void rdform(Hglob *g){
                        htmlerror(g->name, g->lineno, "nested forms illegal\n");
                        break;
                }
-               g->form=emallocz(sizeof(Form), 1);
+               g->form=emalloc(sizeof(Form));
                s=pl_getattr(g->attr, "action");
-               g->form->action=strdup((s && s[0]) ? s : g->dst->url->fullname);
+               g->form->action=strdup((s && *s) ? s : g->dst->url->fullname);
                s=pl_getattr(g->attr, "method");
-               if(s==0)
+               if(s==0 || *s==0)
                        g->form->method=GET;
                else if(cistrcmp(s, "post")==0)
                        g->form->method=POST;
@@ -113,23 +113,31 @@ void rdform(Hglob *g){
                                        "unknown form method %s\n", s);
                        g->form->method=GET;
                }
+               s=pl_getattr(g->attr, "enctype");
+               if(s && cistrcmp(s, "multipart/form-data")==0)
+                       g->form->ctype = "multipart/form-data; boundary=" BOUNDARY;
                g->form->fields=0;
 
                g->form->next = g->dst->form;
                g->dst->form = g->form;
-
                break;
        case Tag_input:
        case Tag_button:
                if(g->form==0){
-               BadTag:
-                       htmlerror(g->name, g->lineno, "<%s> not in form, ignored\n",
-                               tag[g->tag].name);
-                       break;
-               }
-               f=newfield(g->form);
+                       /* no form, assume link button */
+                       form = emalloc(sizeof(Form));   
+                       form->method = 0;
+                       form->fields = 0;
+                       form->efields = 0;
+                       if(g->state->link[0])
+                               form->action = strdup(g->state->link);
+                       form->next = g->dst->form;
+                       g->dst->form = form;
+                       f=newfield(form);
+               } else
+                       f=newfield(g->form);
                s=pl_getattr(g->attr, "name");
-               if(s==0)
+               if(s==0 || *s == 0)
                        f->name=0;
                else
                        f->name=strdup(s);
@@ -140,26 +148,62 @@ void rdform(Hglob *g){
                        f->value=strdup(s);
                f->checked=pl_hasattr(g->attr, "checked");
                s=pl_getattr(g->attr, "size");
-               if(s==0)
+               if(s==0 || *s==0)
                        f->size=20;
                else
                        f->size=atoi(s);
                s=pl_getattr(g->attr, "maxlength");
-               if(s==0)
+               if(s==0 || *s==0)
                        f->maxlength=0x3fffffff;
                else
                        f->maxlength=atoi(s);
                s=pl_getattr(g->attr, "type");
-               if(g->tag == Tag_button && (s==0 || cistrcmp(s, "reset") || cistrcmp(s, "button")))
-                       s = "submit";
-               if(s==0 || cistrcmp(s, "text")==0 || 
-                  cistrcmp(s, "password")==0 || cistrcmp(s, "int")==0 ||
-                  cistrcmp(s, "email")==0){
-                       s=pl_getattr(g->attr, "name");
-                       if(s!=0 && strcmp(s, "isindex")==0)
+               if((g->tag == Tag_button) && 
+                  (s==0 || cistrcmp(s, "reset") || cistrcmp(s, "button")))
+                       s="submit";
+               else if(s==0)
+                       s="text";
+               if(cistrcmp(s, "checkbox")==0)
+                       f->type=CHECK;
+               else if(cistrcmp(s, "radio")==0)
+                       f->type=RADIO;
+               else if(cistrcmp(s, "submit")==0)
+                       f->type=SUBMIT;
+               else if(cistrcmp(s, "image")==0){
+                       f->type=SUBMIT;
+                       s=pl_getattr(g->attr, "src");
+                       if(s && *s)
+                               nstrcpy(g->state->image, s, sizeof(g->state->image));
+                       s=pl_getattr(g->attr, "width");
+                       if(s && *s)
+                               g->state->width=strtolength(g, HORIZ, s);
+                       s=pl_getattr(g->attr, "height");
+                       if(s && *s)
+                               g->state->height=strtolength(g, VERT, s);
+                       s=pl_getattr(g->attr, "alt");
+                       if(s==0 || *s == 0) s = f->value;
+                       pl_htmloutput(g, g->nsp, s, f);
+                       g->state->image[0] = 0;
+                       g->state->width=0;
+                       g->state->height=0;
+                       break;
+               }
+               else if(cistrcmp(s, "button")==0)
+                       f->type=BUTTON;
+               else if(cistrcmp(s, "file")==0)
+                       f->type=FILE;
+               else if(cistrcmp(s, "reset")==0)
+                       f->type=RESET;
+               else if(cistrcmp(s, "hidden")==0)
+                       f->type=HIDDEN;
+               else{
+                       f->type=TYPEIN;
+                       if(cistrcmp(s, "password")==0)
+                               f->type=PASSWD;
+                       s=f->name;
+                       if(s && cistrcmp(s, "isindex")==0)
                                f->type=INDEX;
-                       else
-                               f->type=TYPEIN;
+
                        /*
                         * If there's exactly one attribute, use its value as the name,
                         * regardless of the attribute name.  This makes
@@ -172,27 +216,6 @@ void rdform(Hglob *g){
                                        f->name=strdup("no-name");
                        }
                }
-               else if(cistrcmp(s, "checkbox")==0)
-                       f->type=CHECK;
-               else if(cistrcmp(s, "radio")==0)
-                       f->type=RADIO;
-               else if(cistrcmp(s, "submit")==0)
-                       f->type=SUBMIT;
-               else if(cistrcmp(s, "button")==0)
-                       f->type=BUTTON;
-               else if(cistrcmp(s, "image")==0){
-                       /* presotto's egregious hack to make image submits do something */
-                       if(f->name){
-                               free(f->name);
-                               f->name=0;
-                       }
-                       f->type=SUBMIT;
-               } else if(cistrcmp(s, "reset")==0)
-                       f->type=RESET;
-               else if(cistrcmp(s, "hidden")==0)
-                       f->type=HIDDEN;
-               else
-                       f->type=TYPEIN;
                if((f->type==CHECK || f->type==RADIO) && !pl_hasattr(g->attr, "value")){
                        free(f->value);
                        f->value=strdup("on");
@@ -201,23 +224,23 @@ void rdform(Hglob *g){
                        pl_htmloutput(g, g->nsp, f->value[0]?f->value:"blank field", f);
                break;
        case Tag_select:
-               if(g->form==0) goto BadTag;
+               if(g->form==0){
+               BadTag:
+                       htmlerror(g->name, g->lineno, "<%s> not in form, ignored\n",
+                               tag[g->tag].name);
+                       break;
+               }
                f=newfield(g->form);
                s=pl_getattr(g->attr, "name");
-               if(s==0){
+               if(s==0 || *s==0){
                        f->name=strdup("select");
                        htmlerror(g->name, g->lineno, "select has no name=\n");
                }
                else
                        f->name=strdup(s);
-               s=pl_getattr(g->attr, "size");
-               if(s==0) f->size=4;
-               else{
-                       f->size=atoi(s);
-                       if(f->size<=0) f->size=1;
-               }
                f->multiple=pl_hasattr(g->attr, "multiple");
                f->type=SELECT;
+               f->size=0;
                f->options=0;
                g->text=g->token;
                g->tp=g->text;
@@ -226,7 +249,9 @@ void rdform(Hglob *g){
        case Tag_option:
                if(g->form==0) goto BadTag;
                if((f=g->form->efields)==0) goto BadTag;
-               o=emallocz(sizeof(Option), 1);
+               if(f->size<8)
+                       f->size++;
+               o=emalloc(sizeof(Option));
                for(op=&f->options;*op;op=&(*op)->next);
                *op=o;
                o->next=0;
@@ -237,6 +262,8 @@ void rdform(Hglob *g){
                *g->tp++=' ';
                o->def=pl_hasattr(g->attr, "selected");
                o->selected=o->def;
+               if(pl_hasattr(g->attr, "disabled"))
+                       o->selected=0;
                s=pl_getattr(g->attr, "value");
                if(s==0)
                        o->value=o->label+1;
@@ -247,16 +274,16 @@ void rdform(Hglob *g){
                if(g->form==0) goto BadTag;
                f=newfield(g->form);
                s=pl_getattr(g->attr, "name");
-               if(s==0){
+               if(s==0 || *s==0){
                        f->name=strdup("enter text");
                        htmlerror(g->name, g->lineno, "select has no name=\n");
                }
                else
                        f->name=strdup(s);
                s=pl_getattr(g->attr, "rows");
-               f->rows=s?atoi(s):8;
+               f->rows=(s && *s)?atoi(s):8;
                s=pl_getattr(g->attr, "cols");
-               f->cols=s?atoi(s):30;
+               f->cols=(s && *s)?atoi(s):30;
                f->type=TEXTWIN;
                /* suck up initial text */
                pl_htmloutput(g, g->nsp, f->name, f);
@@ -271,7 +298,7 @@ void rdform(Hglob *g){
                form->fields=0;
                form->efields=0;
                s=pl_getattr(g->attr, "action");
-               form->action=strdup((s && s[0]) ? s : g->dst->url->fullname);
+               form->action=strdup((s && *s) ? s : g->dst->url->fullname);
                form->method=GET;
                form->fields=0;
                f=newfield(form);
@@ -341,6 +368,9 @@ void mkfieldpanel(Rtext *t){
        case TYPEIN:
                f->p=plentry(0, 0, f->size*chrwidth, f->value, h_submittype);
                break;
+       case PASSWD:
+               f->p=plentry(0, USERFL, f->size*chrwidth, f->value, h_submittype);
+               break;
        case CHECK:
                f->p=plcheckbutton(0, 0, "", h_checkinput);
                f->state=f->checked;
@@ -360,7 +390,12 @@ void mkfieldpanel(Rtext *t){
        case BUTTON:
                f->p=plbutton(0, 0, f->value[0]?f->value:"button", h_buttoninput);
                break;
+       case FILE:
+               f->p=plbutton(0, 0, f->value[0]?f->value:"file", h_fileinput);
+               break;
        case SELECT:
+               if(f->size <= 0)
+                       f->size=1;
                f->pulldown=plgroup(0,0);
                scrl=plscrollbar(f->pulldown, PACKW|FILLY);
                win=pllist(f->pulldown, PACKN, nullgen, f->size, h_select);
@@ -372,20 +407,11 @@ void mkfieldpanel(Rtext *t){
                f->p->fixedsize.x=f->pulldown->r.max.x-f->pulldown->r.min.x;
                break;
        case TEXTWIN:
-               menu=plgroup(0,0);
                f->p=plframe(0,0);
                pllabel(f->p, PACKN|FILLX, f->name);
                scrl=plscrollbar(f->p, PACKW|FILLY);
-               pop=plpopup(f->p, PACKN|FILLX, 0, menu, 0);
-               f->textwin=pledit(pop, EXPAND, Pt(f->cols*chrwidth, f->rows*font->height),
-                       0, 0, h_edit);
+               f->textwin=pledit(f->p, EXPAND, Pt(f->cols*chrwidth, f->rows*font->height), 0, 0, 0);
                f->textwin->userp=f;
-               button=plbutton(menu, PACKN|FILLX, "cut", h_cut);
-               button->userp=f->textwin;
-               button=plbutton(menu, PACKN|FILLX, "paste", h_paste);
-               button->userp=f->textwin;
-               button=plbutton(menu, PACKN|FILLX, "snarf", h_snarf);
-               button->userp=f->textwin;
                plscroll(f->textwin, 0, scrl);
                break;
        case INDEX:
@@ -397,7 +423,7 @@ void mkfieldpanel(Rtext *t){
                free(t->text);
                t->text=0;
                t->p=f->p;
-               t->hot=1;
+               t->flags|=PL_HOT;
        }
 }
 void h_checkinput(Panel *p, int, int v){
@@ -433,9 +459,18 @@ void h_resetinput(Panel *p, int){
        Option *o;
        for(f=((Field *)p->userp)->form->fields;f;f=f->next) switch(f->type){
        case TYPEIN:
-       case PASSWD:
                plinitentry(f->p, 0, f->size*chrwidth, f->value, 0);
                break;
+       case PASSWD:
+               plinitentry(f->p, USERFL, f->size*chrwidth, f->value, 0);
+               break;
+       case FILE:
+               free(f->value);
+               f->value=strdup("");
+               if(f->p==nil) break;
+               f->p->state=0;
+               pldraw(f->p, screen);
+               break;
        case CHECK:
        case RADIO:
                f->state=f->checked;
@@ -448,80 +483,33 @@ void h_resetinput(Panel *p, int){
        }
        pldraw(text, screen);
 }
+
 void h_buttoninput(Panel *p, int){
+       Field *f;
+
+       f = p->userp;
+       if(f && f->form && f->form->method != POST && f->form->action)
+               geturl(f->form->action, -1, 0, 0);
 }
-void h_edit(Panel *p){
-       plgrabkb(p);
-}
-Rune *snarfbuf=0;
-int nsnarfbuf=0;
-void h_snarf(Panel *p, int){
-       int s0, s1;
-       Rune *text;
-       p=p->userp;
-       plegetsel(p, &s0, &s1);
-       if(s0==s1) return;
-       text=pleget(p);
-       if(snarfbuf) free(snarfbuf);
-       nsnarfbuf=s1-s0;
-       snarfbuf=malloc(nsnarfbuf*sizeof(Rune));
-       if(snarfbuf==0){
-               fprint(2, "No mem\n");
-               exits("no mem");
-       }
-       memmove(snarfbuf, text+s0, nsnarfbuf*sizeof(Rune));
-}
-void h_cut(Panel *p, int b){
-       h_snarf(p, b);
-       plepaste(p->userp, 0, 0);
-}
-void h_paste(Panel *p, int){
-       plepaste(p->userp, snarfbuf, nsnarfbuf);
-}
-int ulen(char *s){
-       int len;
-       len=0;
-       for(;*s;s++){
-               if(strchr("/$-_@.!*'(), ", *s)
-               || 'a'<=*s && *s<='z'
-               || 'A'<=*s && *s<='Z'
-               || '0'<=*s && *s<='9')
-                       len++;
-               else
-                       len+=3;
-       }
-       return len;
-}
-int hexdigit(int v){
-       return 0<=v && v<=9?'0'+v:'A'+v-10;
-}
-char *ucpy(char *buf, char *s){
-       for(;*s;s++){
-               if(strchr("/$-_@.!*'(),", *s)
-               || 'a'<=*s && *s<='z'
-               || 'A'<=*s && *s<='Z'
-               || '0'<=*s && *s<='9')
-                       *buf++=*s;
-               else if(*s==' ')
-                       *buf++='+';
-               else{
-                       *buf++='%';
-                       *buf++=hexdigit((*s>>4)&15);
-                       *buf++=hexdigit(*s&15);
-               }
-       }
-       *buf='\0';
-       return buf;
-}
-char *runetou(char *buf, Rune r){
-       char rbuf[2];
-       if(r<=255){
-               rbuf[0]=r;
-               rbuf[1]='\0';
-               buf=ucpy(buf, rbuf);
+
+void h_fileinput(Panel *p, int){
+       char name[NNAME];
+       Field *f;
+
+       f = p->userp;
+       nstrcpy(name, f->value, sizeof(name));
+       for(;;){
+               if(eenter("Upload file", name, sizeof(name), &mouse) <= 0)
+                       break;
+               if(access(name, AREAD) == 0)
+                       break;
        }
-       return buf;
+       free(f->value);
+       f->value = strdup(name);
+       p->state = name[0] != 0;
+       pldraw(f->p, screen);
 }
+
 /*
  * If there's exactly one button with type=text, then
  * a CR in the button is supposed to submit the form.
@@ -530,110 +518,198 @@ void h_submittype(Panel *p, char *){
        int ntype;
        Field *f;
        ntype=0;
-       for(f=((Field *)p->userp)->form->fields;f;f=f->next) if(f->type==TYPEIN) ntype++;
+       for(f=((Field *)p->userp)->form->fields;f;f=f->next)
+               if(f->type==TYPEIN || f->type==PASSWD)
+                       ntype++;
        if(ntype==1) h_submitinput(p, 0);
 }
 void h_submitindex(Panel *p, char *){
        h_submitinput(p, 0);
 }
-void h_submitinput(Panel *p, int){
-       Form *form;
-       int size, nrune;
-       char *buf, *bufp, sep;
-       Rune *rp;
-       Field *f;
+
+void mencodeform(Form *form, int fd){
+       char *b, *p, *sep;
+       int ifd, n, nb;
        Option *o;
-       form=((Field *)p->userp)->form;
-       if(form->method==GET) size=ulen(form->action)+1;
-       else size=1;
-       for(f=form->fields;f;f=f->next) switch(f->type){
+       Field *f;
+       Rune *rp;
+
+       sep = "--" BOUNDARY;
+       for(f=form->fields;f;f=f->next)switch(f->type){
        case TYPEIN:
        case PASSWD:
-               size+=ulen(f->name)+1+ulen(plentryval(f->p))+1;
-               break;
-       case INDEX:
-               size+=ulen(plentryval(f->p))+1;
+               fprint(fd, "%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s",
+                       sep, f->name, plentryval(f->p));
+               sep = "\r\n--" BOUNDARY;
                break;
        case CHECK:
        case RADIO:
+       case SUBMIT:
                if(!f->state) break;
        case HIDDEN:
-               size+=ulen(f->name)+1+ulen(f->value)+1;
+               if(f->name==0 || f->value==0)
+                       break;
+               fprint(fd, "%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s",
+                       sep, f->name, f->value);
+               sep = "\r\n--" BOUNDARY;
                break;
        case SELECT:
+               if(f->name==0) break;
                for(o=f->options;o;o=o->next)
-                       if(o->selected)
-                               size+=ulen(f->name)+1+ulen(o->value)+1;
+                       if(o->selected && o->value){
+                               fprint(fd, "%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s",
+                                       sep, f->name, o->value);
+                               sep = "\r\n--" BOUNDARY;
+                       }
                break;
        case TEXTWIN:
-               size+=ulen(f->name)+1+plelen(f->textwin)*3+1;
+               if(f->name==0) break;
+               n=plelen(f->textwin);
+               rp=pleget(f->textwin);
+               p=b=malloc(UTFmax*n+1);
+               if(b == nil)
+                       break;
+               while(n > 0){
+                       p += runetochar(p, rp);
+                       rp++;
+                       n--;
+               }
+               *p = 0;
+               fprint(fd, "%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s",
+                       sep, f->name, b);
+               sep = "\r\n--" BOUNDARY;
+               free(b);
+               break;
+       case FILE:
+               if(f->name==0 || f->value[0]==0)
+                       break;
+               if(p = strrchr(f->value, '/'))
+                       p++;
+               if(p == 0 || *p == 0)
+                       p = f->value;
+               if((b = malloc(nb = 8192)) == nil)
+                       break;
+               if((ifd = open(f->value, OREAD)) >= 0){
+                       if(filetype(ifd, b, nb) < 0)
+                               strcpy(b, "application/octet-stream");
+                       fprint(fd, "%s\r\nContent-Disposition: form-data; name=\"%s\"; filename=\"%s\""
+                               "\r\nContent-Type: %s\r\n\r\n", sep, f->name, p, b);
+                       sep = "\r\n--" BOUNDARY;
+                       while((n = read(ifd, b, nb)) > 0)
+                               if(write(fd, b, n) != n)
+                                       break;
+                       close(ifd);
+               }
+               free(b);
                break;
        }
-       buf=emalloc(size);
-       if(form->method==GET){
-               strcpy(buf, form->action);
-               sep='?';
-       }
-       else{
-               buf[0]='\0';
-               sep=0;
-       }
-       bufp=buf+strlen(buf);
-       if(form->method==GET && bufp!=buf && bufp[-1]=='?') *--bufp='\0'; /* spurious ? */
+       fprint(fd, "%s--\r\n", sep);
+}
+
+void uencodeform(Form *form, int fd){
+       char *b, *p, *sep;
+       Option *o;
+       Field *f;
+       Rune *rp;
+       int n;
+
+       sep = "";
        for(f=form->fields;f;f=f->next) switch(f->type){
        case TYPEIN:
        case PASSWD:
-               if(sep) *bufp++=sep;
-               sep='&';
-               bufp=ucpy(bufp, f->name);
-               *bufp++='=';
-               bufp=ucpy(bufp, plentryval(f->p));
+               fprint(fd, "%s%U=%U", sep, f->name, plentryval(f->p));
+               sep = "&";
                break;
        case INDEX:
-               if(sep) *bufp++=sep;
-               sep='&';
-               bufp=ucpy(bufp, plentryval(f->p));
+               fprint(fd, "%s%U", sep, plentryval(f->p));
+               sep = "&";
                break;
        case CHECK:
        case RADIO:
+       case SUBMIT:
                if(!f->state) break;
        case HIDDEN:
-               if(sep) *bufp++=sep;
-               sep='&';
-               bufp=ucpy(bufp, f->name);
-               *bufp++='=';
-               bufp=ucpy(bufp, f->value);
+               if(f->name==0 || f->value==0)
+                       break;
+               fprint(fd, "%s%U=%U", sep, f->name, f->value);
+               sep = "&";
                break;
        case SELECT:
+               if(f->name==0) break;
                for(o=f->options;o;o=o->next)
-                       if(o->selected){
-                               if(sep) *bufp++=sep;
-                               sep='&';
-                               bufp=ucpy(bufp, f->name);
-                               *bufp++='=';
-                               bufp=ucpy(bufp, o->value);
+                       if(o->selected && o->value){
+                               fprint(fd, "%s%U=%U", sep, f->name, o->value);
+                               sep = "&";
                        }
                break;
        case TEXTWIN:
-               if(sep) *bufp++=sep;
-               sep='&';
-               bufp=ucpy(bufp, f->name);
-               *bufp++='=';
+               if(f->name==0) break;
+               n=plelen(f->textwin);
                rp=pleget(f->textwin);
-               for(nrune=plelen(f->textwin);nrune!=0;--nrune)
-                       bufp=runetou(bufp, *rp++);
-               *bufp='\0';
+               p=b=malloc(UTFmax*n+1);
+               if(b == nil)
+                       break;
+               while(n > 0){
+                       p += runetochar(p, rp);
+                       rp++;
+                       n--;
+               }
+               *p = 0;
+               fprint(fd, "%s%U=%U", sep, f->name, b);
+               sep = "&";
+               free(b);
                break;
        }
-       if(form->method==GET){
-               if(debug)fprint(2, "GET %s\n", buf);
-               geturl(buf, GET, 0, 0, 0);
+}
+
+void h_submitinput(Panel *p, int){
+       char buf[NNAME];
+       Form *form;
+       Field *f;
+       int n, fd;
+
+       f = p->userp;
+       form=f->form;
+       for(f=form->fields;f;f=f->next)
+               if(f->type==SUBMIT)
+                       f->state = (f->p == p);
+
+       switch(form->method){
+       case GET:
+               strcpy(buf, "/tmp/mfXXXXXXXXXXX");
+               fd = create(mktemp(buf), ORDWR|ORCLOSE, 0600);
+               break;
+       case POST:
+               fd = urlpost(selurl(form->action), form->ctype);
+               break;
+       default:
+               return;
+       }
+
+       if(fd < 0){
+               message("submit: %r");
+               return;
        }
-       else{
-               if(debug)fprint(2, "POST %s: %s\n", form->action, buf);
-               geturl(form->action, POST, buf, 0, 0);
+       if(form->method==GET){
+               fprint(fd, "%s?", form->action);
+               uencodeform(form, fd);
+               seek(fd, 0, 0);
+               n = readn(fd, buf, sizeof(buf));
+               close(fd);
+               if(n < 0 || n >= sizeof(buf)){
+                       message("submit: too large");
+                       return;
+               }
+               buf[n] = 0;
+               geturl(buf, -1, 0, 0);
+       } else {
+               /* only set for multipart/form-data */
+               if(form->ctype)
+                       mencodeform(form, fd);
+               else
+                       uencodeform(form, fd);
+               geturl(form->action, fd, 0, 0);
        }
-       free(buf);
 }
 
 void freeform(void *p)
@@ -666,3 +742,19 @@ void freeform(void *p)
                free(form);
        }
 }
+
+int Ufmt(Fmt *f){
+       char *s = va_arg(f->args, char*);
+       for(; *s; s++){
+               if(strchr("/$-_@.!*'(),", *s)
+               || 'a'<=*s && *s<='z'
+               || 'A'<=*s && *s<='Z'
+               || '0'<=*s && *s<='9')
+                       fmtprint(f, "%c", *s);
+               else if(*s==' ')
+                       fmtprint(f, "+");
+               else
+                       fmtprint(f, "%%%.2X", *s & 0xFF);
+       }
+       return 0;
+}