Merge branch 'master' into devel

Conflicts:
	.travis.yml
	CMakeLists.txt
	src/session.c
diff --git a/.travis.yml b/.travis.yml
index 01055c2..e5da833 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -34,4 +34,3 @@
 
 after_success:
   - if [ "$TRAVIS_OS_NAME" = "linux" -a "$CC" = "gcc" ]; then codecov; fi
-
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8c5660a..915d307 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -37,8 +37,8 @@
 
 # set version
 set(LIBNETCONF2_MAJOR_VERSION 0)
-set(LIBNETCONF2_MINOR_VERSION 6)
-set(LIBNETCONF2_MICRO_VERSION 16)
+set(LIBNETCONF2_MINOR_VERSION 7)
+set(LIBNETCONF2_MICRO_VERSION 6)
 set(LIBNETCONF2_VERSION ${LIBNETCONF2_MAJOR_VERSION}.${LIBNETCONF2_MINOR_VERSION}.${LIBNETCONF2_MICRO_VERSION})
 set(LIBNETCONF2_SOVERSION ${LIBNETCONF2_MAJOR_VERSION}.${LIBNETCONF2_MINOR_VERSION})
 
diff --git a/src/io.c b/src/io.c
index c8b721a..e6cc293 100644
--- a/src/io.c
+++ b/src/io.c
@@ -577,6 +577,8 @@
         return -1;
     }
 
+    DBG("Session %u: sending message:\n%.*s", session->id, count, buf);
+
     switch (session->ti_type) {
     case NC_TI_NONE:
         return -1;
diff --git a/src/messages_client.c b/src/messages_client.c
index 2b97401..cc0ecea 100644
--- a/src/messages_client.c
+++ b/src/messages_client.c
@@ -37,11 +37,11 @@
 }
 
 API struct nc_rpc *
