
com.caucho.quercus.lib.UrlModule Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of quercus Show documentation
Show all versions of quercus Show documentation
A PHP engine implemented in 100% Java
/*
* Copyright (c) 1998-2012 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source 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 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source 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, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.quercus.lib;
import com.caucho.quercus.annotation.Optional;
import com.caucho.quercus.annotation.This;
import com.caucho.quercus.env.ArrayValue;
import com.caucho.quercus.env.ArrayValueImpl;
import com.caucho.quercus.env.BooleanValue;
import com.caucho.quercus.env.ConstStringValue;
import com.caucho.quercus.env.Env;
import com.caucho.quercus.env.LongValue;
import com.caucho.quercus.env.ObjectValue;
import com.caucho.quercus.env.StringBuilderOutputStream;
import com.caucho.quercus.env.StringValue;
import com.caucho.quercus.env.UnicodeBuilderValue;
import com.caucho.quercus.env.UnsetValue;
import com.caucho.quercus.env.Value;
import com.caucho.quercus.lib.file.BinaryInput;
import com.caucho.quercus.lib.file.BinaryStream;
import com.caucho.quercus.lib.file.FileModule;
import com.caucho.quercus.module.AbstractQuercusModule;
import com.caucho.util.Base64;
import com.caucho.util.CharBuffer;
import com.caucho.util.L10N;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.URL;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
/**
* PHP URL
*/
public class UrlModule
extends AbstractQuercusModule
{
private static final L10N L = new L10N(UrlModule.class);
private static final Logger log
= Logger.getLogger(UrlModule.class.getName());
public static final int PHP_URL_SCHEME = 0;
public static final int PHP_URL_HOST = 1;
public static final int PHP_URL_PORT = 2;
public static final int PHP_URL_USER = 3;
public static final int PHP_URL_PASS = 4;
public static final int PHP_URL_PATH = 5;
public static final int PHP_URL_QUERY = 6;
public static final int PHP_URL_FRAGMENT = 7;
private static final StringValue SCHEME_V
= new ConstStringValue("scheme");
private static final StringValue SCHEME_U
= new UnicodeBuilderValue("scheme");
private static final StringValue USER_V
= new ConstStringValue("user");
private static final StringValue USER_U
= new UnicodeBuilderValue("user");
private static final StringValue PASS_V
= new ConstStringValue("pass");
private static final StringValue PASS_U
= new UnicodeBuilderValue("pass");
private static final StringValue HOST_V
= new ConstStringValue("host");
private static final StringValue HOST_U
= new UnicodeBuilderValue("host");
private static final StringValue PORT_V
= new ConstStringValue("port");
private static final StringValue PORT_U
= new UnicodeBuilderValue("port");
private static final StringValue PATH_V
= new ConstStringValue("path");
private static final StringValue PATH_U
= new UnicodeBuilderValue("path");
private static final StringValue QUERY_V
= new ConstStringValue("query");
private static final StringValue QUERY_U
= new UnicodeBuilderValue("query");
private static final StringValue FRAGMENT_V
= new ConstStringValue("fragment");
private static final StringValue FRAGMENT_U
= new UnicodeBuilderValue("fragment");
/**
* Encodes base64
*/
public static String base64_encode(StringValue s)
{
CharBuffer cb = new CharBuffer();
byte []buffer = new byte[3];
int strlen = s.length();
int offset = 0;
for (; offset + 3 <= strlen; offset += 3) {
buffer[0] = (byte) s.charAt(offset);
buffer[1] = (byte) s.charAt(offset + 1);
buffer[2] = (byte) s.charAt(offset + 2);
Base64.encode(cb, buffer, 0, 3);
}
if (offset < strlen)
buffer[0] = (byte) s.charAt(offset);
if (offset + 1 < strlen)
buffer[1] = (byte) s.charAt(offset + 1);
if (offset + 2 < strlen)
buffer[2] = (byte) s.charAt(offset + 2);
Base64.encode(cb, buffer, 0, strlen - offset);
return cb.toString();
}
/**
* Decodes base64
*/
public static Value base64_decode(Env env,
StringValue str,
@Optional boolean isStrict)
{
if (str.length() == 0)
return str;
StringValue sb = env.createBinaryBuilder();
OutputStream os = new StringBuilderOutputStream(sb);
try {
Base64.decodeIgnoreWhitespace(str.toSimpleReader(), os);
} catch (IOException e) {
env.warning(e);
return BooleanValue.FALSE;
}
return sb;
}
/**
* Connects to the given URL using a HEAD request to retreive
* the headers sent in the response.
*/
public static Value get_headers(Env env, String urlString,
@Optional Value format)
{
Socket socket = null;
try {
URL url = new URL(urlString);
if (! url.getProtocol().equals("http")
&& ! url.getProtocol().equals("https")) {
env.warning(L.l("Not an HTTP URL"));
return null;
}
int port = 80;
if (url.getPort() < 0) {
if (url.getProtocol().equals("http"))
port = 80;
else if (url.getProtocol().equals("https"))
port = 443;
} else {
port = url.getPort();
}
socket = new Socket(url.getHost(), port);
OutputStream out = socket.getOutputStream();
InputStream in = socket.getInputStream();
StringBuilder request = new StringBuilder();
request.append("HEAD ");
if (url.getPath() != null)
request.append(url.getPath());
if (url.getQuery() != null)
request.append("?" + url.getQuery());
if (url.getRef() != null)
request.append("#" + url.getRef());
request.append(" HTTP/1.0\r\n");
if (url.getHost() != null)
request.append("Host: " + url.getHost() + "\r\n");
request.append("\r\n");
OutputStreamWriter writer = new OutputStreamWriter(out);
writer.write(request.toString());
writer.flush();
LineNumberReader reader = new LineNumberReader(new InputStreamReader(in));
ArrayValue result = new ArrayValueImpl();
if (format.toBoolean()) {
for (String line = reader.readLine();
line != null;
line = reader.readLine()) {
line = line.trim();
if (line.length() == 0)
continue;
int colon = line.indexOf(':');
ArrayValue values;
if (colon < 0)
result.put(env.createString(line.trim()));
else {
StringValue key =
env.createString(line.substring(0, colon).trim());
StringValue value;
if (colon < line.length())
value = env.createString(line.substring(colon + 1).trim());
else
value = env.getEmptyString();
if (result.get(key) != UnsetValue.UNSET)
values = (ArrayValue)result.get(key);
else {
values = new ArrayValueImpl();
result.put(key, values);
}
values.put(value);
}
}
// collapse single entries
for (Value key : result.keySet()) {
Value value = result.get(key);
if (value.isArray() && ((ArrayValue)value).getSize() == 1)
result.put(key, ((ArrayValue)value).get(LongValue.ZERO));
}
} else {
for (String line = reader.readLine();
line != null;
line = reader.readLine()) {
line = line.trim();
if (line.length() == 0)
continue;
result.put(env.createString(line.trim()));
}
}
return result;
} catch (Exception e) {
env.warning(e);
return BooleanValue.FALSE;
} finally {
try {
if (socket != null)
socket.close();
} catch (IOException e) {
env.warning(e);
}
}
}
/**
* Extracts the meta tags from a file and returns them as an array.
*/
public static Value get_meta_tags(Env env,
StringValue filename,
@Optional boolean useIncludePath)
{
InputStream in = null;
ArrayValue result = new ArrayValueImpl();
try {
BinaryStream stream
= FileModule.fopen(env, filename, "r", useIncludePath, null);
if (stream == null || ! (stream instanceof BinaryInput))
return result;
BinaryInput input = (BinaryInput) stream;
while (! input.isEOF()) {
String tag = getNextTag(input);
if (tag.equalsIgnoreCase("meta")) {
String name = null;
String content = null;
String [] attr;
while ((attr = getNextAttribute(input)) != null) {
if (name == null && attr[0].equalsIgnoreCase("name")) {
if (attr.length > 1)
name = attr[1];
} else if (content == null && attr[0].equalsIgnoreCase("content")) {
if (attr.length > 1)
content = attr[1];
}
if (name != null && content != null) {
result.put(env.createString(name), env.createString(content));
break;
}
}
} else if (tag.equalsIgnoreCase("/head"))
break;
}
} catch (IOException e) {
env.warning(e);
} finally {
try {
if (in != null)
in.close();
} catch (IOException e) {
env.warning(e);
}
}
return result;
}
public static Value http_build_query(Env env,
Value formdata,
@Optional StringValue numeric_prefix,
@Optional("'&'") StringValue separator)
{
StringValue result = env.createUnicodeBuilder();
httpBuildQueryImpl(env,
result,
formdata,
env.getEmptyString(),
numeric_prefix,
separator);
return result;
}
private static void httpBuildQueryImpl(Env env,
StringValue result,
Value formdata,
StringValue path,
StringValue numeric_prefix,
StringValue separator)
{
Set extends Map.Entry> entrySet;
if (formdata.isArray()) {
entrySet = ((ArrayValue) formdata).entrySet();
}
else if (formdata.isObject()) {
entrySet = ((ObjectValue) formdata).entrySet();
}
else {
env.warning(L.l("formdata must be an array or object"));
return;
}
boolean isFirst = true;
for (Map.Entry entry : entrySet) {
if (! isFirst) {
if (separator != null)
result.append(separator);
else
result.append("&");
}
isFirst = false;
StringValue newPath = makeNewPath(path, entry.getKey(), numeric_prefix);
Value entryValue = entry.getValue();
if (entryValue.isArray() || entryValue.isObject()) {
// can always throw away the numeric prefix on recursive calls
httpBuildQueryImpl(env, result, entryValue, newPath, null, separator);
} else {
result.append(newPath);
result.append("=");
result.append(urlencode(entry.getValue().toStringValue()));
}
}
}
private static StringValue makeNewPath(StringValue oldPath,
Value key,
StringValue numeric_prefix)
{
StringValue path = oldPath.createStringBuilder();
if (oldPath.length() != 0) {
path.append(oldPath);
//path.append('[');
path.append("%5B");
urlencode(path, key.toStringValue());
//path.append(']');
path.append("%5D");
return path;
}
else if (key.isLongConvertible() && numeric_prefix != null) {
urlencode(path, numeric_prefix);
urlencode(path, key.toStringValue());
return path;
}
else {
urlencode(path, key.toStringValue());
return path;
}
}
/**
* Creates a http string.
*/
/*
public String http_build_query(Value value,
@Optional String prefix)
{
StringBuilder sb = new StringBuilder();
int index = 0;
if (value instanceof ArrayValue) {
ArrayValue array = (ArrayValue) value;
for (Map.Entry entry : array.entrySet()) {
Value keyValue = entry.getKey();
Value v = entry.getValue();
String key;
if (keyValue.isLongConvertible())
key = prefix + keyValue;
else
key = keyValue.toString();
if (v instanceof ArrayValue)
http_build_query(sb, key, (ArrayValue) v);
else {
if (sb.length() > 0)
sb.append('&');
sb.append(key);
sb.append('=');
urlencode(sb, v.toString());
}
}
}
return sb.toString();
}
*/
/**
* Creates a http string.
*/
/*
private void http_build_query(StringBuilder sb,
String prefix,
ArrayValue array)
{
for (Map.Entry entry : array.entrySet()) {
Value keyValue = entry.getKey();
Value v = entry.getValue();
String key = prefix + '[' + keyValue + ']';
if (v instanceof ArrayValue)
http_build_query(sb, key, (ArrayValue) v);
else {
if (sb.length() > 0)
sb.append('&');
sb.append(key);
sb.append('=');
urlencode(sb, v.toString());
}
}
}
*/
/**
* Parses the URL into an array.
*/
public static Value parse_url(Env env,
StringValue str,
@Optional("-1") int component)
{
boolean isUnicode = env.isUnicodeSemantics();
ArrayValueImpl array = new ArrayValueImpl();
parseUrl(env, str, array, isUnicode);
switch (component) {
case PHP_URL_SCHEME:
return array.get(isUnicode ? SCHEME_U : SCHEME_V);
case PHP_URL_HOST:
return array.get(isUnicode ? HOST_U : HOST_V);
case PHP_URL_PORT:
return array.get(isUnicode ? PORT_U : PORT_V);
case PHP_URL_USER:
return array.get(isUnicode ? USER_U : USER_V);
case PHP_URL_PASS:
return array.get(isUnicode ? PASS_U : PASS_V);
case PHP_URL_PATH:
return array.get(isUnicode ? PATH_U : PATH_V);
case PHP_URL_QUERY:
return array.get(isUnicode ? QUERY_U : QUERY_V);
case PHP_URL_FRAGMENT:
return array.get(isUnicode ? FRAGMENT_U : FRAGMENT_V);
}
return array;
}
private static void parseUrl(Env env,
StringValue str,
ArrayValue array,
boolean isUnicode)
{
int strlen = str.length();
if (strlen == 0) {
array.put(PATH_V, PATH_U, env.getEmptyString(), isUnicode);
return;
}
int i = 0;
char ch;
int colon = str.indexOf(":");
boolean hasHost = false;
if (0 <= colon) {
int end = colon;
if (colon + 1 < strlen && str.charAt(colon + 1) == '/') {
if (colon + 2 < strlen && str.charAt(colon + 2) == '/') {
end = colon + 2;
if (colon + 3 < strlen && str.charAt(colon + 3) == '/') {
}
else {
hasHost = true;
}
}
StringValue sb = env.createStringBuilder();
sb.append(str, 0, colon);
array.put(SCHEME_V, SCHEME_U, sb, isUnicode);
i = end + 1;
}
else if (colon + 1 == strlen
|| (ch = str.charAt(colon + 1)) <= '0'
|| '9' <= ch) {
StringValue sb = env.createStringBuilder();
sb.append(str, 0, colon);
array.put(SCHEME_V, SCHEME_U, sb, isUnicode);
i = colon + 1;
}
else {
hasHost = true;
}
}
colon = str.indexOf(':', i);
int question = str.indexOf('?', i);
int pound = str.indexOf('#', i);
int atSign = str.lastIndexOf('@');
StringValue user = null;
StringValue pass = null;
// username:password
if (0 <= atSign && (question < 0 || atSign < question) && hasHost) {
if (0 <= colon && colon < atSign) {
if (i < colon) {
user = env.createStringBuilder();
user.append(str, i, colon);
}
if (colon + 1 < atSign) {
pass = env.createStringBuilder();
pass.append(str, colon + 1, atSign);
}
i = atSign + 1;
colon = str.indexOf(':', i);
}
else {
user = env.createStringBuilder();
user.append(str, i, atSign);
i = atSign + 1;
}
}
if (0 <= i && hasHost) {
int slash = str.indexOf('/', i);
if (i < colon) {
StringValue sb = env.createStringBuilder();
sb.append(str, i, colon);
array.put(HOST_V, HOST_U, sb, isUnicode);
int end;
if (i < slash)
end = slash;
else if (i < question)
end = question + 1;
else if (i < pound)
end = pound + 1;
else
end = strlen;
if (0 < end - (colon + 1)) {
int port = 0;
for (int j = colon + 1; j < end; j++) {
ch = str.charAt(j);
if ('0' <= ch && ch <= '9')
port = port * 10 + ch - '0';
else
break;
}
array.put(PORT_V, PORT_U, LongValue.create(port), isUnicode);
}
i = end;
}
else if (i < question && (slash < i || question < slash)) {
StringValue sb = env.createStringBuilder();
sb.append(str, i, question);
array.put(HOST_V, HOST_U, sb, isUnicode);
i = question + 1;
}
else if (i < slash) {
StringValue sb = env.createStringBuilder();
sb.append(str, i, slash);
array.put(HOST_V, HOST_U, sb, isUnicode);
i = slash;
}
else if (i < pound) {
StringValue sb = env.createStringBuilder();
sb.append(str, i, pound);
array.put(HOST_V, HOST_U, sb, isUnicode);
i = pound + 1;
}
else {
StringValue sb = env.createStringBuilder();
sb.append(str, i, strlen);
array.put(HOST_V, HOST_U, sb, isUnicode);
i = strlen;
}
}
// insert user and password after port
if (user != null)
array.put(USER_V, USER_U, user, isUnicode);
if (pass != null)
array.put(PASS_V, PASS_U, pass, isUnicode);
if (i < question) {
StringValue sb = env.createStringBuilder();
sb.append(str, i, question);
array.put(PATH_V, PATH_U, sb, isUnicode);
i = question + 1;
}
if (0 <= pound) {
if (i < pound) {
StringValue sb = env.createStringBuilder();
sb.append(str, i, pound);
if (0 <= question)
array.put(QUERY_V, QUERY_U, sb, isUnicode);
else
array.put(PATH_V, PATH_U, sb, isUnicode);
}
if (pound + 1 < strlen) {
StringValue sb = env.createStringBuilder();
sb.append(str, pound + 1, strlen);
array.put(FRAGMENT_V, FRAGMENT_U, sb, isUnicode);
}
}
else if (i < strlen) {
StringValue sb = env.createStringBuilder();
sb.append(str, i, strlen);
if (0 <= question)
array.put(QUERY_V, QUERY_U, sb, isUnicode);
else
array.put(PATH_V, PATH_U, sb, isUnicode);
}
}
/**
* Returns the decoded string.
*/
public static String rawurldecode(String s)
{
if (s == null)
return "";
int len = s.length();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < len; i++) {
char ch = s.charAt(i);
if (ch == '%' && i + 2 < len) {
int d1 = s.charAt(i + 1);
int d2 = s.charAt(i + 2);
int v = 0;
if ('0' <= d1 && d1 <= '9')
v = 16 * (d1 - '0');
else if ('a' <= d1 && d1 <= 'f')
v = 16 * (d1 - 'a' + 10);
else if ('A' <= d1 && d1 <= 'F')
v = 16 * (d1 - 'A' + 10);
else {
sb.append('%');
continue;
}
if ('0' <= d2 && d2 <= '9')
v += (d2 - '0');
else if ('a' <= d2 && d2 <= 'f')
v += (d2 - 'a' + 10);
else if ('A' <= d2 && d2 <= 'F')
v += (d2 - 'A' + 10);
else {
sb.append('%');
continue;
}
i += 2;
sb.append((char) v);
}
else
sb.append(ch);
}
return sb.toString();
}
/**
* Encodes the url
*/
public static String rawurlencode(String str)
{
if (str == null)
return "";
StringBuilder sb = new StringBuilder();
for (int i = 0; i < str.length(); i++) {
char ch = str.charAt(i);
if ('a' <= ch && ch <= 'z'
|| 'A' <= ch && ch <= 'Z'
|| '0' <= ch && ch <= '9'
|| ch == '-' || ch == '_' || ch == '.' || ch == '~') {
sb.append(ch);
}
else {
sb.append('%');
sb.append(toHexDigit(ch >> 4));
sb.append(toHexDigit(ch));
}
}
return sb.toString();
}
enum ParseUrlState {
INIT, USER, PASS, HOST, PORT, PATH, QUERY, FRAGMENT
};
/**
* Gets the magic quotes value.
*/
public static StringValue urlencode(StringValue str)
{
StringValue sb = str.createStringBuilder();
urlencode(sb, str);
return sb;
}
/**
* Gets the magic quotes value.
*/
private static void urlencode(StringValue sb, StringValue str)
{
int len = str.length();
for (int i = 0; i < len; i++) {
char ch = str.charAt(i);
if ('a' <= ch && ch <= 'z')
sb.append(ch);
else if ('A' <= ch && ch <= 'Z')
sb.append(ch);
else if ('0' <= ch && ch <= '9')
sb.append(ch);
else if (ch == '-' || ch == '_' || ch == '.')
sb.append(ch);
else if (ch == ' ')
sb.append('+');
else {
sb.append('%');
sb.append(toHexDigit(ch / 16));
sb.append(toHexDigit(ch));
}
}
}
/**
* Returns the decoded string.
*/
public static String urldecode(String s)
{
if (s == null)
return "";
int len = s.length();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < len; i++) {
char ch = s.charAt(i);
if (ch == '%' && i + 2 < len) {
int d1 = s.charAt(i + 1);
int d2 = s.charAt(i + 2);
int v = 0;
if ('0' <= d1 && d1 <= '9')
v = 16 * (d1 - '0');
else if ('a' <= d1 && d1 <= 'f')
v = 16 * (d1 - 'a' + 10);
else if ('A' <= d1 && d1 <= 'F')
v = 16 * (d1 - 'A' + 10);
else {
sb.append('%');
continue;
}
if ('0' <= d2 && d2 <= '9')
v += (d2 - '0');
else if ('a' <= d2 && d2 <= 'f')
v += (d2 - 'a' + 10);
else if ('A' <= d2 && d2 <= 'F')
v += (d2 - 'A' + 10);
else {
sb.append('%');
continue;
}
i += 2;
sb.append((char) v);
}
else if (ch == '+')
sb.append(' ');
else
sb.append(ch);
}
return sb.toString();
}
private static String getNextTag(BinaryInput input)
throws IOException
{
StringBuilder tag = new StringBuilder();
for (int ch = 0; ! input.isEOF() && ch != '<'; ch = input.read()) {
// intentionally left empty
}
while (! input.isEOF()) {
int ch = input.read();
if (Character.isWhitespace(ch))
break;
tag.append((char) ch);
}
return tag.toString();
}
/**
* Finds the next attribute in the stream and return the key and value
* as an array.
*/
private static String [] getNextAttribute(BinaryInput input)
throws IOException
{
int ch;
consumeWhiteSpace(input);
StringBuilder attribute = new StringBuilder();
while (! input.isEOF()) {
ch = input.read();
if (isValidAttributeCharacter(ch))
attribute.append((char) ch);
else {
input.unread();
break;
}
}
if (attribute.length() == 0)
return null;
consumeWhiteSpace(input);
if (input.isEOF())
return new String[] { attribute.toString() };
ch = input.read();
if (ch != '=') {
input.unread();
return new String[] { attribute.toString() };
}
consumeWhiteSpace(input);
// check for quoting
int quote = ' ';
boolean quoted = false;
if (input.isEOF())
return new String[] { attribute.toString() };
ch = input.read();
if (ch == '"' || ch == '\'') {
quoted = true;
quote = ch;
} else
input.unread();
StringBuilder value = new StringBuilder();
while (! input.isEOF()) {
ch = input.read();
// mimics PHP behavior
if ((quoted && ch == quote)
|| (! quoted && Character.isWhitespace(ch)) || ch == '>')
break;
value.append((char) ch);
}
return new String[] { attribute.toString(), value.toString() };
}
private static void consumeWhiteSpace(BinaryInput input)
throws IOException
{
int ch = 0;
while (! input.isEOF() && Character.isWhitespace(ch = input.read())) {
// intentionally left empty
}
if (! Character.isWhitespace(ch))
input.unread();
}
private static boolean isValidAttributeCharacter(int ch)
{
return Character.isLetterOrDigit(ch)
|| (ch == '-') || (ch == '.') || (ch == '_') || (ch == ':');
}
private static char toHexDigit(int d)
{
d = d & 0xf;
if (d < 10)
return (char) ('0' + d);
else
return (char) ('A' + d - 10);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy