notifications FEATURE basic support for sending notifications from server side
diff --git a/src/io.c b/src/io.c
index 40fe815..a9afb58 100644
--- a/src/io.c
+++ b/src/io.c
@@ -964,6 +964,7 @@
     const char *attrs;
     struct lyd_node *content;
     struct lyxml_elem *rpc_elem;
+    struct nc_server_notif *notif;
     struct nc_server_reply *reply;
     struct nc_server_reply_error *error_rpl;
     char *buf = NULL;
@@ -1057,8 +1058,13 @@
         break;
 
     case NC_MSG_NOTIF:
-        nc_write_clb((void *)&arg, "<notification xmlns=\""NC_NS_NOTIF"\"/>", 21 + 47 + 3, 0);
-        /* TODO content */
+        notif = va_arg(ap, struct nc_server_notif *);
+
+        nc_write_clb((void *)&arg, "<notification xmlns=\""NC_NS_NOTIF"\">", 21 + 47 + 2, 0);
+        nc_write_clb((void *)&arg, "<eventTime>", 11, 0);
+        nc_write_clb((void *)&arg, notif->eventtime, strlen(notif->eventtime), 0);
+        nc_write_clb((void *)&arg, "</eventTime>", 12, 0);
+        lyd_print_clb(nc_write_xmlclb, (void *)&arg, notif->tree, LYD_XML, 0);
         nc_write_clb((void *)&arg, "</notification>", 12, 0);
         break;
 
diff --git a/src/messages_p.h b/src/messages_p.h
index ca171f7..e832b08 100644
--- a/src/messages_p.h
+++ b/src/messages_p.h
@@ -68,6 +68,11 @@
     struct lyd_node *tree;   /**< libyang data tree of the message (NETCONF operation) */
 };
 
+struct nc_server_notif {
+    char *eventtime;        /**< eventTime of the notification */
+    struct lyd_node *tree;  /**< libyang data tree of the message */
+};
+
 struct nc_client_reply_error {
     NC_RPL type;
     struct nc_err *err;
diff --git a/src/messages_server.c b/src/messages_server.c
index 20e805d..1d7aec4 100644
--- a/src/messages_server.c
+++ b/src/messages_server.c
@@ -19,8 +19,8 @@
 
 #include <libyang/libyang.h>
 
-#include "session_server.h"
 #include "libnetconf.h"
+#include "session_server.h"
 
 extern struct nc_server_opts server_opts;
 
@@ -802,3 +802,39 @@
     free(err->other);
     free(err);
 }
+
+API struct nc_server_notif *
+nc_server_notif_new(struct lyd_node* event, char *eventtime, int eventtime_const)
+{
+    struct nc_server_notif *ntf;
+
+    if (!event || event->schema->nodetype != LYS_NOTIF) {
+        ERRARG("event");
+        return NULL;
+    } else if (!eventtime) {
+        ERRARG("eventtime");
+        return NULL;
+    }
+
+    ntf = malloc(sizeof *ntf);
+    if (eventtime_const) {
+        ntf->eventtime = strdup(eventtime);
+    } else {
+        ntf->eventtime = eventtime;
+    }
+    ntf->tree = event;
+
+    return ntf;
+}
+
+API void
+nc_server_notif_free(struct nc_server_notif *notif)
+{
+    if (!notif) {
+        return;
+    }
+
+    lyd_free(notif->tree);
+    free(notif->eventtime);
+    free(notif);
+}
diff --git a/src/messages_server.h b/src/messages_server.h
index 733fe5c..feb5bdc 100644
--- a/src/messages_server.h
+++ b/src/messages_server.h
@@ -18,6 +18,7 @@
 #include <stdint.h>
 
 #include "netconf.h"
+#include "session.h"
 
 /**
  * @brief Enumeration of NETCONF errors
@@ -62,6 +63,11 @@
 struct nc_server_reply;
 
 /**
+ * @brief NETCONF server Event Notification object
+ */
+struct nc_server_notif;
+
+/**
  * @brief NETCONF server error structure
  */
 struct nc_server_error;
@@ -278,4 +284,41 @@
  */
 void nc_err_free(struct nc_server_error *err);
 
+/**
+ * @brief Create Event Notification object to be sent to the subscribed client(s).
+ *
+ * @param[in] event Notification data tree (valid as LYD_OPT_NOTIF) from libyang. The tree is directly used in created
+ * object, so the caller is supposed to not free the tree on its own, but only via freeng the created object.
+ * @param[in] eventtime YANG dateTime format value of the time when the event was generated by the event source.
+ * Caller can use nc_time2datetime() to create the value from the time_t value. By default, the \p eventtime is handled
+ * the same way as the \p event tree - it is directly used in the created object and caller is supposed to avoid any
+ * further manipulation of the string. This can be changed by the \p eventtime_const parameter, which (set to nonzero)
+ * cause to make copy of the provided \p eventtime instead of using it directly.
+ * @param[in] eventtime_const Flag for changing the \p eventtime handling.
+ * @return Newly created structure of the Event Notification object to be sent to the clients via nc_server_send_notif()
+ * and freed using nc_server_notif_free().
+ */
+struct nc_server_notif *nc_server_notif_new(struct lyd_node* event, char *eventtime, int eventtime_const);
+
+/**
+ * @brief Send NETCONF Event Notification via the session.
+ *
+ * @param[in] session NETCONF session where the Event Notification will be written.
+ * @param[in] notif NETCOFN Notification object to send via specified session. Object can be created by
+ *            nc_notif_new() function.
+ * @param[in] timeout Timeout for writing in milliseconds. Use negative value for infinite
+ *            waiting and 0 for return if data cannot be sent immediately.
+ * @return #NC_MSG_NOTIF on success,
+ *         #NC_MSG_WOULDBLOCK in case of a busy session, and
+ *         #NC_MSG_ERROR on error.
+ */
+NC_MSG_TYPE nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout);
+
+/**
+ * @brief Free a server Event Notification object.
+ *
+ * @param[in] notif Server Event Notification object to free.
+ */
+void nc_server_notif_free(struct nc_server_notif *notif);
+
 #endif /* NC_MESSAGES_SERVER_H_ */
