OpenVPN 3 Core Library
Loading...
Searching...
No Matches
path.hpp
Go to the documentation of this file.
1// OpenVPN -- An application to securely tunnel IP networks
2// over a single port, with support for SSL/TLS-based
3// session authentication and key exchange,
4// packet encryption, packet authentication, and
5// packet compression.
6//
7// Copyright (C) 2012- OpenVPN Inc.
8//
9// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
10//
11
12// General-purpose methods for handling filesystem pathnames
13
14#ifndef OPENVPN_COMMON_PATH_H
15#define OPENVPN_COMMON_PATH_H
16
17#include <string>
18
22
23namespace openvpn::path {
24
25// Directory separators. The first char in dirsep is the primary
26// separator for the platform, while subsequent chars are also
27// recognized as separators.
28namespace {
29#if defined(OPENVPN_PLATFORM_WIN) || defined(OPENVPN_PATH_SIMULATE_WINDOWS)
30// Windows
31const char dirsep[] = "\\/"; // CONST GLOBAL
32#else
33// Unix
34const char dirsep[] = "/\\"; // CONST GLOBAL
35#endif
36} // namespace
37
38// true if char is a directory separator
39inline bool is_dirsep(const char c)
40{
41 for (const char *p = dirsep; *p != '\0'; ++p)
42 if (c == *p)
43 return true;
44 return false;
45}
46
47inline bool win_dev(const std::string &path, const bool fully_qualified)
48{
49#if defined(OPENVPN_PLATFORM_WIN) || defined(OPENVPN_PATH_SIMULATE_WINDOWS)
50 // Identify usage such as "c:\\".
51 return path.length() >= 3
52 && ((path[0] >= 'a' && path[0] <= 'z') || (path[0] >= 'A' && path[0] <= 'Z'))
53 && path[1] == ':'
54 && (!fully_qualified || is_dirsep(path[2]));
55#else
56 return false;
57#endif
58}
59
60// true if path is fully qualified
61inline bool is_fully_qualified(const std::string &path)
62{
63 return win_dev(path, true) || (path.length() > 0 && is_dirsep(path[0]));
64}
65
66// does path refer to regular file without directory traversal
67inline bool is_flat(const std::string &path)
68{
69 return path.length() > 0
70 && path != "."
71 && path != ".."
72 && path.find_first_of(dirsep) == std::string::npos
73 && !win_dev(path, false);
74}
75
76inline std::string basename(const std::string &path)
77{
78 const size_t pos = path.find_last_of(dirsep);
79 if (pos != std::string::npos)
80 {
81 const size_t p = pos + 1;
82 if (p >= path.length())
83 return "";
84 else
85 return path.substr(p);
86 }
87 else
88 return path;
89}
90
91inline std::string dirname(const std::string &path)
92{
93 const size_t pos = path.find_last_of(dirsep);
94 if (pos != std::string::npos)
95 {
96 if (pos == 0)
97 return "/";
98 else
99 return path.substr(0, pos);
100 }
101 else
102 return "";
103}
104
105// return true if path is a regular file that doesn't try to traverse via ".." or "/..."
106inline bool is_contained(const std::string &path)
107{
108 if (path.empty())
109 return false;
110 if (win_dev(path, false))
111 return false;
112 if (is_dirsep(path[0]))
113 return false;
114
115 // look for ".." in path
116 enum State
117 {
118 SEP, // immediately after separator
119 MID, // middle of dir
120 DOT_2, // looking for second '.'
121 POST_DOT_2, // after ".."
122 };
123 State state = SEP;
124 for (const auto c : path)
125 {
126 switch (state)
127 {
128 case SEP:
129 if (c == '.')
130 state = DOT_2;
131 else if (!is_dirsep(c))
132 state = MID;
133 break;
134 case MID:
135 if (is_dirsep(c))
136 state = SEP;
137 break;
138 case DOT_2:
139 if (c == '.')
140 state = POST_DOT_2;
141 else if (is_dirsep(c))
142 state = SEP;
143 else
144 state = MID;
145 break;
146 case POST_DOT_2:
147 if (is_dirsep(c))
148 return false;
149 state = MID;
150 break;
151 }
152 }
153 return state != POST_DOT_2;
154}
155
156inline std::string ext(const std::string &basename)
157{
158 const size_t pos = basename.find_last_of('.');
159 if (pos != std::string::npos)
160 {
161 const size_t p = pos + 1;
162 if (p >= basename.length())
163 return "";
164 else
165 return basename.substr(p);
166 }
167 else
168 return "";
169}
170
171inline std::string root(const std::string &basename)
172{
173 const size_t pos = basename.find_last_of('.');
174 if (pos != std::string::npos)
175 return basename.substr(0, pos);
176 else
177 return basename;
178}
179
180inline std::string join(const std::string &p1, const std::string &p2)
181{
182 if (p1.empty() || is_fully_qualified(p2))
183 return p2;
184 else
185 return string::add_trailing_copy(p1, dirsep[0]) + p2;
186}
187
188template <typename... Args>
189inline std::string join(const std::string &p1, const std::string &p2, Args... args)
190{
191 return join(join(p1, p2), args...);
192}
193
194} // namespace openvpn::path
195
196#endif // OPENVPN_COMMON_STRING_H
std::string ext(const std::string &basename)
Definition path.hpp:156
bool is_contained(const std::string &path)
Definition path.hpp:106
std::string dirname(const std::string &path)
Definition path.hpp:91
std::string root(const std::string &basename)
Definition path.hpp:171
bool win_dev(const std::string &path, const bool fully_qualified)
Definition path.hpp:47
std::string basename(const std::string &path)
Definition path.hpp:76
std::string join(const std::string &p1, const std::string &p2)
Definition path.hpp:180
bool is_fully_qualified(const std::string &path)
Definition path.hpp:61
bool is_dirsep(const char c)
Definition path.hpp:39
bool is_flat(const std::string &path)
Definition path.hpp:67
std::string add_trailing_copy(const std::string &str, const char c)
Definition string.hpp:175