package org.limewire.lws.server;
import java.net.InetAddress;
import java.net.URLDecoder;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.TimeZone;
import org.limewire.io.NetworkUtils;
import org.limewire.service.ErrorService;
/**
* Utility methods for this package.
*/
public final class LWSServerUtil {
private final static String ERROR_START = "ERROR:";
final static Map<String, String> EMPTY_MAP_OF_STRING_X_STRING = new HashMap<String, String>();
private LWSServerUtil() {
// nothing
}
/**
* Returns <code>true</code> if <code>s</code> is <code>null</code> or
* <code>""</code>, otherwise <code>false</code>.
*
* @param s {@link String} in question
* @return <code>true</code> if <code>s</code> is <code>null</code> or
* <code>""</code>, otherwise <code>false</code>.
*/
public static boolean isEmpty(final String s) {
return s == null || s.equals("");
}
/**
* Parses and returns CGI arguments.
*
* @param rest the string to parse
* @return CGI arguments from <tt>rest</tt>
*/
public static Map<String, String> parseArgs(final String rest) {
if (isEmpty(rest))
return Collections.emptyMap();
final Map<String, String> res = new HashMap<String, String>(3);
for (StringTokenizer st = new StringTokenizer(rest, "&", false); st
.hasMoreTokens();) {
final String pair = st.nextToken();
final int ieq = pair.indexOf('=');
String key, val;
if (ieq == -1) {
key = pair;
val = null;
} else {
key = pair.substring(0, ieq);
val = pair.substring(ieq + 1);
}
res.put(key.trim(), val == null ? val : val.trim());
}
return res;
}
/**
* Returns a string suitable for the date for a cookie.
*
* @return a string suitable for the date for a cookie
*/
public static String createCookieDate() {
// Wdy, DD-Mon-YYYY HH:MM:SS GMT
Format f = new SimpleDateFormat("E, dd-MM-yyyy kk:mm:ss");
Date date = Calendar.getInstance(TimeZone.getTimeZone("GMT")).getTime();
return f.format(date) + " GMT";
}
/**
* Returns the simple name for the class <tt>c</tt>.
*
* @param c the class in question
* @return the simple name for the class <tt>c</tt>
*/
static String simpleName(final Class c) {
String s = c.getName();
int ilastDot = s.lastIndexOf(".");
return ilastDot == -1 ? s : s.substring(ilastDot + 1);
}
/**
* Returns the error code found in {@link ErrorCodes} or <tt>null</tt> if it's
* not an error.
*
* @param line {@link String} containing an error to unwrap
* @return error code found in {@link ErrorCodes} or <tt>null</tt> if it's
* not an error
*/
public static String unwrapError(final String line) {
if (line.startsWith(ERROR_START)) {
return line.substring(ERROR_START.length()).trim();
} else {
return null;
}
}
/**
* Returns the appropriate string for identifying an error.
*
* @param error the error message
* @return the appropriate string for identifying an error
*/
public static String wrapError(final String error) {
return ERROR_START + error;
}
/**
* Will remove the method application to <tt>res</tt>.
*
*/
public static String removeCallback(final String res) {
if (res == null) return null;
String start = "(" + LWSDispatcherSupport.Constants.CALLBACK_QUOTE_STRING;
String end = LWSDispatcherSupport.Constants.CALLBACK_QUOTE_STRING + ")";
int istart = res.indexOf(start);
if (istart == -1) return res;
int iend = res.lastIndexOf(end);
if (iend == -1) return res;
return res.substring(istart + start.length(), iend);
}
private static boolean isValidKey(final String key) {
if (key == null || key.equals("")) return false;
//
// No periods, this is an indication of an error
//
for (int i=0, I=key.length(); i<I; i++) {
if (key.charAt(i) == '.') return false;
}
return true;
}
/**
* Returns <tt>true</tt> if <tt>key</tt> is a valid public key,
* <tt>false</tt> otherwise.
*
* @param key key in question
* @return <tt>true</tt> if <tt>key</tt> is a valid public key,
* <tt>false</tt> otherwise
*/
public static boolean isValidPublicKey(final String key) {
return isValidKey(key);
}
/**
* Returns <tt>true</tt> if <tt>key</tt> is a valid shared key,
* <tt>false</tt> otherwise.
*
* @param key key in question
* @return <tt>true</tt> if <tt>key</tt> is a valid shared key,
* <tt>false</tt> otherwise
*/
public static boolean isValidSharedKey(final String key) {
return isValidKey(key);
}
/**
* Returns <tt>true</tt> if <tt>key</tt> is a valid private key,
* <tt>false</tt> otherwise.
*
* @param key key in question
* @return <tt>true</tt> if <tt>key</tt> is a valid private key,
* <tt>false</tt> otherwise
*/
public static boolean isValidPrivateKey(final String key) {
return isValidKey(key);
}
/**
* Returns the string IP address for the given address.
*
* @param addr address in question
* @return the string IP address for the given address
*/
public static String getIPAddress(final InetAddress addr) {
if (addr == null)
return null;
byte[] bs = addr.getAddress();
return NetworkUtils.ip2string(bs);
}
/**
* Generates a public or private key. <br>
* INVARIANT: {@link #isValidPublicKey(String)}({@link #generateKey()})
* <tt> == true</tt> and {@link #isValidPrivateKey(String)}({@link #generateKey()})
* <tt> == true</tt>
*
* @return a public or private key
*/
public static String generateKey() {
final StringBuilder sb = new StringBuilder();
for (int i = 0; i < LWSDispatcherSupport.Constants.KEY_LENGTH;) {
final int r = 'A' + (int) (Math.random() * ('Z' - 'A'));
final char c = (char) r;
if (c == ';')
continue;
sb.append(c);
i++;
}
return sb.toString();
}
/**
* Returns a string with the urlencoded arguments removed with these
* arguments separated by {@link Constants#ARGUMENT_SEPARATOR}. This method
* has the side-affect of putting these urlencoded arguments into
* <tt>args</tt>. Arguments with no equals sign will have
* <code>null</code> values, arguments with nothing after the equal sign
* will have an empty value, non-empty arguments will be as expected. Here
* are a couple examples:
* <ul>
* <li>one=1 → <code>{one=1}</code></li>
* <li>one= → <code>{one=}</code></li>
* <li>one → <code>{one=null}</code></li>
* </ul>
*
* @param cmd original command, this can be <code>null</code>
* @param args original arguments (SIDEEFFECT: these are updated)
* @return a string with the urlencoded arguments removed with these
* arguments separated by {@link Constants#ARGUMENT_SEPARATOR}
*/
public static String addURLEncodedArguments(final String cmd,
final Map<String, String> args) {
if (cmd == null) return null;
int ihuh = cmd.indexOf("?");
if (ihuh == -1) return cmd;
final String newCmd = cmd.substring(0, ihuh);
//
// We have to decode the rest because the commands are identified using
// CGI parameters, but the value of the arguments are often parameters,
// and get encoded.
//
String tmpRest = cmd.substring(ihuh + 1);
try {
tmpRest = URLDecoder.decode(tmpRest, "UTF-8");
} catch (Exception e) { ErrorService.error(e); }
final String rest = tmpRest;
for (StringTokenizer st = new StringTokenizer(rest, "&", false);
st.hasMoreTokens();) {
final String tok = st.nextToken();
int ieq = tok.indexOf('=');
String key, val;
if (ieq == -1) {
key = tok;
val = null;
} else {
key = tok.substring(0, ieq);
val = tok.substring(ieq + 1);
}
args.put(key, val);
}
return newCmd;
}
/**
* Returns <code>true</code> is <code>res</code> isn't <code>null</code>
* and starts with {@link #ERROR_START}.
*
* @param res the {@link String} in question
* @return <code>true</code> is <code>res</code> isn't <code>null</code>
* and starts with {@link #ERROR_START}
*/
public static boolean isError(String res) {
return res != null && res.startsWith(ERROR_START);
}
}