<commit_before>/*
 * Zed Attack Proxy (ZAP) and its related class files.
 * 
 * ZAP is an HTTP/HTTPS proxy for assessing web application security.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0 
 *   
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License. 
 */
package org.zaproxy.zap.extension.api;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.security.SecureRandom;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.regex.Pattern;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.commons.httpclient.URIException;
import org.apache.commons.lang.time.DateUtils;
import org.apache.log4j.Logger;
import org.parosproxy.paros.core.proxy.ProxyParam;
import org.parosproxy.paros.model.Model;
import org.parosproxy.paros.network.HttpHeader;
import org.parosproxy.paros.network.HttpInputStream;
import org.parosproxy.paros.network.HttpMalformedHeaderException;
import org.parosproxy.paros.network.HttpMessage;
import org.parosproxy.paros.network.HttpOutputStream;
import org.parosproxy.paros.network.HttpRequestHeader;
import org.parosproxy.paros.view.View;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import net.sf.json.JSONObject;

public class API {
	public enum Format {XML, HTML, JSON, JSONP, UI, OTHER};
	public enum RequestType {action, view, other, pconn};
	
	/**
	 * The custom domain to access the ZAP API while proxying through ZAP.
	 * 
	 * @see #getBaseURL(boolean)
	 */
	public static final String API_DOMAIN = "zap";

	/**
	 * The HTTP URL to access the ZAP API while proxying through ZAP.
	 * 
	 * @see #getBaseURL(boolean)
	 */
	public static final String API_URL = "http://" + API_DOMAIN + "/";

	/**
	 * The HTTPS URL to access the ZAP API while proxying through ZAP.
	 * 
	 * @see #getBaseURL(boolean)
	 */
	public static final String API_URL_S = "https://" + API_DOMAIN + "/";
	public static final String API_KEY_PARAM = "apikey";
	public static final String API_NONCE_PARAM = "apinonce";

	private static Pattern patternParam = Pattern.compile("&", Pattern.CASE_INSENSITIVE);
	private static final String CALL_BACK_URL = "/zapCallBackUrl/";

	private static final String STATUS_OK = "200 OK";
	private static final String STATUS_BAD_REQUEST = "400 Bad Request";
	private static final String STATUS_INTERNAL_SERVER_ERROR = "500 Internal Server Error";

	private Map<String, ApiImplementor> implementors = new HashMap<>();
	private static API api = null;
	private WebUI webUI = new WebUI(this);
	private Map<String, ApiImplementor> callBacks = new HashMap<>();

	private Map<String, ApiImplementor> shortcuts = new HashMap<>();
	
	private Map<String, Nonce> nonces = Collections.synchronizedMap(new HashMap<String, Nonce>());
	
	/**
	 * The options for the API.
	 * 
	 * @see #getOptionsParamApi()
	 */
	private OptionsParamApi optionsParamApi;

	/**
	 * The options of the local proxy.
	 * 
	 * @see #getProxyParam()
	 */
	private ProxyParam proxyParam;

	private Random random = new SecureRandom();
    private static final Logger logger = Logger.getLogger(API.class);

	private static synchronized API newInstance() {
		if (api == null) {
			api = new API();
		}
		return api;
	}
	
	public static API getInstance() {
		if (api == null) {
			newInstance();
		}
		return api;
	}

	/**
	 * Registers the given {@code ApiImplementor} to the ZAP API.
	 * <p>
	 * The implementor is not registed if the {@link ApiImplementor#getPrefix() API implementor prefix} is already in use.
	 * <p>
	 * <strong>Note:</strong> The preferred method to add an {@code ApiImplementor} is through the method
	 * {@link org.parosproxy.paros.extension.ExtensionHook#addApiImplementor(ApiImplementor)
	 * ExtensionHook.addApiImplementor(ApiImplementor)} when the corresponding
	 * {@link org.parosproxy.paros.extension.Extension#hook(org.parosproxy.paros.extension.ExtensionHook) extension is hooked}.
	 * Only use this method if really necessary.
	 *
	 * @param impl the implementor that will be registered
	 * @see #removeApiImplementor(ApiImplementor)
	 */
	public void registerApiImplementor (ApiImplementor impl) {
		if (implementors.get(impl.getPrefix()) != null) {
			logger.error("Second attempt to register API implementor with prefix of " + impl.getPrefix());
			return;
		}
		implementors.put(impl.getPrefix(), impl);
		for (String shortcut : impl.getApiShortcuts()) {
			logger.debug("Registering API shortcut: " + shortcut);
			if (this.shortcuts.containsKey(shortcut)) {
				logger.error("Duplicate API shortcut: " + shortcut);
			}
			this.shortcuts.put("/" + shortcut, impl);
		}
	}
	