diff --git a/src/session_p.h b/src/session_p.h
index 09936a8..3491089 100644
--- a/src/session_p.h
+++ b/src/session_p.h
@@ -226,7 +226,6 @@
     /* NETCONF data */
     uint32_t id;                 /**< NETCONF session ID (session-id-type) */
     NC_VERSION version;          /**< NETCONF protocol version */
-    volatile pthread_t *ntf_tid; /**< running notifications thread - TODO client-side only for now */
 
     /* Transport implementation */
     NC_TRANSPORT_IMPL ti_type;   /**< transport implementation type to select items from ti union */
@@ -266,6 +265,7 @@
     const char **cpblts;           /**< list of server's capabilities on client side */
     struct nc_msg_cont *replies;   /**< queue for RPC replies received instead of notifications */
     struct nc_msg_cont *notifs;    /**< queue for notifications received instead of RPC reply */
+    volatile pthread_t *ntf_tid;   /**< running notifications receiving thread */
 
     /* server side only data */
     time_t session_start;          /**< time the session was created */
diff --git a/src/session_server.c b/src/session_server.c
index 0de6b1a..4e0de9c 100644
--- a/src/session_server.c
+++ b/src/session_server.c
@@ -851,7 +851,7 @@
  *          NC_PSPOLL_RPC
  */
 static int
-nc_recv_rpc(struct nc_session *session, struct nc_server_rpc **rpc)
+nc_server_recv_rpc(struct nc_session *session, struct nc_server_rpc **rpc)
 {
     struct lyxml_elem *xml = NULL;
     NC_MSG_TYPE msgtype;
@@ -924,6 +924,39 @@
     return NC_PSPOLL_ERROR;
 }
 
+API NC_MSG_TYPE
+nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
+{
+    NC_MSG_TYPE result = NC_MSG_NOTIF;
+    int ret;
+
+    /* check parameters */
+    if (!session) {
+        ERRARG("session");
+        return NC_MSG_ERROR;
+    } else if (!notif || !notif->tree || !notif->eventtime) {
+        ERRARG("notif");
+        return NC_MSG_ERROR;
+    }
+
+    /* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
+    ret = nc_timedlock(session->ti_lock, timeout);
+    if (ret < 0) {
+        return NC_MSG_ERROR;
+    } else if (!ret) {
+        return NC_MSG_WOULDBLOCK;
+    }
+
+    ret = nc_write_msg(session, NC_MSG_NOTIF, notif);
+    if (ret == -1) {
+        ERR("Session %u: failed to write notification.", session->id);
+        result = NC_MSG_ERROR;
+    }
+    pthread_mutex_unlock(session->ti_lock);
+
+    return result;
+}
+
 /* must be called holding the session lock!
  * returns: NC_PSPOLL_ERROR,
  *          NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
@@ -931,7 +964,7 @@
  *          0
  */
 static int
-nc_send_reply(struct nc_session *session, struct nc_server_rpc *rpc)
+nc_server_send_reply(struct nc_session *session, struct nc_server_rpc *rpc)
 {
     nc_rpc_clb clb;
     struct nc_server_reply *reply;
@@ -1155,7 +1188,7 @@
         goto finish;
     }
 
-    ret = nc_recv_rpc(cur_session, &rpc);
+    ret = nc_server_recv_rpc(cur_session, &rpc);
     if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
         pthread_mutex_unlock(cur_session->ti_lock);
         if (cur_session->status != NC_STATUS_RUNNING) {
@@ -1167,7 +1200,7 @@
     cur_session->last_rpc = time(NULL);
 
     /* process RPC */
-    ret |= nc_send_reply(cur_session, rpc);
+    ret |= nc_server_send_reply(cur_session, rpc);
 
     pthread_mutex_unlock(cur_session->ti_lock);
     if (cur_session->status != NC_STATUS_RUNNING) {
diff --git a/tests/test_io.c b/tests/test_io.c
index 6ad68ad..5b23b1c 100644
--- a/tests/test_io.c
+++ b/tests/test_io.c
@@ -27,9 +27,9 @@
 #include <cmocka.h>
 #include <libyang/libyang.h>
 
+#include <messages_p.h>
 #include <session_p.h>
 #include <session_client.h>
-#include <messages_p.h>
 #include "config.h"
 
 struct wr {