1 //
2 // VMime library (http://www.vmime.org)
3 // Copyright (C) 2002-2013 Vincent Richard <vincent@vmime.org>
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License as
7 // published by the Free Software Foundation; either version 3 of
8 // the License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License along
16 // with this program; if not, write to the Free Software Foundation, Inc.,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 //
19 // Linking this library statically or dynamically with other modules is making
20 // a combined work based on this library. Thus, the terms and conditions of
21 // the GNU General Public License cover the whole combination.
22 //
23
24 #include "vmime/config.hpp"
25
26
27 #if VMIME_PLATFORM_IS_WINDOWS && VMIME_HAVE_FILESYSTEM_FEATURES
28
29
30 #include "vmime/platforms/windows/windowsFile.hpp"
31
32 #include <windows.h>
33 #include <string.h>
34
35 #include "vmime/exception.hpp"
36 #include "vmime/utility/stringUtils.hpp"
37
38
39 namespace vmime {
40 namespace platforms {
41 namespace windows {
42
43
create(const vmime::utility::file::path & path) const44 shared_ptr <vmime::utility::file> windowsFileSystemFactory::create(const vmime::utility::file::path& path) const
45 {
46 return make_shared <windowsFile>(path);
47 }
48
49
stringToPath(const vmime::string & str) const50 const vmime::utility::file::path windowsFileSystemFactory::stringToPath(const vmime::string& str) const
51 {
52 return (stringToPathImpl(str));
53 }
54
55
pathToString(const vmime::utility::file::path & path) const56 const vmime::string windowsFileSystemFactory::pathToString(const vmime::utility::file::path& path) const
57 {
58 return (pathToStringImpl(path));
59 }
60
61
stringToPathImpl(const vmime::string & str)62 const vmime::utility::file::path windowsFileSystemFactory::stringToPathImpl(const vmime::string& str)
63 {
64 vmime::size_t offset = 0;
65 vmime::size_t prev = 0;
66
67 vmime::utility::file::path path;
68
69 while ((offset = str.find_first_of("\\", offset)) != vmime::string::npos)
70 {
71 if (offset != prev)
72 {
73 path.appendComponent
74 (vmime::utility::file::path::component
75 (vmime::string(str.begin() + prev, str.begin() + offset)));
76 }
77
78 prev = offset + 1;
79 offset++;
80 }
81
82 if (prev < str.length())
83 {
84 path.appendComponent
85 (vmime::utility::file::path::component
86 (vmime::string(str.begin() + prev, str.end())));
87 }
88
89 return (path);
90 }
91
92
pathToStringImpl(const vmime::utility::file::path & path)93 const vmime::string windowsFileSystemFactory::pathToStringImpl(const vmime::utility::file::path& path)
94 {
95 vmime::string native = "";
96
97 for (int i = 0 ; i < path.getSize() ; ++i)
98 {
99 if (i > 0)
100 native += "\\";
101
102 native += path[i].getBuffer();
103 }
104
105 return (native);
106 }
107
isValidPathComponent(const vmime::utility::file::path::component & comp) const108 bool windowsFileSystemFactory::isValidPathComponent(const vmime::utility::file::path::component& comp) const
109 {
110 return isValidPathComponent(comp, false);
111 }
112
isValidPathComponent(const vmime::utility::file::path::component & comp,bool firstComponent) const113 bool windowsFileSystemFactory::isValidPathComponent(
114 const vmime::utility::file::path::component& comp,
115 bool firstComponent) const
116 {
117 const string& buffer = comp.getBuffer();
118
119 // If first component, check if component is a drive
120 if (firstComponent && (buffer.length() == 2) && (buffer[1] == ':'))
121 {
122 char drive = tolower(buffer[0]);
123 if ((drive >= 'a') && (drive <= 'z'))
124 return true;
125 }
126
127 // Check for invalid characters
128 for (size_t i = 0 ; i < buffer.length() ; ++i)
129 {
130 const unsigned char c = buffer[i];
131
132 switch (c)
133 {
134 // Reserved characters
135 case '<': case '>': case ':':
136 case '"': case '/': case '\\':
137 case '|': case '$': case '*':
138
139 return false;
140
141 default:
142
143 if (c <= 31)
144 return false;
145 }
146 }
147
148 string upperBuffer = vmime::utility::stringUtils::toUpper(buffer);
149
150 // Check for reserved names
151 if (upperBuffer.length() == 3)
152 {
153 if (upperBuffer == "CON" || buffer == "PRN" || buffer == "AUX" || buffer == "NUL")
154 return false;
155 }
156 else if (upperBuffer.length() == 4)
157 {
158 if ((upperBuffer.substr(0, 3) == "COM") && // COM0 to COM9
159 (upperBuffer[3] >= '0') && (upperBuffer[3] <= '9'))
160 {
161 return false;
162 }
163 else if ((upperBuffer.substr(0, 3) == "LPT") && // LPT0 to LPT9
164 (upperBuffer[3] >= '0') && (upperBuffer[3] <= '9'))
165 {
166 return false;
167 }
168 }
169 return true;
170 }
171
172
isValidPath(const vmime::utility::file::path & path) const173 bool windowsFileSystemFactory::isValidPath(const vmime::utility::file::path& path) const
174 {
175 for (int i = 0 ; i < path.getSize() ; ++i)
176 {
177 if (!isValidPathComponent(path[i], (i==0)))
178 return false;
179 }
180
181 return true;
182 }
183
184
reportError(const vmime::utility::path & path,const int err)185 void windowsFileSystemFactory::reportError(const vmime::utility::path& path, const int err)
186 {
187 vmime::string desc;
188
189 LPVOID lpMsgBuf;
190 if (FormatMessage(
191 FORMAT_MESSAGE_ALLOCATE_BUFFER |
192 FORMAT_MESSAGE_FROM_SYSTEM |
193 FORMAT_MESSAGE_IGNORE_INSERTS,
194 NULL,
195 err,
196 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
197 (LPTSTR) &lpMsgBuf,
198 0,
199 NULL ))
200 {
201 desc = (char*)lpMsgBuf;
202 LocalFree( lpMsgBuf );
203 }
204
205 throw vmime::exceptions::filesystem_exception(desc, path);
206 }
207
windowsFile(const vmime::utility::file::path & path)208 windowsFile::windowsFile(const vmime::utility::file::path& path)
209 : m_path(path), m_nativePath(windowsFileSystemFactory::pathToStringImpl(path))
210 {
211 }
212
createFile()213 void windowsFile::createFile()
214 {
215 HANDLE hFile = CreateFile(
216 m_nativePath.c_str(),
217 GENERIC_WRITE,
218 FILE_SHARE_READ | FILE_SHARE_WRITE,
219 NULL,
220 CREATE_ALWAYS,
221 FILE_ATTRIBUTE_NORMAL,
222 NULL);
223 if (hFile == INVALID_HANDLE_VALUE)
224 windowsFileSystemFactory::reportError(m_path, GetLastError());
225
226 CloseHandle(hFile);
227 }
228
createDirectory(const bool createAll)229 void windowsFile::createDirectory(const bool createAll)
230 {
231 createDirectoryImpl(m_path, m_path, createAll);
232 }
233
isFile() const234 bool windowsFile::isFile() const
235 {
236 DWORD dwFileAttribute = GetFileAttributes(m_nativePath.c_str());
237 if (dwFileAttribute == INVALID_FILE_ATTRIBUTES)
238 return false;
239 return (dwFileAttribute & FILE_ATTRIBUTE_DIRECTORY) == 0;
240 }
241
isDirectory() const242 bool windowsFile::isDirectory() const
243 {
244 DWORD dwFileAttribute = GetFileAttributes(m_nativePath.c_str());
245 if (dwFileAttribute == INVALID_FILE_ATTRIBUTES)
246 return false;
247 return (dwFileAttribute & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY;
248 }
249
canRead() const250 bool windowsFile::canRead() const
251 {
252 HANDLE hFile = CreateFile(
253 m_nativePath.c_str(),
254 GENERIC_READ,
255 FILE_SHARE_READ,
256 NULL,
257 OPEN_EXISTING,
258 FILE_ATTRIBUTE_NORMAL,
259 NULL);
260 if (hFile == INVALID_HANDLE_VALUE)
261 return false;
262 CloseHandle(hFile);
263 return true;
264 }
265
canWrite() const266 bool windowsFile::canWrite() const
267 {
268 HANDLE hFile = CreateFile(
269 m_nativePath.c_str(),
270 GENERIC_WRITE,
271 FILE_SHARE_WRITE,
272 NULL,
273 OPEN_ALWAYS,
274 FILE_ATTRIBUTE_NORMAL,
275 NULL);
276 if (hFile == INVALID_HANDLE_VALUE)
277 return false;
278 CloseHandle(hFile);
279 return true;
280 }
281
getLength()282 windowsFile::length_type windowsFile::getLength()
283 {
284 HANDLE hFile = CreateFile(
285 m_nativePath.c_str(),
286 GENERIC_READ,
287 FILE_SHARE_READ,
288 NULL,
289 OPEN_EXISTING,
290 FILE_ATTRIBUTE_NORMAL,
291 NULL);
292 if (hFile == INVALID_HANDLE_VALUE)
293 windowsFileSystemFactory::reportError(m_path, GetLastError());
294
295 DWORD dwSize = GetFileSize(hFile, NULL);
296 CloseHandle(hFile);
297
298 return dwSize;
299 }
300
getFullPath() const301 const vmime::utility::path& windowsFile::getFullPath() const
302 {
303 return m_path;
304 }
305
exists() const306 bool windowsFile::exists() const
307 {
308 WIN32_FIND_DATA findData;
309 HANDLE hFind = FindFirstFile(m_nativePath.c_str(), &findData);
310 if (hFind != INVALID_HANDLE_VALUE)
311 {
312 FindClose(hFind);
313 return true;
314 }
315 return false;
316 }
317
getParent() const318 shared_ptr <vmime::utility::file> windowsFile::getParent() const
319 {
320 if (m_path.isEmpty())
321 return null;
322 else
323 return make_shared <windowsFile>(m_path.getParent());
324 }
325
rename(const path & newName)326 void windowsFile::rename(const path& newName)
327 {
328 const vmime::string newNativeName = windowsFileSystemFactory::pathToStringImpl(newName);
329 if (MoveFile(m_nativePath.c_str(), newNativeName.c_str()))
330 {
331 m_path = newName;
332 m_nativePath = newNativeName;
333 }
334 else
335 windowsFileSystemFactory::reportError(m_path, GetLastError());
336 }
337
remove()338 void windowsFile::remove()
339 {
340 if (!DeleteFile(m_nativePath.c_str()))
341 windowsFileSystemFactory::reportError(m_path, GetLastError());
342 }
343
getFileWriter()344 shared_ptr <vmime::utility::fileWriter> windowsFile::getFileWriter()
345 {
346 return make_shared <windowsFileWriter>(m_path, m_nativePath);
347 }
348
getFileReader()349 shared_ptr <vmime::utility::fileReader> windowsFile::getFileReader()
350 {
351 return make_shared <windowsFileReader>(m_path, m_nativePath);
352 }
353
getFiles() const354 shared_ptr <vmime::utility::fileIterator> windowsFile::getFiles() const
355 {
356 return make_shared <windowsFileIterator>(m_path, m_nativePath);
357 }
358
createDirectoryImpl(const vmime::utility::file::path & fullPath,const vmime::utility::file::path & path,const bool recursive)359 void windowsFile::createDirectoryImpl(const vmime::utility::file::path& fullPath, const vmime::utility::file::path& path, const bool recursive)
360 {
361 const vmime::string nativePath = windowsFileSystemFactory::pathToStringImpl(path);
362
363 windowsFile tmp(path);
364 if (tmp.isDirectory())
365 return;
366
367 if (!path.isEmpty() && recursive)
368 createDirectoryImpl(fullPath, path.getParent(), true);
369
370 if (!CreateDirectory(nativePath.c_str(), NULL))
371 windowsFileSystemFactory::reportError(fullPath, GetLastError());
372 }
373
windowsFileIterator(const vmime::utility::file::path & path,const vmime::string & nativePath)374 windowsFileIterator::windowsFileIterator(const vmime::utility::file::path& path, const vmime::string& nativePath)
375 : m_path(path), m_nativePath(nativePath), m_moreElements(false), m_hFind(INVALID_HANDLE_VALUE)
376 {
377 findFirst();
378 }
379
~windowsFileIterator()380 windowsFileIterator::~windowsFileIterator()
381 {
382 if (m_hFind != INVALID_HANDLE_VALUE)
383 FindClose(m_hFind);
384 }
385
hasMoreElements() const386 bool windowsFileIterator::hasMoreElements() const
387 {
388 return m_moreElements;
389 }
390
nextElement()391 shared_ptr <vmime::utility::file> windowsFileIterator::nextElement()
392 {
393 shared_ptr <vmime::utility::file> pFile = make_shared <windowsFile>
394 (m_path / vmime::utility::file::path::component(m_findData.cFileName));
395
396 findNext();
397
398 return pFile;
399 }
400
findFirst()401 void windowsFileIterator::findFirst()
402 {
403 m_hFind = FindFirstFile(m_nativePath.c_str(), &m_findData);
404 if (m_hFind == INVALID_HANDLE_VALUE)
405 {
406 m_moreElements = false;
407 return;
408 }
409
410 m_moreElements = true;
411 if (isCurrentOrParentDir())
412 findNext();
413 }
414
findNext()415 void windowsFileIterator::findNext()
416 {
417 do
418 {
419 if (!FindNextFile(m_hFind, &m_findData))
420 {
421 m_moreElements = false;
422 return;
423 }
424 }
425 while (isCurrentOrParentDir());
426 }
427
isCurrentOrParentDir() const428 bool windowsFileIterator::isCurrentOrParentDir() const
429 {
430 vmime::string s(m_findData.cFileName);
431 if ((s == ".") || (s == ".."))
432 return true;
433 return false;
434 }
435
windowsFileReader(const vmime::utility::file::path & path,const vmime::string & nativePath)436 windowsFileReader::windowsFileReader(const vmime::utility::file::path& path, const vmime::string& nativePath)
437 : m_path(path), m_nativePath(nativePath)
438 {
439 }
440
getInputStream()441 shared_ptr <vmime::utility::inputStream> windowsFileReader::getInputStream()
442 {
443 HANDLE hFile = CreateFile(
444 m_nativePath.c_str(),
445 GENERIC_READ,
446 FILE_SHARE_READ,
447 NULL,
448 OPEN_EXISTING,
449 0,
450 NULL);
451 if (hFile == INVALID_HANDLE_VALUE)
452 windowsFileSystemFactory::reportError(m_path, GetLastError());
453 return make_shared <windowsFileReaderInputStream>(m_path, hFile);
454 }
455
windowsFileReaderInputStream(const vmime::utility::file::path & path,HANDLE hFile)456 windowsFileReaderInputStream::windowsFileReaderInputStream(const vmime::utility::file::path& path, HANDLE hFile)
457 : m_path(path), m_hFile(hFile)
458 {
459 }
460
~windowsFileReaderInputStream()461 windowsFileReaderInputStream::~windowsFileReaderInputStream()
462 {
463 CloseHandle(m_hFile);
464 }
465
eof() const466 bool windowsFileReaderInputStream::eof() const
467 {
468 DWORD dwSize = GetFileSize(m_hFile, NULL);
469 DWORD dwPosition = SetFilePointer(m_hFile, 0, NULL, FILE_CURRENT);
470 return (dwSize == dwPosition);
471 }
472
reset()473 void windowsFileReaderInputStream::reset()
474 {
475 SetFilePointer(m_hFile, 0, NULL, FILE_BEGIN);
476 }
477
read(byte_t * const data,const size_t count)478 size_t windowsFileReaderInputStream::read(byte_t* const data, const size_t count)
479 {
480 DWORD dwBytesRead;
481 if (!ReadFile(m_hFile, (LPVOID)data, (DWORD)count, &dwBytesRead, NULL))
482 windowsFileSystemFactory::reportError(m_path, GetLastError());
483 return dwBytesRead;
484 }
485
skip(const size_t count)486 size_t windowsFileReaderInputStream::skip(const size_t count)
487 {
488 DWORD dwCurPos = SetFilePointer(m_hFile, 0, NULL, FILE_CURRENT);
489 DWORD dwNewPos = SetFilePointer(m_hFile, (LONG)count, NULL, FILE_CURRENT);
490 return (dwNewPos - dwCurPos);
491 }
492
getPosition() const493 size_t windowsFileReaderInputStream::getPosition() const
494 {
495 DWORD dwCurPos = SetFilePointer(m_hFile, 0, NULL, FILE_CURRENT);
496
497 if (dwCurPos == INVALID_SET_FILE_POINTER)
498 windowsFileSystemFactory::reportError(m_path, GetLastError());
499
500 return static_cast <size_t>(dwCurPos);
501 }
502
seek(const size_t pos)503 void windowsFileReaderInputStream::seek(const size_t pos)
504 {
505 DWORD dwNewPos = SetFilePointer(m_hFile, (LONG)pos, NULL, FILE_BEGIN);
506
507 if (dwNewPos == INVALID_SET_FILE_POINTER)
508 windowsFileSystemFactory::reportError(m_path, GetLastError());
509 }
510
windowsFileWriter(const vmime::utility::file::path & path,const vmime::string & nativePath)511 windowsFileWriter::windowsFileWriter(const vmime::utility::file::path& path, const vmime::string& nativePath)
512 : m_path(path), m_nativePath(nativePath)
513 {
514 }
515
getOutputStream()516 shared_ptr <vmime::utility::outputStream> windowsFileWriter::getOutputStream()
517 {
518 HANDLE hFile = CreateFile(
519 m_nativePath.c_str(),
520 GENERIC_WRITE,
521 FILE_SHARE_WRITE,
522 NULL,
523 CREATE_ALWAYS,
524 FILE_ATTRIBUTE_NORMAL,
525 NULL);
526 if (hFile == INVALID_HANDLE_VALUE)
527 windowsFileSystemFactory::reportError(m_path, GetLastError());
528 return make_shared <windowsFileWriterOutputStream>(m_path, hFile);
529 }
530
windowsFileWriterOutputStream(const vmime::utility::file::path & path,HANDLE hFile)531 windowsFileWriterOutputStream::windowsFileWriterOutputStream(const vmime::utility::file::path& path, HANDLE hFile)
532 : m_path(path), m_hFile(hFile)
533 {
534 }
535
~windowsFileWriterOutputStream()536 windowsFileWriterOutputStream::~windowsFileWriterOutputStream()
537 {
538 CloseHandle(m_hFile);
539 }
540
writeImpl(const byte_t * const data,const size_t count)541 void windowsFileWriterOutputStream::writeImpl(const byte_t* const data, const size_t count)
542 {
543 DWORD dwBytesWritten;
544 if (!WriteFile(m_hFile, data, (DWORD)count, &dwBytesWritten, NULL))
545 windowsFileSystemFactory::reportError(m_path, GetLastError());
546 }
547
548
flush()549 void windowsFileWriterOutputStream::flush()
550 {
551 // TODO
552 }
553
554
555 } // windows
556 } // platforms
557 } // vmime
558
559
560 #endif // VMIME_PLATFORM_IS_WINDOWS && VMIME_HAVE_FILESYSTEM_FEATURES
561
562