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