FEATURE new session context checking and creation

TODO get schemas using <get-schema>
diff --git a/src/session.c b/src/session.c
index 58309bf..9898e71 100644
--- a/src/session.c
+++ b/src/session.c
@@ -104,7 +104,7 @@
 }
 
 int
-handshake(struct nc_session *session)
+nc_handshake(struct nc_session *session)
 {
     NC_MSG_TYPE type;
 
@@ -122,9 +122,89 @@
 }
 
 static int
-connect_load_schemas(struct ly_ctx *ctx)
+ctx_load_model(struct nc_session *session, const char *cpblt, int get_schema_support)
 {
-    int fd;
+    struct lys_module *module;
+    char *ptr, *ptr2;
+    char *model_name, *revision = NULL, *features = NULL;
+
+    /*if (get_schema_support) {
+        * TODO *
+    } else*/ {
+        /* parse module */
+        ptr = strstr(cpblt, "module=");
+        if (!ptr) {
+            WRN("Unknown capability \"%s\" could not be parsed.");
+            return 1;
+        }
+        ptr += 7;
+        ptr2 = strchr(ptr, '&');
+        if (!ptr2) {
+            ptr2 = ptr + strlen(ptr);
+        }
+        model_name = strndup(ptr, ptr2 - ptr);
+
+        /* parse revision */
+        ptr = strstr(cpblt, "revision=");
+        if (ptr) {
+            ptr += 9;
+            ptr2 = strchr(ptr, '&');
+            if (!ptr2) {
+                ptr2 = ptr + strlen(ptr);
+            }
+            revision = strndup(ptr, ptr2 - ptr);
+        }
+
+        /* load module */
+        module = ly_ctx_load_module(session->ctx, NULL, model_name, revision);
+
+        free(model_name);
+        free(revision);
+        if (!module) {
+            return 1;
+        }
+
+        /* parse features */
+        ptr = strstr(cpblt, "features=");
+        if (ptr) {
+            ptr += 9;
+            ptr2 = strchr(ptr, '&');
+            if (!ptr2) {
+                ptr2 = ptr + strlen(ptr);
+            }
+            features = strndup(ptr, ptr2 - ptr);
+        }
+
+        /* enable features */
+        if (features) {
+            /* basically manual strtok_r (to avoid macro) */
+            ptr2 = features;
+            for (ptr = features; *ptr; ++ptr) {
+                if (*ptr == ',') {
+                    *ptr = '\0';
+                    /* remember last feature */
+                    ptr2 = ptr + 1;
+                }
+            }
+
+            ptr = features;
+            lys_features_enable(module, ptr);
+            while (ptr != ptr2) {
+                ptr += strlen(ptr) + 1;
+                lys_features_enable(module, ptr);
+            }
+
+            free(features);
+        }
+    }
+
+    return 0;
+}
+
+static int
+ctx_load_ietf_netconf(struct ly_ctx *ctx, const char **cpblts)
+{
+    int fd, i;
     struct lys_module *ietfnc;
 
     fd = open(SCHEMAS_DIR"ietf-netconf.yin", O_RDONLY);
@@ -134,69 +214,87 @@
     }
     if (!(ietfnc = lys_read(ctx, fd, LYS_IN_YIN))) {
         ERR("Loading base NETCONF schema (%s) failed.", SCHEMAS_DIR"ietf-netconf");
+        close(fd);
         return 1;
     }
     close(fd);
 
     /* set supported capabilities from ietf-netconf */
