blob: 24296e4fc5efbac109bcfb49fb1ec9703de013b0 [file] [log] [blame]
Radek Krejci206fcd62015-10-07 15:42:48 +02001/**
2 * \file io.c
3 * \author Radek Krejci <rkrejci@cesnet.cz>
4 * \brief libnetconf2 - input/output functions
5 *
6 * Copyright (c) 2015 CESNET, z.s.p.o.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of the Company nor the names of its contributors
18 * may be used to endorse or promote products derived from this
19 * software without specific prior written permission.
20 *
21 */
22
Radek Krejcife0b3472015-10-12 13:43:42 +020023#define _GNU_SOURCE /* asprintf */
Radek Krejci206fcd62015-10-07 15:42:48 +020024#include <assert.h>
25#include <errno.h>
26#include <poll.h>
Radek Krejcife0b3472015-10-12 13:43:42 +020027#include <stdarg.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020028#include <stdlib.h>
29#include <string.h>
30#include <unistd.h>
31
32#include <libyang/libyang.h>
33
34#include "config.h"
35#include "libnetconf.h"
36#include "session_p.h"
Radek Krejcife0b3472015-10-12 13:43:42 +020037#include "messages_p.h"
Radek Krejci206fcd62015-10-07 15:42:48 +020038
Radek Krejcife0b3472015-10-12 13:43:42 +020039#define BUFFERSIZE 512
Radek Krejci206fcd62015-10-07 15:42:48 +020040
41static ssize_t
42nc_read(struct nc_session *session, char *buf, size_t count)
43{
44 size_t size = 0;
45 ssize_t r;
46
47 assert(session);
48 assert(buf);
49
50 if (!count) {
51 return 0;
52 }
53
Michal Vasko38a7c6c2015-12-04 12:29:20 +010054 switch (session->ti_type) {
55 case NC_TI_NONE:
56 return 0;
57
Radek Krejci206fcd62015-10-07 15:42:48 +020058 case NC_TI_FD:
59 /* read via standard file descriptor */
Radek Krejci206fcd62015-10-07 15:42:48 +020060 while(count) {
61 r = read(session->ti.fd.in, &(buf[size]), count);
62 if (r < 0) {
63 if (errno == EAGAIN) {
64 usleep(NC_READ_SLEEP);
65 continue;
66 } else {
67 ERR("Reading from file descriptor (%d) failed (%s).", session->ti.fd.in, strerror(errno));
68 return -1;
69 }
70 } else if (r == 0) {
71 ERR("Communication file descriptor (%d) unexpectedly closed.", session->ti.fd.in);
72 return -1;
73 }
74
75 size = size + r;
76 count = count - r;
77 }
78 break;
79
Michal Vaskofb2fb762015-10-27 11:44:32 +010080#ifdef ENABLE_SSH
Radek Krejci206fcd62015-10-07 15:42:48 +020081 case NC_TI_LIBSSH:
82 /* read via libssh */
83 while(count) {
84 r = ssh_channel_read(session->ti.libssh.channel, &(buf[size]), count, 0);
85 if (r == SSH_AGAIN) {
86 usleep (NC_READ_SLEEP);
87 continue;
88 } else if (r == SSH_ERROR) {
89 ERR("Reading from the SSH channel failed (%zd: %s)",
90 ssh_get_error_code(session->ti.libssh.session), ssh_get_error(session->ti.libssh.session));
91 return -1;
92 } else if (r == 0) {
93 if (ssh_channel_is_eof(session->ti.libssh.channel)) {
94 ERR("Communication socket unexpectedly closed (libssh).");
95 return -1;
96 }
97 usleep (NC_READ_SLEEP);
98 continue;
99 }
100
101 size = size + r;
102 count = count - r;
103 }
104 break;
105#endif
106
107#ifdef ENABLE_TLS
108 case NC_TI_OPENSSL:
109 /* read via OpenSSL */
Radek Krejcid0046592015-10-08 12:52:02 +0200110 while(count) {
111 r = SSL_read(session->ti.tls, &(buf[size]), count);
112 if (r <= 0) {
113 int x;
114 switch (x = SSL_get_error(session->ti.tls, r)) {
115 case SSL_ERROR_WANT_READ:
116 usleep(NC_READ_SLEEP);
117 continue;
118 case SSL_ERROR_ZERO_RETURN:
119 ERR("Communication socket unexpectedly closed (OpenSSL).");
120 return -1;
121 default:
122 ERR("Reading from the TLS session failed (SSL code %d)", x);
123 return -1;
124 }
Radek Krejci206fcd62015-10-07 15:42:48 +0200125 }
Radek Krejcid0046592015-10-08 12:52:02 +0200126 size = size + r;
127 count = count - r;
Radek Krejci206fcd62015-10-07 15:42:48 +0200128 }
129 break;
130#endif
131 }
132
133 return (ssize_t)size;
134}
135
136static ssize_t
137nc_read_chunk(struct nc_session *session, size_t len, char **chunk)
138{
139 ssize_t r;
140
141 assert(session);
142 assert(chunk);
143
144 if (!len) {
145 return 0;
146 }
147
148 *chunk = malloc ((len + 1) * sizeof **chunk);
149 if (!*chunk) {
150 ERRMEM;
151 return -1;
152 }
153
154 r = nc_read(session, *chunk, len);
155 if (r <= 0) {
156 free(*chunk);
157 return -1;
158 }
159
160 /* terminating null byte */
Radek Krejcife0b3472015-10-12 13:43:42 +0200161 (*chunk)[r] = 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200162
163 return r;
164}
165
166static ssize_t
167nc_read_until(struct nc_session *session, const char *endtag, size_t limit, char **result)
168{
169 char *chunk = NULL;
170 size_t size, count = 0, r, len;
171
172 assert(session);
173 assert(endtag);
174
175 if (limit && limit < BUFFERSIZE) {
176 size = limit;
177 } else {
178 size = BUFFERSIZE;
179 }
180 chunk = malloc ((size + 1) * sizeof *chunk);
Radek Krejcib791b532015-10-08 15:29:34 +0200181 if (!chunk) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200182 ERRMEM;
183 return -1;
184 }
185
186 len = strlen(endtag);
187 while(1) {
188 if (limit && count == limit) {
189 free(chunk);
190 WRN("%s: reading limit (%d) reached.", __func__, limit);
191 ERR("Invalid input data (missing \"%s\" sequence).", endtag);
192 return -1;
193 }
194
195 /* resize buffer if needed */
196 if (count == size) {
197 /* get more memory */
198 size = size + BUFFERSIZE;
199 char *tmp = realloc (chunk, (size + 1) * sizeof *tmp);
200 if (!tmp) {
201 ERRMEM;
202 free(chunk);
203 return -1;
204 }
205 chunk = tmp;
206 }
207
208 /* get another character */
209 r = nc_read(session, &(chunk[count]), 1);
210 if (r != 1) {
211 free(chunk);
212 return -1;
213 }
214
215 count++;
216
217 /* check endtag */
218 if (count >= len) {
219 if (!strncmp(endtag, &(chunk[count - len]), len)) {
220 /* endtag found */
221 break;
222 }
223 }
224 }
225
226 /* terminating null byte */
227 chunk[count] = 0;
228
229 if (result) {
230 *result = chunk;
Radek Krejcife0b3472015-10-12 13:43:42 +0200231 } else {
232 free(chunk);
Radek Krejci206fcd62015-10-07 15:42:48 +0200233 }
234 return count;
235}
236
237NC_MSG_TYPE
Michal Vasko05ba9df2016-01-13 14:40:27 +0100238nc_read_msg(struct nc_session *session, struct lyxml_elem **data)
239{
240 int ret;
241 char *msg = NULL, *chunk, *aux;
242 uint64_t chunk_len, len = 0;
243
244 /* read the message */
245 switch (session->version) {
246 case NC_VERSION_10:
247 ret = nc_read_until(session, NC_VERSION_10_ENDTAG, 0, &msg);
248 if (ret == -1) {
249 goto error;
250 }
251
252 /* cut off the end tag */
253 msg[ret - NC_VERSION_10_ENDTAG_LEN] = '\0';
254 break;
255 case NC_VERSION_11:
256 while (1) {
257 ret = nc_read_until(session, "\n#", 0, NULL);
258 if (ret == -1) {
259 goto error;
260 }
261 ret = nc_read_until(session, "\n", 0, &chunk);
262 if (ret == -1) {
263 goto error;
264 }
265
266 if (!strcmp(chunk, "#\n")) {
267 /* end of chunked framing message */
268 free(chunk);
269 break;
270 }
271
272 /* convert string to the size of the following chunk */
273 chunk_len = strtoul(chunk, (char **)NULL, 10);
274 free(chunk);
275 if (!chunk_len) {
276 ERR("Invalid frame chunk size detected, fatal error.");
277 goto error;
278 }
279
280 /* now we have size of next chunk, so read the chunk */
281 ret = nc_read_chunk(session, chunk_len, &chunk);
282 if (ret == -1) {
283 goto error;
284 }
285
286 /* realloc message buffer, remember to count terminating null byte */
287 aux = realloc(msg, len + chunk_len + 1);
288 if (!aux) {
289 ERRMEM;
290 goto error;
291 }
292 msg = aux;
293 memcpy(msg + len, chunk, chunk_len);
294 len += chunk_len;
295 msg[len] = '\0';
296 free(chunk);
297 }
298
299 break;
300 }
301 DBG("Received message (session %u):\n%s", session->id, msg);
302
303 /* build XML tree */
304 *data = lyxml_read_data(session->ctx, msg, 0);
305 if (!*data) {
306 goto error;
307 } else if (!(*data)->ns) {
308 ERR("Invalid message root element (invalid namespace).");
309 goto error;
310 }
311 free(msg);
312 msg = NULL;
313
314 /* get and return message type */
315 if (!strcmp((*data)->ns->value, NC_NS_BASE)) {
316 if (!strcmp((*data)->name, "rpc")) {
317 return NC_MSG_RPC;
318 } else if (!strcmp((*data)->name, "rpc-reply")) {
319 return NC_MSG_REPLY;
320 } else if (!strcmp((*data)->name, "hello")) {
321 return NC_MSG_HELLO;
322 } else {
323 ERR("Invalid message root element (invalid name \"%s\").", (*data)->name);
324 goto error;
325 }
326 } else if (!strcmp((*data)->ns->value, NC_NS_NOTIF)) {
327 if (!strcmp((*data)->name, "notification")) {
328 return NC_MSG_NOTIF;
329 } else {
330 ERR("Invalid message root element (invalid name \"%s\").", (*data)->name);
331 goto error;
332 }
333 } else {
334 ERR("Invalid message root element (invalid namespace \"%s\").", (*data)->ns->value);
335 goto error;
336 }
337
338error:
339 /* cleanup */
340 free(msg);
341 free(*data);
342 *data = NULL;
343
344 if (session->side == NC_SERVER && session->version == NC_VERSION_11) {
345 /* NETCONF version 1.1 define sending error reply from the server */
346 /* TODO
347 reply = nc_reply_error(nc_err_new(NC_ERR_MALFORMED_MSG));
348 if (reply == NULL) {
349 ERROR("Unable to create the \'Malformed message\' reply");
350 nc_session_close(session, NC_SESSION_TERM_OTHER);
351 return (NC_MSG_UNKNOWN);
352 }
353
354 if (nc_session_send_reply(session, NULL, reply) == 0) {
355 ERROR("Unable to send the \'Malformed message\' reply");
356 nc_session_close(session, NC_SESSION_TERM_OTHER);
357 return (NC_MSG_UNKNOWN);
358 }
359 nc_reply_free(reply);
360 */
361 }
362
363 ERR("Malformed message received.");
364 /* TODO - destroy the session */
365
366 return NC_MSG_ERROR;
367}
368
369NC_MSG_TYPE
370nc_read_msg_poll(struct nc_session *session, int timeout, struct lyxml_elem **data)
Radek Krejci206fcd62015-10-07 15:42:48 +0200371{
372 int status;
373 int revents;
374 struct pollfd fds;
375 const char *emsg = NULL;
Radek Krejci206fcd62015-10-07 15:42:48 +0200376
377 assert(data);
378 *data = NULL;
379
380 /* fill fds structure for poll */
381 if (session->ti_type == NC_TI_FD) {
382 fds.fd = session->ti.fd.in;
383#ifdef ENABLE_TLS
384 } else if (session->ti_type == NC_TI_OPENSSL) {
Radek Krejcid0046592015-10-08 12:52:02 +0200385 fds.fd = SSL_get_fd(session->ti.tls);
Radek Krejci206fcd62015-10-07 15:42:48 +0200386#endif
387 }
388
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100389 while (1) {
Radek Krejci206fcd62015-10-07 15:42:48 +0200390 /* poll loop */
391
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100392 switch (session->ti_type) {
393 case NC_TI_NONE:
394 return NC_MSG_ERROR;
395
Michal Vaskofb2fb762015-10-27 11:44:32 +0100396#ifdef ENABLE_SSH
Radek Krejci206fcd62015-10-07 15:42:48 +0200397 case NC_TI_LIBSSH:
398 /* we are getting data from libssh's channel */
399 status = ssh_channel_poll_timeout(session->ti.libssh.channel, timeout, 0);
400 if (status > 0) {
401 revents = POLLIN;
Radek Krejci206fcd62015-10-07 15:42:48 +0200402 } else if (status == SSH_EOF) {
403 emsg = "SSH channel closed";
404 } else {
405 if (!session->ti.libssh.channel) {
406 emsg = strerror(errno);
407 } else if (session->ti.libssh.session) {
408 emsg = ssh_get_error(session->ti.libssh.session);
409 } else {
410 emsg = "description not available";
411 }
412 }
413 break;
414#endif
415
416#ifdef ENABLE_TLS
417 case NC_TI_OPENSSL:
418 /* no break - same processing as in case of standard file descriptors */
419#endif
420 case NC_TI_FD:
421 fds.events = POLLIN;
422 fds.revents = 0;
423 status = poll(&fds, 1, timeout);
424 revents = (unsigned long int) fds.revents;
425 break;
426 }
427
428 /* process the poll result */
429 if (status == 0) {
430 /* timed out */
431 return NC_MSG_WOULDBLOCK;
432 } else if ((status == -1) && (errno == EINTR)) {
433 /* poll was interrupted */
434 continue;
435 } else if (status < 0) {
436 /* poll failed - something really bad happened, close the session */
437 ERR("Input channel error (%s).", emsg ? emsg : strerror(errno));
438
439 /* TODO - destroy the session */
440
441 return NC_MSG_ERROR;
442 } else { /* status > 0 */
443 /* in case of standard (non-libssh) poll, there still can be an error */
444 if ((revents & POLLHUP) || (revents & POLLERR)) {
445 /* close client's socket (it's probably already closed by peer */
446 ERR("Input channel closed.");
447
448 /* TODO - destroy the session */
449
450 return NC_MSG_ERROR;
451 }
452
453 /* we have something to read, so get out of the loop */
454 break;
455 }
456 }
457
Michal Vasko05ba9df2016-01-13 14:40:27 +0100458 return nc_read_msg(session, data);
Radek Krejci206fcd62015-10-07 15:42:48 +0200459}
Radek Krejcife0b3472015-10-12 13:43:42 +0200460
461#define WRITE_BUFSIZE (2 * BUFFERSIZE)
462struct wclb_arg {
463 struct nc_session *session;
464 char buf[WRITE_BUFSIZE];
465 size_t len;
466};
467
468static ssize_t
Michal Vasko086311b2016-01-08 09:53:11 +0100469write_text_(struct nc_session *session, const void *buf, size_t count)
Radek Krejcife0b3472015-10-12 13:43:42 +0200470{
Radek Krejcife0b3472015-10-12 13:43:42 +0200471 switch (session->ti_type) {
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100472 case NC_TI_NONE:
473 return -1;
474
Radek Krejcife0b3472015-10-12 13:43:42 +0200475 case NC_TI_FD:
Michal Vasko086311b2016-01-08 09:53:11 +0100476 return write(session->ti.fd.out, buf, count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200477
Michal Vaskofb2fb762015-10-27 11:44:32 +0100478#ifdef ENABLE_SSH
Radek Krejcife0b3472015-10-12 13:43:42 +0200479 case NC_TI_LIBSSH:
Michal Vasko086311b2016-01-08 09:53:11 +0100480 return ssh_channel_write(session->ti.libssh.channel, buf, count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200481#endif
Radek Krejcife0b3472015-10-12 13:43:42 +0200482#ifdef ENABLE_TLS
483 case NC_TI_OPENSSL:
Michal Vasko086311b2016-01-08 09:53:11 +0100484 return SSL_write(session->ti.tls, buf, count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200485#endif
486 }
487
488 return -1;
489}
490
Michal Vasko086311b2016-01-08 09:53:11 +0100491static ssize_t
492write_starttag_and_msg(struct nc_session *session, const void *buf, size_t count)
493{
494 int c = 0;
495 char chunksize[20];
496
497 if (session->version == NC_VERSION_11) {
498 sprintf(chunksize, "\n#%zu\n", count);
499 c = write_text_(session, chunksize, strlen(chunksize));
500 }
501 return write_text_(session, buf, count) + c;
502}
503
Radek Krejcife0b3472015-10-12 13:43:42 +0200504static int
505write_endtag(struct nc_session *session)
506{
507 switch(session->ti_type) {
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100508 case NC_TI_NONE:
509 return 0;
510
Radek Krejcife0b3472015-10-12 13:43:42 +0200511 case NC_TI_FD:
512 if (session->version == NC_VERSION_11) {
513 write(session->ti.fd.out, "\n##\n", 4);
514 } else {
515 write(session->ti.fd.out, "]]>]]>", 6);
516 }
517 break;
518
Michal Vaskofb2fb762015-10-27 11:44:32 +0100519#ifdef ENABLE_SSH
Radek Krejcife0b3472015-10-12 13:43:42 +0200520 case NC_TI_LIBSSH:
521 if (session->version == NC_VERSION_11) {
522 ssh_channel_write(session->ti.libssh.channel, "\n##\n", 4);
523 } else {
524 ssh_channel_write(session->ti.libssh.channel, "]]>]]>", 6);
525 }
526 break;
527#endif
528
529#ifdef ENABLE_TLS
530 case NC_TI_OPENSSL:
531 if (session->version == NC_VERSION_11) {
532 SSL_write(session->ti.tls, "\n##\n", 4);
533 } else {
534 SSL_write(session->ti.tls, "]]>]]>", 6);
535 }
536 break;
537#endif
538 }
539
540 return 0;
541}
542
543static void
544write_clb_flush(struct wclb_arg *warg)
545{
546 /* flush current buffer */
547 if (warg->len) {
Michal Vasko086311b2016-01-08 09:53:11 +0100548 write_starttag_and_msg(warg->session, warg->buf, warg->len);
Radek Krejcife0b3472015-10-12 13:43:42 +0200549 warg->len = 0;
550 }
551}
552
553static ssize_t
554write_clb(void *arg, const void *buf, size_t count)
555{
556 struct wclb_arg *warg = (struct wclb_arg *)arg;
557
558 if (!buf) {
559 write_clb_flush(warg);
560
561 /* endtag */
562 write_endtag(warg->session);
563 return 0;
564 }
565
566 if (warg->len && (warg->len + count > WRITE_BUFSIZE)) {
567 /* dump current buffer */
568 write_clb_flush(warg);
569 }
570 if (count > WRITE_BUFSIZE) {
571 /* write directly */
Michal Vasko086311b2016-01-08 09:53:11 +0100572 write_starttag_and_msg(warg->session, buf, count);
Radek Krejcife0b3472015-10-12 13:43:42 +0200573 } else {
574 /* keep in buffer and write later */
575 memcpy(&warg->buf[warg->len], buf, count);
576 warg->len += count; /* is <= WRITE_BUFSIZE */
577 }
578
579 return (ssize_t)count;
580}
581
Michal Vasko05ba9df2016-01-13 14:40:27 +0100582static void
583write_error(struct wclb_arg *arg, struct nc_server_error *err)
584{
585 uint16_t i;
586 char str_sid[11];
587
588 write_clb((void *)arg, "<rpc-error>", 11);
589
590 write_clb((void *)arg, "<error-type>", 12);
591 switch (err->type) {
592 case NC_ERR_TYPE_TRAN:
593 write_clb((void *)arg, "transport", 9);
594 break;
595 case NC_ERR_TYPE_RPC:
596 write_clb((void *)arg, "rpc", 3);
597 break;
598 case NC_ERR_TYPE_PROT:
599 write_clb((void *)arg, "protocol", 8);
600 break;
601 case NC_ERR_TYPE_APP:
602 write_clb((void *)arg, "application", 11);
603 break;
604 default:
605 ERRINT;
606 return;
607 }
608 write_clb((void *)arg, "</error-type>", 13);
609
610 write_clb((void *)arg, "<error-tag>", 11);
611 switch (err->tag) {
612 case NC_ERR_IN_USE:
613 write_clb((void *)arg, "in-use", 6);
614 break;
615 case NC_ERR_INVALID_VALUE:
616 write_clb((void *)arg, "invalid-value", 13);
617 break;
618 case NC_ERR_TOO_BIG:
619 write_clb((void *)arg, "too-big", 7);
620 break;
621 case NC_ERR_MISSING_ATTR:
622 write_clb((void *)arg, "missing-attribute", 17);
623 break;
624 case NC_ERR_BAD_ATTR:
625 write_clb((void *)arg, "bad-attribute", 13);
626 break;
627 case NC_ERR_UNKNOWN_ATTR:
628 write_clb((void *)arg, "unknown-attribute", 17);
629 break;
630 case NC_ERR_MISSING_ELEM:
631 write_clb((void *)arg, "missing-element", 15);
632 break;
633 case NC_ERR_BAD_ELEM:
634 write_clb((void *)arg, "bad-element", 11);
635 break;
636 case NC_ERR_UNKNOWN_ELEM:
637 write_clb((void *)arg, "unknown-element", 15);
638 break;
639 case NC_ERR_UNKNOWN_NS:
640 write_clb((void *)arg, "unknown-namespace", 17);
641 break;
642 case NC_ERR_ACCESS_DENIED:
643 write_clb((void *)arg, "access-denied", 13);
644 break;
645 case NC_ERR_LOCK_DENIED:
646 write_clb((void *)arg, "lock-denied", 11);
647 break;
648 case NC_ERR_RES_DENIED:
649 write_clb((void *)arg, "resource-denied", 15);
650 break;
651 case NC_ERR_ROLLBACK_FAILED:
652 write_clb((void *)arg, "rollback-failed", 15);
653 break;
654 case NC_ERR_DATA_EXISTS:
655 write_clb((void *)arg, "data-exists", 11);
656 break;
657 case NC_ERR_DATA_MISSING:
658 write_clb((void *)arg, "data-missing", 12);
659 break;
660 case NC_ERR_OP_NOT_SUPPORTED:
661 write_clb((void *)arg, "operation-not-supported", 23);
662 break;
663 case NC_ERR_OP_FAILED:
664 write_clb((void *)arg, "operation-failed", 16);
665 break;
666 case NC_ERR_MALFORMED_MSG:
667 write_clb((void *)arg, "malformed-message", 17);
668 break;
669 default:
670 ERRINT;
671 return;
672 }
673 write_clb((void *)arg, "</error-tag>", 12);
674
675 write_clb((void *)arg, "<error-severity>error</error-severity>", 38);
676
677 if (err->apptag) {
678 write_clb((void *)arg, "<error-app-tag>", 15);
679 write_clb((void *)arg, err->apptag, strlen(err->apptag));
680 write_clb((void *)arg, "</error-app-tag>", 16);
681 }
682
683 if (err->path) {
684 write_clb((void *)arg, "<error-path>", 12);
685 write_clb((void *)arg, err->path, strlen(err->path));
686 write_clb((void *)arg, "</error-path>", 13);
687 }
688
689 if (err->message) {
690 write_clb((void *)arg, "<error-message", 14);
691 if (err->message_lang) {
692 write_clb((void *)arg, " xml:lang=\"", 11);
693 write_clb((void *)arg, err->message_lang, strlen(err->message_lang));
694 write_clb((void *)arg, "\"", 1);
695 }
696 write_clb((void *)arg, ">", 1);
697 write_clb((void *)arg, err->message, strlen(err->message));
698 write_clb((void *)arg, "</error-message>", 16);
699 }
700
701 if (err->sid || err->attr || err->elem || err->ns || err->other) {
702 write_clb((void *)arg, "<error-info>", 12);
703
704 if (err->sid) {
705 write_clb((void *)arg, "<session-id>", 12);
706 sprintf(str_sid, "%u", err->sid);
707 write_clb((void *)arg, str_sid, strlen(str_sid));
708 write_clb((void *)arg, "</session-id>", 13);
709 }
710
711 for (i = 0; i < err->attr_count; ++i) {
712 write_clb((void *)arg, "<bad-attribute>", 15);
713 write_clb((void *)arg, err->attr[i], strlen(err->attr[i]));
714 write_clb((void *)arg, "</bad-attribute>", 16);
715 }
716
717 for (i = 0; i < err->elem_count; ++i) {
718 write_clb((void *)arg, "<bad-element>", 13);
719 write_clb((void *)arg, err->elem[i], strlen(err->elem[i]));
720 write_clb((void *)arg, "</bad-element>", 14);
721 }
722
723 for (i = 0; i < err->ns_count; ++i) {
724 write_clb((void *)arg, "<bad-namespace>", 15);
725 write_clb((void *)arg, err->ns[i], strlen(err->ns[i]));
726 write_clb((void *)arg, "</bad-namespace>", 16);
727 }
728
729 for (i = 0; i < err->other_count; ++i) {
730 lyxml_dump_clb(write_clb, (void *)arg, err->other[i], 0);
731 }
732
733 write_clb((void *)arg, "</error-info>", 13);
734 }
735
736 write_clb((void *)arg, "</rpc-error>", 12);
737}
738
Radek Krejcid116db42016-01-08 15:36:30 +0100739int
740nc_write_msg(struct nc_session *session, NC_MSG_TYPE type, ...)
Radek Krejcife0b3472015-10-12 13:43:42 +0200741{
Radek Krejcid116db42016-01-08 15:36:30 +0100742 va_list ap;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100743 int count;
Radek Krejcife0b3472015-10-12 13:43:42 +0200744 const char *attrs;
745 struct lyd_node *content;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100746 struct lyxml_elem *rpc_elem;
747 struct nc_server_reply *reply;
748 struct nc_server_reply_error *error_rpl;
Radek Krejcid116db42016-01-08 15:36:30 +0100749 char *buf = NULL;
750 struct wclb_arg arg;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200751 const char **capabilities;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100752 uint32_t *sid = NULL, i;
Radek Krejcife0b3472015-10-12 13:43:42 +0200753
Radek Krejcid116db42016-01-08 15:36:30 +0100754 va_start(ap, type);
Radek Krejcife0b3472015-10-12 13:43:42 +0200755
756 arg.session = session;
757 arg.len = 0;
758
759 switch (type) {
760 case NC_MSG_RPC:
761 content = va_arg(ap, struct lyd_node *);
762 attrs = va_arg(ap, const char *);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100763
Radek Krejcife0b3472015-10-12 13:43:42 +0200764 count = asprintf(&buf, "<rpc xmlns=\"%s\" message-id=\"%"PRIu64"\"%s>",
765 NC_NS_BASE, session->msgid + 1, attrs ? attrs : "");
766 write_clb((void *)&arg, buf, count);
767 free(buf);
768 lyd_print_clb(write_clb, (void *)&arg, content, LYD_XML);
769 write_clb((void *)&arg, "</rpc>", 6);
770
771 session->msgid++;
772 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100773
Radek Krejcife0b3472015-10-12 13:43:42 +0200774 case NC_MSG_REPLY:
Michal Vasko05ba9df2016-01-13 14:40:27 +0100775 rpc_elem = va_arg(ap, struct lyxml_elem *);
776 reply = va_arg(ap, struct nc_server_reply *);
777
Radek Krejcife0b3472015-10-12 13:43:42 +0200778 write_clb((void *)&arg, "<rpc-reply", 10);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100779 lyxml_dump_clb(write_clb, (void *)&arg, rpc_elem, LYXML_DUMP_ATTRS);
Radek Krejcife0b3472015-10-12 13:43:42 +0200780 write_clb((void *)&arg, ">", 1);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100781 switch (reply->type) {
782 case NC_RPL_OK:
783 write_clb((void *)&arg, "<ok/>", 5);
784 break;
785 case NC_RPL_DATA:
786 write_clb((void *)&arg, "<data>", 6);
787 lyd_print_clb(write_clb, (void *)&arg, ((struct nc_reply_data *)reply)->data, LYD_XML);
788 write_clb((void *)&arg, "<data/>", 7);
789 break;
790 case NC_RPL_ERROR:
791 error_rpl = (struct nc_server_reply_error *)reply;
792 for (i = 0; i < error_rpl->count; ++i) {
793 write_error(&arg, error_rpl->err[i]);
794 }
795 break;
796 default:
797 ERRINT;
798 write_clb((void *)&arg, NULL, 0);
799 va_end(ap);
800 return -1;
801 }
Radek Krejcife0b3472015-10-12 13:43:42 +0200802 write_clb((void *)&arg, "</rpc-reply>", 12);
803 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100804
Radek Krejcife0b3472015-10-12 13:43:42 +0200805 case NC_MSG_NOTIF:
806 write_clb((void *)&arg, "<notification xmlns=\""NC_NS_NOTIF"\"/>", 21 + 47 + 3);
807 /* TODO content */
808 write_clb((void *)&arg, "</notification>", 12);
809 break;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100810
Radek Krejcid116db42016-01-08 15:36:30 +0100811 case NC_MSG_HELLO:
812 if (session->version != NC_VERSION_10) {
813 va_end(ap);
814 return -1;
815 }
816 capabilities = va_arg(ap, const char **);
817 sid = va_arg(ap, uint32_t*);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100818
Radek Krejcid116db42016-01-08 15:36:30 +0100819 count = asprintf(&buf, "<hello xmlns=\"%s\"><capabilities>", NC_NS_BASE);
820 write_clb((void *)&arg, buf, count);
821 free(buf);
822 for (i = 0; capabilities[i]; i++) {
823 count = asprintf(&buf, "<capability>%s</capability>", capabilities[i]);
824 write_clb((void *)&arg, buf, count);
825 free(buf);
826 }
827 if (sid) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100828 count = asprintf(&buf, "</capabilities><session-id>%u</session-id></hello>", *sid);
Radek Krejcid116db42016-01-08 15:36:30 +0100829 write_clb((void *)&arg, buf, count);
830 free(buf);
831 } else {
832 write_clb((void *)&arg, "</capabilities></hello>", 23);
833 }
Radek Krejcid116db42016-01-08 15:36:30 +0100834 break;
Michal Vaskoed462342016-01-12 12:33:48 +0100835
Radek Krejcife0b3472015-10-12 13:43:42 +0200836 default:
Radek Krejcid116db42016-01-08 15:36:30 +0100837 va_end(ap);
Radek Krejcife0b3472015-10-12 13:43:42 +0200838 return -1;
839 }
840
841 /* flush message */
842 write_clb((void *)&arg, NULL, 0);
Radek Krejcife0b3472015-10-12 13:43:42 +0200843
844 va_end(ap);
Radek Krejcid116db42016-01-08 15:36:30 +0100845 return 0;
Radek Krejcife0b3472015-10-12 13:43:42 +0200846}