// jDownloader - Downloadmanager // Copyright (C) 2008 JD-Team support@jdownloader.org // // 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. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. package jd.parser.html; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.Random; import java.util.regex.Matcher; import java.util.regex.Pattern; import jd.http.requests.RequestVariable; import jd.parser.Regex; import org.appwork.utils.logging.Log; public class Form { public enum MethodType { GET, POST, PUT, UNKNOWN } /** * */ //private static final long serialVersionUID = 5837247484638868257L; /** * Ein Array mit allen Forms dessen Inhalt dem matcher entspricht. Achtung * der Matcher bezieht sich nicht auf die Properties einer Form sondern auf * den Text der zwischen der Form steht. Dafür gibt es die formProperties */ public static Form[] getForms(final Object requestInfo) { final LinkedList<Form> forms = new LinkedList<Form>(); final Pattern pattern = Pattern.compile("<[\\s]*form(.*?)>(.*?)<[\\s]*/[\\s]*form[\\s]*>|<[\\s]*form(.*?)>(.+)", Pattern.CASE_INSENSITIVE | Pattern.DOTALL); final Matcher formmatcher = pattern.matcher(requestInfo.toString().replaceAll("(?s)<!--.*?-->", "")); while (formmatcher.find()) { final String total = formmatcher.group(0); // System.out.println(inForm); final Form form = new Form(total); forms.add(form); } return forms.toArray(new Form[forms.size()]); } /** * Action der Form entspricht auch oft einer URL */ private String action; private java.util.List<InputField> inputfields; private String htmlcode = null; private MethodType method = MethodType.GET; /* default encoding for http forms */ private String encoding = "application/x-www-form-urlencoded"; private InputField preferredSubmit; private final HashMap<String, String> keyValueMap; public Form() { this.inputfields = new ArrayList<InputField>(); this.keyValueMap = new HashMap<String, String>(); } public Form(final String total) { this(); this.parse(total); } public void addInputField(final InputField nv) { this.inputfields.add(nv); } public void addInputFieldAt(final InputField nv, final int i) { this.inputfields.add(i, nv); } /** * Gibt zurück ob der gesuchte needle String im html Text bgefunden wurde * * @param fileNotFound * @return */ public boolean containsHTML(final String needle) { return new Regex(this.htmlcode, needle).matches(); } public boolean equalsIgnoreCase(final Form f) { return this.toString().equalsIgnoreCase(f.toString()); } public String getAction() { return this.action; } public String getAction(final String baseURL) { URL baseurl = null; if (baseURL == null) { baseurl = null; } else { try { baseurl = new URL(baseURL); } catch (final MalformedURLException e) { Log.exception(e); } } String ret = this.action; if (this.action == null || this.action.matches("[\\s]*")) { if (baseurl == null) { return null; } ret = baseurl.toString(); } else if (!ret.matches("https?://.*")) { if (baseurl == null) { return null; } if (ret.charAt(0) == '/') { if (baseurl.getPort() > 0 && baseurl.getPort() != 80) { ret = "http://" + baseurl.getHost() + ":" + baseurl.getPort() + ret; } else { ret = "http://" + baseurl.getHost() + ret; } } else if (ret.charAt(0) == '&') { final String base = baseurl.toString(); if (base.matches("http://.*/.*")) { ret = base + ret; } else { ret = base + "/" + ret; } } else if (ret.charAt(0) == '?') { final String base = baseurl.toString(); if (base.matches("http://.*/.*")) { ret = base.replaceFirst("\\?.*", "") + ret; } else { ret = base + "/" + ret; } } else if (ret.charAt(0) == '#') { final String base = baseurl.toString(); if (base.matches("http://.*/.*")) { ret = base + ret; } else { ret = base + "/" + ret; } } else { final String base = baseurl.toString(); if (base.matches("https?://.*/.*")) { ret = base.substring(0, base.lastIndexOf("/")) + "/" + ret; } else { ret = base + "/" + ret; } } } return ret; } public String getEncoding() { return this.encoding; } public String getHtmlCode() { return this.htmlcode; } /** * Gets the first inputfiled with this key. REMEMBER. There can be more than * one file with this key * * @param key * @return */ public InputField getInputField(final String key) { for (final InputField ipf : this.inputfields) { if (ipf.getKey() != null && ipf.getKey().equalsIgnoreCase(key)) { return ipf; } } return null; } // public boolean hasSubmitValue(String value) { // for (String submit : this.submitValues) { // try { // if (submit == value || submit.equalsIgnoreCase(value)) return true; // } catch (NullPointerException e) { // // // jd.controlling.JDLogger.getLogger().log(java.util.logging.Level.SEVERE, // "Exception occurred",e); // } // } // return false; // // } public InputField getInputFieldByName(final String name) { for (final InputField ipf : this.inputfields) { if (ipf.getKey() != null && ipf.getKey().equalsIgnoreCase(name)) { return ipf; } } return null; } public InputField getInputFieldByProperty(final String key) { for (final InputField ipf : this.inputfields) { if (ipf.get(key) != null && ipf.get(key).equalsIgnoreCase(key)) { return ipf; } } return null; } public InputField getInputFieldByType(final String type) { for (final InputField ipf : this.inputfields) { if (ipf.getType() != null && ipf.getType().equalsIgnoreCase(type)) { return ipf; } } return null; } public java.util.List<InputField> getInputFields() { return this.inputfields; } public java.util.List<InputField> getInputFieldsByType(final String type) { final java.util.List<InputField> ret = new ArrayList<InputField>(); for (final InputField ipf : this.inputfields) { if (ipf.getType() != null && org.appwork.utils.Regex.matches(ipf.getType(), type)) { ret.add(ipf); } } return ret; } public MethodType getMethod() { return this.method; } public InputField getPreferredSubmit() { return this.preferredSubmit; } /** * GIbt alle variablen als propertyString zurück * * @return */ public String getPropertyString() { final StringBuilder stbuffer = new StringBuilder(); boolean first = true; for (final InputField ipf : this.inputfields) { /* nameless key-value are not being sent, see firefox */ if (ipf.getKey() == null) { continue; } if (first) { first = false; } else { stbuffer.append("&"); } stbuffer.append(ipf.getKey()); stbuffer.append("="); stbuffer.append(ipf.getValue()); } return stbuffer.toString(); } /** * Gibt ein RegexObject bezüglich des Form htmltextes zurück * * @param compile * @return */ public Regex getRegex(final Pattern compile) { return new Regex(this.htmlcode, compile); } /** * Gibt ein RegexObject bezüglich des Form htmltextes zurück * * @param compile * @return */ public Regex getRegex(final String string) { return new Regex(this.htmlcode, string); } /** * Returns a list of requestvariables * * @return */ public java.util.List<RequestVariable> getRequestVariables() { final java.util.List<RequestVariable> ret = new ArrayList<RequestVariable>(); for (final InputField ipf : this.inputfields) { // Do not send not prefered Submit types if (this.getPreferredSubmit() != null && ipf.getType() != null && ipf.getType().equalsIgnoreCase("submit") && this.getPreferredSubmit() != ipf) { continue; } if (ipf.getKey() == null) { continue;/* * nameless key-value are not being sent, see firefox */ } if (ipf.getValue() == null) { continue; } if (ipf.getType() != null && ipf.getType().equalsIgnoreCase("image")) { ret.add(new RequestVariable(ipf.getKey() + ".x", new Random().nextInt(100) + "")); ret.add(new RequestVariable(ipf.getKey() + ".y", new Random().nextInt(100) + "")); } else { ret.add(new RequestVariable(ipf.getKey(), ipf.getValue())); } } return ret; } public String getStringProperty(final String property) { // TODO Auto-generated method stub return this.keyValueMap.get(property); } public HashMap<String, String> getVarsMap() { final HashMap<String, String> ret = new HashMap<String, String>(); for (final InputField ipf : this.inputfields) { /* nameless key-value are not being sent, see firefox */ if (ipf.getKey() == null) { continue; } ret.put(ipf.getKey(), ipf.getValue()); } return ret; } public boolean hasInputFieldByName(final String name) { return this.getInputFieldByName(name) != null; } private void parse(final String total) { this.htmlcode = total; // form.baseRequest = requestInfo; final String header = new Regex(total, "<[\\s]*form(.*?)>").getMatch(0); // // <[\\s]*form(.*?)>(.*?)<[\\s]*/[\\s]*form[\\s]*>|<[\\s]*form(.*?)>(.+) final String[][] headerEntries = new Regex(header, "[\"' ](\\w+?)[ ]*=[ ]*[\"'](.*?)[\"']").getMatches(); final String[][] headerEntries2 = new Regex(header, "[\"' ](\\w+?)[ ]*=[ ]*([^>^ ^\"^']+)").getMatches(); this.parseHeader(headerEntries); this.parseHeader(headerEntries2); this.parseInputFields(); // if (form.action == null) { // form.action = // requestInfo.getConnection().getURL().toString(); // } // form.vars.add(form.getInputFields(inForm)); } private void parseHeader(final String[][] headerEntries) { String key; String value; String lowvalue; for (final String[] entry : headerEntries) { key = entry[0]; value = entry[1]; lowvalue = value.toLowerCase(); if (key.equalsIgnoreCase("action")) { this.setAction(value); } else if (key.equalsIgnoreCase("enctype")) { this.setEncoding(value); } else if (key.equalsIgnoreCase("method")) { if (lowvalue.matches(".*post.*")) { this.setMethod(MethodType.POST); } else if (lowvalue.matches(".*get.*")) { this.setMethod(MethodType.GET); } else if (lowvalue.matches(".*put.*")) { this.setMethod(MethodType.PUT); } else { this.setMethod(MethodType.POST); } } else { this.setProperty(key, value); } } } private void parseInputFields() { this.inputfields = new ArrayList<InputField>(); final Matcher matcher = Pattern.compile("(?s)(<[\\s]*(input|textarea|select).*?>)", Pattern.CASE_INSENSITIVE).matcher(this.htmlcode); while (matcher.find()) { final InputField nv = InputField.parse(matcher.group(1)); if (nv != null) { this.addInputField(nv); } } } /** * Changes the value of the first filed with the key key to value. if no * field exists, a new one is created. * * @param key * @param value */ public void put(final String key, final String value) { final InputField ipf = this.getInputField(key); if (ipf != null) { ipf.setValue(value); } else { this.inputfields.add(new InputField(key, value)); } } /** * Removes the first inputfiled with this key. REMEMBER. There can be more * than one file with this key * * @param key * @return */ public void remove(final String key) { /* * inputfields extends hashmap which overrides hashCode, thats why we * use iterator here */ final Iterator<InputField> it = this.inputfields.iterator(); while (it.hasNext()) { final InputField ipf = it.next(); if (ipf.getKey() == null && key == null) { it.remove(); return; } if (ipf.getKey() != null && ipf.getKey().equalsIgnoreCase(key)) { it.remove(); return; } } } public void setAction(final String action) { this.action = action; } public void setEncoding(final String encoding) { this.encoding = encoding; } public void setMethod(final MethodType method) { this.method = method; } /** * Us the i-th submit field when submitted * * @param i */ public void setPreferredSubmit(int i) { this.preferredSubmit = null; for (final InputField ipf : this.inputfields) { if (ipf.getType() != null && ipf.getValue() != null && ipf.getType().equalsIgnoreCase("submit") && i-- <= 0) { this.preferredSubmit = ipf; return; } } throw new IllegalArgumentException("No such Submitfield: " + i); } /** * Tell the form which submit field to use * * @param preferredSubmit */ public void setPreferredSubmit(final String preferredSubmit) { this.preferredSubmit = null; for (final InputField ipf : this.inputfields) { if (ipf.getType() != null && ipf.getValue() != null && ipf.getType().equalsIgnoreCase("submit") && ipf.getValue().equalsIgnoreCase(preferredSubmit)) { this.preferredSubmit = ipf; return; } } Log.L.warning("No exact match for submit found! Trying to find best match now!"); for (final InputField ipf : this.inputfields) { if (ipf.getType() != null && ipf.getValue() != null && ipf.getType().equalsIgnoreCase("submit") && ipf.getValue().contains(preferredSubmit)) { this.preferredSubmit = ipf; return; } } throw new IllegalArgumentException("No such Submitfield: " + preferredSubmit); } public void setProperty(final String key, final String value) { this.keyValueMap.put(key, value); } @Override public String toString() { final StringBuilder ret = new StringBuilder(); ret.append("Action: "); ret.append(this.action); ret.append('\n'); if (this.method == MethodType.POST) { ret.append("Method: POST\n"); } else if (this.method == MethodType.GET) { ret.append("Method: GET\n"); } else if (this.method == MethodType.PUT) { ret.append("Method: PUT is not supported\n"); } else if (this.method == MethodType.UNKNOWN) { ret.append("Method: Unknown\n"); } for (final InputField ipf : this.inputfields) { ret.append(ipf.toString()); ret.append('\n'); } ret.append(this.keyValueMap.toString()); return ret.toString(); } }