mod_netconf: provide information about NETCONF session

Add mod_netconf message type to provide information about the session.
diff --git a/src/README b/src/README
index 0033fb4..be81acb 100644
--- a/src/README
+++ b/src/README
@@ -51,10 +51,19 @@
 key: bad-namespace (string)
 key: session-id (string)
 
+4) INFO
+key: type (int), value: 3
+key: sid (string), value: session ID
+key: version (string), value: NETCONF protocol version
+key: host (string), value: hostname of the NETCONF server
+key: port (string), value: port of the NETCONF server
+key: user (string), value: username of the user holding the NETCONF session
+key: capabilities (array of strings), value: list of supported capabilities
+
 Requests
 ~~~~~~~~
 1) Request to create NETCONF session
-key: type (int), value: 3
+key: type (int), value: 4
 key: user (string)
 key: pass (string), value: plain text password
 
@@ -63,18 +72,18 @@
 key: port (string), "830" if not specified
 
 2) Request to close NETCONF session
-key: type (int), value: 4
+key: type (int), value: 5
 key: session (string), value: unique session identifier
 
 3) NETCONF <get>
-key: type (int), value: 5
+key: type (int), value: 6
 key: session (string), value: unique session identifier
 
 Optional:
 key: filter (string), value: xml subtree filter
 
 4) NETCONF <get-config>
-key: type (int), value: 6
+key: type (int), value: 7
 key: session (string), value: unique session identifier
 key: source (string), value: running|startup|candidate
 
@@ -82,7 +91,7 @@
 key: filter (string), value: xml subtree filter
 
 5) NETCONF <edit-config>
-key: type (int), value: 7
+key: type (int), value: 8
 key: session (string), value: unique session identifier
 key: target (string), value: running|startup|candidate
 key: config (string), value: editing configuration data according to NETCONF RFC
@@ -92,7 +101,7 @@
 key: error-option (string), value: stop-on-error|continue-on-error|rollback-on-error 
 
 6) NETCONF <copy-config>
-key: type (int), value: 8
+key: type (int), value: 9
 key: session (string), value: unique session identifier
 key: target (string), value: running|startup|candidate
 
@@ -101,23 +110,26 @@
 key: config (string), value: new complete configuration data, if source not specified
 
 7) NETCONF <delete-config>
-key: type (int), value: 9
-key: session (string), value: unique session identifier
-key: target (string), value: running|startup|candidate
-
-8) NETCONF <lock>
 key: type (int), value: 10
 key: session (string), value: unique session identifier
 key: target (string), value: running|startup|candidate
 
-9) NETCONF <unlock>
+8) NETCONF <lock>
 key: type (int), value: 11
 key: session (string), value: unique session identifier
 key: target (string), value: running|startup|candidate
 
-10) NETCONF <kill-session>
+9) NETCONF <unlock>
 key: type (int), value: 12
 key: session (string), value: unique session identifier
+key: target (string), value: running|startup|candidate
+
+10) NETCONF <kill-session>
+key: type (int), value: 13
+key: session (string), value: unique session identifier
 key: session-id (string), value: ID of the session to kill
 
+11) Provide information about NETCONF session
+key: type (int), value: 14
+key: session (string), value: unique session identifier
 
diff --git a/src/mod_netconf.c b/src/mod_netconf.c
index e6f69ac..43080a8 100644
--- a/src/mod_netconf.c
+++ b/src/mod_netconf.c
@@ -97,6 +97,7 @@
 	REPLY_OK,
 	REPLY_DATA,
 	REPLY_ERROR,
+	REPLY_INFO,
 	MSG_CONNECT,
 	MSG_DISCONNECT,
 	MSG_GET,
@@ -106,7 +107,8 @@
 	MSG_DELETECONFIG,
 	MSG_LOCK,
 	MSG_UNLOCK,
-	MSG_KILL
+	MSG_KILL,
+	MSG_INFO
 } MSG_TYPE;
 
 #define MSG_OK 0
@@ -522,12 +524,14 @@
 	struct pollfd fds;
 	int status;
 	mod_netconf_cfg *cfg;
-	json_object *request, *reply;
+	json_object *request, *reply, *json_obj;
 	int operation;
 	char* session_key, *data;
 	const char *msgtext;
 	const char *host, *port, *user, *pass;
 	const char *target, *source, *filter, *config, *defop, *erropt, *sid;
+	struct nc_session *session = NULL;
+	struct nc_cpblts* cpblts;
 	NC_DATASTORE ds_type_s, ds_type_t;
 	NC_EDIT_DEFOP_TYPE defop_type = 0;
 	NC_EDIT_ERROPT_TYPE erropt_type = 0;
@@ -686,6 +690,9 @@
 				/* null global JSON error-reply */
 				err_reply = NULL;
 
