[params] Allow for arbitrary HTTP request headers to be specified

Extend the request parameter mechanism to allow for arbitrary HTTP
headers to be specified via e.g.:

  params
  param --header Referer http://www.example.com
  imgfetch http://192.168.0.1/script.ipxe##params

Signed-off-by: Michael Brown <mcb30@ipxe.org>
diff --git a/src/core/params.c b/src/core/params.c
index 23206be..189fc22 100644
--- a/src/core/params.c
+++ b/src/core/params.c
@@ -123,10 +123,12 @@
  * @v params		Parameter list
  * @v key		Parameter key
  * @v value		Parameter value
+ * @v flags		Parameter flags
  * @ret param		Parameter, or NULL on failure
  */
 struct parameter * add_parameter ( struct parameters *params,
-				   const char *key, const char *value ) {
+				   const char *key, const char *value,
+				   unsigned int flags ) {
 	struct parameter *param;
 	size_t key_len;
 	size_t value_len;
@@ -147,11 +149,14 @@
 	param->key = key_copy;
 	strcpy ( value_copy, value );
 	param->value = value_copy;
+	param->flags = flags;
 
 	/* Add to list of parameters */
 	list_add_tail ( &param->list, &params->entries );
 
-	DBGC ( params, "PARAMS \"%s\" added \"%s\"=\"%s\"\n",
-	       params->name, param->key, param->value );
+	DBGC ( params, "PARAMS \"%s\" added \"%s\"=\"%s\"%s%s\n",
+	       params->name, param->key, param->value,
+	       ( ( param->flags & PARAMETER_FORM ) ? "(form)" : "" ),
+	       ( ( param->flags & PARAMETER_HEADER ) ? "(header)" : "" ) );
 	return param;
 }
diff --git a/src/hci/commands/param_cmd.c b/src/hci/commands/param_cmd.c
index 9e3260d..dad99f8 100644
--- a/src/hci/commands/param_cmd.c
+++ b/src/hci/commands/param_cmd.c
@@ -90,12 +90,16 @@
 struct param_options {
 	/** Parameter list name */
 	char *params;
+	/** Parameter is a header */
+	int header;
 };
 
 /** "param" option list */
 static struct option_descriptor param_opts[] = {
 	OPTION_DESC ( "params", 'p', required_argument,
 		      struct param_options, params, parse_string ),
+	OPTION_DESC ( "header", 'H', no_argument,
+		      struct param_options, header, parse_flag ),
 };
 
 /** "param" command descriptor */
@@ -114,6 +118,7 @@
 	struct param_options opts;
 	char *key;
 	char *value;
+	unsigned int flags;
 	struct parameters *params;
 	struct parameter *param;
 	int rc;
@@ -132,12 +137,15 @@
 		goto err_parse_value;
 	}
 
+	/* Construct flags */
+	flags = ( opts.header ? PARAMETER_HEADER : PARAMETER_FORM );
+
 	/* Identify parameter list */
 	if ( ( rc = parse_parameters ( opts.params, &params ) ) != 0 )
 		goto err_parse_parameters;
 
 	/* Add parameter */
-	param = add_parameter ( params, key, value );
+	param = add_parameter ( params, key, value, flags );
 	if ( ! param ) {
 		rc = -ENOMEM;
 		goto err_add_parameter;
diff --git a/src/include/ipxe/params.h b/src/include/ipxe/params.h
index 955f57a..61e46e0 100644
--- a/src/include/ipxe/params.h
+++ b/src/include/ipxe/params.h
@@ -32,8 +32,16 @@
 	const char *key;
 	/** Value */
 	const char *value;
+	/** Flags */
+	unsigned int flags;
 };
 
+/** Request parameter is a form parameter */
+#define PARAMETER_FORM 0x0001
+
+/** Request parameter is a header parameter */
+#define PARAMETER_HEADER 0x0002
+
 /**
  * Increment request parameter list reference count
  *
@@ -78,6 +86,7 @@
 extern struct parameters * find_parameters ( const char *name );
 extern struct parameters * create_parameters ( const char *name );
 extern struct parameter * add_parameter ( struct parameters *params,
-					  const char *key, const char *value );
+					  const char *key, const char *value,
+					  unsigned int flags );
 
 #endif /* _IPXE_PARAMS_H */
