chiark / gitweb /
tree-wide: drop 'This file is part of systemd' blurb
[elogind.git] / src / libelogind / sd-bus / bus-internal.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3   Copyright 2013 Lennart Poettering
4 ***/
5
6 #include "alloc-util.h"
7 #include "bus-internal.h"
8 #include "bus-message.h"
9 #include "hexdecoct.h"
10 #include "string-util.h"
11
12 bool object_path_is_valid(const char *p) {
13         const char *q;
14         bool slash;
15
16         if (!p)
17                 return false;
18
19         if (p[0] != '/')
20                 return false;
21
22         if (p[1] == 0)
23                 return true;
24
25         for (slash = true, q = p+1; *q; q++)
26                 if (*q == '/') {
27                         if (slash)
28                                 return false;
29
30                         slash = true;
31                 } else {
32                         bool good;
33
34                         good =
35                                 (*q >= 'a' && *q <= 'z') ||
36                                 (*q >= 'A' && *q <= 'Z') ||
37                                 (*q >= '0' && *q <= '9') ||
38                                 *q == '_';
39
40                         if (!good)
41                                 return false;
42
43                         slash = false;
44                 }
45
46         if (slash)
47                 return false;
48
49         return true;
50 }
51
52 char* object_path_startswith(const char *a, const char *b) {
53         const char *p;
54
55         if (!object_path_is_valid(a) ||
56             !object_path_is_valid(b))
57                 return NULL;
58
59         if (streq(b, "/"))
60                 return (char*) a + 1;
61
62         p = startswith(a, b);
63         if (!p)
64                 return NULL;
65
66         if (*p == 0)
67                 return (char*) p;
68
69         if (*p == '/')
70                 return (char*) p + 1;
71
72         return NULL;
73 }
74
75 bool interface_name_is_valid(const char *p) {
76         const char *q;
77         bool dot, found_dot = false;
78
79         if (isempty(p))
80                 return false;
81
82         for (dot = true, q = p; *q; q++)
83                 if (*q == '.') {
84                         if (dot)
85                                 return false;
86
87                         found_dot = dot = true;
88                 } else {
89                         bool good;
90
91                         good =
92                                 (*q >= 'a' && *q <= 'z') ||
93                                 (*q >= 'A' && *q <= 'Z') ||
94                                 (!dot && *q >= '0' && *q <= '9') ||
95                                 *q == '_';
96
97                         if (!good)
98                                 return false;
99
100                         dot = false;
101                 }
102
103         if (q - p > 255)
104                 return false;
105
106         if (dot)
107                 return false;
108
109         if (!found_dot)
110                 return false;
111
112         return true;
113 }
114
115 bool service_name_is_valid(const char *p) {
116         const char *q;
117         bool dot, found_dot = false, unique;
118
119         if (isempty(p))
120                 return false;
121
122         unique = p[0] == ':';
123
124         for (dot = true, q = unique ? p+1 : p; *q; q++)
125                 if (*q == '.') {
126                         if (dot)
127                                 return false;
128
129                         found_dot = dot = true;
130                 } else {
131                         bool good;
132
133                         good =
134                                 (*q >= 'a' && *q <= 'z') ||
135                                 (*q >= 'A' && *q <= 'Z') ||
136                                 ((!dot || unique) && *q >= '0' && *q <= '9') ||
137                                 IN_SET(*q, '_', '-');
138
139                         if (!good)
140                                 return false;
141
142                         dot = false;
143                 }
144
145         if (q - p > 255)
146                 return false;
147
148         if (dot)
149                 return false;
150
151         if (!found_dot)
152                 return false;
153
154         return true;
155 }
156
157 #if 0 /// UNNEEDED by elogind
158 char* service_name_startswith(const char *a, const char *b) {
159         const char *p;
160
161         if (!service_name_is_valid(a) ||
162             !service_name_is_valid(b))
163                 return NULL;
164
165         p = startswith(a, b);
166         if (!p)
167                 return NULL;
168
169         if (*p == 0)
170                 return (char*) p;
171
172         if (*p == '.')
173                 return (char*) p + 1;
174
175         return NULL;
176 }
177 #endif // 0
178
179 bool member_name_is_valid(const char *p) {
180         const char *q;
181
182         if (isempty(p))
183                 return false;
184
185         for (q = p; *q; q++) {
186                 bool good;
187
188                 good =
189                         (*q >= 'a' && *q <= 'z') ||
190                         (*q >= 'A' && *q <= 'Z') ||
191                         (*q >= '0' && *q <= '9') ||
192                         *q == '_';
193
194                 if (!good)
195                         return false;
196         }
197
198         if (q - p > 255)
199                 return false;
200
201         return true;
202 }
203
204 /*
205  * Complex pattern match
206  * This checks whether @a is a 'complex-prefix' of @b, or @b is a
207  * 'complex-prefix' of @a, based on strings that consist of labels with @c as
208  * spearator. This function returns true if:
209  *   - both strings are equal
210  *   - either is a prefix of the other and ends with @c
211  * The second rule makes sure that either string needs to be fully included in
212  * the other, and the string which is considered the prefix needs to end with a
213  * separator.
214  */
215 static bool complex_pattern_check(char c, const char *a, const char *b) {
216         bool separator = false;
217
218         if (!a && !b)
219                 return true;
220
221         if (!a || !b)
222                 return false;
223
224         for (;;) {
225                 if (*a != *b)
226                         return (separator && (*a == 0 || *b == 0));
227
228                 if (*a == 0)
229                         return true;
230
231                 separator = *a == c;
232
233                 a++, b++;
234         }
235 }
236
237 bool namespace_complex_pattern(const char *pattern, const char *value) {
238         return complex_pattern_check('.', pattern, value);
239 }
240
241 bool path_complex_pattern(const char *pattern, const char *value) {
242         return complex_pattern_check('/', pattern, value);
243 }
244
245 /*
246  * Simple pattern match
247  * This checks whether @a is a 'simple-prefix' of @b, based on strings that
248  * consist of labels with @c as separator. This function returns true, if:
249  *   - if @a and @b are equal
250  *   - if @a is a prefix of @b, and the first following character in @b (or the
251  *     last character in @a) is @c
252  * The second rule basically makes sure that if @a is a prefix of @b, then @b
253  * must follow with a new label separated by @c. It cannot extend the label.
254  */
255 static bool simple_pattern_check(char c, const char *a, const char *b) {
256         bool separator = false;
257
258         if (!a && !b)
259                 return true;
260
261         if (!a || !b)
262                 return false;
263
264         for (;;) {
265                 if (*a != *b)
266                         return *a == 0 && (*b == c || separator);
267
268                 if (*a == 0)
269                         return true;
270
271                 separator = *a == c;
272
273                 a++, b++;
274         }
275 }
276
277 bool namespace_simple_pattern(const char *pattern, const char *value) {
278         return simple_pattern_check('.', pattern, value);
279 }
280
281 bool path_simple_pattern(const char *pattern, const char *value) {
282         return simple_pattern_check('/', pattern, value);
283 }
284
285 int bus_message_type_from_string(const char *s, uint8_t *u) {
286         if (streq(s, "signal"))
287                 *u = SD_BUS_MESSAGE_SIGNAL;
288         else if (streq(s, "method_call"))
289                 *u = SD_BUS_MESSAGE_METHOD_CALL;
290         else if (streq(s, "error"))
291                 *u = SD_BUS_MESSAGE_METHOD_ERROR;
292         else if (streq(s, "method_return"))
293                 *u = SD_BUS_MESSAGE_METHOD_RETURN;
294         else
295                 return -EINVAL;
296
297         return 0;
298 }
299
300 const char *bus_message_type_to_string(uint8_t u) {
301         if (u == SD_BUS_MESSAGE_SIGNAL)
302                 return "signal";
303         else if (u == SD_BUS_MESSAGE_METHOD_CALL)
304                 return "method_call";
305         else if (u == SD_BUS_MESSAGE_METHOD_ERROR)
306                 return "error";
307         else if (u == SD_BUS_MESSAGE_METHOD_RETURN)
308                  return "method_return";
309         else
310                 return NULL;
311 }
312
313 char *bus_address_escape(const char *v) {
314         const char *a;
315         char *r, *b;
316
317         r = new(char, strlen(v)*3+1);
318         if (!r)
319                 return NULL;
320
321         for (a = v, b = r; *a; a++) {
322
323                 if ((*a >= '0' && *a <= '9') ||
324                     (*a >= 'a' && *a <= 'z') ||
325                     (*a >= 'A' && *a <= 'Z') ||
326                     strchr("_-/.", *a))
327                         *(b++) = *a;
328                 else {
329                         *(b++) = '%';
330                         *(b++) = hexchar(*a >> 4);
331                         *(b++) = hexchar(*a & 0xF);
332                 }
333         }
334
335         *b = 0;
336         return r;
337 }
338
339 int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error) {
340         assert(m);
341
342         if (r < 0) {
343                 if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL)
344                         sd_bus_reply_method_errno(m, r, error);
345
346         } else if (sd_bus_error_is_set(error)) {
347                 if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL)
348                         sd_bus_reply_method_error(m, error);
349         } else
350                 return r;
351
352         log_debug("Failed to process message type=%s sender=%s destination=%s path=%s interface=%s member=%s cookie=%" PRIu64 " reply_cookie=%" PRIu64 " signature=%s error-name=%s error-message=%s: %s",
353                   bus_message_type_to_string(m->header->type),
354                   strna(sd_bus_message_get_sender(m)),
355                   strna(sd_bus_message_get_destination(m)),
356                   strna(sd_bus_message_get_path(m)),
357                   strna(sd_bus_message_get_interface(m)),
358                   strna(sd_bus_message_get_member(m)),
359                   BUS_MESSAGE_COOKIE(m),
360                   m->reply_cookie,
361                   strna(m->root_container.signature),
362                   strna(m->error.name),
363                   strna(m->error.message),
364                   bus_error_message(error, r));
365
366         return 1;
367 }