-    lys_features_enable(ietfnc, "writable-running");
-    lys_features_enable(ietfnc, "candidate");
-    //lys_features_enable(ietfnc, "confirmed-commit");
-    lys_features_enable(ietfnc, "rollback-on-error");
-    lys_features_enable(ietfnc, "validate");
-    lys_features_enable(ietfnc, "startup");
-    lys_features_enable(ietfnc, "url");
-    lys_features_enable(ietfnc, "xpath");
+    for (i = 0; cpblts[i]; ++i) {
+        if (!strncmp(cpblts[i], "urn:ietf:params:netconf:capability:", 35)) {
+            if (!strncmp(cpblts[i] + 35, "writable-running", 16)) {
+                lys_features_enable(ietfnc, "writable-running");
+            } else if (!strncmp(cpblts[i] + 35, "candidate", 9)) {
+                lys_features_enable(ietfnc, "candidate");
+            } else if (!strcmp(cpblts[i] + 35, "confirmed-commit:1.1")) {
+                lys_features_enable(ietfnc, "confirmed-commit");
+            } else if (!strncmp(cpblts[i] + 35, "rollback-on-error", 17)) {
+                lys_features_enable(ietfnc, "rollback-on-error");
+            } else if (!strcmp(cpblts[i] + 35, "validate:1.1")) {
+                lys_features_enable(ietfnc, "validate");
+            } else if (!strncmp(cpblts[i] + 35, "startup", 7)) {
+                lys_features_enable(ietfnc, "startup");
+            } else if (!strncmp(cpblts[i] + 35, "url", 3)) {
+                lys_features_enable(ietfnc, "url");
+            } else if (!strncmp(cpblts[i] + 35, "xpath", 5)) {
+                lys_features_enable(ietfnc, "xpath");
+            }
+        }
+    }
 
     return 0;
 }
 