-nc_rpc_generic(const struct lyd_node *data, NC_PARAMTYPE paramtype)
+nc_rpc_act_generic(const struct lyd_node *data, NC_PARAMTYPE paramtype)
 {
-    struct nc_rpc_generic *rpc;
+    struct nc_rpc_act_generic *rpc;
 
-    if (!data || (data->schema->nodetype != LYS_RPC) || data->next || (data->prev != data)) {
+    if (!data || data->next || (data->prev != data)) {
         ERRARG("data");
         return NULL;
     }
@@ -52,7 +52,7 @@
         return NULL;
     }
 
-    rpc->type = NC_RPC_GENERIC;
+    rpc->type = NC_RPC_ACT_GENERIC;
     rpc->has_data = 1;
     if (paramtype == NC_PARAMTYPE_DUP_AND_FREE) {
         rpc->content.data = lyd_dup(data, 1);
@@ -65,9 +65,9 @@
 }
 
 API struct nc_rpc *
-nc_rpc_generic_xml(const char *xml_str, NC_PARAMTYPE paramtype)
+nc_rpc_act_generic_xml(const char *xml_str, NC_PARAMTYPE paramtype)
 {
-    struct nc_rpc_generic *rpc;
+    struct nc_rpc_act_generic *rpc;
 
     if (!xml_str) {
         ERRARG("xml_str");
@@ -80,7 +80,7 @@
         return NULL;
     }
 
-    rpc->type = NC_RPC_GENERIC;
+    rpc->type = NC_RPC_ACT_GENERIC;
     rpc->has_data = 0;
     if (paramtype == NC_PARAMTYPE_DUP_AND_FREE) {
         rpc->content.xml_str = strdup(xml_str);
@@ -516,7 +516,7 @@
 API void
 nc_rpc_free(struct nc_rpc *rpc)
 {
-    struct nc_rpc_generic *rpc_generic;
+    struct nc_rpc_act_generic *rpc_generic;
     struct nc_rpc_getconfig *rpc_getconfig;
     struct nc_rpc_edit *rpc_edit;
     struct nc_rpc_copy *rpc_copy;
@@ -533,8 +533,8 @@
     }
 
     switch (rpc->type) {
-    case NC_RPC_GENERIC:
-        rpc_generic = (struct nc_rpc_generic *)rpc;
+    case NC_RPC_ACT_GENERIC:
+        rpc_generic = (struct nc_rpc_act_generic *)rpc;
         if (rpc_generic->free) {
             if (rpc_generic->has_data) {
                 lyd_free(rpc_generic->content.data);
diff --git a/src/messages_client.h b/src/messages_client.h
index 6d5eca0..c3af824 100644
--- a/src/messages_client.h
+++ b/src/messages_client.h
@@ -25,7 +25,7 @@
  */
 typedef enum {
     NC_RPC_UNKNOWN = 0, /**< invalid RPC. */
-    NC_RPC_GENERIC,     /**< user-defined generic RPC. */
+    NC_RPC_ACT_GENERIC, /**< user-defined generic RPC/action. */
 
     /* ietf-netconf */
     NC_RPC_GETCONFIG,   /**< \<get-config\> RPC. */
@@ -184,7 +184,7 @@
 NC_RPC_TYPE nc_rpc_get_type(const struct nc_rpc *rpc);
 
 /**
- * @brief Create a generic NETCONF RPC
+ * @brief Create a generic NETCONF RPC or action
  *
  * Note that created object can be sent via any NETCONF session that shares the context
  * of the \p data.
@@ -193,10 +193,10 @@
  * @param[in] paramtype How to further manage data parameters.
  * @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
  */
-struct nc_rpc *nc_rpc_generic(const struct lyd_node *data, NC_PARAMTYPE paramtype);
+struct nc_rpc *nc_rpc_act_generic(const struct lyd_node *data, NC_PARAMTYPE paramtype);
 
 /**
- * @brief Create a generic NETCONF RPC from an XML string
+ * @brief Create a generic NETCONF RPC or action from an XML string
  *
  * Note that functions to create any RPC object do not check validity of the provided
  * parameters. It is checked later while sending the RPC via a specific NETCONF session
@@ -208,7 +208,7 @@
  * @param[in] paramtype How to further manage data parameters.
  * @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
  */
-struct nc_rpc *nc_rpc_generic_xml(const char *xml_str, NC_PARAMTYPE paramtype);
+struct nc_rpc *nc_rpc_act_generic_xml(const char *xml_str, NC_PARAMTYPE paramtype);
 
 /**
  * @brief Create NETCONF RPC \<get-config\>
diff --git a/src/messages_p.h b/src/messages_p.h
index 7a2d6d0..66e0546 100644
--- a/src/messages_p.h
+++ b/src/messages_p.h
@@ -78,8 +78,8 @@
     NC_RPC_TYPE type;
 };
 
-struct nc_rpc_generic {
-    NC_RPC_TYPE type;       /**< NC_RPC_GENERIC */
+struct nc_rpc_act_generic {
+    NC_RPC_TYPE type;       /**< NC_RPC_ACT_GENERIC */
     int has_data;           /**< 1 for content.data, 0 for content.xml_str */
     union {
         struct lyd_node *data;  /**< parsed RPC data */
diff --git a/src/session.c b/src/session.c
index ba55dbc..7eb08dd 100644
--- a/src/session.c
+++ b/src/session.c
@@ -516,10 +516,10 @@
 nc_server_get_cpblts(struct ly_ctx *ctx)
 {
     struct lyd_node *child, *child2, *yanglib;
-    struct lyd_node_leaf_list **features = NULL, *ns = NULL, *rev = NULL, *name = NULL;
+    struct lyd_node_leaf_list **features = NULL, **deviations = NULL, *ns = NULL, *rev = NULL, *name = NULL;
     const char **cpblts;
     const struct lys_module *mod;
-    int size = 10, count, feat_count = 0, i, str_len;
+    int size = 10, count, feat_count = 0, dev_count = 0, i, str_len;
 #define NC_CPBLT_BUF_LEN 512
     char str[NC_CPBLT_BUF_LEN];
 
@@ -638,9 +638,18 @@
                     if (!features) {
                         ERRMEM;
                         free(cpblts);
+                        free(deviations);
                         return NULL;
                     }
                     features[feat_count - 1] = (struct lyd_node_leaf_list *)child2;
+                } else if (!strcmp(child2->schema->name, "deviation")) {
+                    deviations = nc_realloc(deviations, ++dev_count * sizeof *deviations);
+                    if (!deviations) {
+                        ERRMEM;
+                        free(cpblts);
+                        free(features);
+                        return NULL;
+                    }
                 }
             }
 
@@ -667,15 +676,38 @@
                     str_len += strlen(features[i]->value_str);
                 }
             }
+            if (dev_count) {
+                strcat(str, "&deviations=");
+                str_len += 12;
+                for (i = 0; i < dev_count; ++i) {
+                    if (str_len + 1 + strlen(deviations[i]->value_str) >= NC_CPBLT_BUF_LEN) {
+                        ERRINT;
+                        break;
+                    }
+                    if (i) {
+                        strcat(str, ",");
+                        ++str_len;
+                    }
+                    strcat(str, deviations[i]->value_str);
+                    str_len += strlen(deviations[i]->value_str);
+                }
+            }
 
             add_cpblt(ctx, str, &cpblts, &size, &count);
 
             ns = NULL;
             name = NULL;
             rev = NULL;
-            free(features);
-            features = NULL;
-            feat_count = 0;
+            if (features || feat_count) {
+                free(features);
+                features = NULL;
+                feat_count = 0;
+            }
+            if (deviations || dev_count) {
+                free(deviations);
+                deviations = NULL;
+                dev_count = 0;
+            }
         }
     }
 
diff --git a/src/session_client.c b/src/session_client.c
index c048837..a3b7d85 100644
--- a/src/session_client.c
+++ b/src/session_client.c
@@ -191,8 +191,8 @@
 }
 
 static char *
-libyang_module_clb(const char *name, const char *revision, void *user_data, LYS_INFORMAT *format,
-                   void (**free_model_data)(void *model_data))
+libyang_module_clb(const char *mod_name, const char *mod_rev, const char *submod_name, const char *submod_rev,
+                   void *user_data, LYS_INFORMAT *format, void (**free_model_data)(void *model_data))
 {
     struct nc_session *session = (struct nc_session *)user_data;
     struct nc_rpc *rpc;
@@ -204,7 +204,11 @@
     uint64_t msgid;
 
     /* TODO later replace with yang to reduce model size? */
-    rpc = nc_rpc_getschema(name, revision, "yin", NC_PARAMTYPE_CONST);
+    if (submod_name) {
+        rpc = nc_rpc_getschema(submod_name, submod_rev, "yin", NC_PARAMTYPE_CONST);
+    } else {
+        rpc = nc_rpc_getschema(mod_name, mod_rev, "yin", NC_PARAMTYPE_CONST);
+    }
     *format = LYS_IN_YIN;
 
     while ((msg = nc_send_rpc(session, rpc, 0, &msgid)) == NC_MSG_WOULDBLOCK) {
@@ -735,11 +739,11 @@
 {
     struct lyxml_elem *iter;
     const struct lys_node *schema = NULL;
-    struct lyd_node *data = NULL;
+    struct lyd_node *data = NULL, *next, *elem;
     struct nc_client_reply_error *error_rpl;
     struct nc_reply_data *data_rpl;
     struct nc_reply *reply = NULL;
-    struct nc_rpc_generic *rpc_gen;
+    struct nc_rpc_act_generic *rpc_gen;
     int i;
 
     if (!xml->child) {
@@ -803,22 +807,39 @@
     /* some RPC output */
     } else {
         switch (rpc->type) {
-        case NC_RPC_GENERIC:
-            rpc_gen = (struct nc_rpc_generic *)rpc;
+        case NC_RPC_ACT_GENERIC:
+            rpc_gen = (struct nc_rpc_act_generic *)rpc;
 
             if (rpc_gen->has_data) {
-                schema = rpc_gen->content.data->schema;
+                data = rpc_gen->content.data;
             } else {
                 data = lyd_parse_mem(ctx, rpc_gen->content.xml_str, LYD_XML, LYD_OPT_RPC | parseroptions);
                 if (!data) {
-                    ERR("Failed to parse a generic RPC XML.");
+                    ERR("Failed to parse a generic RPC/action XML.");
                     return NULL;
                 }
+            }
+            if (data->schema->nodetype == LYS_RPC) {
+                /* RPC */
                 schema = data->schema;
+            } else {
+                /* action */
+                LY_TREE_DFS_BEGIN(data, next, elem) {
+                    if (elem->schema->nodetype == LYS_ACTION) {
+                        schema = elem->schema;
+                        break;
+                    }
+                    LY_TREE_DFS_END(data, next, elem);
+                }
+            }
+
+            /* cleanup */
+            if (data != rpc_gen->content.data) {
                 lyd_free(data);
                 data = NULL;
             }
             if (!schema) {
+                /* only with action, if there is no action, it should not have gotten this far */
                 ERRINT;
                 return NULL;
             }
@@ -1278,7 +1299,7 @@
 {
     NC_MSG_TYPE r;
     int ret;
-    struct nc_rpc_generic *rpc_gen;
+    struct nc_rpc_act_generic *rpc_gen;
     struct nc_rpc_getconfig *rpc_gc;
     struct nc_rpc_edit *rpc_e;
     struct nc_rpc_copy *rpc_cp;
@@ -1310,7 +1331,7 @@
         return NC_MSG_ERROR;
     }
 
-    if ((rpc->type != NC_RPC_GETSCHEMA) && (rpc->type != NC_RPC_GENERIC) && (rpc->type != NC_RPC_SUBSCRIBE)) {
+    if ((rpc->type != NC_RPC_GETSCHEMA) && (rpc->type != NC_RPC_ACT_GENERIC) && (rpc->type != NC_RPC_SUBSCRIBE)) {
         ietfnc = ly_ctx_get_module(session->ctx, "ietf-netconf", NULL);
         if (!ietfnc) {
             ERR("Session %u: missing ietf-netconf schema in the context.", session->id);
@@ -1319,8 +1340,8 @@
     }
 
     switch (rpc->type) {
-    case NC_RPC_GENERIC:
-        rpc_gen = (struct nc_rpc_generic *)rpc;
+    case NC_RPC_ACT_GENERIC:
+        rpc_gen = (struct nc_rpc_act_generic *)rpc;
 
         if (rpc_gen->has_data) {
             data = rpc_gen->content.data;
diff --git a/src/session_server.c b/src/session_server.c
index 1e15754..7744ede 100644
--- a/src/session_server.c
+++ b/src/session_server.c
@@ -923,6 +923,8 @@
 {
     nc_rpc_clb clb;
     struct nc_server_reply *reply;
+    struct lys_node *rpc_act = NULL;
+    struct lyd_node *next, *elem;
     int ret = 0, r;
 
     if (!rpc) {
@@ -930,11 +932,29 @@
         return NC_PSPOLL_ERROR;
     }
 
-    /* no callback, reply with a not-implemented error */
-    if (!rpc->tree->schema->priv) {
+    if (rpc->tree->schema->nodetype == LYS_RPC) {
+        /* RPC */
+        rpc_act = rpc->tree->schema;
+    } else {
+        /* action */
+        LY_TREE_DFS_BEGIN(rpc->tree, next, elem) {
+            if (elem->schema->nodetype == LYS_ACTION) {
+                rpc_act = elem->schema;
+                break;
+            }
+            LY_TREE_DFS_END(rpc->tree, next, elem);
+        }
+        if (!rpc_act) {
+            ERRINT;
+            return NC_PSPOLL_ERROR;
+        }
+    }
+
+    if (!rpc_act->priv) {
+        /* no callback, reply with a not-implemented error */
         reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
     } else {
-        clb = (nc_rpc_clb)rpc->tree->schema->priv;
+        clb = (nc_rpc_clb)rpc_act->priv;
         reply = clb(rpc->tree, session);
     }