uci: implement config level ACLs
[project/rpcd.git] / uci.c
1 /*
2  * luci-rpcd - LuCI UBUS RPC server
3  *
4  *   Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
5  *
6  * Permission to use, copy, modify, and/or distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18
19 #include "uci.h"
20 #include "session.h"
21
22 static struct blob_buf buf;
23 static struct uci_context *cursor;
24
25 enum {
26         RPC_G_CONFIG,
27         RPC_G_SECTION,
28         RPC_G_OPTION,
29         RPC_G_TYPE,
30         RPC_G_MATCH,
31         RPC_G_SESSION,
32         __RPC_G_MAX,
33 };
34
35 static const struct blobmsg_policy rpc_uci_get_policy[__RPC_G_MAX] = {
36         [RPC_G_CONFIG]  = { .name = "config",  .type = BLOBMSG_TYPE_STRING },
37         [RPC_G_SECTION] = { .name = "section", .type = BLOBMSG_TYPE_STRING },
38         [RPC_G_OPTION]  = { .name = "option",  .type = BLOBMSG_TYPE_STRING },
39         [RPC_G_TYPE]    = { .name = "type",    .type = BLOBMSG_TYPE_STRING },
40         [RPC_G_MATCH]   = { .name = "match",   .type = BLOBMSG_TYPE_TABLE  },
41         [RPC_G_SESSION] = { .name = "ubus_rpc_session",
42                                                .type = BLOBMSG_TYPE_STRING },
43 };
44
45 enum {
46         RPC_A_CONFIG,
47         RPC_A_TYPE,
48         RPC_A_NAME,
49         RPC_A_VALUES,
50         RPC_A_SESSION,
51         __RPC_A_MAX,
52 };
53
54 static const struct blobmsg_policy rpc_uci_add_policy[__RPC_A_MAX] = {
55         [RPC_A_CONFIG]  = { .name = "config",  .type = BLOBMSG_TYPE_STRING },
56         [RPC_A_TYPE]    = { .name = "type",    .type = BLOBMSG_TYPE_STRING },
57         [RPC_A_NAME]    = { .name = "name",    .type = BLOBMSG_TYPE_STRING },
58         [RPC_A_VALUES]  = { .name = "values",  .type = BLOBMSG_TYPE_TABLE  },
59         [RPC_A_SESSION] = { .name = "ubus_rpc_session",
60                                                .type = BLOBMSG_TYPE_STRING },
61 };
62
63 enum {
64         RPC_S_CONFIG,
65         RPC_S_SECTION,
66         RPC_S_TYPE,
67         RPC_S_MATCH,
68         RPC_S_VALUES,
69         RPC_S_SESSION,
70         __RPC_S_MAX,
71 };
72
73 static const struct blobmsg_policy rpc_uci_set_policy[__RPC_S_MAX] = {
74         [RPC_S_CONFIG]  = { .name = "config",   .type = BLOBMSG_TYPE_STRING },
75         [RPC_S_SECTION] = { .name = "section",  .type = BLOBMSG_TYPE_STRING },
76         [RPC_S_TYPE]    = { .name = "type",     .type = BLOBMSG_TYPE_STRING },
77         [RPC_S_MATCH]   = { .name = "match",    .type = BLOBMSG_TYPE_TABLE  },
78         [RPC_S_VALUES]  = { .name = "values",   .type = BLOBMSG_TYPE_TABLE  },
79         [RPC_S_SESSION] = { .name = "ubus_rpc_session",
80                                                 .type = BLOBMSG_TYPE_STRING },
81 };
82
83 enum {
84         RPC_D_CONFIG,
85         RPC_D_SECTION,
86         RPC_D_TYPE,
87         RPC_D_MATCH,
88         RPC_D_OPTION,
89         RPC_D_OPTIONS,
90         RPC_D_SESSION,
91         __RPC_D_MAX,
92 };
93
94 static const struct blobmsg_policy rpc_uci_delete_policy[__RPC_D_MAX] = {
95         [RPC_D_CONFIG]  = { .name = "config",   .type = BLOBMSG_TYPE_STRING },
96         [RPC_D_SECTION] = { .name = "section",  .type = BLOBMSG_TYPE_STRING },
97         [RPC_D_TYPE]    = { .name = "type",     .type = BLOBMSG_TYPE_STRING },
98         [RPC_D_MATCH]   = { .name = "match",    .type = BLOBMSG_TYPE_TABLE  },
99         [RPC_D_OPTION]  = { .name = "option",   .type = BLOBMSG_TYPE_STRING },
100         [RPC_D_OPTIONS] = { .name = "options",  .type = BLOBMSG_TYPE_ARRAY  },
101         [RPC_D_SESSION] = { .name = "ubus_rpc_session",
102                                                 .type = BLOBMSG_TYPE_STRING },
103 };
104
105 enum {
106         RPC_R_CONFIG,
107         RPC_R_SECTION,
108         RPC_R_OPTION,
109         RPC_R_NAME,
110         RPC_R_SESSION,
111         __RPC_R_MAX,
112 };
113
114 static const struct blobmsg_policy rpc_uci_rename_policy[__RPC_R_MAX] = {
115         [RPC_R_CONFIG]  = { .name = "config",   .type = BLOBMSG_TYPE_STRING },
116         [RPC_R_SECTION] = { .name = "section",  .type = BLOBMSG_TYPE_STRING },
117         [RPC_R_OPTION]  = { .name = "option",   .type = BLOBMSG_TYPE_STRING },
118         [RPC_R_NAME]    = { .name = "name",     .type = BLOBMSG_TYPE_STRING },
119         [RPC_R_SESSION] = { .name = "ubus_rpc_session",
120                                                 .type = BLOBMSG_TYPE_STRING },
121 };
122
123 enum {
124         RPC_O_CONFIG,
125         RPC_O_SECTIONS,
126         RPC_O_SESSION,
127         __RPC_O_MAX,
128 };
129
130 static const struct blobmsg_policy rpc_uci_order_policy[__RPC_O_MAX] = {
131         [RPC_O_CONFIG]   = { .name = "config",   .type = BLOBMSG_TYPE_STRING },
132         [RPC_O_SECTIONS] = { .name = "sections", .type = BLOBMSG_TYPE_ARRAY  },
133         [RPC_O_SESSION]  = { .name = "ubus_rpc_session",
134                                                  .type = BLOBMSG_TYPE_STRING },
135 };
136
137 enum {
138         RPC_C_CONFIG,
139         RPC_C_SESSION,
140         __RPC_C_MAX,
141 };
142
143 static const struct blobmsg_policy rpc_uci_config_policy[__RPC_C_MAX] = {
144         [RPC_C_CONFIG]   = { .name = "config",  .type = BLOBMSG_TYPE_STRING },
145         [RPC_C_SESSION]  = { .name = "ubus_rpc_session",
146                                                 .type = BLOBMSG_TYPE_STRING },
147 };
148
149 /*
150  * Turn uci error state into ubus return code
151  */
152 static int
153 rpc_uci_status(void)
154 {
155         switch (cursor->err)
156         {
157         case UCI_OK:
158                 return UBUS_STATUS_OK;
159
160         case UCI_ERR_INVAL:
161                 return UBUS_STATUS_INVALID_ARGUMENT;
162
163         case UCI_ERR_NOTFOUND:
164                 return UBUS_STATUS_NOT_FOUND;
165
166         default:
167                 return UBUS_STATUS_UNKNOWN_ERROR;
168         }
169 }
170
171 /*
172  * Test read access to given config. If the passed "sid" blob attribute pointer
173  * is NULL then the precedure was not invoked through the ubus-rpc so we do not
174  * perform access control and always assume true.
175  */
176 static bool
177 rpc_uci_read_access(struct blob_attr *sid, struct blob_attr *config)
178 {
179         if (!sid)
180                 return true;
181
182         return rpc_session_access(blobmsg_data(sid), "uci",
183                                   blobmsg_data(config), "read");
184 }
185
186 /*
187  * Test write access to given config. If the passed "sid" blob attribute pointer
188  * is NULL then the precedure was not invoked through the ubus-rpc so we do not
189  * perform access control and always assume true.
190  */
191 static bool
192 rpc_uci_write_access(struct blob_attr *sid, struct blob_attr *config)
193 {
194         if (!sid)
195                 return true;
196
197         return rpc_session_access(blobmsg_data(sid), "uci",
198                                   blobmsg_data(config), "write");
199 }
200
201 /*
202  * Format applicable blob value as string and place a pointer to the string
203  * buffer in "p". Uses a static string buffer.
204  */
205 static bool
206 rpc_uci_format_blob(struct blob_attr *v, const char **p)
207 {
208         static char buf[21];
209
210         *p = NULL;
211
212         switch (blobmsg_type(v))
213         {
214         case BLOBMSG_TYPE_STRING:
215                 if (blobmsg_data_len(v) > 1)
216                         *p = blobmsg_data(v);
217                 break;
218
219         case BLOBMSG_TYPE_INT64:
220                 snprintf(buf, sizeof(buf), "%"PRIu64, blobmsg_get_u64(v));
221                 *p = buf;
222                 break;
223
224         case BLOBMSG_TYPE_INT32:
225                 snprintf(buf, sizeof(buf), "%u", blobmsg_get_u32(v));
226                 *p = buf;
227                 break;
228
229         case BLOBMSG_TYPE_INT16:
230                 snprintf(buf, sizeof(buf), "%u", blobmsg_get_u16(v));
231                 *p = buf;
232                 break;
233
234         case BLOBMSG_TYPE_INT8:
235                 snprintf(buf, sizeof(buf), "%u", !!blobmsg_get_u8(v));
236                 *p = buf;
237                 break;
238
239         default:
240                 break;
241         }
242
243         return !!*p;
244 }
245
246 /*
247  * Lookup the given uci_ptr and enable extended lookup format if the .section
248  * value of the uci_ptr looks like extended syntax. Uses an internal copy
249  * of the given uci_ptr to perform the lookup as failing extended section
250  * lookup operations in libuci will zero our the uci_ptr struct.
251  * Copies the internal uci_ptr back to given the uci_ptr on success.
252  */
253 static int
254 rpc_uci_lookup(struct uci_ptr *ptr)
255 {
256         int rv;
257         struct uci_ptr lookup = *ptr;
258
259         if (!lookup.s && lookup.section && *lookup.section == '@')
260                 lookup.flags |= UCI_LOOKUP_EXTENDED;
261
262         rv = uci_lookup_ptr(cursor, &lookup, NULL, true);
263
264         if (!rv)
265                 *ptr = lookup;
266
267         return rv;
268 }
269
270 /*
271  * Checks whether the given uci_option object matches the given string value.
272  *  1) If the uci_option is of type list, check whether any of the list elements
273  *     equals to the given string
274  *  2) If the uci_option is of type string, parse it into space separated tokens
275  *     and check if any of the tokens equals to the given string.
276  *  Returns true if a list element or token matched the given string.
277  */
278 static bool
279 rpc_uci_match_option(struct uci_option *o, const char *cmp)
280 {
281         struct uci_element *e;
282         char *s, *p;
283
284         if (o->type == UCI_TYPE_LIST)
285         {
286                 uci_foreach_element(&o->v.list, e)
287                         if (e->name && !strcmp(e->name, cmp))
288                                 return true;
289
290                 return false;
291         }
292
293         if (!o->v.string)
294                 return false;
295
296         s = strdup(o->v.string);
297
298         if (!s)
299                 return false;
300
301         for (p = strtok(s, " \t"); p; p = strtok(NULL, " \t"))
302         {
303                 if (!strcmp(p, cmp))
304                 {
305                         free(s);
306                         return true;
307                 }
308         }
309
310         free(s);
311         return false;
312 }
313
314 /*
315  * Checks whether the given uci_section matches the type and value blob attrs.
316  *  1) Returns false if "type" is given and the section type does not match
317  *     the value specified in the "type" string blob attribute, else continue.
318  *  2) Tests whether any key in the "matches" table blob attribute exists in
319  *     the given uci_section and whether each value is contained in the
320  *     corresponding uci option value (see rpc_uci_match_option()).
321  *  3) A missing or empty "matches" table blob attribute is always considered
322  *     to be a match.
323  * Returns true if "type" matches or is NULL and "matches" matches or is NULL.
324  */
325 static bool
326 rpc_uci_match_section(struct uci_section *s,
327                       struct blob_attr *type, struct blob_attr *matches)
328 {
329         struct uci_element *e;
330         struct blob_attr *cur;
331         const char *cmp;
332         bool match = false;
333         bool empty = true;
334         int rem;
335
336         if (type && strcmp(s->type, blobmsg_data(type)))
337                 return false;
338
339         if (!matches)
340                 return true;
341
342         blobmsg_for_each_attr(cur, matches, rem)
343         {
344                 if (!rpc_uci_format_blob(cur, &cmp))
345                         continue;
346
347                 uci_foreach_element(&s->options, e)
348                 {
349                         if (strcmp(e->name, blobmsg_name(cur)))
350                                 continue;
351
352                         if (!rpc_uci_match_option(uci_to_option(e), cmp))
353                                 return false;
354
355                         match = true;
356                 }
357
358                 empty = false;
359         }
360
361         return (empty || match);
362 }
363
364 /*
365  * Dump the given uci_option value into the global blobmsg buffer and use
366  * given "name" as key.
367  *  1) If the uci_option is of type list, put a table into the blob buffer and
368  *     add each list item as string to it.
369  *  2) If the uci_option is of type string, put its value directly into the blob
370  *     buffer.
371  */
372 static void
373 rpc_uci_dump_option(struct uci_option *o, const char *name)
374 {
375         void *c;
376         struct uci_element *e;
377
378         switch (o->type)
379         {
380         case UCI_TYPE_STRING:
381                 blobmsg_add_string(&buf, name, o->v.string);
382                 break;
383
384         case UCI_TYPE_LIST:
385                 c = blobmsg_open_array(&buf, name);
386
387                 uci_foreach_element(&o->v.list, e)
388                         blobmsg_add_string(&buf, NULL, e->name);
389
390                 blobmsg_close_array(&buf, c);
391                 break;
392
393         default:
394                 break;
395         }
396 }
397
398 /*
399  * Dump the given uci_section object into the global blobmsg buffer and use
400  * given "name" as key.
401  * Puts a table into the blob buffer and puts each section option member value
402  * as value into the table using the option name as key.
403  * Adds three special keys ".anonymous", ".type" and ".name" which specify the
404  * corresponding section properties.
405  */
406 static void
407 rpc_uci_dump_section(struct uci_section *s, const char *name, int index)
408 {
409         void *c;
410         struct uci_option *o;
411         struct uci_element *e;
412
413         c = blobmsg_open_table(&buf, name);
414
415         blobmsg_add_u8(&buf, ".anonymous", s->anonymous);
416         blobmsg_add_string(&buf, ".type", s->type);
417         blobmsg_add_string(&buf, ".name", s->e.name);
418
419         if (index >= 0)
420                 blobmsg_add_u32(&buf, ".index", index);
421
422         uci_foreach_element(&s->options, e)
423         {
424                 o = uci_to_option(e);
425                 rpc_uci_dump_option(o, o->e.name);
426         }
427
428         blobmsg_close_table(&buf, c);
429 }
430
431 /*
432  * Dump the given uci_package object into the global blobmsg buffer and use
433  * given "name" as key.
434  * Puts a table into the blob buffer and puts each package section member as
435  * value into the table using the section name as key.
436  * Only dumps sections matching the given "type" and "matches", see explaination
437  * of rpc_uci_match_section() for details.
438  */
439 static void
440 rpc_uci_dump_package(struct uci_package *p, const char *name,
441                      struct blob_attr *type, struct blob_attr *matches)
442 {
443         void *c;
444         struct uci_element *e;
445         int i = -1;
446
447         c = blobmsg_open_table(&buf, name);
448
449         uci_foreach_element(&p->sections, e)
450         {
451                 i++;
452
453                 if (!rpc_uci_match_section(uci_to_section(e), type, matches))
454                         continue;
455
456                 rpc_uci_dump_section(uci_to_section(e), e->name, i);
457         }
458
459         blobmsg_close_table(&buf, c);
460 }
461
462
463 static int
464 rpc_uci_get(struct ubus_context *ctx, struct ubus_object *obj,
465             struct ubus_request_data *req, const char *method,
466             struct blob_attr *msg)
467 {
468         struct blob_attr *tb[__RPC_G_MAX];
469         struct uci_package *p = NULL;
470         struct uci_ptr ptr = { 0 };
471
472         blobmsg_parse(rpc_uci_get_policy, __RPC_G_MAX, tb,
473                       blob_data(msg), blob_len(msg));
474
475         if (!tb[RPC_G_CONFIG])
476                 return UBUS_STATUS_INVALID_ARGUMENT;
477
478         if (!rpc_uci_read_access(tb[RPC_G_SESSION], tb[RPC_G_CONFIG]))
479                 return UBUS_STATUS_PERMISSION_DENIED;
480
481         ptr.package = blobmsg_data(tb[RPC_G_CONFIG]);
482         uci_load(cursor, ptr.package, &p);
483
484         if (!p)
485                 goto out;
486
487         if (tb[RPC_G_SECTION])
488         {
489                 ptr.section = blobmsg_data(tb[RPC_G_SECTION]);
490
491                 if (tb[RPC_G_OPTION])
492                         ptr.option = blobmsg_data(tb[RPC_G_OPTION]);
493         }
494
495         if (rpc_uci_lookup(&ptr) || !(ptr.flags & UCI_LOOKUP_COMPLETE))
496                 goto out;
497
498         blob_buf_init(&buf, 0);
499
500         switch (ptr.last->type)
501         {
502         case UCI_TYPE_PACKAGE:
503                 rpc_uci_dump_package(ptr.p, "values", tb[RPC_G_TYPE], tb[RPC_G_MATCH]);
504                 break;
505
506         case UCI_TYPE_SECTION:
507                 rpc_uci_dump_section(ptr.s, "values", -1);
508                 break;
509
510         case UCI_TYPE_OPTION:
511                 rpc_uci_dump_option(ptr.o, "value");
512                 break;
513
514         default:
515                 break;
516         }
517
518         ubus_send_reply(ctx, req, buf.head);
519
520 out:
521         if (p)
522                 uci_unload(cursor, p);
523
524         return rpc_uci_status();
525 }
526
527 static int
528 rpc_uci_add(struct ubus_context *ctx, struct ubus_object *obj,
529             struct ubus_request_data *req, const char *method,
530             struct blob_attr *msg)
531 {
532         struct blob_attr *tb[__RPC_A_MAX];
533         struct blob_attr *cur, *elem;
534         struct uci_package *p = NULL;
535         struct uci_section *s;
536         struct uci_ptr ptr = { 0 };
537         int rem, rem2;
538
539         blobmsg_parse(rpc_uci_add_policy, __RPC_A_MAX, tb,
540                       blob_data(msg), blob_len(msg));
541
542         if (!tb[RPC_A_CONFIG] || !tb[RPC_A_TYPE])
543                 return UBUS_STATUS_INVALID_ARGUMENT;
544
545         if (!rpc_uci_write_access(tb[RPC_A_SESSION], tb[RPC_A_CONFIG]))
546                 return UBUS_STATUS_PERMISSION_DENIED;
547
548         ptr.package = blobmsg_data(tb[RPC_A_CONFIG]);
549
550         uci_load(cursor, ptr.package, &p);
551
552         if (!p)
553                 goto out;
554
555         /* add named section */
556         if (tb[RPC_A_NAME])
557         {
558                 ptr.section = blobmsg_data(tb[RPC_A_NAME]);
559                 ptr.value   = blobmsg_data(tb[RPC_A_TYPE]);
560                 ptr.option  = NULL;
561
562                 if (rpc_uci_lookup(&ptr) || uci_set(cursor, &ptr))
563                         goto out;
564         }
565
566         /* add anon section */
567         else
568         {
569                 if (uci_add_section(cursor, p, blobmsg_data(tb[RPC_A_TYPE]), &s) || !s)
570                         goto out;
571
572                 ptr.section = s->e.name;
573         }
574
575         if (tb[RPC_A_VALUES])
576         {
577                 blobmsg_for_each_attr(cur, tb[RPC_A_VALUES], rem)
578                 {
579                         ptr.o = NULL;
580                         ptr.option = blobmsg_name(cur);
581
582                         if (rpc_uci_lookup(&ptr) || !ptr.s)
583                                 continue;
584
585                         switch (blobmsg_type(cur))
586                         {
587                         case BLOBMSG_TYPE_ARRAY:
588                                 blobmsg_for_each_attr(elem, cur, rem2)
589                                         if (rpc_uci_format_blob(elem, &ptr.value))
590                                                 uci_add_list(cursor, &ptr);
591                                 break;
592
593                         default:
594                                 if (rpc_uci_format_blob(cur, &ptr.value))
595                                         uci_set(cursor, &ptr);
596                                 break;
597                         }
598                 }
599         }
600
601         uci_save(cursor, p);
602
603         blob_buf_init(&buf, 0);
604         blobmsg_add_string(&buf, "section", ptr.section);
605         ubus_send_reply(ctx, req, buf.head);
606
607 out:
608         if (p)
609                 uci_unload(cursor, p);
610
611         return rpc_uci_status();
612 }
613
614 /*
615  * Turn value from a blob attribute into uci set operation
616  *  1) if the blob is of type array, delete existing option (if any) and
617  *     emit uci add_list operations for each element
618  *  2) if the blob is not an array but an option of type list exists,
619  *     delete existing list and emit uci set operation for the blob value
620  *  3) in all other cases only emit a set operation if there is no existing
621  *     option of if the existing options value differs from the blob value
622  */
623 static void
624 rpc_uci_merge_set(struct blob_attr *opt, struct uci_ptr *ptr)
625 {
626         struct blob_attr *cur;
627         int rem;
628
629         ptr->o = NULL;
630         ptr->option = blobmsg_name(opt);
631
632         if (rpc_uci_lookup(ptr) || !ptr->s)
633                 return;
634
635         if (blobmsg_type(opt) == BLOBMSG_TYPE_ARRAY)
636         {
637                 if (ptr->o)
638                         uci_delete(cursor, ptr);
639
640                 blobmsg_for_each_attr(cur, opt, rem)
641                         if (rpc_uci_format_blob(cur, &ptr->value))
642                                 uci_add_list(cursor, ptr);
643         }
644         else if (ptr->o && ptr->o->type == UCI_TYPE_LIST)
645         {
646                 uci_delete(cursor, ptr);
647
648                 if (rpc_uci_format_blob(opt, &ptr->value))
649                         uci_set(cursor, ptr);
650         }
651         else if (rpc_uci_format_blob(opt, &ptr->value))
652         {
653                 if (!ptr->o || !ptr->o->v.string || strcmp(ptr->o->v.string, ptr->value))
654                         uci_set(cursor, ptr);
655         }
656 }
657
658 static int
659 rpc_uci_set(struct ubus_context *ctx, struct ubus_object *obj,
660             struct ubus_request_data *req, const char *method,
661             struct blob_attr *msg)
662 {
663         struct blob_attr *tb[__RPC_S_MAX];
664         struct blob_attr *cur;
665         struct uci_package *p = NULL;
666         struct uci_element *e;
667         struct uci_ptr ptr = { 0 };
668         int rem;
669
670         blobmsg_parse(rpc_uci_set_policy, __RPC_S_MAX, tb,
671                       blob_data(msg), blob_len(msg));
672
673         if (!tb[RPC_S_CONFIG] || !tb[RPC_S_VALUES] ||
674                 (!tb[RPC_S_SECTION] && !tb[RPC_S_TYPE] && !tb[RPC_S_MATCH]))
675                 return UBUS_STATUS_INVALID_ARGUMENT;
676
677         if (!rpc_uci_write_access(tb[RPC_S_SESSION], tb[RPC_S_CONFIG]))
678                 return UBUS_STATUS_PERMISSION_DENIED;
679
680         ptr.package = blobmsg_data(tb[RPC_S_CONFIG]);
681         uci_load(cursor, ptr.package, &p);
682
683         if (!p)
684                 goto out;
685
686         if (tb[RPC_S_SECTION])
687         {
688                 ptr.section = blobmsg_data(tb[RPC_S_SECTION]);
689                 blobmsg_for_each_attr(cur, tb[RPC_S_VALUES], rem)
690                         rpc_uci_merge_set(cur, &ptr);
691         }
692         else
693         {
694                 uci_foreach_element(&p->sections, e)
695                 {
696                         if (!rpc_uci_match_section(uci_to_section(e),
697                                                    tb[RPC_S_TYPE], tb[RPC_S_MATCH]))
698                                 continue;
699
700                         ptr.s = NULL;
701                         ptr.section = e->name;
702
703                         blobmsg_for_each_attr(cur, tb[RPC_S_VALUES], rem)
704                                 rpc_uci_merge_set(cur, &ptr);
705                 }
706         }
707
708         uci_save(cursor, p);
709
710 out:
711         if (p)
712                 uci_unload(cursor, p);
713
714         return rpc_uci_status();
715 }
716
717 /*
718  * Delete option or section from uci specified by given blob attribute pointer
719  *  1) if the blob is of type array, delete any option named after each element
720  *  2) if the blob is of type string, delete the option named after its value
721  *  3) if the blob is NULL, delete entire section
722  */
723 static void
724 rpc_uci_merge_delete(struct blob_attr *opt, struct uci_ptr *ptr)
725 {
726         struct blob_attr *cur;
727         int rem;
728
729         if (rpc_uci_lookup(ptr) || !ptr->s)
730                 return;
731
732         if (!opt)
733         {
734                 ptr->o = NULL;
735                 ptr->option = NULL;
736
737                 uci_delete(cursor, ptr);
738         }
739         else if (blobmsg_type(opt) == BLOBMSG_TYPE_ARRAY)
740         {
741                 blobmsg_for_each_attr(cur, opt, rem)
742                 {
743                         if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
744                                 continue;
745
746                         ptr->o = NULL;
747                         ptr->option = blobmsg_data(cur);
748
749                         if (rpc_uci_lookup(ptr) || !ptr->o)
750                                 continue;
751
752                         uci_delete(cursor, ptr);
753                 }
754         }
755         else if (blobmsg_type(opt) == BLOBMSG_TYPE_STRING)
756         {
757                 ptr->o = NULL;
758                 ptr->option = blobmsg_data(opt);
759
760                 if (rpc_uci_lookup(ptr) || !ptr->o)
761                         return;
762
763                 uci_delete(cursor, ptr);
764         }
765 }
766
767 static int
768 rpc_uci_delete(struct ubus_context *ctx, struct ubus_object *obj,
769                struct ubus_request_data *req, const char *method,
770                struct blob_attr *msg)
771 {
772         struct blob_attr *tb[__RPC_D_MAX];
773         struct uci_package *p = NULL;
774         struct uci_element *e, *tmp;
775         struct uci_ptr ptr = { 0 };
776
777         blobmsg_parse(rpc_uci_delete_policy, __RPC_D_MAX, tb,
778                       blob_data(msg), blob_len(msg));
779
780         if (!tb[RPC_D_CONFIG] ||
781                 (!tb[RPC_D_SECTION] && !tb[RPC_D_TYPE] && !tb[RPC_D_MATCH]))
782                 return UBUS_STATUS_INVALID_ARGUMENT;
783
784         if (!rpc_uci_write_access(tb[RPC_D_SESSION], tb[RPC_D_CONFIG]))
785                 return UBUS_STATUS_PERMISSION_DENIED;
786
787         ptr.package = blobmsg_data(tb[RPC_D_CONFIG]);
788         uci_load(cursor, ptr.package, &p);
789
790         if (!p)
791                 goto out;
792
793         if (tb[RPC_D_SECTION])
794         {
795                 ptr.section = blobmsg_data(tb[RPC_D_SECTION]);
796
797                 if (tb[RPC_D_OPTIONS])
798                         rpc_uci_merge_delete(tb[RPC_D_OPTIONS], &ptr);
799                 else
800                         rpc_uci_merge_delete(tb[RPC_D_OPTION], &ptr);
801         }
802         else
803         {
804                 uci_foreach_element_safe(&p->sections, tmp, e)
805                 {
806                         if (!rpc_uci_match_section(uci_to_section(e),
807                                                    tb[RPC_D_TYPE], tb[RPC_D_MATCH]))
808                                 continue;
809
810                         ptr.s = NULL;
811                         ptr.section = e->name;
812
813                         if (tb[RPC_D_OPTIONS])
814                                 rpc_uci_merge_delete(tb[RPC_D_OPTIONS], &ptr);
815                         else
816                                 rpc_uci_merge_delete(tb[RPC_D_OPTION], &ptr);
817                 }
818         }
819
820         uci_save(cursor, p);
821
822 out:
823         if (p)
824                 uci_unload(cursor, p);
825
826         return rpc_uci_status();
827 }
828
829 static int
830 rpc_uci_rename(struct ubus_context *ctx, struct ubus_object *obj,
831                struct ubus_request_data *req, const char *method,
832                struct blob_attr *msg)
833 {
834         struct blob_attr *tb[__RPC_R_MAX];
835         struct uci_package *p = NULL;
836         struct uci_ptr ptr = { 0 };
837
838         blobmsg_parse(rpc_uci_rename_policy, __RPC_R_MAX, tb,
839                       blob_data(msg), blob_len(msg));
840
841         if (!tb[RPC_R_CONFIG] || !tb[RPC_R_SECTION] || !tb[RPC_R_NAME])
842                 return UBUS_STATUS_INVALID_ARGUMENT;
843
844         if (!rpc_uci_write_access(tb[RPC_R_SESSION], tb[RPC_R_CONFIG]))
845                 return UBUS_STATUS_PERMISSION_DENIED;
846
847         ptr.package = blobmsg_data(tb[RPC_R_CONFIG]);
848         ptr.section = blobmsg_data(tb[RPC_R_SECTION]);
849         ptr.value   = blobmsg_data(tb[RPC_R_NAME]);
850
851         if (tb[RPC_R_OPTION])
852                 ptr.option = blobmsg_data(tb[RPC_R_OPTION]);
853
854         uci_load(cursor, ptr.package, &p);
855
856         if (!p)
857                 goto out;
858
859         if (uci_lookup_ptr(cursor, &ptr, NULL, true))
860                 goto out;
861
862         if ((ptr.option && !ptr.o) || !ptr.s)
863         {
864                 cursor->err = UCI_ERR_NOTFOUND;
865                 goto out;
866         }
867
868         if (uci_rename(cursor, &ptr))
869                 goto out;
870
871         uci_save(cursor, p);
872
873 out:
874         if (p)
875                 uci_unload(cursor, p);
876
877         return rpc_uci_status();
878 }
879
880 static int
881 rpc_uci_order(struct ubus_context *ctx, struct ubus_object *obj,
882               struct ubus_request_data *req, const char *method,
883               struct blob_attr *msg)
884 {
885         struct blob_attr *tb[__RPC_O_MAX];
886         struct blob_attr *cur;
887         struct uci_package *p = NULL;
888         struct uci_ptr ptr = { 0 };
889         int rem, i = 1;
890
891         blobmsg_parse(rpc_uci_order_policy, __RPC_O_MAX, tb,
892                       blob_data(msg), blob_len(msg));
893
894         if (!tb[RPC_O_CONFIG] || !tb[RPC_O_SECTIONS])
895                 return UBUS_STATUS_INVALID_ARGUMENT;
896
897         if (!rpc_uci_write_access(tb[RPC_O_SESSION], tb[RPC_O_CONFIG]))
898                 return UBUS_STATUS_PERMISSION_DENIED;
899
900         ptr.package = blobmsg_data(tb[RPC_O_CONFIG]);
901
902         uci_load(cursor, ptr.package, &p);
903
904         if (!p)
905                 goto out;
906
907         blobmsg_for_each_attr(cur, tb[RPC_O_SECTIONS], rem)
908         {
909                 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
910                         continue;
911
912                 ptr.s = NULL;
913                 ptr.section = blobmsg_data(cur);
914
915                 if (uci_lookup_ptr(cursor, &ptr, NULL, true) || !ptr.s)
916                         continue;
917
918                 uci_reorder_section(cursor, ptr.s, i++);
919         }
920
921         uci_save(cursor, p);
922
923 out:
924         if (p)
925                 uci_unload(cursor, p);
926
927         return rpc_uci_status();
928 }
929
930 static void
931 rpc_uci_dump_change(struct uci_delta *d)
932 {
933         void *c;
934         const char *types[] = {
935                 [UCI_CMD_REORDER]  = "order",
936                 [UCI_CMD_REMOVE]   = "remove",
937                 [UCI_CMD_RENAME]   = "rename",
938                 [UCI_CMD_ADD]      = "add",
939                 [UCI_CMD_LIST_ADD] = "list-add",
940                 [UCI_CMD_LIST_DEL] = "list-del",
941                 [UCI_CMD_CHANGE]   = "set",
942         };
943
944         if (!d->section)
945                 return;
946
947         c = blobmsg_open_array(&buf, NULL);
948
949         blobmsg_add_string(&buf, NULL, types[d->cmd]);
950         blobmsg_add_string(&buf, NULL, d->section);
951
952         if (d->e.name)
953                 blobmsg_add_string(&buf, NULL, d->e.name);
954
955         if (d->value)
956         {
957                 if (d->cmd == UCI_CMD_REORDER)
958                         blobmsg_add_u32(&buf, NULL, strtoul(d->value, NULL, 10));
959                 else
960                         blobmsg_add_string(&buf, NULL, d->value);
961         }
962
963         blobmsg_close_array(&buf, c);
964 }
965
966 static int
967 rpc_uci_changes(struct ubus_context *ctx, struct ubus_object *obj,
968                 struct ubus_request_data *req, const char *method,
969                 struct blob_attr *msg)
970 {
971         struct blob_attr *tb[__RPC_C_MAX];
972         struct uci_package *p = NULL;
973         struct uci_element *e;
974         void *c;
975
976         blobmsg_parse(rpc_uci_config_policy, __RPC_C_MAX, tb,
977                       blob_data(msg), blob_len(msg));
978
979         if (!tb[RPC_C_CONFIG])
980                 return UBUS_STATUS_INVALID_ARGUMENT;
981
982         if (!rpc_uci_read_access(tb[RPC_C_SESSION], tb[RPC_C_CONFIG]))
983                 return UBUS_STATUS_PERMISSION_DENIED;
984
985         uci_load(cursor, blobmsg_data(tb[RPC_C_CONFIG]), &p);
986
987         if (!p)
988                 goto out;
989
990         blob_buf_init(&buf, 0);
991         c = blobmsg_open_array(&buf, "changes");
992
993         uci_foreach_element(&p->saved_delta, e)
994                 rpc_uci_dump_change(uci_to_delta(e));
995
996         blobmsg_close_array(&buf, c);
997
998         ubus_send_reply(ctx, req, buf.head);
999
1000 out:
1001         if (p)
1002                 uci_unload(cursor, p);
1003
1004         return rpc_uci_status();
1005 }
1006
1007 static int
1008 rpc_uci_revert_commit(struct blob_attr *msg, bool commit)
1009 {
1010         struct blob_attr *tb[__RPC_C_MAX];
1011         struct uci_package *p = NULL;
1012         struct uci_ptr ptr = { 0 };
1013
1014         blobmsg_parse(rpc_uci_config_policy, __RPC_C_MAX, tb,
1015                       blob_data(msg), blob_len(msg));
1016
1017         if (!tb[RPC_C_CONFIG])
1018                 return UBUS_STATUS_INVALID_ARGUMENT;
1019
1020         if (!rpc_uci_write_access(tb[RPC_C_SESSION], tb[RPC_C_CONFIG]))
1021                 return UBUS_STATUS_PERMISSION_DENIED;
1022
1023         ptr.package = blobmsg_data(tb[RPC_C_CONFIG]);
1024         uci_load(cursor, ptr.package, &p);
1025
1026         if (!p || uci_lookup_ptr(cursor, &ptr, NULL, true) || !ptr.p)
1027                 goto out;
1028
1029         if (commit)
1030                 uci_commit(cursor, &p, false);
1031         else
1032                 uci_revert(cursor, &ptr);
1033
1034 out:
1035         if (p)
1036                 uci_unload(cursor, p);
1037
1038         return rpc_uci_status();
1039 }
1040
1041 static int
1042 rpc_uci_revert(struct ubus_context *ctx, struct ubus_object *obj,
1043                struct ubus_request_data *req, const char *method,
1044                struct blob_attr *msg)
1045 {
1046         return rpc_uci_revert_commit(msg, false);
1047 }
1048
1049 static int
1050 rpc_uci_commit(struct ubus_context *ctx, struct ubus_object *obj,
1051                struct ubus_request_data *req, const char *method,
1052                struct blob_attr *msg)
1053 {
1054         return rpc_uci_revert_commit(msg, true);
1055 }
1056
1057 static int
1058 rpc_uci_configs(struct ubus_context *ctx, struct ubus_object *obj,
1059                 struct ubus_request_data *req, const char *method,
1060                 struct blob_attr *msg)
1061 {
1062         char **configs;
1063         void *c;
1064         int i;
1065
1066         if (uci_list_configs(cursor, &configs))
1067                 goto out;
1068
1069         blob_buf_init(&buf, 0);
1070
1071         c = blobmsg_open_array(&buf, "configs");
1072
1073         for (i = 0; configs[i]; i++)
1074                 blobmsg_add_string(&buf, NULL, configs[i]);
1075
1076         blobmsg_close_array(&buf, c);
1077
1078         ubus_send_reply(ctx, req, buf.head);
1079
1080 out:
1081         return rpc_uci_status();
1082 }
1083
1084
1085 int rpc_uci_api_init(struct ubus_context *ctx)
1086 {
1087         static const struct ubus_method uci_methods[] = {
1088                 { .name = "configs", .handler = rpc_uci_configs },
1089                 UBUS_METHOD("get",     rpc_uci_get,     rpc_uci_get_policy),
1090                 UBUS_METHOD("add",     rpc_uci_add,     rpc_uci_add_policy),
1091                 UBUS_METHOD("set",     rpc_uci_set,     rpc_uci_set_policy),
1092                 UBUS_METHOD("delete",  rpc_uci_delete,  rpc_uci_delete_policy),
1093                 UBUS_METHOD("rename",  rpc_uci_rename,  rpc_uci_rename_policy),
1094                 UBUS_METHOD("order",   rpc_uci_order,   rpc_uci_order_policy),
1095                 UBUS_METHOD("changes", rpc_uci_changes, rpc_uci_config_policy),
1096                 UBUS_METHOD("revert",  rpc_uci_revert,  rpc_uci_config_policy),
1097                 UBUS_METHOD("commit",  rpc_uci_commit,  rpc_uci_config_policy),
1098         };
1099
1100         static struct ubus_object_type uci_type =
1101                 UBUS_OBJECT_TYPE("luci-rpc-uci", uci_methods);
1102
1103         static struct ubus_object obj = {
1104                 .name = "uci",
1105                 .type = &uci_type,
1106                 .methods = uci_methods,
1107                 .n_methods = ARRAY_SIZE(uci_methods),
1108         };
1109
1110         cursor = uci_alloc_context();
1111
1112         if (!cursor)
1113                 return UBUS_STATUS_UNKNOWN_ERROR;
1114
1115         return ubus_add_object(ctx, &obj);
1116 }