mod_netconf: support for generic NETCONF operation
diff --git a/src/mod_netconf.c b/src/mod_netconf.c
index 11acd67..0ff5543 100644
--- a/src/mod_netconf.c
+++ b/src/mod_netconf.c
@@ -93,7 +93,8 @@
 	MSG_LOCK,
 	MSG_UNLOCK,
 	MSG_KILL,
-	MSG_INFO
+	MSG_INFO,
+	MSG_GENERIC
 } MSG_TYPE;
 
 #define MSG_OK 0
@@ -236,7 +237,7 @@
 
 }
 
-static int netconf_close(server_rec* server, apr_hash_t* conns, char* session_key)
+static int netconf_close(server_rec* server, apr_hash_t* conns, const char* session_key)
 {
 	struct nc_session *ns = NULL;
 
@@ -258,7 +259,7 @@
 	}
 }
 
-static int netconf_op(server_rec* server, apr_hash_t* conns, char* session_key, nc_rpc* rpc)
+static int netconf_op(server_rec* server, apr_hash_t* conns, const char* session_key, nc_rpc* rpc)
 {
 	struct nc_session *session = NULL;
 	nc_reply* reply;
@@ -302,7 +303,8 @@
 		return (EXIT_FAILURE);
 	}
 }
-static char* netconf_opdata(server_rec* server, apr_hash_t* conns, char* session_key, nc_rpc* rpc)
+
+static char* netconf_opdata(server_rec* server, apr_hash_t* conns, const char* session_key, nc_rpc* rpc)
 {
 	struct nc_session *session = NULL;
 	nc_reply* reply;
@@ -352,7 +354,7 @@
 	}
 }
 
-static char* netconf_getconfig(server_rec* server, apr_hash_t* conns, char* session_key, NC_DATASTORE source, const char* filter)
+static char* netconf_getconfig(server_rec* server, apr_hash_t* conns, const char* session_key, NC_DATASTORE source, const char* filter)
 {
 	nc_rpc* rpc;
 	struct nc_filter *f = NULL;
@@ -376,7 +378,7 @@
 	return (data);
 }
 
-static char* netconf_get(server_rec* server, apr_hash_t* conns, char* session_key, const char* filter)
+static char* netconf_get(server_rec* server, apr_hash_t* conns, const char* session_key, const char* filter)
 {
 	nc_rpc* rpc;
 	struct nc_filter *f = NULL;
@@ -400,7 +402,7 @@
 	return (data);
 }
 
-static int netconf_copyconfig(server_rec* server, apr_hash_t* conns, char* session_key, NC_DATASTORE source, NC_DATASTORE target, const char* config)
+static int netconf_copyconfig(server_rec* server, apr_hash_t* conns, const char* session_key, NC_DATASTORE source, NC_DATASTORE target, const char* config)
 {
 	nc_rpc* rpc;
 	int retval = EXIT_SUCCESS;
@@ -417,7 +419,7 @@
 	return (retval);
 }
 
-static int netconf_editconfig(server_rec* server, apr_hash_t* conns, char* session_key, NC_DATASTORE target, NC_EDIT_DEFOP_TYPE defop, NC_EDIT_ERROPT_TYPE erropt, const char* config)
+static int netconf_editconfig(server_rec* server, apr_hash_t* conns, const char* session_key, NC_DATASTORE target, NC_EDIT_DEFOP_TYPE defop, NC_EDIT_ERROPT_TYPE erropt, const char* config)
 {
 	nc_rpc* rpc;
 	int retval = EXIT_SUCCESS;
@@ -434,7 +436,7 @@
 	return (retval);
 }
 
-static int netconf_killsession(server_rec* server, apr_hash_t* conns, char* session_key, const char* sid)
+static int netconf_killsession(server_rec* server, apr_hash_t* conns, const char* session_key, const char* sid)
 {
 	nc_rpc* rpc;
 	int retval = EXIT_SUCCESS;
@@ -451,7 +453,7 @@
 	return (retval);
 }
 
-static int netconf_onlytargetop(server_rec* server, apr_hash_t* conns, char* session_key, NC_DATASTORE target, nc_rpc* (*op_func)(NC_DATASTORE))
+static int netconf_onlytargetop(server_rec* server, apr_hash_t* conns, const char* session_key, NC_DATASTORE target, nc_rpc* (*op_func)(NC_DATASTORE))
 {
 	nc_rpc* rpc;
 	int retval = EXIT_SUCCESS;
@@ -468,21 +470,85 @@
 	return (retval);
 }
 
