db16129dc05ece83f1bfec9b3ae8b8308c30a377
[src/app-framework-main.git] / src / wgt.c
1 /*
2  Copyright 2015 IoT.bzh
3
4  author: José Bollo <jose.bollo@iot.bzh>
5
6  Licensed under the Apache License, Version 2.0 (the "License");
7  you may not use this file except in compliance with the License.
8  You may obtain a copy of the License at
9
10      http://www.apache.org/licenses/LICENSE-2.0
11
12  Unless required by applicable law or agreed to in writing, software
13  distributed under the License is distributed on an "AS IS" BASIS,
14  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  See the License for the specific language governing permissions and
16  limitations under the License.
17 */
18
19 #define _GNU_SOURCE
20
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <unistd.h>
24 #include <fcntl.h>
25 #include <errno.h>
26 #include <assert.h>
27 #include <limits.h>
28 #include <string.h>
29 #include <ctype.h>
30
31
32 #include "wgt.h"
33
34 struct wgt {
35         int refcount;
36         int rootfd;
37         int nrlocales;
38         char **locales;
39 };
40
41 /* a valid subpath is a relative path not looking deeper than root using .. */
42 static int validsubpath(const char *subpath)
43 {
44         int l = 0, i = 0;
45         if (subpath[i] == '/')
46                 return 0;
47         while(subpath[i]) {
48                 switch(subpath[i++]) {
49                 case '.':
50                         if (!subpath[i])
51                                 break;
52                         if (subpath[i] == '/') {
53                                 i++;
54                                 break;
55                         }
56                         if (subpath[i++] == '.') {
57                                 if (!subpath[i]) {
58                                         l--;
59                                         break;
60                                 }
61                                 if (subpath[i++] == '/') {
62                                         l--;
63                                         break;
64                                 }
65                         }
66                 default:
67                         while(subpath[i] && subpath[i] != '/')
68                                 i++;
69                         if (l >= 0)
70                                 l++;
71                 case '/':
72                         break;
73                 }
74         }
75         return l >= 0;
76 }
77
78 static const char *normalsubpath(const char *subpath)
79 {
80         while(*subpath == '/')
81                 subpath++;
82         return validsubpath(subpath) ? subpath : NULL;
83 }
84
85 struct wgt *wgt_create()
86 {
87         struct wgt *wgt = malloc(sizeof * wgt);
88         if (!wgt)
89                 errno = ENOMEM;
90         else {
91                 wgt->refcount = 1;
92                 wgt->rootfd = -1;
93                 wgt->nrlocales = 0;
94                 wgt->locales = NULL;
95         }
96         return wgt;
97 }
98
99 void wgt_disconnect(struct wgt *wgt)
100 {
101         assert(wgt);
102         if (wgt->rootfd >= 0)
103                 close(wgt->rootfd);
104         wgt->rootfd = -1;
105 }
106
107 void wgt_locales_reset(struct wgt *wgt)
108 {
109         assert(wgt);
110         while(wgt->nrlocales)
111                 free(wgt->locales[--wgt->nrlocales]);
112 }
113
114 void wgt_addref(struct wgt *wgt)
115 {
116         assert(wgt);
117         wgt->refcount++;
118 }
119
120 void wgt_unref(struct wgt *wgt)
121 {
122         assert(wgt);
123         if (!--wgt->refcount) {
124                 wgt_disconnect(wgt);
125                 wgt_locales_reset(wgt);
126                 free(wgt->locales);
127                 free(wgt);
128         }
129 }
130
131 int wgt_connectat(struct wgt *wgt, int dirfd, const char *pathname)
132 {
133         int rfd;
134
135         assert(wgt);
136
137         rfd = (pathname && *pathname) ? openat(dirfd, pathname, O_PATH|O_DIRECTORY) : dup(dirfd);
138         if (rfd < 0)
139                 return rfd;
140
141         if (wgt->rootfd >= 0)
142                 close(wgt->rootfd);
143         wgt->rootfd = rfd;
144         return 0;
145 }
146
147 int wgt_connect(struct wgt *wgt, const char *pathname)
148 {
149         return wgt_connectat(wgt, AT_FDCWD, pathname);
150 }
151
152 struct wgt *wgt_createat(int dirfd, const char *pathname)
153 {
154         struct wgt *wgt = wgt_create();
155         if (wgt) {
156                 if (wgt_connectat(wgt, dirfd, pathname)) {
157                         wgt_unref(wgt);
158                         wgt = NULL;
159                 }
160         }
161         return wgt;
162 }
163
164 int wgt_is_connected(struct wgt *wgt)
165 {
166         assert(wgt);
167         return wgt->rootfd != -1;
168 }
169
170 int wgt_has(struct wgt *wgt, const char *filename)
171 {
172         assert(wgt);
173         assert(wgt_is_connected(wgt));
174
175         filename = normalsubpath(filename);
176         if (!filename) {
177                 errno = EINVAL;
178                 return -1;
179         }
180         return 0 == faccessat(wgt->rootfd, filename, F_OK, 0);
181 }
182
183 int wgt_open_read(struct wgt *wgt, const char *filename)
184 {
185         assert(wgt);
186         assert(wgt_is_connected(wgt));
187         filename = normalsubpath(filename);
188         if (!filename) {
189                 errno = EINVAL;
190                 return -1;
191         }
192         return openat(wgt->rootfd, filename, O_RDONLY);
193 }
194
195 static int locadd(struct wgt *wgt, const char *locstr, int length)
196 {
197         int i;
198         char *item, **ptr;
199
200         item = strndup(locstr, length);
201         if (item != NULL) {
202                 for (i = 0 ; item[i] ; i++)
203                         item[i] = tolower(item[i]);
204                 for (i = 0 ; i < wgt->nrlocales ; i++)
205                         if (!strcmp(item, wgt->locales[i])) {
206                                 free(item);
207                                 return 0;
208                         }
209
210                 ptr = realloc(wgt->locales, (1 + wgt->nrlocales) * sizeof(wgt->locales[0]));
211                 if (ptr) {
212                         wgt->locales = ptr;
213                         wgt->locales[wgt->nrlocales++] = item;
214                         return 0;
215                 }
216                 free(item);
217         }
218         errno = ENOMEM;
219         return -1;
220 }
221
222 int wgt_locales_add(struct wgt *wgt, const char *locstr)
223 {
224         const char *stop, *next;
225         assert(wgt);
226         while (*locstr) {
227                 stop = strchrnul(locstr, ',');
228                 next = stop + !!*stop;
229                 while (locstr != stop) {
230                         if (locadd(wgt, locstr, stop - locstr))
231                                 return -1;
232                         do { stop--; } while(stop > locstr && *stop != '-');
233                 }
234                 locstr = next;
235         }
236         return 0;
237 }
238
239 int wgt_locales_score(struct wgt *wgt, const char *lang)
240 {
241         int i;
242
243         assert(wgt);
244         if (lang)
245                 for (i = 0 ; i < wgt->nrlocales ; i++)
246                         if (!strcasecmp(lang, wgt->locales[i]))
247                                 return i;
248
249         return INT_MAX;
250 }
251
252 static const char *localize(struct wgt *wgt, const char *filename, char path[PATH_MAX])
253 {
254         int i;
255
256         filename = normalsubpath(filename);
257         if (!filename) {
258                 errno = EINVAL;
259                 return NULL;
260         }
261         for (i = 0 ; i < wgt->nrlocales ; i++) {
262                 if (snprintf(path, PATH_MAX, "locales/%s/%s", wgt->locales[i], filename) >= PATH_MAX) {
263                         errno = EINVAL;
264                         return NULL;
265                 }
266                 if (0 == faccessat(wgt->rootfd, path, F_OK, 0))
267                         return path;
268         }
269         if (0 == faccessat(wgt->rootfd, filename, F_OK, 0))
270                 return filename;
271         errno = ENOENT;
272         return NULL;
273 }
274
275 char *wgt_locales_locate(struct wgt *wgt, const char *filename)
276 {
277         char path[PATH_MAX];
278         char * result;
279         const char * loc;
280
281         assert(wgt);
282         assert(wgt_is_connected(wgt));
283         loc = localize(wgt, filename, path);
284         if (!loc)
285                 result = NULL;
286         else {
287                 result = strdup(loc);
288                 if (!result)
289                         errno = ENOMEM;
290         }
291         return result;
292 }
293
294
295 int wgt_locales_open_read(struct wgt *wgt, const char *filename)
296 {
297         char path[PATH_MAX];
298         const char *loc;
299
300         assert(wgt);
301         assert(wgt_is_connected(wgt));
302         loc = localize(wgt, filename, path);
303         if (!loc)
304                 return -1;
305
306         return openat(wgt->rootfd, loc, O_RDONLY);
307 }
308
309
310 #if defined(TEST_wgt_validsubpath)
311 #include <stdio.h>
312 void t(const char *subpath, int validity) {
313   printf("%s -> %d = %d, %s\n", subpath, validity, validsubpath(subpath), validsubpath(subpath)==validity ? "ok" : "NOT OK");
314 }
315 int main() {
316   t("/",0);
317   t("..",0);
318   t(".",1);
319   t("../a",0);
320   t("a/..",1);
321   t("a/../////..",0);
322   t("a/../b/..",1);
323   t("a/b/c/..",1);
324   t("a/b/c/../..",1);
325   t("a/b/c/../../..",1);
326   t("a/b/c/../../../.",1);
327   t("./..a/././..b/..c/./.././.././../.",1);
328   t("./..a/././..b/..c/./.././.././.././..",0);
329   t("./..a//.//./..b/..c/./.././/./././///.././.././a/a/a/a/a",1);
330   return 0;
331 }
332 #endif
333