-struct nc_session *
-connect_init(struct ly_ctx *ctx)
+/* session with an empty context is assumed */
+int
+nc_ctx_fill(struct nc_session *session)
 {
-    struct nc_session *session = NULL;
-    const char *str;
-    int r;
+    int i, get_schema_support;
 
-    /* prepare session structure */
-    session = calloc(1, sizeof *session);
-    if (!session) {
-        ERRMEM;
-        return NULL;
-    }
-    session->status = NC_STATUS_STARTING;
-    session->side = NC_CLIENT;
+    assert(session->cpblts && session->ctx);
 
-    /* YANG context for the session */
-    if (ctx) {
-        session->flags |= NC_SESSION_SHAREDCTX;
-        session->ctx = ctx;
-
-        /* check presence of the required schemas */
-        if (!ly_ctx_get_module(session->ctx, "ietf-netconf", NULL)) {
-            str = ly_ctx_get_searchdir(session->ctx);
-            ly_ctx_set_searchdir(session->ctx, SCHEMAS_DIR);
-            r = connect_load_schemas(session->ctx);
-            ly_ctx_set_searchdir(session->ctx, str);
-
-            if (r) {
-                nc_session_free(session);
-                return NULL;
-            }
+    /* check if get-schema is supported */
+    get_schema_support = 0;
+    for (i = 0; session->cpblts[i]; ++i) {
+        if (!strncmp(session->cpblts[i], "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring", 51)) {
+            get_schema_support = 1;
+            break;
         }
-    } else {
-        session->ctx = ly_ctx_new(SCHEMAS_DIR);
-
-        /* load basic NETCONF schemas required for libnetconf work */
-        if (connect_load_schemas(session->ctx)) {
-            nc_session_free(session);
-            return NULL;
-        }
-
-        ly_ctx_set_searchdir(session->ctx, schema_searchpath);
     }
 
-    return session;
+    /* load base model disregarding whether it's in capabilities (but NETCONF capabilities are used to enable features) */
+    if (ctx_load_ietf_netconf(session->ctx, session->cpblts)) {
+        goto fail;
+    }
+
+    /* load all other models */
+    for (i = 0; session->cpblts[i]; ++i) {
+        if (!strncmp(session->cpblts[i], "urn:ietf:params:netconf:capability", 34)
+                || !strncmp(session->cpblts[i], "urn:ietf:params:netconf:base", 28)) {
+            continue;
+        }
+
+        ctx_load_model(session, session->cpblts[i], get_schema_support);
+    }
+
+    return 0;
+
+fail:
+    free(session->ctx);
+    return 1;
+}
+
+int
+nc_ctx_check(struct nc_session *session)
+{
+    /* check presence of the required base schema */
+    if (!ly_ctx_get_module(session->ctx, "ietf-netconf", NULL)) {
+        if (ctx_load_ietf_netconf(session->ctx, session->cpblts)) {
+            return 1;
+        }
+    }
+
+    return 0;
 }
 
 API struct nc_session *
@@ -210,31 +308,53 @@
     }
 
     /* prepare session structure */
-    session = connect_init(ctx);
+    session = calloc(1, sizeof *session);
     if (!session) {
+        ERRMEM;
         return NULL;
     }
+    session->status = NC_STATUS_STARTING;
+    session->side = NC_CLIENT;
 
     /* transport specific data */
     session->ti_type = NC_TI_FD;
     session->ti.fd.in = fdin;
     session->ti.fd.out = fdout;
 
+    /* assign context (dicionary needed for handshake) */
+    if (!ctx) {
+        ctx = ly_ctx_new(SCHEMAS_DIR);
+    } else {
+        session->flags |= NC_SESSION_SHAREDCTX;
+    }
+    session->ctx = ctx;
+
     /* NETCONF handshake */
-    if (handshake(session)) {
-        goto error;
+    if (nc_handshake(session)) {
+        goto fail;
+    }
+
+    /* check/fill libyang context */
+    if (session->flags & NC_SESSION_SHAREDCTX) {
+        if (nc_ctx_check(session)) {
+            goto fail;
+        }
+    } else {
+        if (nc_ctx_fill(session)) {
+            goto fail;
+        }
     }
 
     session->status = NC_STATUS_RUNNING;
     return session;
 
-error:
+fail:
     nc_session_free(session);
     return NULL;
 }
 
 int
-connect_getsocket(const char* host, unsigned short port)
+nc_connect_getsocket(const char* host, unsigned short port)
 {
     int sock = -1;
     int i;
@@ -458,7 +578,8 @@
         LY_TREE_FOR(xml->child, cpblt) {
             i++;
         }
-        *list = calloc(i, sizeof **list);
+        /* last item remains NULL */
+        *list = calloc(i + 1, sizeof **list);
         if (!*list) {
             ERRMEM;
             return -1;
@@ -529,7 +650,7 @@
                 }
                 flag = 1;
 
-                if ((ver = parse_cpblts(node, NULL)) < 0) {
+                if ((ver = parse_cpblts(node, &session->cpblts)) < 0) {
                     goto error;
                 }
                 session->version = ver;
@@ -563,7 +684,7 @@
                 }
                 flag = 1;
 
-                if ((ver = parse_cpblts(node, NULL)) < 0) {
+                if ((ver = parse_cpblts(node, &session->cpblts)) < 0) {
                     goto error;
                 }
                 session->version = ver;
diff --git a/src/session.h b/src/session.h
index cb7478b..e2bca47 100644
--- a/src/session.h
+++ b/src/session.h
@@ -83,7 +83,9 @@
  *                (ignoring what is actually supported by the server side). If not set,
  *                YANG context is created for the session using \<get-schema\> (if supported
  *                by the server side) or/and by searching for YANG schemas in the searchpath
- *                (see nc_schema_searchpath()).
+ *                (see nc_schema_searchpath()). In every case except not providing context
+ *                to connect to a server supporting \<get-schema\> it is possible that
+ *                the session context will not include all the models supported by the server.
  * @return Created NETCONF session object or NULL in case of error.
  */
 struct nc_session *nc_connect_inout(int fdin, int fdout, struct ly_ctx *ctx);
@@ -128,7 +130,9 @@
  *                (ignoring what is actually supported by the server side). If not set,
  *                YANG context is created for the session using \<get-schema\> (if supported
  *                by the server side) or/and by searching for YANG schemas in the searchpath
- *                (see nc_schema_searchpath()).
+ *                (see nc_schema_searchpath()). In every case except not providing context
+ *                to connect to a server supporting \<get-schema\> it is possible that
+ *                the session context will not include all the models supported by the server.
  * @return Created NETCONF session object or NULL in case of error.
  */
 struct nc_session *nc_connect_ssh(const char *host, unsigned short port, const char* username, struct ly_ctx *ctx);
@@ -149,7 +153,9 @@
  *                (ignoring what is actually supported by the server side). If not set,
  *                YANG context is created for the session using \<get-schema\> (if supported
  *                by the server side) or/and by searching for YANG schemas in the searchpath
- *                (see nc_schema_searchpath()).
+ *                (see nc_schema_searchpath()). In every case except not providing context
+ *                to connect to a server supporting \<get-schema\> it is possible that
+ *                the session context will not include all the models supported by the server.
  * @return Created NETCONF session object or NULL in case of error.
  */
 struct nc_session *nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx);
@@ -165,7 +171,9 @@
  *                (ignoring what is actually supported by the server side). If not set,
  *                YANG context is created for the session using \<get-schema\> (if supported
  *                by the server side) or/and by searching for YANG schemas in the searchpath
- *                (see nc_schema_searchpath()).
+ *                (see nc_schema_searchpath()). In every case except not providing context
+ *                to connect to a server supporting \<get-schema\> it is possible that
+ *                the session context will not include all the models supported by the server.
  * @return Created NETCONF session object or NULL in case of error.
  */
 struct nc_session *nc_connect_ssh_channel(struct nc_session *session, struct ly_ctx *ctx);
@@ -249,7 +257,9 @@
  *                (ignoring what is actually supported by the server side). If not set,
  *                YANG context is created for the session using \<get-schema\> (if supported
  *                by the server side) or/and by searching for YANG schemas in the searchpath
- *                (see nc_schema_searchpath()).
+ *                (see nc_schema_searchpath()). In every case except not providing context
+ *                to connect to a server supporting \<get-schema\> it is possible that
+ *                the session context will not include all the models supported by the server.
  * @return Created NETCONF session object or NULL in case of error.
  */
 struct nc_session *nc_connect_tls(const char *host, unsigned short port, const char *username, struct ly_ctx *ctx);
@@ -266,7 +276,9 @@
  *                (ignoring what is actually supported by the server side). If not set,
  *                YANG context is created for the session using \<get-schema\> (if supported
  *                by the server side) or/and by searching for YANG schemas in the searchpath
- *                (see nc_schema_searchpath()).
+ *                (see nc_schema_searchpath()). In every case except not providing context
+ *                to connect to a server supporting \<get-schema\> it is possible that
+ *                the session context will not include all the models supported by the server.
  * @return Created NETCONF session object or NULL in case of error.
  */
 struct nc_session *nc_connect_libssl(SSL *tls, struct ly_ctx *ctx);
diff --git a/src/session_p.h b/src/session_p.h
index dd80974..c5eee5b 100644
--- a/src/session_p.h
+++ b/src/session_p.h
@@ -179,6 +179,43 @@
 };
 
 /**
+ * @brief Fill libyang context in \p session. Context models are based on the stored session
+ *        capabilities. If the server does not support \<get-schema\>, the models are searched
+ *        for in the directory set using nc_schema_searchpath().
+ *
+ * @param[in] session Session to create the context for.
+ * @return 0 on success, non-zero on failure.
+ */
+int nc_ctx_fill(struct nc_session *session);
+
+/**
+ * @brief Check whether the libyang context in \p session is suitable for NETCONF use
+ *        meaning whether the ietf-netconf model is loaded.
+ *
+ * @param[in] session Session with the capabilities to be supported if loading ietf-netconf
+ *                    explicitly.
+ * @return 0 on success, non-zero on failure.
+ */
+int nc_ctx_check(struct nc_session *session);
+
+/**
+ * @brief Create and connect a socket.
+ *
+ * @param[in] host Hostname to connect to.
+ * @param[in] port Port to connect on.
+ * @return Connected socket or -1 on error.
+ */
+int nc_connect_getsocket(const char *host, unsigned short port);
+
+/**
+ * @brief Perform NETCONF handshake on \p session.
+ *
+ * @param[in] session NETCONF session to use.
+ * @return 0 on success, non-zero on failure.
+ */
+int nc_handshake(struct nc_session *session);
+
+/**
  * Functions
  * - io.c
  */
diff --git a/src/session_ssh.c b/src/session_ssh.c
index a9b874c..382cf9f 100644
--- a/src/session_ssh.c
+++ b/src/session_ssh.c
@@ -46,16 +46,12 @@
 
 #include "libnetconf.h"
 #include "session.h"
+#include "session_p.h"
 
 static struct nc_ssh_auth_opts ssh_opts = {
     .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 3}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLIC_KEYS, 1}}
 };
 
