/src/p11-kit/common/path.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2005 Stefan Walter |
3 | | * Copyright (c) 2011 Collabora Ltd. |
4 | | * Copyright (c) 2013 Red Hat Inc. |
5 | | * |
6 | | * Redistribution and use in source and binary forms, with or without |
7 | | * modification, are permitted provided that the following conditions |
8 | | * are met: |
9 | | * |
10 | | * * Redistributions of source code must retain the above |
11 | | * copyright notice, this list of conditions and the |
12 | | * following disclaimer. |
13 | | * * Redistributions in binary form must reproduce the |
14 | | * above copyright notice, this list of conditions and |
15 | | * the following disclaimer in the documentation and/or |
16 | | * other materials provided with the distribution. |
17 | | * * The names of contributors to this software may not be |
18 | | * used to endorse or promote products derived from this |
19 | | * software without specific prior written permission. |
20 | | * |
21 | | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
22 | | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
23 | | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
24 | | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
25 | | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
26 | | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
27 | | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS |
28 | | * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
29 | | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
30 | | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF |
31 | | * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
32 | | * DAMAGE. |
33 | | * |
34 | | * |
35 | | * CONTRIBUTORS |
36 | | * Stef Walter <stefw@redhat.com> |
37 | | */ |
38 | | |
39 | | #include "config.h" |
40 | | |
41 | | #include "buffer.h" |
42 | | #include "debug.h" |
43 | | #include "message.h" |
44 | | #include "path.h" |
45 | | #include "url.h" |
46 | | |
47 | | #include <assert.h> |
48 | | #include <errno.h> |
49 | | #include <stdarg.h> |
50 | | #include <stdlib.h> |
51 | | #include <string.h> |
52 | | |
53 | | #ifdef OS_UNIX |
54 | | #include <pwd.h> |
55 | | #include <unistd.h> |
56 | | #endif |
57 | | |
58 | | #ifdef OS_WIN32 |
59 | | #include <shlobj.h> |
60 | | #endif |
61 | | |
62 | | |
63 | | char * |
64 | | p11_path_base (const char *path) |
65 | 0 | { |
66 | | #ifdef OS_WIN32 |
67 | | const char *delims = "/\\"; |
68 | | #else |
69 | 0 | const char *delims = "/"; |
70 | 0 | #endif |
71 | |
|
72 | 0 | const char *end; |
73 | 0 | const char *beg; |
74 | |
|
75 | 0 | return_val_if_fail (path != NULL, NULL); |
76 | | |
77 | | /* Any trailing slashes */ |
78 | 0 | end = path + strlen (path); |
79 | 0 | while (end != path) { |
80 | 0 | if (!strchr (delims, *(end - 1))) |
81 | 0 | break; |
82 | 0 | end--; |
83 | 0 | } |
84 | | |
85 | | /* Find the last slash after those */ |
86 | 0 | beg = end; |
87 | 0 | while (beg != path) { |
88 | 0 | if (strchr (delims, *(beg - 1))) |
89 | 0 | break; |
90 | 0 | beg--; |
91 | 0 | } |
92 | |
|
93 | 0 | return strndup (beg, end - beg); |
94 | 0 | } |
95 | | |
96 | | static inline bool |
97 | | is_path_separator (char ch) |
98 | 0 | { |
99 | 0 | return (ch == '/' |
100 | | #ifdef OS_WIN32 |
101 | | || ch == '\\' |
102 | | #endif |
103 | 0 | ); |
104 | 0 | } |
105 | | |
106 | | static inline bool |
107 | | is_path_separator_or_null (char ch) |
108 | 0 | { |
109 | 0 | return is_path_separator (ch) || ch == '\0'; |
110 | 0 | } |
111 | | |
112 | | static char * |
113 | | expand_homedir (const char *remainder) |
114 | 0 | { |
115 | 0 | const char *env; |
116 | |
|
117 | 0 | if (getauxval (AT_SECURE)) { |
118 | 0 | errno = EPERM; |
119 | 0 | return NULL; |
120 | 0 | } |
121 | | |
122 | 0 | while (is_path_separator (remainder[0])) |
123 | 0 | remainder++; |
124 | 0 | if (remainder[0] == '\0') |
125 | 0 | remainder = NULL; |
126 | | |
127 | | /* Expand $XDG_CONFIG_HOME */ |
128 | 0 | if (remainder != NULL && |
129 | 0 | strncmp (remainder, ".config", 7) == 0 && |
130 | 0 | is_path_separator_or_null (remainder[7])) { |
131 | 0 | env = getenv ("XDG_CONFIG_HOME"); |
132 | 0 | if (env && env[0]) |
133 | 0 | return p11_path_build (env, remainder + 8, NULL); |
134 | 0 | } |
135 | | |
136 | 0 | env = getenv ("HOME"); |
137 | 0 | if (env && env[0]) { |
138 | 0 | return p11_path_build (env, remainder, NULL); |
139 | |
|
140 | 0 | } else { |
141 | 0 | #ifdef OS_UNIX |
142 | 0 | char buf[1024]; |
143 | 0 | struct passwd pws; |
144 | 0 | struct passwd *pwd = NULL; |
145 | 0 | int error; |
146 | 0 | int ret; |
147 | |
|
148 | 0 | errno = 0; |
149 | 0 | ret = getpwuid_r (getuid (), &pws, buf, sizeof (buf), &pwd); |
150 | 0 | if (pwd == NULL) { |
151 | 0 | if (ret == 0) |
152 | 0 | error = ESRCH; |
153 | 0 | else |
154 | 0 | error = errno; |
155 | 0 | p11_message_err (error, "couldn't lookup home directory for user %d", getuid ()); |
156 | 0 | errno = error; |
157 | 0 | return NULL; |
158 | 0 | } |
159 | | |
160 | 0 | return p11_path_build (pwd->pw_dir, remainder, NULL); |
161 | |
|
162 | | #else /* OS_WIN32 */ |
163 | | char directory[MAX_PATH + 1]; |
164 | | |
165 | | if (!SHGetSpecialFolderPathA (NULL, directory, CSIDL_PROFILE, TRUE)) { |
166 | | p11_message ("couldn't lookup home directory for user"); |
167 | | errno = ENOTDIR; |
168 | | return NULL; |
169 | | } |
170 | | |
171 | | return p11_path_build (directory, remainder, NULL); |
172 | | |
173 | | #endif /* OS_WIN32 */ |
174 | 0 | } |
175 | 0 | } |
176 | | |
177 | | char * |
178 | | p11_path_expand (const char *path) |
179 | 0 | { |
180 | 0 | return_val_if_fail (path != NULL, NULL); |
181 | | |
182 | 0 | if (strncmp (path, "~", 1) == 0 && |
183 | 0 | is_path_separator_or_null (path[1])) { |
184 | 0 | return expand_homedir (path + 1); |
185 | |
|
186 | 0 | } else { |
187 | 0 | return strdup (path); |
188 | 0 | } |
189 | 0 | } |
190 | | |
191 | | bool |
192 | | p11_path_absolute (const char *path) |
193 | 0 | { |
194 | 0 | return_val_if_fail (path != NULL, false); |
195 | | |
196 | 0 | return (path[0] == '/') |
197 | | #ifdef OS_WIN32 |
198 | | || (path[0] != '\0' && path[1] == ':' && path[2] == '\\') |
199 | | #endif |
200 | 0 | ; |
201 | 0 | } |
202 | | |
203 | | char * |
204 | | p11_path_build (const char *path, |
205 | | ...) |
206 | 0 | { |
207 | | #ifdef OS_WIN32 |
208 | | const char delim = '\\'; |
209 | | #else |
210 | 0 | const char delim = '/'; |
211 | 0 | #endif |
212 | 0 | const char *first = path; |
213 | 0 | char *built; |
214 | 0 | size_t len; |
215 | 0 | size_t at; |
216 | 0 | size_t num; |
217 | 0 | size_t until; |
218 | 0 | va_list va; |
219 | |
|
220 | 0 | return_val_if_fail (path != NULL, NULL); |
221 | | |
222 | 0 | len = 1; |
223 | 0 | va_start (va, path); |
224 | 0 | while (path != NULL) { |
225 | 0 | size_t old_len = len; |
226 | 0 | len += strlen (path) + 1; |
227 | 0 | if (len < old_len) { |
228 | 0 | va_end (va); |
229 | 0 | return_val_if_reached (NULL); |
230 | 0 | } |
231 | 0 | path = va_arg (va, const char *); |
232 | 0 | } |
233 | 0 | va_end (va); |
234 | |
|
235 | 0 | built = malloc (len + 1); |
236 | 0 | return_val_if_fail (built != NULL, NULL); |
237 | | |
238 | 0 | at = 0; |
239 | 0 | path = first; |
240 | 0 | va_start (va, path); |
241 | 0 | while (path != NULL) { |
242 | 0 | num = strlen (path); |
243 | | |
244 | | /* Trim beginning of path */ |
245 | 0 | while (is_path_separator (path[0])) { |
246 | | /* But preserve the leading path component */ |
247 | 0 | if (!at && !is_path_separator (path[1])) |
248 | 0 | break; |
249 | 0 | path++; |
250 | 0 | num--; |
251 | 0 | } |
252 | | |
253 | | /* Trim end of the path */ |
254 | 0 | until = (at > 0) ? 0 : 1; |
255 | 0 | while (num > until && is_path_separator_or_null (path[num - 1])) |
256 | 0 | num--; |
257 | |
|
258 | 0 | if (at != 0) { |
259 | 0 | if (num == 0) { |
260 | 0 | path = va_arg (va, const char *); |
261 | 0 | continue; |
262 | 0 | } |
263 | 0 | if (built[at - 1] != delim) |
264 | 0 | built[at++] = delim; |
265 | 0 | } |
266 | | |
267 | 0 | assert (at + num < len); |
268 | 0 | memcpy (built + at, path, num); |
269 | 0 | at += num; |
270 | |
|
271 | 0 | path = va_arg (va, const char *); |
272 | 0 | } |
273 | 0 | va_end (va); |
274 | |
|
275 | 0 | assert (at < len); |
276 | 0 | built[at] = '\0'; |
277 | 0 | return built; |
278 | 0 | } |
279 | | |
280 | | char * |
281 | | p11_path_parent (const char *path) |
282 | 0 | { |
283 | 0 | const char *e; |
284 | 0 | char *parent; |
285 | 0 | bool had = false; |
286 | |
|
287 | 0 | return_val_if_fail (path != NULL, NULL); |
288 | | |
289 | | /* Find the end of the last component */ |
290 | 0 | e = path + strlen (path); |
291 | 0 | while (e != path && is_path_separator_or_null (*e)) |
292 | 0 | e--; |
293 | | |
294 | | /* Find the beginning of the last component */ |
295 | 0 | while (e != path && !is_path_separator_or_null (*e)) { |
296 | 0 | had = true; |
297 | 0 | e--; |
298 | 0 | } |
299 | | |
300 | | /* Find the end of the last component */ |
301 | 0 | while (e != path && is_path_separator_or_null (*e)) |
302 | 0 | e--; |
303 | |
|
304 | 0 | if (e == path) { |
305 | 0 | if (!had) |
306 | 0 | return NULL; |
307 | 0 | parent = strdup ("/"); |
308 | 0 | } else { |
309 | 0 | parent = strndup (path, (e - path) + 1); |
310 | 0 | } |
311 | | |
312 | 0 | return_val_if_fail (parent != NULL, NULL); |
313 | 0 | return parent; |
314 | 0 | } |
315 | | |
316 | | bool |
317 | | p11_path_prefix (const char *string, |
318 | | const char *prefix) |
319 | 0 | { |
320 | 0 | int a, b; |
321 | |
|
322 | 0 | return_val_if_fail (string != NULL, false); |
323 | 0 | return_val_if_fail (prefix != NULL, false); |
324 | | |
325 | 0 | a = strlen (string); |
326 | 0 | b = strlen (prefix); |
327 | |
|
328 | 0 | return a > b && |
329 | 0 | strncmp (string, prefix, b) == 0 && |
330 | 0 | is_path_separator_or_null (string[b]); |
331 | 0 | } |
332 | | |
333 | | void |
334 | | p11_path_canon (char *name) |
335 | 0 | { |
336 | 0 | static const char *VALID = |
337 | 0 | "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_"; |
338 | 0 | int i; |
339 | |
|
340 | 0 | return_if_fail (name != NULL); |
341 | | |
342 | 0 | for (i = 0; name[i] != '\0'; i++) { |
343 | 0 | if (strchr (VALID, name[i]) == NULL) |
344 | 0 | name[i] = '_'; |
345 | 0 | } |
346 | 0 | } |
347 | | |
348 | | char * |
349 | | p11_path_encode (const char *path) |
350 | 0 | { |
351 | 0 | static const char *VALID = |
352 | 0 | "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_/\\"; |
353 | 0 | p11_buffer buf; |
354 | 0 | char *result; |
355 | |
|
356 | 0 | return_val_if_fail (path != NULL, NULL); |
357 | | |
358 | 0 | if (!p11_buffer_init_null (&buf, strlen (path))) |
359 | 0 | return_val_if_reached (NULL); |
360 | | |
361 | 0 | p11_url_encode ((unsigned char *)path, |
362 | 0 | (unsigned char *)path + strlen (path), |
363 | 0 | VALID, |
364 | 0 | &buf); |
365 | 0 | return_val_if_fail (p11_buffer_ok (&buf), NULL); |
366 | | |
367 | 0 | result = p11_buffer_steal (&buf, NULL); |
368 | 0 | p11_buffer_uninit (&buf); |
369 | |
|
370 | 0 | return result; |
371 | 0 | } |
372 | | |
373 | | char * |
374 | | p11_path_decode (const char *path) |
375 | 0 | { |
376 | 0 | return (char *) p11_url_decode (path, path + strlen (path), "", NULL); |
377 | 0 | } |