com.adobe.xfa.protocol.ProtocolUtils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aem-sdk-api Show documentation
Show all versions of aem-sdk-api Show documentation
The Adobe Experience Manager SDK
The newest version!
/*
* ADOBE CONFIDENTIAL
*
* Copyright 2005 Adobe Systems Incorporated All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains the property of
* Adobe Systems Incorporated and its suppliers, if any. The intellectual and
* technical concepts contained herein are proprietary to Adobe Systems
* Incorporated and its suppliers and may be covered by U.S. and Foreign
* Patents, patents in process, and are protected by trade secret or copyright
* law. Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained from
* Adobe Systems Incorporated.
*/
package com.adobe.xfa.protocol;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLStreamHandler;
import java.security.SecureRandom;
import java.util.Random;
import com.adobe.xfa.Node;
import com.adobe.xfa.TextNode;
import com.adobe.xfa.ut.FindBugsSuppress;
import com.adobe.xfa.ut.Resolver;
import com.adobe.xfa.ut.StringHolder;
import com.adobe.xfa.ut.StringUtils;
/**
* This class provides some utility methods to support some
* protocol I/O capabilities.
*
* @exclude from published api.
*/
public final class ProtocolUtils {
private ProtocolUtils() {
}
private final static byte[] CRLF = asciiBytes("\r\n");
private final static byte[] DASH_DASH = asciiBytes("--");
private final static byte[] CONTENT_DISPOSITION_FORM_DATA = asciiBytes("Content-Disposition: form-data; ");
private final static byte[] NAME_EQUALS_QUOTE = asciiBytes("name=\"");
private final static byte[] QUOTE = asciiBytes("\"");
private final static byte[] SEMICOLON_SPACE = asciiBytes("; ");
private final static byte[] FILENAME_EQUALS_QUOTE = asciiBytes("filename=\"");
private final static byte[] CONTENT_TYPE = asciiBytes("nContent-Type: ");
private final static byte[] CONTENT_TRANSFER_ENCODING_BINARY = asciiBytes("Content-Transfer-Encoding: binary");
private final static byte[] IMAGE_GIF = asciiBytes("image/gif");
private final static byte[] IMAGE_JPG = asciiBytes("image/jpeg");
private final static byte[] TEXT_PLAIN = asciiBytes("text/plain");
private final static byte[] FILE_NOT_FOUND = asciiBytes("[File not found]");
private static byte[] asciiBytes(String s) {
byte[] bytes = new byte[s.length()];
for (int i = 0; i < s.length(); i++)
bytes[i] = (byte)s.charAt(i);
return bytes;
}
/**
* Reads bytes from the input stream into the given array of bytes.
* This method blocks until all input data is available, end of file is
* detected, or an exception is thrown. Contrary to
* {@link java.io.InputStream#read(byte[])}, this method will block
* until a full buffer's worth of data is read.
* @param b the buffer into which the data is read.
* @return the number of bytes read, or -1 when
* end of the stream has been reached.
* @see java.io.InputStream#read(byte[])
*/
public static int read(InputStream is, byte [] b) throws IOException {
int offset = 0;
do {
int n = is.read(b, offset, b.length - offset);
if (n != -1)
offset += n;
else /* if (n == -1) */
break;
} while (offset < b.length);
if (offset == 0)
return -1;
return offset;
}
public static InputStream checkUrl(String sBaseUrl, String sPath,
boolean bTrusted, StringHolder sRealUrl) {
if (sRealUrl != null)
sRealUrl.value = null;
StringBuilder sBase = new StringBuilder(sBaseUrl);
if (sBase.length() > 0) {
if (sBase.charAt(sBase.length() - 1) != '/')
sBase.append('/');
}
sPath = sPath.replace('\\', '/');
URI uri = null;
//
// If path is relative then concatenate the url and path.
//
if (! isAbsolute(sPath)) {
if (!StringUtils.isEmpty(sBaseUrl)) {
// JavaPort: use URI.resolve() to concatenate the base url and path.
URI baseURI = getUriForString(sBase.toString());
uri = baseURI.resolve(sPath);
}
else if (bTrusted) {
uri = getUriForString(sPath);
}
}
//
// Else path is absolute So only allow when trusted.
//
else if (bTrusted) {
uri = getUriForString(sPath);
}
if (uri == null)
return null;
// Javaport: hereon, the following differs from C++.
URL url = null;
String sScheme = uri.getScheme();
Protocol protocol = Resolver.getProtocol(sScheme);
URLStreamHandler streamHandler = null;
if (protocol != null)
streamHandler = protocol.getURLStreamHandler();
try {
if (streamHandler != null)
url = new URL(null, uri.toString(), streamHandler);
else
url = uri.toURL();
}
catch (MalformedURLException ex) {
return null;
}
InputStream resolved = null;
try {
resolved = url.openStream();
}
catch (IOException ex) {
return null;
}
if (sRealUrl != null)
sRealUrl.value = url.toString();
return resolved;
}
private static URI getUriForString(String sPath) {
try {
URI uri = new URI(sPath);
if (uri.getScheme() != null &&
uri.getScheme().length() > 1) // Windows drive letter? Assume no single-letter schemes.
return uri;
}
catch (URISyntaxException e) {
}
File oPath = new File(sPath);
return oPath.toURI();
}
public static boolean isAbsolute(String sPath) {
try {
URI uri = new URI(sPath);
if (uri.getScheme() != null &&
uri.getScheme().length() > 1) // Windows drive letter? Assume no single-letter schemes.)
return uri.isAbsolute();
}
catch (URISyntaxException e) {
}
File oPath = new File(sPath);
return oPath.isAbsolute();
}
/**
* Normalizes the given base URL. Base URLs are those from which
* relative URIs can be resolved.
* @param sBaseUrl a base URL.
*/
public static String normalizeBaseUrl(String sBaseUrl) {
if (StringUtils.isEmpty(sBaseUrl))
return sBaseUrl;
//
// Ensure msBaseUrl is terminated with a '/' for otherwise
// relative URIs won't resolve correctly.
//
char cSep = getFileOrUriSeparator(sBaseUrl);
int nLen = sBaseUrl.length();
if (sBaseUrl.charAt(nLen - 1) != cSep)
sBaseUrl += cSep;
URI uri = null;
try {
uri = new URI(sBaseUrl);
}
catch (URISyntaxException e) {
}
if (uri != null && uri.isOpaque()) {
// A base path that looks like this: 'dest:somedir/'
// will fail to resolve when combined with another file uri.
// Adding a slash after the schema name will fix this.
String sScheme = uri.getScheme();
String sSchemeSpecificPart = uri.getSchemeSpecificPart();
if (sScheme != null && sSchemeSpecificPart != null) {
sBaseUrl = sScheme + ":/" + sSchemeSpecificPart;
}
}
return sBaseUrl;
}
/**
* Convenience method for opening an input URL without worrying about
* all the additional parameters.
* @param sUrl Name of resource to open. This may be a local file or a
* remote location.
* @return Input stream corresponding to the URL opened; null if the
* operation fails.
*/
public static InputStream openUrl (String sUrl) {
return checkUrl ("", sUrl, true, null);
}
// /*
// * Parse a HTTP header and extract its Content-Type value.
// *
// * Space for the returned string is malloc()'ed. If there's
// * no Content-Type, an empty string is returned.
// */
// public static String parserHeader(String src) {
// throw new ExFull(ResId.UNSUPPORTED_OPERATION, "ProtocolUtils.parserHeader");
// // Javaport: TODO
// // char* q = strstr(src, "\r\nContent-Type: ");
// // if (! q)
// // q = strstr(src, "\nContent-Type: ");
// // if (! q)
// // q = strstr(src, "\nContent-type: ");
// // char* p = q;
// // if (q) {
// // //
// // // Advance to start of Content-Type value.
// // //
// // while (*p++ != ' ')
// // ;
// // //
// // // Advance to end of Content-Type value.
// // //
// // for (q = p; *q; q++) {
// // if (*q == '\r' || *q == '\n')
// // break;
// // }
// // }
// // //
// // // Strdup Content-Type value.
// // //
// // char* res = (char*) Malloc(q - p + 1);
// // strncpy(res, p, q - p);
// // res[q - p] = '\0';
// // return res;
// }
private static final int BOUNDARYSIZE = 32;
//private static final int BUFSIZE = 4 * 1024;
private static final char hexdigit[] = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};
/*
* As per RFC 1738, URLs syntax states that only alphanumerics
* and the special characters, $-_.+!*'(), and the reserved
* characters, ;/?:@&= (when used for their reserved purposes)
* may be unencoded within an URL. All others are unsafe and
* need to be escaped.
*/
private static final byte urlsafe[] = {
/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
/*20*/ 0,1,0,0,1,0,0,1,1,1,1,0,1,1,1,1, /* !"#$%&'()*+,-./ */
/*30*/ 1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, /* 0123456789:;<=>? */
/*40*/ 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* @ABCDEFGHIJKLMNO */
/*50*/ 1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1, /* PQRSTUVWXYZ[\]^_ */
/*60*/ 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* `abcdefghijklmno */
/*70*/ 1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0 /* pqrstuvwxyz{|}~? */
};
private static final byte[] base64 = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/'
};
/**
* Generates a MIME boundary string given an optional prefix.
*/
@FindBugsSuppress(code="NP")
public static byte[] mimeBoundary(String prefix) {
byte[] res = new byte[BOUNDARYSIZE - 1];
int n = 0;
if (prefix != null) {
byte[] prefixBytes = null;
try {
prefixBytes = prefix.getBytes("US-ASCII");
}
catch (UnsupportedEncodingException ignored) {
// not possible - US-ASCII is always supported
}
n = prefixBytes.length;
if (n > BOUNDARYSIZE / 4)
n = BOUNDARYSIZE / 4;
System.arraycopy(prefixBytes, 0, res, 0, n);
}
//Bug#2742678
//Generate a seed first from current time
byte[] seed = new byte[8];
long currentTime = System.currentTimeMillis();
for (int i=0;i<8;++i) seed[i]=(byte)((currentTime>>i)&0xff);
SecureRandom randomizer = new SecureRandom(seed);
for (int i = n; i < BOUNDARYSIZE - 1; i++)
res[n++] = base64[randomizer.nextInt(base64.length)];
return res;
}
// /*
// * Write 'nobj' bytes from the memory 'mem' into area 'arena'.
// * Return the number of bytes written.
// */
// public static int mimeWrite(/*char* mem, int nobj, struct arena_s* arena*/) {
// throw new ExFull(ResId.UNSUPPORTED_OPERATION, "ProtocolUtils#mimeWrite");
// // Javaport: TODO
// // int nwrote = nobj;
// // if (arena->used + nwrote > arena->size) {
// // do
// // arena->size.append(BUFSIZE);
// // while (arena->used + nwrote > arena->size);
// // arena->pbuf = (char*) Realloc(arena->pbuf, arena->size);
// // }
// // memcpy(&arena->pbuf[arena->used], mem, nwrote);
// // arena->used.append(nwrote);
// // return nwrote;
// }
/*
* Write an RFC 1867 compliant MIME section into area 'arena'.
*/
public static byte[] mimeSection(byte[] boundary, byte[] name, byte[] file,
byte[] type, byte[] value) {
ByteArrayOutputStream sArena = new ByteArrayOutputStream();
assert(boundary != null);
assert((name != null) || (file != null));
try {
sArena.write(CRLF);
sArena.write(DASH_DASH);
sArena.write(boundary);
sArena.write(CRLF);
sArena.write(CONTENT_DISPOSITION_FORM_DATA);
if (name != null) {
sArena.write(NAME_EQUALS_QUOTE);
sArena.write(name);
sArena.write(QUOTE);
}
String fileName = null;
if (file != null) {
fileName = new String(file, "US-ASCII");
if (name != null)
sArena.write(SEMICOLON_SPACE);
sArena.write(FILENAME_EQUALS_QUOTE);
sArena.write(file);
sArena.write(QUOTE);
}
byte[] contentType = null;
if (type != null)
contentType = type;
else if (file != null)
contentType = mimeType(fileName);
if (contentType != null) {
sArena.write(CRLF);
sArena.write(CONTENT_TYPE);
sArena.write(contentType);
if (!new String(contentType, "US-ASCII").startsWith("text/")) {
sArena.write(CRLF);
sArena.write(CONTENT_TRANSFER_ENCODING_BINARY);
}
}
sArena.write(CRLF);
sArena.write(CRLF);
if (file != null) {
File fp = new File(fileName);
if (!fp.exists()) {
sArena.write(FILE_NOT_FOUND);
}
else {
InputStream is = null;
try {
is = new BufferedInputStream(new FileInputStream(fileName));
byte[] buffer = new byte[4096];
int nBytesRead;
while ((nBytesRead = is.read(buffer)) > 0)
sArena.write(buffer, 0, nBytesRead);
}
catch (FileNotFoundException e) {
sArena.write(FILE_NOT_FOUND);
}
catch (UnsupportedEncodingException e) {
sArena.write(FILE_NOT_FOUND);
}
catch (IOException e) {
sArena.write(FILE_NOT_FOUND);
}
finally {
if (is != null) {
try { is.close(); }
catch (IOException ignored) {}
}
}
}
}
else if (value != null) {
sArena.write(value);
}
}
catch (IOException ignored) {
// Not possible - ByteArrayOutputStream doesn't throw IOException
}
return sArena.toByteArray();
}
/*
* Write an RFC 1867 compliant MIME trailer into area 'arena'.
*/
public static byte[] mimeTrailer(byte[] boundary) {
assert(boundary != null);
byte[] result = new byte[CRLF.length + DASH_DASH.length + boundary.length + DASH_DASH.length];
System.arraycopy(CRLF, 0, result, 0, CRLF.length);
System.arraycopy(DASH_DASH, 0, result, CRLF.length, DASH_DASH.length);
System.arraycopy(boundary, 0, result, CRLF.length + DASH_DASH.length, boundary.length);
System.arraycopy(DASH_DASH, 0, result, CRLF.length + DASH_DASH.length + boundary.length, DASH_DASH.length);
return result;
}
/*
* Determine MIME type of the given 'file'.
*/
private static byte[] mimeType(String file) {
assert(file != null);
//
// Scan through a few well-known MIME types (limited to libCurl's list
// for compatibility).
//
if (file.endsWith(".gif"))
return IMAGE_GIF;
else if (file.endsWith(".jpg") || file.endsWith(".jpeg)"))
return IMAGE_JPG;
return TEXT_PLAIN;
}
/*
* URL encodes the given string to produce content encoded as type
* application/x-www-form-urlencoded as defined in RFC 1738 (approximately).
* Note that this is not a general URL encoding function and does not
* handle current URL encoding standards as specified in RFC 3986.
* In particular, non-ASCII characters may be silently truncated.
* @param src the string to encode.
* @return the encoded string.
*/
public static String urlEncode(String src) {
int n = src.length();
int needs_encoding = 0;
for (int i = 0; i < n; i++) {
char chr = src.charAt(i);
if (chr < '\u0020' || '\u007F' < chr || urlsafe[chr - 32] == 0)
needs_encoding++;
}
if (needs_encoding == 0)
return src;
StringBuilder dst = new StringBuilder(n + needs_encoding * 2);
for (int i = 0; i < n; i++) {
char chr = src.charAt(i);
if (chr < '\u0020' || '\u007F' < chr || urlsafe[chr - 32] == 0) {
dst.append('%');
int nUCS2 = chr;
dst.append(hexdigit[nUCS2 >> 4 & 0xF]);
dst.append(hexdigit[nUCS2 & 0xF]);
}
else {
dst.append(chr);
}
}
return dst.toString();
}
/**
* URL decodes the given string.
* @param src the string to decode.
* @return the decoded string.
*/
public static String urlDecode(String src) {
assert(src != null);
StringBuilder res = new StringBuilder();
boolean needsDecoding = false;
for (int i = 0, n = src.length(); i < n; i++) {
char chr = src.charAt(i);
if (chr == '+' ) {
res.append(' ');
needsDecoding = true;
}
/* If its the first digit of hex number Then decode it. */
else if (chr == '%') {
if (i + 1 < n) {
chr = src.charAt(++i);
if ('0' <= chr && chr <= '9')
chr -= '0';
else if ('A' <= chr && chr <= 'F')
chr -= 'A' - 10;
else /* if ('a' <= chr && chr <= 'f') */
chr -= 'a' - 10;
int hex = chr * 16;
if (i + 1 < n) {
chr = src.charAt(++i);
if ('0' <= chr && chr <= '9')
chr -= '0';
else if ('A' <= chr && chr <= 'F')
chr -= 'A' - 10;
else /* if ('a' <= chr && chr <= 'f') */
chr -= 'a' - 10;
hex += chr;
res.append((char) hex);
}
}
needsDecoding = true;
}
/* Else its just a regular character */
else
res.append(chr);
}
return needsDecoding ? res.toString() : src;
}
public static String getTemplateBasePathFromConfig(Node contextNode) {
String sBasePath = null;
if (contextNode != null) {
Node tree = contextNode.resolveNode("template.base");
if (tree != null && tree.getXFAChildCount() == 1) {
TextNode textNode = (TextNode) tree.getFirstXFAChild();
sBasePath = textNode.getValue();
}
}
return sBasePath;
}
public static String getTemplateUriPathFromConfig(Node contextNode) {
String sUriPath = null;
if (contextNode != null) {
Node tree = contextNode.resolveNode("template.uri");
if (tree != null && tree.getXFAChildCount() == 1) {
TextNode textNode = (TextNode) tree.getFirstXFAChild();
sUriPath = textNode.getValue();
//
// Strip away the filename.
//
if (sUriPath.length() > 0) {
char cSep = getFileOrUriSeparator(sUriPath);
//
// Purge the filename from the path.
//
int nSlash = sUriPath.lastIndexOf(cSep);
if (nSlash != sUriPath.length() - 1)
sUriPath = sUriPath.substring(0, nSlash - 1);
}
}
}
return sUriPath;
}
private static char getFileOrUriSeparator(String uri) {
return uri.indexOf('\\') >= 0 ? '\\' : '/';
}
}