PST File Format SDK v0.4
Loading...
Searching...
No Matches
util.h
Go to the documentation of this file.
1
9
10#ifndef PSTSDK_UTIL_UTIL_H
11#define PSTSDK_UTIL_UTIL_H
12
13#include <cstdio>
14#include <cstdlib>
15#include <cstring>
16#include <cerrno>
17#include <time.h>
18#include <memory>
19#include <string>
20#include <vector>
21#include <boost/utility.hpp>
22
23#include "pstsdk/util/errors.h"
25
26#if defined(_WIN32) || defined(__MINGW32__)
27#define NOMINMAX
28#include <windows.h>
29#endif
30
31#ifdef _MSC_VER
32#include <share.h>
33#endif
34
35namespace pstsdk
36{
37
44class file : private boost::noncopyable
45{
46public:
47 // TODO
48 file() {
49 m_pfile = nullptr;
50 m_filename = L"";
51 }
52
57 file(const std::wstring& filename, bool writable = false);
58
60 ~file();
61
67 virtual size_t read(std::vector<byte>& buffer, ulonglong offset) const;
68
70
76 virtual size_t write(const std::vector<byte>& buffer, ulonglong offset);
78
79private:
80 std::wstring m_filename;
81 FILE * m_pfile;
82};
83
84
93time_t filetime_to_time_t(ulonglong filetime);
94
103ulonglong time_t_to_filetime(time_t time);
104
113time_t vt_date_to_time_t(double vt_time);
114
123double time_t_to_vt_date(time_t time);
124
130bool test_bit(const byte* pbytes, ulong bit);
131
136std::string bytes_to_string(const std::vector<byte> &bytes);
137
142std::wstring bytes_to_wstring(const std::vector<byte> &bytes);
143
148std::vector<byte> wstring_to_bytes(const std::wstring &wstr);
149
150} // end pstsdk namespace
151
152inline pstsdk::file::file(const std::wstring& filename, bool writable)
153: m_filename(filename)
154{
155 // "r+b" rather than "w+b": every write in this library is an in-place edit of
156 // an existing store, so truncating on open would destroy it.
157 const char* mode = writable ? "r+b" : "rb";
158
159#ifdef _MSC_VER
160 // not fopen_s: it opens exclusively, so a second handle on the same store
161 // fails, and both this library and its callers open one store more than once
162 m_pfile = _fsopen(std::string(filename.begin(), filename.end()).c_str(), mode, _SH_DENYNO);
163#else
164 m_pfile = fopen(std::string(filename.begin(), filename.end()).c_str(), mode);
165#endif
166 if(m_pfile == NULL)
167 throw std::runtime_error("fopen failed");
168}
169
171{
172 if (!m_pfile) return;
173 fflush(m_pfile);
174 fclose(m_pfile);
175}
176
177inline size_t pstsdk::file::read(std::vector<byte>& buffer, ulonglong offset) const
178{
179#ifdef _MSC_VER
180 if(_fseeki64(m_pfile, offset, SEEK_SET) != 0)
181#else
182 if(fseek(m_pfile, offset, SEEK_SET) != 0)
183#endif
184 {
185 throw std::out_of_range("fseek failed");
186 }
187
188 size_t read = fread(&buffer[0], 1, buffer.size(), m_pfile);
189
190 if(read != buffer.size())
191 throw std::out_of_range("fread failed");
192
193 return read;
194}
195
197inline size_t pstsdk::file::write(const std::vector<byte>& buffer, ulonglong offset)
198{
199#ifdef _MSC_VER
200 if(_fseeki64(m_pfile, offset, SEEK_SET) != 0)
201#else
202 if(fseek(m_pfile, offset, SEEK_SET) != 0)
203#endif
204 {
205 throw std::out_of_range("fseek failed");
206 }
207
208 size_t write = fwrite(&buffer[0], 1, buffer.size(), m_pfile);
209
210 if(write != buffer.size())
211 throw std::out_of_range("fwrite failed");
212
213 // another handle on this file has its own buffer, and on Windows those are
214 // not coherent with this one until the bytes actually land
215 fflush(m_pfile);
216
217 return write;
218}
220
222{
223 const ulonglong jan1970 = 116444736000000000ULL;
224
225 return (filetime - jan1970) / 10000000;
226}
227
229{
230 const ulonglong jan1970 = 116444736000000000ULL;
231
232 return (time * 10000000) + jan1970;
233}
234
236{
237 throw not_implemented("vt_date_to_time_t");
238}
239
241{
242 throw not_implemented("vt_date_to_time_t");
243}
244
245inline bool pstsdk::test_bit(const byte* pbytes, ulong bit)
246{
247 return (*(pbytes + (bit >> 3)) & (0x80 >> (bit & 7))) != 0;
248}
249
250#if defined(_WIN32) || defined(__MINGW32__)
251
252// We know that std::wstring is always UCS-2LE on Windows.
253
254inline std::wstring pstsdk::bytes_to_wstring(const std::vector<byte> &bytes)
255{
256 if(bytes.size() == 0)
257 return std::wstring();
258
259 return std::wstring(reinterpret_cast<const wchar_t *>(&bytes[0]), bytes.size()/sizeof(wchar_t));
260}
261
262inline std::vector<pstsdk::byte> pstsdk::wstring_to_bytes(const std::wstring &wstr)
263{
264 if(wstr.size() == 0)
265 return std::vector<byte>();
266
267 const byte *begin = reinterpret_cast<const byte *>(&wstr[0]);
268 return std::vector<byte>(begin, begin + wstr.size()*sizeof(wchar_t));
269}
270
271// Assume UCS-2LE, use WideCharToMultiByte to support pre Win 10
272inline std::string pstsdk::bytes_to_string(const std::vector<byte> &bytes)
273{
274 // Do the wstr cast and use c_str to avoid any issues stemming from null terminator
275 std::wstring ws = bytes_to_wstring(bytes);
276 size_t utf8_sz = WideCharToMultiByte(CP_UTF8, 0, ws.c_str(), ws.size(), NULL, 0, NULL, NULL);
277
278 std::vector<char> utf8_str(utf8_sz);
279 size_t copied = WideCharToMultiByte(CP_UTF8, 0, ws.c_str(), ws.size(), &utf8_str.data()[0], utf8_sz, NULL, NULL);
280
281 if (copied != utf8_sz)
282 throw std::runtime_error("Expected to copy " + std::to_string(utf8_sz) + " bytes but copied " + std::to_string(copied));
283
284 return { utf8_str.begin(), utf8_str.end() };
285}
286
287#else // !(defined(_WIN32) || defined(__MINGW32__))
288
289// We're going to have to do this the hard way, since we don't know how
290// big wchar_t really is, or what encoding it uses.
291#include <iconv.h>
292
293// Not "WCHAR_T": libiconv implements that through the platform's mbrtowc, so
294// on macOS it follows LC_CTYPE and rejects anything outside it. Under the C
295// locale a right single quote comes back EILSEQ. glibc hardwires WCHAR_T to
296// UCS-4 and never showed this. Both libraries take a fixed width name, and
297// every platform reaching this branch is little endian with a 4 byte wchar_t.
298static_assert(sizeof(wchar_t) == 4, "expected a 4 byte wchar_t off Windows");
299#define PSTSDK_WCHAR_ENCODING "UTF-32LE"
300
301inline std::wstring pstsdk::bytes_to_wstring(const std::vector<byte> &bytes)
302{
303 if(bytes.size() == 0)
304 return std::wstring();
305
306 // Up to one wchar_t for every 2 bytes, if there are no surrogate pairs.
307 if(bytes.size() % 2 != 0)
308 throw std::runtime_error("Cannot interpret odd number of bytes as UTF-16LE");
309 std::wstring out(bytes.size() / 2, L'\0');
310
311 iconv_t cd(::iconv_open(PSTSDK_WCHAR_ENCODING, "UTF-16LE"));
312 if(cd == (iconv_t)(-1)) {
313 perror("bytes_to_wstring");
314 throw std::runtime_error("Unable to convert from UTF-16LE to wstring");
315 }
316
317 const char *inbuf = reinterpret_cast<const char *>(&bytes[0]);
318 size_t inbytesleft = bytes.size();
319 char *outbuf = reinterpret_cast<char *>(&out[0]);
320 size_t outbytesleft = out.size() * sizeof(wchar_t);
321 size_t result = ::iconv(cd, const_cast<char **>(&inbuf), &inbytesleft, &outbuf, &outbytesleft);
322 const int err = errno;
323 ::iconv_close(cd);
324 if(result == (size_t)(-1) || inbytesleft > 0 || outbytesleft % sizeof(wchar_t) != 0)
325 throw std::runtime_error("Failed to convert from UTF-16LE to wstring: "
326 + std::string(std::strerror(err)) + ", "
327 + std::to_string(inbytesleft) + " of "
328 + std::to_string(bytes.size()) + " bytes left");
329
330 out.resize(out.size() - (outbytesleft / sizeof(wchar_t)));
331 return out;
332}
333
334// Allows reading wstring props directly into std::string for non-Windows
335inline std::string pstsdk::bytes_to_string(const std::vector<byte> &bytes)
336{
337 if(bytes.size() == 0)
338 return std::string();
339
340 // Up to one wchar_t for every 2 bytes, if there are no surrogate pairs.
341 if(bytes.size() % 2 != 0)
342 throw std::runtime_error("Cannot interpret odd number of bytes as UTF-16LE");
343
344 // Up to 4 bytes out per char
345 std::string out(bytes.size() * 2, L'\0');
346
347 iconv_t cd(::iconv_open("UTF-8", "UTF-16LE"));
348 if(cd == (iconv_t)(-1)) {
349 perror("bytes_to_string");
350 throw std::runtime_error("Unable to convert from UTF-16LE to string");
351 }
352
353 const char *inbuf = reinterpret_cast<const char *>(&bytes[0]);
354 size_t inbytesleft = bytes.size();
355 char *outbuf = reinterpret_cast<char *>(&out[0]);
356 size_t outbytesleft = out.size();
357 size_t result = ::iconv(cd, const_cast<char **>(&inbuf), &inbytesleft, &outbuf, &outbytesleft);
358 ::iconv_close(cd);
359
360 if(result == (size_t)(-1) || inbytesleft > 0)
361 throw std::runtime_error("Failed to convert from UTF-16LE to string");
362
363 out.resize(out.size() - outbytesleft);
364
365 return out;
366}
367
368inline std::vector<pstsdk::byte> pstsdk::wstring_to_bytes(const std::wstring &wstr)
369{
370 if(wstr.size() == 0)
371 return std::vector<byte>();
372
373 // Up to 4 bytes per character if all codepoints are surrogate pairs.
374 std::vector<byte> out(wstr.size() * 4);
375
376 iconv_t cd(::iconv_open("UTF-16LE", PSTSDK_WCHAR_ENCODING));
377 if(cd == (iconv_t)(-1)) {
378 perror("wstring_to_bytes");
379 throw std::runtime_error("Unable to convert from wstring to UTF-16LE");
380 }
381
382 const char *inbuf = reinterpret_cast<const char *>(&wstr[0]);
383 size_t inbytesleft = wstr.size() * sizeof(wchar_t);
384 char *outbuf = reinterpret_cast<char *>(&out[0]);
385 size_t outbytesleft = out.size();
386 size_t result = ::iconv(cd, const_cast<char **>(&inbuf), &inbytesleft, &outbuf, &outbytesleft);
387 ::iconv_close(cd);
388 if(result == (size_t)(-1) || inbytesleft > 0)
389 throw std::runtime_error("Failed to convert from wstring to UTF-16LE");
390
391 out.resize(out.size() - outbytesleft);
392 return out;
393}
394
395#endif // !(defined(_WIN32) || defined(__MINGW32__))
396
397#endif
Contains references to other bth_node allocations.
Definition heap.h:364
const_iterator end() const
Returns a STL style iterator positioned at the "end" entry.
Definition btree.h:93
const_iterator begin() const
Returns a STL style iterator positioned at the first entry.
Definition btree.h:85
A generic class to read and write to a file.
Definition util.h:45
virtual size_t read(std::vector< byte > &buffer, ulonglong offset) const
Read from the file.
Definition util.h:177
~file()
Close the file.
Definition util.h:170
This function or method has not been implemented.
Definition errors.h:30
The exceptions used by pstsdk.
boost::uint64_t ulonglong
Definition primitives.h:70
boost::uint32_t ulong
Definition primitives.h:68
ulonglong time_t_to_filetime(time_t time)
Convert from a time_t to filetime.
Definition util.h:228
time_t filetime_to_time_t(ulonglong filetime)
Convert from a filetime to time_t.
Definition util.h:221
std::wstring bytes_to_wstring(const std::vector< byte > &bytes)
Convert an array of bytes to a std::wstring.
Definition util.h:301
double time_t_to_vt_date(time_t time)
Convert from a time_t to a VT_DATE.
Definition util.h:240
time_t vt_date_to_time_t(double vt_time)
Convert from a VT_DATE to a time_t.
Definition util.h:235
bool test_bit(const byte *pbytes, ulong bit)
Test to see if the specified bit in the buffer is set.
Definition util.h:245
std::string bytes_to_string(const std::vector< byte > &bytes)
Convert an array of bytes to a std::wstring.
Definition util.h:335
std::vector< byte > wstring_to_bytes(const std::wstring &wstr)
Convert a std::wstring to an array of bytes.
Definition util.h:368
Contains the definition of all in memory representations of disk structures.
Definition disk.h:19
Primitive structures defined by MS-PST and MAPI.
#define PSTSDK_WCHAR_ENCODING
Definition util.h:299