/** * */ package com.grendelscan.commons.html; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; import org.apache.http.HttpHeaders; import org.apache.http.client.methods.HttpPost; import org.cobra_grendel.html.FormInput; import org.cobra_grendel.html.domimpl.HTMLBaseInputElement; import org.cobra_grendel.html.domimpl.HTMLButtonElementImpl; import org.cobra_grendel.html.domimpl.HTMLFormElementImpl; import org.cobra_grendel.html.domimpl.HTMLInputElementImpl; import org.cobra_grendel.html.domimpl.HTMLOptionElementImpl; import org.cobra_grendel.html.domimpl.HTMLSelectElementImpl; import org.cobra_grendel.html.domimpl.HTMLTextAreaElementImpl; import org.cobra_grendel.html.js.Executor; import org.mozilla.javascript.Function; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.NodeList; import org.w3c.dom.html2.HTMLElement; import org.w3c.dom.html2.HTMLFormElement; import org.w3c.dom.html2.HTMLOptGroupElement; import com.grendelscan.commons.FileUtils; import com.grendelscan.commons.StringUtils; import com.grendelscan.commons.collections.CollectionUtils; import com.grendelscan.commons.http.HttpConstants; import com.grendelscan.commons.http.URIStringUtils; import com.grendelscan.commons.http.dataHandling.containers.HtmlQueryContainer; import com.grendelscan.commons.http.dataHandling.containers.TransactionContainer; import com.grendelscan.commons.http.transactions.StandardHttpTransaction; import com.grendelscan.commons.http.transactions.UnrequestableTransaction; /** * @author david * */ public class HtmlFormUtils { private static final Logger LOGGER = LoggerFactory.getLogger(HtmlFormUtils.class); private static final String[] MODIFABLE_INPUT_TAG_NAMES = new String[] { "INPUT", "TEXTAREA", "SELECT" }; private static final String[] ALL_INPUT_TAG_NAMES = new String[] { "INPUT", "TEXTAREA", "SELECT", "BUTTON" }; private static String buildAction(final StandardHttpTransaction originalTransaction, final HTMLFormElementImpl form) throws UnrequestableTransaction { String action = URIStringUtils.cleanupWhitespace(form.getAction()); try { if (action == null || action.isEmpty()) { action = originalTransaction.getRequestWrapper().getURI(); } if (!URIStringUtils.isAbsolute(action)) { URI base = new URI(form.getBaseURI()); // Need to use this instead of the document URL. May not be the same action = base.resolve(action).toASCIIString(); } } catch (URISyntaxException e) { LOGGER.warn("Problem with form action (" + action + "): " + e.toString(), e); throw new UnrequestableTransaction("URL format problem", e); } return action; } private static void buildQuery(final StandardHttpTransaction transaction, final HTMLFormElementImpl form) { ArrayList<FormInput> formInputs = new ArrayList<FormInput>(); for (HTMLBaseInputElement element : getAllQueryElements(form)) { formInputs.addAll(CollectionUtils.arrayToCollection(element.getFormInputs())); } for (FormInput input : formInputs) { java.io.File file = input.getFileValue(); String value; if (file == null) { value = input.getTextValue(); } else { value = FileUtils.readFile(file.getAbsolutePath()); } TransactionContainer transactionContainer = transaction.getTransactionContainer(); HtmlQueryContainer<?> htmlQueryContainer = transactionContainer.getOrCreateHtmlQueryContainer(); htmlQueryContainer.addParameter(input.getName(), value); } } private static StandardHttpTransaction buildRequest(final TransactionSource source, final StandardHttpTransaction originalTransaction, final HTMLFormElementImpl form, final int testJobId) throws UnrequestableTransaction { String method = form.getMethod().toUpperCase(); if (method == null || method.isEmpty()) { method = "GET"; } String action = buildAction(originalTransaction, form); StandardHttpTransaction transaction = originalTransaction.cloneForReferer(source, testJobId); transaction.getRequestWrapper().setMethod(method); transaction.getRequestWrapper().setURI(action, true); setEncodingType(transaction, method, form); buildQuery(transaction, form); return transaction; } public static HTMLFormElementImpl findForm(final Document doc, final String formHash) { NodeList forms = doc.getElementsByTagName("FORM"); for (int index = 0; index < forms.getLength(); index++) { HTMLFormElementImpl form = (HTMLFormElementImpl) forms.item(index); if (getFormHash(form).equals(formHash)) { return form; } } return null; } public static List<HTMLBaseInputElement> getAllQueryElements(final HTMLFormElementImpl form) { List<HTMLBaseInputElement> elements = new ArrayList<HTMLBaseInputElement>(1); for (String name : ALL_INPUT_TAG_NAMES) { NodeList nodes = form.getElementsByTagName(name); for (int index = 0; index < nodes.getLength(); index++) { HTMLBaseInputElement element = (HTMLBaseInputElement) nodes.item(index); elements.add(element); } } return elements; } public static String getFormHash(final HTMLFormElement form) { List<Class<? extends HTMLElement>> formElements = new ArrayList<Class<? extends HTMLElement>>(); formElements.add(HTMLButtonElementImpl.class); formElements.add(HTMLInputElementImpl.class); formElements.add(HTMLSelectElementImpl.class); formElements.add(HTMLTextAreaElementImpl.class); formElements.add(HTMLBaseInputElement.class); formElements.add(HTMLFormElementImpl.class); formElements.add(HTMLOptGroupElement.class); formElements.add(HTMLOptionElementImpl.class); return StringUtils.md5Hash(HtmlNodeWriter.write(form, true, formElements)); } public static List<HTMLInputElementImpl> getInputElements(final HTMLFormElementImpl form) { List<HTMLInputElementImpl> elements = new ArrayList<HTMLInputElementImpl>(1); NodeList nodes = form.getElementsByTagName("INPUT"); for (int index = 0; index < nodes.getLength(); index++) { HTMLInputElementImpl element = (HTMLInputElementImpl) nodes.item(index); if (element.getName() == null || element.getName().isEmpty()) { continue; // Can't use } elements.add(element); } return elements; } public static List<HTMLBaseInputElement> getModifiableQueryElements(final HTMLFormElementImpl form) { List<HTMLBaseInputElement> elements = new ArrayList<HTMLBaseInputElement>(1); for (String name : MODIFABLE_INPUT_TAG_NAMES) { NodeList nodes = form.getElementsByTagName(name); for (int index = 0; index < nodes.getLength(); index++) { HTMLBaseInputElement element = (HTMLBaseInputElement) nodes.item(index); elements.add(element); } } return elements; } private static void setEncodingType(final StandardHttpTransaction transaction, final String method, final HTMLFormElementImpl form) { if (method.equals(HttpPost.METHOD_NAME)) { if (form.getEnctype() == null || form.getEnctype().isEmpty()) { transaction.getRequestWrapper().getHeaders().addHeader(HttpHeaders.CONTENT_TYPE, HttpConstants.ENCODING_APPLICATION_X_WWW_FORM_URLENCODED); } else if (form.getEnctype().equalsIgnoreCase(HttpConstants.ENCODING_MULTIPART_FORM_DATA) || form.getEnctype().equalsIgnoreCase(HttpConstants.ENCODING_MULTIPART_FORM_DATA) || form.getEnctype().equalsIgnoreCase(HttpConstants.ENCODING_APPLICATION_X_WWW_FORM_URLENCODED)) { transaction.getRequestWrapper().getHeaders().addHeader(HttpHeaders.CONTENT_TYPE, form.getEnctype().toLowerCase()); } else { IllegalStateException e = new IllegalStateException("Unknown encoding type: " + form.getEnctype()); LOGGER.error(e.toString(), e); throw e; } } } public static StandardHttpTransaction submitForm(final TransactionSource source, final StandardHttpTransaction originalTransaction, final HTMLFormElementImpl form, final int testJobId) throws UnrequestableTransaction { Function onsubmit = form.getOnsubmit(); if (onsubmit != null) { if (!Executor.executeFunction(form, onsubmit, null)) { LOGGER.info("The onsubmit handler failed, but we're submitting the form anyway"); } } StandardHttpTransaction transaction = buildRequest(source, originalTransaction, form, testJobId); return transaction; } }