diff --git a/licenses/LICENSE-UPnP.txt b/licenses/LICENSE-UPnP.txt
index 7e2a36863ab69c5834876529c9c6d3e8a7191134..e75a1be16db05461699f2c32ae17d65906a6521e 100644
--- a/licenses/LICENSE-UPnP.txt
+++ b/licenses/LICENSE-UPnP.txt
@@ -1,12 +1,21 @@
-Copyright (C) 2003-2006 Satoshi Konno
+Copyright (c) 2003-2010, Satoshi Konno
+
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
 
-1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 
-
-2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 
-
-3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. 
+    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+    * Neither the name of the Cyber Garage nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
 
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/router/java/src/org/cybergarage/http/HTTP.java b/router/java/src/org/cybergarage/http/HTTP.java
index ec7ccbde18013f52dc9ea08145be26e0cd20f491..554da7f257f423ec711be699b4ddb895d0c1bcc6 100644
--- a/router/java/src/org/cybergarage/http/HTTP.java
+++ b/router/java/src/org/cybergarage/http/HTTP.java
@@ -30,7 +30,7 @@
 
 package org.cybergarage.http;
 
-import java.net.*;
+import java.net.URL;
 
 public class HTTP 
 {
@@ -45,6 +45,8 @@ public class HTTP
 	public static final String VERSION_11 = "1.1";
 		
 	public static final String CRLF = "\r\n";
+	public static final byte CR = '\r';
+	public static final byte LF = '\n';
 	public static final String TAB = "\t";
 	
 	public static final String SOAP_ACTION = "SOAPACTION";
@@ -59,19 +61,20 @@ public class HTTP
 	
 	public static final String DATE = "Date";
 	public static final String CACHE_CONTROL = "Cache-Control";
-		public static final String NO_CACHE = "no-cache";
-		public static final String MAX_AGE = "max-age";
+	public static final String NO_CACHE = "no-cache";
+	public static final String MAX_AGE = "max-age";
 	public static final String CONNECTION = "Connection";
-		public static final String CLOSE = "close";
+	public static final String CLOSE = "close";
 	public static final String KEEP_ALIVE = "Keep-Alive";
 	public static final String CONTENT_TYPE = "Content-Type";
+	public static final String CHARSET = "charset";
 	public static final String CONTENT_LENGTH = "Content-Length";
 	public static final String CONTENT_RANGE = "Content-Range";
-		public static final String CONTENT_RANGE_BYTES = "bytes"; 
+	public static final String CONTENT_RANGE_BYTES = "bytes"; 
 	// Thanks for Brent Hills (10/20/04)
 	public static final String RANGE = "Range";
 	public static final String TRANSFER_ENCODING = "Transfer-Encoding";
-		public static final String CHUNKED = "Chunked";
+	public static final String CHUNKED = "Chunked";
 	public static final String LOCATION = "Location";
 	public static final String SERVER = "Server";
 
diff --git a/router/java/src/org/cybergarage/http/HTTPHeader.java b/router/java/src/org/cybergarage/http/HTTPHeader.java
index d09446bdc6ef934dca1f75dbf2cf596611caa839..b6ff370d6ab988f35f4d31ca1b454f24f070b31b 100644
--- a/router/java/src/org/cybergarage/http/HTTPHeader.java
+++ b/router/java/src/org/cybergarage/http/HTTPHeader.java
@@ -18,12 +18,15 @@
 
 package org.cybergarage.http;
 
-import java.io.*;
+import java.io.IOException;
+import java.io.LineNumberReader;
+import java.io.StringReader;
 
-import org.cybergarage.util.*;
+import org.cybergarage.util.Debug;
 
 public class HTTPHeader 
-{
+{
+	private static int MAX_LENGTH = 1024;
 	private String name;
 	private String value;
 
@@ -111,9 +114,10 @@ public class HTTPHeader
 	}
 
 	public final static String getValue(String data, String name)
-	{
-		StringReader strReader = new StringReader(data);
-		LineNumberReader lineReader = new LineNumberReader(strReader);
+	{
+		/* Thanks for Stephan Mehlhase (2010-10-26) */
+		StringReader strReader = new StringReader(data);
+		LineNumberReader lineReader = new LineNumberReader(strReader, Math.min(data.length(), MAX_LENGTH));
 		return getValue(lineReader, name);
 	}
 
diff --git a/router/java/src/org/cybergarage/http/HTTPPacket.java b/router/java/src/org/cybergarage/http/HTTPPacket.java
index 3c0c78e63e732253f7be3033ac62067b2113066c..9a7575f32d5eba598d297fd97bd9532569fcfcd7 100644
--- a/router/java/src/org/cybergarage/http/HTTPPacket.java
+++ b/router/java/src/org/cybergarage/http/HTTPPacket.java
@@ -5,11 +5,11 @@
 *	Copyright (C) Satoshi Konno 2002-2004
 *
 *	File: HTTPConnection.java
-*
-*	Revision;
-*
-*	11/18/02
-*		- first revision.
+*
+*	Revision;
+*
+*	11/18/02
+*		- first revision.
 *	09/02/03
 *		- Giordano Sassaroli <sassarol@cefriel.it>
 *		- Problem : The API is unable to receive responses from the Microsoft UPnP stack
@@ -59,29 +59,47 @@
 *		  hasTransferEncoding(), setTransferEncoding(), getTransferEncoding(), isChunked().
 *	03/02/05
 *		- Changed post() to suppot chunked stream.
+*	06/11/05
+*		- Added setHost().
+*	07/07/05
+*		- Lee Peik Feng <pflee@users.sourceforge.net>
+*		- Andrey Ovchar <AOvchar@consultitnow.com>
+*		- Fixed set() to parse the chunk size as a hex string.
+*	11/02/05
+*		- Changed set() to use BufferedInputStream instead of BufferedReader to
+*		  get the content as a byte stream.
+*	11/06/05
+*		- Added getCharSet().
+*		- Changed getContentString() to return the content string using the charset.
 *
 *******************************************************************/
 
 package org.cybergarage.http;
 
-import java.io.*;
-import java.util.*;
-
-import org.cybergarage.net.*;
-import org.cybergarage.util.*;
-import java.util.Calendar;
-
-public class HTTPPacket 
-{
-	////////////////////////////////////////////////
-	//	Constructor
-	////////////////////////////////////////////////
-	
-	public HTTPPacket()
+import java.io.BufferedInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.util.Calendar;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import org.cybergarage.net.HostInterface;
+import org.cybergarage.util.Debug;
+import org.cybergarage.util.StringUtil;
+
+public class HTTPPacket 
+{
+	////////////////////////////////////////////////
+	//	Constructor
+	////////////////////////////////////////////////
+	
+	public HTTPPacket()
 	{
 		setVersion(HTTP.VERSION);
-		setContentInputStream(null);
-	}
+		setContentInputStream(null);
+	}
 
 	public HTTPPacket(HTTPPacket httpPacket)
 	{
@@ -129,12 +147,38 @@ public class HTTPPacket
 	//	set
 	////////////////////////////////////////////////
 	
+	private String readLine(BufferedInputStream in)
+	{
+		ByteArrayOutputStream lineBuf = new ByteArrayOutputStream();
+		byte readBuf[] = new byte[1];
+		
+ 		try {
+ 			int	readLen = in.read(readBuf);
+ 			while (0 < readLen) {
+ 				if (readBuf[0] == HTTP.LF)
+ 					break;
+ 				if (readBuf[0] != HTTP.CR) 
+ 					lineBuf.write(readBuf[0]);
+ 	 			readLen = in.read(readBuf);
+			}
+ 		}
+ 		catch (InterruptedIOException e) {
+ 			//Ignoring warning because it's a way to break the HTTP connecttion
+ 			//TODO Create a new level of Logging and log the event
+		}
+		catch (IOException e) {
+			Debug.warning(e);
+		}
+
+		return lineBuf.toString();
+	}
+	
 	protected boolean set(InputStream in, boolean onlyHeaders)
 	{
  		try {
-			BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+ 			BufferedInputStream reader = new BufferedInputStream(in);
 			
-			String firstLine = reader.readLine();
+			String firstLine = readLine(reader);
 			if (firstLine == null || firstLine.length() <= 0)
 				return false;
 			setFirstLine(firstLine);
@@ -148,15 +192,15 @@ public class HTTPPacket
 				//stream, so the code should check the presence of the actual
 				//response in the stream.
 				//skip all header lines
-				String headerLine = reader.readLine();
+				String headerLine = readLine(reader);
 				while ((headerLine != null) && (0 < headerLine.length()) ) {
 					HTTPHeader header = new HTTPHeader(headerLine);
 					if (header.hasName() == true)
 						setHeader(header);
-					headerLine = reader.readLine();
+					headerLine = readLine(reader);
 				}
 				//look forward another first line
-				String actualFirstLine = reader.readLine();
+				String actualFirstLine = readLine(reader);
 				if ((actualFirstLine != null) && (0 < actualFirstLine.length()) ) {
 					//this is the actual first line
 					setFirstLine(actualFirstLine);
@@ -165,12 +209,12 @@ public class HTTPPacket
 				}
 			}
 				
-			String headerLine = reader.readLine();
+			String headerLine = readLine(reader);
 			while ((headerLine != null) && (0 < headerLine.length()) ) {
 				HTTPHeader header = new HTTPHeader(headerLine);
 				if (header.hasName() == true)
 					setHeader(header);
-				headerLine = reader.readLine();
+				headerLine = readLine(reader);
 			}
 				
 			if (onlyHeaders == true) {
@@ -183,19 +227,24 @@ public class HTTPPacket
 			long contentLen = 0;
 			if (isChunkedRequest == true) {
 				try {
-					String chunkSizeLine = reader.readLine();
-					contentLen = Long.parseLong(new String(chunkSizeLine.getBytes(), 0, chunkSizeLine.length()-2));
+					String chunkSizeLine = readLine(reader);
+					// Thanks for Lee Peik Feng <pflee@users.sourceforge.net> (07/07/05)
+					//contentLen = Long.parseLong(new String(chunkSizeLine.getBytes(), 0, chunkSizeLine.length()-2), 16);
+					contentLen = (chunkSizeLine != null) ? Long.parseLong(chunkSizeLine.trim(), 16) : 0;
 				}
 				catch (Exception e) {};
 			}
 			else
 				contentLen = getContentLength();
 						
-			StringBuilder contentBuf = new StringBuilder();
+			ByteArrayOutputStream contentBuf = new ByteArrayOutputStream();
 			
 			while (0 < contentLen) {
 				int chunkSize = HTTP.getChunkSize();
-				char readBuf[] = new char[chunkSize];
+				
+				/* Thanks for Stephan Mehlhase (2010-10-26) */
+				byte readBuf[] = new byte[(int) (contentLen > chunkSize ? chunkSize : contentLen)];
+				
 				long readCnt = 0;
 				while (readCnt < contentLen) {
 					try {
@@ -206,7 +255,7 @@ public class HTTPPacket
 						int readLen = reader.read(readBuf, 0, (int)bufReadLen);
 						if (readLen < 0)
 							break;
-						contentBuf.append(new String(readBuf, 0, readLen));
+						contentBuf.write(readBuf, 0, readLen);
 						readCnt += readLen;
 					}
 					catch (Exception e)
@@ -226,8 +275,9 @@ public class HTTPPacket
 					} while (skipLen < HTTP.CRLF.length());
 					// read next chunk size
 					try {
-						String chunkSizeLine = reader.readLine();
-						contentLen = Long.parseLong(new String(chunkSizeLine.getBytes(), 0, chunkSizeLine.length()-2));
+						String chunkSizeLine = readLine(reader);
+						// Thanks for Lee Peik Feng <pflee@users.sourceforge.net> (07/07/05)
+						contentLen = Long.parseLong(new String(chunkSizeLine.getBytes(), 0, chunkSizeLine.length()-2), 16);
 					}
 					catch (Exception e) {
 						contentLen = 0;
@@ -237,9 +287,7 @@ public class HTTPPacket
 					contentLen = 0;
 			}
 
-			// Thanks for Ralf G. R. Bergs (02/09/04)
-			String contentStr = contentBuf.toString();
-			setContent(contentStr.getBytes(), false);
+			setContent(contentBuf.toByteArray(), false);
  		}
 		catch (Exception e) {
 			Debug.warning(e);
@@ -312,47 +360,47 @@ public class HTTPPacket
 	
 	public boolean hasFirstLine()
 	{
-		return (0 < firstLine.length()) ? true : false;
-	}
-	
-	////////////////////////////////////////////////
-	//	Header
-	////////////////////////////////////////////////
-
-	private Vector httpHeaderList = new Vector();
-	
-	public int getNHeaders()
-	{
-		return httpHeaderList.size();
-	}
-
-	public void addHeader(HTTPHeader header)
-	{
-		httpHeaderList.add(header);
-	}
-
-	public void addHeader(String name, String value)
-	{
-		HTTPHeader header = new HTTPHeader(name, value);
-		httpHeaderList.add(header);
-	}
-
-	public HTTPHeader getHeader(int n)
-	{
-		return (HTTPHeader)httpHeaderList.get(n);
-	}
-	
-	public HTTPHeader getHeader(String name)
-	{
-		int nHeaders = getNHeaders();
-		for (int n=0; n<nHeaders; n++) {
-			HTTPHeader header = getHeader(n);
-			String headerName = header.getName();
-			if (headerName.equalsIgnoreCase(name) == true)
-				return header;			
-		}
-		return null;
-	}
+		return (0 < firstLine.length()) ? true : false;
+	}
+	
+	////////////////////////////////////////////////
+	//	Header
+	////////////////////////////////////////////////
+
+	private Vector httpHeaderList = new Vector();
+	
+	public int getNHeaders()
+	{
+		return httpHeaderList.size();
+	}
+
+	public void addHeader(HTTPHeader header)
+	{
+		httpHeaderList.add(header);
+	}
+
+	public void addHeader(String name, String value)
+	{
+		HTTPHeader header = new HTTPHeader(name, value);
+		httpHeaderList.add(header);
+	}
+
+	public HTTPHeader getHeader(int n)
+	{
+		return (HTTPHeader)httpHeaderList.get(n);
+	}
+	
+	public HTTPHeader getHeader(String name)
+	{
+		int nHeaders = getNHeaders();
+		for (int n=0; n<nHeaders; n++) {
+			HTTPHeader header = getHeader(n);
+			String headerName = header.getName();
+			if (headerName.equalsIgnoreCase(name) == true)
+				return header;			
+		}
+		return null;
+	}
 
 	public void clearHeaders()
 	{
@@ -364,16 +412,16 @@ public class HTTPPacket
 	{
 		return (getHeader(name) != null) ? true : false;
 	}
-
-	public void setHeader(String name, String value)
-	{
-		HTTPHeader header = getHeader(name);
-		if (header != null) {
-			header.setValue(value);
-			return;
-		}
-		addHeader(name, value);
-	}
+
+	public void setHeader(String name, String value)
+	{
+		HTTPHeader header = getHeader(name);
+		if (header != null) {
+			header.setValue(value);
+			return;
+		}
+		addHeader(name, value);
+	}
 
 	public void setHeader(String name, int value)
 	{
@@ -384,19 +432,19 @@ public class HTTPPacket
 	{
 		setHeader(name, Long.toString(value));
 	}
-	
-	public void setHeader(HTTPHeader header)
-	{
-		setHeader(header.getName(), header.getValue());
-	}
-
-	public String getHeaderValue(String name)
-	{
-		HTTPHeader header = getHeader(name);
-		if (header == null)
-			return "";
-		return header.getValue();
-	}
+	
+	public void setHeader(HTTPHeader header)
+	{
+		setHeader(header.getName(), header.getValue());
+	}
+
+	public String getHeaderValue(String name)
+	{
+		HTTPHeader header = getHeader(name);
+		if (header == null)
+			return "";
+		return header.getValue();
+	}
 
 	////////////////////////////////////////////////
 	// set*Value
@@ -431,7 +479,7 @@ public class HTTPPacket
 	{
 		return getStringHeaderValue(name, "\"", "\"");
 	}
-
+
 	public void setIntegerHeader(String name, int value)
 	{
 		setHeader(name, Integer.toString(value));
@@ -442,13 +490,13 @@ public class HTTPPacket
 		setHeader(name, Long.toString(value));
 	}
 	
-	public int getIntegerHeaderValue(String name)
-	{
-		HTTPHeader header = getHeader(name);
-		if (header == null)
-			return 0;
-		return StringUtil.toInteger(header.getValue());
-	}
+	public int getIntegerHeaderValue(String name)
+	{
+		HTTPHeader header = getHeader(name);
+		if (header == null)
+			return 0;
+		return StringUtil.toInteger(header.getValue());
+	}
 
 	public long getLongHeaderValue(String name)
 	{
@@ -457,14 +505,14 @@ public class HTTPPacket
 			return 0;
 		return StringUtil.toLong(header.getValue());
 	}
-
+
 	////////////////////////////////////////////////
 	//	getHeader
 	////////////////////////////////////////////////
 	
 	public String getHeaderString()
 	{
-		StringBuilder str = new StringBuilder();
+		StringBuffer str = new StringBuffer();
 	
 		int nHeaders = getNHeaders();
 		for (int n=0; n<nHeaders; n++) {
@@ -475,48 +523,57 @@ public class HTTPPacket
 		return str.toString();
 	}
 
-	////////////////////////////////////////////////
-	//	Contents
-	////////////////////////////////////////////////
-
-	private byte content[] = new byte[0];
-	
-	public void setContent(byte data[], boolean updateWithContentLength)
-	{
+	////////////////////////////////////////////////
+	//	Contents
+	////////////////////////////////////////////////
+
+	private byte content[] = new byte[0];
+	
+	public void setContent(byte data[], boolean updateWithContentLength)
+	{
 		content = data;
-		if (updateWithContentLength == true)
-			setContentLength(data.length);
-	}
-
+		if (updateWithContentLength == true)
+			setContentLength(data.length);
+	}
+
 	public void setContent(byte data[])
 	{
 		setContent(data, true);
 	}
 	
-	public void setContent(String data, boolean updateWithContentLength)
-	{
-		setContent(data.getBytes(), updateWithContentLength);
-	}
-
+	public void setContent(String data, boolean updateWithContentLength)
+	{
+		setContent(data.getBytes(), updateWithContentLength);
+	}
+
 	public void setContent(String data)
 	{
 		setContent(data, true);
 	}
 	
-	public  byte []getContent()
-	{
-		return content;
-	}
-
-	public  String getContentString()
-	{
-		return new String(content);
+	public  byte []getContent()
+	{
+		return content;
+	}
+
+	public  String getContentString()
+	{
+		String charSet = getCharSet();
+		if (charSet == null || charSet.length() <= 0)
+			return new String(content);
+		try {
+			return new String(content, charSet);
+		}
+		catch (Exception e) {
+			Debug.warning(e);
+		}
+		return new String(content);
 	}
 	
 	public boolean hasContent()
 	{
 		return (content.length > 0) ? true : false;
-	}
+	}
 
 	////////////////////////////////////////////////
 	//	Contents (InputStream)
@@ -538,35 +595,61 @@ public class HTTPPacket
 	{
 		return (contentInput != null) ? true : false;
 	}
-
-	////////////////////////////////////////////////
-	//	ContentType
-	////////////////////////////////////////////////
-
-	public void setContentType(String type)
-	{
-		setHeader(HTTP.CONTENT_TYPE, type);
-	}
-
-	public String getContentType()
-	{
-		return getHeaderValue(HTTP.CONTENT_TYPE);
-	}
-
-	////////////////////////////////////////////////
-	//	ContentLength
-	////////////////////////////////////////////////
-
-	public void setContentLength(long len)
-	{
-		setLongHeader(HTTP.CONTENT_LENGTH, len);
-	}
-
-	public long getContentLength()
-	{
-		return getLongHeaderValue(HTTP.CONTENT_LENGTH);
-	}
-
+
+	////////////////////////////////////////////////
+	//	ContentType
+	////////////////////////////////////////////////
+
+	public void setContentType(String type)
+	{
+		setHeader(HTTP.CONTENT_TYPE, type);
+	}
+
+	public String getContentType()
+	{
+		return getHeaderValue(HTTP.CONTENT_TYPE);
+	}
+
+	////////////////////////////////////////////////
+	//	Charset
+	////////////////////////////////////////////////
+
+	public String getCharSet()
+	{
+		String contentType = getContentType();
+		if (contentType == null)
+			return "";
+		contentType = contentType.toLowerCase();
+		int charSetIdx = contentType.indexOf(HTTP.CHARSET);
+		if (charSetIdx < 0)
+			return "";
+		int charSetEndIdx = charSetIdx + HTTP.CHARSET.length() + 1; 
+		String charSet = new String(contentType.getBytes(), charSetEndIdx, (contentType.length() - charSetEndIdx));
+		if (charSet.length() < 0)
+			return "";
+		if (charSet.charAt(0) == '\"')
+			charSet = charSet.substring(1, (charSet.length() - 1));
+		if (charSet.length() < 0)
+			return "";
+		if (charSet.charAt((charSet.length()-1)) == '\"')
+			charSet = charSet.substring(0, (charSet.length() - 1));
+		return charSet;
+	}
+
+	////////////////////////////////////////////////
+	//	ContentLength
+	////////////////////////////////////////////////
+
+	public void setContentLength(long len)
+	{
+		setLongHeader(HTTP.CONTENT_LENGTH, len);
+	}
+
+	public long getContentLength()
+	{
+		return getLongHeaderValue(HTTP.CONTENT_LENGTH);
+	}
+
 	////////////////////////////////////////////////
 	//	Connection
 	////////////////////////////////////////////////
@@ -642,7 +725,7 @@ public class HTTPPacket
 		// Skip bytes
 		if (strToken.hasMoreTokens() == false)
 			return range;
-		strToken.nextToken(" ");
+		String bytesStr = strToken.nextToken(" ");
 		// Get first-byte-pos
 		if (strToken.hasMoreTokens() == false)
 			return range;
@@ -686,10 +769,10 @@ public class HTTPPacket
 		return range[2];
 	}
 	
-	////////////////////////////////////////////////
-	//	CacheControl
-	////////////////////////////////////////////////
-
+	////////////////////////////////////////////////
+	//	CacheControl
+	////////////////////////////////////////////////
+
 	public void setCacheControl(String directive)
 	{
 		setHeader(HTTP.CACHE_CONTROL, directive);
@@ -701,29 +784,29 @@ public class HTTPPacket
 		setHeader(HTTP.CACHE_CONTROL, strVal);
 	}
 	
-	public void setCacheControl(int value)
-	{
+	public void setCacheControl(int value)
+	{
 		setCacheControl(HTTP.MAX_AGE, value);
-	}
-
-	public String getCacheControl()
-	{
-		return getHeaderValue(HTTP.CACHE_CONTROL);
-	}
-
-	////////////////////////////////////////////////
-	//	Server
-	////////////////////////////////////////////////
-
-	public void setServer(String name)
-	{
-		setHeader(HTTP.SERVER, name);
-	}
-
-	public String getServer()
-	{
-		return getHeaderValue(HTTP.SERVER);
-	}
+	}
+
+	public String getCacheControl()
+	{
+		return getHeaderValue(HTTP.CACHE_CONTROL);
+	}
+
+	////////////////////////////////////////////////
+	//	Server
+	////////////////////////////////////////////////
+
+	public void setServer(String name)
+	{
+		setHeader(HTTP.SERVER, name);
+	}
+
+	public String getServer()
+	{
+		return getHeaderValue(HTTP.SERVER);
+	}
 
 	////////////////////////////////////////////////
 	//	Host
@@ -737,26 +820,34 @@ public class HTTPPacket
 		setHeader(HTTP.HOST, hostAddr + ":" + Integer.toString(port));
 	}
 
+	public void setHost(String host)
+	{
+		String hostAddr = host;
+		if (HostInterface.isIPv6Address(host) == true)
+			hostAddr = "[" + host + "]";
+		setHeader(HTTP.HOST, hostAddr);
+	}
+	
 	public String getHost()
 	{
 		return getHeaderValue(HTTP.HOST);
 	}
 
-
-	////////////////////////////////////////////////
-	//	Date
-	////////////////////////////////////////////////
-
-	public void setDate(Calendar cal)
-	{
-		Date date = new Date(cal);
-		setHeader(HTTP.DATE, date.getDateString());
-	}
-
-	public String getDate()
-	{
-		return getHeaderValue(HTTP.DATE);
-	}
+
+	////////////////////////////////////////////////
+	//	Date
+	////////////////////////////////////////////////
+
+	public void setDate(Calendar cal)
+	{
+		Date date = new Date(cal);
+		setHeader(HTTP.DATE, date.getDateString());
+	}
+
+	public String getDate()
+	{
+		return getHeaderValue(HTTP.DATE);
+	}
 
 	////////////////////////////////////////////////
 	//	Connection
@@ -804,5 +895,5 @@ public class HTTPPacket
 		return false;
 	}
 */
-}
-
+}
+
diff --git a/router/java/src/org/cybergarage/http/HTTPRequest.java b/router/java/src/org/cybergarage/http/HTTPRequest.java
index f4c6f16e2cda3d58979995c969d1957966b40858..6735427244c4728c87ffe6e758bd17488edc2790 100644
--- a/router/java/src/org/cybergarage/http/HTTPRequest.java
+++ b/router/java/src/org/cybergarage/http/HTTPRequest.java
@@ -44,15 +44,35 @@
 *		- Added to check the range of Content-Range request in post().
 *	03/02/05
 *		- Changed post() to suppot chunked stream.
+*	06/10/05
+*		- Changed post() to add a HOST headedr before the posting.
+*	07/07/05
+*		- Lee Peik Feng <pflee@users.sourceforge.net>
+*		- Fixed post() to output the chunk size as a hex string.
 *
 ******************************************************************/
 
 package org.cybergarage.http;
 
-import java.io.*;
-import java.net.*;
-import java.util.*;
-
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.net.Socket;
+import java.net.SocketException;
+import java.net.InetSocketAddress;
+import java.util.StringTokenizer;
+
+import org.cybergarage.util.Debug;
+/**
+ * 
+ * This class rappresnet an HTTP <b>request</b>, and act as HTTP client when it sends the request<br>
+ * 
+ * @author Satoshi "skonno" Konno
+ * @author Stefano "Kismet" Lenzi
+ * @version 1.8
+ *
+ */
 public class HTTPRequest extends HTTPPacket
 {
 	////////////////////////////////////////////////
@@ -61,6 +81,7 @@ public class HTTPRequest extends HTTPPacket
 	
 	public HTTPRequest()
 	{
+		setVersion(HTTP.VERSION_10);
 	}
 
 	public HTTPRequest(InputStream in)
@@ -296,7 +317,7 @@ public class HTTPRequest extends HTTPPacket
 	
 	public String getHeader()
 	{
-		StringBuilder str = new StringBuilder();
+		StringBuffer str = new StringBuffer();
 		
 		str.append(getFirstLineString());
 		
@@ -370,6 +391,8 @@ public class HTTPRequest extends HTTPPacket
 	{
 		HTTPResponse httpRes = new HTTPResponse();
 
+		setHost(host);
+		
 		setConnection((isKeepAlive == true) ? HTTP.KEEP_ALIVE : HTTP.CLOSE);
 		
 		boolean isHeaderRequest = isHeadRequest();
@@ -378,8 +401,11 @@ public class HTTPRequest extends HTTPPacket
 		InputStream in = null;
 		
  		try {
- 			if (postSocket == null)
-				postSocket = new Socket(host, port);
+ 			if (postSocket == null){
+ 				// Thanks for Hao Hu 
+				postSocket = new Socket();
+				postSocket.connect(new InetSocketAddress(host, port), HTTPServer.DEFAULT_TIMEOUT);
+ 			}
 
 			out = postSocket.getOutputStream();
 			PrintStream pout = new PrintStream(out);
@@ -395,7 +421,8 @@ public class HTTPRequest extends HTTPPacket
 			
 			if (0 < contentLength) {
 				if (isChunkedRequest == true) {
-					String chunSizeBuf = Long.toString(contentLength);
+					// Thanks for Lee Peik Feng <pflee@users.sourceforge.net> (07/07/05)
+					String chunSizeBuf = Long.toHexString(contentLength);
 					pout.print(chunSizeBuf);
 					pout.print(HTTP.CRLF);
 				}
@@ -413,9 +440,14 @@ public class HTTPRequest extends HTTPPacket
 
 			in = postSocket.getInputStream();
 			httpRes.set(in, isHeaderRequest);		
-		}
-		catch (Exception e) {
-			httpRes.setStatusCode(HTTPStatus.INTERNAL_SERVER_ERROR);
+		} catch (SocketException e) {
+			httpRes.setStatusCode(HTTPStatus.INTERNAL_SERVER_ERROR);
+			Debug.warning(e);
+		} catch (IOException e) {
+			//Socket create but without connection
+			//TODO Blacklistening the device
+			httpRes.setStatusCode(HTTPStatus.INTERNAL_SERVER_ERROR);
+			Debug.warning(e);
 		} finally {
 			if (isKeepAlive == false) {	
 				try {
@@ -479,7 +511,7 @@ public class HTTPRequest extends HTTPPacket
 	
 	public String toString()
 	{
-		StringBuilder str = new StringBuilder();
+		StringBuffer str = new StringBuffer();
 
 		str.append(getHeader());
 		str.append(HTTP.CRLF);
diff --git a/router/java/src/org/cybergarage/http/HTTPResponse.java b/router/java/src/org/cybergarage/http/HTTPResponse.java
index b5c24787890b75668410ed046631cb43a7e68246..e028e02f52b9710e359846798f55a12d6fe360d3 100644
--- a/router/java/src/org/cybergarage/http/HTTPResponse.java
+++ b/router/java/src/org/cybergarage/http/HTTPResponse.java
@@ -19,7 +19,7 @@
 
 package org.cybergarage.http;
 
-import java.io.*;
+import java.io.InputStream;
 
 public class HTTPResponse extends HTTPPacket
 {
@@ -29,6 +29,7 @@ public class HTTPResponse extends HTTPPacket
 	
 	public HTTPResponse()
 	{
+		setVersion(HTTP.VERSION_11);
 		setContentType(HTML.CONTENT_TYPE);
 		setServer(HTTPServer.getName());
 		setContent("");
@@ -84,7 +85,7 @@ public class HTTPResponse extends HTTPPacket
 	
 	public String getHeader()
 	{
-		StringBuilder str = new StringBuilder();
+		StringBuffer str = new StringBuffer();
 	
 		str.append(getStatusLineString());
 		str.append(getHeaderString());
@@ -98,7 +99,7 @@ public class HTTPResponse extends HTTPPacket
 	
 	public String toString()
 	{
-		StringBuilder str = new StringBuilder();
+		StringBuffer str = new StringBuffer();
 
 		str.append(getStatusLineString());
 		str.append(getHeaderString());
diff --git a/router/java/src/org/cybergarage/http/HTTPServer.java b/router/java/src/org/cybergarage/http/HTTPServer.java
index f589e2e06147f1112ee077a7e31bf53ae15db647..1ac591744889ca269d6971448f76eb3dfbaff0ef 100644
--- a/router/java/src/org/cybergarage/http/HTTPServer.java
+++ b/router/java/src/org/cybergarage/http/HTTPServer.java
@@ -5,36 +5,57 @@
 *	Copyright (C) Satoshi Konno 2002-2003
 *
 *	File: HTTPServer.java
-*
-*	Revision;
-*
-*	12/12/02
-*		- first revision.
+*
+*	Revision;
+*
+*	12/12/02
+*		- first revision.
 *	10/20/03
 *		- Improved the HTTP server using multithreading.
 *	08/27/04
 *		- Changed accept() to set a default timeout, HTTP.DEFAULT_TIMEOUT, to the socket.
 *	
 ******************************************************************/
-
-package org.cybergarage.http;
-
-import java.io.*;
-import java.net.*;
-
-import org.cybergarage.util.*;
-
-public class HTTPServer implements Runnable
-{
-	////////////////////////////////////////////////
-	//	Constants
-	////////////////////////////////////////////////
-
+
+package org.cybergarage.http;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+import org.cybergarage.util.Debug;
+import org.cybergarage.util.ListenerList;
+
+/**
+ * 
+ * This class identifies an HTTP over TCP server<br>
+ * The server must be initialized iether by the {@link HTTPServer#open(InetAddress, int)} or the {@link HTTPServer#open(String, int)} method.<br>
+ * Optionally a set of {@link HTTPRequestListener} may be set<br>
+ * The server then can be started or stopped by the method {@link HTTPServer#start()} and {@link HTTPServer#stop()}
+ * 
+ * @author Satoshi "skonno" Konno
+ * @author Stefano "Kismet" Lenzi
+ * @version 1.8
+ *
+ */
+public class HTTPServer implements Runnable
+{
+	////////////////////////////////////////////////
+	//	Constants
+	////////////////////////////////////////////////
+
 	public final static String NAME = "CyberHTTP";
 	public final static String VERSION = "1.0";
 
 	public final static int DEFAULT_PORT = 80;
-
+	
+	/**
+	 * Default timeout connection for HTTP comunication
+	 * @since 1.8
+	 */
+	public final static int DEFAULT_TIMEOUT = DEFAULT_PORT * 1000;
+	
 	public static String getName()
 	{
 		String osName = System.getProperty("os.name");
@@ -42,27 +63,33 @@ public class HTTPServer implements Runnable
 		return osName + "/"  + osVer + " " + NAME + "/" + VERSION;
 	}
 	
-	////////////////////////////////////////////////
-	//	Constructor
-	////////////////////////////////////////////////
-	
-	public HTTPServer()
+	////////////////////////////////////////////////
+	//	Constructor
+	////////////////////////////////////////////////
+	
+	public HTTPServer()
 	{
-		serverSock = null;
-	}
-
-	////////////////////////////////////////////////
-	//	ServerSocket
-	////////////////////////////////////////////////
-
-	private ServerSocket serverSock = null;
+		serverSock = null;
+		
+	}
+
+	////////////////////////////////////////////////
+	//	ServerSocket
+	////////////////////////////////////////////////
+
+	private ServerSocket serverSock = null;
 	private InetAddress bindAddr = null;
 	private int bindPort = 0;
-	
-	public ServerSocket getServerSock()
-	{
-		return serverSock;
-	}
+	/**
+	 * Store the current TCP timeout value
+	 * The variable should be accessed by getter and setter metho
+	 */
+	protected int timeout = DEFAULT_TIMEOUT;
+	
+	public ServerSocket getServerSock()
+	{
+		return serverSock;
+	}
 
 	public String getBindAddress()
 	{
@@ -76,105 +103,134 @@ public class HTTPServer implements Runnable
 		return bindPort;
 	}
 	
+	
+	
 	////////////////////////////////////////////////
 	//	open/close
 	////////////////////////////////////////////////
-	
-	public boolean open(String addr, int port)
+	
+	/**
+	 * Get the current socket timeout
+	 * @since 1.8
+	 */
+	public synchronized int getTimeout() {
+		return timeout;
+	}
+
+	/**
+	 * Set the current socket timeout
+	 * @param longout new timeout
+	 * @since 1.8
+	 */
+	public synchronized void setTimeout(int timeout) {
+		this.timeout = timeout;
+	}
+
+	public boolean open(InetAddress addr,int port){
+		if (serverSock != null)
+			return true;
+		try {
+			serverSock = new ServerSocket(bindPort, 0, bindAddr);
+		}catch (IOException e) {
+			return false;
+		}
+		return true;
+	}
+	
+	public boolean open(String addr, int port)
 	{
 		if (serverSock != null)
 			return true;
-		try {
+		try {
 			bindAddr = InetAddress.getByName(addr);
 			bindPort = port;
 			serverSock = new ServerSocket(bindPort, 0, bindAddr);
-			serverSock.setSoTimeout(10*1000);
 		}
 		catch (IOException e) {
 			return false;
 		}
-		return true;
-	}
-
-	public boolean close()
-	{
+		return true;
+	}
+
+	public boolean close()
+	{
 		if (serverSock == null)
 			return true;
 		try {
 			serverSock.close();
 			serverSock = null;
 			bindAddr = null;
-			bindPort = 0;
-		}
-		catch (Exception e) {
+			bindPort = 0;
+		}
+		catch (Exception e) {
 			Debug.warning(e);
-			return false;
+			return false;
 		}
-		return true;
-	}
-
-	public Socket accept()
-	{
+		return true;
+	}
+
+	public Socket accept()
+	{
 		if (serverSock == null)
 			return null;
-		try {
+		try {
 			Socket sock = serverSock.accept();
-			sock.setSoTimeout(HTTP.DEFAULT_PORT * 1000);
-			return sock;
-		}
-		catch (Exception e) {
-			return null;
-		}
-	}
-
-	public boolean isOpened()
-	{
-		return (serverSock != null) ? true : false;
-	}
-
-	////////////////////////////////////////////////
-	//	httpRequest
-	////////////////////////////////////////////////
-
-	private ListenerList httpRequestListenerList = new ListenerList();
-	 	
-	public void addRequestListener(HTTPRequestListener listener)
-	{
-		httpRequestListenerList.add(listener);
-	}		
-
-	public void removeRequestListener(HTTPRequestListener listener)
-	{
-		httpRequestListenerList.remove(listener);
-	}		
-
-	public void performRequestListener(HTTPRequest httpReq)
-	{
-		int listenerSize = httpRequestListenerList.size();
-		for (int n=0; n<listenerSize; n++) {
-			HTTPRequestListener listener = (HTTPRequestListener)httpRequestListenerList.get(n);
-			listener.httpRequestRecieved(httpReq);
-		}
-	}		
-	
-	////////////////////////////////////////////////
-	//	run	
-	////////////////////////////////////////////////
-
-	private Thread httpServerThread = null;
-		
-	public void run()
-	{
-		if (isOpened() == false)
-			return;
-			
-		Thread thisThread = Thread.currentThread();
-		
-		while (httpServerThread == thisThread) {
+			sock.setSoTimeout(getTimeout());
+			return sock;
+		}
+		catch (Exception e) {
+			return null;
+		}
+	}
+
+	public boolean isOpened()
+	{
+		return (serverSock != null) ? true : false;
+	}
+
+	////////////////////////////////////////////////
+	//	httpRequest
+	////////////////////////////////////////////////
+
+	private ListenerList httpRequestListenerList = new ListenerList();
+	 	
+	public void addRequestListener(HTTPRequestListener listener)
+	{
+		httpRequestListenerList.add(listener);
+	}		
+
+	public void removeRequestListener(HTTPRequestListener listener)
+	{
+		httpRequestListenerList.remove(listener);
+	}		
+
+	public void performRequestListener(HTTPRequest httpReq)
+	{
+		int listenerSize = httpRequestListenerList.size();
+		for (int n=0; n<listenerSize; n++) {
+			HTTPRequestListener listener = (HTTPRequestListener)httpRequestListenerList.get(n);
+			listener.httpRequestRecieved(httpReq);
+		}
+	}		
+	
+	////////////////////////////////////////////////
+	//	run	
+	////////////////////////////////////////////////
+
+	private Thread httpServerThread = null;
+		
+	public void run()
+	{
+		if (isOpened() == false)
+			return;
+			
+		Thread thisThread = Thread.currentThread();
+		
+		while (httpServerThread == thisThread) {
 			Thread.yield();
 			Socket sock;
 			try {
-				Debug.message("accept ...");
+				Debug.message("accept ...");
 				sock = accept();
 				if (sock != null)
 					Debug.message("sock = " + sock.getRemoteSocketAddress());
@@ -186,19 +242,20 @@ public class HTTPServer implements Runnable
 			HTTPServerThread httpServThread = new HTTPServerThread(this, sock);
 			httpServThread.start(); 
 			Debug.message("httpServThread ...");
-		}
-	}
-	
-	public boolean start()
-	{
-		httpServerThread = new Thread(this, "UPnP-HTTPServer");
+		}
+	}
+	
+	public boolean start(){
+		StringBuffer name = new StringBuffer("Cyber.HTTPServer/");
+		name.append(serverSock.getLocalSocketAddress());
+		httpServerThread = new Thread(this,name.toString());
 		httpServerThread.start();
-		return true;
-	}
-	
-	public boolean stop()
+		return true;
+	}
+	
+	public boolean stop()
 	{
 		httpServerThread = null;
-		return true;
-	}
+		return true;
+	}
 }
diff --git a/router/java/src/org/cybergarage/http/HTTPServerList.java b/router/java/src/org/cybergarage/http/HTTPServerList.java
index 3a4e6588760fef953b549117a38324db945c2a03..fb85ad8f5f16d364ed72f4e25f10d450160213eb 100644
--- a/router/java/src/org/cybergarage/http/HTTPServerList.java
+++ b/router/java/src/org/cybergarage/http/HTTPServerList.java
@@ -1,104 +1,133 @@
-/******************************************************************
-*
-*	CyberUPnP for Java
-*
-*	Copyright (C) Satoshi Konno 2002-2003
+/******************************************************************
 *
-*	File: HTTPServerList.java
-*
-*	Revision;
-*
-*	05/08/03
-*		- first revision.
-*
-******************************************************************/
+*	CyberUPnP for Java
+*
+*	Copyright (C) Satoshi Konno 2002-2003
+*
+*	File: HTTPServerList.java
+*
+*	Revision;
+*
+*	05/08/03
+*		- first revision.
+*	24/03/06
+*		- Stefano Lenzi:added debug information as request by Stephen More
+*
+******************************************************************/
+
+package org.cybergarage.http;
+
+import java.net.InetAddress;
+import java.util.Vector;
+
+import org.cybergarage.net.HostInterface;
+import org.cybergarage.upnp.Device;
+
+public class HTTPServerList extends Vector 
+{
+	////////////////////////////////////////////////
+	//	Constructor
+	////////////////////////////////////////////////
+	
+	private InetAddress[] binds = null;
+	private int port = Device.HTTP_DEFAULT_PORT;
+	
+	public HTTPServerList() {
+	}
+	
+	public HTTPServerList(InetAddress[] list, int port) {
+		this.binds = list;
+		this.port = port;
+	}
+
+	////////////////////////////////////////////////
+	//	Methods
+	////////////////////////////////////////////////
+
+	public void addRequestListener(HTTPRequestListener listener)
+	{
+		int nServers = size();
+		for (int n=0; n<nServers; n++) {
+			HTTPServer server = getHTTPServer(n);
+			server.addRequestListener(listener);
+		}
+	}		
+	
+	public HTTPServer getHTTPServer(int n)
+	{
+		return (HTTPServer)get(n);
+	}
+
+	////////////////////////////////////////////////
+	//	open/close
+	////////////////////////////////////////////////
+
+	public void close()
+	{
+		int nServers = size();
+		for (int n=0; n<nServers; n++) {
+			HTTPServer server = getHTTPServer(n);
+			server.close();
+		}
+	}
+
+	public int open(){
+		InetAddress[] binds=this.binds;
+		String[] bindAddresses;
+		if(binds!=null){			
+			bindAddresses = new String[binds.length];
+			for (int i = 0; i < binds.length; i++) {
+				bindAddresses[i] = binds[i].getHostAddress();
+			}
+		}else{
+			int nHostAddrs = HostInterface.getNHostAddresses();
+			bindAddresses = new String[nHostAddrs]; 
+			for (int n=0; n<nHostAddrs; n++) {
+				bindAddresses[n] = HostInterface.getHostAddress(n);
+			}
+		}		
+		int j=0;
+		for (int i = 0; i < bindAddresses.length; i++) {
+			HTTPServer httpServer = new HTTPServer();
+			if((bindAddresses[i]==null) || (httpServer.open(bindAddresses[i], port) == false)) {
+				close();
+				clear();
+			}else{
+				add(httpServer);
+				j++;
+			}
+		}
+		return j;
+	}
+	
+	
+	public boolean open(int port) 
+	{
+		this.port=port;
+		return open()!=0;
+	}
+	
+	////////////////////////////////////////////////
+	//	start/stop
+	////////////////////////////////////////////////
+	
+	public void start()
+	{
+		int nServers = size();
+		for (int n=0; n<nServers; n++) {
+			HTTPServer server = getHTTPServer(n);
+			server.start();
+		}
+	}
+
+	public void stop()
+	{
+		int nServers = size();
+		for (int n=0; n<nServers; n++) {
+			HTTPServer server = getHTTPServer(n);
+			server.stop();
+		}
+	}
+
+}
 
-package org.cybergarage.http;
-
-import java.util.*;
-
-import org.cybergarage.net.*;
-
-public class HTTPServerList extends Vector 
-{
-	////////////////////////////////////////////////
-	//	Constructor
-	////////////////////////////////////////////////
-	
-	private static final long serialVersionUID = 2379889735659369065L;
-
-	public HTTPServerList() 
-	{
-	}
-
-	////////////////////////////////////////////////
-	//	Methods
-	////////////////////////////////////////////////
-
-	public void addRequestListener(HTTPRequestListener listener)
-	{
-		int nServers = size();
-		for (int n=0; n<nServers; n++) {
-			HTTPServer server = getHTTPServer(n);
-			server.addRequestListener(listener);
-		}
-	}		
-	
-	public HTTPServer getHTTPServer(int n)
-	{
-		return (HTTPServer)get(n);
-	}
-
-	////////////////////////////////////////////////
-	//	open/close
-	////////////////////////////////////////////////
-
-	public void close()
-	{
-		int nServers = size();
-		for (int n=0; n<nServers; n++) {
-			HTTPServer server = getHTTPServer(n);
-			server.close();
-		}
-	}
-
-	public boolean open(int port) 
-	{
-		int nHostAddrs = HostInterface.getNHostAddresses();
-		for (int n=0; n<nHostAddrs; n++) {
-			String bindAddr = HostInterface.getHostAddress(n);
-			HTTPServer httpServer = new HTTPServer();
-			if (httpServer.open(bindAddr, port) == false) {
-				close();
-				clear();
-				return false;
-			}
-			add(httpServer);
-		}
-		return true;
-	}
-	
-	////////////////////////////////////////////////
-	//	start/stop
-	////////////////////////////////////////////////
-	
-	public void start()
-	{
-		int nServers = size();
-		for (int n=0; n<nServers; n++) {
-			HTTPServer server = getHTTPServer(n);
-			server.start();
-		}
-	}
-
-	public void stop()
-	{
-		int nServers = size();
-		for (int n=0; n<nServers; n++) {
-			HTTPServer server = getHTTPServer(n);
-			server.stop();
-		}
-	}
-
-}
-
diff --git a/router/java/src/org/cybergarage/http/HTTPServerThread.java b/router/java/src/org/cybergarage/http/HTTPServerThread.java
index 40351ecaf0b91c84e21eeb36df4be7cc128aceac..70524edbe3ec9e0c90d95ed083d989746eaee0bb 100644
--- a/router/java/src/org/cybergarage/http/HTTPServerThread.java
+++ b/router/java/src/org/cybergarage/http/HTTPServerThread.java
@@ -15,7 +15,7 @@
 
 package org.cybergarage.http;
 
-import java.net.*;
+import java.net.Socket;
 
 public class HTTPServerThread extends Thread
 {
@@ -28,9 +28,9 @@ public class HTTPServerThread extends Thread
 	
 	public HTTPServerThread(HTTPServer httpServer, Socket sock)
 	{
+        super("Cyber.HTTPServerThread");
 		this.httpServer = httpServer;
 		this.sock = sock;
-		this.setDaemon(true);
 	}
 
 	////////////////////////////////////////////////
diff --git a/router/java/src/org/cybergarage/http/HTTPSocket.java b/router/java/src/org/cybergarage/http/HTTPSocket.java
index 6709aa41a4c1f036aa25ced2606f0f30be68ab40..837f2dcc5fbcc8c4366f10d52b05772fe1deaa80 100644
--- a/router/java/src/org/cybergarage/http/HTTPSocket.java
+++ b/router/java/src/org/cybergarage/http/HTTPSocket.java
@@ -5,11 +5,11 @@
 *	Copyright (C) Satoshi Konno 2002-2004
 *
 *	File: HTTPSocket.java
-*
-*	Revision;
-*
-*	12/12/02
-*		- first revision.
+*
+*	Revision;
+*
+*	12/12/02
+*		- first revision.
 *	03/11/04
 *		- Added the following methods about chunk size.
 *		  setChunkSize(), getChunkSize().
@@ -17,26 +17,32 @@
 *		- Added a isOnlyHeader to post().
 *	03/02/05
 *		- Changed post() to suppot chunked stream.
+*	06/10/05
+*		- Changed post() to add a Date headedr to the HTTPResponse before the posting.
+*	07/07/05
+*		- Lee Peik Feng <pflee@users.sourceforge.net>
+*		- Fixed post() to output the chunk size as a hex string.
 *	
 ******************************************************************/
-
-package org.cybergarage.http;
-
-import java.io.*;
-import java.net.*;
-import java.util.*;
-
-public class HTTPSocket
-{
-	////////////////////////////////////////////////
-	//	Constructor
-	////////////////////////////////////////////////
-	
-	public HTTPSocket(Socket socket)
-	{
-		setSocket(socket);
+
+package org.cybergarage.http;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.util.Calendar;
+
+public class HTTPSocket
+{
+	////////////////////////////////////////////////
+	//	Constructor
+	////////////////////////////////////////////////
+	
+	public HTTPSocket(Socket socket)
+	{
+		setSocket(socket);
 		open();
-	}
+	}
 
 	public HTTPSocket(HTTPSocket socket)
 	{
@@ -45,11 +51,11 @@ public class HTTPSocket
 		setOutputStream(socket.getOutputStream());
 	}
 	
-	public void finalize()
-	{
-		close();
-	}
-	
+	public void finalize()
+	{
+		close();
+	}
+	
 	////////////////////////////////////////////////
 	//	Socket
 	////////////////////////////////////////////////
@@ -86,27 +92,27 @@ public class HTTPSocket
 
 	private InputStream sockIn = null;
 	private OutputStream sockOut = null;
-
+
 	private void setInputStream(InputStream in)
 	{
 		sockIn = in;
 	}
 	
-	public InputStream getInputStream()
-	{
-		return sockIn;
-	}
+	public InputStream getInputStream()
+	{
+		return sockIn;
+	}
 
 	private void setOutputStream(OutputStream out)
 	{
 		sockOut = out;
 	}
-	
-	private OutputStream getOutputStream()
-	{
-		return sockOut;
-	}
-
+	
+	private OutputStream getOutputStream()
+	{
+		return sockOut;
+	}
+
 	////////////////////////////////////////////////
 	//	open/close
 	////////////////////////////////////////////////
@@ -119,6 +125,7 @@ public class HTTPSocket
 			sockOut = sock.getOutputStream();
 		}
 		catch (Exception e) {
+			//TODO Add blacklistening of the UPnP Device
 			return false;
 		}
 		return true;
@@ -140,20 +147,22 @@ public class HTTPSocket
 		return true;
 	}
 	
-	////////////////////////////////////////////////
-	//	post
-	////////////////////////////////////////////////
-
-	private boolean post(HTTPResponse httpRes, byte content[], long contentOffset, long contentLength, boolean isOnlyHeader)
-	{
+	////////////////////////////////////////////////
+	//	post
+	////////////////////////////////////////////////
+
+	private boolean post(HTTPResponse httpRes, byte content[], long contentOffset, long contentLength, boolean isOnlyHeader)
+	{
+		//TODO Check for bad HTTP agents, this method may be list for IOInteruptedException and for blacklistening
 		httpRes.setDate(Calendar.getInstance());
-		OutputStream out = getOutputStream();
-
+		
+		OutputStream out = getOutputStream();
+
 		try {
 			httpRes.setContentLength(contentLength);
-			
-			out.write(httpRes.getHeader().getBytes());
-			out.write(HTTP.CRLF.getBytes());
+			
+			out.write(httpRes.getHeader().getBytes());
+			out.write(HTTP.CRLF.getBytes());
 			if (isOnlyHeader == true) {
 				out.flush();
 				return true;
@@ -162,7 +171,8 @@ public class HTTPSocket
 			boolean isChunkedResponse = httpRes.isChunked();
 			
 			if (isChunkedResponse == true) {
-				String chunSizeBuf = Long.toString(contentLength);
+				// Thanks for Lee Peik Feng <pflee@users.sourceforge.net> (07/07/05)
+				String chunSizeBuf = Long.toHexString(contentLength);
 				out.write(chunSizeBuf.getBytes());
 				out.write(HTTP.CRLF.getBytes());
 			}
@@ -174,20 +184,22 @@ public class HTTPSocket
 				out.write("0".getBytes());
 				out.write(HTTP.CRLF.getBytes());
 			}
-			
-			out.flush();
-		}
-		catch (Exception e) {
-			//Debug.warning(e);
+			
+			out.flush();
+		}
+		catch (Exception e) {
+			//Debug.warning(e);
 			return false;
 		}
 		
-		return true;
+		return true;
 	}
-	
+	
 	private boolean post(HTTPResponse httpRes, InputStream in, long contentOffset, long contentLength, boolean isOnlyHeader)
 	{
+		//TODO Check for bad HTTP agents, this method may be list for IOInteruptedException and for blacklistening
 		httpRes.setDate(Calendar.getInstance());
+		
 		OutputStream out = getOutputStream();
 
 		try {
@@ -213,7 +225,8 @@ public class HTTPSocket
 			int readLen = in.read(readBuf, 0, (int)readSize);
 			while (0 < readLen && readCnt < contentLength) {
 				if (isChunkedResponse == true) {
-					String chunSizeBuf = Long.toString(readLen);
+					// Thanks for Lee Peik Feng <pflee@users.sourceforge.net> (07/07/05)
+					String chunSizeBuf = Long.toHexString(readLen);
 					out.write(chunSizeBuf.getBytes());
 					out.write(HTTP.CRLF.getBytes());
 				}
@@ -242,6 +255,7 @@ public class HTTPSocket
 	
 	public boolean post(HTTPResponse httpRes, long contentOffset, long contentLength, boolean isOnlyHeader)
 	{
+		//TODO Close if Connection != keep-alive
 		if (httpRes.hasContentInputStream() == true)
 			return post(httpRes,httpRes.getContentInputStream(), contentOffset, contentLength, isOnlyHeader);
 		return post(httpRes,httpRes.getContent(), contentOffset, contentLength, isOnlyHeader);
diff --git a/router/java/src/org/cybergarage/http/HTTPStatus.java b/router/java/src/org/cybergarage/http/HTTPStatus.java
index b112b2bd03a81069ee9b39f73ae32cfb410594f9..651fecb34b974a5eee27061c2eaf5f35d5653736 100644
--- a/router/java/src/org/cybergarage/http/HTTPStatus.java
+++ b/router/java/src/org/cybergarage/http/HTTPStatus.java
@@ -25,9 +25,9 @@
 
 package org.cybergarage.http;
 
-import java.util.*;
-
-import org.cybergarage.util.*;
+import java.util.StringTokenizer;
+
+import org.cybergarage.util.Debug;
 
 public class HTTPStatus 
 {
diff --git a/router/java/src/org/cybergarage/http/ParameterList.java b/router/java/src/org/cybergarage/http/ParameterList.java
index 9a0b2a6d1d42792e54651182051639a1589464ed..9f395be6ad52f49f1e597653813bd0c8cd0c1dd4 100644
--- a/router/java/src/org/cybergarage/http/ParameterList.java
+++ b/router/java/src/org/cybergarage/http/ParameterList.java
@@ -15,12 +15,10 @@
 
 package org.cybergarage.http;
 
-import java.util.*;
+import java.util.Vector;
 
 public class ParameterList extends Vector 
 {
-	private static final long serialVersionUID = -6026765325018137641L;
-
 	public ParameterList() 
 	{
 	}
diff --git a/router/java/src/org/cybergarage/net/HostInterface.java b/router/java/src/org/cybergarage/net/HostInterface.java
index 798f33dcac61cdbf01ecc2ce491c763d87492eec..b4453c93d3d7b0162ede272cb28d0a5a9f65ba4c 100644
--- a/router/java/src/org/cybergarage/net/HostInterface.java
+++ b/router/java/src/org/cybergarage/net/HostInterface.java
@@ -25,8 +25,16 @@
 
 package org.cybergarage.net;
 
-import java.net.*;
-import java.util.*;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.cybergarage.util.Debug;
 
 public class HostInterface
 {
@@ -43,6 +51,9 @@ public class HostInterface
 	////////////////////////////////////////////////
 	
 	private static String ifAddress = "";
+	public final static int IPV4_BITMASK =  0x0001;
+	public final static int IPV6_BITMASK =  0x0010;
+	public final static int LOCAL_BITMASK = 0x0100;
 
 	public final static void setInterface(String ifaddr)
 	{
@@ -101,10 +112,62 @@ public class HostInterface
 				}
 			}
 		}
-		catch(Exception e){};
+		catch(Exception e){
+			Debug.warning(e);
+		};
 		return nHostAddrs;
 	}
 
+	/**
+	 * 
+	 * @param ipfilter
+	 * @param interfaces
+	 * @return
+	 * @since 1.8.0
+	 * @author Stefano "Kismet" Lenzi &lt;kismet.sl@gmail.com&gt;
+	 */
+	public final static InetAddress[] getInetAddress(int ipfilter,String[] interfaces){
+		Enumeration nis;
+		if(interfaces!=null){
+			Vector iflist = new Vector();
+			for (int i = 0; i < interfaces.length; i++) {
+				NetworkInterface ni;
+				try {
+					ni = NetworkInterface.getByName(interfaces[i]);
+				} catch (SocketException e) {
+					continue;
+				}
+				if(ni != null) iflist.add(ni);
+
+			}
+			nis = iflist.elements();
+		}else{
+			try {
+				nis = NetworkInterface.getNetworkInterfaces();
+			} catch (SocketException e) {
+				return null;
+			}
+		}		
+		ArrayList addresses = new ArrayList();
+		while (nis.hasMoreElements()){
+			NetworkInterface ni = (NetworkInterface)nis.nextElement();			
+			Enumeration addrs = ni.getInetAddresses();
+			while (addrs.hasMoreElements()) {
+				InetAddress addr = (InetAddress)addrs.nextElement();
+				if(((ipfilter & LOCAL_BITMASK)==0) && addr.isLoopbackAddress())
+					continue;
+				
+				if (((ipfilter & IPV4_BITMASK)!=0) && addr instanceof Inet4Address ) {						
+					addresses.add(addr);
+				}else if (((ipfilter & IPV6_BITMASK)!=0)&& addr instanceof InetAddress) {
+					addresses.add(addr);
+				}
+			}
+		}
+		return (InetAddress[]) addresses.toArray(new InetAddress[]{});
+	}
+	
+	
 	public final static String getHostAddress(int n)
 	{
 		if (hasAssignedInterface() == true)
@@ -143,7 +206,9 @@ public class HostInterface
 	{
 		try {
 			InetAddress addr = InetAddress.getByName(host);
-			return (addr instanceof Inet6Address);
+			if (addr instanceof Inet6Address)
+				return true;
+			return false;
 		}
 		catch (Exception e) {}
 		return false;
@@ -153,7 +218,9 @@ public class HostInterface
 	{
 		try {
 			InetAddress addr = InetAddress.getByName(host);
-			return (addr instanceof Inet4Address);
+			if (addr instanceof Inet4Address)
+				return true;
+			return false;
 		}
 		catch (Exception e) {}
 		return false;
diff --git a/router/java/src/org/cybergarage/soap/SOAP.java b/router/java/src/org/cybergarage/soap/SOAP.java
index dec42f7ed8719f07151b8fa6137531482c755564..bc44b35a8205644985eb011a0b8478b4c2224dcb 100644
--- a/router/java/src/org/cybergarage/soap/SOAP.java
+++ b/router/java/src/org/cybergarage/soap/SOAP.java
@@ -15,7 +15,8 @@
 
 package org.cybergarage.soap;
 
-import org.cybergarage.xml.*;
+import org.cybergarage.xml.Node;
+import org.cybergarage.xml.Parser;
 
 public class SOAP
 {
diff --git a/router/java/src/org/cybergarage/soap/SOAPRequest.java b/router/java/src/org/cybergarage/soap/SOAPRequest.java
index d07b12d681bf60f0240c252ae1b9d0702af3971b..f355b0a92e28280684837f452c73320a8ea97a45 100644
--- a/router/java/src/org/cybergarage/soap/SOAPRequest.java
+++ b/router/java/src/org/cybergarage/soap/SOAPRequest.java
@@ -20,11 +20,15 @@
 
 package org.cybergarage.soap;
 
-import java.io.*;
-
-import org.cybergarage.http.*;
-import org.cybergarage.xml.*;
-import org.cybergarage.util.*;
+import java.io.ByteArrayInputStream;
+
+import org.cybergarage.http.HTTP;
+import org.cybergarage.http.HTTPRequest;
+import org.cybergarage.http.HTTPResponse;
+import org.cybergarage.util.Debug;
+import org.cybergarage.xml.Node;
+import org.cybergarage.xml.Parser;
+import org.cybergarage.xml.ParserException;
 
 public class SOAPRequest extends HTTPRequest
 {
@@ -172,12 +176,12 @@ public class SOAPRequest extends HTTPRequest
 	
 	public void print()
 	{
-		System.out.println(toString());
+		Debug.message(toString());
 		if (hasContent() == true)
 			return;
 		Node rootElem = getRootNode();
 		if (rootElem == null)
 			return;
-		System.out.println(rootElem.toString());
+		Debug.message(rootElem.toString());
 	}
 }
diff --git a/router/java/src/org/cybergarage/soap/SOAPResponse.java b/router/java/src/org/cybergarage/soap/SOAPResponse.java
index fb59f3a21eeb76689b1c5a69a114609d51cb6cc8..4326cd2cc87c4e285beca85ad7eac6ade2b1bd95 100644
--- a/router/java/src/org/cybergarage/soap/SOAPResponse.java
+++ b/router/java/src/org/cybergarage/soap/SOAPResponse.java
@@ -5,11 +5,11 @@
 *	Copyright (C) Satoshi Konno 2002
 *
 *	File: SOAPResponse.java
-*
-*	Revision;
-*
-*	12/17/02
-*		- first revision.
+*
+*	Revision;
+*
+*	12/17/02
+*		- first revision.
 *	02/13/04
 *		- Ralf G. R. Bergs <Ralf@Ber.gs>, Inma Marin Lopez <inma@dif.um.es>.
 *		- Added XML header, <?xml version="1.0"?> to setContent().
@@ -17,23 +17,25 @@
 *		- Changed the XML header to <?xml version="1.0" encoding="utf-8"?> in setContent().
 *	
 ******************************************************************/
-
-package org.cybergarage.soap;
-
-import org.cybergarage.http.*;
-import org.cybergarage.xml.*;
-
-public class SOAPResponse extends HTTPResponse
-{
-	////////////////////////////////////////////////
-	//	Constructor
-	////////////////////////////////////////////////
-	
-	public SOAPResponse()
+
+package org.cybergarage.soap;
+
+import org.cybergarage.http.HTTPResponse;
+import org.cybergarage.util.Debug;
+import org.cybergarage.xml.Node;
+import org.cybergarage.xml.XML;
+
+public class SOAPResponse extends HTTPResponse
+{
+	////////////////////////////////////////////////
+	//	Constructor
+	////////////////////////////////////////////////
+	
+	public SOAPResponse()
 	{
 		setRootNode(SOAP.createEnvelopeBodyNode());
 		setContentType(XML.CONTENT_TYPE);
-	}
+	}
 
 	public SOAPResponse(HTTPResponse httpRes)
 	{
@@ -49,25 +51,25 @@ public class SOAPResponse extends HTTPResponse
 		setContentType(XML.CONTENT_TYPE);
 	}
 
-	////////////////////////////////////////////////
-	//	Node
-	////////////////////////////////////////////////
-
-	private Node rootNode;
-	
-	private void setRootNode(Node node)
-	{
+	////////////////////////////////////////////////
+	//	Node
+	////////////////////////////////////////////////
+
+	private Node rootNode;
+	
+	private void setRootNode(Node node)
+	{
 		rootNode = node;
-	}
-	
-	private Node getRootNode()
-	{
-		return rootNode;
-	}
-	
-	////////////////////////////////////////////////
-	//	SOAP Basic
-	////////////////////////////////////////////////
+	}
+	
+	private Node getRootNode()
+	{
+		return rootNode;
+	}
+	
+	////////////////////////////////////////////////
+	//	SOAP Basic
+	////////////////////////////////////////////////
 
 	public void setEnvelopeNode(Node node)
 	{
@@ -78,88 +80,88 @@ public class SOAPResponse extends HTTPResponse
 	{
 		return getRootNode();
 	}
-	
-	public Node getBodyNode()
-	{
-		Node envNode = getEnvelopeNode();
-		if (envNode == null)
-			return null;
-		return envNode.getNodeEndsWith(SOAP.BODY);
-	}
-
-	public Node getMethodResponseNode(String name)
-	{
-		Node bodyNode = getBodyNode();
-		if (bodyNode == null)
-			return null;
-		String methodResName = name + SOAP.RESPONSE;
-		return bodyNode.getNodeEndsWith(methodResName);
-	}
-
-	public Node getFaultNode()
-	{
-		Node bodyNode = getBodyNode();
-		if (bodyNode == null)
-			return null;
-		return bodyNode.getNodeEndsWith(SOAP.FAULT);
-	}
-
-	public Node getFaultCodeNode()
-	{
-		Node faultNode = getFaultNode();
-		if (faultNode == null)
-			return null;
-		return faultNode.getNodeEndsWith(SOAP.FAULT_CODE);
-	}
-
-	public Node getFaultStringNode()
-	{
-		Node faultNode = getFaultNode();
-		if (faultNode == null)
-			return null;
-		return faultNode.getNodeEndsWith(SOAP.FAULT_STRING);
-	}
-
-	public Node getFaultActorNode()
-	{
-		Node faultNode = getFaultNode();
-		if (faultNode == null)
-			return null;
-		return faultNode.getNodeEndsWith(SOAP.FAULTACTOR);
-	}
-
-	public Node getFaultDetailNode()
-	{
-		Node faultNode = getFaultNode();
-		if (faultNode == null)
-			return null;
-		return faultNode.getNodeEndsWith(SOAP.DETAIL);
-	}
-
-	public String getFaultCode()
-	{
-		Node node = getFaultCodeNode();
-		if (node == null)
-			return "";
-		return node.getValue();
-	}
-	
-	public String getFaultString()
-	{
-		Node node = getFaultStringNode();
-		if (node == null)
-			return "";
-		return node.getValue();
-	}
-	
-	public String getFaultActor()
-	{
-		Node node = getFaultActorNode();
-		if (node == null)
-			return "";
-		return node.getValue();
-	}
-
+	
+	public Node getBodyNode()
+	{
+		Node envNode = getEnvelopeNode();
+		if (envNode == null)
+			return null;
+		return envNode.getNodeEndsWith(SOAP.BODY);
+	}
+
+	public Node getMethodResponseNode(String name)
+	{
+		Node bodyNode = getBodyNode();
+		if (bodyNode == null)
+			return null;
+		String methodResName = name + SOAP.RESPONSE;
+		return bodyNode.getNodeEndsWith(methodResName);
+	}
+
+	public Node getFaultNode()
+	{
+		Node bodyNode = getBodyNode();
+		if (bodyNode == null)
+			return null;
+		return bodyNode.getNodeEndsWith(SOAP.FAULT);
+	}
+
+	public Node getFaultCodeNode()
+	{
+		Node faultNode = getFaultNode();
+		if (faultNode == null)
+			return null;
+		return faultNode.getNodeEndsWith(SOAP.FAULT_CODE);
+	}
+
+	public Node getFaultStringNode()
+	{
+		Node faultNode = getFaultNode();
+		if (faultNode == null)
+			return null;
+		return faultNode.getNodeEndsWith(SOAP.FAULT_STRING);
+	}
+
+	public Node getFaultActorNode()
+	{
+		Node faultNode = getFaultNode();
+		if (faultNode == null)
+			return null;
+		return faultNode.getNodeEndsWith(SOAP.FAULTACTOR);
+	}
+
+	public Node getFaultDetailNode()
+	{
+		Node faultNode = getFaultNode();
+		if (faultNode == null)
+			return null;
+		return faultNode.getNodeEndsWith(SOAP.DETAIL);
+	}
+
+	public String getFaultCode()
+	{
+		Node node = getFaultCodeNode();
+		if (node == null)
+			return "";
+		return node.getValue();
+	}
+	
+	public String getFaultString()
+	{
+		Node node = getFaultStringNode();
+		if (node == null)
+			return "";
+		return node.getValue();
+	}
+	
+	public String getFaultActor()
+	{
+		Node node = getFaultActorNode();
+		if (node == null)
+			return "";
+		return node.getValue();
+	}
+
 	////////////////////////////////////////////////
 	//	XML Contents
 	////////////////////////////////////////////////
@@ -180,12 +182,12 @@ public class SOAPResponse extends HTTPResponse
 	
 	public void print()
 	{
-		System.out.println(toString());
+		Debug.message(toString());
 		if (hasContent() == true)
 			return;
 		Node rootElem = getRootNode();
 		if (rootElem == null)
 			return;
-		System.out.println(rootElem.toString());
+		Debug.message(rootElem.toString());
 	}
 }
diff --git a/router/java/src/org/cybergarage/upnp/Action.java b/router/java/src/org/cybergarage/upnp/Action.java
index 9cf98a44d471a803715024ef633894ab4116762d..56b13d9a350039b8981715c52bd4ea6ed791e995 100644
--- a/router/java/src/org/cybergarage/upnp/Action.java
+++ b/router/java/src/org/cybergarage/upnp/Action.java
@@ -5,11 +5,11 @@
 *	Copyright (C) Satoshi Konno 2002-2004
 *
 *	File: Action.java
-*
-*	Revision;
-*
-*	12/05/02
-*		- first revision.
+*
+*	Revision;
+*
+*	12/05/02
+*		- first revision.
 *	08/30/03
 *		- Gordano Sassaroli <sassarol@cefriel.it>
 *		- Problem    : When invoking an action that has at least one out parameter, an error message is returned
@@ -22,63 +22,84 @@
 *	07/09/04
 *		- Thanks for Dimas <cyberrate@users.sourceforge.net> and Stefano Lenzi <kismet-sl@users.sourceforge.net>
 *		- Changed postControlAction() to set the status code to the UPnPStatus.
+*	04/12/06
+*		- Added setUserData() and getUserData() to set a user original data object.
 *
 ******************************************************************/
-
-package org.cybergarage.upnp;
-
-import org.cybergarage.xml.*;
-import org.cybergarage.util.*;
-
-import org.cybergarage.upnp.xml.*;
-import org.cybergarage.upnp.control.*;
-
-public class Action
-{
-	////////////////////////////////////////////////
-	//	Constants
-	////////////////////////////////////////////////
-	
-	public final static String ELEM_NAME = "action";
-
-	////////////////////////////////////////////////
-	//	Member
-	////////////////////////////////////////////////
-
-	private Node serviceNode;
-	private Node actionNode;
-
-	private Node getServiceNode()
-	{
-		return serviceNode;
-	}
-
-	public Service getService()
-	{
-		return new Service(getServiceNode());
-	}
-	
-	public Node getActionNode()
-	{
-		return actionNode;
-	}
-	
-	////////////////////////////////////////////////
-	//	Constructor
-	////////////////////////////////////////////////
-
-	public Action(Node serviceNode, Node actionNode)
-	{
-		this.serviceNode = serviceNode;
-		this.actionNode = actionNode;
-	}
+
+package org.cybergarage.upnp;
+import java.util.Iterator;
+
+import org.cybergarage.upnp.control.ActionListener;
+import org.cybergarage.upnp.control.ActionRequest;
+import org.cybergarage.upnp.control.ActionResponse;
+import org.cybergarage.upnp.control.ControlResponse;
+import org.cybergarage.upnp.xml.ActionData;
+import org.cybergarage.util.Debug;
+import org.cybergarage.util.Mutex;
+import org.cybergarage.xml.Node;
+
+public class Action
+{
+	////////////////////////////////////////////////
+	//	Constants
+	////////////////////////////////////////////////
+	
+	public final static String ELEM_NAME = "action";
+
+	////////////////////////////////////////////////
+	//	Member
+	////////////////////////////////////////////////
+
+	private Node serviceNode;
+	private Node actionNode;
+
+	private Node getServiceNode()
+	{
+		return serviceNode;
+	}
+
+	public Service getService()
+	{
+		return new Service(getServiceNode());
+	}
+	
+	void setService(Service s){
+		serviceNode=s.getServiceNode();
+		/*To ensure integrity of the XML structure*/
+		Iterator i = getArgumentList().iterator();
+		while (i.hasNext()) {
+			Argument arg = (Argument) i.next();
+			arg.setService(s);
+		}		
+	}
+	
+	public Node getActionNode()
+	{
+		return actionNode;
+	}
+	
+	////////////////////////////////////////////////
+	//	Constructor
+	////////////////////////////////////////////////
+	public Action(Node serviceNode){
+		//TODO Test
+		this.serviceNode = serviceNode;
+		this.actionNode = new Node(Action.ELEM_NAME);		
+	}
+
+	public Action(Node serviceNode, Node actionNode)
+	{
+		this.serviceNode = serviceNode;
+		this.actionNode = actionNode;
+	}
 
 	public Action(Action action)
 	{
 		this.serviceNode = action.getServiceNode();
 		this.actionNode = action.getActionNode();
 	}
-
+
 	////////////////////////////////////////////////
 	// Mutex
 	////////////////////////////////////////////////
@@ -95,50 +116,67 @@ public class Action
 		mutex.unlock();
 	}
 	
-	////////////////////////////////////////////////
-	//	isActionNode
-	////////////////////////////////////////////////
-
-	public static boolean isActionNode(Node node)
-	{
-		return Action.ELEM_NAME.equals(node.getName());
-	}
-
-	////////////////////////////////////////////////
-	//	name
-	////////////////////////////////////////////////
-
-	private final static String NAME = "name";
-	
-	public void setName(String value)
-	{
-		getActionNode().setNode(NAME, value);
-	}
-
-	public String getName()
-	{
-		return getActionNode().getNodeValue(NAME);
-	}
-
-	////////////////////////////////////////////////
-	//	argumentList
-	////////////////////////////////////////////////
-
-	public ArgumentList getArgumentList()
-	{
-		ArgumentList argumentList = new ArgumentList();
-		Node argumentListNode = getActionNode().getNode(ArgumentList.ELEM_NAME);
-		if (argumentListNode == null)
-			return argumentList;
-		int nodeCnt = argumentListNode.getNNodes();
-		for (int n=0; n<nodeCnt; n++) {
-			Node node = argumentListNode.getNode(n);
-			if (Argument.isArgumentNode(node) == false)
-				continue;
-			Argument argument = new Argument(getServiceNode(), node);
-			argumentList.add(argument);
-		} 
-		return argumentList;
+	////////////////////////////////////////////////
+	//	isActionNode
+	////////////////////////////////////////////////
+
+	public static boolean isActionNode(Node node)
+	{
+		return Action.ELEM_NAME.equals(node.getName());
+	}
+
+	////////////////////////////////////////////////
+	//	name
+	////////////////////////////////////////////////
+
+	private final static String NAME = "name";
+	
+	public void setName(String value)
+	{
+		getActionNode().setNode(NAME, value);
+	}
+
+	public String getName()
+	{
+		return getActionNode().getNodeValue(NAME);
+	}
+
+	////////////////////////////////////////////////
+	//	argumentList
+	////////////////////////////////////////////////
+
+	public ArgumentList getArgumentList()
+	{
+		ArgumentList argumentList = new ArgumentList();
+		Node argumentListNode = getActionNode().getNode(ArgumentList.ELEM_NAME);
+		if (argumentListNode == null)
+			return argumentList;
+		int nodeCnt = argumentListNode.getNNodes();
+		for (int n=0; n<nodeCnt; n++) {
+			Node node = argumentListNode.getNode(n);
+			if (Argument.isArgumentNode(node) == false)
+				continue;
+			Argument argument = new Argument(getServiceNode(), node);
+			argumentList.add(argument);
+		} 
+		return argumentList;
+	}	
+	
+	public void setArgumentList(ArgumentList al){
+		Node argumentListNode = getActionNode().getNode(ArgumentList.ELEM_NAME);
+		if (argumentListNode == null){
+			argumentListNode = new Node(ArgumentList.ELEM_NAME);
+			getActionNode().addNode(argumentListNode);
+		}else{
+			argumentListNode.removeAllNodes();
+		}
+		Iterator i = al.iterator();
+		while (i.hasNext()) {
+			Argument a = (Argument) i.next();
+			a.setService(getService());
+			argumentListNode.addNode(a.getArgumentNode());
+		}
+		
 	}
 
 	public ArgumentList getInputArgumentList()
@@ -179,15 +217,40 @@ public class Action
 			if (argName == null)
 				continue;
 			if (name.equals(argName) == true)
-				return arg;
+				return arg;
 		}
 		return null;
 	}
 
+	/**
+	 * @deprecated You should use one of the following methods instead:<br />
+	 *  - {@link #setInArgumentValues(ArgumentList)} <br/>
+	 *  - {@link #setOutArgumentValues(ArgumentList)} 
+	 */
 	public void setArgumentValues(ArgumentList argList)
 	{
 		getArgumentList().set(argList);
 	}
+
+	/**
+	 * 
+	 * @param argList
+	 * @since 1.8.0
+	 */
+	public void setInArgumentValues(ArgumentList argList)
+	{
+		getArgumentList().setReqArgs(argList); 
+	}
+	
+	/**
+	 * 
+	 * @param argList
+	 * @since 1.8.0
+	 */
+	public void setOutArgumentValues(ArgumentList argList)
+	{
+		getArgumentList().setResArgs(argList);
+	}
 	
 	public void setArgumentValue(String name, String value)
 	{
@@ -323,7 +386,12 @@ public class Action
 		if (ctrlRes.isSuccessful() == false)
 			return false;
 		ArgumentList outArgList = ctrlRes.getResponse();
-		actionArgList.set(outArgList);
+        try {
+            actionArgList.setResArgs(outArgList);
+        } catch (IllegalArgumentException ex){
+            setStatus(UPnPStatus.INVALID_ARGS,"Action succesfully delivered but invalid arguments returned.");
+            return false;
+        }
 		return true;
 	}
 
@@ -367,4 +435,19 @@ public class Action
 		return upnpStatus;
 	}
 	
+	////////////////////////////////////////////////
+	//	userData
+	////////////////////////////////////////////////
+
+	private Object userData = null; 
+	
+	public void setUserData(Object data) 
+	{
+		userData = data;
+	}
+
+	public Object getUserData() 
+	{
+		return userData;
+	}
 }
diff --git a/router/java/src/org/cybergarage/upnp/ActionList.java b/router/java/src/org/cybergarage/upnp/ActionList.java
index 6a5a688dc8bc52b2bf0aecf294fb0d60cd79f940..f18130c46bb118ebbf4ca85985a129f815014a96 100644
--- a/router/java/src/org/cybergarage/upnp/ActionList.java
+++ b/router/java/src/org/cybergarage/upnp/ActionList.java
@@ -15,15 +15,14 @@
 
 package org.cybergarage.upnp;
 
-import java.util.*;
+import java.util.Vector;
 
 public class ActionList extends Vector 
 {
 	////////////////////////////////////////////////
 	//	Constants
 	////////////////////////////////////////////////
-
-	private static final long serialVersionUID = 1965922721316119846L;
+	
 	public final static String ELEM_NAME = "actionList";
 
 	////////////////////////////////////////////////
diff --git a/router/java/src/org/cybergarage/upnp/AllowedValue.java b/router/java/src/org/cybergarage/upnp/AllowedValue.java
index 24bc45769279285e271cb97cc3a8fd3e2b0ce341..133d8417ea516d327e68853dd4045926a9da4cb5 100644
--- a/router/java/src/org/cybergarage/upnp/AllowedValue.java
+++ b/router/java/src/org/cybergarage/upnp/AllowedValue.java
@@ -5,26 +5,26 @@
 *	Copyright (C) Satoshi Konno 2002-2004
 *
 *	File: AllowedValue.java
-*
-*	Revision:
-*
-*	03/27/04
-*		- first revision.
+*
+*	Revision:
+*
+*	03/27/04
+*		- first revision.
 *	
 ******************************************************************/
-
-package org.cybergarage.upnp;
 
-import org.cybergarage.xml.*;
-
-public class AllowedValue
-{
-	////////////////////////////////////////////////
-	//	Constants
-	////////////////////////////////////////////////
-	
-	public final static String ELEM_NAME = "allowedValue";
-
+package org.cybergarage.upnp;
+
+import org.cybergarage.xml.Node;
+
+public class AllowedValue
+{
+	////////////////////////////////////////////////
+	//	Constants
+	////////////////////////////////////////////////
+	
+	public final static String ELEM_NAME = "allowedValue";
+
 	////////////////////////////////////////////////
 	//	Member
 	////////////////////////////////////////////////
@@ -45,15 +45,30 @@ public class AllowedValue
 		allowedValueNode = node;
 	}
 
-	////////////////////////////////////////////////
-	//	isAllowedValueNode
-	////////////////////////////////////////////////
-
-	public static boolean isAllowedValueNode(Node node)
-	{
-		return ELEM_NAME.equals(node.getName());
-	}
-
+	/**
+	 * Create an AllowedValue by the value String,
+	 * and will create the Node structure by itself
+	 * 
+	 * @param value The value that will be associate to thi object
+	 * 
+	 * @author Stefano "Kismet" Lenzi - kismet-sl@users.sourceforge.net  - 2005
+	 */
+	public AllowedValue(String value) {
+
+		//TODO Some test are done not stable
+		allowedValueNode = new Node(ELEM_NAME); //better (twa)
+		setValue(value);						//better (twa)
+	}
+
+	////////////////////////////////////////////////
+	//	isAllowedValueNode
+	////////////////////////////////////////////////
+
+	public static boolean isAllowedValueNode(Node node)
+	{
+		return ELEM_NAME.equals(node.getName());
+	}
+
 	////////////////////////////////////////////////
 	//	Value
 	////////////////////////////////////////////////
diff --git a/router/java/src/org/cybergarage/upnp/AllowedValueList.java b/router/java/src/org/cybergarage/upnp/AllowedValueList.java
index da10589962ad3e3ae4658f734c02e7c17df9eaa1..a0b0b31316de4c28147b3a74e38d5da72476a259 100644
--- a/router/java/src/org/cybergarage/upnp/AllowedValueList.java
+++ b/router/java/src/org/cybergarage/upnp/AllowedValueList.java
@@ -5,29 +5,29 @@
 *	Copyright (C) Satoshi Konno 2002-2004
 *
 *	File: AllowedValueList.java
-*
-*	Revision:
-*
-*	03/27/04
-*		- first revision.
+*
+*	Revision:
+*
+*	03/27/04
+*		- first revision.
 *	02/28/05
 *		- Changed to use AllowedValue instead of String as the member.
 *	
 ******************************************************************/
-
-package org.cybergarage.upnp;
-
-import java.util.*;
-
-public class AllowedValueList extends Vector
-{
-	////////////////////////////////////////////////
-	//	Constants
-	////////////////////////////////////////////////
-	
-	private static final long serialVersionUID = 5740394642751180992L;
-	public final static String ELEM_NAME = "allowedValueList";
-
+
+package org.cybergarage.upnp;
+
+import java.util.Iterator;
+import java.util.Vector;
+
+public class AllowedValueList extends Vector
+{
+	////////////////////////////////////////////////
+	//	Constants
+	////////////////////////////////////////////////
+	
+	public final static String ELEM_NAME = "allowedValueList";
+
 
 	////////////////////////////////////////////////
 	//	Constructor
@@ -36,6 +36,14 @@ public class AllowedValueList extends Vector
 	public AllowedValueList() 
 	{
 	}
+
+	public AllowedValueList(String[] values) {
+		for (int i = 0; i < values.length; i++) {
+			add(new AllowedValue(values[i]));
+		};
+		
+	}
+
 	
 	////////////////////////////////////////////////
 	//	Methods
@@ -46,4 +54,12 @@ public class AllowedValueList extends Vector
 		return (AllowedValue)get(n);
 	}
 
+	public boolean isAllowed(String v){
+		for (Iterator i = this.iterator(); i.hasNext();) {
+			AllowedValue av = (AllowedValue) i.next();
+			if(av.getValue().equals(v))
+				return true;
+		}
+		return false;
+	}
 }
diff --git a/router/java/src/org/cybergarage/upnp/AllowedValueRange.java b/router/java/src/org/cybergarage/upnp/AllowedValueRange.java
index 3c4ba60c1ea6d04f37a093cd5958d15de01ad10c..679c8e0c635877f18e342996d4fe336c9245581c 100644
--- a/router/java/src/org/cybergarage/upnp/AllowedValueRange.java
+++ b/router/java/src/org/cybergarage/upnp/AllowedValueRange.java
@@ -5,71 +5,86 @@
 *	Copyright (C) Satoshi Konno 2002-2004
 *
 *	File: AllowedValueRange.java
-*
-*	Revision:
-*
-*	03/27/04
-*		- first revision.
+*
+*	Revision:
+*
+*	03/27/04
+*		- first revision.
 *	
 ******************************************************************/
-
-package org.cybergarage.upnp;
-
-import org.cybergarage.xml.*;
-
-public class AllowedValueRange
-{
-	////////////////////////////////////////////////
-	//	Constants
-	////////////////////////////////////////////////
-	
-	public final static String ELEM_NAME = "allowedValueRange";
-
-	////////////////////////////////////////////////
-	//	Member
-	////////////////////////////////////////////////
-
-	private Node allowedValueRangeNode;
-
-	public Node getAllowedValueRangeNode()
-	{
-		return allowedValueRangeNode;
-	}
-	
-	////////////////////////////////////////////////
-	//	Constructor
-	////////////////////////////////////////////////
-
-	public AllowedValueRange(Node node)
-	{
-		allowedValueRangeNode = node;
-	}
-
-	////////////////////////////////////////////////
-	//	isAllowedValueRangeNode
-	////////////////////////////////////////////////
-
-	public static boolean isAllowedValueRangeNode(Node node)
-	{
-		return ELEM_NAME.equals(node.getName());
-	}
-
-	////////////////////////////////////////////////
-	//	minimum
-	////////////////////////////////////////////////
-
-	private final static String MINIMUM = "minimum";
-	
-	public void setMinimum(String value)
-	{
-		getAllowedValueRangeNode().setNode(MINIMUM, value);
-	}
-
-	public String getMinimum()
-	{
-		return getAllowedValueRangeNode().getNodeValue(MINIMUM);
-	}
-
+
+package org.cybergarage.upnp;
+
+import org.cybergarage.xml.Node;
+
+public class AllowedValueRange
+{
+	////////////////////////////////////////////////
+	//	Constants
+	////////////////////////////////////////////////
+	
+	public final static String ELEM_NAME = "allowedValueRange";
+
+	////////////////////////////////////////////////
+	//	Member
+	////////////////////////////////////////////////
+
+	private Node allowedValueRangeNode;
+
+	public Node getAllowedValueRangeNode()
+	{
+		return allowedValueRangeNode;
+	}
+	
+	////////////////////////////////////////////////
+	//	Constructor
+	////////////////////////////////////////////////
+
+	public AllowedValueRange(Node node)
+	{
+		allowedValueRangeNode = node;
+	}
+
+	public AllowedValueRange(){
+		//TODO Test
+		allowedValueRangeNode = new Node(ELEM_NAME);
+	}
+	////////////////////////////////////////////////
+	//	isAllowedValueRangeNode
+	////////////////////////////////////////////////
+	
+	public AllowedValueRange(Number max, Number min, Number step) {
+		//TODO Test
+		allowedValueRangeNode = new Node(ELEM_NAME);
+		if(max!=null)
+			setMaximum(max.toString());
+		if(min!=null)
+			setMinimum(min.toString());
+		if(step!=null)
+			setStep(step.toString());
+	}
+
+	public static boolean isAllowedValueRangeNode(Node node)
+	{
+		return ELEM_NAME.equals(node.getName());
+	}
+
+	////////////////////////////////////////////////
+	//	minimum
+	////////////////////////////////////////////////
+
+	private final static String MINIMUM = "minimum";
+	
+	public void setMinimum(String value)
+	{
+		getAllowedValueRangeNode().setNode(MINIMUM, value);
+	}
+
+	public String getMinimum()
+	{
+		return getAllowedValueRangeNode().getNodeValue(MINIMUM);
+	}
+
 	////////////////////////////////////////////////
 	//	maximum
 	////////////////////////////////////////////////
@@ -86,19 +101,19 @@ public class AllowedValueRange
 		return getAllowedValueRangeNode().getNodeValue(MAXIMUM);
 	}
 
-	////////////////////////////////////////////////
-	//	width
-	////////////////////////////////////////////////
-
-	private final static String STEP = "step";
-	
-	public void setStep(String value)
-	{
-		getAllowedValueRangeNode().setNode(STEP, value);
-	}
-
-	public String getStep()
-	{
-		return getAllowedValueRangeNode().getNodeValue(STEP);
-	}
+	////////////////////////////////////////////////
+	//	width
+	////////////////////////////////////////////////
+
+	private final static String STEP = "step";
+	
+	public void setStep(String value)
+	{
+		getAllowedValueRangeNode().setNode(STEP, value);
+	}
+
+	public String getStep()
+	{
+		return getAllowedValueRangeNode().getNodeValue(STEP);
+	}
 }
diff --git a/router/java/src/org/cybergarage/upnp/Argument.java b/router/java/src/org/cybergarage/upnp/Argument.java
index 6a1907551ab3dc0960dfe67334816a7be7e67077..86e2fba5e80d9377492c5a73cf725bf22c626048 100644
--- a/router/java/src/org/cybergarage/upnp/Argument.java
+++ b/router/java/src/org/cybergarage/upnp/Argument.java
@@ -5,11 +5,11 @@
 *	Copyright (C) Satoshi Konno 2002
 *
 *	File: Argument.java
-*
-*	Revision;
-*
-*	12/05/02
-*		- first revision.
+*
+*	Revision;
+*
+*	12/05/02
+*		- first revision.
 *	03/28/04
 *		- Added getRelatedStateVariable().
 *		- Changed setRelatedStateVariable() to setRelatedStateVariableName().
@@ -17,37 +17,38 @@
 *		- Added getActionNode() and getAction().
 *		- Added getServiceNode() and getService().
 *		- Added the parent service node to the constructor.
+*	04/12/06
+*		- Added setUserData() and getUserData() to set a user original data object.
 *
 ******************************************************************/
-
-package org.cybergarage.upnp;
-
-import org.cybergarage.xml.*;
-
-import org.cybergarage.upnp.xml.*;
-
-public class Argument
-{
-	////////////////////////////////////////////////
-	//	Constants
-	////////////////////////////////////////////////
-	
-	public final static String ELEM_NAME = "argument";
+
+package org.cybergarage.upnp;
+
+import org.cybergarage.upnp.xml.ArgumentData;
+import org.cybergarage.xml.Node;
+
+public class Argument
+{
+	////////////////////////////////////////////////
+	//	Constants
+	////////////////////////////////////////////////
+	
+	public final static String ELEM_NAME = "argument";
 
 	public final static String IN = "in";
 	public final static String OUT = "out";
-
-	////////////////////////////////////////////////
-	//	Member
-	////////////////////////////////////////////////
-
-	private Node argumentNode;
+
+	////////////////////////////////////////////////
+	//	Member
+	////////////////////////////////////////////////
+
+	private Node argumentNode;
 	private Node serviceNode;
-
-	public Node getArgumentNode()
-	{
-		return argumentNode;
-	}
+
+	public Node getArgumentNode()
+	{
+		return argumentNode;
+	}
 
 	private Node getServiceNode()
 	{
@@ -59,6 +60,10 @@ public class Argument
 		return new Service(getServiceNode());
 	}
 	
+	void setService(Service s){
+		s.getServiceNode();
+	}
+	
 	public Node getActionNode()
 	{
 		Node argumentLinstNode = getArgumentNode().getParentNode();
@@ -71,27 +76,32 @@ public class Argument
 			return null;
 		return actionNode;
 	}
-	
+	
 	public Action getAction()
 	{
 		return new Action(getServiceNode(), getActionNode());
 	}
 	
-	////////////////////////////////////////////////
-	//	Constructor
-	////////////////////////////////////////////////
-
+	////////////////////////////////////////////////
+	//	Constructor
+	////////////////////////////////////////////////
+
 	public Argument()
 	{
-		argumentNode = new Node();
+		argumentNode = new Node(ELEM_NAME);
 		serviceNode = null;
 	}
 	
-	public Argument(Node servNode, Node argNode)
+	public Argument(Node servNode){
+		argumentNode = new Node(ELEM_NAME);
+		serviceNode = servNode;		
+	}
+	
+	public Argument(Node servNode, Node argNode)
 	{
-		serviceNode = servNode;
-		argumentNode = argNode;
-	}
+		serviceNode = servNode;
+		argumentNode = argNode;
+	}
 
 	public Argument(String name, String value)
 	{
@@ -100,46 +110,46 @@ public class Argument
 		setValue(value);
 	}
 
-	////////////////////////////////////////////////
-	//	isArgumentNode
-	////////////////////////////////////////////////
-
-	public static boolean isArgumentNode(Node node)
-	{
-		return Argument.ELEM_NAME.equals(node.getName());
-	}
-
-	////////////////////////////////////////////////
-	//	name
-	////////////////////////////////////////////////
-
-	private final static String NAME = "name";
-	
-	public void setName(String value)
-	{
-		getArgumentNode().setNode(NAME, value);
-	}
-
-	public String getName()
-	{
-		return getArgumentNode().getNodeValue(NAME);
-	}
-
-	////////////////////////////////////////////////
-	//	direction
-	////////////////////////////////////////////////
-
-	private final static String DIRECTION = "direction";
-	
-	public void setDirection(String value)
-	{
-		getArgumentNode().setNode(DIRECTION, value);
-	}
-
-	public String getDirection()
-	{
-		return getArgumentNode().getNodeValue(DIRECTION);
-	}
+	////////////////////////////////////////////////
+	//	isArgumentNode
+	////////////////////////////////////////////////
+
+	public static boolean isArgumentNode(Node node)
+	{
+		return Argument.ELEM_NAME.equals(node.getName());
+	}
+
+	////////////////////////////////////////////////
+	//	name
+	////////////////////////////////////////////////
+
+	private final static String NAME = "name";
+	
+	public void setName(String value)
+	{
+		getArgumentNode().setNode(NAME, value);
+	}
+
+	public String getName()
+	{
+		return getArgumentNode().getNodeValue(NAME);
+	}
+
+	////////////////////////////////////////////////
+	//	direction
+	////////////////////////////////////////////////
+
+	private final static String DIRECTION = "direction";
+	
+	public void setDirection(String value)
+	{
+		getArgumentNode().setNode(DIRECTION, value);
+	}
+
+	public String getDirection()
+	{
+		return getArgumentNode().getNodeValue(DIRECTION);
+	}
 
 	public boolean isInDirection()
 	{
@@ -153,22 +163,22 @@ public class Argument
 	{
 		return !isInDirection();
 	}
-	
-	////////////////////////////////////////////////
-	//	relatedStateVariable
-	////////////////////////////////////////////////
-
-	private final static String RELATED_STATE_VARIABLE = "relatedStateVariable";
-	
-	public void setRelatedStateVariableName(String value)
-	{
-		getArgumentNode().setNode(RELATED_STATE_VARIABLE, value);
-	}
-
-	public String getRelatedStateVariableName()
-	{
-		return getArgumentNode().getNodeValue(RELATED_STATE_VARIABLE);
-	}
+	
+	////////////////////////////////////////////////
+	//	relatedStateVariable
+	////////////////////////////////////////////////
+
+	private final static String RELATED_STATE_VARIABLE = "relatedStateVariable";
+	
+	public void setRelatedStateVariableName(String value)
+	{
+		getArgumentNode().setNode(RELATED_STATE_VARIABLE, value);
+	}
+
+	public String getRelatedStateVariableName()
+	{
+		return getArgumentNode().getNodeValue(RELATED_STATE_VARIABLE);
+	}
 
 	public StateVariable getRelatedStateVariable()
 	{
@@ -226,6 +236,18 @@ public class Argument
 	}
 
 	////////////////////////////////////////////////
-	//	Related
+	//	userData
 	////////////////////////////////////////////////
+
+	private Object userData = null; 
+	
+	public void setUserData(Object data) 
+	{
+		userData = data;
+	}
+
+	public Object getUserData() 
+	{
+		return userData;
+	}
 }
diff --git a/router/java/src/org/cybergarage/upnp/ArgumentList.java b/router/java/src/org/cybergarage/upnp/ArgumentList.java
index 95620d281e8cd9697582073e73b33bc953397e49..3f1a5ec44e5149caa20bb6816081111fce190050 100644
--- a/router/java/src/org/cybergarage/upnp/ArgumentList.java
+++ b/router/java/src/org/cybergarage/upnp/ArgumentList.java
@@ -1,77 +1,121 @@
-/******************************************************************
-*
-*	CyberUPnP for Java
-*
-*	Copyright (C) Satoshi Konno 2002
+/******************************************************************
 *
-*	File: ArgumentList.java
-*
-*	Revision:
-*
-*	12/05/02
-*		- first revision.
-*
-******************************************************************/
+*	CyberUPnP for Java
+*
+*	Copyright (C) Satoshi Konno 2002
+*
+*	File: ArgumentList.java
+*
+*	Revision:
+*
+*	12/05/02
+*		- first revision.
+*
+******************************************************************/
+
+package org.cybergarage.upnp;
+
+import java.util.Vector;
+
+public class ArgumentList extends Vector 
+{
+	////////////////////////////////////////////////
+	//	Constants
+	////////////////////////////////////////////////
+	
+	public final static String ELEM_NAME = "argumentList";
+
+	////////////////////////////////////////////////
+	//	Constructor
+	////////////////////////////////////////////////
+	
+	public ArgumentList() 
+	{
+	}
+	
+	////////////////////////////////////////////////
+	//	Methods
+	////////////////////////////////////////////////
+	
+	public Argument getArgument(int n)
+	{
+		return (Argument)get(n);
+	}
+
+	public Argument getArgument(String name)
+	{
+		int nArgs = size();
+		for (int n=0; n<nArgs; n++) {
+			Argument arg = getArgument(n);
+			String argName = arg.getName();
+			if (argName == null)
+				continue;
+			if (argName.equals(name) == true)
+				return arg;
+		}
+		return null;
+	}
+
+	////////////////////////////////////////////////
+	//	Methods
+	////////////////////////////////////////////////
+	/**
+	 * @deprecated
+	 */
+	public void set(ArgumentList inArgList)
+	{
+		int nInArgs = inArgList.size();
+		for (int n=0; n<nInArgs; n++) {
+			Argument inArg = inArgList.getArgument(n);
+			String inArgName = inArg.getName();
+			Argument arg = getArgument(inArgName);
+			if (arg == null)
+				continue;
+			arg.setValue(inArg.getValue());
+		}
+	}
+
+	/**
+	 * Set all the Argument which are Input Argoument to the given value in 
+	 * the argument list
+	 * 
+	 * @param inArgList
+	 */
+	public void setReqArgs(ArgumentList inArgList)
+	{
+        int nArgs = size();
+        for (int n=0; n<nArgs; n++) { 
+            Argument arg = getArgument(n);
+            if (arg.isInDirection()){
+	            String argName = arg.getName();
+	            Argument inArg = inArgList.getArgument(argName);
+	            if (inArg == null)
+	                throw new IllegalArgumentException("Argument \"" + argName + "\" missing.");
+	            arg.setValue(inArg.getValue());
+            }
+        }
+	}
+	/**
+	 * Set all the Argument which are Output Argoument to the given value in 
+	 * the argument list
+	 * 
+	 * @param outArgList
+	 */
+	public void setResArgs(ArgumentList outArgList)
+	{
+        int nArgs = size();
+        for (int n=0; n<nArgs; n++) {
+            Argument arg = getArgument(n);
+            if (arg.isOutDirection()){
+	            String argName = arg.getName();
+	            Argument outArg = outArgList.getArgument(argName);
+	            if (outArg == null)
+	                throw new IllegalArgumentException("Argument \"" + argName + "\" missing.");
+	            arg.setValue(outArg.getValue());
+            }
+        }
+	}
+
+
+}
 
-package org.cybergarage.upnp;
-
-import java.util.*;
-
-public class ArgumentList extends Vector 
-{
-	////////////////////////////////////////////////
-	//	Constants
-	////////////////////////////////////////////////
-	
-	private static final long serialVersionUID = -5412792105767389170L;
-	public final static String ELEM_NAME = "argumentList";
-
-	////////////////////////////////////////////////
-	//	Constructor
-	////////////////////////////////////////////////
-	
-	public ArgumentList() 
-	{
-	}
-	
-	////////////////////////////////////////////////
-	//	Methods
-	////////////////////////////////////////////////
-	
-	public Argument getArgument(int n)
-	{
-		return (Argument)get(n);
-	}
-
-	public Argument getArgument(String name)
-	{
-		int nArgs = size();
-		for (int n=0; n<nArgs; n++) {
-			Argument arg = getArgument(n);
-			String argName = arg.getName();
-			if (argName == null)
-				continue;
-			if (argName.equals(name) == true)
-				return arg;
-		}
-		return null;
-	}
-
-	////////////////////////////////////////////////
-	//	Methods
-	////////////////////////////////////////////////
-	
-	public void set(ArgumentList inArgList)
-	{
-		int nInArgs = inArgList.size();
-		for (int n=0; n<nInArgs; n++) {
-			Argument inArg = inArgList.getArgument(n);
-			String inArgName = inArg.getName();
-			Argument arg = getArgument(inArgName);
-			if (arg == null)
-				continue;
-			arg.setValue(inArg.getValue());
-		}
-	}
-}
-
diff --git a/router/java/src/org/cybergarage/upnp/ControlPoint.java b/router/java/src/org/cybergarage/upnp/ControlPoint.java
index 03cc3726478f1d691e0e5e1863732f4221991925..6172bcf12cbd2f12998f31511f68ab2d37b34175 100644
--- a/router/java/src/org/cybergarage/upnp/ControlPoint.java
+++ b/router/java/src/org/cybergarage/upnp/ControlPoint.java
@@ -5,11 +5,11 @@
 *	Copyright (C) Satoshi Konno 2002-2004
 *
 *	File: ControlPoint.java
-*
-*	Revision:
-*
-*	11/18/02
-*		- first revision.
+*
+*	Revision:
+*
+*	11/18/02
+*		- first revision.
 *	05/13/03
 *		- Changed to create socket threads each local interfaces.
 *		  (HTTP, SSDPNotiry, SSDPSerachResponse)
@@ -54,37 +54,62 @@
 *		  the ControlPoint base class adds/removes a UPnP device
 *	03/30/05
 *		- Changed addDevice() to use Parser::parse(URL).
+*	04/12/06
+*		- Added setUserData() and getUserData() to set a user original data object.
 *
 *******************************************************************/
-
-package org.cybergarage.upnp;
-
-import org.cybergarage.net.*;
-import org.cybergarage.util.*;
-import org.cybergarage.xml.*;
-import org.cybergarage.http.*;
-
-import org.cybergarage.upnp.control.*;
-import org.cybergarage.upnp.ssdp.*;
-import org.cybergarage.upnp.device.*;
-import org.cybergarage.upnp.event.*;
-
-import java.net.*;
-
-public class ControlPoint implements HTTPRequestListener
-{
+
+package org.cybergarage.upnp;
+
+import java.net.InetAddress;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.cybergarage.http.HTTPRequest;
+import org.cybergarage.http.HTTPRequestListener;
+import org.cybergarage.http.HTTPServerList;
+import org.cybergarage.net.HostInterface;
+import org.cybergarage.upnp.control.RenewSubscriber;
+import org.cybergarage.upnp.device.DeviceChangeListener;
+import org.cybergarage.upnp.device.Disposer;
+import org.cybergarage.upnp.device.NotifyListener;
+import org.cybergarage.upnp.device.ST;
+import org.cybergarage.upnp.device.SearchResponseListener;
+import org.cybergarage.upnp.device.USN;
+import org.cybergarage.upnp.event.EventListener;
+import org.cybergarage.upnp.event.NotifyRequest;
+import org.cybergarage.upnp.event.Property;
+import org.cybergarage.upnp.event.PropertyList;
+import org.cybergarage.upnp.event.Subscription;
+import org.cybergarage.upnp.event.SubscriptionRequest;
+import org.cybergarage.upnp.event.SubscriptionResponse;
+import org.cybergarage.upnp.ssdp.SSDP;
+import org.cybergarage.upnp.ssdp.SSDPNotifySocketList;
+import org.cybergarage.upnp.ssdp.SSDPPacket;
+import org.cybergarage.upnp.ssdp.SSDPSearchRequest;
+import org.cybergarage.upnp.ssdp.SSDPSearchResponseSocketList;
+import org.cybergarage.util.Debug;
+import org.cybergarage.util.ListenerList;
+import org.cybergarage.util.Mutex;
+import org.cybergarage.xml.Node;
+import org.cybergarage.xml.NodeList;
+import org.cybergarage.xml.Parser;
+import org.cybergarage.xml.ParserException;
+
+public class ControlPoint implements HTTPRequestListener
+{
 	private final static int DEFAULT_EVENTSUB_PORT = 8058;
 	private final static int DEFAULT_SSDP_PORT = 8008;
 	private final static int DEFAULT_EXPIRED_DEVICE_MONITORING_INTERVAL = 60;
 	
 	private final static String DEFAULT_EVENTSUB_URI = "/evetSub";
 	
-	////////////////////////////////////////////////
-	//	Member
-	////////////////////////////////////////////////
-	
-	private SSDPNotifySocketList ssdpNotifySocketList;
-	private SSDPSearchResponseSocketList ssdpSearchResponseSocketList;
+	////////////////////////////////////////////////
+	//	Member
+	////////////////////////////////////////////////
+	
+	private SSDPNotifySocketList ssdpNotifySocketList;
+	private SSDPSearchResponseSocketList ssdpSearchResponseSocketList;
 
 	private SSDPNotifySocketList getSSDPNotifySocketList()
 	{
@@ -105,14 +130,13 @@ public class ControlPoint implements HTTPRequestListener
 		UPnP.initialize();
 	}
 	
-	////////////////////////////////////////////////
-	//	Constructor
-	////////////////////////////////////////////////
-
-	public ControlPoint(int ssdpPort, int httpPort)
-	{
-		ssdpNotifySocketList = new SSDPNotifySocketList();
-		ssdpSearchResponseSocketList = new SSDPSearchResponseSocketList();
+	////////////////////////////////////////////////
+	//	Constructor
+	////////////////////////////////////////////////
+
+	public ControlPoint(int ssdpPort, int httpPort,InetAddress[] binds){
+		ssdpNotifySocketList = new SSDPNotifySocketList(binds);
+		ssdpSearchResponseSocketList = new SSDPSearchResponseSocketList(binds);
 		
 		setSSDPPort(ssdpPort);
 		setHTTPPort(httpPort);
@@ -124,7 +148,11 @@ public class ControlPoint implements HTTPRequestListener
 				
 		setNMPRMode(false);
 		setRenewSubscriber(null);
-	}
+	}
+	
+	public ControlPoint(int ssdpPort, int httpPort){
+		this(ssdpPort,httpPort,null);
+	}
 
 	public ControlPoint()
 	{
@@ -179,7 +207,7 @@ public class ControlPoint implements HTTPRequestListener
 	public void setHTTPPort(int port) {
 		httpPort = port;
 	}
-	
+	
 	////////////////////////////////////////////////
 	//	NMPR
 	////////////////////////////////////////////////
@@ -196,35 +224,35 @@ public class ControlPoint implements HTTPRequestListener
 		return nmprMode;
 	}
 	
-	////////////////////////////////////////////////
-	//	Device List
-	////////////////////////////////////////////////
-
-	private NodeList devNodeList = new NodeList();
-
-	private void addDevice(Node rootNode)
-	{
-		devNodeList.add(rootNode);
-	}
-
-	private synchronized void addDevice(SSDPPacket ssdpPacket)
+	////////////////////////////////////////////////
+	//	Device List
+	////////////////////////////////////////////////
+
+	private NodeList devNodeList = new NodeList();
+
+	private void addDevice(Node rootNode)
+	{
+		devNodeList.add(rootNode);
+	}
+
+	private synchronized void addDevice(SSDPPacket ssdpPacket)
 	{
 		if (ssdpPacket.isRootDevice() == false)
 			return;
 			
-		String usn = ssdpPacket.getUSN();
-		String udn = USN.getUDN(usn);
-		Device dev = getDevice(udn);
+		String usn = ssdpPacket.getUSN();
+		String udn = USN.getUDN(usn);
+		Device dev = getDevice(udn);
 		if (dev != null) {
 			dev.setSSDPPacket(ssdpPacket);
-			return;
+			return;
 		}
 		
-		String location = ssdpPacket.getLocation();
-		try {	
+		String location = ssdpPacket.getLocation();
+		try {	
 			URL locationUrl = new URL(location);
 			Parser parser = UPnP.getXMLParser();
-			Node rootNode = parser.parse(locationUrl);
+			Node rootNode = parser.parse(locationUrl);
 			Device rootDev = getDevice(rootNode);
 			if (rootDev == null)
 				return;
@@ -237,16 +265,16 @@ public class ControlPoint implements HTTPRequestListener
 			// control point application must implement the DeviceChangeListener interface
 			// to receive the notifications)
 			performAddDeviceListener( rootDev );
-		}
+		}
 		catch (MalformedURLException me) {
 			Debug.warning(ssdpPacket.toString());
 			Debug.warning(me);
 		}
-		catch (ParserException pe) {
+		catch (ParserException pe) {
 			Debug.warning(ssdpPacket.toString());
-			Debug.warning(pe);
-		}
-	}
+			Debug.warning(pe);
+		}
+	}
 
 	private Device getDevice(Node rootNode)
 	{
@@ -257,26 +285,26 @@ public class ControlPoint implements HTTPRequestListener
 				return null;
 		return new Device(rootNode, devNode);
 	}
-
-	public DeviceList getDeviceList()
-	{
-		DeviceList devList = new DeviceList();
-		int nRoots = devNodeList.size();
-		for (int n=0; n<nRoots; n++) {
+
+	public DeviceList getDeviceList()
+	{
+		DeviceList devList = new DeviceList();
+		int nRoots = devNodeList.size();
+		for (int n=0; n<nRoots; n++) {
 			Node rootNode = devNodeList.getNode(n);
 			Device dev = getDevice(rootNode);
-			if (dev == null)
-				continue;
-			devList.add(dev);
-		} 
-		return devList;
-	}
-
-	public Device getDevice(String name)
-	{
-		int nRoots = devNodeList.size();
-		for (int n=0; n<nRoots; n++) {
-			Node rootNode = devNodeList.getNode(n);
+			if (dev == null)
+				continue;
+			devList.add(dev);
+		} 
+		return devList;
+	}
+
+	public Device getDevice(String name)
+	{
+		int nRoots = devNodeList.size();
+		for (int n=0; n<nRoots; n++) {
+			Node rootNode = devNodeList.getNode(n);
 			Device dev = getDevice(rootNode);
 			if (dev == null)
 				continue;
@@ -284,18 +312,18 @@ public class ControlPoint implements HTTPRequestListener
 				return dev;
 			Device cdev = dev.getDevice(name);
 			if (cdev != null)
-				return cdev;
-		} 
-		return null;
-	}
-
-	public boolean hasDevice(String name)
-	{
-		return (getDevice(name) != null) ? true : false;
-	}
-
-	private void removeDevice(Node rootNode)
-	{
+				return cdev;
+		} 
+		return null;
+	}
+
+	public boolean hasDevice(String name)
+	{
+		return (getDevice(name) != null) ? true : false;
+	}
+
+	private void removeDevice(Node rootNode)
+	{
 		// Thanks for Oliver Newell (2004/10/16)
 		// Invoke device removal listener prior to actual removal so Device node 
 		// remains valid for the duration of the listener (application may want
@@ -304,29 +332,29 @@ public class ControlPoint implements HTTPRequestListener
 		if( dev != null && dev.isRootDevice() )
 			performRemoveDeviceListener( dev );
 	    
-		devNodeList.remove(rootNode);
-	}
-
-	private void removeDevice(Device dev)
+		devNodeList.remove(rootNode);
+	}
+
+	protected void removeDevice(Device dev)
 	{
 		if (dev == null)
 			return;
 		removeDevice(dev.getRootNode());
 	}
 	
-	private void removeDevice(String name)
-	{
-		Device dev = getDevice(name);
-		removeDevice(dev);
-	}
-
-	private void removeDevice(SSDPPacket packet)
-	{
-		if (packet.isByeBye() == false)
-			return;
-		String usn = packet.getUSN();
-		String udn = USN.getUDN(usn);
-		removeDevice(udn);
+	protected void removeDevice(String name)
+	{
+		Device dev = getDevice(name);
+		removeDevice(dev);
+	}
+
+	private void removeDevice(SSDPPacket packet)
+	{
+		if (packet.isByeBye() == false)
+			return;
+		String usn = packet.getUSN();
+		String udn = USN.getUDN(usn);
+		removeDevice(udn);
 	}
 	
 	////////////////////////////////////////////////
@@ -353,7 +381,7 @@ public class ControlPoint implements HTTPRequestListener
 	
 	public void setExpiredDeviceMonitoringInterval(long interval)
 	{
-		expiredDeviceMonitoringInterval = interval;
+		expiredDeviceMonitoringInterval = interval;
 	}
 
 	public long getExpiredDeviceMonitoringInterval()
@@ -371,9 +399,9 @@ public class ControlPoint implements HTTPRequestListener
 		return deviceDisposer;
 	}
 	
-	////////////////////////////////////////////////
+	////////////////////////////////////////////////
 	//	Notify
-	////////////////////////////////////////////////
+	////////////////////////////////////////////////
 
 	private ListenerList deviceNotifyListenerList = new ListenerList();
 	 	
@@ -392,10 +420,14 @@ public class ControlPoint implements HTTPRequestListener
 		int listenerSize = deviceNotifyListenerList.size();
 		for (int n=0; n<listenerSize; n++) {
 			NotifyListener listener = (NotifyListener)deviceNotifyListenerList.get(n);
-			listener.deviceNotifyReceived(ssdpPacket);
+			try{
+				listener.deviceNotifyReceived(ssdpPacket);
+			}catch(Exception e){
+				Debug.warning("NotifyListener returned an error:", e);
+			}
 		}
 	}
-
+
 	////////////////////////////////////////////////
 	//	SearchResponse
 	////////////////////////////////////////////////
@@ -417,7 +449,13 @@ public class ControlPoint implements HTTPRequestListener
 		int listenerSize = deviceSearchResponseListenerList.size();
 		for (int n=0; n<listenerSize; n++) {
 			SearchResponseListener listener = (SearchResponseListener)deviceSearchResponseListenerList.get(n);
-			listener.deviceSearchResponseReceived(ssdpPacket);
+			try{
+				listener.deviceSearchResponseReceived(ssdpPacket);
+			}catch(Exception e){
+				Debug.warning("SearchResponseListener returned an error:", e);
+			}
+
+
 		}
 	}
 
@@ -465,10 +503,11 @@ public class ControlPoint implements HTTPRequestListener
 	public void notifyReceived(SSDPPacket packet)
 	{
 		if (packet.isRootDevice() == true) {
-			if (packet.isAlive() == true)
+			if (packet.isAlive() == true){
 				addDevice(packet);
-			if (packet.isByeBye() == true) 
+			}else if (packet.isByeBye() == true){ 
 				removeDevice(packet);
+			}
 		}
 		performNotifyListener(packet);
 	}
@@ -479,12 +518,12 @@ public class ControlPoint implements HTTPRequestListener
 			addDevice(packet);
 		performSearchResponseListener(packet);
 	}
-
-	////////////////////////////////////////////////
-	//	M-SEARCH
-	////////////////////////////////////////////////
 
-	private int searchMx = SSDP.DEFAULT_MSEARCH_MX;
+	////////////////////////////////////////////////
+	//	M-SEARCH
+	////////////////////////////////////////////////
+
+	private int searchMx = SSDP.DEFAULT_MSEARCH_MX;
 
 	public int getSearchMx()
 	{
@@ -711,7 +750,7 @@ public class ControlPoint implements HTTPRequestListener
 		}		
 		return null;
 	}
-	
+	
 	////////////////////////////////////////////////
 	//	getSubscriberService	
 	////////////////////////////////////////////////
@@ -769,11 +808,11 @@ public class ControlPoint implements HTTPRequestListener
 		return renewSubscriber;	
 	}
 	
-	////////////////////////////////////////////////
-	//	run	
-	////////////////////////////////////////////////
-
-	public boolean start(String target, int mx)
+	////////////////////////////////////////////////
+	//	run	
+	////////////////////////////////////////////////
+
+	public boolean start(String target, int mx)
 	{
 		stop();
 		
@@ -803,7 +842,7 @@ public class ControlPoint implements HTTPRequestListener
 			return false;
 		ssdpNotifySocketList.setControlPoint(this);			
 		ssdpNotifySocketList.start();
-		
+		
 		////////////////////////////////////////
 		// SeachResponse Socket
 		////////////////////////////////////////
@@ -845,9 +884,9 @@ public class ControlPoint implements HTTPRequestListener
 			renewSub.start();
 		}
 		
-		return true;
-	}
-	
+		return true;
+	}
+	
 	public boolean start(String target)
 	{
 		return start(target, SSDP.DEFAULT_MSEARCH_MX);
@@ -858,10 +897,10 @@ public class ControlPoint implements HTTPRequestListener
 		return start(ST.ROOT_DEVICE, SSDP.DEFAULT_MSEARCH_MX);
 	}
 	
-	public boolean stop()
+	public boolean stop()
 	{ 
 		unsubscribe();
-		
+		
 		SSDPNotifySocketList ssdpNotifySocketList = getSSDPNotifySocketList();
 		ssdpNotifySocketList.stop();
 		ssdpNotifySocketList.close();
@@ -897,9 +936,25 @@ public class ControlPoint implements HTTPRequestListener
 			setRenewSubscriber(null);
 		}
 		
-		return true;
-	}
+		return true;
+	}
 
+	////////////////////////////////////////////////
+	//	userData
+	////////////////////////////////////////////////
+
+	private Object userData = null; 
+	
+	public void setUserData(Object data) 
+	{
+		userData = data;
+	}
+
+	public Object getUserData() 
+	{
+		return userData;
+	}
+	
 	////////////////////////////////////////////////
 	//	print	
 	////////////////////////////////////////////////
diff --git a/router/java/src/org/cybergarage/upnp/Device.java b/router/java/src/org/cybergarage/upnp/Device.java
index 9dd9c15f30a9418bfb6a030cd61553b977d8d2d3..2457b3609779878266a9f639c95d653e6ce7190b 100644
--- a/router/java/src/org/cybergarage/upnp/Device.java
+++ b/router/java/src/org/cybergarage/upnp/Device.java
@@ -5,11 +5,11 @@
 *	Copyright (C) Satoshi Konno 2002-2004
 *
 *	File: Device.java
-*
-*	Revision:
-*
-*	11/28/02
-*		- first revision.
+*
+*	Revision:
+*
+*	11/28/02
+*		- first revision.
 *	02/26/03
 *		- URLBase is updated automatically.
 * 		- Description of a root device is returned from the XML node tree.
@@ -82,64 +82,104 @@
 * 	04/25/05
 *		- Thanks for Mikael Hakman <mhakman@dkab.net>
 *		- Added a new setActionListener() and serQueryListner() to include the sub devices. 
+*	07/24/05
+*		- Thanks for Stefano Lenzi <kismet-sl@users.sourceforge.net>
+*		- Fixed a bug of getParentDevice() to return the parent device normally.
+*	02/21/06
+*		- Changed httpRequestRecieved() not to ignore HEAD requests.
+*	04/12/06
+*		- Added setUserData() and getUserData() to set a user original data object.
+*	03/29/08
+*		- Added isRunning() to know whether the device is running.
 * 
 ******************************************************************/
-
-package org.cybergarage.upnp;
-
-import java.net.*;
-import java.io.*;
-import java.util.*;
-
-import org.cybergarage.net.*;
-import org.cybergarage.http.*;
-import org.cybergarage.util.*;
-import org.cybergarage.xml.*;
-import org.cybergarage.soap.*;
-
-import org.cybergarage.upnp.ssdp.*;
-import org.cybergarage.upnp.device.*;
-import org.cybergarage.upnp.control.*;
-import org.cybergarage.upnp.event.*;
-import org.cybergarage.upnp.xml.*;
-
-public class Device implements org.cybergarage.http.HTTPRequestListener, SearchListener
-{
-	////////////////////////////////////////////////
-	//	Constants
-	////////////////////////////////////////////////
-	
-	public final static String ELEM_NAME = "device";
+
+package org.cybergarage.upnp;
+
+import java.io.File;
+import java.io.InputStream;
+import java.net.InetAddress;
+import java.net.URL;
+import java.util.Calendar;
+
+import org.cybergarage.http.HTTP;
+import org.cybergarage.http.HTTPRequest;
+import org.cybergarage.http.HTTPResponse;
+import org.cybergarage.http.HTTPServerList;
+import org.cybergarage.http.HTTPStatus;
+import org.cybergarage.net.HostInterface;
+import org.cybergarage.soap.SOAPResponse;
+import org.cybergarage.upnp.control.ActionListener;
+import org.cybergarage.upnp.control.ActionRequest;
+import org.cybergarage.upnp.control.ActionResponse;
+import org.cybergarage.upnp.control.ControlRequest;
+import org.cybergarage.upnp.control.ControlResponse;
+import org.cybergarage.upnp.control.QueryListener;
+import org.cybergarage.upnp.control.QueryRequest;
+import org.cybergarage.upnp.device.Advertiser;
+import org.cybergarage.upnp.device.Description;
+import org.cybergarage.upnp.device.InvalidDescriptionException;
+import org.cybergarage.upnp.device.NTS;
+import org.cybergarage.upnp.device.ST;
+import org.cybergarage.upnp.device.SearchListener;
+import org.cybergarage.upnp.device.USN;
+import org.cybergarage.upnp.event.Subscriber;
+import org.cybergarage.upnp.event.Subscription;
+import org.cybergarage.upnp.event.SubscriptionRequest;
+import org.cybergarage.upnp.event.SubscriptionResponse;
+import org.cybergarage.upnp.ssdp.SSDPNotifyRequest;
+import org.cybergarage.upnp.ssdp.SSDPNotifySocket;
+import org.cybergarage.upnp.ssdp.SSDPPacket;
+import org.cybergarage.upnp.ssdp.SSDPSearchResponse;
+import org.cybergarage.upnp.ssdp.SSDPSearchResponseSocket;
+import org.cybergarage.upnp.ssdp.SSDPSearchSocketList;
+import org.cybergarage.upnp.xml.DeviceData;
+import org.cybergarage.util.Debug;
+import org.cybergarage.util.FileUtil;
+import org.cybergarage.util.Mutex;
+import org.cybergarage.util.TimerUtil;
+import org.cybergarage.xml.Node;
+import org.cybergarage.xml.Parser;
+import org.cybergarage.xml.ParserException;
+import org.cybergarage.xml.XML;
+
+public class Device implements org.cybergarage.http.HTTPRequestListener, SearchListener
+{
+	////////////////////////////////////////////////
+	//	Constants
+	////////////////////////////////////////////////
+	
+	public final static String ELEM_NAME = "device";
 	public final static String UPNP_ROOTDEVICE = "upnp:rootdevice";
-
+
 	public final static int DEFAULT_STARTUP_WAIT_TIME = 1000;
 	public final static int DEFAULT_DISCOVERY_WAIT_TIME = 300;
-	public final static int DEFAULT_LEASE_TIME = 30 * 60;
+	public final static int DEFAULT_LEASE_TIME = 30 * 60;
 
 	public final static int HTTP_DEFAULT_PORT = 4004;
 
 	public final static String DEFAULT_DESCRIPTION_URI = "/description.xml";
 	
-	////////////////////////////////////////////////
-	//	Member
-	////////////////////////////////////////////////
-
-	private Node rootNode;
-	private Node deviceNode;
-
-	public Node getRootNode()
-	{
+	////////////////////////////////////////////////
+	//	Member
+	////////////////////////////////////////////////
+
+	private Node rootNode;
+	private Node deviceNode;
+
+	public Node getRootNode()
+	{
 		if (rootNode != null)
 			return rootNode;
 		if (deviceNode == null)
 			return null;
-		return deviceNode.getRootNode();
-	}
-
-	public Node getDeviceNode()
-	{
-		return deviceNode;
-	}
+		return deviceNode.getRootNode();
+	}
+
+	public Node getDeviceNode()
+	{
+		return deviceNode;
+	}
 
 	public void setRootNode(Node node)
 	{
@@ -150,7 +190,7 @@ public class Device implements org.cybergarage.http.HTTPRequestListener, SearchL
 	{
 		deviceNode = node;
 	}
-				
+				
 	////////////////////////////////////////////////
 	//	Initialize
 	////////////////////////////////////////////////
@@ -160,34 +200,44 @@ public class Device implements org.cybergarage.http.HTTPRequestListener, SearchL
 		UPnP.initialize();
 	}
 	
-	////////////////////////////////////////////////
-	//	Constructor
-	////////////////////////////////////////////////
-
-	public Device(Node root, Node device)
-	{
-		rootNode = root;
+	////////////////////////////////////////////////
+	//	Constructor
+	////////////////////////////////////////////////
+
+	public Device(Node root, Node device)
+	{
+		rootNode = root;
 		deviceNode = device;
 		setUUID(UPnP.createUUID());
 		setWirelessMode(false);
-	}
-
-	public Device()
-	{
-		this(null, null);
-	}
-	
-	public Device(Node device)
-	{
-		this(null, device);
-	}
-
-	public Device(File descriptionFile) throws InvalidDescriptionException
-	{
-		this(null, null);
-		loadDescription(descriptionFile);
-	}
+	}
 
+	public Device()
+	{
+		this(null, null);
+	}
+	
+	public Device(Node device)
+	{
+		this(null, device);
+	}
+
+	public Device(File descriptionFile) throws InvalidDescriptionException
+	{
+		this(null, null);
+		loadDescription(descriptionFile);
+	}
+
+	/**
+	 * @since 1.8.0
+	 */
+	public Device(InputStream input) throws InvalidDescriptionException
+	{
+		this(null, null);
+		loadDescription(input);
+	}
+
+	
 	public Device(String descriptionFileName) throws InvalidDescriptionException
 	{
 		this(new File(descriptionFileName));
@@ -209,6 +259,47 @@ public class Device implements org.cybergarage.http.HTTPRequestListener, SearchL
 		mutex.unlock();
 	}
 	
+	////////////////////////////////////////////////
+	//	getAbsoluteURL
+	////////////////////////////////////////////////
+	
+	public String getAbsoluteURL(String urlString)
+	{
+		try {
+			URL url = new URL(urlString);
+			return url.toString();
+		}
+		catch (Exception e) {}
+		
+		Device rootDev = getRootDevice();
+		String urlBaseStr = rootDev.getURLBase();
+		
+		// Thanks for Steven Yen (2003/09/03)
+		if (urlBaseStr == null || urlBaseStr.length() <= 0) {
+			String location = rootDev.getLocation();
+			String locationHost = HTTP.getHost(location);
+			int locationPort = HTTP.getPort(location);
+			urlBaseStr = HTTP.getRequestHostURL(locationHost, locationPort);
+		}
+
+		urlString = HTTP.toRelativeURL(urlString);
+		String absUrl = urlBaseStr + urlString;
+		try {
+			URL url = new URL(absUrl);
+			return url.toString();
+		}
+		catch (Exception e) {}
+			
+		absUrl = HTTP.getAbsoluteURL(urlBaseStr, urlString);
+		try {
+			URL url = new URL(absUrl);
+			return url.toString();
+		}
+		catch (Exception e) {}
+		
+		return "";
+	}
+
 	////////////////////////////////////////////////
 	//	NMPR
 	////////////////////////////////////////////////
@@ -300,15 +391,71 @@ public class Device implements org.cybergarage.http.HTTPRequestListener, SearchL
 	
 	// Thanks for Stefano Lenzi (07/24/04)
 
-	public Device getParentDevice()
-	{ 
+	/**
+	 * 
+	 * @return A Device that contain this object.<br>
+	 * 	Return <code>null</code> if this is a root device.
+	 * @author Stefano "Kismet" Lenzi 
+	 */
+	public Device getParentDevice()	{ 
 		if(isRootDevice())
 			return null;
 		Node devNode = getDeviceNode();
+		Node aux = null;
 		//<device><deviceList><device>
-		devNode = devNode.getParentNode().getParentNode().getNode(Device.ELEM_NAME);
-		return new Device(devNode);
-	}
+		aux = devNode.getParentNode().getParentNode();
+		return new Device(aux);
+	}
+	/**
+	 * Add a Service to device without checking for duplicate or syntax error
+	 * 
+	 * @param s Add Service s to the Device
+	 */
+	public void addService(Service s) {
+		Node serviceListNode = getDeviceNode().getNode(ServiceList.ELEM_NAME);
+		if (serviceListNode == null) {
+			serviceListNode = new Node(ServiceList.ELEM_NAME);
+			getDeviceNode().addNode(serviceListNode);
+		}
+		serviceListNode.addNode(s.getServiceNode());
+	}
+
+	/**
+	 * Add a Device to device without checking for duplicate or syntax error.
+	 * This method set or reset the root node of the Device and itself<br>
+	 * <br>
+	 * Note: This method should be used to create a dynamic<br>
+	 * Device withtout writing any XML that describe the device<br>.
+	 * 
+	 * @param d Add Device d to the Device
+	 * 
+	 * @author Stefano "Kismet" Lenzi - kismet-sl@users.sourceforge.net  - 2005
+	 * 
+	 */
+	public void addDevice(Device d) {
+		Node deviceListNode = getDeviceNode().getNode(DeviceList.ELEM_NAME);
+		if (deviceListNode == null) {
+			//deviceListNode = new Node(ServiceList.ELEM_NAME); twa wrong ELEM_NAME;
+			deviceListNode = new Node(DeviceList.ELEM_NAME);
+			getDeviceNode().addNode(deviceListNode);
+		}
+		deviceListNode.addNode(d.getDeviceNode());
+		d.setRootNode(null);
+		if(getRootNode()==null){
+			Node root = new Node(RootDescription.ROOT_ELEMENT);
+			root.setNameSpace("",RootDescription.ROOT_ELEMENT_NAMESPACE);
+			Node spec = new Node(RootDescription.SPECVERSION_ELEMENT);
+			Node maj =new Node(RootDescription.MAJOR_ELEMENT);
+			maj.setValue("1");
+			Node min =new Node(RootDescription.MINOR_ELEMENT);
+			min.setValue("0");
+			spec.addNode(maj);
+			spec.addNode(min);
+			root.addNode(spec);		
+			setRootNode(root);
+		}			
+	}	
+	
 
 	////////////////////////////////////////////////
 	//	UserData
@@ -326,46 +473,72 @@ public class Device implements org.cybergarage.http.HTTPRequestListener, SearchL
 		return userData;
 	}
 	
-	////////////////////////////////////////////////
-	//	Description
-	////////////////////////////////////////////////
+	////////////////////////////////////////////////
+	//	Description
+	////////////////////////////////////////////////
 
 	private void setDescriptionFile(File file)
 	{
 		getDeviceData().setDescriptionFile(file);
 	}
-
-	public File getDescriptionFile()
-	{
-		return getDeviceData().getDescriptionFile();
-	}
-
-	private void setDescriptionURI(String uri)
-	{
-		getDeviceData().setDescriptionURI(uri);
-	}
-
-	private String getDescriptionURI()
-	{
-		return getDeviceData().getDescriptionURI();
-	}
-
-	private boolean isDescriptionURI(String uri)
-	{
-		String descriptionURI = getDescriptionURI();
-		if (uri == null || descriptionURI == null)
-			return false;
-		return descriptionURI.equals(uri);
-	}
-
-	public String getDescriptionFilePath()
-	{
-		File descriptionFile = getDescriptionFile();
-		if (descriptionFile == null)
-			return "";
-		return descriptionFile.getAbsoluteFile().getParent();
-	}
-
+
+	public File getDescriptionFile()
+	{
+		return getDeviceData().getDescriptionFile();
+	}
+
+	private void setDescriptionURI(String uri)
+	{
+		getDeviceData().setDescriptionURI(uri);
+	}
+
+	private String getDescriptionURI()
+	{
+		return getDeviceData().getDescriptionURI();
+	}
+
+	private boolean isDescriptionURI(String uri)
+	{
+		String descriptionURI = getDescriptionURI();
+		if (uri == null || descriptionURI == null)
+			return false;
+		return descriptionURI.equals(uri);
+	}
+
+	public String getDescriptionFilePath()
+	{
+		File descriptionFile = getDescriptionFile();
+		if (descriptionFile == null)
+			return "";
+		return descriptionFile.getAbsoluteFile().getParent();
+	}
+	
+	/**
+	 * @since 1.8.0
+	 */
+	public boolean loadDescription(InputStream input) throws InvalidDescriptionException
+	{
+		try {
+			Parser parser = UPnP.getXMLParser();
+			rootNode = parser.parse(input);
+			if (rootNode == null)
+				throw new InvalidDescriptionException(Description.NOROOT_EXCEPTION);
+			deviceNode = rootNode.getNode(Device.ELEM_NAME);
+			if (deviceNode == null)
+				throw new InvalidDescriptionException(Description.NOROOTDEVICE_EXCEPTION);
+		}
+		catch (ParserException e) {
+			throw new InvalidDescriptionException(e);
+		}
+		
+		if (initializeLoadedDescription() == false)
+			return false;
+
+		setDescriptionFile(null);
+				
+		return true;
+	}	
+
 	public boolean loadDescription(String descString) throws InvalidDescriptionException
 	{
 		try {
@@ -389,28 +562,28 @@ public class Device implements org.cybergarage.http.HTTPRequestListener, SearchL
 		return true;
 	}
 	
-	public boolean loadDescription(File file) throws InvalidDescriptionException
-	{
-		try {
+	public boolean loadDescription(File file) throws InvalidDescriptionException
+	{
+		try {
 			Parser parser = UPnP.getXMLParser();
-			rootNode = parser.parse(file);
-			if (rootNode == null)
-				throw new InvalidDescriptionException(Description.NOROOT_EXCEPTION, file);
-			deviceNode = rootNode.getNode(Device.ELEM_NAME);
-			if (deviceNode == null)
-				throw new InvalidDescriptionException(Description.NOROOTDEVICE_EXCEPTION, file);
-		}
-		catch (ParserException e) {
-			throw new InvalidDescriptionException(e);
-		}
+			rootNode = parser.parse(file);
+			if (rootNode == null)
+				throw new InvalidDescriptionException(Description.NOROOT_EXCEPTION, file);
+			deviceNode = rootNode.getNode(Device.ELEM_NAME);
+			if (deviceNode == null)
+				throw new InvalidDescriptionException(Description.NOROOTDEVICE_EXCEPTION, file);
+		}
+		catch (ParserException e) {
+			throw new InvalidDescriptionException(e);
+		}
 		
 		if (initializeLoadedDescription() == false)
-			return false;
+			return false;
 
 		setDescriptionFile(file);
-				
-		return true;
-	}
+				
+		return true;
+	}
 
 	private boolean initializeLoadedDescription()
 	{
@@ -425,95 +598,94 @@ public class Device implements org.cybergarage.http.HTTPRequestListener, SearchL
 		return true;
 	}
 	
-	////////////////////////////////////////////////
-	//	isDeviceNode
-	////////////////////////////////////////////////
-
-	public static boolean isDeviceNode(Node node)
-	{
-		return Device.ELEM_NAME.equals(node.getName());
-	}
-	
-	////////////////////////////////////////////////
-	//	Root Device
-	////////////////////////////////////////////////
+	////////////////////////////////////////////////
+	//	isDeviceNode
+	////////////////////////////////////////////////
+
+	public static boolean isDeviceNode(Node node)
+	{
+		return Device.ELEM_NAME.equals(node.getName());
+	}
+	
+	////////////////////////////////////////////////
+	//	Root Device
+	////////////////////////////////////////////////
 
-	public boolean isRootDevice()
-	{
-		return (getRootNode() != null) ? true : false;
+	public boolean isRootDevice(){
+		return getRootNode().getNode("device").getNodeValue(UDN).equals(getUDN());
 	}
 	
-	////////////////////////////////////////////////
-	//	Root Device
-	////////////////////////////////////////////////
+	////////////////////////////////////////////////
+	//	Root Device
+	////////////////////////////////////////////////
 
 	public void setSSDPPacket(SSDPPacket packet)
 	{
 		getDeviceData().setSSDPPacket(packet);
 	}
 
-	public SSDPPacket getSSDPPacket()
-	{
-		if (isRootDevice() == false)
+	public SSDPPacket getSSDPPacket()
+	{
+		if (isRootDevice() == false)
 			return null;
 		return getDeviceData().getSSDPPacket();
-	}
-	
-	////////////////////////////////////////////////
-	//	Location 
-	////////////////////////////////////////////////
-
-	public void setLocation(String value)
-	{
-		getDeviceData().setLocation(value);
-	}
-
-	public String getLocation()
-	{
-		SSDPPacket packet = getSSDPPacket();
-		if (packet != null)
-			return packet.getLocation();
-		return getDeviceData().getLocation();
-	}
-
-	////////////////////////////////////////////////
-	//	LeaseTime 
-	////////////////////////////////////////////////
-
-	public void setLeaseTime(int value)
-	{
+	}
+	
+	////////////////////////////////////////////////
+	//	Location 
+	////////////////////////////////////////////////
+
+	public void setLocation(String value)
+	{
+		getDeviceData().setLocation(value);
+	}
+
+	public String getLocation()
+	{
+		SSDPPacket packet = getSSDPPacket();
+		if (packet != null)
+			return packet.getLocation();
+		return getDeviceData().getLocation();
+	}
+
+	////////////////////////////////////////////////
+	//	LeaseTime 
+	////////////////////////////////////////////////
+
+	public void setLeaseTime(int value)
+	{
 		getDeviceData().setLeaseTime(value);
 		Advertiser adv = getAdvertiser();
 		if (adv != null) {
 			announce();
 			adv.restart();
 		}
-	}
-
-	public int getLeaseTime()
-	{
-		SSDPPacket packet = getSSDPPacket();
-		if (packet != null)
-			return packet.getLeaseTime();	
-		return getDeviceData().getLeaseTime();
-	}
-
-	////////////////////////////////////////////////
-	//	TimeStamp 
-	////////////////////////////////////////////////
-
-	public long getTimeStamp()
-	{
-		SSDPPacket packet = getSSDPPacket();
-		if (packet != null)
-			return packet.getTimeStamp();		
-		return 0;
-	}
-
-	public long getElapsedTime()
-	{
-		return (System.currentTimeMillis() - getTimeStamp()) / 1000;
-	}
+	}
+
+	public int getLeaseTime()
+	{
+		SSDPPacket packet = getSSDPPacket();
+		if (packet != null)
+			return packet.getLeaseTime();	
+		return getDeviceData().getLeaseTime();
+	}
+
+	////////////////////////////////////////////////
+	//	TimeStamp 
+	////////////////////////////////////////////////
+
+	public long getTimeStamp()
+	{
+		SSDPPacket packet = getSSDPPacket();
+		if (packet != null)
+			return packet.getTimeStamp();		
+		return 0;
+	}
+
+	public long getElapsedTime()
+	{
+		return (System.currentTimeMillis() - getTimeStamp()) / 1000;
+	}
 
 	public boolean isExpired()
 	{
@@ -523,15 +695,15 @@ public class Device implements org.cybergarage.http.HTTPRequestListener, SearchL
 			return true;
 		return false;
 	}
-	
-	////////////////////////////////////////////////
-	//	URL Base
-	////////////////////////////////////////////////
-
-	private final static String URLBASE_NAME = "URLBase";
-	
-	private void setURLBase(String value)
-	{
+	
+	////////////////////////////////////////////////
+	//	URL Base
+	////////////////////////////////////////////////
+
+	private final static String URLBASE_NAME = "URLBase";
+	
+	private void setURLBase(String value)
+	{
 		if (isRootDevice() == true) {
 			Node node = getRootNode().getNode(URLBASE_NAME);
 			if (node != null) {
@@ -544,37 +716,37 @@ public class Device implements org.cybergarage.http.HTTPRequestListener, SearchL
 			if (getRootNode().hasNodes() == false)
 				index = 1;
 			getRootNode().insertNode(node, index);
-		}
-	}
-
+		}
+	}
+
 	private void updateURLBase(String host)
 	{
 		String urlBase = HostInterface.getHostURL(host, getHTTPPort(), "");
 		setURLBase(urlBase);
 	}
   
-	public String getURLBase()
-	{
-		if (isRootDevice() == true)
-			return getRootNode().getNodeValue(URLBASE_NAME);
-		return "";
-	}
-
-	////////////////////////////////////////////////
-	//	deviceType
-	////////////////////////////////////////////////
-
-	private final static String DEVICE_TYPE = "deviceType";
-	
-	public void setDeviceType(String value)
-	{
-		getDeviceNode().setNode(DEVICE_TYPE, value);
-	}
-
-	public String getDeviceType()
-	{
-		return getDeviceNode().getNodeValue(DEVICE_TYPE);
-	}
+	public String getURLBase()
+	{
+		if (isRootDevice() == true)
+			return getRootNode().getNodeValue(URLBASE_NAME);
+		return "";
+	}
+
+	////////////////////////////////////////////////
+	//	deviceType
+	////////////////////////////////////////////////
+
+	private final static String DEVICE_TYPE = "deviceType";
+	
+	public void setDeviceType(String value)
+	{
+		getDeviceNode().setNode(DEVICE_TYPE, value);
+	}
+
+	public String getDeviceType()
+	{
+		return getDeviceNode().getNodeValue(DEVICE_TYPE);
+	}
 
 	public boolean isDeviceType(String value)
 	{
@@ -582,211 +754,211 @@ public class Device implements org.cybergarage.http.HTTPRequestListener, SearchL
 			return false;
 		return value.equals(getDeviceType());
 	}
-
-	////////////////////////////////////////////////
-	//	friendlyName
-	////////////////////////////////////////////////
-
-	private final static String FRIENDLY_NAME = "friendlyName";
-	
-	public void setFriendlyName(String value)
-	{
-		getDeviceNode().setNode(FRIENDLY_NAME, value);
-	}
-
-	public String getFriendlyName()
-	{
-		return getDeviceNode().getNodeValue(FRIENDLY_NAME);
-	}
-
-	////////////////////////////////////////////////
-	//	manufacture
-	////////////////////////////////////////////////
-
-	private final static String MANUFACTURE = "manufacture";
-	
-	public void setManufacture(String value)
-	{
-		getDeviceNode().setNode(MANUFACTURE, value);
-	}
-
-	public String getManufacture()
-	{
-		return getDeviceNode().getNodeValue(MANUFACTURE);
-	}
-
-	////////////////////////////////////////////////
-	//	manufactureURL
-	////////////////////////////////////////////////
-
-	private final static String MANUFACTURE_URL = "manufactureURL";
-	
-	public void setManufactureURL(String value)
-	{
-		getDeviceNode().setNode(MANUFACTURE_URL, value);
-	}
-
-	public String getManufactureURL()
-	{
-		return getDeviceNode().getNodeValue(MANUFACTURE_URL);
-	}
-
-	////////////////////////////////////////////////
-	//	modelDescription
-	////////////////////////////////////////////////
-
-	private final static String MODEL_DESCRIPTION = "modelDescription";
-	
-	public void setModelDescription(String value)
-	{
-		getDeviceNode().setNode(MODEL_DESCRIPTION, value);
-	}
-
-	public String getModelDescription()
-	{
-		return getDeviceNode().getNodeValue(MODEL_DESCRIPTION);
-	}
-
-	////////////////////////////////////////////////
-	//	modelName
-	////////////////////////////////////////////////
-
-	private final static String MODEL_NAME = "modelName";
-	
-	public void setModelName(String value)
-	{
-		getDeviceNode().setNode(MODEL_NAME, value);
-	}
-
-	public String getModelName()
-	{
-		return getDeviceNode().getNodeValue(MODEL_NAME);
-	}
-
-	////////////////////////////////////////////////
-	//	modelNumber
-	////////////////////////////////////////////////
-
-	private final static String MODEL_NUMBER = "modelNumber";
-	
-	public void setModelNumber(String value)
-	{
-		getDeviceNode().setNode(MODEL_NUMBER, value);
-	}
-
-	public String getModelNumber()
-	{
-		return getDeviceNode().getNodeValue(MODEL_NUMBER);
-	}
-
-	////////////////////////////////////////////////
-	//	modelURL
-	////////////////////////////////////////////////
-
-	private final static String MODEL_URL = "modelURL";
-	
-	public void setModelURL(String value)
-	{
-		getDeviceNode().setNode(MODEL_URL, value);
-	}
-
-	public String getModelURL()
-	{
-		return getDeviceNode().getNodeValue(MODEL_URL);
-	}
-
-	////////////////////////////////////////////////
-	//	serialNumber
-	////////////////////////////////////////////////
-
-	private final static String SERIAL_NUMBER = "serialNumber";
-	
-	public void setSerialNumber(String value)
-	{
-		getDeviceNode().setNode(SERIAL_NUMBER, value);
-	}
-
-	public String getSerialNumber()
-	{
-		return getDeviceNode().getNodeValue(SERIAL_NUMBER);
-	}
-
-	////////////////////////////////////////////////
-	//	UDN
-	////////////////////////////////////////////////
-
-	private final static String UDN = "UDN";
-	
-	public void setUDN(String value)
-	{
-		getDeviceNode().setNode(UDN, value);
-	}
-
-	public String getUDN()
-	{
-		return getDeviceNode().getNodeValue(UDN);
-	}
+
+	////////////////////////////////////////////////
+	//	friendlyName
+	////////////////////////////////////////////////
+
+	private final static String FRIENDLY_NAME = "friendlyName";
+	
+	public void setFriendlyName(String value)
+	{
+		getDeviceNode().setNode(FRIENDLY_NAME, value);
+	}
+
+	public String getFriendlyName()
+	{
+		return getDeviceNode().getNodeValue(FRIENDLY_NAME);
+	}
+
+	////////////////////////////////////////////////
+	//	manufacture
+	////////////////////////////////////////////////
+
+	private final static String MANUFACTURE = "manufacturer";
+	
+	public void setManufacture(String value)
+	{
+		getDeviceNode().setNode(MANUFACTURE, value);
+	}
+
+	public String getManufacture()
+	{
+		return getDeviceNode().getNodeValue(MANUFACTURE);
+	}
+
+	////////////////////////////////////////////////
+	//	manufactureURL
+	////////////////////////////////////////////////
+
+	private final static String MANUFACTURE_URL = "manufacturerURL";
+	
+	public void setManufactureURL(String value)
+	{
+		getDeviceNode().setNode(MANUFACTURE_URL, value);
+	}
+
+	public String getManufactureURL()
+	{
+		return getDeviceNode().getNodeValue(MANUFACTURE_URL);
+	}
+
+	////////////////////////////////////////////////
+	//	modelDescription
+	////////////////////////////////////////////////
+
+	private final static String MODEL_DESCRIPTION = "modelDescription";
+	
+	public void setModelDescription(String value)
+	{
+		getDeviceNode().setNode(MODEL_DESCRIPTION, value);
+	}
+
+	public String getModelDescription()
+	{
+		return getDeviceNode().getNodeValue(MODEL_DESCRIPTION);
+	}
+
+	////////////////////////////////////////////////
+	//	modelName
+	////////////////////////////////////////////////
+
+	private final static String MODEL_NAME = "modelName";
+	
+	public void setModelName(String value)
+	{
+		getDeviceNode().setNode(MODEL_NAME, value);
+	}
+
+	public String getModelName()
+	{
+		return getDeviceNode().getNodeValue(MODEL_NAME);
+	}
+
+	////////////////////////////////////////////////
+	//	modelNumber
+	////////////////////////////////////////////////
+
+	private final static String MODEL_NUMBER = "modelNumber";
+	
+	public void setModelNumber(String value)
+	{
+		getDeviceNode().setNode(MODEL_NUMBER, value);
+	}
+
+	public String getModelNumber()
+	{
+		return getDeviceNode().getNodeValue(MODEL_NUMBER);
+	}
+
+	////////////////////////////////////////////////
+	//	modelURL
+	////////////////////////////////////////////////
+
+	private final static String MODEL_URL = "modelURL";
+	
+	public void setModelURL(String value)
+	{
+		getDeviceNode().setNode(MODEL_URL, value);
+	}
+
+	public String getModelURL()
+	{
+		return getDeviceNode().getNodeValue(MODEL_URL);
+	}
+
+	////////////////////////////////////////////////
+	//	serialNumber
+	////////////////////////////////////////////////
+
+	private final static String SERIAL_NUMBER = "serialNumber";
+	
+	public void setSerialNumber(String value)
+	{
+		getDeviceNode().setNode(SERIAL_NUMBER, value);
+	}
+
+	public String getSerialNumber()
+	{
+		return getDeviceNode().getNodeValue(SERIAL_NUMBER);
+	}
+
+	////////////////////////////////////////////////
+	//	UDN
+	////////////////////////////////////////////////
+
+	private final static String UDN = "UDN";
+	
+	public void setUDN(String value)
+	{
+		getDeviceNode().setNode(UDN, value);
+	}
+
+	public String getUDN()
+	{
+		return getDeviceNode().getNodeValue(UDN);
+	}
 
 	public boolean hasUDN()
 	{
 		String udn = getUDN();
 		if (udn == null || udn.length() <= 0)
 			return false;
-		return true;
-	}
-	
-	////////////////////////////////////////////////
-	//	UPC
-	////////////////////////////////////////////////
-
-	private final static String UPC = "UPC";
-	
-	public void setUPC(String value)
-	{
-		getDeviceNode().setNode(UPC, value);
-	}
-
-	public String getUPC()
-	{
-		return getDeviceNode().getNodeValue(UPC);
-	}
-
-	////////////////////////////////////////////////
-	//	presentationURL
-	////////////////////////////////////////////////
-
-	private final static String presentationURL = "presentationURL";
-	
-	public void setPresentationURL(String value)
-	{
-		getDeviceNode().setNode(presentationURL, value);
-	}
-
-	public String getPresentationURL()
-	{
-		return getDeviceNode().getNodeValue(presentationURL);
-	}
-
-	////////////////////////////////////////////////
-	//	deviceList
-	////////////////////////////////////////////////
-
-	public DeviceList getDeviceList()
-	{
-		DeviceList devList = new DeviceList();
-		Node devListNode = getDeviceNode().getNode(DeviceList.ELEM_NAME);
-		if (devListNode == null)
-			return devList;
-		int nNode = devListNode.getNNodes();
-		for (int n=0; n<nNode; n++) {
-			Node node = devListNode.getNode(n);
-			if (Device.isDeviceNode(node) == false)
-				continue;
-			Device dev = new Device(node);
-			devList.add(dev);
-		} 
-		return devList;
-	}
+		return true;
+	}
+	
+	////////////////////////////////////////////////
+	//	UPC
+	////////////////////////////////////////////////
+
+	private final static String UPC = "UPC";
+	
+	public void setUPC(String value)
+	{
+		getDeviceNode().setNode(UPC, value);
+	}
+
+	public String getUPC()
+	{
+		return getDeviceNode().getNodeValue(UPC);
+	}
+
+	////////////////////////////////////////////////
+	//	presentationURL
+	////////////////////////////////////////////////
+
+	private final static String presentationURL = "presentationURL";
+	
+	public void setPresentationURL(String value)
+	{
+		getDeviceNode().setNode(presentationURL, value);
+	}
+
+	public String getPresentationURL()
+	{
+		return getDeviceNode().getNodeValue(presentationURL);
+	}
+
+	////////////////////////////////////////////////
+	//	deviceList
+	////////////////////////////////////////////////
+
+	public DeviceList getDeviceList()
+	{
+		DeviceList devList = new DeviceList();
+		Node devListNode = getDeviceNode().getNode(DeviceList.ELEM_NAME);
+		if (devListNode == null)
+			return devList;
+		int nNode = devListNode.getNNodes();
+		for (int n=0; n<nNode; n++) {
+			Node node = devListNode.getNode(n);
+			if (Device.isDeviceNode(node) == false)
+				continue;
+			Device dev = new Device(node);
+			devList.add(dev);
+		} 
+		return devList;
+	}
 
 	public boolean isDevice(String name)
 	{
@@ -831,26 +1003,26 @@ public class Device implements org.cybergarage.http.HTTPRequestListener, SearchL
 		return null;
 	}
 	
-	////////////////////////////////////////////////
-	//	serviceList
-	////////////////////////////////////////////////
-
-	public ServiceList getServiceList()
-	{
-		ServiceList serviceList = new ServiceList();
-		Node serviceListNode = getDeviceNode().getNode(ServiceList.ELEM_NAME);
-		if (serviceListNode == null)
-			return serviceList;
-		int nNode = serviceListNode.getNNodes();
-		for (int n=0; n<nNode; n++) {
-			Node node = serviceListNode.getNode(n);
-			if (Service.isServiceNode(node) == false)
-				continue;
-			Service service = new Service(node);
-			serviceList.add(service);
-		} 
-		return serviceList;
-	}
+	////////////////////////////////////////////////
+	//	serviceList
+	////////////////////////////////////////////////
+
+	public ServiceList getServiceList()
+	{
+		ServiceList serviceList = new ServiceList();
+		Node serviceListNode = getDeviceNode().getNode(ServiceList.ELEM_NAME);
+		if (serviceListNode == null)
+			return serviceList;
+		int nNode = serviceListNode.getNNodes();
+		for (int n=0; n<nNode; n++) {
+			Node node = serviceListNode.getNode(n);
+			if (Service.isServiceNode(node) == false)
+				continue;
+			Service service = new Service(node);
+			serviceList.add(service);
+		} 
+		return serviceList;
+	}
 
 	public Service getService(String name)
 	{
@@ -1037,25 +1209,25 @@ public class Device implements org.cybergarage.http.HTTPRequestListener, SearchL
 		return null;
 	}
 
-	////////////////////////////////////////////////
-	//	iconList
-	////////////////////////////////////////////////
-
-	public IconList getIconList()
-	{
-		IconList iconList = new IconList();
-		Node iconListNode = getDeviceNode().getNode(IconList.ELEM_NAME);
-		if (iconListNode == null)
-			return iconList;
-		int nNode = iconListNode.getNNodes();
-		for (int n=0; n<nNode; n++) {
-			Node node = iconListNode.getNode(n);
-			if (Icon.isIconNode(node) == false)
-				continue;
-			Icon icon = new Icon(node);
-			iconList.add(icon);
-		} 
-		return iconList;
+	////////////////////////////////////////////////
+	//	iconList
+	////////////////////////////////////////////////
+
+	public IconList getIconList()
+	{
+		IconList iconList = new IconList();
+		Node iconListNode = getDeviceNode().getNode(IconList.ELEM_NAME);
+		if (iconListNode == null)
+			return iconList;
+		int nNode = iconListNode.getNNodes();
+		for (int n=0; n<nNode; n++) {
+			Node node = iconListNode.getNode(n);
+			if (Icon.isIconNode(node) == false)
+				continue;
+			Icon icon = new Icon(node);
+			iconList.add(icon);
+		} 
+		return iconList;
 	}
 	
 	public Icon getIcon(int n)
@@ -1064,8 +1236,26 @@ public class Device implements org.cybergarage.http.HTTPRequestListener, SearchL
 		if (n < 0 && (iconList.size()-1) < n)
 			return null;
 		return iconList.getIcon(n);
-	}
+	}
 
+	public Icon getSmallestIcon()
+	{
+		Icon smallestIcon = null;		
+		IconList iconList = getIconList();
+		int iconCount = iconList.size();
+		for (int n=0; n < iconCount; n++) {
+			Icon icon = iconList.getIcon(n);
+			if (null == smallestIcon) {
+				smallestIcon = icon;
+				continue;
+			}
+			if (icon.getWidth() < smallestIcon.getWidth())
+				smallestIcon = icon;			
+		}
+		
+		return smallestIcon;
+	}
+	
 	////////////////////////////////////////////////
 	//	Notify
 	////////////////////////////////////////////////
@@ -1103,7 +1293,6 @@ public class Device implements org.cybergarage.http.HTTPRequestListener, SearchL
 	{
 		TimerUtil.waitRandom(DEFAULT_DISCOVERY_WAIT_TIME);
 	}
-		
 	public void announce(String bindAddr)
 	{
 		String devLocation = getLocationURL(bindAddr);
@@ -1123,6 +1312,11 @@ public class Device implements org.cybergarage.http.HTTPRequestListener, SearchL
 			ssdpReq.setNT(devNT);
 			ssdpReq.setUSN(devUSN);
 			ssdpSock.post(ssdpReq);
+			 
+			String devUDN = getUDN(); 
+			ssdpReq.setNT(devUDN); 
+			ssdpReq.setUSN(devUDN); 
+			ssdpSock.post(ssdpReq); 			
 		}
 		
 		// uuid:device-UUID::urn:schemas-upnp-org:device:deviceType:v 
@@ -1150,18 +1344,29 @@ public class Device implements org.cybergarage.http.HTTPRequestListener, SearchL
 		}
 	}
 
-	public void announce()
-	{
+	public void announce(){
 		notifyWait();
-		
-		int nHostAddrs = HostInterface.getNHostAddresses();
-		for (int n=0; n<nHostAddrs; n++) {
-			String bindAddr = HostInterface.getHostAddress(n);
-			if (bindAddr == null || bindAddr.length() <= 0)
+		InetAddress[] binds = getDeviceData().getHTTPBindAddress();
+		String[] bindAddresses;
+		if(binds!=null){			
+			bindAddresses = new String[binds.length];
+			for (int i = 0; i < binds.length; i++) {
+				bindAddresses[i] = binds[i].getHostAddress();
+			}
+		}else{
+			int nHostAddrs = HostInterface.getNHostAddresses();
+			bindAddresses = new String[nHostAddrs]; 
+			for (int n=0; n<nHostAddrs; n++) {
+				bindAddresses[n] = HostInterface.getHostAddress(n);
+			}
+		}		
+		for (int j = 0; j < bindAddresses.length; j++) {
+			if(bindAddresses[j] == null || bindAddresses[j].length() == 0)
 				continue;
 			int ssdpCount = getSSDPAnnounceCount();
 			for (int i=0; i<ssdpCount; i++)
-				announce(bindAddr);
+				announce(bindAddresses[j]);
+			
 		}
 	}
 	
@@ -1206,24 +1411,38 @@ public class Device implements org.cybergarage.http.HTTPRequestListener, SearchL
 		}
 	}
 
-	public void byebye()
-	{
-		int nHostAddrs = HostInterface.getNHostAddresses();
-		for (int n=0; n<nHostAddrs; n++) {
-			String bindAddr = HostInterface.getHostAddress(n);
-			if (bindAddr == null || bindAddr.length() <= 0)
+	public void byebye(){
+
+		InetAddress[] binds = getDeviceData().getHTTPBindAddress();
+		String[] bindAddresses;
+		if(binds!=null){			
+			bindAddresses = new String[binds.length];
+			for (int i = 0; i < binds.length; i++) {
+				bindAddresses[i] = binds[i].getHostAddress();
+			}
+		}else{
+			int nHostAddrs = HostInterface.getNHostAddresses();
+			bindAddresses = new String[nHostAddrs]; 
+			for (int n=0; n<nHostAddrs; n++) {
+				bindAddresses[n] = HostInterface.getHostAddress(n);
+			}
+		}		
+		
+		for (int j = 0; j < bindAddresses.length; j++) {			
+			if (bindAddresses[j] == null || bindAddresses[j].length() <= 0)
 				continue;
 			int ssdpCount = getSSDPAnnounceCount();
 			for (int i=0; i<ssdpCount; i++)
-				byebye(bindAddr);
-		}
+				byebye(bindAddresses[j]);
+		}		
 	}
-
-	////////////////////////////////////////////////
-	//	Search
-	////////////////////////////////////////////////
 
-	public boolean postSearchResponse(SSDPPacket ssdpPacket, String st, String usn)
+	////////////////////////////////////////////////
+	//	Search
+	////////////////////////////////////////////////
+
+    private static Calendar cal = Calendar.getInstance();
+    public boolean postSearchResponse(SSDPPacket ssdpPacket, String st, String usn)
 	{
 		String localAddr = ssdpPacket.getLocalAddress();
 		Device rootDev = getRootDevice();
@@ -1231,7 +1450,7 @@ public class Device implements org.cybergarage.http.HTTPRequestListener, SearchL
 		
 		SSDPSearchResponse ssdpRes = new SSDPSearchResponse();
 		ssdpRes.setLeaseTime(getLeaseTime());
-		ssdpRes.setDate(Calendar.getInstance());
+		ssdpRes.setDate(cal);
 		ssdpRes.setST(st);
 		ssdpRes.setUSN(usn);
 		ssdpRes.setLocation(rootDevLocation);
@@ -1314,29 +1533,73 @@ public class Device implements org.cybergarage.http.HTTPRequestListener, SearchL
 	//	HTTP Server	
 	////////////////////////////////////////////////
 
-	public void setHTTPPort(int port)
-	{
+	public void setHTTPPort(int port)
+	{
 		getDeviceData().setHTTPPort(port);
-	}
-	
-	public int getHTTPPort()
-	{
-		return getDeviceData().getHTTPPort();
-	}
-
-	public void httpRequestRecieved(HTTPRequest httpReq)
+	}
+	
+	public int getHTTPPort()
+	{
+		return getDeviceData().getHTTPPort();
+	}
+
+	public void setHTTPBindAddress(InetAddress[] inets){
+		this.getDeviceData().setHTTPBindAddress(inets);
+	}
+	
+	public InetAddress[] getHTTPBindAddress(){
+		return this.getDeviceData().getHTTPBindAddress();
+	}	
+	
+	/**
+	 * 
+	 * @return
+	 * @since 1.8
+	 */
+	public String getSSDPIPv4MulticastAddress(){
+		return this.getDeviceData().getMulticastIPv4Address();
+	}	
+	
+	/**
+	 * 
+	 * @param ip
+	 * @since 1.8
+	 */
+	public void getSSDPIPv4MulticastAddress(String ip){
+		this.getDeviceData().setMulticastIPv4Address(ip);
+	}	
+	
+	/**
+	 * 
+	 * @return
+	 * @since 1.8
+	 */
+	public String getSSDPIPv6MulticastAddress(){
+		return this.getDeviceData().getMulticastIPv6Address();
+	}	
+	
+	/**
+	 * 
+	 * @param ip
+	 * @since 1.8
+	 */
+	public void getSSDPIPv6MulticastAddress(String ip){
+		this.getDeviceData().setMulticastIPv6Address(ip);
+	}	
+	
+	public void httpRequestRecieved(HTTPRequest httpReq)
 	{
 		if (Debug.isOn() == true)
 			httpReq.print();
 	
-		if (httpReq.isGetRequest() == true) {
-			httpGetRequestRecieved(httpReq);
+		if (httpReq.isGetRequest() == true || httpReq.isHeadRequest() == true) {
+			httpGetRequestRecieved(httpReq);
 			return;
-		}
-		if (httpReq.isPostRequest() == true) {
-			httpPostRequestRecieved(httpReq);
-			return;
-		}
+		}
+		if (httpReq.isPostRequest() == true) {
+			httpPostRequestRecieved(httpReq);
+			return;
+		}
 
 		if (httpReq.isSubscribeRequest() == true || httpReq.isUnsubscribeRequest() == true) {
 			SubscriptionRequest subReq = new SubscriptionRequest(httpReq);
@@ -1345,11 +1608,11 @@ public class Device implements org.cybergarage.http.HTTPRequestListener, SearchL
 		}
 
 		httpReq.returnBadRequest();
-	}
+	}
 
 	private synchronized byte[] getDescriptionData(String host)
 	{
-		if (isNMPRMode() == false)
+		if (isNMPRMode() == false)
 			updateURLBase(host);
 		Node rootNode = getRootNode();
 		if (rootNode == null)
@@ -1362,21 +1625,23 @@ public class Device implements org.cybergarage.http.HTTPRequestListener, SearchL
 		return desc.getBytes();
 	}
 	
-	private void httpGetRequestRecieved(HTTPRequest httpReq)
+	private void httpGetRequestRecieved(HTTPRequest httpReq)
 	{
 		String uri = httpReq.getURI();
 		Debug.message("httpGetRequestRecieved = " + uri);
-		if (uri == null) {
+		if (uri == null) {
 			httpReq.returnBadRequest();
-			return;
-		}
+			return;
+		}
 					
 		Device embDev;
 		Service embService;
 		
-		byte fileByte[] = new byte[0];
+		byte fileByte[] = new byte[0];
 		if (isDescriptionURI(uri) == true) {
 			String localAddr = httpReq.getLocalAddress();
+			if ((localAddr == null) || (localAddr.length() <= 0))
+				localAddr = HostInterface.getInterface();
 			fileByte = getDescriptionData(localAddr);
 		}
 		else if ((embDev = getDeviceByDescriptionURI(uri)) != null) {
@@ -1390,17 +1655,17 @@ public class Device implements org.cybergarage.http.HTTPRequestListener, SearchL
 			httpReq.returnBadRequest();
 			return;
 		}
-		
+		
 		HTTPResponse httpRes = new HTTPResponse();
 		if (FileUtil.isXMLFileName(uri) == true)
-			httpRes.setContentType(XML.CONTENT_TYPE);
-		httpRes.setStatusCode(HTTPStatus.OK);
-		httpRes.setContent(fileByte);
-
-		httpReq.post(httpRes);
-	}
-
-	private void httpPostRequestRecieved(HTTPRequest httpReq)
+			httpRes.setContentType(XML.CONTENT_TYPE);
+		httpRes.setStatusCode(HTTPStatus.OK);
+		httpRes.setContent(fileByte);
+
+		httpReq.post(httpRes);
+	}
+
+	private void httpPostRequestRecieved(HTTPRequest httpReq)
 	{
 		if (httpReq.isSOAPAction() == true) {
 			//SOAPRequest soapReq = new SOAPRequest(httpReq);
@@ -1408,7 +1673,7 @@ public class Device implements org.cybergarage.http.HTTPRequestListener, SearchL
 			return;
 		}
 		httpReq.returnBadRequest();
-	}
+	}
 
 	////////////////////////////////////////////////
 	//	SOAP
@@ -1452,6 +1717,13 @@ public class Device implements org.cybergarage.http.HTTPRequestListener, SearchL
 		ctlReq.post(actRes);
 	}
 
+   private void invalidArgumentsControlRecieved(ControlRequest ctlReq)
+    {
+        ControlResponse actRes = new ActionResponse();
+        actRes.setFaultResponse(UPnPStatus.INVALID_ARGS);
+        ctlReq.post(actRes);
+    }
+
 	private void deviceActionControlRecieved(ActionRequest ctlReq, Service service)
 	{
 		if (Debug.isOn() == true)
@@ -1465,7 +1737,12 @@ public class Device implements org.cybergarage.http.HTTPRequestListener, SearchL
 		}
 		ArgumentList actionArgList = action.getArgumentList();
 		ArgumentList reqArgList = ctlReq.getArgumentList();
-		actionArgList.set(reqArgList);
+        try {
+            actionArgList.setReqArgs(reqArgList);
+        } catch (IllegalArgumentException ex){
+            invalidArgumentsControlRecieved(ctlReq);
+            return;
+       }
 		if (action.performActionListener(ctlReq) == false)
 			invalidActionControlRecieved(ctlReq);
 	}
@@ -1607,14 +1884,83 @@ public class Device implements org.cybergarage.http.HTTPRequestListener, SearchL
 			subRes.print();
 	}		
 	
-	////////////////////////////////////////////////
-	//	Thread	
-	////////////////////////////////////////////////
+	////////////////////////////////////////////////
+	//	Thread	
+	////////////////////////////////////////////////
 
 	private HTTPServerList getHTTPServerList() 
 	{
 		return getDeviceData().getHTTPServerList();
 	}
+	/**
+	 * 
+	 * @param port The port to use for binding the SSDP service
+	 */
+	public void setSSDPPort(int port){
+		this.getDeviceData().setSSDPPort(port);
+	}
+
+	/**
+	 * 
+	 * @return The port to use for binding the SSDP service
+	 */
+	public int getSSDPPort(){
+		return this.getDeviceData().getSSDPPort();
+	}
+	
+	
+
+	/**
+	 * 
+	 * @param inets The IP that will be used for binding the SSDP service.
+	 * 		Use <code>null</code> to get the default beahvior 
+	 */
+	public void setSSDPBindAddress(InetAddress[] inets){
+		this.getDeviceData().setSSDPBindAddress(inets);
+	}
+		
+	
+	/**
+	 * 
+	 * @return inets The IP that will be used for binding the SSDP service.
+	 * 		null means the default setted by the class UPnP
+	 */
+	public InetAddress[] getSSDPBindAddress(){
+		return this.getDeviceData().getSSDPBindAddress();
+	}	
+	
+	/**
+	 * 
+	 * @param ip The IPv4 address used for Multicast comunication
+	 */
+	public void setMulticastIPv4Address(String ip){
+		this.getDeviceData().setMulticastIPv4Address(ip);
+	}
+
+	/**
+	 * 
+	 * @return The IPv4 address used for Multicast comunication
+	 */
+	public String getMulticastIPv4Address(){
+		return this.getDeviceData().getMulticastIPv4Address();
+	}
+	
+	/**
+	 * 
+	 * @param ip The IPv address used for Multicast comunication
+	 */
+	public void setMulticastIPv6Address(String ip){
+		this.getDeviceData().setMulticastIPv6Address(ip);
+	}
+
+	/**
+	 * 
+	 * @return The IPv address used for Multicast comunication
+	 */
+	public String getMulticastIPv6Address(){
+		return this.getDeviceData().getMulticastIPv6Address();
+	}
+	
 
 	private SSDPSearchSocketList getSSDPSearchSocketList() 
 	{
@@ -1631,10 +1977,10 @@ public class Device implements org.cybergarage.http.HTTPRequestListener, SearchL
 		return getDeviceData().getAdvertiser();
 	}
 
-	public boolean start()
+	public boolean start()
 	{
 		stop(true);
-		
+		
 		////////////////////////////////////////
 		// HTTP Server
 		////////////////////////////////////////
@@ -1676,8 +2022,8 @@ public class Device implements org.cybergarage.http.HTTPRequestListener, SearchL
 		setAdvertiser(adv);
 		adv.start();
 		
-		return true;
-	}
+		return true;
+	}
 
 	private boolean stop(boolean doByeBye)
 	{
@@ -1702,12 +2048,17 @@ public class Device implements org.cybergarage.http.HTTPRequestListener, SearchL
 
 		return true;
 	}
-	
-	public boolean stop()
+	
+	public boolean stop()
+	{
+		return stop(true);
+	}
+
+	public boolean isRunning()
 	{
-		return stop(true);
-	}
-
+		return (getAdvertiser() != null) ? true : false;
+	}
+
 	////////////////////////////////////////////////
 	// Interface Address
 	////////////////////////////////////////////////
@@ -1775,35 +2126,51 @@ public class Device implements org.cybergarage.http.HTTPRequestListener, SearchL
 			}
 		}
 	}
+
+	////////////////////////////////////////////////
+	//	userData
+	////////////////////////////////////////////////
+
+	private Object userData = null; 
 	
-	////////////////////////////////////////////////
-	//	output
-	////////////////////////////////////////////////
-
-/*
-	public void output(PrintWriter ps) 
-	{
-		ps.println("deviceType = " + getDeviceType());
-		ps.println("freindlyName = " + getFriendlyName());
-		ps.println("presentationURL = " + getPresentationURL());
-
-		DeviceList devList = getDeviceList();
-		ps.println("devList = " + devList.size());
-		
-		ServiceList serviceList = getServiceList();
-		ps.println("serviceList = " + serviceList.size());
-
-		IconList iconList = getIconList();
-		ps.println("iconList = " + iconList.size());
-	}
-
-	public void print()
-	{
-		PrintWriter pr = new PrintWriter(System.out);
-		output(pr);
-		pr.flush();
-	}
-*/
+	public void setUserData(Object data) 
+	{
+		userData = data;
+	}
+
+	public Object getUserData() 
+	{
+		return userData;
+	}
+	
+	////////////////////////////////////////////////
+	//	output
+	////////////////////////////////////////////////
+
+/*
+	public void output(PrintWriter ps) 
+	{
+		ps.println("deviceType = " + getDeviceType());
+		ps.println("freindlyName = " + getFriendlyName());
+		ps.println("presentationURL = " + getPresentationURL());
+
+		DeviceList devList = getDeviceList();
+		ps.println("devList = " + devList.size());
+		
+		ServiceList serviceList = getServiceList();
+		ps.println("serviceList = " + serviceList.size());
+
+		IconList iconList = getIconList();
+		ps.println("iconList = " + iconList.size());
+	}
+
+	public void print()
+	{
+		PrintWriter pr = new PrintWriter(System.out);
+		output(pr);
+		pr.flush();
+	}
+*/
 
 }
 
diff --git a/router/java/src/org/cybergarage/upnp/DeviceList.java b/router/java/src/org/cybergarage/upnp/DeviceList.java
index 729b2245f45f1657fbd3be48a27c2ae1e92d44a2..afa23ffc914ee5692ffe76bf69cd3b79105e90f2 100644
--- a/router/java/src/org/cybergarage/upnp/DeviceList.java
+++ b/router/java/src/org/cybergarage/upnp/DeviceList.java
@@ -1,46 +1,45 @@
-/******************************************************************
-*
-*	CyberUPnP for Java
-*
-*	Copyright (C) Satoshi Konno 2002
+/******************************************************************
 *
-*	File: DeviceList.java
-*
-*	Revision;
-*
-*	12/04/02
-*		- first revision.
-*
-******************************************************************/
+*	CyberUPnP for Java
+*
+*	Copyright (C) Satoshi Konno 2002
+*
+*	File: DeviceList.java
+*
+*	Revision;
+*
+*	12/04/02
+*		- first revision.
+*
+******************************************************************/
+
+package org.cybergarage.upnp;
+
+import java.util.Vector;
+
+public class DeviceList extends Vector 
+{
+	////////////////////////////////////////////////
+	//	Constants
+	////////////////////////////////////////////////
+	
+	public final static String ELEM_NAME = "deviceList";
+
+	////////////////////////////////////////////////
+	//	Constructor
+	////////////////////////////////////////////////
+	
+	public DeviceList() 
+	{
+	}
+	
+	////////////////////////////////////////////////
+	//	Methods
+	////////////////////////////////////////////////
+	
+	public Device getDevice(int n)
+	{
+		return (Device)get(n);
+	}
+}
 
-package org.cybergarage.upnp;
-
-import java.util.*;
-
-public class DeviceList extends Vector 
-{
-	////////////////////////////////////////////////
-	//	Constants
-	////////////////////////////////////////////////
-	
-	private static final long serialVersionUID = 3773784061607435126L;
-	public final static String ELEM_NAME = "deviceList";
-
-	////////////////////////////////////////////////
-	//	Constructor
-	////////////////////////////////////////////////
-	
-	public DeviceList() 
-	{
-	}
-	
-	////////////////////////////////////////////////
-	//	Methods
-	////////////////////////////////////////////////
-	
-	public Device getDevice(int n)
-	{
-		return (Device)get(n);
-	}
-}
-
diff --git a/router/java/src/org/cybergarage/upnp/Icon.java b/router/java/src/org/cybergarage/upnp/Icon.java
index 7566b3117412290b3fbeb635fd11e97f6d0a7c10..0f76c44a38bdfe90b2c7d48eee24ec254e117974 100644
--- a/router/java/src/org/cybergarage/upnp/Icon.java
+++ b/router/java/src/org/cybergarage/upnp/Icon.java
@@ -10,12 +10,14 @@
 *
 *	11/28/02
 *		- first revision.
+*	04/12/06
+*		- Added setUserData() and getUserData() to set a user original data object.
 *	
 ******************************************************************/
 
 package org.cybergarage.upnp;
 
-import org.cybergarage.xml.*;
+import org.cybergarage.xml.Node;
 
 public class Icon
 {
@@ -81,9 +83,21 @@ public class Icon
 		getIconNode().setNode(WIDTH, value);
 	}
 
-	public String getWidth()
-	{
-		return getIconNode().getNodeValue(WIDTH);
+	public void setWidth(int value)
+	{
+		try {
+			setWidth(Integer.toString(value));
+		}
+		catch (Exception e) {};
+	}
+	
+	public int getWidth()
+	{
+		try {
+			return Integer.parseInt(getIconNode().getNodeValue(WIDTH));
+		}
+		catch (Exception e) {};
+		return 0;
 	}
 
 	////////////////////////////////////////////////
@@ -97,9 +111,21 @@ public class Icon
 		getIconNode().setNode(HEIGHT, value);
 	}
 
-	public String getHeight()
+	public void setHeight(int value)
+	{
+		try {
+			setHeight(Integer.toString(value));
+		}
+		catch (Exception e) {};
+	}
+	
+	public int getHeight()
 	{
-		return getIconNode().getNodeValue(HEIGHT);
+		try {
+			return Integer.parseInt(getIconNode().getNodeValue(HEIGHT));
+		}
+		catch (Exception e) {};
+		return 0;
 	}
 
 	////////////////////////////////////////////////
@@ -132,5 +158,21 @@ public class Icon
 	public String getURL()
 	{
 		return getIconNode().getNodeValue(URL);
-	}
+	}
+	
+	////////////////////////////////////////////////
+	//	userData
+	////////////////////////////////////////////////
+
+	private Object userData = null; 
+	
+	public void setUserData(Object data) 
+	{
+		userData = data;
+	}
+
+	public Object getUserData() 
+	{
+		return userData;
+	}
 }
diff --git a/router/java/src/org/cybergarage/upnp/IconList.java b/router/java/src/org/cybergarage/upnp/IconList.java
index 2e60de0fc83feef54dccc4a6d266941a0f66522d..a78f810cc9d060f352f1f8b9a13d75301321eef4 100644
--- a/router/java/src/org/cybergarage/upnp/IconList.java
+++ b/router/java/src/org/cybergarage/upnp/IconList.java
@@ -15,7 +15,7 @@
 
 package org.cybergarage.upnp;
 
-import java.util.*;
+import java.util.Vector;
 
 public class IconList extends Vector 
 {
@@ -23,7 +23,6 @@ public class IconList extends Vector
 	//	Constants
 	////////////////////////////////////////////////
 	
-	private static final long serialVersionUID = -1097238335037012991L;
 	public final static String ELEM_NAME = "iconList";
 
 	////////////////////////////////////////////////
diff --git a/router/java/src/org/cybergarage/upnp/RootDescription.java b/router/java/src/org/cybergarage/upnp/RootDescription.java
new file mode 100644
index 0000000000000000000000000000000000000000..19b5ad92632e51efcd18b60c82ae90dd9281f335
--- /dev/null
+++ b/router/java/src/org/cybergarage/upnp/RootDescription.java
@@ -0,0 +1,19 @@
+package org.cybergarage.upnp;
+
+
+/**
+ * @author Stefano "Kismet" Lenzi - kismet-sl@users.sourceforge.net  <br> 
+ * 		Copyright (c) 2005
+ *
+ */
+public interface RootDescription {
+
+	public final String ROOT_ELEMENT = "root";
+	public final String ROOT_ELEMENT_NAMESPACE = "urn:schemas-upnp-org:device-1-0"; 
+		
+	
+	public final String SPECVERSION_ELEMENT = "specVersion";
+	public final String MAJOR_ELEMENT = "major";
+	public final String MINOR_ELEMENT = "minor";
+	public final String SERVICE_LIST_ELEMENT = "serviceList";
+}
diff --git a/router/java/src/org/cybergarage/upnp/Service.java b/router/java/src/org/cybergarage/upnp/Service.java
index 2febe00df46e7528bfe904138015ec9f06875def..3f517b0bfe2c034d5f2445776914081981876c7c 100644
--- a/router/java/src/org/cybergarage/upnp/Service.java
+++ b/router/java/src/org/cybergarage/upnp/Service.java
@@ -5,11 +5,11 @@
 *	Copyright (C) Satoshi Konno 2002-2003
 *
 *	File: Service.java
-*
-*	Revision;
-*
-*	11/28/02
-*		- first revision.
+*
+*	Revision;
+*
+*	11/28/02
+*		- first revision.
 *	04/12/02
 *		- Holmes, Arran C <acholm@essex.ac.uk>
 *		- Fixed SERVICE_ID constant instead of "serviceId".
@@ -60,52 +60,99 @@
 * 	04/25/05
 *		- Thanks for Mikael Hakman <mhakman@dkab.net>
 * 		- Changed getSCPDData() to add a XML declaration at first line.
+*	06/21/05
+*		- Changed notify() to continue when the subscriber is null.
+*	04/12/06
+*		- Added setUserData() and getUserData() to set a user original data object.
+*	09/18/2010 Robin V. <robinsp@gmail.com>
+*		- Fixed getSCPDNode() not to occur recursive http get requests.
 *
 ******************************************************************/
 
-package org.cybergarage.upnp;
-
-import java.io.*;
-import java.net.*;
-
-import org.cybergarage.http.*;
-import org.cybergarage.xml.*;
-import org.cybergarage.util.*;
-
-import org.cybergarage.upnp.ssdp.*;
-import org.cybergarage.upnp.xml.*;
-import org.cybergarage.upnp.device.*;
-import org.cybergarage.upnp.control.*;
-import org.cybergarage.upnp.event.*;
-
-public class Service
-{
-	////////////////////////////////////////////////
-	//	Constants
-	////////////////////////////////////////////////
-	
-	public final static String ELEM_NAME = "service";
-
-	////////////////////////////////////////////////
-	//	Member
-	////////////////////////////////////////////////
-
-	private Node serviceNode;
-
-	public Node getServiceNode()
-	{
-		return serviceNode;
-	}
-
-	////////////////////////////////////////////////
-	//	Constructor
-	////////////////////////////////////////////////
-
-	public Service(Node node)
-	{
-		serviceNode = node;
-	}
-
+package org.cybergarage.upnp;
+
+import java.io.File;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Iterator;
+
+import org.cybergarage.http.HTTP;
+import org.cybergarage.http.HTTPResponse;
+import org.cybergarage.upnp.control.ActionListener;
+import org.cybergarage.upnp.control.QueryListener;
+import org.cybergarage.upnp.device.InvalidDescriptionException;
+import org.cybergarage.upnp.device.NTS;
+import org.cybergarage.upnp.device.ST;
+import org.cybergarage.upnp.event.NotifyRequest;
+import org.cybergarage.upnp.event.Subscriber;
+import org.cybergarage.upnp.event.SubscriberList;
+import org.cybergarage.upnp.ssdp.SSDPNotifyRequest;
+import org.cybergarage.upnp.ssdp.SSDPNotifySocket;
+import org.cybergarage.upnp.ssdp.SSDPPacket;
+import org.cybergarage.upnp.xml.ServiceData;
+import org.cybergarage.util.Debug;
+import org.cybergarage.util.Mutex;
+import org.cybergarage.util.StringUtil;
+import org.cybergarage.xml.Node;
+import org.cybergarage.xml.Parser;
+import org.cybergarage.xml.ParserException;
+
+public class Service
+{
+	////////////////////////////////////////////////
+	//	Constants
+	////////////////////////////////////////////////
+	
+	public final static String ELEM_NAME = "service";
+
+	////////////////////////////////////////////////
+	//	Member
+	////////////////////////////////////////////////
+
+	private Node serviceNode;
+
+	public Node getServiceNode()
+	{
+		return serviceNode;
+	}
+
+	////////////////////////////////////////////////
+	//	Constructor
+	////////////////////////////////////////////////
+	public static final String SCPD_ROOTNODE="scpd";
+	public static final String SCPD_ROOTNODE_NS="urn:schemas-upnp-org:service-1-0"; 
+	
+	public static final String SPEC_VERSION="specVersion";
+	public static final String MAJOR="major";
+	public static final String MAJOR_VALUE="1";
+	public static final String MINOR="minor";
+	public static final String MINOR_VALUE="0";
+	
+	public Service(){
+		this(new Node(ELEM_NAME));
+		
+		Node sp = new Node(SPEC_VERSION);
+		
+		Node M =new Node(MAJOR);
+		M.setValue(MAJOR_VALUE);
+		sp.addNode(M);
+				
+		Node m =new Node(MINOR);
+		m.setValue(MINOR_VALUE);
+		sp.addNode(m);
+		
+		//Node scpd = new Node(SCPD_ROOTNODE,SCPD_ROOTNODE_NS); wrong!
+		Node scpd = new Node(SCPD_ROOTNODE); 					// better (twa)
+		scpd.addAttribute("xmlns",SCPD_ROOTNODE_NS); 			// better (twa)
+		scpd.addNode(sp);
+		getServiceData().setSCPDNode(scpd);
+	}
+
+	public Service(Node node)
+	{
+		serviceNode = node;
+	}
+
 	////////////////////////////////////////////////
 	// Mutex
 	////////////////////////////////////////////////
@@ -122,78 +169,78 @@ public class Service
 		mutex.unlock();
 	}
 	
-	////////////////////////////////////////////////
-	//	isServiceNode
-	////////////////////////////////////////////////
-
-	public static boolean isServiceNode(Node node)
-	{
-		return Service.ELEM_NAME.equals(node.getName());
-	}
-	
-	////////////////////////////////////////////////
-	//	Device/Root Node
-	////////////////////////////////////////////////
-
-	private Node getDeviceNode()
-	{
-		Node node = getServiceNode().getParentNode();
-		if (node == null)
-			return null;
-		return node.getParentNode();
-	}
-
-	private Node getRootNode()
-	{
-		return getServiceNode().getRootNode();
-	}
-
-	////////////////////////////////////////////////
-	//	Device
-	////////////////////////////////////////////////
-
-	public Device getDevice()
-	{
-		return new Device(getRootNode(), getDeviceNode());
-	}
+	////////////////////////////////////////////////
+	//	isServiceNode
+	////////////////////////////////////////////////
+
+	public static boolean isServiceNode(Node node)
+	{
+		return Service.ELEM_NAME.equals(node.getName());
+	}
+	
+	////////////////////////////////////////////////
+	//	Device/Root Node
+	////////////////////////////////////////////////
+
+	private Node getDeviceNode()
+	{
+		Node node = getServiceNode().getParentNode();
+		if (node == null)
+			return null;
+		return node.getParentNode();
+	}
+
+	private Node getRootNode()
+	{
+		return getServiceNode().getRootNode();
+	}
+
+	////////////////////////////////////////////////
+	//	Device
+	////////////////////////////////////////////////
+
+	public Device getDevice()
+	{
+		return new Device(getRootNode(), getDeviceNode());
+	}
 
 	public Device getRootDevice()
 	{
 		return getDevice().getRootDevice();
 	}
 
-	////////////////////////////////////////////////
-	//	serviceType
-	////////////////////////////////////////////////
-
-	private final static String SERVICE_TYPE = "serviceType";
-	
-	public void setServiceType(String value)
-	{
-		getServiceNode().setNode(SERVICE_TYPE, value);
-	}
-
-	public String getServiceType()
-	{
-		return getServiceNode().getNodeValue(SERVICE_TYPE);
-	}
-
-	////////////////////////////////////////////////
-	//	serviceID
-	////////////////////////////////////////////////
-
-	private final static String SERVICE_ID = "serviceId";
-	
-	public void setServiceID(String value)
-	{
-		getServiceNode().setNode(SERVICE_ID, value);
-	}
-
-	public String getServiceID()
-	{
-		return getServiceNode().getNodeValue(SERVICE_ID);
-	}
-
+	////////////////////////////////////////////////
+	//	serviceType
+	////////////////////////////////////////////////
+
+	private final static String SERVICE_TYPE = "serviceType";
+	
+	public void setServiceType(String value)
+	{
+		getServiceNode().setNode(SERVICE_TYPE, value);
+	}
+
+	public String getServiceType()
+	{
+		return getServiceNode().getNodeValue(SERVICE_TYPE);
+	}
+
+	////////////////////////////////////////////////
+	//	serviceID
+	////////////////////////////////////////////////
+
+	private final static String SERVICE_ID = "serviceId";
+	
+	public void setServiceID(String value)
+	{
+		getServiceNode().setNode(SERVICE_ID, value);
+	}
+
+	public String getServiceID()
+	{
+		return getServiceNode().getNodeValue(SERVICE_ID);
+	}
+
 	////////////////////////////////////////////////
 	//	isURL
 	////////////////////////////////////////////////
@@ -213,73 +260,73 @@ public class Service
 		return false;
 	}
 	
-	////////////////////////////////////////////////
-	//	SCPDURL
-	////////////////////////////////////////////////
-
-	private final static String SCPDURL = "SCPDURL";
-	
-	public void setSCPDURL(String value)
-	{
-		getServiceNode().setNode(SCPDURL, value);
-	}
-
-	public String getSCPDURL()
-	{
-		return getServiceNode().getNodeValue(SCPDURL);
-	}
-
+	////////////////////////////////////////////////
+	//	SCPDURL
+	////////////////////////////////////////////////
+
+	private final static String SCPDURL = "SCPDURL";
+	
+	public void setSCPDURL(String value)
+	{
+		getServiceNode().setNode(SCPDURL, value);
+	}
+
+	public String getSCPDURL()
+	{
+		return getServiceNode().getNodeValue(SCPDURL);
+	}
+
 	public boolean isSCPDURL(String url)
 	{
 		return isURL(getSCPDURL(), url);
 	}
 	
-	////////////////////////////////////////////////
-	//	controlURL
-	////////////////////////////////////////////////
-
-	private final static String CONTROL_URL = "controlURL";
-	
-	public void setControlURL(String value)
-	{
-		getServiceNode().setNode(CONTROL_URL, value);
-	}
-
-	public String getControlURL()
-	{
-		return getServiceNode().getNodeValue(CONTROL_URL);
-	}
+	////////////////////////////////////////////////
+	//	controlURL
+	////////////////////////////////////////////////
+
+	private final static String CONTROL_URL = "controlURL";
+	
+	public void setControlURL(String value)
+	{
+		getServiceNode().setNode(CONTROL_URL, value);
+	}
+
+	public String getControlURL()
+	{
+		return getServiceNode().getNodeValue(CONTROL_URL);
+	}
 
 	public boolean isControlURL(String url)
 	{
 		return isURL(getControlURL(), url);
 	}
-
-	////////////////////////////////////////////////
-	//	eventSubURL
-	////////////////////////////////////////////////
-
-	private final static String EVENT_SUB_URL = "eventSubURL";
-	
-	public void setEventSubURL(String value)
-	{
-		getServiceNode().setNode(EVENT_SUB_URL, value);
-	}
-
-	public String getEventSubURL()
-	{
-		return getServiceNode().getNodeValue(EVENT_SUB_URL);
-	}
+
+	////////////////////////////////////////////////
+	//	eventSubURL
+	////////////////////////////////////////////////
+
+	private final static String EVENT_SUB_URL = "eventSubURL";
+	
+	public void setEventSubURL(String value)
+	{
+		getServiceNode().setNode(EVENT_SUB_URL, value);
+	}
+
+	public String getEventSubURL()
+	{
+		return getServiceNode().getNodeValue(EVENT_SUB_URL);
+	}
 
 	public boolean isEventSubURL(String url)
 	{
 		return isURL(getEventSubURL(), url);
 	}
-	
-	////////////////////////////////////////////////
-	//	SCPD node
-	////////////////////////////////////////////////
-
+	
+	////////////////////////////////////////////////
+	//	SCPD node
+	////////////////////////////////////////////////
+
 	public boolean loadSCPD(String scpdStr) throws InvalidDescriptionException
 	{
 		try {
@@ -306,70 +353,101 @@ public class Service
 		data.setSCPDNode(scpdNode);
 		return true;
 	}
+
+	/**
+	 * @since 1.8.0 
+	 */
+	public boolean loadSCPD(InputStream input) throws ParserException
+	{
+		Parser parser = UPnP.getXMLParser();
+		Node scpdNode = parser.parse(input);
+		if (scpdNode == null)
+			return false;
+		ServiceData data = getServiceData();
+		data.setSCPDNode(scpdNode);
+		return true;
+	}
+	
 	
-	private Node getSCPDNode(URL scpdUrl) throws ParserException
-	{
+    public void setDescriptionURL(String value)
+    {
+            getServiceData().setDescriptionURL(value);
+    }
+
+    public String getDescriptionURL()
+    {
+            return getServiceData().getDescriptionURL();
+    }
+	
+	
+	private Node getSCPDNode(URL scpdUrl) throws ParserException
+	{
 		Parser parser = UPnP.getXMLParser();
-		return parser.parse(scpdUrl);
+		return parser.parse(scpdUrl);
 	}
-	
+	
 	private Node getSCPDNode(File scpdFile) throws ParserException
 	{
 		Parser parser = UPnP.getXMLParser();
 		return parser.parse(scpdFile);
 	}
-
-	private Node getSCPDNode()
-	{
-		ServiceData data = getServiceData();
-		Node scpdNode = data.getSCPDNode();
-		if (scpdNode != null)
-			return scpdNode;
-		
-		String scpdURLStr = getSCPDURL();
-		try {
-			URL scpdUrl = new URL(scpdURLStr);
-			scpdNode = getSCPDNode(scpdUrl);
-		}
-		catch (Exception e1) {
-			Device rootDev = getRootDevice();
-			String urlBaseStr = rootDev.getURLBase();
-			// Thanks for Steven Yen (2003/09/03)
-			if (urlBaseStr == null || urlBaseStr.length() <= 0) {
-				String location = rootDev.getLocation();
-				String locationHost = HTTP.getHost(location);
-				int locationPort = HTTP.getPort(location);
-				urlBaseStr = HTTP.getRequestHostURL(locationHost, locationPort);
-			}
-			scpdURLStr = HTTP.toRelativeURL(scpdURLStr);
-			String newScpdURLStr = urlBaseStr + scpdURLStr;
-			try {
-				URL newScpdURL = new URL(newScpdURLStr);
-				scpdNode = getSCPDNode(newScpdURL);
-			}
-			catch (Exception e2) {
-				newScpdURLStr = HTTP.getAbsoluteURL(urlBaseStr, scpdURLStr);
-				try {
-					URL newScpdURL = new URL(newScpdURLStr);
-					scpdNode = getSCPDNode(newScpdURL);
-				}
-				catch (Exception e3) {
-					newScpdURLStr = rootDev.getDescriptionFilePath() + scpdURLStr;
-					try {
-						scpdNode = getSCPDNode(new File(newScpdURLStr));
-					}
-					catch (Exception e4) {
-						Debug.warning(e4);
-					}
+
+	private Node getSCPDNode()
+	{
+		ServiceData data = getServiceData();
+		Node scpdNode = data.getSCPDNode();
+		if (scpdNode != null)
+			return scpdNode;
+		
+		// Thanks for Jaap (Sep 18, 2010)
+		Device rootDev = getRootDevice();
+		if (rootDev == null)
+			return null;
+		
+		String scpdURLStr = getSCPDURL();
+
+		// Thanks for Robin V. (Sep 18, 2010)
+		String rootDevPath = rootDev.getDescriptionFilePath();
+		if(rootDevPath!=null) {
+			File f;
+			f = new File(rootDevPath.concat(scpdURLStr));
+		
+			if(f.exists()) {
+				try {
+					scpdNode = getSCPDNode(f);
+				} catch (ParserException e) {
+					// TODO Auto-generated catch block
+					e.printStackTrace();
+				}
+				if(scpdNode!=null) {
+					data.setSCPDNode(scpdNode);
+					return scpdNode;
 				}
-			}
-		}
-
-		data.setSCPDNode(scpdNode);
-		
-		return scpdNode;
-	}
-
+			}
+		}
+
+		try {
+			URL scpdUrl = new URL(rootDev.getAbsoluteURL(scpdURLStr));
+			scpdNode = getSCPDNode(scpdUrl);		
+			if (scpdNode != null) {
+				data.setSCPDNode(scpdNode);
+				return scpdNode;
+			}
+		}
+		catch (Exception e) {}
+		
+		String newScpdURLStr = rootDev.getDescriptionFilePath() + HTTP.toRelativeURL(scpdURLStr);
+		try {
+			scpdNode = getSCPDNode(new File(newScpdURLStr));
+			return scpdNode;
+		}
+		catch (Exception e) {
+			Debug.warning(e);
+		}
+		
+		return null;
+	}
+
 	public byte[] getSCPDData()
 	{
 		Node scpdNode = getSCPDNode();
@@ -383,30 +461,29 @@ public class Service
 		return desc.getBytes();
 	}
 	
-	////////////////////////////////////////////////
-	//	actionList
-	////////////////////////////////////////////////
-
-	public ActionList getActionList()
-	{
-		ActionList actionList = new ActionList();
-		Node scdpNode = getSCPDNode();
-		if (scdpNode == null)
-			return actionList;
-		Node actionListNode = scdpNode.getNode(ActionList.ELEM_NAME);
-		if (actionListNode == null)
-			return actionList;
-		Node serviceNode = getServiceNode();
-		int nNode = actionListNode.getNNodes();
-		for (int n=0; n<nNode; n++) {
-			Node node = actionListNode.getNode(n);
-			if (Action.isActionNode(node) == false)
-				continue;
-			Action action = new Action(serviceNode, node);
-			actionList.add(action);
-		} 
-		return actionList;
-	}
+	////////////////////////////////////////////////
+	//	actionList
+	////////////////////////////////////////////////
+
+	public ActionList getActionList()
+	{
+		ActionList actionList = new ActionList();
+		Node scdpNode = getSCPDNode();
+		if (scdpNode == null)
+			return actionList;
+		Node actionListNode = scdpNode.getNode(ActionList.ELEM_NAME);
+		if (actionListNode == null)
+			return actionList;
+		int nNode = actionListNode.getNNodes();
+		for (int n=0; n<nNode; n++) {
+			Node node = actionListNode.getNode(n);
+			if (Action.isActionNode(node) == false)
+				continue;
+			Action action = new Action(serviceNode, node);
+			actionList.add(action);
+		} 
+		return actionList;
+	}
 
 	public Action getAction(String actionName)
 	{
@@ -422,28 +499,44 @@ public class Service
 		}
 		return null;
 	}
-	
-	////////////////////////////////////////////////
-	//	serviceStateTable
-	////////////////////////////////////////////////
-
-	public ServiceStateTable getServiceStateTable()
-	{
-		ServiceStateTable stateTable = new ServiceStateTable();
-		Node stateTableNode = getSCPDNode().getNode(ServiceStateTable.ELEM_NAME);
-		if (stateTableNode == null)
+	
+	public void addAction(Action a){
+		Iterator i = a.getArgumentList().iterator();
+		while (i.hasNext()) {
+			Argument arg = (Argument) i.next();
+			arg.setService(this);
+		}
+
+		Node scdpNode = getSCPDNode();
+		Node actionListNode = scdpNode.getNode(ActionList.ELEM_NAME);
+		if (actionListNode == null){			
+			actionListNode = new Node(ActionList.ELEM_NAME);
+			scdpNode.addNode(actionListNode);
+		}
+		actionListNode.addNode(a.getActionNode());
+	}
+	
+	////////////////////////////////////////////////
+	//	serviceStateTable
+	////////////////////////////////////////////////
+
+	public ServiceStateTable getServiceStateTable()
+	{
+		ServiceStateTable stateTable = new ServiceStateTable();
+		Node stateTableNode = getSCPDNode().getNode(ServiceStateTable.ELEM_NAME);
+		if (stateTableNode == null)
 			return stateTable;
-		Node serviceNode = getServiceNode();
-		int nNode = stateTableNode.getNNodes();
-		for (int n=0; n<nNode; n++) {
-			Node node = stateTableNode.getNode(n);
-			if (StateVariable.isStateVariableNode(node) == false)
-				continue;
-			StateVariable serviceVar = new StateVariable(serviceNode, node);
-			stateTable.add(serviceVar);
-		} 
-		return stateTable;
-	}
+		Node serviceNode = getServiceNode();
+		int nNode = stateTableNode.getNNodes();
+		for (int n=0; n<nNode; n++) {
+			Node node = stateTableNode.getNode(n);
+			if (StateVariable.isStateVariableNode(node) == false)
+				continue;
+			StateVariable serviceVar = new StateVariable(serviceNode, node);
+			stateTable.add(serviceVar);
+		} 
+		return stateTable;
+	}
 
 	public StateVariable getStateVariable(String name)
 	{
@@ -657,6 +750,8 @@ public class Service
 			subs[n] = subList.getSubscriber(n);
 		for (int n=0; n<subListCnt; n++) {
 			Subscriber sub = subs[n];
+			if (sub == null)
+				continue;
 			if (sub.isExpired() == true)
 				removeSubscriber(sub);
 		}
@@ -668,6 +763,8 @@ public class Service
 			subs[n] = subList.getSubscriber(n);
 		for (int n=0; n<subListCnt; n++) {
 			Subscriber sub = subs[n];
+			if (sub == null)
+				continue;
 			if (notify(sub, stateVar) == false) {
 				/* Don't remove for NMPR specification.
 				removeSubscriber(sub);
@@ -744,4 +841,47 @@ public class Service
 			action.setActionListener(listener);
 		}
 	}
+
+	/**
+	 * Add the StateVariable to the service.<br>
+	 * <br>
+	 * Note: This method should be used to create a dynamic<br>
+	 * Device withtout writing any XML that describe the device<br>.
+	 * <br>
+	 * Note: that no control for duplicate StateVariable is done.
+	 * 
+	 * @param var StateVariable that will be added
+	 * 
+	 * @author Stefano "Kismet" Lenzi - kismet-sl@users.sourceforge.net  - 2005
+	 */
+	public void addStateVariable(StateVariable var) {
+		//TODO Some test are done not stable
+		Node stateTableNode = getSCPDNode().getNode(ServiceStateTable.ELEM_NAME);
+		if (stateTableNode == null){
+			stateTableNode = new Node(ServiceStateTable.ELEM_NAME);
+			/*
+			 * Force the node <serviceStateTable> to be the first node inside <scpd>
+			 */
+			//getSCPDNode().insertNode(stateTableNode,0);
+			getSCPDNode().addNode(stateTableNode);		
+		}
+		var.setServiceNode(getServiceNode());
+		stateTableNode.addNode(var.getStateVariableNode());
+	}
+
+	////////////////////////////////////////////////
+	//	userData
+	////////////////////////////////////////////////
+
+	private Object userData = null; 
+	
+	public void setUserData(Object data) 
+	{
+		userData = data;
+	}
+
+	public Object getUserData() 
+	{
+		return userData;
+	}
 }
diff --git a/router/java/src/org/cybergarage/upnp/ServiceList.java b/router/java/src/org/cybergarage/upnp/ServiceList.java
index 0a23154ebffc66331ede6cef2e566fe863a5dba3..55dee276f039372cabd56d56f5807fb67857090a 100644
--- a/router/java/src/org/cybergarage/upnp/ServiceList.java
+++ b/router/java/src/org/cybergarage/upnp/ServiceList.java
@@ -17,7 +17,7 @@
 
 package org.cybergarage.upnp;
 
-import java.util.*;
+import java.util.Vector;
 
 public class ServiceList extends Vector 
 {
@@ -25,7 +25,6 @@ public class ServiceList extends Vector
 	//	Constants
 	////////////////////////////////////////////////
 	
-	private static final long serialVersionUID = 6372904993975135597L;
 	public final static String ELEM_NAME = "serviceList";
 
 	////////////////////////////////////////////////
diff --git a/router/java/src/org/cybergarage/upnp/ServiceStateTable.java b/router/java/src/org/cybergarage/upnp/ServiceStateTable.java
index 0f1b7eed3a86cc8d88a112581747c4dce94ce3c8..daddfbf861713e5c84e9e3f11851d58876bf941d 100644
--- a/router/java/src/org/cybergarage/upnp/ServiceStateTable.java
+++ b/router/java/src/org/cybergarage/upnp/ServiceStateTable.java
@@ -15,7 +15,7 @@
 
 package org.cybergarage.upnp;
 
-import java.util.*;
+import java.util.Vector;
 
 public class ServiceStateTable extends Vector 
 {
@@ -23,7 +23,6 @@ public class ServiceStateTable extends Vector
 	//	Constants
 	////////////////////////////////////////////////
 	
-	private static final long serialVersionUID = 7626909231678469365L;
 	public final static String ELEM_NAME = "serviceStateTable";
 
 	////////////////////////////////////////////////
diff --git a/router/java/src/org/cybergarage/upnp/StateVariable.java b/router/java/src/org/cybergarage/upnp/StateVariable.java
index 45b5e301ad51a75c1e5ac5d19534def0d7dcf01b..9016652d64aa76f79d707d470a8e36ffdeef09f2 100644
--- a/router/java/src/org/cybergarage/upnp/StateVariable.java
+++ b/router/java/src/org/cybergarage/upnp/StateVariable.java
@@ -5,10 +5,10 @@
 *	Copyright (C) Satoshi Konno 2002
 *
 *	File: StateVariable.java
-*
-*	Revision;
-*
-*	12/06/02
+*
+*	Revision;
+*
+*	12/06/02
 *		- first revision.
 *	06/17/03
 *		- Added setSendEvents(), isSendEvents().
@@ -39,36 +39,45 @@
 *		- Fixed setValue() to compare only when the current value is not null.
 *	02/28/05
 *		- Changed getAllowedValueList() to use AllowedValue instead of String as the member.
+*	04/12/06
+*		- Added setUserData() and getUserData() to set a user original data object.
 *	
 ******************************************************************/
-
-package org.cybergarage.upnp;
-
-import org.cybergarage.xml.*;
-import org.cybergarage.util.*;
-
-import org.cybergarage.upnp.control.*;
-import org.cybergarage.upnp.xml.*;
-
-public class StateVariable extends NodeData
-{
-	////////////////////////////////////////////////
-	//	Constants
-	////////////////////////////////////////////////
-	
-	public final static String ELEM_NAME = "stateVariable";
-
-	////////////////////////////////////////////////
-	//	Member
-	////////////////////////////////////////////////
-
-	private Node stateVariableNode;
+
+package org.cybergarage.upnp;
+import java.util.Iterator;
+
+import org.cybergarage.upnp.control.QueryListener;
+import org.cybergarage.upnp.control.QueryRequest;
+import org.cybergarage.upnp.control.QueryResponse;
+import org.cybergarage.upnp.xml.NodeData;
+import org.cybergarage.upnp.xml.StateVariableData;
+import org.cybergarage.util.Debug;
+import org.cybergarage.xml.Node;
+
+public class StateVariable extends NodeData
+{
+	////////////////////////////////////////////////
+	//	Constants
+	////////////////////////////////////////////////
+	
+	public final static String ELEM_NAME = "stateVariable";
+
+	////////////////////////////////////////////////
+	//	Member
+	////////////////////////////////////////////////
+
+	private Node stateVariableNode;
 	private Node serviceNode;
 
 	public Node getServiceNode()
 	{
 		return serviceNode;
 	}
+	
+	void setServiceNode(Node n){
+		serviceNode=n;
+	}
 
 	public Service getService()
 	{
@@ -77,68 +86,68 @@ public class StateVariable extends NodeData
 			return null;
 		return new Service(serviceNode);
 	}
-
-	public Node getStateVariableNode()
-	{
-		return stateVariableNode;
-	}
-	
-	////////////////////////////////////////////////
-	//	Constructor
-	////////////////////////////////////////////////
-
+
+	public Node getStateVariableNode()
+	{
+		return stateVariableNode;
+	}
+	
+	////////////////////////////////////////////////
+	//	Constructor
+	////////////////////////////////////////////////
+
 	public StateVariable()
 	{
 		this.serviceNode = null;
-		this.stateVariableNode = new Node();
+		this.stateVariableNode = new Node(ELEM_NAME);
 	}
 	
-	public StateVariable(Node serviceNode, Node stateVarNode)
-	{
-		this.serviceNode = serviceNode;
-		this.stateVariableNode = stateVarNode;
-	}
-
-	////////////////////////////////////////////////
-	//	isStateVariableNode
-	////////////////////////////////////////////////
-
-	public static boolean isStateVariableNode(Node node)
-	{
-		return StateVariable.ELEM_NAME.equals(node.getName());
-	}
-
-	////////////////////////////////////////////////
-	//	name
-	////////////////////////////////////////////////
-
-	private final static String NAME = "name";
-	
-	public void setName(String value)
-	{
-		getStateVariableNode().setNode(NAME, value);
-	}
-
-	public String getName()
-	{
-		return getStateVariableNode().getNodeValue(NAME);
-	}
-
-	////////////////////////////////////////////////
-	//	dataType
-	////////////////////////////////////////////////
-
-	private final static String DATATYPE = "dataType";
-	
-	public void setDataType(String value)
-	{
-		getStateVariableNode().setNode(DATATYPE, value);
-	}
-
-	public String getDataType()
-	{
-		return getStateVariableNode().getNodeValue(DATATYPE);
-	}
+	public StateVariable(Node serviceNode, Node stateVarNode)
+	{
+		this.serviceNode = serviceNode;
+		this.stateVariableNode = stateVarNode;
+	}
+
+	////////////////////////////////////////////////
+	//	isStateVariableNode
+	////////////////////////////////////////////////
+
+	public static boolean isStateVariableNode(Node node)
+	{
+		return StateVariable.ELEM_NAME.equals(node.getName());
+	}
+
+	////////////////////////////////////////////////
+	//	name
+	////////////////////////////////////////////////
+
+	private final static String NAME = "name";
+	
+	public void setName(String value)
+	{
+		getStateVariableNode().setNode(NAME, value);
+	}
+
+	public String getName()
+	{
+		return getStateVariableNode().getNodeValue(NAME);
+	}
+
+	////////////////////////////////////////////////
+	//	dataType
+	////////////////////////////////////////////////
+
+	private final static String DATATYPE = "dataType";
+	
+	public void setDataType(String value)
+	{
+		getStateVariableNode().setNode(DATATYPE, value);
+	}
+
+	public String getDataType()
+	{
+		return getStateVariableNode().getNodeValue(DATATYPE);
+	}
 
 	////////////////////////////////////////////////
 	// dataType
@@ -238,7 +247,7 @@ public class StateVariable extends NodeData
 		AllowedValueList valueList= new AllowedValueList();
 		Node valueListNode = getStateVariableNode().getNode(AllowedValueList.ELEM_NAME);
 		if (valueListNode == null)
-			return valueList;
+			return null;
 		int nNode = valueListNode.getNNodes();
 		for (int n=0; n<nNode; n++) {
 			Node node = valueListNode.getNode(n);
@@ -250,10 +259,41 @@ public class StateVariable extends NodeData
 		return valueList;
 	}
 
+	/**
+	 * This method ovverride the value of the AllowedValueList Node<br>
+	 * of this object. <br>
+	 * <br>
+	 * Note: This method should be used to create a dynamic<br>
+	 * Device withtout writing any XML that describe the device<br>.
+	 * <br>
+	 * Note2: The enforce the constraint of the SCPD rule the<br>
+	 * AllowedValueList and AllowedValueRange are mutal exclusive<br>
+	 * the last set will be the only present<br>
+	 * 
+	 * @param avl The new AllowedValueList
+	 * 
+	 * @author Stefano "Kismet" Lenzi - kismet-sl@users.sourceforge.net  - 2005
+	 */
+	public void setAllowedValueList(AllowedValueList avl) {
+		//TODO Some test done not stable 	
+		getStateVariableNode().removeNode(AllowedValueList.ELEM_NAME);
+		getStateVariableNode().removeNode(AllowedValueRange.ELEM_NAME);
+		Node n = new Node(AllowedValueList.ELEM_NAME);
+		Iterator i=avl.iterator();
+		while (i.hasNext()) {
+			AllowedValue av = (AllowedValue) i.next();
+			//n.addNode(new Node(AllowedValue.ELEM_NAME,av.getValue())); wrong!
+			n.addNode(av.getAllowedValueNode());						//better (twa)
+		}
+		getStateVariableNode().addNode(n);		
+		
+	}
+
+	
 	public boolean hasAllowedValueList()
 	{
 		AllowedValueList valueList = getAllowedValueList();
-		return (0 < valueList.size()) ? true : false;
+		return (valueList != null) ? true : false;
 	}
 	
 	////////////////////////////////////////////////
@@ -267,6 +307,29 @@ public class StateVariable extends NodeData
 			return null;
 		return new AllowedValueRange(valueRangeNode);
 	}
+	
+	/**
+	 * This method ovverride the value of the AllowedValueRange Node<br>
+	 * of this object. <br>
+	 * <br>
+	 * Note: This method should be used to create a dynamic<br>
+	 * Device withtout writing any XML that describe the device<br>.
+	 * <br>
+	 * Note2: The enforce the constraint of the SCPD rule the<br>
+	 * AllowedValueList and AllowedValueRange are mutal exclusive<br>
+	 * the last set will be the only present<br>
+	 * 
+	 * @param avr The new AllowedValueRange
+	 * 
+	 * @author Stefano "Kismet" Lenzi - kismet-sl@users.sourceforge.net  - 2005
+	 */	
+	public void setAllowedValueRange(AllowedValueRange avr){
+		//TODO Some test done not stable
+		getStateVariableNode().removeNode(AllowedValueList.ELEM_NAME);
+		getStateVariableNode().removeNode(AllowedValueRange.ELEM_NAME);
+		getStateVariableNode().addNode(avr.getAllowedValueRangeNode());
+		
+	}
 
 	public boolean hasAllowedValueRange()
 	{
@@ -371,4 +434,44 @@ public class StateVariable extends NodeData
 	{
 		return upnpStatus;
 	}
+
+	private static final String DEFAULT_VALUE = "defaultValue";
+	////////////////////////////////////////////////
+	/**
+	 * Get the value of DefaultValue of this StateVariable
+	 * 
+	 * @author Stefano Lenzi kismet-sl@users.sourceforge.net
+	 */
+	public String getDefaultValue() {
+		return getStateVariableNode().getNodeValue(DEFAULT_VALUE);
+	}
+
+	/**
+	 * This method ovverride the value of the DefaultValue of this object. <br>
+	 * <br>
+	 * Note: This method should be used to create a dynamic<br>
+	 * Device withtout writing any XML that describe the device<br>.
+	 * 
+	 * @param value The new String value
+	 * 
+	 * @author Stefano Lenzi kismet-sl@users.sourceforge.net
+	 */	
+	public void setDefaultValue(String value){
+		getStateVariableNode().setNode(DEFAULT_VALUE,value);
+	}
+	////////////////////////////////////////////////
+	//	userData
+	////////////////////////////////////////////////
+
+	private Object userData = null; 
+	
+	public void setUserData(Object data) 
+	{
+		userData = data;
+	}
+
+	public Object getUserData() 
+	{
+		return userData;
+	}
 }
diff --git a/router/java/src/org/cybergarage/upnp/UPnP.java b/router/java/src/org/cybergarage/upnp/UPnP.java
index e27a5db40e03669ca1698597b6dac007b04f9730..caa00f5656d313c35dc039aaa22da12ccef91b6f 100644
--- a/router/java/src/org/cybergarage/upnp/UPnP.java
+++ b/router/java/src/org/cybergarage/upnp/UPnP.java
@@ -5,53 +5,63 @@
 *	Copyright (C) Satoshi Konno 2002-2003
 *
 *	File: UPnP.java
-*
-*	Revision;
-*
-*	11/18/02
-*		- first revision.
+*
+*	Revision;
+*
+*	11/18/02
+*		- first revision.
 *	05/13/03
 *		- Added support for IPv6 and loopback address.
 *	12/26/03
 *		- Added support for XML Parser
 *	06/18/03
 *		- Added INMPR03 and INMPR03_VERSION.
+*	04/14/06
+*		- Added some functios about time-to-live, and the default value is 4.
+*	05/11/09
+*		- Changed loadDefaultXMLParser() to load org.cybergarage.xml.parser.XmlPullParser at first.
 *	
 ******************************************************************/
-
-package org.cybergarage.upnp;
-
-import org.cybergarage.upnp.ssdp.*;
-//import org.cybergarage.util.*;
-import org.cybergarage.xml.*;
-import org.cybergarage.xml.parser.*;
-import org.cybergarage.soap.*;
-import org.cybergarage.net.*;
-
-public class UPnP
-{
-	////////////////////////////////////////////////
-	//	Constants
-	////////////////////////////////////////////////
-	
-	public final static String NAME = "CyberLink";
-	public final static String VERSION = "1.7";
+
+package org.cybergarage.upnp;
+
+import org.cybergarage.net.HostInterface;
+import org.cybergarage.soap.SOAP;
+import org.cybergarage.upnp.ssdp.SSDP;
+import org.cybergarage.util.Debug;
+import org.cybergarage.xml.Parser;
+
+public class UPnP
+{
+	////////////////////////////////////////////////
+	//	Constants
+	////////////////////////////////////////////////
+	
+	/**
+	 * Name of the system properties used to identifies the default XML Parser.<br>
+	 * The value of the properties MUST BE the fully qualified class name of<br>
+	 * XML Parser which CyberLink should use. 
+	 */
+	public final static String XML_CLASS_PROPERTTY="cyberlink.upnp.xml.parser";
+	
+	public final static String NAME = "CyberLinkJava";
+	public final static String VERSION = "1.8";
 
 	public final static int SERVER_RETRY_COUNT = 100;
 	public final static int DEFAULT_EXPIRED_DEVICE_EXTRA_TIME = 60;
 
-	public final static String getServerName()
+	public final static String getServerName()
 	{
 		String osName = System.getProperty("os.name");
 		String osVer = System.getProperty("os.version");
-		return osName + "/"  + osVer + " UPnP/1.0 " + NAME + "/" + VERSION;
-	}
+		return osName + "/"  + osVer + " UPnP/1.0 " + NAME + "/" + VERSION;
+	}
 	
 	public final static String INMPR03 = "INMPR03";
 	public final static String INMPR03_VERSION = "1.0";
 	public final static int INMPR03_DISCOVERY_OVER_WIRELESS_COUNT = 4;
 
-	public final static String XML_DECLARATION = "<?xml version=\"1.0\"?>";
+	public final static String XML_DECLARATION = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"; 
 	
 	////////////////////////////////////////////////
 	//	Enable / Disable
@@ -193,8 +203,67 @@ public class UPnP
 	
 	public final static Parser getXMLParser()
 	{
+		if(xmlParser == null){
+			xmlParser = loadDefaultXMLParser();
+			if(xmlParser == null)
+				throw new RuntimeException("No XML parser defined. And unable to laod any. \n" +
+						"Try to invoke UPnP.setXMLParser before UPnP.getXMLParser");			
+			SOAP.setXMLParser(xmlParser);
+		}
 		return xmlParser;
 	}
+
+	/**
+	 * This method loads the default XML Parser using the following behavior:
+	 *  - First if present loads the parsers specified by the system property {@link UPnP#XML_CLASS_PROPERTTY}<br>
+	 *  - Second by a fall-back technique, it tries to load the XMLParser from one<br>
+	 *  of the following classes: {@link JaxpParser}, {@link kXML2Parser}, {@link XercesParser}
+	 * 
+	 * @return {@link Parser} which has been loaded successuflly or null otherwise
+	 * 
+	 * @since 1.8.0
+	 */
+	private static Parser loadDefaultXMLParser() {
+		Parser parser = null;
+		
+		String[] parserClass = new String[]{
+				System.getProperty(XML_CLASS_PROPERTTY),
+				"org.cybergarage.xml.parser.XmlPullParser",
+				"org.cybergarage.xml.parser.JaxpParser",
+				"org.cybergarage.xml.parser.kXML2Parser",
+				"org.cybergarage.xml.parser.XercesParser"
+		};
+		
+		for (int i = 0; i < parserClass.length; i++) {
+			if(parserClass[i]==null)
+				continue;
+			try {
+				parser = (Parser) Class.forName(parserClass[i]).newInstance();
+				return parser;
+			} catch (Throwable e) {
+				Debug.warning("Unable to load "+parserClass[i]+" as XMLParser due to "+e);
+			}
+		}
+		return null;
+	}
+	
+	////////////////////////////////////////////////
+	//	TTL
+	////////////////////////////////////////////////	
+
+	public final static int DEFAULT_TTL = 4;
+
+	private static int timeToLive = DEFAULT_TTL;
+	
+	public final static void setTimeToLive(int value)
+	{
+		timeToLive = value;
+	}
+	
+	public final static int getTimeToLive()
+	{
+		return timeToLive;
+	}
 	
 	////////////////////////////////////////////////
 	//	Initialize
@@ -206,16 +275,14 @@ public class UPnP
 		// Interface Option
 		////////////////////////////
 		
-		setXMLParser(new JaxpParser());
 		//setXMLParser(new kXML2Parser());
 		
+		
 		////////////////////////////
-		// Interface Option
+		// TimeToLive
 		////////////////////////////
-		/*
-		if (HostInterface.hasIPv6Addresses() == true)
-			setEnable(USE_ONLY_IPV6_ADDR);
-		*/
+		
+		setTimeToLive(DEFAULT_TTL);
 		
 		////////////////////////////
 		// Debug Option
diff --git a/router/java/src/org/cybergarage/upnp/UPnPStatus.java b/router/java/src/org/cybergarage/upnp/UPnPStatus.java
index f00ba69210652c72f721e0e38d7d52abc5dee83a..0244a6b1df9d3f73a76e714e213b8e950a41101d 100644
--- a/router/java/src/org/cybergarage/upnp/UPnPStatus.java
+++ b/router/java/src/org/cybergarage/upnp/UPnPStatus.java
@@ -16,6 +16,7 @@
 ******************************************************************/
 
 package org.cybergarage.upnp;
+import org.cybergarage.http.HTTPStatus;
 
 public class UPnPStatus
 {
@@ -39,8 +40,8 @@ public class UPnPStatus
 		case INVALID_VAR: return "Invalid Var";
 		case PRECONDITION_FAILED: return "Precondition Failed";
 		case ACTION_FAILED: return "Action Failed";
+	        default: return HTTPStatus.code2String(code);
 		}
-		return "";
 	}
 	
 	////////////////////////////////////////////////
diff --git a/router/java/src/org/cybergarage/upnp/control/ControlResponse.java b/router/java/src/org/cybergarage/upnp/control/ControlResponse.java
index 36fcab89d4e88140e1fedc1c660c8ae76ccf8695..b764a6ae7324586f5479554d74c90ee943ee7e78 100644
--- a/router/java/src/org/cybergarage/upnp/control/ControlResponse.java
+++ b/router/java/src/org/cybergarage/upnp/control/ControlResponse.java
@@ -102,6 +102,11 @@ public class ControlResponse extends SOAPResponse
 		return faultNode;
 	}
 	
+	private Node createFaultResponseNode(int errCode)
+	{
+		return createFaultResponseNode(errCode, UPnPStatus.code2String(errCode));
+	}
+
 	////////////////////////////////////////////////
 	//	UPnP Error
 	////////////////////////////////////////////////
diff --git a/router/java/src/org/cybergarage/upnp/control/QueryRequest.java b/router/java/src/org/cybergarage/upnp/control/QueryRequest.java
index d222051fc55de62dbbaac40dec8e7d4b62de1f9d..ad8b437e5a2d5a198a519eabb71d659a787cde5b 100644
--- a/router/java/src/org/cybergarage/upnp/control/QueryRequest.java
+++ b/router/java/src/org/cybergarage/upnp/control/QueryRequest.java
@@ -74,6 +74,8 @@ public class QueryRequest extends ControlRequest
 	{
 		Service service = stateVar.getService();		
 		
+		String ctrlURL = service.getControlURL();
+
 		setRequestHost(service);
 
 		setEnvelopeNode(SOAP.createEnvelopeBodyNode());
diff --git a/router/java/src/org/cybergarage/upnp/device/InvalidDescriptionException.java b/router/java/src/org/cybergarage/upnp/device/InvalidDescriptionException.java
index a08b3a39324c5f3cce6bc9cef8443df3354947c9..5145245895ad6831b845da44c58b0bf80618344a 100644
--- a/router/java/src/org/cybergarage/upnp/device/InvalidDescriptionException.java
+++ b/router/java/src/org/cybergarage/upnp/device/InvalidDescriptionException.java
@@ -19,8 +19,6 @@ import java.io.*;
 
 public class InvalidDescriptionException extends Exception
 {
-	private static final long serialVersionUID = -3144583349586910509L;
-
 	public InvalidDescriptionException()
 	{
 		super();
diff --git a/router/java/src/org/cybergarage/upnp/event/NotifyRequest.java b/router/java/src/org/cybergarage/upnp/event/NotifyRequest.java
index c8891e1d21091106f303b148a0afd2258e5c88f3..14bae34e108a012422b4b12eb82e4a53557c7ea5 100644
--- a/router/java/src/org/cybergarage/upnp/event/NotifyRequest.java
+++ b/router/java/src/org/cybergarage/upnp/event/NotifyRequest.java
@@ -115,6 +115,7 @@ public class NotifyRequest extends SOAPRequest
 
 	public boolean setRequest(Subscriber sub, String varName, String value)
 	{
+		String callback = sub.getDeliveryURL();
 		String sid = sub.getSID();
 		long notifyCnt = sub.getNotifyCount();
 		String host = sub.getDeliveryHost();
@@ -154,6 +155,19 @@ public class NotifyRequest extends SOAPRequest
 		return propSetNode;
 	}
 	
+	private Node getVariableNode()
+	{
+		Node rootNode = getEnvelopeNode();
+		if (rootNode == null)
+			return null;
+		if (rootNode.hasNodes() == false)
+			return null;
+		Node propNode = rootNode.getNode(0);
+		if (propNode.hasNodes() == false)
+			return null;
+		return propNode.getNode(0);
+	}
+
 	// Thanks for Giordano Sassaroli <sassarol@cefriel.it> (09/08/03)
 	private Property getProperty(Node varNode) 
 	{
diff --git a/router/java/src/org/cybergarage/upnp/event/PropertyList.java b/router/java/src/org/cybergarage/upnp/event/PropertyList.java
index f890bb3afa890940d958709ca7d5eed3a6be2770..f2d8a22a7599ffb52c635506d01ac73c0b6239f1 100644
--- a/router/java/src/org/cybergarage/upnp/event/PropertyList.java
+++ b/router/java/src/org/cybergarage/upnp/event/PropertyList.java
@@ -23,7 +23,6 @@ public class PropertyList extends Vector
 	//	Constants
 	////////////////////////////////////////////////
 	
-	private static final long serialVersionUID = 8718064210738306226L;
 	public final static String ELEM_NAME = "PropertyList";
 
 	////////////////////////////////////////////////
diff --git a/router/java/src/org/cybergarage/upnp/event/SubscriberList.java b/router/java/src/org/cybergarage/upnp/event/SubscriberList.java
index 0a991204a2cfc95981f052dc3e44bd5a65eee480..63bfdb7865a459c8db73b45b11e54f169be93671 100644
--- a/router/java/src/org/cybergarage/upnp/event/SubscriberList.java
+++ b/router/java/src/org/cybergarage/upnp/event/SubscriberList.java
@@ -25,8 +25,6 @@ public class SubscriberList extends Vector
 	//	Constructor
 	////////////////////////////////////////////////
 	
-	private static final long serialVersionUID = -648427977601494972L;
-
 	public SubscriberList() 
 	{
 	}
diff --git a/router/java/src/org/cybergarage/upnp/event/Subscription.java b/router/java/src/org/cybergarage/upnp/event/Subscription.java
index c03638daad23e6a5906b480aa6e942034618a26b..0685e4fa8b7fae8766d97c729a7b6007696c3c32 100644
--- a/router/java/src/org/cybergarage/upnp/event/Subscription.java
+++ b/router/java/src/org/cybergarage/upnp/event/Subscription.java
@@ -68,6 +68,8 @@ public class Subscription
 	{
 		if (headerValue == null)
 			return "";
+		if (headerValue.startsWith(Subscription.UUID) == false)
+			return headerValue;
 		return headerValue.substring(Subscription.UUID.length(), headerValue.length());
 	}
 	
diff --git a/router/java/src/org/cybergarage/upnp/event/SubscriptionRequest.java b/router/java/src/org/cybergarage/upnp/event/SubscriptionRequest.java
index a3e11cc651b67813131d69c1e60bf2ce71e85a40..4ba930c12c63efb46b9f01173f7bb1b32392513d 100644
--- a/router/java/src/org/cybergarage/upnp/event/SubscriptionRequest.java
+++ b/router/java/src/org/cybergarage/upnp/event/SubscriptionRequest.java
@@ -43,12 +43,12 @@ public class SubscriptionRequest extends HTTPRequest
 	//	Constructor
 	////////////////////////////////////////////////
 
-	public SubscriptionRequest()
-	{
+	public SubscriptionRequest(){
+		setContentLength(0);
 	}
 
-	public SubscriptionRequest(HTTPRequest httpReq)
-	{
+	public SubscriptionRequest(HTTPRequest httpReq){
+		this();
 		set(httpReq);
 	}
 	
diff --git a/router/java/src/org/cybergarage/upnp/ssdp/HTTPMUSocket.java b/router/java/src/org/cybergarage/upnp/ssdp/HTTPMUSocket.java
index 4f6361f59410e412b065c54abab0c4f97ca09e42..57c60d0099db39c3132e6bede355a3fae24a0143 100644
--- a/router/java/src/org/cybergarage/upnp/ssdp/HTTPMUSocket.java
+++ b/router/java/src/org/cybergarage/upnp/ssdp/HTTPMUSocket.java
@@ -5,11 +5,11 @@
 *	Copyright (C) Satoshi Konno 2002-2004
 *
 *	File: HTTPMU.java
-*
-*	Revision;
-*
-*	11/18/02
-*		- first revision.
+*
+*	Revision;
+*
+*	11/18/02
+*		- first revision.
 *	09/03/03
 *		- Changed to open the socket using setReuseAddress().
 *	12/10/03
@@ -19,39 +19,55 @@
 *	11/19/04
 *		- Theo Beisch <theo.beisch@gmx.de>
 *		- Changed send() to set the TTL as 4.
+*	08/23/07
+*		- Thanks for Kazuyuki Shudo
+*		- Changed receive() to throw IOException.
+*	01/10/08
+*		- Changed getLocalAddress() to return a brank string when the ssdpMultiGroup or ssdpMultiIf is null on Android m3-rc37a.
 *	
 ******************************************************************/
-
-package org.cybergarage.upnp.ssdp;
-
-import java.net.*;
-import java.util.*;
-
-import org.cybergarage.http.*;
-import org.cybergarage.util.*;
-
-public class HTTPMUSocket
-{
-	////////////////////////////////////////////////
-	//	Member
-	////////////////////////////////////////////////
-
-	private InetSocketAddress ssdpMultiGroup = null;
-	private MulticastSocket ssdpMultiSock = null;
+
+package org.cybergarage.upnp.ssdp;
+
+import java.net.DatagramPacket;
+import java.net.MulticastSocket;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.NetworkInterface;
+import java.util.Enumeration;
+import java.io.IOException;
+
+import org.cybergarage.http.HTTPRequest;
+import org.cybergarage.upnp.UPnP;
+import org.cybergarage.util.Debug;
+
+// Dummy Class for Android m3-rc37a
+// import org.cybergarage.android.MulticastSocket;
+
+public class HTTPMUSocket
+{
+	////////////////////////////////////////////////
+	//	Member
+	////////////////////////////////////////////////
+
+	private InetSocketAddress ssdpMultiGroup = null;
+	private MulticastSocket ssdpMultiSock = null;
 	private NetworkInterface ssdpMultiIf = null;
-		 	
-	////////////////////////////////////////////////
-	//	Constructor
-	////////////////////////////////////////////////
-	
+	
+	////////////////////////////////////////////////
+	//	Constructor
+	////////////////////////////////////////////////
+	
 	public HTTPMUSocket()
 	{
 	}
 	
-	public HTTPMUSocket(String addr, int port, String bindAddr)
-	{
-		open(addr, port, bindAddr);
-	}
+	public HTTPMUSocket(String addr, int port, String bindAddr)
+	{
+		open(addr, port, bindAddr);
+	}
 
 	protected void finalize()
 	{
@@ -64,6 +80,8 @@ public class HTTPMUSocket
 
 	public String getLocalAddress()
 	{
+		if (ssdpMultiGroup == null || ssdpMultiIf == null)
+			return "";
 		InetAddress mcastAddr = ssdpMultiGroup.getAddress();
 		Enumeration addrs = ssdpMultiIf.getInetAddresses();
 		while (addrs.hasMoreElements()) {
@@ -76,6 +94,34 @@ public class HTTPMUSocket
 		return "";
 	}
 
+	/**
+	 * 
+	 * @return the destination port for multicast packet
+	 * @since 1.8
+	 */
+	public int getMulticastPort(){
+		return ssdpMultiGroup.getPort();
+	}
+	
+	/**
+	 * 
+	 * @return the source port for multicast packet
+	 * @since 1.8
+	 */
+	public int getLocalPort(){
+		return ssdpMultiSock.getLocalPort();
+	}
+	
+	/**
+	 * 
+	 * @return the opened {@link MulticastSocket}
+	 * @since 1.8 
+	 */
+	public MulticastSocket getSocket(){
+		return ssdpMultiSock;
+	}
+	
+	
 	////////////////////////////////////////////////
 	//	MulticastAddr
 	////////////////////////////////////////////////
@@ -90,109 +136,124 @@ public class HTTPMUSocket
 		return getMulticastInetAddress().getHostAddress();
 	}
 	
-	////////////////////////////////////////////////
-	//	open/close
-	////////////////////////////////////////////////
-
-	public boolean open(String addr, int port, String bindAddr)
-	{
-		try {
+	/**
+	 * @param addr {@link String} rappresenting the multicast hostname to join into.
+	 * @param port int rappresenting the port to be use poth as source and destination
+	 * @param bindAddr {@link InetAddress} which identify the hostname of the interface 
+	 * 		to use for sending and recieving multicast packet
+	 */
+	public boolean open(String addr,int port, InetAddress bindAddr){
+		try {
 			ssdpMultiSock = new MulticastSocket(null);
 			ssdpMultiSock.setReuseAddress(true);
 			InetSocketAddress bindSockAddr = new InetSocketAddress(port);
 			ssdpMultiSock.bind(bindSockAddr);
 			ssdpMultiGroup = new InetSocketAddress(InetAddress.getByName(addr), port);
-			ssdpMultiIf = NetworkInterface.getByInetAddress(InetAddress.getByName(bindAddr));
+			ssdpMultiIf = NetworkInterface.getByInetAddress(bindAddr);
 			ssdpMultiSock.joinGroup(ssdpMultiGroup, ssdpMultiIf);
-		}
-		catch (Exception e) {
-			Debug.warning(e);
-			return false;
-		}
-		
-		return true;
-	}
-
-	public boolean close()
-	{
-		if (ssdpMultiSock == null)
-			return true;
-			
-		try {
-			ssdpMultiSock.leaveGroup(ssdpMultiGroup, ssdpMultiIf);
+		}
+		catch (Exception e) {
+			Debug.warning(e);
+			return false;
+		}
+		
+		return true;		
+	}
+	
+	public boolean open(String addr, int port, String bindAddr)
+	{
+		try {
+			return open(addr,port,InetAddress.getByName(bindAddr));
+		}catch (Exception e) {
+			Debug.warning(e);
+			return false;
+		}
+	}
+
+	public boolean close()
+	{
+		if (ssdpMultiSock == null)
+			return true;
+			
+		try {
+			ssdpMultiSock.leaveGroup(ssdpMultiGroup, ssdpMultiIf);
+            ssdpMultiSock.close();
 			ssdpMultiSock = null;
-		}
-		catch (Exception e) {
-			//Debug.warning(e);
-			return false;
-		}
-		
-		return true;
-	}
-
-	////////////////////////////////////////////////
-	//	send
-	////////////////////////////////////////////////
-
-	public boolean send(String msg, String bindAddr, int bindPort)
-	{
+		}
+		catch (Exception e) {
+			//Debug.warning(e);
+			return false;
+		}
+		
+		return true;
+	}
+
+	////////////////////////////////////////////////
+	//	send
+	////////////////////////////////////////////////
+
+	public boolean send(String msg, String bindAddr, int bindPort)
+	{
 		try {
 			MulticastSocket msock;
 			if ((bindAddr) != null && (0 < bindPort)) {
 				msock = new MulticastSocket(null);
 				msock.bind(new InetSocketAddress(bindAddr, bindPort));
-			}
-			else 
+			}else{ 
 				msock = new MulticastSocket();
+			}
 			DatagramPacket dgmPacket = new DatagramPacket(msg.getBytes(), msg.length(), ssdpMultiGroup);
-			// Thnaks for Tho Beisch (11/09/04)
-			msock.setTimeToLive(4);
+			// Thnaks for Theo Beisch (11/09/04)
+			msock.setTimeToLive(UPnP.getTimeToLive());
 			msock.send(dgmPacket);
 			msock.close();
-		}
-		catch (Exception e) {
-			Debug.warning(e);
-			return false;
-		}
-		return true;
-	}
-
-	public boolean send(String msg)
-	{
-		return send(msg, null, -1);
-	}
-
-	////////////////////////////////////////////////
-	//	post (HTTPRequest)
-	////////////////////////////////////////////////
-
-	public boolean post(HTTPRequest req, String bindAddr, int bindPort)
-	{
+		}
+		catch (Exception e) {
+			Debug.warning(e);
+			return false;
+		}
+		return true;
+	}
+
+	public boolean send(String msg)
+	{
+		return send(msg, null, -1);
+	}
+
+	////////////////////////////////////////////////
+	//	post (HTTPRequest)
+	////////////////////////////////////////////////
+
+	public boolean post(HTTPRequest req, String bindAddr, int bindPort)
+	{
 		return send(req.toString(), bindAddr, bindPort);
-	}
+	}
 
 	public boolean post(HTTPRequest req)
 	{
 		return send(req.toString(), null, -1);
 	}
-
-	////////////////////////////////////////////////
-	//	reveive
-	////////////////////////////////////////////////
-
-	public SSDPPacket receive()
-	{
-		byte ssdvRecvBuf[] = new byte[SSDP.RECV_MESSAGE_BUFSIZE];
+
+	////////////////////////////////////////////////
+	//	reveive
+	////////////////////////////////////////////////
+
+	public SSDPPacket receive() throws IOException
+	{
+		byte ssdvRecvBuf[] = new byte[SSDP.RECV_MESSAGE_BUFSIZE];
  		SSDPPacket recvPacket = new SSDPPacket(ssdvRecvBuf, ssdvRecvBuf.length);
 		recvPacket.setLocalAddress(getLocalAddress());
- 		try {
-			ssdpMultiSock.receive(recvPacket.getDatagramPacket());
-			recvPacket.setTimeStamp(System.currentTimeMillis());
-		}
-		catch (Exception e) {
-			//Debug.warning(e);
-		}
- 		return recvPacket;
-	}
+
+		// Thanks for Kazuyuki Shudo (08/23/07)
+		// Thanks for Stephan Mehlhase (2010-10-26)
+		if (ssdpMultiSock != null)
+			ssdpMultiSock.receive(recvPacket.getDatagramPacket()); // throws IOException
+		else 
+			throw new IOException("Multicast socket has already been closed.");
+		
+		recvPacket.setTimeStamp(System.currentTimeMillis());
+ 		
+		return recvPacket;
+	}
 }
-
+
diff --git a/router/java/src/org/cybergarage/upnp/ssdp/HTTPUSocket.java b/router/java/src/org/cybergarage/upnp/ssdp/HTTPUSocket.java
index 221311c554888397b8892a683f11a92aed599cc9..78b4711d4d3e414fcf6859b32c29f246467d7c23 100644
--- a/router/java/src/org/cybergarage/upnp/ssdp/HTTPUSocket.java
+++ b/router/java/src/org/cybergarage/upnp/ssdp/HTTPUSocket.java
@@ -23,9 +23,12 @@
 
 package org.cybergarage.upnp.ssdp;
 
-import java.net.*;
-
-import org.cybergarage.util.*;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+
+import org.cybergarage.util.Debug;
 
 public class HTTPUSocket
 {
@@ -75,6 +78,15 @@ public class HTTPUSocket
 	{
 		localAddr = addr;
 	}
+
+	/**
+	 * 
+	 * @return {@link DatagramSocket} open for receieving packets
+	 * @since 1.8
+	 */
+	public DatagramSocket getUDPSocket(){
+		return ssdpUniSock;
+	}	
 	
 	public String getLocalAddress()
 	{
@@ -106,17 +118,30 @@ public class HTTPUSocket
 	{
 		close();
 		
+		try {
+			// Changed to bind the specified address and port for Android v1.6 (2009/10/07)
+			InetSocketAddress bindInetAddr = new InetSocketAddress(InetAddress.getByName(bindAddr), bindPort);
+			ssdpUniSock = new DatagramSocket(bindInetAddr);
+		}
+		catch (Exception e) {
+			Debug.warning(e);
+			return false;
+		}
+
+		/*
 		try {
 			// Bind only using the port without the interface address. (2003/12/12)
-			InetSocketAddress bindSock = new InetSocketAddress(/*InetAddress.getByName(bindAddr), */ bindPort);
+			InetSocketAddress bindInetAddr = new InetSocketAddress(bindPort);
 			ssdpUniSock = new DatagramSocket(null);
 			ssdpUniSock.setReuseAddress(true);
-			ssdpUniSock.bind(bindSock);
+			ssdpUniSock.bind(bindInetAddr);
+			return true;
 		}
 		catch (Exception e) {
 			Debug.warning(e);
 			return false;
 		}
+		*/
 		
 		setLocalAddress(bindAddr);
 		
diff --git a/router/java/src/org/cybergarage/upnp/ssdp/SSDP.java b/router/java/src/org/cybergarage/upnp/ssdp/SSDP.java
index 8f692355f740715c9c12b1bae0f73a0342d7bb4e..0d1867ff55909dc2faaf20bc580e4fad2740cbb2 100644
--- a/router/java/src/org/cybergarage/upnp/ssdp/SSDP.java
+++ b/router/java/src/org/cybergarage/upnp/ssdp/SSDP.java
@@ -5,27 +5,49 @@
 *	Copyright (C) Satoshi Konno 2002
 *
 *	File: SSDP.java
-*
-*	Revision;
-*
-*	11/18/02
-*		- first revision.
+*
+*	Revision;
+*
+*	11/18/02
+*		- first revision.
 *	05/13/03
 *		- Added constants for IPv6.
+*	08/03/05
+*		- Thanks for Stefano Lenzi <kismet-sl at users.sourceforge.net>
+*		  and Mikael <mhakman at users.sourceforge.net>
+*		- Fixed getLeaseTime() to parse normally when the value includes extra strings such as white space.
 *	
 ******************************************************************/
-
-package org.cybergarage.upnp.ssdp;
-
-public class SSDP
-{
-	////////////////////////////////////////////////
-	//	Constants
-	////////////////////////////////////////////////
-
-	public static final int PORT = 1900;
-	
-	public static final String ADDRESS = "239.255.255.250";
+
+package org.cybergarage.upnp.ssdp;
+
+import org.cybergarage.util.Debug;
+
+/**
+ * 
+ * This class defines constant value related to SSDP.<br>
+ * All the values defined here are complaint to the UPnP Standard 
+ * 
+ * @author Satoshi "skonno" Konno
+ * @author Stefano "Kismet" Lenzi
+ * @version 1.0
+ *
+ */
+public class SSDP
+{
+	////////////////////////////////////////////////
+	//	Constants
+	////////////////////////////////////////////////
+
+	/**
+	 * Default destination port for SSDP multicast messages
+	 */
+	public static final int PORT = 1900;
+	
+	/**
+	 * Default IPv4 multicast address for SSDP messages
+	 */
+	public static final String ADDRESS = "239.255.255.250";
 
 	public static final String IPV6_LINK_LOCAL_ADDRESS = "FF02::C";
 	public static final String IPV6_SUBNET_ADDRESS = "FF03::C";
@@ -37,7 +59,7 @@ public class SSDP
 
 	public static final void setIPv6Address(String addr)
 	{
-		IPV6_ADDRESS = addr;
+		IPV6_ADDRESS = addr;
 	}
 
 	public static final String getIPv6Address()
@@ -45,9 +67,9 @@ public class SSDP
 		return IPV6_ADDRESS;
 	}
 	
-	public static final int DEFAULT_MSEARCH_MX = 3;
-
-	public static final int RECV_MESSAGE_BUFSIZE = 1024;
+	public static final int DEFAULT_MSEARCH_MX = 3;
+
+	public static final int RECV_MESSAGE_BUFSIZE = 1024;
 
 	////////////////////////////////////////////////
 	//	Initialize
@@ -61,17 +83,27 @@ public class SSDP
 	////////////////////////////////////////////////
 	//	LeaseTime
 	////////////////////////////////////////////////
-	
-	public final static int getLeaseTime(String cacheCont)
-	{
-		int equIdx = cacheCont.indexOf('=');
-		int mx = 0;
-		try {
-			String mxStr = new String(cacheCont.getBytes(), equIdx+1, cacheCont.length() - (equIdx+1));
-			mx = Integer.parseInt(mxStr);
-		}
-		catch (Exception e) {}
-		return mx;
+
+	public final static int getLeaseTime(String cacheCont){
+		/*
+		 * Search for max-age keyword instead of equals sign Found value of
+		 * max-age ends at next comma or end of string
+		 */ 
+		int mx = 0;  
+		int maxAgeIdx = cacheCont.indexOf("max-age");  
+		if (maxAgeIdx >= 0) {  
+			int endIdx = cacheCont.indexOf(',',maxAgeIdx);  
+			if (endIdx < 0)  
+				endIdx = cacheCont.length();  
+			try {  
+				maxAgeIdx = cacheCont.indexOf("=",maxAgeIdx); 
+				String mxStr = cacheCont.substring(maxAgeIdx+1,endIdx).trim();  
+				mx = Integer.parseInt(mxStr);  
+			} catch (Exception e) {  
+				Debug.warning (e);  
+			} 
+		}  
+		return mx; 
 	}
 }
-
+
diff --git a/router/java/src/org/cybergarage/upnp/ssdp/SSDPNotifyRequest.java b/router/java/src/org/cybergarage/upnp/ssdp/SSDPNotifyRequest.java
index b2fb3c5c0bdc2cbfef4175e60177edf2db129271..1df63ff6c6704c3593f1a03758cfd4c4dc22ddd4 100644
--- a/router/java/src/org/cybergarage/upnp/ssdp/SSDPNotifyRequest.java
+++ b/router/java/src/org/cybergarage/upnp/ssdp/SSDPNotifyRequest.java
@@ -15,7 +15,7 @@
 
 package org.cybergarage.upnp.ssdp;
 
-import org.cybergarage.http.*;
+import org.cybergarage.http.HTTP;
 
 public class SSDPNotifyRequest extends SSDPRequest
 {
diff --git a/router/java/src/org/cybergarage/upnp/ssdp/SSDPNotifySocket.java b/router/java/src/org/cybergarage/upnp/ssdp/SSDPNotifySocket.java
index 6a33af7c3c2d18fd3a4fc3f075fca9a57a9f6925..3090da33c7194ba587c355bdb980766a2ae7ded9 100644
--- a/router/java/src/org/cybergarage/upnp/ssdp/SSDPNotifySocket.java
+++ b/router/java/src/org/cybergarage/upnp/ssdp/SSDPNotifySocket.java
@@ -5,11 +5,11 @@
 *	Copyright (C) Satoshi Konno 2002-2003
 *
 *	File: SSDPNotifySocket.java
-*
-*	Revision;
-*
-*	11/20/02
-*		- first revision.
+*
+*	Revision;
+*
+*	11/20/02
+*		- first revision.
 *	05/13/03
 *		- Added support for IPv6.
 *	02/20/04
@@ -19,27 +19,42 @@
 *		- Mikael Hakman <mhakman@dkab.net>
 *		- Handle receive() returning null.
 *		- Added close() in stop().
+*	08/23/07
+*		- Thanks for Kazuyuki Shudo
+* 		- Changed run() to catch IOException of HTTPMUSocket::receive().
+*	01/31/08
+*		- Changed start() not to abort when the interface infomation is null on Android m3-rc37a.
 *	
 ******************************************************************/
-
-package org.cybergarage.upnp.ssdp;
 
-import java.net.*;
+package org.cybergarage.upnp.ssdp;
+
+import java.net.*;
+import java.io.IOException;
 
 import org.cybergarage.net.*;
 import org.cybergarage.util.*;
 import org.cybergarage.http.*;
-import org.cybergarage.upnp.*;
-
-public class SSDPNotifySocket extends HTTPMUSocket implements Runnable
+import org.cybergarage.upnp.*;
+
+/**
+ * 
+ * This class identifies a SSDP socket only for <b>notifing packet</b>.<br>
+ * 
+ * @author Satoshi "skonno" Konno
+ * @author Stefano "Kismet" Lenzi
+ * @version 1.8
+ *
+ */
+public class SSDPNotifySocket extends HTTPMUSocket implements Runnable
 {
 	private boolean useIPv6Address;
-	
-	////////////////////////////////////////////////
-	//	Constructor
-	////////////////////////////////////////////////
-	
-	public SSDPNotifySocket(String bindAddr)
+	
+	////////////////////////////////////////////////
+	//	Constructor
+	////////////////////////////////////////////////
+	
+	public SSDPNotifySocket(String bindAddr)
 	{
 		String addr = SSDP.ADDRESS;
 		useIPv6Address = false;
@@ -48,29 +63,32 @@ public class SSDPNotifySocket extends HTTPMUSocket implements Runnable
 			useIPv6Address = true;
 		}
 		open(addr, SSDP.PORT, bindAddr);
-		setControlPoint(null);
-	}
-
-	////////////////////////////////////////////////
-	//	ControlPoint	
-	////////////////////////////////////////////////
-
-	private ControlPoint controlPoint = null;
-	
-	public void setControlPoint(ControlPoint ctrlp)
-	{
-		this.controlPoint = ctrlp;
-	}
-
-	public ControlPoint getControlPoint()
-	{
-		return controlPoint;
-	}
+		setControlPoint(null);
+	}
 
 	////////////////////////////////////////////////
-	//	post (SSDPNotifySocket)
+	//	ControlPoint	
 	////////////////////////////////////////////////
 
+	private ControlPoint controlPoint = null;
+	
+	public void setControlPoint(ControlPoint ctrlp)
+	{
+		this.controlPoint = ctrlp;
+	}
+
+	public ControlPoint getControlPoint()
+	{
+		return controlPoint;
+	}
+
+	/**
+	 * This method send a {@link SSDPNotifyRequest} over {@link SSDPNotifySocket}
+	 * 
+	 * @param req the {@link SSDPNotifyRequest} to send
+	 * @return true if and only if the trasmission succeced<br>
+	 * 	Because it rely on UDP doesn't mean that it's also recieved
+	 */
 	public boolean post(SSDPNotifyRequest req)
 	{
 		String ssdpAddr = SSDP.ADDRESS;
@@ -80,21 +98,29 @@ public class SSDPNotifySocket extends HTTPMUSocket implements Runnable
 		return post((HTTPRequest)req);
 	}
 
-	////////////////////////////////////////////////
-	//	run	
-	////////////////////////////////////////////////
-
-	private Thread deviceNotifyThread = null;
-		
-	public void run()
-	{
+	////////////////////////////////////////////////
+	//	run	
+	////////////////////////////////////////////////
+
+	private Thread deviceNotifyThread = null;
+		
+	public void run()
+	{
 		Thread thisThread = Thread.currentThread();
-		
+		
 		ControlPoint ctrlPoint = getControlPoint();
-		
-		while (deviceNotifyThread == thisThread) {
-			Thread.yield();
-			SSDPPacket packet = receive();
+		
+		while (deviceNotifyThread == thisThread) {
+			Thread.yield();
+
+			// Thanks for Kazuyuki Shudo (08/23/07)
+			SSDPPacket packet = null;
+			try {
+				packet = receive();
+			}
+			catch (IOException e) { 
+				break;
+			}
 			
 			// Thanks for Mikael Hakman (04/20/05)
 			if (packet == null)
@@ -104,28 +130,35 @@ public class SSDPNotifySocket extends HTTPMUSocket implements Runnable
 			InetAddress maddr = getMulticastInetAddress();
 			InetAddress pmaddr = packet.getHostInetAddress();
 			if (maddr.equals(pmaddr) == false) {
-				Debug.warning("Invalidate Multicast Recieved : " + maddr + "," + pmaddr);
+				Debug.warning("Invalidate Multicast Recieved from IP " + maddr + " on " + pmaddr);
 				continue;
 			}
-												
+			//TODO Must be performed on a different Thread in order to prevent UDP packet losses.
 			if (ctrlPoint != null)
-				ctrlPoint.notifyReceived(packet); 
-		}
-	}
-	
-	public void start()
-	{
-		deviceNotifyThread = new Thread(this, "UPnP-SSDPNotifySocket");
-		deviceNotifyThread.setDaemon(true);
-		deviceNotifyThread.start();
-	}
-	
-	public void stop()
-	{
+				ctrlPoint.notifyReceived(packet); 
+		}
+	}
+	
+	public void start(){
+		StringBuffer name = new StringBuffer("Cyber.SSDPNotifySocket/");
+		String localAddr = this.getLocalAddress();
+		// localAddr is null on Android m3-rc37a (01/30/08)
+		if (localAddr != null && 0 < localAddr.length()) {
+			name.append(this.getLocalAddress()).append(':');
+			name.append(this.getLocalPort()).append(" -> ");
+			name.append(this.getMulticastAddress()).append(':');
+			name.append(this.getMulticastPort());
+		}
+		deviceNotifyThread = new Thread(this,name.toString());
+		deviceNotifyThread.start();
+	}
+	
+	public void stop()
+	{
 		// Thanks for Mikael Hakman (04/20/05)
 		close();
 		
-		deviceNotifyThread = null;
-	}
+		deviceNotifyThread = null;
+	}
 }
-
+
diff --git a/router/java/src/org/cybergarage/upnp/ssdp/SSDPNotifySocketList.java b/router/java/src/org/cybergarage/upnp/ssdp/SSDPNotifySocketList.java
index d0541510c0d5d440a0531fc6a6b3174f6fa5d13e..64a2649650e85c72d21602a08c2ae3646adf7dc7 100644
--- a/router/java/src/org/cybergarage/upnp/ssdp/SSDPNotifySocketList.java
+++ b/router/java/src/org/cybergarage/upnp/ssdp/SSDPNotifySocketList.java
@@ -1,106 +1,129 @@
-/******************************************************************
-*
-*	CyberUPnP for Java
-*
-*	Copyright (C) Satoshi Konno 2002-2003
+/******************************************************************
 *
-*	File: HTTPServerList.java
-*
-*	Revision;
-*
-*	05/11/03
-*		- first revision.
-*
-******************************************************************/
+*	CyberUPnP for Java
+*
+*	Copyright (C) Satoshi Konno 2002-2003
+*
+*	File: HTTPServerList.java
+*
+*	Revision;
+*
+*	05/11/03
+*		- first revision.
+*
+******************************************************************/
+
+package org.cybergarage.upnp.ssdp;
+
+import java.net.InetAddress;
+import java.util.*;
+
+import org.cybergarage.net.*;
+
+import org.cybergarage.upnp.*;
+
+public class SSDPNotifySocketList extends Vector 
+{
+	////////////////////////////////////////////////
+	//	Constructor
+	////////////////////////////////////////////////
+	
+	private InetAddress[] binds = null;
+
+	public SSDPNotifySocketList() {
+	}
+	
+	/**
+	 * 
+	 * @param binds The host to bind the service <tt>null</tt> means to bind to default.
+	 * @since 1.8
+	 */
+	public SSDPNotifySocketList(InetAddress[] binds){
+		this.binds=binds;
+	}
+
+	////////////////////////////////////////////////
+	//	Methods
+	////////////////////////////////////////////////
+	
+	public SSDPNotifySocket getSSDPNotifySocket(int n)
+	{
+		return (SSDPNotifySocket)get(n);
+	}
+
+	////////////////////////////////////////////////
+	//	ControlPoint
+	////////////////////////////////////////////////
+
+	public void setControlPoint(ControlPoint ctrlPoint)
+	{
+		int nSockets = size();
+		for (int n=0; n<nSockets; n++) {
+			SSDPNotifySocket sock = getSSDPNotifySocket(n);
+			sock.setControlPoint(ctrlPoint);
+		}
+	}
+
+	////////////////////////////////////////////////
+	//	Methods
+	////////////////////////////////////////////////
+	
+	public boolean open(){
+		InetAddress[] binds=this.binds ;
+		String[] bindAddresses;
+		if(binds!=null){			
+			bindAddresses = new String[binds.length];
+			for (int i = 0; i < binds.length; i++) {
+				bindAddresses[i] = binds[i].getHostAddress();
+			}
+		}else{
+			int nHostAddrs = HostInterface.getNHostAddresses();
+			bindAddresses = new String[nHostAddrs]; 
+			for (int n=0; n<nHostAddrs; n++) {
+				bindAddresses[n] = HostInterface.getHostAddress(n);
+			}
+		}		
+		
+		for (int i = 0; i < bindAddresses.length; i++) {
+			if(bindAddresses[i]!=null){
+				SSDPNotifySocket ssdpNotifySocket = new SSDPNotifySocket(bindAddresses[i]);
+				add(ssdpNotifySocket);
+			}
+		}
+		return true;
+	}
+	
+	public void close()
+	{
+		int nSockets = size();
+		for (int n=0; n<nSockets; n++) {
+			SSDPNotifySocket sock = getSSDPNotifySocket(n);
+			sock.close();
+		}
+		clear();
+	}
+	
+	////////////////////////////////////////////////
+	//	Methods
+	////////////////////////////////////////////////
+	
+	public void start()
+	{
+		int nSockets = size();
+		for (int n=0; n<nSockets; n++) {
+			SSDPNotifySocket sock = getSSDPNotifySocket(n);
+			sock.start();
+		}
+	}
+
+	public void stop()
+	{
+		int nSockets = size();
+		for (int n=0; n<nSockets; n++) {
+			SSDPNotifySocket sock = getSSDPNotifySocket(n);
+			sock.stop();
+		}
+	}
+	
+}
 
-package org.cybergarage.upnp.ssdp;
-
-import java.util.*;
-
-import org.cybergarage.net.*;
-
-import org.cybergarage.upnp.*;
-
-public class SSDPNotifySocketList extends Vector 
-{
-	////////////////////////////////////////////////
-	//	Constructor
-	////////////////////////////////////////////////
-	
-	private static final long serialVersionUID = -7066290881503106399L;
-
-	public SSDPNotifySocketList() 
-	{
-	}
-
-	////////////////////////////////////////////////
-	//	Methods
-	////////////////////////////////////////////////
-	
-	public SSDPNotifySocket getSSDPNotifySocket(int n)
-	{
-		return (SSDPNotifySocket)get(n);
-	}
-
-	////////////////////////////////////////////////
-	//	ControlPoint
-	////////////////////////////////////////////////
-
-	public void setControlPoint(ControlPoint ctrlPoint)
-	{
-		int nSockets = size();
-		for (int n=0; n<nSockets; n++) {
-			SSDPNotifySocket sock = getSSDPNotifySocket(n);
-			sock.setControlPoint(ctrlPoint);
-		}
-	}
-
-	////////////////////////////////////////////////
-	//	Methods
-	////////////////////////////////////////////////
-	
-	public boolean open() 
-	{
-		int nHostAddrs = HostInterface.getNHostAddresses();
-		for (int n=0; n<nHostAddrs; n++) {
-			String bindAddr = HostInterface.getHostAddress(n);
-			SSDPNotifySocket ssdpNotifySocket = new SSDPNotifySocket(bindAddr);
-			add(ssdpNotifySocket);
-		}
-		return true;
-	}
-	
-	public void close()
-	{
-		int nSockets = size();
-		for (int n=0; n<nSockets; n++) {
-			SSDPNotifySocket sock = getSSDPNotifySocket(n);
-			sock.close();
-		}
-		clear();
-	}
-	
-	////////////////////////////////////////////////
-	//	Methods
-	////////////////////////////////////////////////
-	
-	public void start()
-	{
-		int nSockets = size();
-		for (int n=0; n<nSockets; n++) {
-			SSDPNotifySocket sock = getSSDPNotifySocket(n);
-			sock.start();
-		}
-	}
-
-	public void stop()
-	{
-		int nSockets = size();
-		for (int n=0; n<nSockets; n++) {
-			SSDPNotifySocket sock = getSSDPNotifySocket(n);
-			sock.stop();
-		}
-	}
-	
-}
-
diff --git a/router/java/src/org/cybergarage/upnp/ssdp/SSDPRequest.java b/router/java/src/org/cybergarage/upnp/ssdp/SSDPRequest.java
index f40442f18ec2b4b3690041a544a0953d1a18b681..17d6eaceaecfcc82dc34b264f26c9e9561c5da08 100644
--- a/router/java/src/org/cybergarage/upnp/ssdp/SSDPRequest.java
+++ b/router/java/src/org/cybergarage/upnp/ssdp/SSDPRequest.java
@@ -18,6 +18,8 @@
 
 package org.cybergarage.upnp.ssdp;
 
+import java.io.InputStream;
+
 import org.cybergarage.http.*;
 
 public class SSDPRequest extends HTTPRequest
@@ -31,6 +33,11 @@ public class SSDPRequest extends HTTPRequest
 		setVersion(HTTP.VERSION_11);
 	}
 
+	public SSDPRequest(InputStream in)
+	{
+		super(in);
+	}
+	
 	////////////////////////////////////////////////
 	//	NT
 	////////////////////////////////////////////////
diff --git a/router/java/src/org/cybergarage/upnp/ssdp/SSDPResponse.java b/router/java/src/org/cybergarage/upnp/ssdp/SSDPResponse.java
index 31b4ed280ec9178940aabbba7c615c649ed1a5a6..fd96ed7c636799454cbef6091b7d164937b8c8fe 100644
--- a/router/java/src/org/cybergarage/upnp/ssdp/SSDPResponse.java
+++ b/router/java/src/org/cybergarage/upnp/ssdp/SSDPResponse.java
@@ -24,6 +24,8 @@
 
 package org.cybergarage.upnp.ssdp;
 
+import java.io.InputStream;
+
 import org.cybergarage.http.*;
 
 public class SSDPResponse extends HTTPResponse
@@ -37,6 +39,11 @@ public class SSDPResponse extends HTTPResponse
 		setVersion(HTTP.VERSION_11);
 	}
 
+	public SSDPResponse(InputStream in)
+	{
+		super(in);
+	}
+	
 	////////////////////////////////////////////////
 	//	ST (SearchTarget)
 	////////////////////////////////////////////////
@@ -114,7 +121,7 @@ public class SSDPResponse extends HTTPResponse
 	
 	public String getHeader()
 	{
-		StringBuilder str = new StringBuilder();
+		StringBuffer str = new StringBuffer();
 	
 		str.append(getStatusLineString());
 		str.append(getHeaderString());
diff --git a/router/java/src/org/cybergarage/upnp/ssdp/SSDPSearchResponseSocket.java b/router/java/src/org/cybergarage/upnp/ssdp/SSDPSearchResponseSocket.java
index 61668872aa40da48b0f5d8f10db91892a9893edc..63d37b7be775c8dd6f54b9263a519ccd59d81928 100644
--- a/router/java/src/org/cybergarage/upnp/ssdp/SSDPSearchResponseSocket.java
+++ b/router/java/src/org/cybergarage/upnp/ssdp/SSDPSearchResponseSocket.java
@@ -5,36 +5,41 @@
 *	Copyright (C) Satoshi Konno 2002
 *
 *	File: SSDPSearchResponseSocket.java
-*
-*	Revision;
-*
-*	11/20/02
-*		- first revision.
+*
+*	Revision;
+*
+*	11/20/02
+*		- first revision.
 *	05/28/03
 *		- Added post() to send a SSDPSearchRequest.
+*	01/31/08
+*		- Changed start() not to abort when the interface infomation is null on Android m3-rc37a.
 *	
 ******************************************************************/
-
-package org.cybergarage.upnp.ssdp;
+
+package org.cybergarage.upnp.ssdp;
+
+import java.net.DatagramSocket;
+import java.net.InetAddress;
 
 import org.cybergarage.upnp.*;
-
-public class SSDPSearchResponseSocket extends HTTPUSocket implements Runnable
-{
-	////////////////////////////////////////////////
-	//	Constructor
-	////////////////////////////////////////////////
+
+public class SSDPSearchResponseSocket extends HTTPUSocket implements Runnable
+{
+	////////////////////////////////////////////////
+	//	Constructor
+	////////////////////////////////////////////////
 
 	public SSDPSearchResponseSocket()
 	{
 		setControlPoint(null);
 	}
 	
-	public SSDPSearchResponseSocket(String bindAddr, int port)
-	{
+	public SSDPSearchResponseSocket(String bindAddr, int port)
+	{
 		super(bindAddr, port);
 		setControlPoint(null);
-	}
+	}
 
 	////////////////////////////////////////////////
 	//	ControlPoint	
@@ -51,49 +56,56 @@ public class SSDPSearchResponseSocket extends HTTPUSocket implements Runnable
 	{
 		return controlPoint;
 	}
-
-	////////////////////////////////////////////////
-	//	run	
-	////////////////////////////////////////////////
-
-	private Thread deviceSearchResponseThread = null;
-		
-	public void run()
-	{
+
+	////////////////////////////////////////////////
+	//	run	
+	////////////////////////////////////////////////
+
+	private Thread deviceSearchResponseThread = null;
+		
+	public void run()
+	{
 		Thread thisThread = Thread.currentThread();
 		
 		ControlPoint ctrlPoint = getControlPoint();
-
-		while (deviceSearchResponseThread == thisThread) {
-			Thread.yield();
+
+		while (deviceSearchResponseThread == thisThread) {
+			Thread.yield();
 			SSDPPacket packet = receive();
 			if (packet == null)
-				break;
+				break;
 			if (ctrlPoint != null)
 				ctrlPoint.searchResponseReceived(packet); 
-		}
-	}
-	
-	public void start()
-	{
-		deviceSearchResponseThread = new Thread(this, "UPnP-SSDPSearchResponseSocket");
-		deviceSearchResponseThread.setDaemon(true);
-		deviceSearchResponseThread.start();
-	}
-	
-	public void stop()
-	{
-		deviceSearchResponseThread = null;
-	}
-
-	////////////////////////////////////////////////
-	//	post
-	////////////////////////////////////////////////
-
-	public boolean post(String addr, int port, SSDPSearchResponse res)
-	{
-		return post(addr, port, res.getHeader());
-	}
+		}
+	}
+	
+	public void start()	{
+
+		StringBuffer name = new StringBuffer("Cyber.SSDPSearchResponseSocket/");
+		DatagramSocket s = getDatagramSocket();
+		// localAddr is null on Android m3-rc37a (01/30/08)
+		InetAddress localAddr = s.getLocalAddress();
+		if (localAddr != null) {
+			name.append(s.getLocalAddress()).append(':');
+			name.append(s.getLocalPort());
+		}
+		deviceSearchResponseThread = new Thread(this,name.toString());
+		deviceSearchResponseThread.start();		
+	}
+	
+	public void stop()
+	{
+		deviceSearchResponseThread = null;
+	}
+
+	////////////////////////////////////////////////
+	//	post
+	////////////////////////////////////////////////
+
+	public boolean post(String addr, int port, SSDPSearchResponse res)
+	{
+		return post(addr, port, res.getHeader());
+	}
 
 	////////////////////////////////////////////////
 	//	post
@@ -104,4 +116,4 @@ public class SSDPSearchResponseSocket extends HTTPUSocket implements Runnable
 		return post(addr, port, req.toString());
 	}
 }
-
+
diff --git a/router/java/src/org/cybergarage/upnp/ssdp/SSDPSearchResponseSocketList.java b/router/java/src/org/cybergarage/upnp/ssdp/SSDPSearchResponseSocketList.java
index e9a7582449dd4ea9f8879a7944d977dc658c2544..077c63c232381c2bd4e5a0541b80518a02d25c0a 100644
--- a/router/java/src/org/cybergarage/upnp/ssdp/SSDPSearchResponseSocketList.java
+++ b/router/java/src/org/cybergarage/upnp/ssdp/SSDPSearchResponseSocketList.java
@@ -1,144 +1,166 @@
-/******************************************************************
-*
-*	CyberUPnP for Java
-*
-*	Copyright (C) Satoshi Konno 2002-2003
+/******************************************************************
 *
-*	File: SSDPSearchResponseSocketList.java
-*
-*	Revision;
-*
-*	05/08/03
-*		- first revision.
-*	05/28/03
-*		- Added post() to send a SSDPSearchRequest.
-*
-******************************************************************/
+*	CyberUPnP for Java
+*
+*	Copyright (C) Satoshi Konno 2002-2003
+*
+*	File: SSDPSearchResponseSocketList.java
+*
+*	Revision;
+*
+*	05/08/03
+*		- first revision.
+*	05/28/03
+*		- Added post() to send a SSDPSearchRequest.
+*
+******************************************************************/
+
+package org.cybergarage.upnp.ssdp;
+
+import java.net.InetAddress;
+import java.util.*;
+
+import org.cybergarage.net.*;
+
+import org.cybergarage.upnp.*;
+
+public class SSDPSearchResponseSocketList extends Vector 
+{
+	////////////////////////////////////////////////
+	//	Constructor
+	////////////////////////////////////////////////
+	
+	private InetAddress[] binds = null;
+	
+	public SSDPSearchResponseSocketList() {
+	}
+	/**
+	 * 
+	 * @param binds The host to bind.Use <tt>null</tt> for the default behavior
+	 */
+	public SSDPSearchResponseSocketList(InetAddress[] binds) {
+		this.binds = binds;
+	}
+
+	
+	
+	////////////////////////////////////////////////
+	//	ControlPoint
+
+	////////////////////////////////////////////////
+	//	ControlPoint
+	////////////////////////////////////////////////
+
+	public void setControlPoint(ControlPoint ctrlPoint)
+	{
+		int nSockets = size();
+		for (int n=0; n<nSockets; n++) {
+			SSDPSearchResponseSocket sock = getSSDPSearchResponseSocket(n);
+			sock.setControlPoint(ctrlPoint);
+		}
+	}
+
+	////////////////////////////////////////////////
+	//	get
+	////////////////////////////////////////////////
+	
+	public SSDPSearchResponseSocket getSSDPSearchResponseSocket(int n)
+	{
+		return (SSDPSearchResponseSocket)get(n);
+	}
+	
+	////////////////////////////////////////////////
+	//	Methods
+	////////////////////////////////////////////////
+	
+	public boolean open(int port){
+		InetAddress[] binds=this.binds ;
+		String[] bindAddresses;
+		if(binds!=null){			
+			bindAddresses = new String[binds.length];
+			for (int i = 0; i < binds.length; i++) {
+				bindAddresses[i] = binds[i].getHostAddress();
+			}
+		}else{
+			int nHostAddrs = HostInterface.getNHostAddresses();
+			bindAddresses = new String[nHostAddrs]; 
+			for (int n=0; n<nHostAddrs; n++) {
+				bindAddresses[n] = HostInterface.getHostAddress(n);
+			}
+		}		
+		try {
+			for (int j = 0; j < bindAddresses.length; j++) {				
+				SSDPSearchResponseSocket socket = new SSDPSearchResponseSocket(bindAddresses[j], port);
+				add(socket);
+			}
+		}catch (Exception e) {
+			stop();
+			close();
+			clear();
+			return false;
+		}
+		return true;
+	}
+
+	public boolean open() 
+	{
+		return open(SSDP.PORT);
+	}
+		
+	public void close()
+	{
+		int nSockets = size();
+		for (int n=0; n<nSockets; n++) {
+			SSDPSearchResponseSocket sock = getSSDPSearchResponseSocket(n);
+			sock.close();
+		}
+		clear();
+	}
+
+	////////////////////////////////////////////////
+	//	Methods
+	////////////////////////////////////////////////
+	
+	public void start()
+	{
+		int nSockets = size();
+		for (int n=0; n<nSockets; n++) {
+			SSDPSearchResponseSocket sock = getSSDPSearchResponseSocket(n);
+			sock.start();
+		}
+	}
+
+	public void stop()
+	{
+		int nSockets = size();
+		for (int n=0; n<nSockets; n++) {
+			SSDPSearchResponseSocket sock = getSSDPSearchResponseSocket(n);
+			sock.stop();
+		}
+	}
+
+	////////////////////////////////////////////////
+	//	Methods
+	////////////////////////////////////////////////
+
+	public boolean post(SSDPSearchRequest req)
+	{
+		boolean ret = true;
+		int nSockets = size();
+		for (int n=0; n<nSockets; n++) {
+			SSDPSearchResponseSocket sock = getSSDPSearchResponseSocket(n);
+			String bindAddr = sock.getLocalAddress();
+			req.setLocalAddress(bindAddr);
+			String ssdpAddr = SSDP.ADDRESS;
+			if (HostInterface.isIPv6Address(bindAddr) == true)
+				ssdpAddr = SSDP.getIPv6Address();
+			//sock.joinGroup(ssdpAddr, SSDP.PORT, bindAddr);
+			if (sock.post(ssdpAddr, SSDP.PORT, req) == false)
+				ret = false;
+			//sock.leaveGroup(ssdpAddr, SSDP.PORT, bindAddr);
+		}
+		return ret;
+	}
+	
+}
 
-package org.cybergarage.upnp.ssdp;
-
-import java.util.*;
-
-import org.cybergarage.net.*;
-
-import org.cybergarage.upnp.*;
-
-public class SSDPSearchResponseSocketList extends Vector 
-{
-	////////////////////////////////////////////////
-	//	Constructor
-	////////////////////////////////////////////////
-	
-	private static final long serialVersionUID = 4509857798038125744L;
-
-	public SSDPSearchResponseSocketList() 
-	{
-	}
-
-	////////////////////////////////////////////////
-	//	ControlPoint
-	////////////////////////////////////////////////
-
-	public void setControlPoint(ControlPoint ctrlPoint)
-	{
-		int nSockets = size();
-		for (int n=0; n<nSockets; n++) {
-			SSDPSearchResponseSocket sock = getSSDPSearchResponseSocket(n);
-			sock.setControlPoint(ctrlPoint);
-		}
-	}
-
-	////////////////////////////////////////////////
-	//	get
-	////////////////////////////////////////////////
-	
-	public SSDPSearchResponseSocket getSSDPSearchResponseSocket(int n)
-	{
-		return (SSDPSearchResponseSocket)get(n);
-	}
-	
-	////////////////////////////////////////////////
-	//	Methods
-	////////////////////////////////////////////////
-	
-	public boolean open(int port)
-	{
-		try {
-			int nHostAddrs = HostInterface.getNHostAddresses();
-			for (int n=0; n<nHostAddrs; n++) {
-				String bindAddr = HostInterface.getHostAddress(n);
-				SSDPSearchResponseSocket socket = new SSDPSearchResponseSocket(bindAddr, port);
-				add(socket);
-			}
-		}
-		catch (Exception e) {
-			stop();
-			close();
-			clear();
-			return false;
-		}
-		return true;
-	}
-
-	public boolean open() 
-	{
-		return open(SSDP.PORT);
-	}
-		
-	public void close()
-	{
-		int nSockets = size();
-		for (int n=0; n<nSockets; n++) {
-			SSDPSearchResponseSocket sock = getSSDPSearchResponseSocket(n);
-			sock.close();
-		}
-		clear();
-	}
-
-	////////////////////////////////////////////////
-	//	Methods
-	////////////////////////////////////////////////
-	
-	public void start()
-	{
-		int nSockets = size();
-		for (int n=0; n<nSockets; n++) {
-			SSDPSearchResponseSocket sock = getSSDPSearchResponseSocket(n);
-			sock.start();
-		}
-	}
-
-	public void stop()
-	{
-		int nSockets = size();
-		for (int n=0; n<nSockets; n++) {
-			SSDPSearchResponseSocket sock = getSSDPSearchResponseSocket(n);
-			sock.stop();
-		}
-	}
-
-	////////////////////////////////////////////////
-	//	Methods
-	////////////////////////////////////////////////
-
-	public boolean post(SSDPSearchRequest req)
-	{
-		boolean ret = true;
-		int nSockets = size();
-		for (int n=0; n<nSockets; n++) {
-			SSDPSearchResponseSocket sock = getSSDPSearchResponseSocket(n);
-			String bindAddr = sock.getLocalAddress();
-			req.setLocalAddress(bindAddr);
-			String ssdpAddr = SSDP.ADDRESS;
-			if (HostInterface.isIPv6Address(bindAddr) == true)
-				ssdpAddr = SSDP.getIPv6Address();
-			//sock.joinGroup(ssdpAddr, SSDP.PORT, bindAddr);
-			if (sock.post(ssdpAddr, SSDP.PORT, req) == false)
-				ret = false;
-			//sock.leaveGroup(ssdpAddr, SSDP.PORT, bindAddr);
-		}
-		return ret;
-	}
-	
-}
-
diff --git a/router/java/src/org/cybergarage/upnp/ssdp/SSDPSearchSocket.java b/router/java/src/org/cybergarage/upnp/ssdp/SSDPSearchSocket.java
index 295e1e143527786aa9a6057e192d10667ecbb35b..204ff84fe3e87a09e72af00c837ec620d76dffca 100644
--- a/router/java/src/org/cybergarage/upnp/ssdp/SSDPSearchSocket.java
+++ b/router/java/src/org/cybergarage/upnp/ssdp/SSDPSearchSocket.java
@@ -5,11 +5,11 @@
 *	Copyright (C) Satoshi Konno 2002-2003
 *
 *	File: SSDPSearchSocket.java
-*
-*	Revision;
-*
-*	12/30/02
-*		- first revision.
+*
+*	Revision;
+*
+*	12/30/02
+*		- first revision.
 *	05/13/03
 *		- Added support for IPv6.
 *	05/28/03
@@ -18,101 +18,177 @@
 *		- Mikael Hakman <mhakman@dkab.net>
 *		- Added close() in stop().
 *		- Added test for null return from receive() in run().
+*	08/23/07
+*		- Thanks for Kazuyuki Shudo
+* 		- Changed run() to catch IOException of HTTPMUSocket::receive().
+*	01/10/08
+*		- Changed start() not to abort when the interface infomation is null on Android m3-rc37a.
 *	
 ******************************************************************/
-
-package org.cybergarage.upnp.ssdp;
-
+
+package org.cybergarage.upnp.ssdp;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.io.IOException;
+
 import org.cybergarage.net.*;
 import org.cybergarage.util.*;
 
-import org.cybergarage.upnp.device.*;
-
-public class SSDPSearchSocket extends HTTPMUSocket implements Runnable
-{
-	public SSDPSearchSocket()
-	{
-	}
+import org.cybergarage.upnp.device.*;
+
+public class SSDPSearchSocket extends HTTPMUSocket implements Runnable
+{
+	private boolean useIPv6Address;
 	
-	public SSDPSearchSocket(String bindAddr)
-	{
-		open(bindAddr);
+	////////////////////////////////////////////////
+	//	Constructor
+	////////////////////////////////////////////////
+
+
+	/**
+	 * 
+	 * @param bindAddr The address to bind the service
+	 * @param port The port used for accepting message
+	 * @param multicast The multicast address to use as destination
+	 * @since 1.8
+	 */
+	public SSDPSearchSocket(String bindAddr,int port,String multicast){
+		open(bindAddr,multicast);
+	}
+
+	/**
+	 * 
+	 * @param bindAddr the binding address for senging multicast packet
+	 * @since 1.8
+	 */
+	public SSDPSearchSocket(InetAddress bindAddr){
+		if(bindAddr.getAddress().length!=4){
+			this.open((Inet6Address)bindAddr);
+		}else{
+			this.open((Inet4Address)bindAddr);
+		}
 	}
 
 	////////////////////////////////////////////////
 	//	Constructor
 	////////////////////////////////////////////////
+	public boolean open(Inet4Address bindAddr){
+		useIPv6Address = false;
+		return open(SSDP.ADDRESS, SSDP.PORT, bindAddr);
+	}
+	
+	public boolean open(Inet6Address bindAddr){
+		useIPv6Address = true;
+		return open(SSDP.getIPv6Address(), SSDP.PORT, bindAddr);
+	}
+	
+	public boolean open(String bind,String multicast){		
+		if ((HostInterface.isIPv6Address(bind) ) && (HostInterface.isIPv6Address(multicast))){
+			useIPv6Address = true;
+		}else if(HostInterface.isIPv4Address(bind) && (HostInterface.isIPv4Address(multicast))){
+			useIPv6Address = false;
+		}else{
+			throw new IllegalArgumentException("Cannot open a UDP Socket for IPv6 address on IPv4 interface or viceversa");
+		}
+		return open(multicast, SSDP.PORT, bind);
+	}
 
+	/**
+	 * 
+	 * @param bindAddr the hostname of the interface to use for senfing multicast packet
+	 * @return true if and only if it open the socket
+	 * @see {@link SSDP} for default multicast and port destination of the packtes 
+	 */
 	public boolean open(String bindAddr)
 	{
 		String addr = SSDP.ADDRESS;
+		useIPv6Address = false;
 		if (HostInterface.isIPv6Address(bindAddr) == true) {
 			addr = SSDP.getIPv6Address();
+			useIPv6Address = true;
 		}
 		return open(addr, SSDP.PORT, bindAddr);
 	}
 	
-	////////////////////////////////////////////////
-	//	deviceSearch
-	////////////////////////////////////////////////
-
-	private ListenerList deviceSearchListenerList = new ListenerList();
-	 	
-	public void addSearchListener(SearchListener listener)
-	{
-		deviceSearchListenerList.add(listener);
-	}		
-
-	public void removeSearchListener(SearchListener listener)
-	{
-		deviceSearchListenerList.remove(listener);
-	}		
-
-	public void performSearchListener(SSDPPacket ssdpPacket)
-	{
-		int listenerSize = deviceSearchListenerList.size();
-		for (int n=0; n<listenerSize; n++) {
-			SearchListener listener = (SearchListener)deviceSearchListenerList.get(n);
-			listener.deviceSearchReceived(ssdpPacket);
-		}
-	}		
-	
-	////////////////////////////////////////////////
-	//	run	
-	////////////////////////////////////////////////
-
-	private Thread deviceSearchThread = null;
-		
-	public void run()
-	{
-		Thread thisThread = Thread.currentThread();
-		
-		while (deviceSearchThread == thisThread) {
-			Thread.yield();
-			SSDPPacket packet = receive();
-			
+	////////////////////////////////////////////////
+	//	deviceSearch
+	////////////////////////////////////////////////
+
+	private ListenerList deviceSearchListenerList = new ListenerList();
+	 	
+	public void addSearchListener(SearchListener listener)
+	{
+		deviceSearchListenerList.add(listener);
+	}		
+
+	public void removeSearchListener(SearchListener listener)
+	{
+		deviceSearchListenerList.remove(listener);
+	}		
+
+	public void performSearchListener(SSDPPacket ssdpPacket)
+	{
+		int listenerSize = deviceSearchListenerList.size();
+		for (int n=0; n<listenerSize; n++) {
+			SearchListener listener = (SearchListener)deviceSearchListenerList.get(n);
+			listener.deviceSearchReceived(ssdpPacket);
+		}
+	}		
+	
+	////////////////////////////////////////////////
+	//	run	
+	////////////////////////////////////////////////
+
+	private Thread deviceSearchThread = null;
+		
+	public void run()
+	{
+		Thread thisThread = Thread.currentThread();
+		
+		while (deviceSearchThread == thisThread) {
+			Thread.yield();
+
+			// Thanks for Kazuyuki Shudo (08/23/07)
+			SSDPPacket packet = null;
+			try {
+				packet = receive();
+			}
+			catch (IOException e) { 
+				break;
+			}
+			
 			// Thanks for Mikael Hakman (04/20/05)
 			if (packet == null)
 				continue;
 				
-			if (packet.isDiscover() == true)
-				performSearchListener(packet);
-		}
-	}
-	
-	public void start()
-	{
-		deviceSearchThread = new Thread(this,"UPnP-SSDPSearchSocket");
-		deviceSearchThread.setDaemon(true);
-		deviceSearchThread.start();
-	}
-	
-	public void stop()
+			//TODO perform delegation with Thread Pooling
+			if (packet.isDiscover() == true)
+				performSearchListener(packet);
+		}
+	}
+	
+	public void start() {
+		StringBuffer name = new StringBuffer("Cyber.SSDPSearchSocket/");
+		String localAddr = this.getLocalAddress();
+		// localAddr is null on Android m3-rc37a (01/30/08)
+		if (localAddr != null && 0 < localAddr.length()) {
+			name.append(this.getLocalAddress()).append(':');
+			name.append(this.getLocalPort()).append(" -> ");
+			name.append(this.getMulticastAddress()).append(':');
+			name.append(this.getMulticastPort());
+		}
+		deviceSearchThread = new Thread(this,name.toString());
+		deviceSearchThread.start();
+	}
+	
+	public void stop()
 	{
 		// Thanks for Mikael Hakman (04/20/05)
 		close();
 		
-		deviceSearchThread = null;
-	}
+		deviceSearchThread = null;
+	}
 }
-
+
diff --git a/router/java/src/org/cybergarage/upnp/ssdp/SSDPSearchSocketList.java b/router/java/src/org/cybergarage/upnp/ssdp/SSDPSearchSocketList.java
index 7fc37241160b33ed0ef0f6368c6101b577aa5e80..185bb15fcc14499a212a68d2f624719d65388390 100644
--- a/router/java/src/org/cybergarage/upnp/ssdp/SSDPSearchSocketList.java
+++ b/router/java/src/org/cybergarage/upnp/ssdp/SSDPSearchSocketList.java
@@ -1,105 +1,151 @@
-/******************************************************************
-*
-*	CyberUPnP for Java
-*
-*	Copyright (C) Satoshi Konno 2002-2003
+/******************************************************************
 *
-*	File: SSDPSearchSocketList.java
-*
-*	Revision;
-*
-*	05/08/03
-*		- first revision.
-*	05/28/03
-*		- Moved post() for SSDPSearchRequest to SSDPResponseSocket.
-*		- Removed open(int).
-*
-******************************************************************/
+*	CyberUPnP for Java
+*
+*	Copyright (C) Satoshi Konno 2002-2003
+*
+*	File: SSDPSearchSocketList.java
+*
+*	Revision;
+*
+*	05/08/03
+*		- first revision.
+*	05/28/03
+*		- Moved post() for SSDPSearchRequest to SSDPResponseSocket.
+*		- Removed open(int).
+*
+******************************************************************/
+
+package org.cybergarage.upnp.ssdp;
+
+import java.net.InetAddress;
+import java.util.Vector;
+
+import org.cybergarage.net.HostInterface;
+import org.cybergarage.upnp.device.SearchListener;
+
+public class SSDPSearchSocketList extends Vector 
+{
+	////////////////////////////////////////////////
+	//	Constructor
+	////////////////////////////////////////////////
+	
+	private InetAddress[] binds = null;
+	private String multicastIPv4 = SSDP.ADDRESS;
+	private String multicastIPv6 = SSDP.getIPv6Address();
+	private int port = SSDP.PORT;
+
+	public SSDPSearchSocketList() 
+	{
+	}
+	/**
+	 * 
+	 * @param binds The IP address that we will used for bindind the service 
+	 */
+	public SSDPSearchSocketList(InetAddress[] binds) {
+		this.binds = binds;
+	}	
+
+	/**
+	 * 
+	 * @param binds The IP address that we will used for bindind the service
+	 * @param port	The port that we will used for bindind the service
+	 * @param multicastIPv4 The IPv4 address that we will used for multicast comunication
+	 * @param multicastIPv6 The IPv6 address that we will used for multicast comunication
+	 * @since 1.8
+	 */
+	public SSDPSearchSocketList(InetAddress[] binds,int port, String multicastIPv4, String multicastIPv6) {
+		this.binds = binds;
+		this.port = port;
+		this.multicastIPv4 = multicastIPv4;
+		this.multicastIPv6 = multicastIPv6;
+	}
+
+	
+	
+
+	////////////////////////////////////////////////
+	//	Methods
+	////////////////////////////////////////////////
+	
+	public SSDPSearchSocket getSSDPSearchSocket(int n)
+	{
+		return (SSDPSearchSocket)get(n);
+	}
+	
+	public void addSearchListener(SearchListener listener)
+	{
+		int nServers = size();
+		for (int n=0; n<nServers; n++) {
+			SSDPSearchSocket sock = getSSDPSearchSocket(n);
+			sock.addSearchListener(listener);
+		}
+	}		
+
+	////////////////////////////////////////////////
+	//	Methods
+	////////////////////////////////////////////////
+	
+	public boolean open() {
+		InetAddress[] binds=this.binds;
+		String[] bindAddresses;
+		if(binds!=null){			
+			bindAddresses = new String[binds.length];
+			for (int i = 0; i < binds.length; i++) {
+				bindAddresses[i] = binds[i].getHostAddress();
+			}
+		}else{
+			int nHostAddrs = HostInterface.getNHostAddresses();
+			bindAddresses = new String[nHostAddrs]; 
+			for (int n=0; n<nHostAddrs; n++) {
+				bindAddresses[n] = HostInterface.getHostAddress(n);
+			}
+		}		
+		
+		for (int i = 0; i < bindAddresses.length; i++) {
+			if(bindAddresses[i]!=null){
+				SSDPSearchSocket ssdpSearchSocket;
+				if(HostInterface.isIPv6Address(bindAddresses[i]))
+					ssdpSearchSocket = new SSDPSearchSocket(bindAddresses[i],port ,multicastIPv6 );
+				else
+					ssdpSearchSocket = new SSDPSearchSocket(bindAddresses[i],port,multicastIPv4 );
+				add(ssdpSearchSocket);
+			}
+		}
+		return true;
+	}
+		
+	public void close()
+	{
+		int nSockets = size();
+		for (int n=0; n<nSockets; n++) {
+			SSDPSearchSocket sock = getSSDPSearchSocket(n);
+			sock.close();
+		}
+		clear();
+	}
+	
+	////////////////////////////////////////////////
+	//	Methods
+	////////////////////////////////////////////////
+	
+	public void start()
+	{
+		int nSockets = size();
+		for (int n=0; n<nSockets; n++) {
+			SSDPSearchSocket sock = getSSDPSearchSocket(n);
+			sock.start();
+		}
+	}
+
+	public void stop()
+	{
+		int nSockets = size();
+		for (int n=0; n<nSockets; n++) {
+			SSDPSearchSocket sock = getSSDPSearchSocket(n);
+			sock.stop();
+		}
+	}
+
+}
 
-package org.cybergarage.upnp.ssdp;
-
-import java.util.*;
-
-import org.cybergarage.net.*;
-
-import org.cybergarage.upnp.device.*;
-
-public class SSDPSearchSocketList extends Vector 
-{
-	////////////////////////////////////////////////
-	//	Constructor
-	////////////////////////////////////////////////
-	
-	private static final long serialVersionUID = 4071292828166415028L;
-
-	public SSDPSearchSocketList() 
-	{
-	}
-
-	////////////////////////////////////////////////
-	//	Methods
-	////////////////////////////////////////////////
-	
-	public SSDPSearchSocket getSSDPSearchSocket(int n)
-	{
-		return (SSDPSearchSocket)get(n);
-	}
-	
-	public void addSearchListener(SearchListener listener)
-	{
-		int nServers = size();
-		for (int n=0; n<nServers; n++) {
-			SSDPSearchSocket sock = getSSDPSearchSocket(n);
-			sock.addSearchListener(listener);
-		}
-	}		
-
-	////////////////////////////////////////////////
-	//	Methods
-	////////////////////////////////////////////////
-	
-	public boolean open() 
-	{
-		int nHostAddrs = HostInterface.getNHostAddresses();
-		for (int n=0; n<nHostAddrs; n++) {
-			String bindAddr = HostInterface.getHostAddress(n);
-			SSDPSearchSocket ssdpSearchSocket = new SSDPSearchSocket(bindAddr);
-			add(ssdpSearchSocket);
-		}
-		return true;
-	}
-		
-	public void close()
-	{
-		int nSockets = size();
-		for (int n=0; n<nSockets; n++) {
-			SSDPSearchSocket sock = getSSDPSearchSocket(n);
-			sock.close();
-		}
-		clear();
-	}
-	
-	////////////////////////////////////////////////
-	//	Methods
-	////////////////////////////////////////////////
-	
-	public void start()
-	{
-		int nSockets = size();
-		for (int n=0; n<nSockets; n++) {
-			SSDPSearchSocket sock = getSSDPSearchSocket(n);
-			sock.start();
-		}
-	}
-
-	public void stop()
-	{
-		int nSockets = size();
-		for (int n=0; n<nSockets; n++) {
-			SSDPSearchSocket sock = getSSDPSearchSocket(n);
-			sock.stop();
-		}
-	}
-
-}
-
diff --git a/router/java/src/org/cybergarage/upnp/xml/DeviceData.java b/router/java/src/org/cybergarage/upnp/xml/DeviceData.java
index dad0a97b9648b00a8e1120753b915e9831a239bd..8ed1f443e300e45c4391cb1895823211454501e7 100644
--- a/router/java/src/org/cybergarage/upnp/xml/DeviceData.java
+++ b/router/java/src/org/cybergarage/upnp/xml/DeviceData.java
@@ -18,6 +18,7 @@
 package org.cybergarage.upnp.xml;
 
 import java.io.*;
+import java.net.InetAddress;
 
 import org.cybergarage.util.*;
 import org.cybergarage.http.*;
@@ -89,10 +90,23 @@ public class DeviceData extends NodeData
 	//	HTTPServer 
 	////////////////////////////////////////////////
 
-	private HTTPServerList httpServerList = new HTTPServerList();		
+	private HTTPServerList httpServerList = null;		
 
 	public HTTPServerList getHTTPServerList() {
-		return httpServerList;
+		if(this.httpServerList==null){
+			this.httpServerList = new HTTPServerList(this.httpBinds,this.httpPort);
+		}
+		return this.httpServerList;
+	}
+	
+	private InetAddress[] httpBinds = null;
+	
+	public void setHTTPBindAddress(InetAddress[] inets){
+		this.httpBinds=inets;
+	}
+	
+	public InetAddress[] getHTTPBindAddress(){
+		return this.httpBinds;
 	}
 
 	////////////////////////////////////////////////
@@ -129,11 +143,98 @@ public class DeviceData extends NodeData
 	// SSDPSearchSocket
 	////////////////////////////////////////////////
 	
-	private SSDPSearchSocketList ssdpSearchSocketList = new SSDPSearchSocketList();
+	private SSDPSearchSocketList ssdpSearchSocketList = null;
+	private String ssdpMulticastIPv4 = SSDP.ADDRESS;
+	private String ssdpMulticastIPv6 = SSDP.getIPv6Address();
+	private int ssdpPort = SSDP.PORT;
+	private InetAddress[] ssdpBinds = null;
 	
 	public SSDPSearchSocketList getSSDPSearchSocketList() {
+		if(this.ssdpSearchSocketList==null){
+			this.ssdpSearchSocketList = new SSDPSearchSocketList(this.ssdpBinds,ssdpPort,ssdpMulticastIPv4,ssdpMulticastIPv6);			
+		}
 		return ssdpSearchSocketList;
 	}
+	
+	/**
+	 * 
+	 * @param port The port to use for binding the SSDP service. 
+	 * 		The port will be used as source port for all SSDP messages 
+	 * @since 1.8
+	 */
+	public void setSSDPPort(int port){
+		this.ssdpPort=port;
+	}
+
+	/**
+	 * 
+	 * @return The port used for binding the SSDP service. 
+	 * 		The port will be used as source port for all SSDP messages 
+	 */
+	public int getSSDPPort(){
+		return this.ssdpPort;
+	}
+	
+	
+	/**
+	 * 
+	 * @param inets The <tt>InetAddress</tt> that will be binded for listing this service. 
+	 * 		Use <code>null</code> for the default behaviur. 
+	 * @see {@link UPnP}
+	 * @see {@link USSDP}
+	 * @see {@link HostInterface} 
+	 * @since 1.8 
+	 */
+	public void setSSDPBindAddress(InetAddress[] inets){
+		this.ssdpBinds=inets;
+	}
+	
+	/**
+	 * 
+	 * @return inets The <tt>InetAddress</tt> that will be binded for this service
+	 * 		<code>null</code> means that defulat behaviur will be used
+	 * @since 1.8
+	 */	
+	public InetAddress[] getSSDPBindAddress(){
+		return this.ssdpBinds;
+	}
+	
+	/**
+	 * 
+	 * @param ip The IPv4 address used as destination address for Multicast comunication
+	 * @since 1.8
+	 */
+	public void setMulticastIPv4Address(String ip){
+		this.ssdpMulticastIPv4=ip;
+	}
+
+	/**
+	 * 
+	 * @return The IPv4 address used for Multicast comunication
+	 */
+	public String getMulticastIPv4Address(){
+		return this.ssdpMulticastIPv4;
+	}
+	
+	/**
+	 * 
+	 * @param ip The IPv6 address used as destination address for Multicast comunication
+	 * @since 1.8
+	 */
+	public void setMulticastIPv6Address(String ip){
+		this.ssdpMulticastIPv6=ip;
+	}
+
+	/**
+	 * 
+	 * @return The IPv6 address used as destination address for Multicast comunication
+	 * @since 1.8
+	 */
+	public String getMulticastIPv6Address(){
+		return this.ssdpMulticastIPv6;
+	}
+	
+	
 
 	////////////////////////////////////////////////
 	// SSDPPacket
diff --git a/router/java/src/org/cybergarage/upnp/xml/ServiceData.java b/router/java/src/org/cybergarage/upnp/xml/ServiceData.java
index bb4dfe979849691e451e42966aee34d4072336c0..5f5eeefda5863ff2a06b0ec135ec4d9c536312ff 100644
--- a/router/java/src/org/cybergarage/upnp/xml/ServiceData.java
+++ b/router/java/src/org/cybergarage/upnp/xml/ServiceData.java
@@ -68,6 +68,20 @@ public class ServiceData extends NodeData
 	// SID
 	////////////////////////////////////////////////
 
+    private String descriptionURL = "";
+
+    public String getDescriptionURL() {
+            return descriptionURL;
+    }
+
+    public void setDescriptionURL(String descriptionURL) {
+            this.descriptionURL = descriptionURL;
+    }
+	
+	////////////////////////////////////////////////
+	// SID
+	////////////////////////////////////////////////
+
 	private String sid = "";
 	
 	public String getSID() {
diff --git a/router/java/src/org/cybergarage/util/Debug.java b/router/java/src/org/cybergarage/util/Debug.java
index 6cd9d939e883ef347272ed92a14ce838a059decc..2976e8cac1fbf2b9d4ffc1f171689b7c51973b04 100644
--- a/router/java/src/org/cybergarage/util/Debug.java
+++ b/router/java/src/org/cybergarage/util/Debug.java
@@ -1,51 +1,81 @@
-/******************************************************************
-*
-*	CyberUtil for Java
-*
-*	Copyright (C) Satoshi Konno 2002
+/******************************************************************
 *
-*	File: Debug.java
-*
-*	Revision;
-*
-*	11/18/02
-*		- first revision.
-*
-******************************************************************/
+*	CyberUtil for Java
+*
+*	Copyright (C) Satoshi Konno 2002
+*
+*	File: Debug.java
+*
+*	Revision;
+*
+*	11/18/02
+*		- first revision.
+*
+******************************************************************/
+
+package org.cybergarage.util;
 
-package org.cybergarage.util;
+import java.io.PrintStream;
 
-public final class Debug
-{
-	public static boolean enabled = false;
-	
-	public static final void on() {
-		enabled = true;
+public final class Debug{		
+	
+	public static Debug debug = new Debug();
+	
+	private PrintStream out = System.out;
+	
+	
+	public Debug(){
+		
 	}
-	public static final void off() {
-		enabled = false;
+	
+	public synchronized PrintStream getOut() {
+		return out;
 	}
-	public static boolean isOn() {
-		return enabled;
+	public synchronized void setOut(PrintStream out) {
+		this.out = out;
+	}
+	
+	
+	public static boolean enabled = false;
+
+	public static Debug getDebug(){
+		return Debug.debug;
+	}
+	
+	public static final void on() {
+		enabled = true;
+	}
+	public static final void off() {
+		enabled = false;
+	}
+	public static boolean isOn() {
+		return enabled;
 	}
 	public static final void message(String s) {
 		if (enabled == true)
-			System.out.println("CyberGarage message : " + s);
-	}
-	public static final void message(String m1, String m2) {
-		if (enabled == true)
-			System.out.println("CyberGarage message : ");
-			System.out.println(m1);
-			System.out.println(m2);
-	}
+			Debug.debug.getOut().println("CyberGarage message : " + s);
+	}
+	public static final void message(String m1, String m2) {
+		if (enabled == true)
+			Debug.debug.getOut().println("CyberGarage message : ");
+			Debug.debug.getOut().println(m1);
+			Debug.debug.getOut().println(m2);
+	}
 	public static final void warning(String s) {
-		System.out.println("CyberGarage warning : " + s);
+		Debug.debug.getOut().println("CyberGarage warning : " + s);
+	}
+	public static final void warning(String m, Exception e) {
+		if(e.getMessage()==null){
+			Debug.debug.getOut().println("CyberGarage warning : " + m + " START");
+			e.printStackTrace(Debug.debug.getOut());
+			Debug.debug.getOut().println("CyberGarage warning : " + m + " END");
+		}else{
+			Debug.debug.getOut().println("CyberGarage warning : " + m + " (" + e.getMessage() + ")");
+			e.printStackTrace(Debug.debug.getOut());
+		}
 	}
-	public static final void warning(String m, Exception e) {
-		System.out.println("CyberGarage warning : " + m + " (" + e.getMessage() + ")");
-	}
-	public static final void warning(Exception e) {
+	public static final void warning(Exception e) {
 		warning(e.getMessage());
-		e.printStackTrace();
+		e.printStackTrace(Debug.debug.getOut());
 	}
-}
\ No newline at end of file
+}
diff --git a/router/java/src/org/cybergarage/util/FileUtil.java b/router/java/src/org/cybergarage/util/FileUtil.java
index 5e9b54a0dd193c3caabcac768fdb8879ff867fbc..ef79f98349a6b899135fdcb2ef2f129c8d6fdb46 100644
--- a/router/java/src/org/cybergarage/util/FileUtil.java
+++ b/router/java/src/org/cybergarage/util/FileUtil.java
@@ -15,7 +15,9 @@
 
 package org.cybergarage.util;
 
-import java.io.*;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
 
 public final class FileUtil
 {
diff --git a/router/java/src/org/cybergarage/util/ListenerList.java b/router/java/src/org/cybergarage/util/ListenerList.java
index afdeff0d65804d322a00d2e8eb55d4b0080438a9..19f5901a1b3d50a2824445c323f4a052c8e83e0a 100644
--- a/router/java/src/org/cybergarage/util/ListenerList.java
+++ b/router/java/src/org/cybergarage/util/ListenerList.java
@@ -15,12 +15,10 @@
 
 package org.cybergarage.util;
 
-import java.util.*;
+import java.util.Vector;
 
 public class ListenerList extends Vector
 {
-	private static final long serialVersionUID = 8039231561720446173L;
-
 	public boolean add(Object obj)
 	{
 		if (0 <= indexOf(obj))
diff --git a/router/java/src/org/cybergarage/util/ThreadCore.java b/router/java/src/org/cybergarage/util/ThreadCore.java
index b03f2f0c35993b8c2b6d4b1cbac1150439fd11ed..245798dd6285bd5a822b442bbcc6e99d624d6dce 100644
--- a/router/java/src/org/cybergarage/util/ThreadCore.java
+++ b/router/java/src/org/cybergarage/util/ThreadCore.java
@@ -10,6 +10,9 @@
 *
 *	01/05/04
 *		- first revision.
+*	08/23/07
+*		- Thanks for Kazuyuki Shudo
+*		- Changed stop() to stop more safety using Thread::interrupt().
 *	
 ******************************************************************/
 
@@ -43,7 +46,7 @@ public class ThreadCore implements Runnable
 	{
 		java.lang.Thread threadObject = getThreadObject();
 		if (threadObject == null) {
-			threadObject = new java.lang.Thread(this);
+			threadObject = new java.lang.Thread(this,"Cyber.ThreadCore");
 			setThreadObject(threadObject);
 			threadObject.start();
 		}
@@ -64,6 +67,10 @@ public class ThreadCore implements Runnable
 		if (threadObject != null) { 
 			//threadObject.destroy();
 			//threadObject.stop();
+			
+			// Thanks for Kazuyuki Shudo (08/23/07)
+			threadObject.interrupt();
+			
 			setThreadObject(null);
 		}
 	}
diff --git a/router/java/src/org/cybergarage/xml/AttributeList.java b/router/java/src/org/cybergarage/xml/AttributeList.java
index 8bb85cd016498a54f3df679f448428ded0c9a9ae..7a0e67091ac7ece115e5ffbc8e3c74642640409a 100644
--- a/router/java/src/org/cybergarage/xml/AttributeList.java
+++ b/router/java/src/org/cybergarage/xml/AttributeList.java
@@ -15,12 +15,10 @@
 
 package org.cybergarage.xml;
 
-import java.util.*;
+import java.util.Vector;
 
 public class AttributeList extends Vector 
 {
-	private static final long serialVersionUID = -5516389508555401104L;
-
 	public AttributeList() 
 	{
 	}
diff --git a/router/java/src/org/cybergarage/xml/Node.java b/router/java/src/org/cybergarage/xml/Node.java
index 93ca5aa0b38b351edb7b75243ecfb700239e3f40..7e34a084f7d35c3851dae420885da7f57248b21b 100644
--- a/router/java/src/org/cybergarage/xml/Node.java
+++ b/router/java/src/org/cybergarage/xml/Node.java
@@ -1,415 +1,468 @@
-/******************************************************************
-*
-*	CyberXML for Java
-*
-*	Copyright (C) Satoshi Konno 2002
-*
-*	File: Element.java
-*
-*	Revision;
-*
-*	11/27/02
-*		- first revision.
-*	11/01/03
-*		- Terje Bakken
-*		- fixed missing escaping of reserved XML characters
-*	11/19/04
-*		- Theo Beisch <theo.beisch@gmx.de>
-*		- Added "&" and "\"" "\\" to toXMLString().
-*	11/19/04
-*		- Theo Beisch <theo.beisch@gmx.de>
-*		- Changed XML::output() to use short notation when the tag value is null.
-*	12/02/04
-*		- Brian Owens <brian@b-owens.com>
-*		- Fixed toXMLString() to convert from "'" to "&apos;" instead of "\".
-*
-******************************************************************/
-
-package org.cybergarage.xml;
-
-import java.io.ByteArrayOutputStream;
-import java.io.PrintWriter;
-
-public class Node
-{
-
-	public Node()
-	{
-		setUserData(null);
-		setParentNode(null);
-	}
-
-	public Node(String name)
-	{
-		this();
-		setName(name);
-	}
-
-	public Node(String ns, String name)
-	{
-		this();
-		setName(ns, name);
-	}
-
-	////////////////////////////////////////////////
-	//	parent node
-	////////////////////////////////////////////////
-
-	private Node parentNode = null;
-	
-	public void setParentNode(Node node)
-	{
-		parentNode = node;
-	}
-
-	public Node getParentNode()
-	{
-		return parentNode;
-	}
-
-	////////////////////////////////////////////////
-	//	root node
-	////////////////////////////////////////////////
-
-	public Node getRootNode()
-	{
-		Node rootNode = null;
-		Node parentNode = getParentNode();
-		while (parentNode != null) {
-			 rootNode = parentNode;
-			 parentNode = rootNode.getParentNode();
-		}
-		return rootNode;
-	}
-
-	////////////////////////////////////////////////
-	//	name
-	////////////////////////////////////////////////
-
-	private String name = new String();
-	
-	public void setName(String name)
-	{
-		this.name = name;
-	}
-
-	public void setName(String ns, String name)
-	{
-		this.name = ns + ":" + name;
-	}
-
-	public String getName()
-	{
-		return name;
-	}
-
-	public boolean isName(String value)
-	{
-		return name.equals(value);
-	}
-	
-	////////////////////////////////////////////////
-	//	value
-	////////////////////////////////////////////////
-
-	private String value = "";
-	
-	public void setValue(String value)
-	{
-		this.value = value;
-	}
-
-	public void setValue(int value)
-	{
-		setValue(Integer.toString(value));
-	}
-
-	public String getValue()
-	{
-		return value;
-	}
-
-	////////////////////////////////////////////////
-	//	Attribute (Basic)
-	////////////////////////////////////////////////
-
-	private AttributeList attrList = new AttributeList();
-
-	public int getNAttributes() {
-		return attrList.size();
-	}
-
-	public Attribute getAttribute(int index) {
-		return attrList.getAttribute(index);
-	}
-
-	public Attribute getAttribute(String name)
-	{
-		return attrList.getAttribute(name);
-	}
-
-	public void addAttribute(Attribute attr) {
-		attrList.add(attr);
-	}
-
-	public void insertAttributeAt(Attribute attr, int index) {
-		attrList.insertElementAt(attr, index);
-	}
-
-	public void addAttribute(String name, String value) {
-		Attribute attr = new Attribute(name, value);
-		addAttribute(attr);
-	}
-
-	public boolean removeAttribute(Attribute attr) {
-		return attrList.remove(attr);
-	}
-
-	public boolean removeAttribute(String name) {
-		return removeAttribute(getAttribute(name));
-	}
-
-	public boolean hasAttributes()
-	{
-		if (0 < getNAttributes())
-			return true;
-		return false;
-	}
-
-	////////////////////////////////////////////////
-	//	Attribute (Extention)
-	////////////////////////////////////////////////
-
-	public void setAttribute(String name, String value) {
-		Attribute attr = getAttribute(name);
-		if (attr != null) {
-			attr.setValue(value);
-			return;
-		}
-		attr = new Attribute(name, value);
-		addAttribute(attr);
-	}
-
-	public void setAttribute(String name, int value) {
-		setAttribute(name, Integer.toString(value));
-	}
-
-	public String getAttributeValue(String name) {
-		Attribute attr = getAttribute(name);
-		if (attr != null)
-			return attr.getValue();
-		return "";
-	}
-
-	public int getAttributeIntegerValue(String name) {
-		String val = getAttributeValue(name);
-		try {
-			return Integer.parseInt(val);
-		}
-		catch (Exception e) {}
-		return 0;
-	}
-	
-	////////////////////////////////////////////////
-	//	Attribute (xmlns)
-	////////////////////////////////////////////////
-
-	public void setNameSpace(String ns, String value)
-	{
-		setAttribute("xmlns:" + ns, value);
-	}
-		
-	////////////////////////////////////////////////
-	//	Child node
-	////////////////////////////////////////////////
-
-	private NodeList nodeList = new NodeList();
-
-	public int getNNodes() {
-		return nodeList.size();
-	}
-
-	public Node getNode(int index) {
-		return nodeList.getNode(index);
-	}
-
-	public Node getNode(String name)
-	{
-		return nodeList.getNode(name);
-	}
-	
-	public Node getNodeEndsWith(String name)
-	{
-		return nodeList.getEndsWith(name);
-	}
-
-	public void addNode(Node node) {
-		node.setParentNode(this);
-		nodeList.add(node);
-	}
-
-	public void insertNode(Node node, int index) {
-		node.setParentNode(this);
-		nodeList.insertElementAt(node, index);
-	}
-
-	public boolean removeNode(Node node) {
-		node.setParentNode(null);
-		return nodeList.remove(node);
-	}
-
-	public boolean removeNode(String name) {
-		return nodeList.remove(getNode(name));
-	}
-
-	public void removeAllNodes()
-	{
-		nodeList.clear();
-	}
-	
-	public boolean hasNodes()
-	{
-		if (0 < getNNodes())
-			return true;
-		return false;
-	}
-	
-	////////////////////////////////////////////////
-	//	Element (Child Node)
-	////////////////////////////////////////////////
-
-	public void setNode(String name, String value) {
-		Node node = getNode(name);
-		if (node != null) {
-			node.setValue(value);
-			return;
-		}
-		node = new Node(name);
-		node.setValue(value);
-		addNode(node);
-	}
-
-	public String getNodeValue(String name) {
-		Node node = getNode(name);
-		if (node != null)
-			return node.getValue();
-		return "";
-	}
-
-	////////////////////////////////////////////////
-	//	userData
-	////////////////////////////////////////////////
-
-	private Object userData = null;
-	
-	public void setUserData(Object data)
-	{
-		userData = data;
-	}
-
-	public Object getUserData()
-	{
-		return userData;
-	}
-
-	
-	////////////////////////////////////////////////
-	//	toString
-	////////////////////////////////////////////////
-
-	public String getIndentLevelString(int nIndentLevel)
-	{
-		char indentString[] = new char[nIndentLevel];
-		for (int n=0; n<nIndentLevel; n++)
-			indentString[n] = '\t' ;
-		return new String(indentString);
-	}
-
-	public void outputAttributes(PrintWriter ps)
-	{
-		int nAttributes = getNAttributes();
-		for (int n=0; n<nAttributes; n++) {
-			Attribute attr = getAttribute(n);
-			ps.print(" " + attr.getName() + "=\"" + XML.escapeXMLChars(attr.getValue()) + "\"");
-		}
-	}
-
-	public void output(PrintWriter ps, int indentLevel, boolean hasChildNode)
-	{
-		String indentString = getIndentLevelString(indentLevel);
-
-		String name = getName();
-		String value = getValue();
-
-		if (hasNodes() == false || hasChildNode == false) {
-			ps.print(indentString + "<" + name);
-			outputAttributes(ps);
-			// Thnaks for Tho Beisch (11/09/04)
-			if (value == null || value.length() == 0) {
-				// No value, so use short notation <node />
-				ps.println(" />");
-			} else {
-				ps.println(">" + XML.escapeXMLChars(value) + "</" + name + ">");
-			}
-			
-			return;
-		}
-		
-		ps.print(indentString + "<" + name);
-		outputAttributes(ps);
-		ps.println(">");
-	
-		int nChildNodes = getNNodes();
-		for (int n=0; n<nChildNodes; n++) {
-			Node cnode = getNode(n);
-			cnode.output(ps, indentLevel+1, true);
-		}
-
-		ps.println(indentString +"</" + name + ">");
-	}
-
-	public String toString(boolean hasChildNode)
-	{
-		ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
-		PrintWriter pr = new PrintWriter(byteOut);
-		output(pr, 0, hasChildNode);
-		pr.flush();
-		return byteOut.toString();
-	}
-		
-	@Override
-    public String toString()
-	{
-		return toString(true);
-	}
-	
-	public String toXMLString(boolean hasChildNode)
-	{
-		String xmlStr = toString();
-		xmlStr = xmlStr.replaceAll("<", "&lt;");
-		xmlStr = xmlStr.replaceAll(">", "&gt;");
-		// Thanks for Theo Beisch (11/09/04)
-		xmlStr = xmlStr.replaceAll("&", "&amp;");
-		xmlStr = xmlStr.replaceAll("\"", "&quot;");
-		// Thanks for Brian Owens (12/02/04)
-		xmlStr = xmlStr.replaceAll("'", "&apos;");
-		return xmlStr;
-	}
-
-	public String toXMLString()
-	{
-		return toXMLString(true);
-	}
-	
-	public void print(boolean hasChildNode)
-	{
-		PrintWriter pr = new PrintWriter(System.out);
-		output(pr, 0, hasChildNode);
-		pr.flush();
-	}
-
-	public void print()
-	{
-		print(true);
-	}
-}
+/******************************************************************
+*
+*	CyberXML for Java
+*
+*	Copyright (C) Satoshi Konno 2002
+*
+*	File: Element.java
+*
+*	Revision;
+*
+*	11/27/02
+*		- first revision.
+*	11/01/03
+*		- Terje Bakken
+*		- fixed missing escaping of reserved XML characters
+*	11/19/04
+*		- Theo Beisch <theo.beisch@gmx.de>
+*		- Added "&" and "\"" "\\" to toXMLString().
+*	11/19/04
+*		- Theo Beisch <theo.beisch@gmx.de>
+*		- Changed XML::output() to use short notation when the tag value is null.
+*	12/02/04
+*		- Brian Owens <brian@b-owens.com>
+*		- Fixed toXMLString() to convert from "'" to "&apos;" instead of "\".
+*	11/07/05
+*		- Changed toString() to return as utf-8 string.
+*	02/08/08
+*		- Added addValue().
+*
+******************************************************************/
+
+package org.cybergarage.xml;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.Iterator;
+
+public class Node 
+{
+
+	/**
+	 * Create a Node with empty UserData and no Parent Node
+	 *
+	 */
+	public Node() 
+	{
+		setUserData(null);
+		setParentNode(null);
+	}
+
+	public Node(String name) 
+	{
+		this();
+		setName(name);
+	}
+
+	public Node(String ns, String name) 
+	{
+		this();
+		setName(ns, name);
+	}
+
+	////////////////////////////////////////////////
+	//	parent node
+	////////////////////////////////////////////////
+
+	private Node parentNode = null; 
+	
+	public void setParentNode(Node node) 
+	{
+		parentNode = node;
+	}
+
+	public Node getParentNode() 
+	{
+		return parentNode;
+	}
+
+	////////////////////////////////////////////////
+	//	root node
+	////////////////////////////////////////////////
+
+	public Node getRootNode() 
+	{
+		Node rootNode = null;
+		Node parentNode = getParentNode();
+		while (parentNode != null) {
+			 rootNode = parentNode;
+			 parentNode = rootNode.getParentNode();
+		}
+		return rootNode;
+	}
+
+	////////////////////////////////////////////////
+	//	name
+	////////////////////////////////////////////////
+
+	private String name = new String(); 
+	
+	public void setName(String name) 
+	{
+		this.name = name;
+	}
+
+	public void setName(String ns, String name) 
+	{
+		this.name = ns + ":" + name;
+	}
+
+	public String getName() 
+	{
+		return name;
+	}
+
+	public boolean isName(String value)
+	{
+		return name.equals(value);	
+	}
+	
+	////////////////////////////////////////////////
+	//	value
+	////////////////////////////////////////////////
+
+	private String value = new String(); 
+	
+	public void setValue(String value) 
+	{
+		this.value = value;
+	}
+
+	public void setValue(int value) 
+	{
+		setValue(Integer.toString(value));
+	}
+
+	public void addValue(String value) 
+	{
+		if (this.value == null) {
+			this.value = value;
+			return;
+		}
+		if (value != null)
+			this.value += value;
+	}
+	
+	public String getValue()
+	{
+		return value;
+	}
+
+	////////////////////////////////////////////////
+	//	Attribute (Basic)
+	////////////////////////////////////////////////
+
+	private AttributeList attrList = new AttributeList();
+
+	public int getNAttributes() {
+		return attrList.size();
+	}
+
+	public Attribute getAttribute(int index) {
+		return attrList.getAttribute(index);
+	}
+
+	public Attribute getAttribute(String name) 
+	{
+		return attrList.getAttribute(name);
+	}
+
+	public void addAttribute(Attribute attr) {
+		attrList.add(attr);
+	}
+
+	public void insertAttributeAt(Attribute attr, int index) {
+		attrList.insertElementAt(attr, index);
+	}
+
+	public void addAttribute(String name, String value) {
+		Attribute attr = new Attribute(name, value);
+		addAttribute(attr);
+	}
+
+	public boolean removeAttribute(Attribute attr) {
+		return attrList.remove(attr);
+	}
+
+	public boolean removeAttribute(String name) {
+		return removeAttribute(getAttribute(name));
+	}
+
+	public boolean hasAttributes()
+	{
+		if (0 < getNAttributes())
+			return true;
+		return false;
+	}
+
+	////////////////////////////////////////////////
+	//	Attribute (Extention)
+	////////////////////////////////////////////////
+
+	public void setAttribute(String name, String value) {
+		Attribute attr = getAttribute(name);
+		if (attr != null) {
+			attr.setValue(value);
+			return;
+		}
+		attr = new Attribute(name, value);
+		addAttribute(attr);
+	}
+
+	public void setAttribute(String name, int value) {
+		setAttribute(name, Integer.toString(value));
+	}
+
+	public String getAttributeValue(String name) {
+		Attribute attr = getAttribute(name);
+		if (attr != null)
+			return attr.getValue();
+		return "";
+	}
+
+	public int getAttributeIntegerValue(String name) {
+		String val = getAttributeValue(name);
+		try {
+			return Integer.parseInt(val);
+		}
+		catch (Exception e) {}
+		return 0;
+	}
+	
+	////////////////////////////////////////////////
+	//	Attribute (xmlns)
+	////////////////////////////////////////////////
+
+	public void setNameSpace(String ns, String value) 
+	{
+		setAttribute("xmlns:" + ns, value);
+	}
+		
+	////////////////////////////////////////////////
+	//	Child node
+	////////////////////////////////////////////////
+
+	private NodeList nodeList = new NodeList();
+
+	public int getNNodes() {
+		return nodeList.size();
+	}
+
+	public Node getNode(int index) {
+		return nodeList.getNode(index);
+	}
+
+	public Node getNode(String name) 
+	{
+		return nodeList.getNode(name);
+	}
+	
+	public Node getNodeEndsWith(String name) 
+	{
+		return nodeList.getEndsWith(name);
+	}
+
+	public void addNode(Node node) {
+		node.setParentNode(this);
+		nodeList.add(node);
+	}
+
+	public void insertNode(Node node, int index) {
+		node.setParentNode(this);
+		nodeList.insertElementAt(node, index);
+	}
+
+	public int getIndex(String name){
+		int index = -1;
+		for (Iterator i = nodeList.iterator(); i.hasNext();) {
+			index++;
+			Node n = (Node) i.next();
+			if(n.getName().equals(name))
+				return index;
+		}
+		return index;
+	}
+
+	public boolean removeNode(Node node) {
+		node.setParentNode(null);
+		return nodeList.remove(node);
+	}
+
+	public boolean removeNode(String name) {
+		return nodeList.remove(getNode(name));
+	}
+
+	public void removeAllNodes()
+	{
+		nodeList.clear();
+	}
+	
+	public boolean hasNodes()
+	{
+		if (0 < getNNodes())
+			return true;
+		return false;
+	}
+	
+	////////////////////////////////////////////////
+	//	Element (Child Node)
+	////////////////////////////////////////////////
+
+	public void setNode(String name, String value) {
+		Node node = getNode(name);
+		if (node != null) {
+			node.setValue(value);
+			return;
+		}
+		node = new Node(name);
+		node.setValue(value);
+		addNode(node);
+	}
+
+	public String getNodeValue(String name) {
+		Node node = getNode(name);
+		if (node != null)
+			return node.getValue();
+		return "";
+	}
+
+	////////////////////////////////////////////////
+	//	userData
+	////////////////////////////////////////////////
+
+	private Object userData = null; 
+	
+	public void setUserData(Object data) 
+	{
+		userData = data;
+	}
+
+	public Object getUserData() 
+	{
+		return userData;
+	}
+	
+	////////////////////////////////////////////////
+	//	toString 
+	////////////////////////////////////////////////
+
+	/**
+	 * Inovoke {@link #getIndentLevelString(int, String)} with <code>"   "</code> as String 
+	 * 
+	 * @see #getIndentLevelString(int, String)
+	 */
+	public String getIndentLevelString(int nIndentLevel) 
+	{
+		return getIndentLevelString(nIndentLevel,"   ");
+	}
+
+	/**
+	 * 
+	 * @param nIndentLevel the level of indentation to produce 
+	 * @param space the String to use for the intendation 
+	 * @since 1.8.0
+	 * @return an indentation String
+	 */
+	public String getIndentLevelString(int nIndentLevel,String space) 
+	{
+		StringBuffer indentString = new StringBuffer(nIndentLevel*space.length()); 
+		for (int n=0; n<nIndentLevel; n++){
+			indentString.append(space);
+		}
+		return indentString.toString();
+	}	
+	
+	public void outputAttributes(PrintWriter ps)
+	{
+		int nAttributes = getNAttributes();
+		for (int n=0; n<nAttributes; n++) {
+			Attribute attr = getAttribute(n);
+			ps.print(" " + attr.getName() + "=\"" + XML.escapeXMLChars(attr.getValue()) + "\"");
+		}
+	}
+
+	public void output(PrintWriter ps, int indentLevel, boolean hasChildNode) 
+	{
+		String indentString = getIndentLevelString(indentLevel);
+
+		String name = getName();
+		String value = getValue();
+
+		if (hasNodes() == false || hasChildNode == false) {		
+			ps.print(indentString + "<" + name);
+			outputAttributes(ps);
+			// Thnaks for Tho Beisch (11/09/04)
+			if (value == null || value.length() == 0) {
+				// Not using the short notation <node /> because it cause compatibility trouble
+				ps.println("></" + name + ">");
+			} else {
+				ps.println(">" + XML.escapeXMLChars(value) + "</" + name + ">");
+			}
+			
+			return;
+		}
+		
+		ps.print(indentString + "<" + name);
+		outputAttributes(ps);
+		ps.println(">");
+	
+		int nChildNodes = getNNodes();
+		for (int n=0; n<nChildNodes; n++) {
+			Node cnode = getNode(n);
+			cnode.output(ps, indentLevel+1, true);
+		}
+
+		ps.println(indentString +"</" + name + ">");
+	}
+
+	public String toString(String enc, boolean hasChildNode)
+	{
+		ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
+		PrintWriter pr = new PrintWriter(byteOut);
+		output(pr, 0, hasChildNode);
+		pr.flush();
+		try {
+			if (enc != null && 0 < enc.length())
+				return byteOut.toString(enc);
+		}
+		catch (UnsupportedEncodingException e) {
+		}
+		return byteOut.toString();
+	}
+		
+	public String toString()
+	{
+		return toString(XML.CHARSET_UTF8, true);
+	}
+	
+	public String toXMLString(boolean hasChildNode)
+	{
+		String xmlStr = toString();
+		xmlStr = xmlStr.replaceAll("<", "&lt;");	
+		xmlStr = xmlStr.replaceAll(">", "&gt;");	
+		// Thanks for Theo Beisch (11/09/04)
+		xmlStr = xmlStr.replaceAll("&", "&amp;");	
+		xmlStr = xmlStr.replaceAll("\"", "&quot;");	
+		// Thanks for Brian Owens (12/02/04)
+		xmlStr = xmlStr.replaceAll("'", "&apos;");	
+		return xmlStr;
+	}
+
+	public String toXMLString()
+	{
+		return toXMLString(true);
+	}
+	
+	public void print(boolean hasChildNode)
+	{
+		PrintWriter pr = new PrintWriter(System.out);
+		output(pr, 0, hasChildNode);
+		pr.flush();
+	}
+
+	public void print()
+	{
+		print(true);
+	}
+}
diff --git a/router/java/src/org/cybergarage/xml/NodeList.java b/router/java/src/org/cybergarage/xml/NodeList.java
index 53ac9bce88394beb4a88493a8f7be20f07271f0b..806b6f22c83a55bcf6c22173d4f43c436a7a0777 100644
--- a/router/java/src/org/cybergarage/xml/NodeList.java
+++ b/router/java/src/org/cybergarage/xml/NodeList.java
@@ -15,12 +15,10 @@
 
 package org.cybergarage.xml;
 
-import java.util.*;
+import java.util.Vector;
 
 public class NodeList extends Vector 
 {
-	private static final long serialVersionUID = 1528884682346143213L;
-
 	public NodeList() 
 	{
 	}
diff --git a/router/java/src/org/cybergarage/xml/Parser.java b/router/java/src/org/cybergarage/xml/Parser.java
index d93cf48a211b9e1294d69b6b78a3f72971f781fc..6d4d69ac74bba8727f244a0edd9afa8b1776937e 100644
--- a/router/java/src/org/cybergarage/xml/Parser.java
+++ b/router/java/src/org/cybergarage/xml/Parser.java
@@ -1,110 +1,128 @@
-/******************************************************************
-*
-*	CyberXML for Java
-*
-*	Copyright (C) Satoshi Konno 2002
+/******************************************************************
 *
-*	File: Parser.java
-*
-*	Revision;
-*
-*	11/26/03
-*		- first revision.
-*	03/30/05
-*		- Change parse(String) to use StringBufferInputStream instead of URL.
-*
-******************************************************************/
-
-package org.cybergarage.xml;
-
-import java.net.*;
-import java.io.*;
-
-public abstract class Parser 
+*	CyberXML for Java
+*
+*	Copyright (C) Satoshi Konno 2002
+*
+*	File: Parser.java
+*
+*	Revision;
+*
+*	11/26/2003
+*		- first revision.
+*	03/30/2005
+*		- Change parse(String) to use StringBufferInputStream instead of URL.
+*	11/11/2009
+*		- Changed Parser::parser() to use ByteArrayInputStream instead of StringBufferInputStream because of bugs in Android v1.6.
+*
+******************************************************************/
+
+package org.cybergarage.xml;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.ByteArrayInputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+import org.cybergarage.http.HTTP;
+import org.cybergarage.http.HTTPRequest;
+import org.cybergarage.http.HTTPResponse;
+
+public abstract class Parser 
 {
-	////////////////////////////////////////////////
-	//	Constructor
-	////////////////////////////////////////////////
-
-	public Parser()
-	{
-	}
-
-	////////////////////////////////////////////////
-	//	parse
-	////////////////////////////////////////////////
-
-	public abstract Node parse(InputStream inStream) throws ParserException;
-
-	////////////////////////////////////////////////
-	//	parse (URL)
-	////////////////////////////////////////////////
-
-	public Node parse(URL locationURL) throws ParserException
-	{
-		try {
-	 		HttpURLConnection urlCon = (HttpURLConnection)locationURL.openConnection();
-			urlCon.setRequestMethod("GET");
-			InputStream urlIn = urlCon.getInputStream();
-
-			Node rootElem = parse(urlIn);
-			
-			urlIn.close();
-			urlCon.disconnect();
-
-			return rootElem;
-			
-		} catch (Exception e) {
-			throw new ParserException(e);
-		}
-		/*
-		String host = locationURL.getHost();
-		int port = locationURL.getPort();
-		String uri = locationURL.getPath();
-		HTTPRequest httpReq = new HTTPRequest();
-		httpReq.setMethod(HTTP.GET);
-		httpReq.setURI(uri);
-		HTTPResponse httpRes = httpReq.post(host, port);
-		if (httpRes.isSuccessful() == false)
-			throw new ParserException(locationURL.toString());
-		String content = new String(httpRes.getContent());
-		StringBufferInputStream strBuf = new StringBufferInputStream(content);
-		return parse(strBuf);
-		*/
-	}
-
-	////////////////////////////////////////////////
-	//	parse (File)
-	////////////////////////////////////////////////
-
-	public Node parse(File descriptionFile) throws ParserException
-	{
-		try {
-			InputStream fileIn = new FileInputStream(descriptionFile);
-			Node root = parse(fileIn);
-			fileIn.close();
-			return root;
-			
-		} catch (Exception e) {
-			throw new ParserException(e);
-		}
-	}
-
-	////////////////////////////////////////////////
-	//	parse (Memory)
-	////////////////////////////////////////////////
-	
-	public Node parse(String descr) throws ParserException
-	{
-		try {
-			StringBufferInputStream decrIn = new StringBufferInputStream(descr);
-			Node root = parse(decrIn);
-			return root;
-		} catch (Exception e) {
-			throw new ParserException(e);
-		}
-	}
-
-}
-
-
+	////////////////////////////////////////////////
+	//	Constructor
+	////////////////////////////////////////////////
+
+	public Parser()
+	{
+	}
+
+	////////////////////////////////////////////////
+	//	parse
+	////////////////////////////////////////////////
+
+	public abstract Node parse(InputStream inStream) throws ParserException;
+
+	////////////////////////////////////////////////
+	//	parse (URL)
+	////////////////////////////////////////////////
+
+	public Node parse(URL locationURL) throws ParserException
+	{
+		String host = locationURL.getHost();
+		int port = locationURL.getPort();
+		// Thanks for Hao Hu 
+		if (port == -1)
+			port = 80;
+		String uri = locationURL.getPath();
+		
+		try {
+	 		HttpURLConnection urlCon = (HttpURLConnection)locationURL.openConnection();
+			urlCon.setRequestMethod("GET");
+			urlCon.setRequestProperty(HTTP.CONTENT_LENGTH,"0");
+			if (host != null)
+				urlCon.setRequestProperty(HTTP.HOST, host);
+
+			InputStream urlIn = urlCon.getInputStream();
+
+			Node rootElem = parse(urlIn);
+			
+			urlIn.close();
+			urlCon.disconnect();
+
+			return rootElem;
+			
+		} catch (Exception e) {
+			//throw new ParserException(e);
+		}
+
+		HTTPRequest httpReq = new HTTPRequest();
+		httpReq.setMethod(HTTP.GET);
+		httpReq.setURI(uri);
+		HTTPResponse httpRes = httpReq.post(host, port);
+		if (httpRes.isSuccessful() == false)
+			throw new ParserException("HTTP comunication failed: no answer from peer." +
+					"Unable to retrive resoure -> "+locationURL.toString());
+		String content = new String(httpRes.getContent());
+		ByteArrayInputStream strBuf = new ByteArrayInputStream(content.getBytes());
+		return parse(strBuf);
+	}
+
+	////////////////////////////////////////////////
+	//	parse (File)
+	////////////////////////////////////////////////
+
+	public Node parse(File descriptionFile) throws ParserException
+	{
+		try {
+			InputStream fileIn = new FileInputStream(descriptionFile);
+			Node root = parse(fileIn);
+			fileIn.close();
+			return root;
+			
+		} catch (Exception e) {
+			throw new ParserException(e);
+		}
+	}
+
+	////////////////////////////////////////////////
+	//	parse (Memory)
+	////////////////////////////////////////////////
+	
+	public Node parse(String descr) throws ParserException
+	{
+		try {
+			InputStream decrIn = new ByteArrayInputStream(descr.getBytes());
+			Node root = parse(decrIn);
+			return root;
+		} catch (Exception e) {
+			throw new ParserException(e);
+		}
+	}
+
+}
+
+
diff --git a/router/java/src/org/cybergarage/xml/ParserException.java b/router/java/src/org/cybergarage/xml/ParserException.java
index eba4f67937fd8284703c5726726284e8be2fc384..87f1553e61f42091b212b3f835e49f31ba9a77ff 100644
--- a/router/java/src/org/cybergarage/xml/ParserException.java
+++ b/router/java/src/org/cybergarage/xml/ParserException.java
@@ -19,8 +19,6 @@ package org.cybergarage.xml;
 
 public class ParserException extends Exception 
 {
-	private static final long serialVersionUID = 7443033796560597360L;
-
 	public ParserException(Exception e)
 	{
 		super(e);
diff --git a/router/java/src/org/cybergarage/xml/XML.java b/router/java/src/org/cybergarage/xml/XML.java
index 9f62cb9d75cb213b77ef899f846aa8ad0e952d83..ce170358c81bfb4b3468abf8b6c6d0a0468b5daf 100644
--- a/router/java/src/org/cybergarage/xml/XML.java
+++ b/router/java/src/org/cybergarage/xml/XML.java
@@ -21,6 +21,7 @@ package org.cybergarage.xml;
 public class XML 
 {
 	public final static String CONTENT_TYPE = "text/xml; charset=\"utf-8\"";
+	public final static String CHARSET_UTF8 = "utf-8";
 
 	////////////////////////////////////////////////
 	// escapeXMLChars
@@ -28,9 +29,9 @@ public class XML
 	
 	private final static String escapeXMLChars(String input, boolean quote) 
 	{
-		StringBuilder out = new StringBuilder();
 		if (input == null)
 			return null;
+		StringBuffer out = new StringBuffer();
 		int oldsize=input.length();
 		char[] old=new char[oldsize];
 		input.getChars(0,oldsize,old,0);
@@ -61,5 +62,25 @@ public class XML
 	{
 		return escapeXMLChars(input, true);
 	}
+	
+	////////////////////////////////////////////////
+	// unescapeXMLChars
+	////////////////////////////////////////////////
+
+	public final static String unescapeXMLChars(String input)
+	{
+		if (input == null)
+			return null;
+		
+		String outStr;
+
+		outStr = input.replace("&amp;", "&");
+		outStr = outStr.replace("&lt;", "<");
+		outStr = outStr.replace("&gt;", ">");
+		outStr = outStr.replace("&apos;", "\'");
+		outStr = outStr.replace("&quot;", "\"");
+		
+		return outStr;
+	}
 }
 
diff --git a/router/java/src/org/cybergarage/xml/parser/JaxpParser.java b/router/java/src/org/cybergarage/xml/parser/JaxpParser.java
index 8de6b06b89869a47ebda8d4bd68800cdafea753e..ef4c1e0aa1846f33d2455e99da764b59331aea1d 100644
--- a/router/java/src/org/cybergarage/xml/parser/JaxpParser.java
+++ b/router/java/src/org/cybergarage/xml/parser/JaxpParser.java
@@ -12,6 +12,10 @@
 *
 *	06/15/04
 *		- first revision.
+*	01/08/08
+*		- Fixed parse() not to occur null exception when the NamedNodeMap is null on Android.
+*	02/08/08
+*		- Change parse() to use Node::addValue() instead of the setValue().
 *
 ******************************************************************/
 
@@ -50,11 +54,15 @@ public class JaxpParser extends Parser
 			
 		String domNodeName = domNode.getNodeName();
 		String domNodeValue = domNode.getNodeValue();
+		NamedNodeMap attrs = domNode.getAttributes(); 
+		int arrrsLen = (attrs != null) ? attrs.getLength() : 0;
 
 //		Debug.message("[" + rank + "] ELEM : " + domNodeName + ", " + domNodeValue + ", type = " + domNodeType + ", attrs = " + arrrsLen);
 
 		if (domNodeType == org.w3c.dom.Node.TEXT_NODE) {
-			parentNode.setValue(domNodeValue);
+			// Change to use Node::addValue() instead of the setValue(). (2008/02/07)
+			//parentNode.setValue(domNodeValue);
+			parentNode.addValue(domNodeValue);
 			return parentNode;
 		}
 
@@ -69,20 +77,26 @@ public class JaxpParser extends Parser
 			parentNode.addNode(node);
 
 		NamedNodeMap attrMap = domNode.getAttributes(); 
-		int attrLen = attrMap.getLength();
-		//Debug.message("attrLen = " + attrLen);
-		for (int n = 0; n<attrLen; n++) {
-			org.w3c.dom.Node attr = attrMap.item(n);
-			String attrName = attr.getNodeName();
-			String attrValue = attr.getNodeValue();
-			node.setAttribute(attrName, attrValue);
+		if (attrMap != null) {
+			int attrLen = attrMap.getLength();
+			//Debug.message("attrLen = " + attrLen);
+			for (int n = 0; n<attrLen; n++) {
+				org.w3c.dom.Node attr = attrMap.item(n);
+				String attrName = attr.getNodeName();
+				String attrValue = attr.getNodeValue();
+				node.setAttribute(attrName, attrValue);
+			}
 		}
 		
 		org.w3c.dom.Node child = domNode.getFirstChild();
-		while (child != null) {
+		if(child==null){ 
+			node.setValue(""); 
+			return node; 
+		}
+		do{
 			parse(node, child, rank+1);
 			child = child.getNextSibling();
-		}
+		}while (child != null);		
 		
 		return node;
 	}