+				/* prepare reply envelope */
+				reply =  json_object_new_object();
+
 				/* process required operation */
 				switch (operation) {
 				case MSG_CONNECT:
@@ -724,8 +731,6 @@
 
 					filter = json_object_get_string(json_object_object_get(request, "filter"));
 
-					reply =  json_object_new_object();
-
 					if ((data = netconf_get(server, netconf_sessions_list, session_key, filter)) == NULL) {
 						if (err_reply == NULL) {
 							json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
@@ -745,8 +750,6 @@
 
 					filter = json_object_get_string(json_object_object_get(request, "filter"));
 
-					reply = json_object_new_object();
-
 					if (ds_type_s == -1) {
 						json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
 						json_object_object_add(reply, "error-message", json_object_new_string("Invalid source repository type requested."));
@@ -770,8 +773,6 @@
 				case MSG_EDITCONFIG:
 					ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: edit-config (session %s)", session_key);
 
-					reply = json_object_new_object();
-
 					defop = json_object_get_string(json_object_object_get(request, "default-operation"));
 					if (defop != NULL) {
 						if (strcmp(defop, "merge") == 0) {
@@ -836,8 +837,6 @@
 					ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: copy-config (session %s)", session_key);
 					config = NULL;
 
-					reply = json_object_new_object();
-
 					if (source == NULL) {
 						/* no explicit source specified -> use config data */
 						ds_type_s = NC_DATASTORE_NONE;
@@ -887,8 +886,6 @@
 						ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: unlock (session %s)", session_key);
 					}
 
-					reply = json_object_new_object();
-
 					if (ds_type_t == -1) {
 						json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
 						json_object_object_add(reply, "error-message", json_object_new_string("Invalid target repository type requested."));
@@ -928,8 +925,6 @@
 
 					sid = json_object_get_string(json_object_object_get(request, "session-id"));
 
-					reply = json_object_new_object();
-
 					if (sid == NULL) {
 						json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
 						json_object_object_add(reply, "error-message", json_object_new_string("Missing session-id parameter."));
@@ -952,7 +947,6 @@
 				case MSG_DISCONNECT:
 					ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: Disconnect session %s", session_key);
 
-					reply =  json_object_new_object();
 					if (netconf_close(server, netconf_sessions_list, session_key) != EXIT_SUCCESS) {
 						if (err_reply == NULL) {
 							json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
@@ -966,6 +960,36 @@
 						json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
 					}
 					break;
+				case MSG_INFO:
+					ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: get info about session %s", session_key);
+
+					session = (struct nc_session *)apr_hash_get(netconf_sessions_list, session_key, APR_HASH_KEY_STRING);
+					if (session != NULL) {
+						json_object_object_add(reply, "sid", json_object_new_string(data = nc_session_get_id(session)));
+						if (data) free(data);
+						json_object_object_add(reply, "version", json_object_new_string((nc_session_get_version(session) == 0)?"1.0":"1.1"));
+						json_object_object_add(reply, "host", json_object_new_string(data = nc_session_get_host(session)));
+						if (data) free(data);
+						json_object_object_add(reply, "port", json_object_new_string(data = nc_session_get_port(session)));
+						if (data) free(data);
+						json_object_object_add(reply, "user", json_object_new_string(data = nc_session_get_user(session)));
+						if (data) free(data);
+						cpblts = nc_session_get_cpblts (session);
+						if (cpblts != NULL) {
+							json_obj = json_object_new_array();
+							nc_cpblts_iter_start (cpblts);
+							while ((data = nc_cpblts_iter_next (cpblts)) != NULL) {
+								json_object_array_add(json_obj, json_object_new_string(data));
+								free (data);
+							}
+							json_object_object_add(reply, "capabilities", json_obj);
+						}
+					} else {
+						json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
+						json_object_object_add(reply, "error-message", json_object_new_string("Invalid session identifier."));
+					}
+
+					break;
 				default:
 					ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Unknown mod_netconf operation requested (%d)", operation);
 					reply =  json_object_new_object();
diff --git a/src/test-client.c b/src/test-client.c
index 9723b0c..7dc121f 100644
--- a/src/test-client.c
+++ b/src/test-client.c
@@ -58,6 +58,7 @@
 	REPLY_OK,
 	REPLY_DATA,
 	REPLY_ERROR,
+	REPLY_INFO,
 	MSG_CONNECT,
 	MSG_DISCONNECT,
 	MSG_GET,
@@ -67,7 +68,8 @@
 	MSG_DELETECONFIG,
 	MSG_LOCK,
 	MSG_UNLOCK,
-	MSG_KILL
+	MSG_KILL,
+	MSG_INFO
 } MSG_TYPE;
 
 void print_help(char* progname)
@@ -84,6 +86,7 @@
 	printf("\tkill-session\n");
 	printf("\tlock\n");
 	printf("\tunlock\n");
+	printf("\tinfo\n");
 }
 
 int main (int argc, char* argv[])
@@ -95,6 +98,7 @@
 	size_t len;
 	char buffer[BUFFER_SIZE];
 	char* line = NULL;
+	int i, alen;
 
 	if (argc != 2) {
 		print_help(argv[0]);
@@ -300,6 +304,16 @@
 		getline (&line, &len, stdin);
 		line[(strlen(line)-1)] = 0;
 		json_object_object_add(msg, "target", json_object_new_string(line));
+	} else if (strcmp(argv[1], "info") == 0) {
+		/*
+		 * Get information about NETCONF session
+		 */
+		msg = json_object_new_object();
+		json_object_object_add(msg, "type", json_object_new_int(MSG_INFO));
+		printf("Session: ");
+		getline (&line, &len, stdin);
+		line[(strlen(line)-1)] = 0;
+		json_object_object_add(msg, "session", json_object_new_string(line));
 	} else {
 		/*
 		 * Unknown request
@@ -342,6 +356,13 @@
 			case json_type_int:
 				printf("%d\n", json_object_get_int(value));
 				break;
+			case json_type_array:
+				printf("\n");
+				alen = json_object_array_length(value);
+				for (i = 0; i < alen; i++) {
+					printf("\t(%d) %s\n", i, json_object_get_string(json_object_array_get_idx(value, i)));
+				}
+				break;
 			default:
 				printf("\n");
 				break;