From a89c5fa96e32d56b00673ab2b0cddd39eca2c89b Mon Sep 17 00:00:00 2001
From: gL2n30Y06arv2 <gL2n30Y06arv2@hotmail.com>
Date: Sat, 26 Dec 2009 01:28:37 +0200
Subject: [PATCH] Decodes struct uri.{user,password} members.

Replaces the encoded user info (user:password), that was kept in struct
uri, with its decoded counterpart.  The decoded version is expected to
be much more useful.  It also attempts to amend the other parts of the
project appropriately.
The reference is section 3.2 of RFC 3986.

Signed-off-by: gL2n30Y06arv2 <gL2n30Y06arv2@hotmail.com>
---
 src/core/uri.c         |   45 ++++++++++++++++++++++++++++-----------------
 src/include/gpxe/uri.h |    4 ++--
 src/net/tcp/ftp.c      |   28 ++++++++++++++++++++++++++--
 src/net/tcp/http.c     |   17 ++---------------
 src/tests/uri_test.c   |    7 +++++++
 5 files changed, 65 insertions(+), 36 deletions(-)

diff --git a/src/core/uri.c b/src/core/uri.c
index 50a96d3..63166e5 100644
--- a/src/core/uri.c
+++ b/src/core/uri.c
@@ -45,9 +45,9 @@ static void dump_uri ( struct uri *uri ) {
 	if ( uri->opaque )
 		DBG ( " opaque \"%s\"", uri->opaque );
 	if ( uri->user )
-		DBG ( " user \"%s\"", uri->user );
+		DBG ( " decoded user \"%s\"", uri->user );
 	if ( uri->password )
-		DBG ( " password \"%s\"", uri->password );
+		DBG ( " decoded password \"%s\"", uri->password );
 	if ( uri->host )
 		DBG ( " host \"%s\"", uri->host );
 	if ( uri->port )
@@ -149,19 +149,25 @@ struct uri * parse_uri ( const char *uri_string ) {
 		uri->path = path;
 	}
 
-	/* Split authority into user[:password] and host[:port] portions */
+	/* Split authority into user[:password] and host[:port]
+	   portions.  Decodes authority. */
 	if ( ( tmp = strchr ( authority, '@' ) ) ) {
 		/* Has user[:password] */
 		*(tmp++) = '\0';
 		uri->host = tmp;
+		uri_decode ( authority, authority /* in place */, 
+				strlen ( authority ) + 1 /* NULL */ );
+		/* assert ( uri->host >= ( authority + 
+					strlen ( authority ) + 1 ) ); */
 		uri->user = authority;
-		if ( ( tmp = strchr ( authority, ':' ) ) ) {
+		if ( ( tmp = strchr ( uri->user, ':' ) ) ) {
 			/* Has password */
 			*(tmp++) = '\0';
 			uri->password = tmp;
 		}
+
 	} else {
-		/* No user:password */
+		/* No user[:password] */
 		uri->host = authority;
 	}
 
@@ -229,13 +235,14 @@ int unparse_uri ( char *buf, size_t size, struct uri *uri ) {
 	/* [user[:password]@]host[:port] */
 	if ( uri->host ) {
 		if ( uri->user ) {
+			used += uri_encode ( uri->user, ( buf + used ),
+						 ( size - used ) );
 			used += ssnprintf ( ( buf + used ), ( size - used ),
-					    "%s", uri->user );
-			if ( uri->password ) {
-				used += ssnprintf ( ( buf + used ),
-						    ( size - used ),
-						    ":%s", uri->password );
-			}
+					    ":" );
+			if ( uri->password ) 
+				used += uri_encode ( uri->password,
+						    ( buf + used ),
+						    ( size - used ) );
 			used += ssnprintf ( ( buf + used ), ( size - used ),
 					    "@" );
 		}
@@ -435,12 +442,14 @@ size_t uri_encode ( const char *raw_string, char *buf, size_t len ) {
 }
 
 /**
- * Decode URI-encoded string
+ * Decode URI-encoded string.
  *
  * @v encoded_string	URI-encoded string
  * @v buf		Buffer to contain decoded string
  * @v len		Length of buffer
  * @ret len		Length of decoded string (excluding NUL)
+ *
+ * Can be used when encoded_string and buf overlap and in place.
  */
 size_t uri_decode ( const char *encoded_string, char *buf, size_t len ) {
 	ssize_t remaining = len;
@@ -448,10 +457,8 @@ size_t uri_decode ( const char *encoded_string, char *buf, size_t len ) {
 	char *hexbuf_end;
 	unsigned char c;
 
-	if ( len )
-		buf[0] = '\0';
-
-	while ( *encoded_string ) {
+	while ( *encoded_string  &&  
+			remaining > 1 /* a NULL is essential */ ) {
 		if ( *encoded_string == '%' ) {
 			encoded_string++;
 			snprintf ( hexbuf, sizeof ( hexbuf ), "%s",
@@ -461,7 +468,11 @@ size_t uri_decode ( const char *encoded_string, char *buf, size_t len ) {
 		} else {
 			c = *(encoded_string++);
 		}
-		ssnprintf ( buf++, remaining--, "%c", c );
+		*(remaining--, buf++) = c;
 	}
+
+	if ( remaining )
+		*buf = '\0';
+
 	return ( len - remaining );
 }
diff --git a/src/include/gpxe/uri.h b/src/include/gpxe/uri.h
index 03c88d2..de8d5a9 100644
--- a/src/include/gpxe/uri.h
+++ b/src/include/gpxe/uri.h
@@ -47,9 +47,9 @@ struct uri {
 	const char *scheme;
 	/** Opaque part */
 	const char *opaque;
-	/** User name */
+	/** Decoded user name */
 	const char *user;
-	/** Password */
+	/** Decoded password */
 	const char *password;
 	/** Host name */
 	const char *host;
diff --git a/src/net/tcp/ftp.c b/src/net/tcp/ftp.c
index 0719bf7..26103f6 100644
--- a/src/net/tcp/ftp.c
+++ b/src/net/tcp/ftp.c
@@ -131,11 +131,35 @@ static const char * ftp_uri_path ( struct ftp_request *ftp ) {
 	return ftp->uri->path;
 }
 
+/**
+ * Retrieve FTP user
+ *
+ * @v ftp		FTP request
+ * @ret user		FTP user
+ */
+static const char * ftp_user ( struct ftp_request *ftp ) {
+	static char *ftp_default_user = "anonymous";
+	return ftp->uri->user ? ftp->uri->user : ftp_default_user;
+}
+
+/**
+ * Retrieve FTP password
+ *
+ * @v ftp		FTP request
+ * @ret password	FTP password
+ */
+static const char * ftp_password ( struct ftp_request *ftp ) {
+	static char *ftp_default_password = 
+					"etherboot@etherboot.org";
+	return ftp->uri->password ? 
+		ftp->uri->password : ftp_default_password;
+}
+
 /** FTP control channel strings */
 static struct ftp_control_string ftp_strings[] = {
 	[FTP_CONNECT]	= { NULL, NULL },
-	[FTP_USER]	= { "USER anonymous", NULL },
-	[FTP_PASS]	= { "PASS etherboot@etherboot.org", NULL },
+	[FTP_USER]	= { "USER ", ftp_user },
+	[FTP_PASS]	= { "PASS ", ftp_password },
 	[FTP_TYPE]	= { "TYPE I", NULL },
 	[FTP_PASV]	= { "PASV", NULL },
 	[FTP_RETR]	= { "RETR ", ftp_uri_path },
diff --git a/src/net/tcp/http.c b/src/net/tcp/http.c
index a02408a..eda9f8b 100644
--- a/src/net/tcp/http.c
+++ b/src/net/tcp/http.c
@@ -437,21 +437,8 @@ static void http_step ( struct process *process ) {
 
 		/* Construct authorisation, if applicable */
 		if ( user ) {
-			char *buf = user_pw;
-			ssize_t remaining = sizeof ( user_pw );
-			size_t len;
-
-			/* URI-decode the username and password */
-			len = uri_decode ( user, buf, remaining );
-			buf += len;
-			remaining -= len;
-			*(remaining--, buf++) = ':';
-			len = uri_decode ( password, buf, remaining );
-			buf += len;
-			remaining -= len;
-			assert ( remaining >= 0 );
-
-			/* Base64-encode the "user:password" string */
+			snprintf ( user_pw, user_pw_len + 1 /* NUL */,
+				   "%s:%s", user, password );
 			base64_encode ( user_pw, user_pw_base64 );
 		}
 
diff --git a/src/tests/uri_test.c b/src/tests/uri_test.c
index 2548760..aa84ace 100644
--- a/src/tests/uri_test.c
+++ b/src/tests/uri_test.c
@@ -23,6 +23,13 @@ static struct uri_test uri_tests[] = {
 	{ "tftp://192.168.0.1/", "/tftpboot/vmlinuz",
 	  "tftp://192.168.0.1/tftpboot/vmlinuz" },
 #if 0
+	{ "ftp://:user%40isp.fr@www.fensystems.co.uk", "",
+	  "ftp://:user%40isp.fr@www.fensystems.co.uk/" },
+	{ "https://user%40isp.fr:@etherboot.org/wiki/page1", "page2",
+	  "https://user%40isp.fr:@etherboot.org/wiki/page2" },
+	{ "http://user:gpxe%40etherboot.org@etherboot.org/wiki/page1",
+	  "../page3",
+	  "http://user:gpxe%40etherboot.org@etherboot.org/page3" },
 	"http://www.etherboot.org/wiki",
 	"mailto:bob@nowhere.com",
 	"ftp://joe:secret@insecure.org:8081/hidden/path/to?what=is#this",
-- 
1.6.5