-/* internal functions from session.c */
-struct nc_session *connect_init(struct ly_ctx *ctx);
-int connect_getsocket(const char *host, unsigned short port);
-int handshake(struct nc_session *session);
-
 API void
 nc_client_init_ssh(void)
 {
@@ -859,11 +855,13 @@
     }
 
     /* prepare session structure */
-    session = connect_init(ctx);
+    session = calloc(1, sizeof *session);
     if (!session) {
+        ERRMEM;
         return NULL;
     }
-    session->ti_type = NC_TI_LIBSSH;
+    session->status = NC_STATUS_STARTING;
+    session->side = NC_CLIENT;
 
     /* transport lock */
     session->ti_lock = malloc(sizeof *session->ti_lock);
@@ -874,9 +872,7 @@
     pthread_mutex_init(session->ti_lock, NULL);
 
     /* other transport-specific data */
-    session->username = lydict_insert(session->ctx, username, 0);
-    session->host = lydict_insert(session->ctx, host, 0);
-    session->port = port;
+    session->ti_type = NC_TI_LIBSSH;
     session->ti.libssh.session = ssh_new();
     if (!session->ti.libssh.session) {
         ERR("Unable to initialize SSH session.");
@@ -884,27 +880,54 @@
     }
 
     /* set some basic SSH session options */
