/* * File: form.cc * * Copyright 2008 Jorge Arellano Cid * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. */ #include "form.hh" #include "html_common.hh" #include #include #include "lout/misc.hh" #include "dw/core.hh" #include "dw/textblock.hh" #include "misc.h" #include "msg.h" #include "debug.h" #include "prefs.h" #include "nav.h" #include "uicmd.hh" using namespace dw; using namespace dw::core; using namespace dw::core::style; /* * Forward declarations */ class DilloHtmlReceiver; class DilloHtmlInput; typedef struct _DilloHtmlSelect DilloHtmlSelect; typedef struct _DilloHtmlOption DilloHtmlOption; static Dstr *Html_encode_text(iconv_t encoder, Dstr **input); static void Html_urlencode_append(Dstr *str, const char *val); static void Html_append_input_urlencode(Dstr *data, const char *name, const char *value); static void Html_append_input_multipart_files(Dstr* data, const char *boundary, const char *name, Dstr *file, const char *filename); static void Html_append_input_multipart(Dstr *data, const char *boundary, const char *name, const char *value); static void Html_append_clickpos_urlencode(Dstr *data, Dstr *name, int x,int y); static void Html_append_clickpos_multipart(Dstr *data, const char *boundary, Dstr *name, int x, int y); static void Html_get_input_values(const DilloHtmlInput *input, bool is_active_submit, Dlist *values); static dw::core::ui::Embed *Html_input_image(DilloHtml *html, const char *tag, int tagsize, DilloHtmlForm *form); static void Html_option_finish(DilloHtml *html); /* * Typedefs */ typedef enum { DILLO_HTML_INPUT_UNKNOWN, DILLO_HTML_INPUT_TEXT, DILLO_HTML_INPUT_PASSWORD, DILLO_HTML_INPUT_CHECKBOX, DILLO_HTML_INPUT_RADIO, DILLO_HTML_INPUT_IMAGE, DILLO_HTML_INPUT_FILE, DILLO_HTML_INPUT_BUTTON, DILLO_HTML_INPUT_HIDDEN, DILLO_HTML_INPUT_SUBMIT, DILLO_HTML_INPUT_RESET, DILLO_HTML_INPUT_BUTTON_SUBMIT, DILLO_HTML_INPUT_BUTTON_RESET, DILLO_HTML_INPUT_SELECT, DILLO_HTML_INPUT_SEL_LIST, DILLO_HTML_INPUT_TEXTAREA, DILLO_HTML_INPUT_INDEX } DilloHtmlInputType; /* * Class declarations */ class DilloHtmlForm { friend class DilloHtmlReceiver; DilloHtml *html; void eventHandler(dw::core::ui::Resource *resource, int click_x, int click_y); public: //BUG: for now everything is public DilloHtmlMethod method; DilloUrl *action; DilloHtmlEnc enc; char *submit_charset; lout::misc::SimpleVector *inputs; int num_entry_fields; int num_submit_buttons; DilloHtmlReceiver *form_receiver; public: DilloHtmlForm (DilloHtml *html, DilloHtmlMethod method, const DilloUrl *action, DilloHtmlEnc enc, const char *charset); ~DilloHtmlForm (); DilloHtmlInput *getCurrentInput (); DilloHtmlInput *getInput (dw::core::ui::Resource *resource); DilloHtmlInput *getRadioInput (const char *name); void reset (); void addInput(DilloHtmlInputType type, dw::core::ui::Embed *embed, const char *name, const char *init_str, DilloHtmlSelect *select, bool_t init_val); DilloUrl *buildQueryUrl(DilloHtmlInput *input, int click_x, int click_y); Dstr *buildQueryData(DilloHtmlInput *active_submit, int x, int y); char *makeMultipartBoundary(iconv_t encoder, DilloHtmlInput *active_submit); }; class DilloHtmlReceiver: public dw::core::ui::Resource::ActivateReceiver, public dw::core::ui::ButtonResource::ClickedReceiver { friend class DilloHtmlForm; DilloHtmlForm* form; DilloHtmlReceiver (DilloHtmlForm* form2) { form = form2; } ~DilloHtmlReceiver () { } void activate (dw::core::ui::Resource *resource); void clicked (dw::core::ui::ButtonResource *resource, int buttonNo, int x, int y); }; struct _DilloHtmlOption { char *value, *content; bool selected, enabled; }; struct _DilloHtmlSelect { lout::misc::SimpleVector *options; }; class DilloHtmlInput { // DilloHtmlForm::addInput() calls connectTo() friend class DilloHtmlForm; public: //BUG: for now everything is public DilloHtmlInputType type; dw::core::ui::Embed *embed; /* May be NULL (think: hidden input) */ char *name; char *init_str; /* note: some overloading - for buttons, init_str is simply the value of the button; for text entries, it is the initial value */ DilloHtmlSelect *select; bool_t init_val; /* only meaningful for buttons */ Dstr *file_data; /* only meaningful for file inputs. todo: may become a list... */ private: void connectTo(DilloHtmlReceiver *form_receiver); public: DilloHtmlInput (DilloHtmlInputType type, dw::core::ui::Embed *embed, const char *name, const char *init_str, DilloHtmlSelect *select, bool_t init_val); ~DilloHtmlInput (); void reset(); }; /* * Form API */ DilloHtmlForm *a_Html_form_new (DilloHtml *html, DilloHtmlMethod method, const DilloUrl *action, DilloHtmlEnc enc, const char *charset) { return new DilloHtmlForm (html,method,action,enc,charset); } void a_Html_form_delete (DilloHtmlForm *form) { delete form; } /* * Form parsing functions */ /* * Handle
tag */ void Html_tag_open_form(DilloHtml *html, const char *tag, int tagsize) { DilloUrl *action; DilloHtmlMethod method; DilloHtmlEnc enc; char *charset, *first; const char *attrbuf; DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style); if (html->InFlags & IN_FORM) { BUG_MSG("nested forms\n"); return; } html->InFlags |= IN_FORM; html->InFlags &= ~IN_SELECT; html->InFlags &= ~IN_OPTION; html->InFlags &= ~IN_TEXTAREA; method = DILLO_HTML_METHOD_GET; if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "method"))) { if (!dStrcasecmp(attrbuf, "post")) method = DILLO_HTML_METHOD_POST; /* todo: maybe deal with unknown methods? */ } if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "action"))) action = a_Html_url_new(html, attrbuf, NULL, 0, 0, 0, 0); else action = a_Url_dup(html->base_url); enc = DILLO_HTML_ENC_URLENCODING; if ((method == DILLO_HTML_METHOD_POST) && ((attrbuf = a_Html_get_attr(html, tag, tagsize, "enctype")))) { if (!dStrcasecmp(attrbuf, "multipart/form-data")) enc = DILLO_HTML_ENC_MULTIPART; } charset = NULL; first = NULL; if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "accept-charset"))) { /* a list of acceptable charsets, separated by commas or spaces */ char *ptr = first = dStrdup(attrbuf); while (ptr && !charset) { char *curr = dStrsep(&ptr, " ,"); if (!dStrcasecmp(curr, "utf-8")) { charset = curr; } else if (!dStrcasecmp(curr, "UNKNOWN")) { /* defined to be whatever encoding the document is in */ charset = html->charset; } } if (!charset) charset = first; } if (!charset) charset = html->charset; html->formNew(method, action, enc, charset); dFree(first); a_Url_free(action); } void Html_tag_close_form(DilloHtml *html, int TagIdx) { static const char *SubmitTag = ""; DilloHtmlForm *form; // int i; if (html->InFlags & IN_FORM) { form = html->getCurrentForm (); /* If we don't have a submit button and the user desires one, let's add a custom one */ if (form->num_submit_buttons == 0) { if (prefs.show_extra_warnings || form->num_entry_fields != 1) BUG_MSG("FORM lacks a Submit button\n"); if (prefs.generate_submit) { BUG_MSG(" (added a submit button internally)\n"); Html_tag_open_input(html, SubmitTag, strlen(SubmitTag)); form->num_submit_buttons = 0; } } // /* Make buttons sensitive again */ // for (i = 0; i < form->inputs->size(); i++) { // input_i = form->inputs->get(i); // /* Check for tricky HTML (e.g. ) */ // if (!input_i->widget) // continue; // if (input_i->type == DILLO_HTML_INPUT_SUBMIT || // input_i->type == DILLO_HTML_INPUT_RESET) { // gtk_widget_set_sensitive(input_i->widget, TRUE); // } else if (input_i->type == DILLO_HTML_INPUT_IMAGE || // input_i->type == DILLO_HTML_INPUT_BUTTON_SUBMIT || // input_i->type == DILLO_HTML_INPUT_BUTTON_RESET) { // a_Dw_button_set_sensitive(DW_BUTTON(input_i->widget), TRUE); // } // } } html->InFlags &= ~IN_FORM; html->InFlags &= ~IN_SELECT; html->InFlags &= ~IN_OPTION; html->InFlags &= ~IN_TEXTAREA; a_Html_pop_tag(html, TagIdx); } /* * Add a new input to current form */ void Html_tag_open_input(DilloHtml *html, const char *tag, int tagsize) { DilloHtmlForm *form; DilloHtmlInputType inp_type; dw::core::ui::Embed *embed = NULL; char *value, *name, *type, *init_str; const char *attrbuf, *label; bool_t init_val = FALSE; if (!(html->InFlags & IN_FORM)) { BUG_MSG(" element outside \n"); return; } if (html->InFlags & IN_SELECT) { BUG_MSG(" element inside element inside