	/**
	 * Removes the given {@code ApiImplementor} from the ZAP API.
	 * <p>
	 * Since TODO add version, the callbacks previously created for the implementor are removed as well.<commit_msg>Fix typo (JavaDoc) in API class<commit_after>/*
 * Zed Attack Proxy (ZAP) and its related class files.
 * 
 * ZAP is an HTTP/HTTPS proxy for assessing web application security.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0 
 *   
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License. 
 */
package org.zaproxy.zap.extension.api;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.security.SecureRandom;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.regex.Pattern;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.commons.httpclient.URIException;
import org.apache.commons.lang.time.DateUtils;
import org.apache.log4j.Logger;
import org.parosproxy.paros.core.proxy.ProxyParam;
import org.parosproxy.paros.model.Model;
import org.parosproxy.paros.network.HttpHeader;
import org.parosproxy.paros.network.HttpInputStream;
import org.parosproxy.paros.network.HttpMalformedHeaderException;
import org.parosproxy.paros.network.HttpMessage;
import org.parosproxy.paros.network.HttpOutputStream;
import org.parosproxy.paros.network.HttpRequestHeader;
import org.parosproxy.paros.view.View;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import net.sf.json.JSONObject;

public class API {
	public enum Format {XML, HTML, JSON, JSONP, UI, OTHER};
	public enum RequestType {action, view, other, pconn};
	
	/**
	 * The custom domain to access the ZAP API while proxying through ZAP.
	 * 
	 * @see #getBaseURL(boolean)
	 */
	public static final String API_DOMAIN = "zap";

	/**
	 * The HTTP URL to access the ZAP API while proxying through ZAP.
	 * 
	 * @see #getBaseURL(boolean)
	 */
	public static final String API_URL = "http://" + API_DOMAIN + "/";

	/**
	 * The HTTPS URL to access the ZAP API while proxying through ZAP.
	 * 
	 * @see #getBaseURL(boolean)
	 */
	public static final String API_URL_S = "https://" + API_DOMAIN + "/";
	public static final String API_KEY_PARAM = "apikey";
	public static final String API_NONCE_PARAM = "apinonce";

	private static Pattern patternParam = Pattern.compile("&", Pattern.CASE_INSENSITIVE);
	private static final String CALL_BACK_URL = "/zapCallBackUrl/";

	private static final String STATUS_OK = "200 OK";
	private static final String STATUS_BAD_REQUEST = "400 Bad Request";
	private static final String STATUS_INTERNAL_SERVER_ERROR = "500 Internal Server Error";

	private Map<String, ApiImplementor> implementors = new HashMap<>();
	private static API api = null;
	private WebUI webUI = new WebUI(this);
	private Map<String, ApiImplementor> callBacks = new HashMap<>();

	private Map<String, ApiImplementor> shortcuts = new HashMap<>();
	
	private Map<String, Nonce> nonces = Collections.synchronizedMap(new HashMap<String, Nonce>());
	
	/**
	 * The options for the API.
	 * 
	 * @see #getOptionsParamApi()
	 */
	private OptionsParamApi optionsParamApi;

	/**
	 * The options of the local proxy.
	 * 
	 * @see #getProxyParam()
	 */
	private ProxyParam proxyParam;

	private Random random = new SecureRandom();
    private static final Logger logger = Logger.getLogger(API.class);

	private static synchronized API newInstance() {
		if (api == null) {
			api = new API();
		}
		return api;
	}
	
	public static API getInstance() {
		if (api == null) {
			newInstance();
		}
		return api;
	}

	/**
	 * Registers the given {@code ApiImplementor} to the ZAP API.
	 * <p>
	 * The implementor is not registered if the {@link ApiImplementor#getPrefix() API implementor prefix} is already in use.
	 * <p>
	 * <strong>Note:</strong> The preferred method to add an {@code ApiImplementor} is through the method
	 * {@link org.parosproxy.paros.extension.ExtensionHook#addApiImplementor(ApiImplementor)
	 * ExtensionHook.addApiImplementor(ApiImplementor)} when the corresponding
	 * {@link org.parosproxy.paros.extension.Extension#hook(org.parosproxy.paros.extension.ExtensionHook) extension is hooked}.
	 * Only use this method if really necessary.
	 *
	 * @param impl the implementor that will be registered
	 * @see #removeApiImplementor(ApiImplementor)
	 */
	public void registerApiImplementor (ApiImplementor impl) {
		if (implementors.get(impl.getPrefix()) != null) {
			logger.error("Second attempt to register API implementor with prefix of " + impl.getPrefix());
			return;
		}
		implementors.put(impl.getPrefix(), impl);
		for (String shortcut : impl.getApiShortcuts()) {
			logger.debug("Registering API shortcut: " + shortcut);
			if (this.shortcuts.containsKey(shortcut)) {
				logger.error("Duplicate API shortcut: " + shortcut);
			}
			this.shortcuts.put("/" + shortcut, impl);
		}
	}
	
	/**
	 * Removes the given {@code ApiImplementor} from the ZAP API.
	 * <p>
	 * Since TODO add version, the callbacks previously created for the implementor are removed as well.
{'fraction_non_alphanumeric': 0.10525387870239775, 'fraction_numerical': 0.0028208744710860366, 'mean_word_length': 4.397240723120837, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 29, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 12}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '241235', 'n_tokens_mistral': 3566, 'n_tokens_neox': 3359, 'n_words': 1044}