diff --git a/exemple.gif b/exemple.gif new file mode 100644 index 0000000..7f8171a Binary files /dev/null and b/exemple.gif differ diff --git a/src/zutil/FileFinder.java b/src/zutil/FileFinder.java index ead7cf3..494bf05 100644 --- a/src/zutil/FileFinder.java +++ b/src/zutil/FileFinder.java @@ -1,12 +1,16 @@ package zutil; +import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.File; +import java.io.FileInputStream; import java.io.FileReader; import java.io.IOException; +import java.io.InputStream; import java.net.URISyntaxException; import java.net.URL; -import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; import java.util.regex.Matcher; /** @@ -19,8 +23,8 @@ public class FileFinder { /** * Returns a String with a relative path from the given path * - * @param file The file to get a relative path from - * @param path The path + * @param file is the file to get a relative path from + * @param path is the path * @return A String with a relative path */ public static String relativePath(File file, String path){ @@ -36,11 +40,11 @@ public class FileFinder { } /** - * Returns the File object for the given file + * Returns the File object for the given file. + * Can not point to files in JAR files. * - * @param path The path to the file (no / if not absolute path) + * @param path is the path to the file (no / if not absolute path) * @return A File object for the file - * @throws URISyntaxException */ public static File find(String path){ try { @@ -50,17 +54,46 @@ public class FileFinder { } return new File(findURL(path).toURI()); } catch (Exception e) { - e.printStackTrace(); + //e.printStackTrace(MultiPrintStream.out); } return null; } + /** + * Returns the URL to the given file + * + * @param path is the path to the file (no / if not absolute path) + * @return A URL object for the file + * @throws URISyntaxException + */ + public static URL findURL(String path){ + return FileFinder.class.getClassLoader().getResource(path); + } + + /** + * Returns a InputStream from the path + * + * @param path is the path to the file (no / if not absolute path) + * @return A InputStream object for the file + */ + public static InputStream getInputStream(String path){ + try { + File file = new File(path); + if(file!=null && file.exists()){ + return new BufferedInputStream( new FileInputStream( file ) ); + } + return FileFinder.class.getClassLoader().getResourceAsStream(path); + } catch (Exception e) { + //e.printStackTrace(MultiPrintStream.out); + } + return null; + } /** * Reads and returns the content of a file as a String. * Or use FileUtils.readFileToString(file); * - * @param file The file to read + * @param file is the file to read * @return The file content * @throws IOException */ @@ -78,45 +111,35 @@ public class FileFinder { } /** - * Returns the URL to the given file + * Returns a ArrayList with all the files in a folder and sub folders * - * @param path The path to the file (no / if not absolute path) - * @return A URL object for the file - * @throws URISyntaxException + * @param dir is the directory to search in + * @return The ArrayList with the files */ - public static URL findURL(String path){ - return FileFinder.class.getClassLoader().getResource(path); + public static List search(File dir){ + return search(dir, new LinkedList(), true); } /** * Returns a ArrayList with all the files in a folder and sub folders * - * @param dir The directory to search in + * @param dir is the directory to search in + * @param fileList is the ArrayList to add the files to + * @param recursice is if the method should search the sub directories to. * @return The ArrayList with the files */ - public static ArrayList search(File dir){ - return search(dir, new ArrayList()); - } - - /** - * Returns a ArrayList with all the files in a folder and sub folders - * - * @param dir The directory to search in - * @param fileList The ArrayList to add the files to - * @return The ArrayList with the files - */ - public static ArrayList search(File dir, ArrayList fileList){ + public static List search(File dir, List fileList, boolean recursive){ String[] temp = dir.list(); File file; if(temp != null){ for(int i=0; i { - public int history_length = 10; - private LinkedList history; - private int historyIndex = 0; - - public History(int histlength){ - history_length = histlength; - history = new LinkedList(); - } - - public void addToHistory(T url){ - while(historyIndex < history.size()-1){ - history.removeLast(); - } - history.addLast(url); - if(history_length < history.size()){ - history.removeFirst(); - } - - historyIndex = history.size()-1; - } - - public T getBackHistory(){ - if(historyIndex > 0){ - historyIndex -= 1; - } - else{ - historyIndex = 0; - } - return history.get(historyIndex); - } - - public T getForwHistory(){ - if(forwHistoryExist()){ - historyIndex += 1; - } - else{ - historyIndex = history.size()-1; - } - return history.get(historyIndex); - } - - public T getCurrentHistory(){ - return history.get(historyIndex); - } - - public boolean forwHistoryExist(){ - if(historyIndex < history.size()-1){ - return true; - } - else{ - return false; - } - } -} diff --git a/src/zutil/MultiPrintStream.java b/src/zutil/MultiPrintStream.java index c95ce6b..91cc65e 100644 --- a/src/zutil/MultiPrintStream.java +++ b/src/zutil/MultiPrintStream.java @@ -332,9 +332,11 @@ public class MultiPrintStream extends PrintStream { for ( int i=0; i)return true; - else if(o instanceof Map)return true; + else if(o instanceof Collection)return true; + else if(o instanceof Map)return true; else if(o instanceof InputStream)return true; else if(o instanceof Reader)return true; else if(o instanceof Dumpable)return true; diff --git a/src/zutil/db/MySQLConnection.java b/src/zutil/db/MySQLConnection.java index 89af2d4..e5f9885 100644 --- a/src/zutil/db/MySQLConnection.java +++ b/src/zutil/db/MySQLConnection.java @@ -21,6 +21,7 @@ public class MySQLConnection { public MySQLConnection(String url, String db, String user, String password) throws SQLException, InstantiationException, IllegalAccessException, ClassNotFoundException{ Class.forName ("com.mysql.jdbc.Driver").newInstance(); + DriverManager.setLoginTimeout(10); conn = DriverManager.getConnection ("jdbc:mysql://"+url+"/"+db, user, password); } @@ -48,8 +49,11 @@ public class MySQLConnection { Statement s = conn.createStatement (); s.executeQuery(sql); ResultSet result = s.getResultSet(); - if(result.next()) - return result.getString(0); + if(result.next()){ + String tmp = result.getString(1); + result.close(); + return tmp; + } return null; } @@ -66,6 +70,22 @@ public class MySQLConnection { return ret; } + /** + * @return the last inserted id or -1 if there was an error + * @throws SQLException + */ + public int getLastInsertID() throws SQLException{ + Statement s = conn.createStatement (); + s.executeQuery("SELECT LAST_INSERT_ID()"); + ResultSet result = s.getResultSet(); + if(result.next()){ + int tmp = result.getInt(1); + result.close(); + return tmp; + } + return -1; + } + /** * Runs a Prepared Statement.
* NOTE: Don't forget to close the PreparedStatement or it can lead to memory leak diff --git a/src/zutil/image/ImageUtil.java b/src/zutil/image/ImageUtil.java index 3c13e2f..7ff9405 100644 --- a/src/zutil/image/ImageUtil.java +++ b/src/zutil/image/ImageUtil.java @@ -1,372 +1,48 @@ package zutil.image; +import java.awt.Graphics2D; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; + /** - * Some util methods for image processing + * This is a static class containing image utilities + * * @author Ziver - * */ public class ImageUtil { - - /** - * Returns the peek value in the image - * - * @param data The image data - * @param startX is the x pixel of the image to start from - * @param startY is the y pixel of the image to start from - * @param stopX is the x pixel of the image to stop - * @param stopY is the y pixel of the image to stop - * @return The peak value of the image - */ - public static int getPeakValue(int[][][] data) { - return getPeakValue(data, 0, 0, data[0].length, data.length); - } - - /** - * Returns the peek value in the image - * - * @param data The image data - * @param startX is the x pixel of the image to start from - * @param startY is the y pixel of the image to start from - * @param stopX is the x pixel of the image to stop - * @param stopY is the y pixel of the image to stop - * @return The peak value of the image - */ - public static int getPeakValue(int[][][] data, int startX, int startY, int stopX, int stopY) { - int peak = 0; - for(int y=startY; y peak) peak = data[y][x][1]; - if(data[y][x][2] > peak) peak = data[y][x][2]; - if(data[y][x][3] > peak) peak = data[y][x][3]; - } - } - return peak; - } /** - * Normalizes the image data by the given scale + * Resizes an BufferedImage * - * @param data The image data - * @param startX is the x pixel of the image to start from - * @param startY is the y pixel of the image to start from - * @param stopX is the x pixel of the image to stop - * @param stopY is the y pixel of the image to stop - * @param scale The scale to normalize the image by + * @param source is the image to resize + * @param width is the wanted width + * @param height is the wanted height + * @param keep_aspect is if the aspect ratio of the image should be kept + * @return the resized image */ - public static void normalize(int[][][] data, int startX, int startY, int stopX, int stopY, double scale) { - for(int y=startY; y height){ + scale_width = scale_height; + }else{ + scale_height = scale_width; } } - int meanSquare = (int)(accum/pixelCount); - int rms = (int)(Math.sqrt(meanSquare)); - return rms; - } - /** - * Multiplies the given image data by the given value - * - * @param data is the image data - * @param startX is the x pixel of the image to start from - * @param startY is the y pixel of the image to start from - * @param stopX is the x pixel of the image to stop - * @param stopY is the y pixel of the image to stop - * @param scale is the number to scale the image color by - */ - public static void scale(int[][][] data, int startX, int startY, int stopX, int stopY, double scale){ - for(int y=startY; y 255) - return 255; - else - return color; + AffineTransform at = AffineTransform.getScaleInstance(scale_width, scale_height); + g2d.drawRenderedImage(source, at); + g2d.dispose(); + return tmp; } } diff --git a/src/zutil/image/RAWImageUtil.java b/src/zutil/image/RAWImageUtil.java new file mode 100644 index 0000000..b5887ce --- /dev/null +++ b/src/zutil/image/RAWImageUtil.java @@ -0,0 +1,372 @@ +package zutil.image; + +/** + * Some util methods for image processing + * @author Ziver + * + */ +public class RAWImageUtil { + + /** + * Returns the peek value in the image + * + * @param data The image data + * @param startX is the x pixel of the image to start from + * @param startY is the y pixel of the image to start from + * @param stopX is the x pixel of the image to stop + * @param stopY is the y pixel of the image to stop + * @return The peak value of the image + */ + public static int getPeakValue(int[][][] data) { + return getPeakValue(data, 0, 0, data[0].length, data.length); + } + + /** + * Returns the peek value in the image + * + * @param data The image data + * @param startX is the x pixel of the image to start from + * @param startY is the y pixel of the image to start from + * @param stopX is the x pixel of the image to stop + * @param stopY is the y pixel of the image to stop + * @return The peak value of the image + */ + public static int getPeakValue(int[][][] data, int startX, int startY, int stopX, int stopY) { + int peak = 0; + for(int y=startY; y peak) peak = data[y][x][1]; + if(data[y][x][2] > peak) peak = data[y][x][2]; + if(data[y][x][3] > peak) peak = data[y][x][3]; + } + } + return peak; + } + + /** + * Normalizes the image data by the given scale + * + * @param data The image data + * @param startX is the x pixel of the image to start from + * @param startY is the y pixel of the image to start from + * @param stopX is the x pixel of the image to stop + * @param stopY is the y pixel of the image to stop + * @param scale The scale to normalize the image by + */ + public static void normalize(int[][][] data, int startX, int startY, int stopX, int stopY, double scale) { + for(int y=startY; y 255) + return 255; + else + return color; + } +} diff --git a/src/zutil/image/filters/BlurFilter.java b/src/zutil/image/filters/BlurFilter.java index 9e0d9b3..96f7e04 100644 --- a/src/zutil/image/filters/BlurFilter.java +++ b/src/zutil/image/filters/BlurFilter.java @@ -3,7 +3,7 @@ package zutil.image.filters; import java.awt.image.BufferedImage; import zutil.image.ImageFilterProcessor; -import zutil.image.ImageUtil; +import zutil.image.RAWImageUtil; import zutil.math.ZMath; public class BlurFilter extends ImageFilterProcessor{ @@ -29,10 +29,10 @@ public class BlurFilter extends ImageFilterProcessor{ @Override public int[][][] process(int[][][] data, int startX, int startY, int stopX, int stopY) { - int inputPeak = ImageUtil.getPeakValue(data); + int inputPeak = RAWImageUtil.getPeakValue(data); int[][][] tmpData = new int[data.length][data[0].length][4]; - int[][][] output = ImageUtil.copyArray(data); + int[][][] output = RAWImageUtil.copyArray(data); //Perform the convolution one or more times in succession int redSum, greenSum, blueSum, outputPeak; for(int i=0; i 0) - output[y+1][x-1][i] = ImageUtil.clip( output[y+1][x-1][i] + (error*3)/16 ); - output[y+1][x+0][i] = ImageUtil.clip( output[y+1][x+0][i] + (error*5)/16 ); + output[y+1][x-1][i] = RAWImageUtil.clip( output[y+1][x-1][i] + (error*3)/16 ); + output[y+1][x+0][i] = RAWImageUtil.clip( output[y+1][x+0][i] + (error*5)/16 ); if (x + 1 < data[0].length) - output[y+1][x+1][i] = ImageUtil.clip( output[y+1][x+1][i] + (error*1)/16 ); + output[y+1][x+1][i] = RAWImageUtil.clip( output[y+1][x+1][i] + (error*1)/16 ); } } } diff --git a/src/zutil/image/filters/MedianFilter.java b/src/zutil/image/filters/MedianFilter.java index 0bd9443..0cad9b8 100644 --- a/src/zutil/image/filters/MedianFilter.java +++ b/src/zutil/image/filters/MedianFilter.java @@ -4,7 +4,7 @@ import java.awt.image.BufferedImage; import zutil.algo.sort.sortable.SortableDataList; import zutil.image.ImageFilterProcessor; -import zutil.image.ImageUtil; +import zutil.image.RAWImageUtil; import zutil.math.ZMath; /** @@ -62,7 +62,7 @@ public class MedianFilter extends ImageFilterProcessor{ */ @Override public int[][][] process(int[][][] data, int startX, int startY, int stopX, int stopY) { - int[][][] tmpData = ImageUtil.copyArray(data); + int[][][] tmpData = RAWImageUtil.copyArray(data); int edgeX = windowSize / 2; int edgeY = windowSize / 2; diff --git a/src/zutil/image/filters/SpotLightFilter.java b/src/zutil/image/filters/SpotLightFilter.java index 5ce549b..101d598 100644 --- a/src/zutil/image/filters/SpotLightFilter.java +++ b/src/zutil/image/filters/SpotLightFilter.java @@ -3,7 +3,7 @@ package zutil.image.filters; import java.awt.image.BufferedImage; import zutil.image.ImageFilterProcessor; -import zutil.image.ImageUtil; +import zutil.image.RAWImageUtil; import zutil.math.ZMath; public class SpotLightFilter extends ImageFilterProcessor{ @@ -64,9 +64,9 @@ public class SpotLightFilter extends ImageFilterProcessor{ } output[y][x][0] = data[y][x][0]; - output[y][x][1] = ImageUtil.clip((int)(scale * data[y][x][1])); - output[y][x][2] = ImageUtil.clip((int)(scale * data[y][x][2])); - output[y][x][3] = ImageUtil.clip((int)(scale * data[y][x][3])); + output[y][x][1] = RAWImageUtil.clip((int)(scale * data[y][x][1])); + output[y][x][2] = RAWImageUtil.clip((int)(scale * data[y][x][2])); + output[y][x][3] = RAWImageUtil.clip((int)(scale * data[y][x][3])); } } return output; diff --git a/src/zutil/network/SSDPServer.java b/src/zutil/network/SSDPServer.java new file mode 100644 index 0000000..442d68d --- /dev/null +++ b/src/zutil/network/SSDPServer.java @@ -0,0 +1,323 @@ +package zutil.network; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.InetAddress; +import java.util.HashMap; +import java.util.Timer; +import java.util.TimerTask; +import java.util.UUID; + +import zutil.MultiPrintStream; +import zutil.network.http.HTTPHeaderParser; +import zutil.network.http.HttpPrintStream; +import zutil.network.threaded.ThreadedUDPNetworkThread; +import zutil.network.threaded.ThreadedUDPNetwork; +import zutil.wrapper.StringOutputStream; + +/** + * A Server class that announces an service by the SSDP + * protocol specified at: + * http://coherence.beebits.net/chrome/site/draft-cai-ssdp-v1-03.txt + * ftp://ftp.pwg.org/pub/pwg/www/hypermail/ps/att-0188/01-psi_SSDP.pdf + * + * @author Ziver + * + * ********* Message clarification: + * ****** Incoming: + * ST: Search Target, this is object of the discovery request, (e.g., ssdp:all, etc.) + * HOST: This is the SSDP multicast address + * MAN: Description of packet type, (e.g., "ssdp:discover", ) + * MX: Wait these few seconds and then send response + * + * ****** Outgoing: + * EXT: required by HTTP - not used with SSDP + * SERVER: informational + * LOCATION: This is the URL to request the QueryEndpointsInterface endpoint + * USN: advertisement UUID + * CACHE-CONTROL: max-age = seconds until advertisement expires + * NT: Notify target same as ST + * NTS: same as Man but for Notify messages + */ +public class SSDPServer extends ThreadedUDPNetwork implements ThreadedUDPNetworkThread{ + public static final String SERVER_INFO = "SSDP Java Server by Ziver Koc"; + public static final int DEFAULT_CACHE_TIME = 60*30; // 30 min + public static final int BUFFER_SIZE = 512; + public static final String SSDP_MULTICAST_ADDR = "239.255.255.250"; + public static final int SSDP_PORT = 1900; + + // instance specific values + private int cache_time; + private NotifyTimer notifyTimer = null; + /** HashMap that contains services as < SearchTargetName, Location > */ + private HashMap services; + /** A Map of all the used USN:s */ + private HashMap usn_map; + + + public static void main(String[] args) throws IOException{ + SSDPServer ssdp = new SSDPServer(); + ssdp.addService("upnp:rootdevice", "nowhere"); + ssdp.start(); + MultiPrintStream.out.println("SSDP Server running"); + } + + public SSDPServer() throws IOException{ + super( null, SSDP_PORT, SSDP_MULTICAST_ADDR ); + super.setThread( this ); + + services = new HashMap(); + usn_map = new HashMap(); + + setChacheTime( DEFAULT_CACHE_TIME ); + enableNotify( true ); + } + + /** + * Adds an service that will be announced. + * + * @param searchTarget is the ST value in SSDP + * @param location is the location of the service + */ + public void addService(String searchTarget, String location){ + services.put( searchTarget, location ); + } + /** + * Remove a service from being announced. This function will + * send out an byebye message to the clients that the service is down. + * + * @param searchTarget is the ST value in SSDP + */ + public void removeService(String searchTarget){ + sendByeBye( searchTarget ); + services.remove( searchTarget ); + } + + /** + * Sets the cache time that will be sent to + * the clients. If notification is enabled then an + * notification message will be sent every cache_time/2 seconds + * + * @param time is the time in seconds + */ + public void setChacheTime(int time){ + cache_time = time; + if( isNotifyEnabled() ){ + enableNotify(false); + enableNotify(true); + } + } + + /** + * Enable or disable notification messages to clients + * every cache_time/2 seconds + */ + public void enableNotify(boolean enable){ + if( enable && notifyTimer==null ){ + notifyTimer = new NotifyTimer(); + Timer timer = new Timer(); + timer.schedule(new NotifyTimer(), 0, cache_time*1000/2); + }else if( !enable && notifyTimer!=null ){ + notifyTimer.cancel(); + notifyTimer = null; + } + } + /** + * @return if notification messages is enabled + */ + public boolean isNotifyEnabled(){ + return notifyTimer != null; + } + + /** + * Handles the incoming packets like this: + * + * ***** REQUEST: + * M-SEARCH * HTTP/1.1 + * S: uuid:ijklmnop-7dec-11d0-a765-00a0c91e6bf6 + * Host: 239.255.255.250:reservedSSDPport + * Man: "ssdp:discover" + * ST: ge:fridge + * MX: 3 + * + * ***** RESPONSE; + * HTTP/1.1 200 OK + * S: uuid:ijklmnop-7dec-11d0-a765-00a0c91e6bf6 + * Ext: + * Cache-Control: no-cache="Ext", max-age = 5000 + * ST: ge:fridge + * USN: uuid:abcdefgh-7dec-11d0-a765-00a0c91e6bf6 + * AL: + * + */ + public void receivedPacket(DatagramPacket packet, ThreadedUDPNetwork network) { + try { + String msg = new String( packet.getData() ); + + HTTPHeaderParser header = new HTTPHeaderParser( msg ); + MultiPrintStream.out.println(header); + + // ******* Respond + // Check that the message is an ssdp discovery message + if( header.getRequestType().equalsIgnoreCase("M-SEARCH") ){ + String man = header.getHTTPAttribute("Man").replace("\"", ""); + String st = header.getHTTPAttribute("ST"); + // Check that its the correct URL and that its an ssdp:discover message + if( header.getRequestURL().equals("*") && man.equalsIgnoreCase("ssdp:discover") ){ + // Check if the requested service exists + if( services.containsKey( st ) ){ + // Generate the SSDP response + StringOutputStream response = new StringOutputStream(); + HttpPrintStream http = new HttpPrintStream( response ); + http.setStatusCode(200); + http.setHeader("Server", SERVER_INFO ); + http.setHeader("ST", st ); + http.setHeader("Location", services.get(st) ); + http.setHeader("EXT", "" ); + http.setHeader("Cache-Control", "max-age = "+cache_time ); + http.setHeader("USN", getUSN(st) ); + + http.close(); + MultiPrintStream.out.println("\n"+response); + byte[] data = response.toString().getBytes(); + packet = new DatagramPacket( + data, data.length, + packet.getAddress(), + packet.getPort()); + network.send( packet ); + } + } + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + + /** + * This thread is a timer task that sends an + * notification message to the network every + * cache_time/2 seconds. + * + * @author Ziver + */ + private class NotifyTimer extends TimerTask { + public void run(){ + sendNotify(); + } + } + /** + * Sends keepalive messages to update the cache of the clients + */ + public void sendNotify(){ + for(String st : services.keySet()){ + sendNotify( st ); + } + } + /** + * Sends an keepalive message to update the cache of the clients + * + * @param searchTarget is the ST value of the service + * + * ********** Message ex: + * NOTIFY * HTTP/1.1 + * Host: 239.255.255.250:reservedSSDPport + * NT: blenderassociation:blender + * NTS: ssdp:alive + * USN: someunique:idscheme3 + * AL: + * Cache-Control: max-age = 7393 + */ + public void sendNotify(String searchTarget){ + try { + // Generate the SSDP response + StringOutputStream msg = new StringOutputStream(); + HttpPrintStream http = new HttpPrintStream( msg, HttpPrintStream.HTTPMessageType.REQUEST ); + http.setRequestType("NOTIFY"); + http.setRequestURL("*"); + http.setHeader("Server", SERVER_INFO ); + http.setHeader("Host", SSDP_MULTICAST_ADDR+":"+SSDP_PORT ); + http.setHeader("NT", searchTarget ); + http.setHeader("NTS", "ssdp:alive" ); + http.setHeader("Location", services.get(searchTarget) ); + http.setHeader("Cache-Control", "max-age = "+cache_time ); + http.setHeader("USN", getUSN(searchTarget) ); + + http.close(); + MultiPrintStream.out.println("\n"+msg); + byte[] data = msg.toString().getBytes(); + DatagramPacket packet = new DatagramPacket( + data, data.length, + InetAddress.getByName( SSDP_MULTICAST_ADDR ), + SSDP_PORT ); + super.send( packet ); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + + /** + * Shutdown message is sent to the clients that all + * the service is shutting down. + */ + public void sendByeBye(){ + for(String st : services.keySet()){ + sendByeBye( st ); + } + } + /** + * Shutdown message is sent to the clients that the service is shutting down + * + * @param searchTarget is the ST value of the service + * + * ********** Message ex: + * NOTIFY * HTTP/1.1 + * Host: 239.255.255.250:reservedSSDPport + * NT: someunique:idscheme3 + * NTS: ssdp:byebye + * USN: someunique:idscheme3 + */ + public void sendByeBye(String searchTarget){ + try { + // Generate the SSDP response + StringOutputStream msg = new StringOutputStream(); + HttpPrintStream http = new HttpPrintStream( msg, HttpPrintStream.HTTPMessageType.REQUEST ); + http.setRequestType("NOTIFY"); + http.setRequestURL("*"); + http.setHeader("Server", SERVER_INFO ); + http.setHeader("Host", SSDP_MULTICAST_ADDR+":"+SSDP_PORT ); + http.setHeader("NT", searchTarget ); + http.setHeader("NTS", "ssdp:byebye" ); + http.setHeader("USN", getUSN(searchTarget) ); + + http.close(); + MultiPrintStream.out.println("\n"+msg); + byte[] data = msg.toString().getBytes(); + DatagramPacket packet = new DatagramPacket( + data, data.length, + InetAddress.getByName( SSDP_MULTICAST_ADDR ), + SSDP_PORT ); + super.send( packet ); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Generates an unique USN for the service + * + * @param searchTarget is the service ST name + * @return an unique string that corresponds to the service + */ + public String getUSN(String searchTarget){ + if( !usn_map.containsKey( searchTarget ) ){ + String usn = "uuid:" + UUID.nameUUIDFromBytes( (searchTarget+services.get(searchTarget)).getBytes() ); + usn_map.put( searchTarget, usn ); + return usn; + } + return usn_map.get( searchTarget ); + } +} diff --git a/src/zutil/network/http/HTTPHeaderParser.java b/src/zutil/network/http/HTTPHeaderParser.java new file mode 100644 index 0000000..1aaaf7f --- /dev/null +++ b/src/zutil/network/http/HTTPHeaderParser.java @@ -0,0 +1,237 @@ +package zutil.network.http; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.HashMap; +import java.util.Scanner; +import java.util.regex.Pattern; + +public class HTTPHeaderParser { + // Some Cached regexes + private static final Pattern colonPattern = Pattern.compile(":"); + private static final Pattern equalPattern = Pattern.compile("="); + private static final Pattern andPattern = Pattern.compile("&"); + private static final Pattern semiColonPattern = Pattern.compile(";"); + + // HTTP info + private String type; + private String url; + private HashMap url_attr; + private float version; + + // params + private HashMap attributes; + private HashMap cookies; + + /** + * Parses the HTTP header information from the stream + * + * @param in is the stream + * @throws IOException + */ + public HTTPHeaderParser(BufferedReader in) throws IOException{ + url_attr = new HashMap(); + attributes = new HashMap(); + cookies = new HashMap(); + + String tmp = null; + if( (tmp=in.readLine()) != null && !tmp.isEmpty() ){ + parseStartLine( tmp ); + while( (tmp=in.readLine()) != null && !tmp.isEmpty() ){ + parseLine( tmp ); + } + } + parseCookies(); + } + + /** + * Parses the HTTP header information from an String + * + * @param in is the string + */ + public HTTPHeaderParser(String in){ + url_attr = new HashMap(); + attributes = new HashMap(); + cookies = new HashMap(); + + Scanner sc = new Scanner(in); + sc.useDelimiter("\n"); + String tmp = null; + if( sc.hasNext() && !(tmp=sc.next()).isEmpty() ){ + parseStartLine( tmp ); + while( sc.hasNext() && !(tmp=sc.next()).isEmpty() ){ + parseLine( tmp ); + } + } + parseCookies(); + } + + /** + * Parses the first header line and ads the values to + * the map and returns the file name and path + * + * @param header The header String + * @param map The HashMap to put the variables to + * @return The path and file name as a String + */ + protected void parseStartLine(String line){ + type = (line.substring(0, line.indexOf(" "))).trim(); + version = Float.parseFloat( line.substring(line.lastIndexOf("HTTP/")+5 , line.length()).trim() ); + line = (line.substring(type.length()+1, line.lastIndexOf("HTTP/"))).trim(); + + // parse URL and attributes + if(line.indexOf('?') > -1){ + url = line.substring(0, line.indexOf('?')); + line = line.substring(line.indexOf('?')+1, line.length()); + parseUrlAttributes(line, url_attr); + } + else{ + url = line; + } + } + + /** + * Parses a String with variables from a get or post + * that was sent from a client and puts the data into a HashMap + * + * @param header is the String containing all the attributes + * @param map is the HashMap to put all the values into + */ + public static void parseUrlAttributes(String attributes, HashMap map){ + String[] tmp; + // get the variables + String[] data = andPattern.split( attributes ); + for(String element : data){ + tmp = equalPattern.split(element, 2); + map.put( + tmp[0].trim(), // Key + (tmp.length>1 ? tmp[1] : "").trim()); //Value + } + } + + /** + * Parses the rest of the header + * + * @param line is the next line in the header + */ + protected void parseLine(String line){ + String[] data = colonPattern.split( line, 2 ); + attributes.put( + data[0].trim().toUpperCase(), // Key + (data.length>1 ? data[1] : "").trim()); //Value + } + + /** + * Parses the attribute "Cookie" and returns a HashMap + * with the values + * + * @return a HashMap with cookie values + */ + protected void parseCookies(){ + if( attributes.containsKey("COOKIE") ){ + String[] tmp = semiColonPattern.split( attributes.get("COOKIE") ); + String[] tmp2; + for(String cookie : tmp){ + tmp2 = equalPattern.split(cookie, 2); + cookies.put( + tmp2[0].trim(), // Key + (tmp2.length>1 ? tmp2[1] : "").trim()); //Value + } + } + } + + /** + * @return the HTTP message type( ex. GET,POST...) + */ + public String getRequestType(){ + return type; + } + /** + * @return the HTTP version of this header + */ + public float getHTTPVersion(){ + return version; + } + /** + * @return the URL that the client sent the server + */ + public String getRequestURL(){ + return url; + } + /** + * Returns the URL attribute value of the given name, + * returns null if there is no such attribute + */ + public String getURLAttribute(String name){ + return url_attr.get( name ); + } + /** + * Returns the HTTP attribute value of the given name, + * returns null if there is no such attribute + */ + public String getHTTPAttribute(String name){ + return attributes.get( name.toUpperCase() ); + } + /** + * Returns the cookie value of the given name, + * returns null if there is no such attribute + */ + public String getCookie(String name){ + return cookies.get( name ); + } + + + /** + * @return athe parsed cookies + */ + public HashMap getCookies(){ + return cookies; + } + /** + * @return the parsed URL values + */ + public HashMap getURLAttributes(){ + return url_attr; + } + /** + * @return the parsed header attributes + */ + public HashMap getAttributes(){ + return attributes; + } + + + public String toString(){ + StringBuffer tmp = new StringBuffer(); + tmp.append("\nType: "); + tmp.append(type); + tmp.append("\nHTTP Version: HTTP/"); + tmp.append(version); + + tmp.append("\nURL: "); + tmp.append(url); + + for( String key : url_attr.keySet() ){ + tmp.append("\nURL Attr: "); + tmp.append(key); + tmp.append("="); + tmp.append( url_attr.get(key) ); + } + + for( String key : attributes.keySet() ){ + tmp.append("\nHTTP Attr: "); + tmp.append(key); + tmp.append("="); + tmp.append( attributes.get(key) ); + } + + for( String key : cookies.keySet() ){ + tmp.append("\nCookie: "); + tmp.append(key); + tmp.append("="); + tmp.append( cookies.get(key) ); + } + + return tmp.toString(); + } +} diff --git a/src/zutil/network/http/HttpPrintStream.java b/src/zutil/network/http/HttpPrintStream.java index 8d934d6..d3c7c21 100644 --- a/src/zutil/network/http/HttpPrintStream.java +++ b/src/zutil/network/http/HttpPrintStream.java @@ -11,17 +11,51 @@ import java.util.HashMap; * @author Ziver * */ -public class HttpPrintStream extends PrintStream{ - private Integer status_code; - private HashMap header; - private HashMap cookie; - private StringBuffer buffer; - private boolean buffer_enabled; +public class HttpPrintStream extends PrintStream{ + // Defines the type of message + public enum HTTPMessageType{ + REQUEST, + RESPONSE + } + // This defines the type of message that will be generated + private HTTPMessageType message_type; + // The status code of the message, ONLY for response + private Integer res_status_code; + // The request type of the message ONLY for request + private String req_type; + // The requesting url ONLY for request + private String req_url; + // An Map of all the header values + private HashMap header; + // An Map of all the cookies + private HashMap cookie; + // The buffered header + private StringBuffer buffer; + // If the header buffering is enabled + private boolean buffer_enabled; + + /** + * Creates an new instance of HttpPrintStream with + * message type of RESPONSE and buffering disabled. + * + * @param out is the OutputStream to send the message + */ public HttpPrintStream(OutputStream out) { + this( out, HTTPMessageType.RESPONSE ); + } + /** + * Creates an new instance of HttpPrintStream with + * message type buffering disabled. + * + * @param out is the OutputStream to send the message + * @param type is the type of message + */ + public HttpPrintStream(OutputStream out, HTTPMessageType type) { super(out); - status_code = 0; + this.message_type = type; + res_status_code = 0; header = new HashMap(); cookie = new HashMap(); buffer = new StringBuffer(); @@ -49,9 +83,9 @@ public class HttpPrintStream extends PrintStream{ * @param value is the value of the cookie * @throws Exception Throws exception if the header has already been sent */ - public void setCookie(String key, String value) throws Exception{ + public void setCookie(String key, String value) throws RuntimeException{ if(cookie == null) - throw new Exception("Header already sent!!!"); + throw new RuntimeException("Header already sent!!!"); cookie.put(key, value); } @@ -62,22 +96,51 @@ public class HttpPrintStream extends PrintStream{ * @param value is the value of the header * @throws Exception Throws exception if the header has already been sent */ - public void setHeader(String key, String value) throws Exception{ + public void setHeader(String key, String value) throws RuntimeException{ if(header == null) - throw new Exception("Header already sent!!!"); + throw new RuntimeException("Header already sent!!!"); header.put(key, value); } - + /** - * Sets the return status code + * Sets the status code of the message, ONLY available in HTTP RESPONSE * * @param code the code from 100 up to 599 - * @throws Exception Throws exception if the header has already been sent + * @throws RuntimeException if the header has already been sent or the message type is wrong */ - public void setStatusCode(int code) throws Exception{ - if(status_code == null) - throw new Exception("Header already sent!!!"); - status_code = code; + public void setStatusCode(int code) throws RuntimeException{ + if( res_status_code == null ) + throw new RuntimeException("Header already sent!!!"); + if( message_type != HTTPMessageType.RESPONSE ) + throw new RuntimeException("Status Code is only available in HTTP RESPONSE!!!"); + res_status_code = code; + } + + /** + * Sets the request type of the message, ONLY available in HTTP REQUEST + * + * @param req_type is the type of the message, e.g. GET, POST... + * @throws RuntimeException if the header has already been sent or the message type is wrong + */ + public void setRequestType(String req_type) throws RuntimeException{ + if( req_type == null ) + throw new RuntimeException("Header already sent!!!"); + if( message_type != HTTPMessageType.REQUEST ) + throw new RuntimeException("Request Message Type is only available in HTTP REQUEST!!!"); + this.req_type = req_type; + } + /** + * Sets the requesting URL of the message, ONLY available in HTTP REQUEST + * + * @param req_url is the URL + * @throws RuntimeException if the header has already been sent or the message type is wrong + */ + public void setRequestURL(String req_url) throws RuntimeException{ + if( req_url == null ) + throw new RuntimeException("Header already sent!!!"); + if( message_type != HTTPMessageType.REQUEST ) + throw new RuntimeException("Request URL is only available in HTTP REQUEST!!!"); + this.req_url = req_url; } /** @@ -102,10 +165,15 @@ public class HttpPrintStream extends PrintStream{ buffer.append(s); } else{ - if(status_code != null){ - super.print("HTTP/1.0 "+status_code+" "+getStatusString(status_code)); + if(res_status_code != null){ + if( message_type==HTTPMessageType.REQUEST ) + super.print(req_type+" "+req_url+" HTTP/1.1"); + else + super.print("HTTP/1.1 "+res_status_code+" "+getStatusString(res_status_code)); super.println(); - status_code = null; + res_status_code = null; + req_type = null; + req_url = null; } if(header != null){ for(String key : header.keySet()){ @@ -115,9 +183,20 @@ public class HttpPrintStream extends PrintStream{ header = null; } if(cookie != null){ - for(String key : cookie.keySet()){ - super.print("Set-Cookie: "+key+"="+cookie.get(key)+";"); - super.println(); + if( !cookie.isEmpty() ){ + if( message_type==HTTPMessageType.REQUEST ){ + super.print("Cookie: "); + for(String key : cookie.keySet()){ + super.print(key+"="+cookie.get(key)+"; "); + } + super.println(); + } + else{ + for(String key : cookie.keySet()){ + super.print("Set-Cookie: "+key+"="+cookie.get(key)+";"); + super.println(); + } + } } super.println(); cookie = null; @@ -136,7 +215,7 @@ public class HttpPrintStream extends PrintStream{ buffer.delete(0, buffer.length()); buffer_enabled = true; } - else if(status_code != null || header != null || cookie != null){ + else if(res_status_code != null || header != null || cookie != null){ printOrBuffer(""); } super.flush(); @@ -165,7 +244,7 @@ public class HttpPrintStream extends PrintStream{ public void print(int x){ printOrBuffer(String.valueOf(x));} public void print(long x){ printOrBuffer(String.valueOf(x));} public void print(Object x){ printOrBuffer(String.valueOf(x));} - + /* public void write(int b) { print((char)b);} public void write(byte buf[], int off, int len){ diff --git a/src/zutil/network/http/HttpServer.java b/src/zutil/network/http/HttpServer.java index 1dfbc9d..c58d015 100644 --- a/src/zutil/network/http/HttpServer.java +++ b/src/zutil/network/http/HttpServer.java @@ -4,22 +4,16 @@ import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; -import java.net.ServerSocket; import java.net.Socket; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.cert.CertificateException; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Timer; import java.util.TimerTask; -import java.util.regex.Pattern; - -import javax.net.ssl.SSLServerSocketFactory; import zutil.MultiPrintStream; +import zutil.network.threaded.ThreadedTCPNetworkServer; +import zutil.network.threaded.ThreadedTCPNetworkServerThread; /** @@ -28,7 +22,7 @@ import zutil.MultiPrintStream; * * @author Ziver */ -public class HttpServer extends Thread{ +public class HttpServer extends ThreadedTCPNetworkServer{ public static final boolean DEBUG = false; public static final String SERVER_VERSION = "StaticInt HttpServer 1.0"; public static final int COOKIE_TTL = 200; @@ -36,8 +30,6 @@ public class HttpServer extends Thread{ public final String server_url; public final int server_port; - private File keyStore; - private String keyStorePass; private HashMap pages; private HttpPage defaultPage; @@ -64,10 +56,9 @@ public class HttpServer extends Thread{ * @param sslCert If this is not null then the server will use a SSL connection with the given certificate */ public HttpServer(String url, int port, File keyStore, String keyStorePass){ + super( port, keyStore, keyStorePass ); this.server_url = url; this.server_port = port; - this.keyStorePass = keyStorePass; - this.keyStore = keyStore; pages = new HashMap(); sessions = Collections.synchronizedMap(new HashMap>()); @@ -75,6 +66,8 @@ public class HttpServer extends Thread{ Timer timer = new Timer(); timer.schedule(new GarbageCollector(), 0, SESSION_TTL / 2); + + MultiPrintStream.out.println("HTTP"+(keyStore==null?"":"S")+" Server ready!"); } /** @@ -119,49 +112,13 @@ public class HttpServer extends Thread{ defaultPage = page; } - public void run(){ - try{ - ServerSocket ss; - if(keyStorePass != null && keyStore != null){ - registerCertificate(keyStore, keyStorePass); - ss = initSSL(server_port); - MultiPrintStream.out.println("Https Server Running!!!"); - } - else{ - ss = new ServerSocket(server_port); - MultiPrintStream.out.println("Http Server Running!!!"); - } - - while(true){ - new HttpServerThread(ss.accept()); - } - } catch (Exception e) { - e.printStackTrace(); + protected ThreadedTCPNetworkServerThread getThreadInstance( Socket s ){ + try { + return new HttpServerThread( s ); + } catch (IOException e) { + e.printStackTrace( MultiPrintStream.out ); } - } - - /** - * Initiates a SSLServerSocket - * - * @param port The port to listen to - * @return The SSLServerSocket - * @throws IOException - */ - private ServerSocket initSSL(int port) throws IOException{ - SSLServerSocketFactory sslserversocketfactory = - (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); - return sslserversocketfactory.createServerSocket(port); - - } - - /** - * Registers the given cert file to the KeyStore - * - * @param certFile The cert file - */ - protected void registerCertificate(File keyStore, String keyStorePass) throws CertificateException, IOException, KeyStoreException, NoSuchProviderException, NoSuchAlgorithmException{ - System.setProperty("javax.net.ssl.keyStore", keyStore.getAbsolutePath()); - System.setProperty("javax.net.ssl.keyStorePassword", keyStorePass); + return null; } /** @@ -170,7 +127,7 @@ public class HttpServer extends Thread{ * @author Ziver * */ - class HttpServerThread extends Thread{ + protected class HttpServerThread implements ThreadedTCPNetworkServerThread{ private HttpPrintStream out; private BufferedReader in; private Socket socket; @@ -179,16 +136,11 @@ public class HttpServer extends Thread{ out = new HttpPrintStream(socket.getOutputStream()); in = new BufferedReader(new InputStreamReader(socket.getInputStream())); this.socket = socket; - start(); - if(DEBUG)MultiPrintStream.out.println("New Connection!!! "+socket.getInetAddress().getHostName()); + if(DEBUG) MultiPrintStream.out.println("New Connection!!! "+socket.getInetAddress().getHostName()); } public void run(){ String tmp = null; - String[] tmpArray, tmpArray2; - Pattern colonPattern = Pattern.compile(":"); - Pattern semiColonPattern = Pattern.compile(";"); - Pattern equalPattern = Pattern.compile("="); String page_url = ""; HashMap client_info = new HashMap(); @@ -197,45 +149,19 @@ public class HttpServer extends Thread{ //**************************** REQUEST ********************************* try { - if(DEBUG)MultiPrintStream.out.println("Reciving Http Request!!!"); - while((tmp=in.readLine()) != null && !tmp.isEmpty()){ - //System.err.println(tmp); - //*********** Handling Get variables - if(tmp.startsWith("GET")){ - // Gets the file URL and get values - tmp = (tmp.substring(5, tmp.indexOf("HTTP/"))).trim(); - page_url = parseHttpHeader(tmp, request); - } - //********* Handling Post variable data - else if(tmp.startsWith("POST")){ - // Gets the file URL and get values - tmp = (tmp.substring(6, tmp.indexOf("HTTP/"))).trim(); - page_url = parseHttpHeader(tmp, request); - } - //********* Handling Cookies - else if(tmp.startsWith("Cookie")){ - tmp = colonPattern.split(tmp)[1]; - tmpArray = semiColonPattern.split(tmp); - for(String e : tmpArray){ - tmpArray2 = equalPattern.split(e); - cookie.put( - tmpArray2[0].trim(), // Key - (tmpArray2.length>1 ? tmpArray2[1] : "").trim()); //Value - } - } - //********* Handling Client info - else{ - tmpArray = colonPattern.split(tmp); - client_info.put( - tmpArray[0].trim(), // Key - (tmpArray.length>1 ? tmpArray[1] : "").trim()); //Value - } - } + if(DEBUG) MultiPrintStream.out.println("Reciving Http Request!!!"); + + HTTPHeaderParser parser = new HTTPHeaderParser(in); + if(DEBUG) MultiPrintStream.out.println(parser); + client_info = parser.getAttributes(); + request = parser.getURLAttributes(); + cookie = parser.getCookies(); + //******* Read in the post data if available - if(client_info.containsKey("Content-Length")){ + if( parser.getHTTPAttribute("Content-Length")!=null ){ // Reads the post data size - tmp = client_info.get("Content-Length"); + tmp = parser.getHTTPAttribute("Content-Length"); int post_data_length = Integer.parseInt( tmp ); // read the data StringBuffer tmpb = new StringBuffer(); @@ -244,19 +170,20 @@ public class HttpServer extends Thread{ tmpb.append((char)in.read()); } - if(client_info.get("Content-Type").contains("application/x-www-form-urlencoded")){ + tmp = parser.getHTTPAttribute("Content-Type"); + if( tmp.contains("application/x-www-form-urlencoded") ){ // get the variables - parseVariables(tmpb.toString(), request); + HTTPHeaderParser.parseUrlAttributes( tmpb.toString(), request ); } - else if(client_info.get("Content-Type").contains("application/soap+xml") || - client_info.get("Content-Type").contains("text/xml") || - client_info.get("Content-Type").contains("text/plain")){ + else if( tmp.contains("application/soap+xml" ) || + tmp.contains("text/xml") || + tmp.contains("text/plain") ){ // save the variables - request.put("" , tmpb.toString()); + request.put( "" , tmpb.toString() ); } - else if(client_info.get("Content-Type").contains("multipart/form-data")){ + else if( tmp.contains("multipart/form-data") ){ // TODO: File upload - throw new Exception("\"multipart-form-data\" Not implemented!!!"); + throw new Exception( "\"multipart-form-data\" Not implemented!!!" ); } } @@ -264,118 +191,71 @@ public class HttpServer extends Thread{ // Get the client session or create one Map client_session; long ttl_time = System.currentTimeMillis()+SESSION_TTL; - if(cookie.containsKey("session_id") && sessions.containsKey(cookie.get("session_id"))){ - client_session = sessions.get(cookie.get("session_id")); + if( cookie.containsKey("session_id") && sessions.containsKey(cookie.get("session_id")) ){ + client_session = sessions.get( cookie.get("session_id") ); // Check if session is still valid - if((Long)client_session.get("ttl") < System.currentTimeMillis()){ + if( (Long)client_session.get("ttl") < System.currentTimeMillis() ){ int session_id = (Integer)client_session.get("session_id"); client_session = Collections.synchronizedMap(new HashMap()); - client_session.put("session_id", session_id); - sessions.put(""+session_id, client_session); + client_session.put( "session_id", session_id); + sessions.put( ""+session_id, client_session); } // renew the session TTL - client_session.put("ttl", ttl_time); + client_session.put( "ttl", ttl_time ); } else{ client_session = Collections.synchronizedMap(new HashMap()); - client_session.put("session_id", nextSessionId); - client_session.put("ttl", ttl_time); - sessions.put(""+nextSessionId, client_session); + client_session.put( "session_id", nextSessionId ); + client_session.put( "ttl", ttl_time ); + sessions.put( ""+nextSessionId, client_session ); nextSessionId++; } // Debug if(DEBUG){ - MultiPrintStream.out.println("# page_url: "+page_url); - MultiPrintStream.out.println("# cookie: "+cookie); - MultiPrintStream.out.println("# client_session: "+client_session); - MultiPrintStream.out.println("# client_info: "+client_info); - MultiPrintStream.out.println("# request: "+request); + MultiPrintStream.out.println( "# page_url: "+page_url ); + MultiPrintStream.out.println( "# cookie: "+cookie ); + MultiPrintStream.out.println( "# client_session: "+client_session ); + MultiPrintStream.out.println( "# client_info: "+client_info ); + MultiPrintStream.out.println( "# request: "+request ); } //**************************** RESPONSE ************************************ - if(DEBUG)MultiPrintStream.out.println("Sending Http Response!!!"); + if(DEBUG) MultiPrintStream.out.println("Sending Http Response!!!"); out.setStatusCode(200); - out.setHeader("Server", SERVER_VERSION); - out.setHeader("Content-Type", "text/html"); - out.setCookie("session_id", ""+client_session.get("session_id")); + out.setHeader( "Server", SERVER_VERSION ); + out.setHeader( "Content-Type", "text/html" ); + out.setCookie( "session_id", ""+client_session.get("session_id") ); - if(!page_url.isEmpty() && pages.containsKey(page_url)){ + if( !page_url.isEmpty() && pages.containsKey(page_url) ){ pages.get(page_url).respond(out, client_info, client_session, cookie, request); } - else if(defaultPage != null){ + else if( defaultPage != null ){ defaultPage.respond(out, client_info, client_session, cookie, request); } else{ - out.setStatusCode(404); - out.println("404 Page Not Found"); + out.setStatusCode( 404 ); + out.println( "404 Page Not Found" ); } //******************************************************************************** } catch (Exception e) { - e.printStackTrace(MultiPrintStream.out); + e.printStackTrace( MultiPrintStream.out ); try { - out.setStatusCode(500); + out.setStatusCode( 500 ); } catch (Exception e1) {} if(e.getMessage() != null) - out.println("500 Internal Server Error(Header: "+tmp+"): "+e.getMessage()); + out.println( "500 Internal Server Error: "+e.getMessage() ); else{ - out.println("500 Internal Server Error(Header: "+tmp+"): "+e.getCause().getMessage()); + out.println( "500 Internal Server Error: "+e.getCause().getMessage() ); } } try{ - if(DEBUG)MultiPrintStream.out.println("Conection Closed!!!"); + if(DEBUG) MultiPrintStream.out.println("Conection Closed!!!"); out.close(); in.close(); socket.close(); - } catch (Exception e) { - e.printStackTrace(MultiPrintStream.out); - } - } - } - - /** - * Parses the first header line and ads the values to - * the map and returns the file name and path - * - * @param header The header String - * @param map The HashMap to put the variables to - * @return The path and file name as a String - */ - private String parseHttpHeader(String header, HashMap map){ - String page_url = ""; - // cut out the page name - if(header.indexOf('?') > -1){ - page_url = header.substring(0, header.indexOf('?')); - header = header.substring(header.indexOf('?')+1, header.length()); - parseVariables(header, map); - } - else{ - page_url = header; - } - - return page_url; - } - - /** - * Parses a String with variables from a get or post - * from a client and puts the data into a HashMap - * - * @param header A String with all the variables - * @param map The HashMap to put all the variables into - */ - private void parseVariables(String header, HashMap map){ - int tmp; - // get the variables - String[] data = header.split("&"); - for(String element : data){ - tmp = element.indexOf('='); - if(tmp > 0){ - map.put( - element.substring(0, tmp ).trim(), // Key - element.substring(tmp+1, element.length() ).trim() ); //Value - } - else{ - map.put(element, ""); + } catch( Exception e ) { + e.printStackTrace( MultiPrintStream.out ); } } } diff --git a/src/zutil/network/nio/NioNetwork.java b/src/zutil/network/nio/NioNetwork.java index b3899a3..4c32e77 100644 --- a/src/zutil/network/nio/NioNetwork.java +++ b/src/zutil/network/nio/NioNetwork.java @@ -36,7 +36,7 @@ public abstract class NioNetwork implements Runnable { * 2 = message debug * 3 = selector debug */ - public static int DEBUG = 2; + public static final int DEBUG = 2; public static enum NetworkType {SERVER, CLIENT}; private NetworkType type; diff --git a/src/zutil/network/threaded/ThreadedTCPNetworkServer.java b/src/zutil/network/threaded/ThreadedTCPNetworkServer.java new file mode 100644 index 0000000..6496f1e --- /dev/null +++ b/src/zutil/network/threaded/ThreadedTCPNetworkServer.java @@ -0,0 +1,115 @@ +package zutil.network.threaded; + +import java.io.File; +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.cert.CertificateException; + +import javax.net.ssl.SSLServerSocketFactory; + +import zutil.MultiPrintStream; + + +/** + * A simple web server that handles both cookies and + * sessions for all the clients + * + * @author Ziver + */ +public abstract class ThreadedTCPNetworkServer extends Thread{ + public final int port; + private File keyStore; + private String keyStorePass; + + /** + * Creates a new instance of the sever + * + * @param port The port that the server should listen to + */ + public ThreadedTCPNetworkServer(int port){ + this(port, null, null); + } + /** + * Creates a new instance of the sever + * + * @param port The port that the server should listen to + * @param sslCert If this is not null then the server will use SSL connection with this keyStore file path + * @param sslCert If this is not null then the server will use a SSL connection with the given certificate + */ + public ThreadedTCPNetworkServer(int port, File keyStore, String keyStorePass){ + this.port = port; + this.keyStorePass = keyStorePass; + this.keyStore = keyStore; + } + + + public void run(){ + ServerSocket ss = null; + try{ + if(keyStorePass != null && keyStore != null){ + registerCertificate(keyStore, keyStorePass); + ss = initSSL( port ); + } + else{ + ss = new ServerSocket( port ); + } + + while(true){ + Socket s = ss.accept(); + ThreadedTCPNetworkServerThread t = getThreadInstance( s ); + if( t!=null ) + new Thread( t ).start(); + else{ + MultiPrintStream.out.println("Unable to instantiate ThreadedTCPNetworkServerThread, closing connection!"); + s.close(); + } + } + } catch(Exception e) { + e.printStackTrace( MultiPrintStream.out ); + } + + if( ss!=null ){ + try{ + ss.close(); + }catch(IOException e){ e.printStackTrace( MultiPrintStream.out ); } + } + } + + /** + * This method returns an new instance of the ThreadedTCPNetworkServerThread + * that will handle the newly made connection, if an null value is returned + * then the ThreadedTCPNetworkServer will close the new connection. + * + * @param s is an new connection to an host + * @return a new instance of an thread or null + */ + protected abstract ThreadedTCPNetworkServerThread getThreadInstance( Socket s ); + + /** + * Initiates a SSLServerSocket + * + * @param port The port to listen to + * @return The SSLServerSocket + * @throws IOException + */ + private ServerSocket initSSL(int port) throws IOException{ + SSLServerSocketFactory sslserversocketfactory = + (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); + return sslserversocketfactory.createServerSocket(port); + + } + + /** + * Registers the given cert file to the KeyStore + * + * @param certFile The cert file + */ + protected void registerCertificate(File keyStore, String keyStorePass) throws CertificateException, IOException, KeyStoreException, NoSuchProviderException, NoSuchAlgorithmException{ + System.setProperty("javax.net.ssl.keyStore", keyStore.getAbsolutePath()); + System.setProperty("javax.net.ssl.keyStorePassword", keyStorePass); + } +} \ No newline at end of file diff --git a/src/zutil/network/threaded/ThreadedTCPNetworkServerThread.java b/src/zutil/network/threaded/ThreadedTCPNetworkServerThread.java new file mode 100644 index 0000000..21f9ddb --- /dev/null +++ b/src/zutil/network/threaded/ThreadedTCPNetworkServerThread.java @@ -0,0 +1,12 @@ +package zutil.network.threaded; + +/** + * The class that will handle the TCP connection will incclude + * this interface + * + * @author Ziver + * + */ +public interface ThreadedTCPNetworkServerThread extends Runnable{ + +} diff --git a/src/zutil/network/threaded/ThreadedUDPNetwork.java b/src/zutil/network/threaded/ThreadedUDPNetwork.java new file mode 100644 index 0000000..cbaefb7 --- /dev/null +++ b/src/zutil/network/threaded/ThreadedUDPNetwork.java @@ -0,0 +1,103 @@ +package zutil.network.threaded; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.MulticastSocket; +import java.net.SocketException; + + + +/** + * A simple web server that handles both cookies and + * sessions for all the clients + * + * @author Ziver + */ +public class ThreadedUDPNetwork extends Thread{ + public static final int BUFFER_SIZE = 512; + + // Type of UDP socket + enum UDPType{ + MULTICAST, + UNICAST + } + protected final UDPType type; + protected final int port; + protected DatagramSocket socket; + protected ThreadedUDPNetworkThread thread = null; + + + /** + * Creates a new unicast instance of the sever + * + * @param thread is the class that will handle incoming packets + * @param port is the port that the server should listen to + * @throws SocketException + */ + public ThreadedUDPNetwork(ThreadedUDPNetworkThread thread, int port) throws SocketException{ + this.type = UDPType.UNICAST; + this.port = port; + setThread( thread ); + + socket = new DatagramSocket( port ); + } + + /** + * Creates a new multicast instance of the sever + * + * @param thread is the class that will handle incoming packets + * @param port is the port that the server should listen to + * @param multicast_addr is the multicast address that the server will listen on + * @throws IOException + */ + public ThreadedUDPNetwork(ThreadedUDPNetworkThread thread, int port, String multicast_addr ) throws IOException{ + this.type = UDPType.MULTICAST; + this.port = port; + setThread( thread ); + + // init udp socket + MulticastSocket msocket = new MulticastSocket( port ); + InetAddress group = InetAddress.getByName( multicast_addr ); + msocket.joinGroup( group ); + + socket = msocket; + } + + + public void run(){ + try{ + while(true){ + byte[] buf = new byte[BUFFER_SIZE]; + DatagramPacket packet = new DatagramPacket(buf, buf.length); + socket.receive( packet ); + if( thread!=null ) + thread.receivedPacket( packet, this ); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Sends the given packet + * + * @param packet is the packet to send + * @throws IOException + */ + public synchronized void send( DatagramPacket packet ) throws IOException{ + socket.send(packet); + } + + /** + * Sets the thread that will handle the incoming packets + * + * @param thread is the thread + */ + public void setThread(ThreadedUDPNetworkThread thread){ + this.thread = thread; + } + + +} \ No newline at end of file diff --git a/src/zutil/network/threaded/ThreadedUDPNetworkThread.java b/src/zutil/network/threaded/ThreadedUDPNetworkThread.java new file mode 100644 index 0000000..48a381a --- /dev/null +++ b/src/zutil/network/threaded/ThreadedUDPNetworkThread.java @@ -0,0 +1,21 @@ +package zutil.network.threaded; + +import java.net.DatagramPacket; + +/** + * This interface is for processing received packets + * from the TNetworkUDPServer + * + * @author Ziver + * + */ +public interface ThreadedUDPNetworkThread extends Runnable{ + + /** + * Packet will be processed in this method + * + * @param packet is the received packet + * @param network is the network class that received the packet + */ + public void receivedPacket(DatagramPacket packet, ThreadedUDPNetwork network); +} diff --git a/src/zutil/struct/HistoryList.java b/src/zutil/struct/HistoryList.java new file mode 100644 index 0000000..54702dc --- /dev/null +++ b/src/zutil/struct/HistoryList.java @@ -0,0 +1,105 @@ +package zutil.struct; + +import java.util.Iterator; +import java.util.LinkedList; + + +public class HistoryList implements Iterable{ + public int history_length; + private LinkedList history; + private int historyIndex = 0; + + /** + * Creates an HistoryList object + */ + public HistoryList(){ + this( Integer.MAX_VALUE ); + } + + /** + * Creates an HistoryList object with an max size + * + * @param histlength the maximum size of the list + */ + public HistoryList(int histlength){ + history_length = histlength; + history = new LinkedList(); + } + + /** + * Returns the item in the given index + * + * @param i is the index + * @return item in that index + */ + public T get(int i){ + return history.get( i ); + } + + /** + * Adds an item to the list and removes the last if + * the list is bigger than the max length. + * + * @param item is the item to add + */ + public void add(T item){ + while(historyIndex < history.size()-1){ + history.removeLast(); + } + history.addLast(item); + if(history_length < history.size()){ + history.removeFirst(); + } + + historyIndex = history.size()-1; + } + + /** + * @return the previous item in the list + */ + public T getPrevious(){ + if(historyIndex > 0){ + historyIndex -= 1; + } + else{ + historyIndex = 0; + } + return history.get(historyIndex); + } + + /** + * @return the next item in the list + */ + public T getNext(){ + if(next()){ + historyIndex += 1; + } + else{ + historyIndex = history.size()-1; + } + return history.get(historyIndex); + } + + public T getCurrent(){ + return history.get(historyIndex); + } + + /** + * @return if there are items newer than the current + */ + public boolean next(){ + if(historyIndex < history.size()-1){ + return true; + } + else{ + return false; + } + } + + /** + * @return an iterator of the list + */ + public Iterator iterator(){ + return history.iterator(); + } +} diff --git a/src/zutil/test/FileFinderHasherTest.java b/src/zutil/test/FileFinderHasherTest.java index bd79c0e..1c74d73 100644 --- a/src/zutil/test/FileFinderHasherTest.java +++ b/src/zutil/test/FileFinderHasherTest.java @@ -2,7 +2,7 @@ package zutil.test; import java.io.File; import java.net.URISyntaxException; -import java.util.ArrayList; +import java.util.List; import zutil.FileFinder; import zutil.Hasher; @@ -12,7 +12,7 @@ public class FileFinderHasherTest { String relativePath = "zutil/test"; File path = FileFinder.find(relativePath); - ArrayList files = FileFinder.search(path); + List files = FileFinder.search(path); for(int i=0; i