session server BUGFIX more accurate description of NETCONF-error
diff --git a/src/session_server.c b/src/session_server.c
index 1c293e5..4f4633e 100644
--- a/src/session_server.c
+++ b/src/session_server.c
@@ -1401,6 +1401,96 @@
 }
 
 /**
+ * @brief Find lysc node mentioned in schema_path.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] ly_err last libyang error.
+ * @return lysc node.
+ */
+static const struct lysc_node *
+nc_rpc_err_find_lysc_node(const struct ly_ctx *ctx, const struct ly_err_item *ly_err)
+{
+    char *str, *last;
+    const struct lysc_node *cn;
+
+    if (!ly_err->schema_path) {
+        return NULL;
+    }
+
+    str = strdup(ly_err->schema_path);
+    if (!str) {
+        return NULL;
+    }
+    last = strrchr(str, '/');
+    if (strchr(last, '@')) {
+        /* ignore attribute part */
+        *last = '\0';
+    }
+    cn = lys_find_path(ctx, NULL, str, 0);
+    free(str);
+
+    return cn;
+}
+
+/**
+ * @brief Find the nth substring delimited by quotes.
+ *
+ * For example: abcd"ef"ghij"kl"mn -> index 0 is "ef", index 1 is "kl".
+ *
+ * @param[in] msg Input string with quoted substring.
+ * @param[in] index Number starting from 0 specifying the nth substring.
+ * @return Copied nth substring without quotes.
+ */
+static char *
+nc_rpc_err_get_quoted_string(const char *msg, uint32_t index)
+{
+    char *ret;
+    const char *start = NULL, *end = NULL, *iter, *tmp;
+    uint32_t quote_cnt = 0, last_quote;
+
+    assert(msg);
+
+    last_quote = (index + 1) * 2;
+    for (iter = msg; *iter; ++iter) {
+        if (*iter != '\"') {
+            continue;
+        }
+        /* updating the start and end pointers - swap */
+        tmp = end;
+        end = iter;
+        start = tmp;
+        if (++quote_cnt == last_quote) {
+            /* nth substring found */
+            break;
+        }
+    }
+
+    if (!start) {
+        return NULL;
+    }
+
+    /* Skip first quote */
+    ++start;
+    /* Copy substring */
+    ret = strndup(start, end - start);
+
+    return ret;
+}
+
+/**
+ * @brief Check that the @p str starts with the @p prefix.
+ *
+ * @param[in] prefix Required prefix.
+ * @param[in] str Input string to check.
+ * @return True if @p str start with @p prefix otherwise False.
+ */
+static ly_bool
+nc_strstarts(const char *prefix, const char *str)
+{
+    return strncmp(str, prefix, strlen(prefix)) == 0;
+}
+
+/**
  * @brief Prepare reply for rpc error.
  *
  * @param[in] session NETCONF session.
@@ -1410,28 +1500,84 @@
 static struct nc_server_reply *
 nc_server_prepare_rpc_err(struct nc_session *session, struct lyd_node *envp)
 {
-    struct lyd_node *node;
+    struct lyd_node *reply = NULL;
+    const struct lysc_node *cn;
     const struct ly_err_item *ly_err;
+    NC_ERR_TYPE errtype;
+    const char *attr;
+    char *str = NULL, *errmsg = NULL, *schema_path = NULL;
+    LY_ERR errcode;
 
+    /* envelope was not parsed */
     if (!envp && (session->version != NC_VERSION_11)) {
         return NULL;
     }
-
     ly_err = ly_err_last(session->ctx);
-    if (envp) {
-        /* at least the envelopes were parsed */
-        node = nc_err(session->ctx, NC_ERR_OP_FAILED, NC_ERR_TYPE_APP);
-        nc_err_set_msg(node, ly_err->msg, "en");
-    } else if (!strcmp("Missing XML namespace.", ly_err->msg)) {
-        node = nc_err(session->ctx, NC_ERR_MISSING_ATTR, NC_ERR_TYPE_RPC, "xmlns", "rpc");
-        nc_err_set_msg(node, ly_err->msg, "en");
-    } else {
+    if (!envp && !strcmp("Missing XML namespace.", ly_err->msg)) {
+        reply = nc_err(session->ctx, NC_ERR_MISSING_ATTR, NC_ERR_TYPE_RPC, "xmlns", "rpc");
+        goto cleanup;
+    } else if (!envp) {
         /* completely malformed message, NETCONF version 1.1 defines sending error reply from
          * the server (RFC 6241 sec. 3) */
-        node = nc_err(session->ctx, NC_ERR_MALFORMED_MSG);
+        reply = nc_err(session->ctx, NC_ERR_MALFORMED_MSG);
+        return nc_server_reply_err(reply);
+    }
+    /* at least the envelopes were parsed */
+    assert(envp);
+
+    /* store strings, to avoid overwriting ly_err */
+    errmsg = strdup(ly_err->msg);
+    if (!errmsg) {
+        reply = nc_err(session->ctx, NC_ERR_OP_FAILED, NC_ERR_TYPE_APP);
+        goto cleanup;
+    }
+    if (ly_err->schema_path) {
+        schema_path = strdup(ly_err->schema_path);
+        if (!schema_path) {
+            reply = nc_err(session->ctx, NC_ERR_OP_FAILED, NC_ERR_TYPE_APP);
+            goto cleanup;
+        }
+    }
+    errcode = ly_err->err;
+
+    /* find out in which layer the error occurred */
+    cn = nc_rpc_err_find_lysc_node(session->ctx, ly_err);
+    if (cn && ((cn->nodetype & LYS_RPC) || (cn->nodetype & LYS_INPUT))) {
+        errtype = NC_ERR_TYPE_PROT;
+    } else {
+        errtype = NC_ERR_TYPE_APP;
     }
 
-    return nc_server_reply_err(node);
+    /* deciding which error to prepare */
+    if (cn && (nc_strstarts("Missing mandatory prefix", errmsg) ||
+            nc_strstarts("Unknown XML prefix", errmsg))) {
+        str = nc_rpc_err_get_quoted_string(errmsg, 1);
+        reply = str ? nc_err(session->ctx, NC_ERR_UNKNOWN_ATTR, errtype, str, cn->name) :
+                nc_err(session->ctx, NC_ERR_OP_FAILED, NC_ERR_TYPE_APP);
+    } else if (cn && nc_strstarts("Annotation definition for attribute", errmsg)) {
+        attr = strrchr(schema_path, ':') + 1;
+        reply = nc_err(session->ctx, NC_ERR_UNKNOWN_ATTR, errtype, attr, cn->name);
+    } else if (nc_strstarts("Invalid character sequence", errmsg)) {
+        reply = nc_err(session->ctx, NC_ERR_MALFORMED_MSG);
+    } else if (errcode == LY_EMEM) {
+        /* <error-tag>resource-denied</error-tag> */
+        reply = nc_err(session->ctx, NC_ERR_RES_DENIED, errtype);
+    } else {
+        /* prepare some generic error */
+        reply = nc_err(session->ctx, NC_ERR_OP_FAILED, NC_ERR_TYPE_APP);
+    }
+
+cleanup:
+    nc_err_set_msg(reply, errmsg, "en");
+
+    /* clear for other errors */
+    ly_err_clean(session->ctx, NULL);
+
+    free(errmsg);
+    free(schema_path);
+    free(str);
+
+    return nc_server_reply_err(reply);
 }
 
 /* should be called holding the session RPC lock! IO lock will be acquired as needed