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