-    ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, session->host);
-    ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_PORT, &session->port);
-    ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, session->username);
+    ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
+    ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_PORT, &port);
+    ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
     ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_TIMEOUT, &timeout);
 
     /* create and assign communication socket */
-    sock = connect_getsocket(host, port);
+    sock = nc_connect_getsocket(host, port);
     if (sock == -1) {
         goto fail;
     }
     ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
 
+    /* temporarily, for session connection */
+    session->host = host;
+    session->username = username;
     if (connect_ssh_session_netconf(session)) {
         goto fail;
     }
 
+    /* assign context (dicionary needed for handshake) */
+    if (!ctx) {
+        ctx = ly_ctx_new(SCHEMAS_DIR);
+    } else {
+        session->flags |= NC_SESSION_SHAREDCTX;
+    }
+    session->ctx = ctx;
+
     /* NETCONF handshake */
-    if (handshake(session)) {
+    if (nc_handshake(session)) {
         goto fail;
     }
 
+    /* check/fill libyang context */
+    if (session->flags & NC_SESSION_SHAREDCTX) {
+        if (nc_ctx_check(session)) {
+            goto fail;
+        }
+    } else {
+        if (nc_ctx_fill(session)) {
+            goto fail;
+        }
+    }
+
+    /* store information into the dictionary */
+    session->host = lydict_insert(ctx, host, 0);
+    session->port = port;
+    session->username = lydict_insert(ctx, username, 0);
+
     session->status = NC_STATUS_RUNNING;
     return session;
 
@@ -916,18 +939,20 @@
 API struct nc_session *
 nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx)
 {
-    const char *host, *username;
-    unsigned short port;
+    char *host = NULL, *username = NULL;
+    unsigned short port = 0;
     int sock;
     struct passwd *pw;
     struct nc_session *session = NULL;
 
     /* prepare session structure */
-    session = connect_init(ctx);
+    session = calloc(1, sizeof *session);
     if (!session) {
+        ERRMEM;
         return NULL;
     }
-    session->ti_type = NC_TI_LIBSSH;
+    session->status = NC_STATUS_STARTING;
+    session->side = NC_CLIENT;
 
     /* transport lock */
     session->ti_lock = malloc(sizeof *session->ti_lock);
@@ -937,37 +962,28 @@
     }
     pthread_mutex_init(session->ti_lock, NULL);
 
