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.empty() && 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.empty()
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 return path.substr(p);
85 }
86 return path;
87}
88
89inline std::string dirname(const std::string &path)
90{
91 const size_t pos = path.find_last_of(dirsep);
92 if (pos != std::string::npos)
93 {
94 if (pos == 0)
95 return "/";
96 return path.substr(0, pos);
97 }
98 return "";
99}
100
101// return true if path is a regular file that doesn't try to traverse via ".." or "/..."
102inline bool is_contained(const std::string &path)
103{
104 if (path.empty())
105 return false;
106 if (win_dev(path, false))
107 return false;
108 if (is_dirsep(path[0]))
109 return false;
110
111 // look for ".." in path
112 enum State
113 {
114 SEP, // immediately after separator
115 MID, // middle of dir
116 DOT_2, // looking for second '.'
117 POST_DOT_2, // after ".."
118 };
119 State state = SEP;
120 for (const auto c : path)
121 {
122 switch (state)
123 {
124 case SEP:
125 if (c == '.')
126 state = DOT_2;
127 else if (!is_dirsep(c))
128 state = MID;
129 break;
130 case MID:
131 if (is_dirsep(c))
132 state = SEP;
133 break;
134 case DOT_2:
135 if (c == '.')
136 state = POST_DOT_2;
137 else if (is_dirsep(c))
138 state = SEP;
139 else
140 state = MID;
141 break;
142 case POST_DOT_2:
143 if (is_dirsep(c))
144 return false;
145 state = MID;
146 break;
147 }
148 }
149 return state != POST_DOT_2;
150}
151
152inline std::string ext(const std::string &basename)
153{
154 const size_t pos = basename.find_last_of('.');
155 if (pos != std::string::npos)
156 {
157 const size_t p = pos + 1;
158 if (p >= basename.length())
159 return "";
160 return basename.substr(p);
161 }
162 return "";
163}
164
165inline std::string root(const std::string &basename)
166{
167 const size_t pos = basename.find_last_of('.');
168 if (pos != std::string::npos)
169 return basename.substr(0, pos);
170 return basename;
171}
172
173inline std::string join(const std::string &p1, const std::string &p2)
174{
175 if (p1.empty() || is_fully_qualified(p2))
176 return p2;
177 return string::add_trailing_copy(p1, dirsep[0]) + p2;
178}
179
180template <typename... Args>
181inline std::string join(const std::string &p1, const std::string &p2, Args... args)
182{
183 return join(join(p1, p2), args...);
184}
185
186} // namespace openvpn::path
187
188#endif // OPENVPN_COMMON_STRING_H
std::string ext(const std::string &basename)
Definition path.hpp:152
bool is_contained(const std::string &path)
Definition path.hpp:102
std::string dirname(const std::string &path)
Definition path.hpp:89
std::string root(const std::string &basename)
Definition path.hpp:165
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:173
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:126