blob: 744f59adc033bf03e487aa9107525fe640e51a10 [file] [log] [blame]
Michal Vasko7bcb48e2016-01-15 10:28:54 +01001/**
2 * \file messages_server.c
3 * \author Michal Vasko <mvasko@cesnet.cz>
4 * \brief libnetconf2 - server NETCONF messages functions
5 *
6 * Copyright (c) 2015 CESNET, z.s.p.o.
7 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01008 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010011 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010012 * https://opensource.org/licenses/BSD-3-Clause
Michal Vasko7bcb48e2016-01-15 10:28:54 +010013 */
14
15#include <ctype.h>
16#include <stdlib.h>
17#include <string.h>
18#include <stdarg.h>
19
20#include <libyang/libyang.h>
21
Michal Vasko11d142a2016-01-19 15:58:24 +010022#include "session_server.h"
Michal Vasko7bcb48e2016-01-15 10:28:54 +010023#include "libnetconf.h"
Michal Vasko1a38c862016-01-15 15:50:07 +010024
25extern struct nc_server_opts server_opts;
Michal Vasko7bcb48e2016-01-15 10:28:54 +010026
27API struct nc_server_reply *
28nc_server_reply_ok(void)
29{
30 struct nc_server_reply *ret;
31
32 ret = malloc(sizeof *ret);
33 if (!ret) {
34 ERRMEM;
35 return NULL;
36 }
37
38 ret->type = NC_RPL_OK;
39 return ret;
40}
41
42API struct nc_server_reply *
43nc_server_reply_data(struct lyd_node *data, NC_PARAMTYPE paramtype)
44{
45 struct nc_server_reply_data *ret;
46
47 if (!data) {
48 ERRARG;
49 return NULL;
50 }
51
52 ret = malloc(sizeof *ret);
53 if (!ret) {
54 ERRMEM;
55 return NULL;
56 }
57
58 ret->type = NC_RPL_DATA;
59 if (paramtype == NC_PARAMTYPE_DUP_AND_FREE) {
60 ret->data = lyd_dup(data, 1);
61 } else {
62 ret->data = data;
63 }
64 if (paramtype != NC_PARAMTYPE_CONST) {
65 ret->free = 1;
66 } else {
67 ret->free = 0;
68 }
69 return (struct nc_server_reply *)ret;
70}
71
72API struct nc_server_reply *
Michal Vasko1a38c862016-01-15 15:50:07 +010073nc_server_reply_err(struct nc_server_error *err)
Michal Vasko7bcb48e2016-01-15 10:28:54 +010074{
75 struct nc_server_reply_error *ret;
76
Michal Vasko1a38c862016-01-15 15:50:07 +010077 if (!err) {
Michal Vasko7bcb48e2016-01-15 10:28:54 +010078 ERRARG;
79 return NULL;
80 }
81
82 ret = malloc(sizeof *ret);
83 if (!ret) {
84 ERRMEM;
85 return NULL;
86 }
87
88 ret->type = NC_RPL_ERROR;
Michal Vasko7bcb48e2016-01-15 10:28:54 +010089 ret->err = malloc(sizeof *ret->err);
90 ret->err[0] = err;
91 ret->count = 1;
92 return (struct nc_server_reply *)ret;
93}
94
95API int
96nc_server_reply_add_err(struct nc_server_reply *reply, struct nc_server_error *err)
97{
98 struct nc_server_reply_error *err_rpl;
99
100 if (!reply || (reply->type != NC_RPL_ERROR) || !err) {
101 ERRARG;
102 return -1;
103 }
104
105 err_rpl = (struct nc_server_reply_error *)reply;
106 ++err_rpl->count;
107 err_rpl->err = realloc(err_rpl->err, err_rpl->count * sizeof *err_rpl->err);
108 err_rpl->err[err_rpl->count - 1] = err;
109 return 0;
110}
111
112API struct nc_server_error *
Michal Vasko1a38c862016-01-15 15:50:07 +0100113nc_err(NC_ERR tag, ...)
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100114{
115 va_list ap;
116 struct nc_server_error *ret;
117 NC_ERR_TYPE type;
118 const char *arg1, *arg2;
119 uint32_t sid;
120
Michal Vasko1a38c862016-01-15 15:50:07 +0100121 if (!tag) {
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100122 ERRARG;
123 return NULL;
124 }
125
126 ret = calloc(1, sizeof *ret);
127 if (!ret) {
128 ERRMEM;
129 return NULL;
130 }
131
132 va_start(ap, tag);
133
134 switch (tag) {
135 case NC_ERR_IN_USE:
136 case NC_ERR_INVALID_VALUE:
137 case NC_ERR_ACCESS_DENIED:
138 case NC_ERR_ROLLBACK_FAILED:
139 case NC_ERR_OP_NOT_SUPPORTED:
140 type = va_arg(ap, NC_ERR_TYPE);
141 if ((type != NC_ERR_TYPE_PROT) && (type == NC_ERR_TYPE_APP)) {
142 goto fail;
143 }
144 break;
145
146 case NC_ERR_TOO_BIG:
147 case NC_ERR_RES_DENIED:
148 type = va_arg(ap, NC_ERR_TYPE);
149 /* nothing to check */
150 break;
151
152 case NC_ERR_MISSING_ATTR:
153 case NC_ERR_BAD_ATTR:
154 case NC_ERR_UNKNOWN_ATTR:
155 type = va_arg(ap, NC_ERR_TYPE);
156 arg1 = va_arg(ap, const char *);
157 arg2 = va_arg(ap, const char *);
158
159 if (type == NC_ERR_TYPE_TRAN) {
160 goto fail;
161 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100162 nc_err_add_bad_attr(ret, arg1);
163 nc_err_add_bad_elem(ret, arg2);
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100164 break;
165
166 case NC_ERR_MISSING_ELEM:
167 case NC_ERR_BAD_ELEM:
168 case NC_ERR_UNKNOWN_ELEM:
169 type = va_arg(ap, NC_ERR_TYPE);
170 arg1 = va_arg(ap, const char *);
171
172 if ((type != NC_ERR_TYPE_PROT) && (type != NC_ERR_TYPE_APP)) {
173 goto fail;
174 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100175 nc_err_add_bad_elem(ret, arg1);
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100176 break;
177
178 case NC_ERR_UNKNOWN_NS:
179 type = va_arg(ap, NC_ERR_TYPE);
180 arg1 = va_arg(ap, const char *);
181 arg2 = va_arg(ap, const char *);
182
183 if ((type != NC_ERR_TYPE_PROT) && (type != NC_ERR_TYPE_APP)) {
184 goto fail;
185 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100186 nc_err_add_bad_elem(ret, arg1);
187 nc_err_add_bad_ns(ret, arg2);
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100188 break;
189
190 case NC_ERR_LOCK_DENIED:
191 sid = va_arg(ap, uint32_t);
192
193 type = NC_ERR_TYPE_PROT;
194 nc_err_set_sid(ret, sid);
195 break;
196
197 case NC_ERR_DATA_EXISTS:
198 case NC_ERR_DATA_MISSING:
199 type = NC_ERR_TYPE_APP;
200 break;
201
202 case NC_ERR_OP_FAILED:
203 type = va_arg(ap, NC_ERR_TYPE);
204
205 if (type == NC_ERR_TYPE_TRAN) {
206 goto fail;
207 }
208 break;
209
210 case NC_ERR_MALFORMED_MSG:
211 type = NC_ERR_TYPE_RPC;
212 break;
213
214 default:
215 goto fail;
216 }
217
218 switch (tag) {
219 case NC_ERR_IN_USE:
Michal Vasko1a38c862016-01-15 15:50:07 +0100220 nc_err_set_msg(ret, "The request requires a resource that already is in use.", "en");
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100221 break;
222 case NC_ERR_INVALID_VALUE:
Michal Vasko1a38c862016-01-15 15:50:07 +0100223 nc_err_set_msg(ret, "The request specifies an unacceptable value for one or more parameters.", "en");
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100224 break;
225 case NC_ERR_TOO_BIG:
Michal Vasko1a38c862016-01-15 15:50:07 +0100226 nc_err_set_msg(ret, "The request or response (that would be generated) is too large for the implementation to handle.", "en");
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100227 break;
228 case NC_ERR_MISSING_ATTR:
Michal Vasko1a38c862016-01-15 15:50:07 +0100229 nc_err_set_msg(ret, "An expected attribute is missing.", "en");
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100230 break;
231 case NC_ERR_BAD_ATTR:
Michal Vasko1a38c862016-01-15 15:50:07 +0100232 nc_err_set_msg(ret, "An attribute value is not correct.", "en");
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100233 break;
234 case NC_ERR_UNKNOWN_ATTR:
Michal Vasko1a38c862016-01-15 15:50:07 +0100235 nc_err_set_msg(ret, "An unexpected attribute is present.", "en");
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100236 break;
237 case NC_ERR_MISSING_ELEM:
Michal Vasko1a38c862016-01-15 15:50:07 +0100238 nc_err_set_msg(ret, "An expected element is missing.", "en");
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100239 break;
240 case NC_ERR_BAD_ELEM:
Michal Vasko1a38c862016-01-15 15:50:07 +0100241 nc_err_set_msg(ret, "An element value is not correct.", "en");
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100242 break;
243 case NC_ERR_UNKNOWN_ELEM:
Michal Vasko1a38c862016-01-15 15:50:07 +0100244 nc_err_set_msg(ret, "An unexpected element is present.", "en");
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100245 break;
246 case NC_ERR_UNKNOWN_NS:
Michal Vasko1a38c862016-01-15 15:50:07 +0100247 nc_err_set_msg(ret, "An unexpected namespace is present.", "en");
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100248 break;
249 case NC_ERR_ACCESS_DENIED:
Michal Vasko1a38c862016-01-15 15:50:07 +0100250 nc_err_set_msg(ret, "Access to the requested protocol operation or data model is denied because authorization failed.", "en");
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100251 break;
252 case NC_ERR_LOCK_DENIED:
Michal Vasko1a38c862016-01-15 15:50:07 +0100253 nc_err_set_msg(ret, "Access to the requested lock is denied because the lock is currently held by another entity.", "en");
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100254 break;
255 case NC_ERR_RES_DENIED:
Michal Vasko1a38c862016-01-15 15:50:07 +0100256 nc_err_set_msg(ret, "Request could not be completed because of insufficient resources.", "en");
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100257 break;
258 case NC_ERR_ROLLBACK_FAILED:
Michal Vasko1a38c862016-01-15 15:50:07 +0100259 nc_err_set_msg(ret, "Request to roll back some configuration change was not completed for some reason.", "en");
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100260 break;
261 case NC_ERR_DATA_EXISTS:
Michal Vasko1a38c862016-01-15 15:50:07 +0100262 nc_err_set_msg(ret, "Request could not be completed because the relevant data model content already exists.", "en");
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100263 break;
264 case NC_ERR_DATA_MISSING:
Michal Vasko1a38c862016-01-15 15:50:07 +0100265 nc_err_set_msg(ret, "Request could not be completed because the relevant data model content does not exist.", "en");
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100266 break;
267 case NC_ERR_OP_NOT_SUPPORTED:
Michal Vasko1a38c862016-01-15 15:50:07 +0100268 nc_err_set_msg(ret, "Request could not be completed because the requested operation is not supported by this implementation.", "en");
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100269 break;
270 case NC_ERR_OP_FAILED:
Michal Vasko1a38c862016-01-15 15:50:07 +0100271 nc_err_set_msg(ret, "Request could not be completed because the requested operation failed for a non-specific reason.", "en");
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100272 break;
273 case NC_ERR_MALFORMED_MSG:
Michal Vasko1a38c862016-01-15 15:50:07 +0100274 nc_err_set_msg(ret, "A message could not be handled because it failed to be parsed correctly.", "en");
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100275 break;
276 default:
277 goto fail;
278 }
279
280 va_end(ap);
281
282 ret->type = type;
283 ret->tag = tag;
284 return ret;
285
286fail:
287 ERRARG;
Michal Vasko11d142a2016-01-19 15:58:24 +0100288 va_end(ap);
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100289 free(ret);
290 return NULL;
291}
292
293API int
Michal Vasko1a38c862016-01-15 15:50:07 +0100294nc_err_set_app_tag(struct nc_server_error *err, const char *error_app_tag)
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100295{
Michal Vasko1a38c862016-01-15 15:50:07 +0100296 if (!err || !error_app_tag) {
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100297 ERRARG;
298 return -1;
299 }
300
301 if (err->apptag) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100302 lydict_remove(server_opts.ctx, err->apptag);
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100303 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100304 err->apptag = lydict_insert(server_opts.ctx, error_app_tag, 0);
Michal Vasko11d142a2016-01-19 15:58:24 +0100305
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100306 return 0;
307}
308
309API int
Michal Vasko1a38c862016-01-15 15:50:07 +0100310nc_err_set_path(struct nc_server_error *err, const char *error_path)
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100311{
Michal Vasko1a38c862016-01-15 15:50:07 +0100312 if (!err || !error_path) {
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100313 ERRARG;
314 return -1;
315 }
316
317 if (err->path) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100318 lydict_remove(server_opts.ctx, err->path);
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100319 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100320 err->path = lydict_insert(server_opts.ctx, error_path, 0);
Michal Vasko11d142a2016-01-19 15:58:24 +0100321
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100322 return 0;
323}
324
325API int
Michal Vasko1a38c862016-01-15 15:50:07 +0100326nc_err_set_msg(struct nc_server_error *err, const char *error_message, const char *lang)
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100327{
Michal Vasko1a38c862016-01-15 15:50:07 +0100328 if (!err || !error_message) {
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100329 ERRARG;
330 return -1;
331 }
332
333 if (err->message) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100334 lydict_remove(server_opts.ctx, err->apptag);
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100335 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100336 err->message = lydict_insert(server_opts.ctx, error_message, 0);
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100337
338 if (err->message_lang) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100339 lydict_remove(server_opts.ctx, err->message_lang);
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100340 }
341 if (lang) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100342 err->message_lang = lydict_insert(server_opts.ctx, lang, 0);
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100343 } else {
344 lang = NULL;
345 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100346
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100347 return 0;
348}
349
350API int
351nc_err_set_sid(struct nc_server_error *err, uint32_t session_id)
352{
353 if (!err || !session_id) {
354 ERRARG;
355 return -1;
356 }
357
358 err->sid = session_id;
359 return 0;
360}
361
362API int
Michal Vasko1a38c862016-01-15 15:50:07 +0100363nc_err_add_bad_attr(struct nc_server_error *err, const char *attr_name)
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100364{
Michal Vasko1a38c862016-01-15 15:50:07 +0100365 if (!err || !attr_name) {
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100366 ERRARG;
367 return -1;
368 }
369
370 ++err->attr_count;
371 err->attr = realloc(err->attr, err->attr_count * sizeof *err->attr);
Michal Vasko1a38c862016-01-15 15:50:07 +0100372 err->attr[err->attr_count - 1] = lydict_insert(server_opts.ctx, attr_name, 0);
Michal Vasko11d142a2016-01-19 15:58:24 +0100373
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100374 return 0;
375}
376
377API int
Michal Vasko1a38c862016-01-15 15:50:07 +0100378nc_err_add_bad_elem(struct nc_server_error *err, const char *elem_name)
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100379{
Michal Vasko1a38c862016-01-15 15:50:07 +0100380 if (!err || !elem_name) {
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100381 ERRARG;
382 return -1;
383 }
384
385 ++err->elem_count;
386 err->elem = realloc(err->elem, err->elem_count * sizeof *err->elem);
Michal Vasko1a38c862016-01-15 15:50:07 +0100387 err->elem[err->elem_count - 1] = lydict_insert(server_opts.ctx, elem_name, 0);
Michal Vasko11d142a2016-01-19 15:58:24 +0100388
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100389 return 0;
390}
391
392API int
Michal Vasko1a38c862016-01-15 15:50:07 +0100393nc_err_add_bad_ns(struct nc_server_error *err, const char *ns_name)
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100394{
Michal Vasko1a38c862016-01-15 15:50:07 +0100395 if (!err || !ns_name) {
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100396 ERRARG;
397 return -1;
398 }
399
400 ++err->ns_count;
401 err->ns = realloc(err->ns, err->ns_count * sizeof *err->ns);
Michal Vasko1a38c862016-01-15 15:50:07 +0100402 err->ns[err->ns_count - 1] = lydict_insert(server_opts.ctx, ns_name, 0);
Michal Vasko11d142a2016-01-19 15:58:24 +0100403
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100404 return 0;
405}
406
407API int
408nc_err_add_info_other(struct nc_server_error *err, struct lyxml_elem *other)
409{
410 if (!err || !other) {
411 ERRARG;
412 return -1;
413 }
414
415 ++err->other_count;
416 err->other = realloc(err->other, err->other_count * sizeof *err->other);
417 err->other[err->other_count - 1] = other;
418 return 0;
419}
420
421void
Michal Vaskoca4a2422016-02-02 12:17:14 +0100422nc_server_rpc_free(struct nc_server_rpc *rpc, struct ly_ctx *ctx)
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100423{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100424 if (!rpc) {
425 return;
426 }
427
Michal Vaskoca4a2422016-02-02 12:17:14 +0100428 lyxml_free(ctx, rpc->root);
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100429 lyd_free(rpc->tree);
Michal Vasko11d142a2016-01-19 15:58:24 +0100430
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100431 free(rpc);
432}
433
434API void
435nc_server_reply_free(struct nc_server_reply *reply)
436{
437 uint32_t i;
438 struct nc_server_reply_data *data_rpl;
439 struct nc_server_reply_error *error_rpl;
440
441 if (!reply) {
442 return;
443 }
444
445 switch (reply->type) {
446 case NC_RPL_DATA:
447 data_rpl = (struct nc_server_reply_data *)reply;
448 if (data_rpl->free) {
449 lyd_free_withsiblings(data_rpl->data);
450 }
451 break;
452 case NC_RPL_OK:
453 /* nothing to free */
454 break;
455 case NC_RPL_ERROR:
456 error_rpl = (struct nc_server_reply_error *)reply;
457 for (i = 0; i < error_rpl->count; ++i) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100458 nc_err_free(error_rpl->err[i]);
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100459 }
460 free(error_rpl->err);
461 break;
462 default:
463 break;
464 }
465 free(reply);
466}
467
468API void
Michal Vasko1a38c862016-01-15 15:50:07 +0100469nc_err_free(struct nc_server_error *err)
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100470{
471 uint32_t i;
472
473 if (!err) {
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100474 return;
475 }
476
Michal Vasko1a38c862016-01-15 15:50:07 +0100477 lydict_remove(server_opts.ctx, err->apptag);
478 lydict_remove(server_opts.ctx, err->path);
479 lydict_remove(server_opts.ctx, err->message);
480 lydict_remove(server_opts.ctx, err->message_lang);
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100481 for (i = 0; i < err->attr_count; ++i) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100482 lydict_remove(server_opts.ctx, err->attr[i]);
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100483 }
484 free(err->attr);
485 for (i = 0; i < err->elem_count; ++i) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100486 lydict_remove(server_opts.ctx, err->elem[i]);
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100487 }
488 free(err->elem);
489 for (i = 0; i < err->ns_count; ++i) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100490 lydict_remove(server_opts.ctx, err->ns[i]);
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100491 }
492 free(err->ns);
493 for (i = 0; i < err->other_count; ++i) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100494 lyxml_free(server_opts.ctx, err->other[i]);
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100495 }
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100496 free(err->other);
497 free(err);
498}