+    session->ti_type = NC_TI_LIBSSH;
     session->ti.libssh.session = ssh_session;
 
-    if (!ctx) {
-        ctx = session->ctx;
-    }
-
     if (ssh_get_fd(ssh_session) == -1) {
         /*
          * There is no file descriptor, we need to create it. (TCP/IP layer)
          */
 
         /* was host, port set? */
-        if (ssh_options_get(ssh_session, SSH_OPTIONS_HOST, (char **)&host) != SSH_OK) {
+        if (ssh_options_get(ssh_session, SSH_OPTIONS_HOST, &host) != SSH_OK) {
             host = NULL;
         }
         ssh_options_get_port(ssh_session, (unsigned int *)&port);
 
         /* remember host */
         if (!host) {
-            host = lydict_insert(ctx, "localhost", 0);
+            host = strdup("localhost");
             ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
-        } else {
-            host = lydict_insert_zc(ctx, (char *)host);
         }
-        session->host = host;
-
-        /* remember port (even if not set, dumb libssh returns 22, no way to know if it was actually set) */
-        session->port = port;
 
         /* create and connect socket */
-        sock = connect_getsocket(host, port);
+        sock = nc_connect_getsocket(host, port);
         if (sock == -1) {
             goto fail;
         }
@@ -980,7 +996,7 @@
          */
 
         /* was username set? */
-        if (ssh_options_get(ssh_session, SSH_OPTIONS_USER, (char **)&username) != SSH_OK) {
+        if (ssh_options_get(ssh_session, SSH_OPTIONS_USER, &username) != SSH_OK) {
             username = NULL;
         }
 
@@ -992,14 +1008,13 @@
                 goto fail;
             }
 
-            username = lydict_insert(ctx, pw->pw_name, 0);
+            username = strdup(pw->pw_name);
             ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
-        } else {
-            username = lydict_insert_zc(ctx, (char *)username);
         }
-        session->username = username;
 
         /* authenticate SSH session */
+        session->host = host;
+        session->username = username;
         if (connect_ssh_session_netconf(session)) {
             goto fail;
         }
@@ -1009,11 +1024,41 @@
      * SSH session is established, create NETCONF session. (Application layer)
      */
 
+    /* assign context (dicionary needed for handshake) */
+    if (!ctx) {
+        ctx = ly_ctx_new(SCHEMAS_DIR);
+    } else {
+        session->flags |= NC_SESSION_SHAREDCTX;
+    }
+    session->ctx = ctx;
+
     /* NETCONF handshake */
-    if (handshake(session)) {
+    if (nc_handshake(session)) {
         goto fail;
     }
 
+    /* check/fill libyang context */
+    if (session->flags & NC_SESSION_SHAREDCTX) {
+        if (nc_ctx_check(session)) {
+            goto fail;
+        }
+    } else {
+        if (nc_ctx_fill(session)) {
+            goto fail;
+        }
+    }
+
+    /* store information into the dictionary */
+    if (host) {
+        session->host = lydict_insert_zc(ctx, host);
+    }
+    if (port) {
+        session->port = port;
+    }
+    if (username) {
+        session->username = lydict_insert_zc(ctx, username);
+    }
+
     session->status = NC_STATUS_RUNNING;
     return session;
 
@@ -1028,20 +1073,17 @@
     struct nc_session *new_session, *ptr;
 
     /* prepare session structure */
-    new_session = connect_init(ctx);
+    new_session = calloc(1, sizeof *new_session);
     if (!new_session) {
+        ERRMEM;
         return NULL;
     }
-    if (!ctx) {
-        ctx = new_session->ctx;
-    }
+    new_session->status = NC_STATUS_STARTING;
+    new_session->side = NC_CLIENT;
 
     /* share some parameters including the session lock */
     new_session->ti_type = NC_TI_LIBSSH;
     new_session->ti_lock = session->ti_lock;
-    new_session->host = lydict_insert(ctx, session->host, 0);
-    new_session->port = session->port;
-    new_session->username = lydict_insert(ctx, session->username, 0);
     new_session->ti.libssh.session = session->ti.libssh.session;
 
     /* create the channel safely */
@@ -1050,21 +1092,44 @@
     /* open a channel */
     new_session->ti.libssh.channel = ssh_channel_new(new_session->ti.libssh.session);
     if (ssh_channel_open_session(new_session->ti.libssh.channel) != SSH_OK) {
-        nc_session_free(new_session);
         ERR("Opening an SSH channel failed (%s)", ssh_get_error(session->ti.libssh.session));
-        return NULL;
+        goto fail;
     }
     /* execute the NETCONF subsystem on the channel */
     if (ssh_channel_request_subsystem(new_session->ti.libssh.channel, "netconf") != SSH_OK) {
-        nc_session_free(new_session);
         ERR("Starting the \"netconf\" SSH subsystem failed (%s)", ssh_get_error(session->ti.libssh.session));
-        return NULL;
+        goto fail;
     }
+
+    /* assign context (dicionary needed for handshake) */
+    if (!ctx) {
+        ctx = ly_ctx_new(SCHEMAS_DIR);
+    } else {
+        session->flags |= NC_SESSION_SHAREDCTX;
+    }
+    session->ctx = ctx;
+
     /* NETCONF handshake */
-    if (handshake(new_session)) {
-        nc_session_free(new_session);
-        return NULL;
+    if (nc_handshake(new_session)) {
+        goto fail;
     }
+
+    /* check/fill libyang context */
+    if (session->flags & NC_SESSION_SHAREDCTX) {
+        if (nc_ctx_check(session)) {
+            goto fail;
+        }
+    } else {
+        if (nc_ctx_fill(session)) {
+            goto fail;
+        }
+    }
+
+    /* store information into session and the dictionary */
+    session->host = lydict_insert(ctx, session->host, 0);
+    session->port = session->port;
+    session->username = lydict_insert(ctx, session->username, 0);
+
     new_session->status = NC_STATUS_RUNNING;
 
     pthread_mutex_unlock(new_session->ti_lock);
@@ -1080,4 +1145,8 @@
     }
 
     return new_session;
+
+fail:
+    nc_session_free(new_session);
+    return NULL;
 }
diff --git a/src/session_tls.c b/src/session_tls.c
index 3592910..c195907 100644
--- a/src/session_tls.c
+++ b/src/session_tls.c
@@ -34,6 +34,7 @@
 
 #include "libnetconf.h"
 #include "session.h"
+#include "session_p.h"
 
 /* TLS certificate verification error messages */
 static const char* verify_ret_msg[] = {
@@ -74,11 +75,6 @@
 
 static struct nc_tls_auth_opts tls_opts;
 
-/* internal functions from session.c */
-struct nc_session *connect_init(struct ly_ctx *ctx);
-int connect_getsocket(const char* host, unsigned short port);
-int handshake(struct nc_session *session);
-
 static int
 tlsauth_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
 {
@@ -294,11 +290,13 @@
     }
 
     /* prepare session structure */
-    session = connect_init(ctx);
+    session = calloc(1, sizeof *session);
     if (!session) {
+        ERRMEM;
         return NULL;
     }
-    session->ti_type = NC_TI_OPENSSL;
+    session->status = NC_STATUS_STARTING;
+    session->side = NC_CLIENT;
 
     /* transport lock */
     session->ti_lock = malloc(sizeof *session->ti_lock);
@@ -309,16 +307,14 @@
     pthread_mutex_init(session->ti_lock, NULL);
 
     /* fill the session */
-    session->username = lydict_insert(session->ctx, username, 0);
-    session->host = lydict_insert(session->ctx, host, 0);
-    session->port = port;
+    session->ti_type = NC_TI_OPENSSL;
     if (!(session->ti.tls = SSL_new(tls_opts.tls_ctx))) {
         ERR("Failed to create new TLS session structure (%s)", ERR_reason_error_string(ERR_get_error()));
         goto fail;
     }
 
     /* create and assign socket */
-    sock = connect_getsocket(host, port);
+    sock = nc_connect_getsocket(host, port);
     if (sock == -1) {
         goto fail;
     }
@@ -343,11 +339,35 @@
         WRN("Server certificate verification problem (%s).", verify_ret_msg[verify]);
     }
 
+    /* assign context (dicionary needed for handshake) */
+    if (!ctx) {
+        ctx = ly_ctx_new(SCHEMAS_DIR);
+    } else {
+        session->flags |= NC_SESSION_SHAREDCTX;
+    }
+    session->ctx = ctx;
+
     /* NETCONF handshake */
-    if (handshake(session)) {
+    if (nc_handshake(session)) {
         goto fail;
     }
 
+    /* check/fill libyang context */
+    if (session->flags & NC_SESSION_SHAREDCTX) {
+        if (nc_ctx_check(session)) {
+            goto fail;
+        }
+    } else {
+        if (nc_ctx_fill(session)) {
+            goto fail;
+        }
+    }
+
+    /* store information into session and the dictionary */
+    session->host = lydict_insert(ctx, host, 0);
+    session->port = port;
+    session->username = lydict_insert(ctx, username, 0);
+
     session->status = NC_STATUS_RUNNING;
     return session;
 
@@ -368,11 +388,13 @@
     }
 
     /* prepare session structure */
-    session = connect_init(ctx);
+    session = calloc(1, sizeof *session);
     if (!session) {
+        ERRMEM;
         return NULL;
     }
-    session->ti_type = NC_TI_OPENSSL;
+    session->status = NC_STATUS_STARTING;
+    session->side = NC_CLIENT;
 
     /* transport lock */
     session->ti_lock = malloc(sizeof *session->ti_lock);
@@ -382,17 +404,33 @@
     }
     pthread_mutex_init(session->ti_lock, NULL);
 
+    session->ti_type = NC_TI_OPENSSL;
     session->ti.tls = tls;
 
+    /* assign context (dicionary needed for handshake) */
     if (!ctx) {
-        ctx = session->ctx;
+        ctx = ly_ctx_new(SCHEMAS_DIR);
+    } else {
+        session->flags |= NC_SESSION_SHAREDCTX;
     }
+    session->ctx = ctx;
 
     /* NETCONF handshake */
-    if (handshake(session)) {
+    if (nc_handshake(session)) {
         goto fail;
     }
 
+    /* check/fill libyang context */
+    if (session->flags & NC_SESSION_SHAREDCTX) {
+        if (nc_ctx_check(session)) {
+            goto fail;
+        }
+    } else {
+        if (nc_ctx_fill(session)) {
+            goto fail;
+        }
+    }
+
     session->status = NC_STATUS_RUNNING;
     return session;