-static int netconf_deleteconfig(server_rec* server, apr_hash_t* conns, char* session_key, NC_DATASTORE target)
+static int netconf_deleteconfig(server_rec* server, apr_hash_t* conns, const char* session_key, NC_DATASTORE target)
 {
 	return (netconf_onlytargetop(server, conns, session_key, target, nc_rpc_deleteconfig));
 }
 
-static int netconf_lock(server_rec* server, apr_hash_t* conns, char* session_key, NC_DATASTORE target)
+static int netconf_lock(server_rec* server, apr_hash_t* conns, const char* session_key, NC_DATASTORE target)
 {
 	return (netconf_onlytargetop(server, conns, session_key, target, nc_rpc_lock));
 }
 
-static int netconf_unlock(server_rec* server, apr_hash_t* conns, char* session_key, NC_DATASTORE target)
+static int netconf_unlock(server_rec* server, apr_hash_t* conns, const char* session_key, NC_DATASTORE target)
 {
 	return (netconf_onlytargetop(server, conns, session_key, target, nc_rpc_unlock));
 }
 
+/**
+ * @return REPLY_OK: 0, *data == NULL
+ *         REPLY_DATA: 0, *data != NULL
+ *         REPLY_ERROR: 1, *data == NULL
+ */
+static int netconf_generic(server_rec* server, apr_hash_t* conns, const char* session_key, const char* content, char** data)
+{
+	struct nc_session *session = NULL;
+	nc_reply* reply;
+	nc_rpc* rpc;
+	int retval = EXIT_SUCCESS;
+
+	/* create requests */
+	rpc = nc_rpc_generic(content);
+	if (rpc == NULL) {
+		ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: creating rpc request failed");
+		return (EXIT_FAILURE);
+	}
+
+	*data = NULL;
+
+	/* get session where send the RPC */
+	session = (struct nc_session *)apr_hash_get(conns, session_key, APR_HASH_KEY_STRING);
+	if (session != NULL) {
+		/* send the request and get the reply */
+		nc_session_send_rpc (session, rpc);
+		if (nc_session_recv_reply (session, &reply) == 0) {
+			nc_rpc_free (rpc);
+			if (nc_session_get_status(session) != NC_SESSION_STATUS_WORKING) {
+				ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: receiving rpc-reply failed");
+				netconf_close(server, conns, session_key);
+				return (EXIT_FAILURE);
+			}
+
+			/* there is error handled by callback */
+			return (EXIT_FAILURE);
+		}
+		nc_rpc_free (rpc);
+
+		switch (nc_reply_get_type (reply)) {
+		case NC_REPLY_DATA:
+			if ((*data = nc_reply_get_data (reply)) == NULL) {
+				ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: no data from reply");
+				return (EXIT_FAILURE);
+			}
+			retval = EXIT_SUCCESS;
+			break;
+		case NC_REPLY_OK:
+			retval = EXIT_SUCCESS;
+			break;
+		default:
+			ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "mod_netconf: unexpected rpc-reply");
+			retval = EXIT_FAILURE;
+			break;
+		}
+		nc_reply_free(reply);
+
+		return (retval);
+	} else {
+		ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Unknown session to process.");
+		return (EXIT_FAILURE);
+	}
+}
+
 server_rec* clb_print_server;
 int clb_print(const char* msg)
 {
@@ -967,6 +1033,35 @@
 					}
 
 					break;
+				case MSG_GENERIC:
+					ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, "Request: generic request for session %s", session_key);
+
+					config = json_object_get_string(json_object_object_get(request, "content"));
+
+					if (config == 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 content parameter."));
+						break;
+					}
+
+					if (netconf_generic(server, netconf_sessions_list, session_key, config, &data) != EXIT_SUCCESS) {
+						if (err_reply == NULL) {
+							json_object_object_add(reply, "type", json_object_new_int(REPLY_ERROR));
+							json_object_object_add(reply, "error-message", json_object_new_string("kill-session failed."));
+						} else {
+							/* use filled err_reply from libnetconf's callback */
+							json_object_put(reply);
+							reply = err_reply;
+						}
+					} else {
+						if (data == NULL) {
+							json_object_object_add(reply, "type", json_object_new_int(REPLY_OK));
+						} else {
+							json_object_object_add(reply, "type", json_object_new_int(REPLY_DATA));
+							json_object_object_add(reply, "data", json_object_new_string(data));
+						}
+					}
+					break;
 				default:
 					ap_log_error(APLOG_MARK, APLOG_ERR, 0, server, "Unknown mod_netconf operation requested (%d)", operation);
 					reply =  json_object_new_object();