diff --git a/src/net/tcp/httpcore.c b/src/net/tcp/httpcore.c
index 7900e68..a62c506 100644
--- a/src/net/tcp/httpcore.c
+++ b/src/net/tcp/httpcore.c
@@ -831,6 +831,7 @@
 static int http_format_headers ( struct http_transaction *http, char *buf,
 				 size_t len ) {
 	struct http_request_header *header;
+	struct parameter *param;
 	size_t used;
 	size_t remaining;
 	char *line;
@@ -844,7 +845,7 @@
 		DBGC2 ( http, "HTTP %p TX %s\n", http, buf );
 	used += ssnprintf ( ( buf + used ), ( len - used ), "\r\n" );
 
-	/* Construct all headers */
+	/* Construct all fixed headers */
 	for_each_table_entry ( header, HTTP_REQUEST_HEADERS ) {
 
 		/* Determine header value length */
@@ -869,6 +870,18 @@
 		used += ssnprintf ( ( buf + used ), ( len - used ), "\r\n" );
 	}
 
+	/* Construct all parameter headers */
+	for_each_param ( param, http->uri->params ) {
+
+		/* Skip non-header parameters */
+		if ( ! ( param->flags & PARAMETER_HEADER ) )
+			continue;
+
+		/* Add parameter */
+		used += ssnprintf ( ( buf + used ), ( len - used ),
+				    "%s: %s\r\n", param->key, param->value );
+	}
+
 	/* Construct terminating newline */
 	used += ssnprintf ( ( buf + used ), ( len - used ), "\r\n" );
 
@@ -1868,6 +1881,10 @@
 	len = 0;
 	for_each_param ( param, params ) {
 
+		/* Skip non-form parameters */
+		if ( ! ( param->flags & PARAMETER_FORM ) )
+			continue;
+
 		/* Add the "&", if applicable */
 		if ( len ) {
 			if ( remaining > 0 )
diff --git a/src/tests/uri_test.c b/src/tests/uri_test.c
index 5e70603..9d2f6db 100644
--- a/src/tests/uri_test.c
+++ b/src/tests/uri_test.c
@@ -98,6 +98,8 @@
 	const char *key;
 	/** Value */
 	const char *value;
+	/** Flags */
+	unsigned int flags;
 };
 
 /** A request parameter URI test */
@@ -428,6 +430,7 @@
 			      file, line );
 			okx ( strcmp ( param->value, list->value ) == 0,
 			      file, line );
+			okx ( param->flags == list->flags, file, line );
 			list++;
 		}
 		okx ( list->key == NULL, file, line );
@@ -456,7 +459,8 @@
 	okx ( params != NULL, file, line );
 	if ( params ) {
 		for ( list = test->list ; list->key ; list++ ) {
-			param = add_parameter ( params, list->key, list->value);
+			param = add_parameter ( params, list->key, list->value,
+						list->flags );
 			okx ( param != NULL, file, line );
 		}
 	}
@@ -884,18 +888,22 @@
 	{
 		"vendor",
 		"10ec",
+		PARAMETER_FORM,
 	},
 	{
 		"device",
 		"8139",
+		PARAMETER_FORM,
 	},
 	{
 		"uuid",
 		"f59fac00-758f-498f-9fe5-87d790045d94",
+		PARAMETER_HEADER,
 	},
 	{
 		NULL,
 		NULL,
+		0,
 	}
 };
 
@@ -917,14 +925,17 @@
 	{
 		"mac",
 		"00:1e:65:80:d3:b6",
+		PARAMETER_FORM,
 	},
 	{
 		"serial",
 		"LXTQ20Z1139322762F2000",
+		PARAMETER_FORM,
 	},
 	{
 		NULL,
 		NULL,
+		0,
 	}
 };