KiCad PCB EDA Suite
Loading...
Searching...
No Matches
wx_filename.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <wx_filename.h>
21#include <string_utils.h>
22
23#include <wx/arrstr.h>
24
25
26WX_FILENAME::WX_FILENAME( const wxString& aPath, const wxString& aFilename )
27 : m_fn( aPath, aFilename ), m_path( aPath ), m_fullName( aFilename )
28{
29}
30
31
32void WX_FILENAME::SetFullName( const wxString& aFileNameAndExtension )
33{
34 m_fullName = aFileNameAndExtension;
35}
36
37
38void WX_FILENAME::SetPath( const wxString& aPath )
39{
40 m_fn.SetPath( aPath );
41 m_path = aPath;
42}
43
44
45wxString WX_FILENAME::GetName() const
46{
47 size_t dot = m_fullName.find_last_of( wxT( '.' ) );
48 return m_fullName.substr( 0, dot );
49}
50
51
53{
54 return m_fullName;
55}
56
57
58wxString WX_FILENAME::GetPath() const
59{
60 return m_path;
61}
62
63
65{
66 return m_path + wxT( '/' ) + m_fullName;
67}
68
69
71{
72 size_t dot = m_fullName.find_last_of( wxT( '.' ) );
73 m_fn.SetName( m_fullName.substr( 0, dot ) );
74 m_fn.SetExt( m_fullName.substr( dot + 1 ) );
75}
76
77
79{
80 resolve();
81
82 if( m_fn.FileExists() )
83 return m_fn.GetModificationTime().GetValue().GetValue();
84
85 return 0;
86}
87
88
89void WX_FILENAME::ResolvePossibleSymlinks( wxFileName& aFilename )
90{
91#ifndef __WINDOWS__
92 if( aFilename.Exists( wxFILE_EXISTS_SYMLINK ) )
93 {
94 char buffer[PATH_MAX];
95 char* realPath = realpath( TO_UTF8( aFilename.GetFullPath() ), buffer );
96
97 if( realPath )
98 aFilename.Assign( wxString::FromUTF8( realPath ) );
99 }
100#endif
101}
102
103
104static bool splitSafeRelativePath( const wxString& aPath, wxArrayString& aParts )
105{
106 aParts.Clear();
107
108 if( aPath.IsEmpty() )
109 return false;
110
111 // A NUL is truncated by the OS: what gets validated would not be what gets written.
112 if( aPath.find( wxUniChar( 0 ) ) != wxString::npos )
113 return false;
114
115 // A ZIP written on Windows can carry backslashes, normalize
116 wxString name = aPath;
117 name.Replace( wxT( "\\" ), wxT( "/" ) );
118
119 // Archive members are relative. Check DOS volumes everywhere so POSIX rejects "C:/evil".
120 if( name.StartsWith( wxT( "/" ) ) )
121 return false;
122
123 wxString volume;
124
125 wxFileName::SplitVolume( name, &volume, nullptr, wxPATH_DOS );
126
127 if( !volume.IsEmpty() )
128 return false;
129
130 for( const wxString& part : wxSplit( name, '/', (wxChar) 0 ) )
131 {
132 // Doubled or trailing separators and "." do not change where the entry lands.
133 if( part.IsEmpty() || part == wxT( "." ) )
134 continue;
135
136 if( part == wxT( ".." ) )
137 return false;
138
139#ifdef _WIN32
140 // ':' opens an alternate data stream, and Win32 folds "evil. " onto "evil".
141 if( part.Contains( wxT( ":" ) ) || part.EndsWith( wxT( "." ) ) || part.EndsWith( wxT( " " ) ) )
142 {
143 return false;
144 }
145#endif
146
147 aParts.Add( part );
148 }
149
150 return !aParts.IsEmpty();
151}
152
153
154bool WX_FILENAME::SplitArchiveEntryName( const wxString& aEntryName, wxArrayString& aParts )
155{
156 return splitSafeRelativePath( aEntryName, aParts );
157}
158
159
160bool WX_FILENAME::IsSafeChildPath( const wxString& aChild )
161{
162 wxArrayString parts;
163
164 return splitSafeRelativePath( aChild, parts ) && parts.GetCount() == 1 && parts[0] == aChild;
165}
166
167
168bool WX_FILENAME::ResolveArchiveEntryPath( const wxString& aDestDir, const wxString& aEntryName, wxFileName& aResult )
169{
170 wxArrayString parts;
171
172 if( !SplitArchiveEntryName( aEntryName, parts ) )
173 return false;
174
175 wxFileName dest = wxFileName::DirName( aDestDir );
176
177 // Not FN_NORMALIZE_FLAGS: expanding "~" or shell shortcuts here would reopen the escape.
178 dest.Normalize( wxPATH_NORM_DOTS | wxPATH_NORM_ABSOLUTE );
179
180 wxFileName target = dest;
181
182 for( size_t ii = 0; ii + 1 < parts.GetCount(); ++ii )
183 target.AppendDir( parts[ii] );
184
185 target.SetFullName( parts.Last() );
186 target.Normalize( wxPATH_NORM_DOTS | wxPATH_NORM_ABSOLUTE );
187
188 // ".." is already rejected, but double check the path is below the dest dir
189 const wxString destPath = dest.GetPathWithSep();
190 const wxString fullPath = target.GetFullPath();
191
192 if( fullPath.length() <= destPath.length() )
193 return false;
194
195 if( wxFileName::IsCaseSensitive() )
196 {
197 if( !fullPath.StartsWith( destPath ) )
198 return false;
199 }
200 else if( !fullPath.Lower().StartsWith( destPath.Lower() ) )
201 {
202 return false;
203 }
204
205 aResult = target;
206 return true;
207}
const char * name
WX_FILENAME(const wxString &aPath, const wxString &aFilename)
void SetFullName(const wxString &aFileNameAndExtension)
static void ResolvePossibleSymlinks(wxFileName &aFilename)
wxString GetPath() const
void SetPath(const wxString &aPath)
wxString m_path
wxFileName m_fn
wxString GetName() const
static bool IsSafeChildPath(const wxString &aChild)
Check that a child path is safe to append to a parent directory.
static bool ResolveArchiveEntryPath(const wxString &aDestDir, const wxString &aEntryName, wxFileName &aResult)
Resolve an untrusted archive entry name against the directory it is extracted into.
static bool SplitArchiveEntryName(const wxString &aEntryName, wxArrayString &aParts)
Split an untrusted archive entry name into its path components.
wxString GetFullPath() const
long long GetTimestamp()
wxString GetFullName() const
wxString m_fullName
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
static bool splitSafeRelativePath(const wxString &aPath, wxArrayString &aParts)