diff --git a/AUTHORS b/AUTHORS index d23c1a9..1faa029 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,54 +1,56 @@ Original version by: Dave Smith Dave Smith Current maintainer: Carl Byington With contributions by: Joseph Nahmias -- bounces Joseph Nahmias Arne Ahrend Nigel Horne Chris Halls Stevens Miller Brad Hards Alexander Grau Antonio Palama Sean Loaring James Woodcock Joachim Metz Robert Simpson Justin Greer Bharath Acharya Robert Harris David Cuadrado Chris Eagle Fridrich Strba Emmanuel Andry hggdh bharder Chris White Roberto Polli Lee Ayres Hugo DesRosiers Kenneth Berland Leo 'costela' Antunes Svante Signell Dominique Leuenberger a.k.a. Dimstar Daniel Gryniewicz AJ Shankar Jeffrey Morlan Hans Liss Igor Stroh Zachary Travis Vitaliy Didik Alfredo Esteban Tim Dufrane Paul Wise + Markus Schnalke + Stuart C. Naifeh Testing team: Mac OSX - Michael Watson Cygwin/Mingw - Fridrich Strba Cygwin - Chris Eagle diff --git a/ChangeLog b/ChangeLog index 0659c46..af36e0d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,949 +1,954 @@ +LibPST 0.6.76 (2021-03-27) +=============================== + * Stuart C. Naifeh - fix rfc2231 encoding when saving messages + to both .eml and .msg formats. + LibPST 0.6.75 (2020-03-22) =============================== * Markus Schnalke - fix from Debian for vcard version format. LibPST 0.6.74 (2020-01-12) =============================== * Paul Wise - many changes for Debian. LibPST 0.6.73 (2019-07-25) =============================== * Tim Dufrane - fix segfault in pst_close() LibPST 0.6.72 (2018-08-01) =============================== * Alfredo Esteban - add -l and -f options to lspst. * fix bydays recurring appointment, allow all 7 days * update for Fedora Python packaging LibPST 0.6.71 (2017-07-21) =============================== * Zachary Travis - Add support for the OST 2013 format, and Content-Disposition filename key fix for outlook compatibility LibPST 0.6.70 (2017-02-08) =============================== * Jeffrey Morlan - pst_getID2 must not recurse into children LibPST 0.6.69 (2016-10-29) =============================== * fix bugs in code allowing folders containing multiple item types LibPST 0.6.68 (2016-08-29) =============================== * allow folders containing multiple item types, e.g. email and calendar * better detection of valid internet headers LibPST 0.6.67 (2016-07-06) =============================== * Jeffrey Morlan - multiple bug fixes and an optimization see 'hg log -v' for details LibPST 0.6.66 (2015-12-21) =============================== * Igor Stroh - Added Content-ID header support LibPST 0.6.65 (2015-09-11) =============================== * Jeffrey Morlan - fix multiple Content-Type headers * Hans Liss - debug level output LibPST 0.6.64 (2015-03-09) =============================== * AJ Shankar fixes for attachment processing and body encodings that contain embedded null chars LibPST 0.6.63 (2013-12-27) =============================== * Daniel Gryniewicz found buffer overrun in LIST_COPY_TIME LibPST 0.6.62 (2013-09-22) =============================== * 983596 - Old dependency filter breaks file coloring LibPST 0.6.61 (2013-08-06) =============================== * move documentation to unversioned directory LibPST 0.6.60 (2013-06-12) =============================== * patch from Dominique Leuenberger to add AC_USE_SYSTEM_EXTENSIONS * add readpst -a option for attachment stripping LibPST 0.6.59 (2013-05-17) =============================== * add autoconf checking for libgsf LibPST 0.6.58 (2012-12-28) =============================== * fix From quoting on embedded rfc/822 messages LibPST 0.6.57 (2012-12-27) =============================== * remove useless dependencies LibPST 0.6.56 (2012-12-24) =============================== * merge -m .msg files code into main branch LibPST 0.6.55 (2012-05-08) =============================== * preserve bcc headers * document -C switch to set default character set * space after colon is not required in header fields LibPST 0.6.54 (2011-11-04) =============================== * embedded rfc822 messages might contain rtf encoded bodies LibPST 0.6.53 (2011-07-10) =============================== * add Status: header in output * allow fork for parallel processing of individual email folders in separate mode * proper handling of --with-boost-python option LibPST 0.6.52 (2011-05-22) =============================== * fix dangling freed pointer in embedded rfc822 message processing * allow broken outlook internet header field - it sometimes contains fragments of the message body rather than headers LibPST 0.6.51 (2011-04-17) =============================== * fix for buffer overrun; attachment size from the secondary list of mapi elements overwrote proper size from the primary list of mapi elements. Fedora bugzilla 696263 LibPST 0.6.50 (2010-12-24) =============================== * rfc2047 and rfc2231 encoding for non-ascii headers and attachment filenames LibPST 0.6.49 (2010-09-13) =============================== * fix to ignore embedded objects that are not email messages LibPST 0.6.48 (2010-09-02) =============================== * fix for broken internet headers from Outlook. * fix ax_python.m4 to look for python2.7 * Subpackage Licensing, add COPYING to -libs. * use mboxrd from quoting for output formats with multiple messages per file * use no from quoting for output formats with single message per file LibPST 0.6.47 (2010-05-07) =============================== * patches from Kenneth Berland for solaris. * fix output file name numbering to start at 1 rather than 2. LibPST 0.6.46 (2010-02-13) =============================== * prefer libpthread over librt for finding sem_init function. * rebuild for Fedora 13 change in implicit dso linking semantics. LibPST 0.6.45 (2009-11-18) =============================== * patch from Hugo DesRosiers to export categories and notes into vcards. * extend that patch to export categories into vcalendar appointments also. LibPST 0.6.44 (2009-09-20) =============================== * fix --help usage; readpstlog is gone, debug files are now ascii text. * patch from Lee Ayres to add file name extensions in separate mode. * allow mixed items types in a folder in separate mode. LibPST 0.6.43 (2009-09-12) =============================== * patches from Justin Greer. add code pages 1200 and 1201 to the list for iconv add support for 0x0201 indirect blocks that point to 0x0101 blocks add readpst -t option to select output item types fix (remove) extra new line inside headers * cleanup base64 encoding to remove duplicate code. * patch from Chris White to avoid segfault with embedded appointments. * patch from Roberto Polli to add creation of some Thunderbird specific meta files. * patch from Justin Greer to ignore b5 tables at offset zero. * output type filtering can now be used to handle folders with multiple item types. * better decoding of rfc822 embedded message attachments. * better detection of dsn delivery reports LibPST 0.6.42 (2009-09-03) =============================== * patch from Fridrich Strba to build with DJGPP DOS cross-compiler. LibPST 0.6.41 (2009-06-23) =============================== * fix ax_python detection - should not use locate command * checking for Fedora versions is not needed LibPST 0.6.40 (2009-06-23) =============================== * Fedora 11 has python2.6 * remove pdf version of the man pages LibPST 0.6.39 (2009-06-21) =============================== * Fedora > 10 moved to boost-python-devel LibPST 0.6.38 (2009-06-21) =============================== * add Python module interface to the shared library for easy scripting. * the shared library must never write to stdout or stderr. * fix pst_attach_to_mem so the caller does not need to initialize the buffer pointer. * remove readpst -C switch, obsolete debugging code. * update version to 4:0:0 since we made many changes to the interface. * removed contact->access_method since we don't have a mapi element for it. * changed pst_attach_to_mem to return pst_binary structure. * decode more recurrence mapi elements. * readpst changes for parallel operation on multi processor machines. * remove readpstlog - the debug log files are now plain ascii. Add locking if needed so parallel jobs can produce debug logs. * more cleanup of the shared library interface, but still not fully thread safe. * make nested mime multipart/alternative to hold the text/html parts so the topmost level is almost always multipart/mixed. * the shared library interface should now be thread safe. * patch from Fridrich Strba to build on win32. * remove unreferenced code. LibPST 0.6.37 (2009-04-17) =============================== * add pst_attach_to_mem() back into the shared library interface. * improve developer documentation. * fix memory leak caught by valgrind. LibPST 0.6.36 (2009-04-14) =============================== * spec file cleanup with multiple sub packages. * add doxygen devel-doc documentation for the shared library. * switch back to fully versioned subpackage dependencies. * more cleanup on external names in the shared object file. LibPST 0.6.35 (2009-04-08) =============================== * fix bug where we failed to pickup the last extended attribute. * patch from Emmanuel Andry to fix potential security bug in pst2dii with printf(err). * properly add trailing mime boundary in all modes. * move version-info into main configure.in, and set it properly * prefix all external symbols in the shared library with pst_ to avoid symbol clashes with other shared libraries. * new Debianization from hggdh. * build separate libpst, libpst-libs, libpst-devel rpms. * remove many functions from the interface by making them static. LibPST 0.6.34 (2009-03-19) =============================== * improve consistency checking when fetching items from the pst file. * avoid putting mixed item types into the same output folder. LibPST 0.6.33 (2009-03-17) =============================== * fix Fedora 11 type mismatch warning (actually an error in this case). * fix large file support, some systems require config.h to be included earlier in the compilation. * compensate for iconv conversion to utf-7 that produces strings that are not null terminated. * don't produce empty attachment files in separate mode. LibPST 0.6.32 (2009-03-14) =============================== * fix ppc64 compile error. LibPST 0.6.31 (2009-03-14) =============================== * bump version for Fedora cvs tagging mistake. LibPST 0.6.30 (2009-03-14) =============================== * improve documentation of .pst format. * remove decrypt option from getidblock - we always decrypt. * rename some structure fields to reflect our better understanding of the pst format. * track character set individually for each mapi element, since some could be unicode (therefore utf8) and others sbcs with character set specified by the mapi object. remove charset option from pst2ldif since we get that from each object now. * more code cleanup. * use AM_ICONV for better portability of the library location. * structure renaming to be more specific. * improve internal doxygen documentation. * avoid emitting bogus empty email messages into contacts and calendar files. LibPST 0.6.29 (2009-02-24) =============================== * fix for 64bit on Fedora 11 LibPST 0.6.28 (2009-02-24) =============================== * add X-libpst-forensic-* headers to capture items of interest that are not used by normal mail clients. * improve decoding of multipart/report and message/rfc822 mime types. * improve character set handling - don't try to convert utf-8 to single byte for fields that were not originally unicode. if the conversion fails, leave the data in utf-8. * fix embedded rfc822 messages with attachments. LibPST 0.6.27 (2009-02-07) =============================== * fix for const correctness on Fedora 11 LibPST 0.6.26 (2009-02-07) =============================== * patch from Fridrich Strba for building on mingw and general cleanup of autoconf files * add processing for pst files of type 0x0f * start adding support for properly building and installing libpst.so and the header files required to use it. * remove version.h since the version number is now in config.h * more const correctness issues regarding getopt() * consistent ordering of our include files. all system includes protected by ifdef HAVE_ from autoconf. * strip and regenerate all MIME headers to avoid duplicates. problem found by Michael Watson on Mac OSX. * do a better job of making unique MIME boundaries. * only use base64 coding when strictly necessary. * more cleanup of #include files. common.h is the only file allowed to include system .h files unprotected by autoconf HAVE_ symbols. define.h is the only other file allowed to include system .h files. define.h is never installed; common.h is installed if we are building the shared library. * recover dropped pragma pack line, use int64_t rather than off_t to avoid forcing users of the shared library to enable large file support. * add pragma packing support for sun compilers. * fix initial from header in mbox format. * start moving to PST_LE_GET* rather than LE*_CPU macros so we can eventually remove the pragma packing. * patch from Fridrich Strba, some systems need extra library for regex. LibPST 0.6.25 (2009-01-16) =============================== * improve handling of content-type charset values in mime parts LibPST 0.6.24 (2008-12-11) =============================== * patch from Chris Eagle to build on cygwin LibPST 0.6.23 (2008-12-04) =============================== * bump version to avoid cvs tagging mistake in Fedora LibPST 0.6.22 (2008-11-28) =============================== * patch from David Cuadrado to process emails with type PST_TYPE_OTHER * base64_encode_multiple() may insert newline, needs larger malloc * subject lines shorter than 2 bytes could segfault LibPST 0.6.21 (2008-10-21) =============================== * fix title bug with old schema in pst2ldif. * also escape commas in distinguished names per rfc4514. LibPST 0.6.20 (2008-10-09) =============================== * add configure option --enable-dii=no to remove dependency on libgd. * many fixes in pst2ldif by Robert Harris. * add -D option to include deleted items, from Justin Greer * fix from Justin Greer to add missing email headers * fix from Justin Greer for my_stristr() * fix for orphan children when building descriptor tree * avoid writing uninitialized data to debug log file * remove unreachable code * create dummy top-of-folder descriptor if needed for corrupt pst files LibPST 0.6.19 (2008-09-14) =============================== * Fix base64 encoding that could create long lines * Initial work on a .so shared library from Bharath Acharya. LibPST 0.6.18 (2008-08-28) =============================== * Fixes for iconv on Mac from Justin Greer. LibPST 0.6.17 (2008-08-05) =============================== * More fixes for 32/64 bit portability on big endian ppc. LibPST 0.6.16 (2008-08-05) =============================== * Use inttypes.h for portable printing of 64 bit items. LibPST 0.6.15 (2008-07-30) =============================== * Patch from Robert Simpson for file handle leak in error case. * Fix for missing length on lz decompression, bug found by Chris White. LibPST 0.6.14 (2008-06-15) =============================== * Fix my mistake in Debian packaging. LibPST 0.6.13 (2008-06-13) =============================== * Patch from Robert Simpson for encryption type 2. * Fix the order of testing item types to avoid claiming there are multiple message stores. LibPST 0.6.12 (2008-06-10) =============================== * Patch from Joachim Metz for Debian packaging, and fix for incorrect length on lz decompression. LibPST 0.6.11 (2008-06-03) =============================== * Use ftello/fseeko to properly handle large files. * Document and properly use datasize field in b5 blocks. * Fix some MSVC compile issues and collect MSVC dependencies into one place. LibPST 0.6.10 (2008-05-29) =============================== * Patch from Robert Simpson fix doubly-linked list in the cache_ptr code, and allow arrays of unicode strings (without converting them). * More changes for Fedora packaging (#434727) * Fixes for const correctness. LibPST 0.6.9 (2008-05-16) =============================== * Patch from Joachim Metz for 64 bit compile. * Signed/unsigned cleanup from 'CFLAGS=-Wextra ./configure'. * Reindent vbuf.c to make it readable. * Fix pst format documentation for 8 byte backpointers. LibPST 0.6.8 (2008-03-05) =============================== * Initial version of pst2dii to convert to Summation dii load file format. * Changes for Fedora packaging (#434727) LibPST 0.6.7 (2008-02-16) =============================== * Work around bogus 7c.b5 blocks in some messages that have been read. They appear to have attachments, but of some unknown format. Before the message was read, it did not have any attachments. * Use autoscan to cleanup our autoconf system. * Use autoconf to detect when we need to use our XGetopt files and other header files. * More fields, including BCC. * Fix missing LE32_CPU byte swapping for FILETIME types. LibPST 0.6.6 (2008-01-31) =============================== * More code cleanup, removing unnecessary null terminations on binary buffers. All pst file reads now go through one function. Logging all pst reads to detect cases where we read the same data multiple times - discovers node sizes are actually 512 bytes. * Switch from cvs to mercurial source control. LibPST 0.6.5 (2008-01-22) =============================== * More code cleanup, removing obsolete code. All the boolean flags of type 0xb have length 4, so these are all 32 bits in the file. Libpst treats them all as 16 bits, but at least we are consistent. * More fields decoded - for example, see We should be able to use that data for much more complete decoding. * Move the rpm group to Applications/Productivity consistent with Evolution. LibPST 0.6.4 (2008-01-19) =============================== * More fixes for Outlook 2003 64 bit parsing. We observed cases of compressed RTF bodies (type 0x1009) with zero length. * Document type 0x0101 descriptor blocks and process them. * Fix large file support - we need to include config.h before any standard headers. * Merge following changes from svn snapshot from Alioth: * Add new fields to appointment for recurring events (SourceForge #304198) * Map IPM.Task items to PST_TYPE_TASK. * Applied patch to remove compiler warnings, thanks! (SourceForge #304314) * Fix crash with unknown reference type * Fix more memory issues detected by valgrind * lspst - add usage message and option parsing using getopt (SourceForge #304199) * Fix crash caused by invalid free calls * Fix crash when email subject is empty * Fix memory and information leak in hex debug dump LibPST 0.6.3 (2008-01-13) =============================== * More type consistency issues found by splint. LibPST 0.6.2 (2008-01-12) =============================== * More fixes for Outlook 2003 64 bit parsing. * All buffer sizes changed to size_t, all file offsets changed to off_t, all function names start with pst_, many other type consistency issues found by splint. Many changes to #llx in debug printing for 64 bit items. All id values are now uint64_t. LibPST 0.6.1 (2008-01-06) =============================== * Outlook 2003 64 bit parsing. Some documentation from Alexander Grau and patches from Sean Loaring . * fix from Antonio Palama for email items that happen to have item->contact non null, and were being processed as contacts. * Add large file support so we can read .pst files larger than 2gb. * Change lspst to be similar to readpst, properly using recursion to walk the tree, and testing item types. Add a man page for lspst. LibPST 0.5.12 (2007-10-02) =============================== * security fix from Brad Hards for buffer overruns in liv-zemple decoding for corrupted or malicious pst files. LibPST 0.5.11 (2007-08-24) =============================== * fix from Stevens Miller for uninitialized variable. LibPST 0.5.10 (2007-08-20) =============================== * fix yet more valgrind errors - finally have a clean memory check. * restructure readpst.c for proper recursive tree walk. * buffer overrun test was backwards, introduced at 0.5.6 * fix broken email attachments, introduced at 0.5.6 LibPST 0.5.9 (2007-08-12) =============================== * fix more valgrind errors. LibPST 0.5.8 (2007-08-10) =============================== * fix more valgrind errors. lzfu_decompress needs to return the actual buffer size, since the lz header overestimates the size. This caused base64_encode to encode undefined bytes into the email attachment. LibPST 0.5.7 (2007-08-09) =============================== * fix valgrind errors, using uninitialized data. * improve debug logging and readpstlog for indented listings. * cleanup documentation. LibPST 0.5.6 (2007-07-15) =============================== * Fix to allow very small pst files with only one node in the tree. We were mixing signed/unsigned types in comparisons. * More progress decoding the basic structure 7c blocks. Many four byte values may be ID2 indices with data outside the buffer. * Start using doxygen to generate internal documentation. LibPST 0.5.5 (2007-07-10) =============================== * merge the following changes from Joe Nahmias version: * Lots of memory fixes. Thanks to Nigel Horne for his assistance tracking these down! * Fixed creation of vCards from contacts, thanks to Nigel Horne for his help with this! * fix for MIME multipart/alternative attachments. * added -c options to readpst manpage. * use 8.3 attachment filename if long filename isn't available. * new -b option to skip rtf-body.rtf attachments. * fix format of From header lines in mbox files. * Add more appointment fields, thanks to Chris Halls for tracking them down! LibPST 0.5.4 (2006-02-25) =============================== * patches from Arne, adding MH mode, remove leading zeros from the generated numbered filenames starting with one rather than zero. Miscellaneous code cleanup. * document the "7c" descriptor block format. LibPST 0.5.3 (2006-02-20) =============================== * switch to GNU autoconf/automake. This breaks the MS VC++ projects since the source code is now in the src subdirectory. * documentation switched to xml, building man pages and html from the master xml copy. * include rpm .spec file for building src and binary rpms. LibPST 0.5.2 (2006-02-18) =============================== * Added pst2ldif to convert the contacts to ldif format for import into ldap databases. * Major changes to libpst.c to properly use the node depth values from the b-tree nodes. We also use the item count values in the nodes rather than trying to guess how many items are active. * Cleanup whitespace - using tabs for every four columns. LibPST 0.5.1 (17 November 2004) =============================== Well, a lot has happened since the last release of libpst. Release / Management: * The project has forked! The new maintainer is Joseph Nahmias. * We have changed hosting sites, thanks to sourceforge for hosting to this point. From this point forward we will be using alioth.debian.org. * The project is now using SubVersioN for source control. You can get the latest code by running: svn co svn://svn.debian.org/svn/libpst/trunk . * See for more information. Code Changes: * Added lspst program to list items in a PST. Still incomplete. * Added vim folding markers to readpst.c * avoid the pseudo-prologue that MS prepends to the email headers * fix build on msvc, since it doesn't have sys/param.h * Re-vamped Makefile: * Only define CFLAGS in Makefileif missing * fixed {un,}install targets in Makefile * Fixed up build process in Makefile * Added mozilla conversion script from David Binard * Fixed bogus creation of readpst.log on every invocation * escaped dashes and apostrophe in manpages * Updated TODO * added manpages from Debian pkg * fix escaped-string length count to consider '\n', thanks to Paul Bakker . * ensure there's a blank line between header and body patch from (SourceForge #890745). * Apply accumulated endian-related patches * Removed unused files, upstream's debian/ dir -- Joe Nahmias LibPST v0.5 =========== It is with GREAT relief that I bring you version 0.5 of the LibPST tools! Through great difficulties, this tool has survived and expanded to become even better. The changes are as follows: * RTF support. We can now decompress RTF bodies in emails, and are saved as attachments * Better support in reading the indexes. Fixed many bugs with them * Improved reliability. "Now we are getting somewhere!" * Improved compiling. Hopefully we won't be hitting too many compile errors now. * vCard handling. Contacts are now exported as vCard entries. * vEvent handling. Support has begun on exporting Calendar entries as events * Support for Journal entries has also begun If you have any problems with this release, don't hesitate to contact me. These changes come to you, as always, free under the GPL license!! What a wonderful thing it is. It does mean that you can write your own program off of this library and distribute it also for free. However, anyone with commercial interests for developing applications they will be charging for are encouraged to get in touch with me, as I am sure we can come to some arrangement. Dave Smith LibPST v0.4.3 ============= Bug fix release. No extra functionality Dave Smith LibPST v0.4.2 ============= The debug system has had an overhaul. The debug messages are no longer printed to the screen when they are enabled. They are dumped to a binary file. There is another utility called "readlog" that I have written to handle these log files. It should make it easier to selectively view bits of a log file. It also shows the position that the log message was printed from. There is a new switch in readpst. It is -d. It enables the user to specify the log file which the binary log is written to. If the switch isn't used, the default file of "readpst.log" is used. The code is now Visual C++ compatible. It has compiled on Visual C++ .net Standard edition, and produces the readpst.exe file. Use the project file included in this distribution. There have been minor improvements elsewhere too. LibPST v0.4.1 ============= Fixed a couple more bugs. Is it me or do bugs just insert themselves in random, hard to find places! Cured a few problems with regard to emails with multiple embedded items. They are not fully re-created using Mime-types, but are accessible with the -S switch (which saves everything as separate items) Fixed a problem reading the first index. Back sliders are now detected. (ie when the value following the current one is smaller, not bigger!) Added some error messages when we try and read outside of the PST file, this was causing a few problems before, cause the return value wasn't always checked, so it was possible to be reading random data, and trying to make sense of it! Anyway, if you find any problems, don't hesitate to mail me Dave Smith LibPST v0.4 =========== Fixed a nasty bug that occasionally corrupted attachments. Another bug with regard to reading of indexes (also occasional). Another output method has been added which is called "Separate". It is activated with the -S switch. It operates in the following manor: |--Inbox-->000000 | 000001 | 000002 |--Sentmail-->0000000 | 0000001 | 0000002 All the emails are stored in separate files counting from 0 upwards, in a folder named as the PST folder. When an email has an attachment, it is saved as a separate file. The filename for the attachment is made up of 2 parts, the first is the email number to which it belongs, the second is its filename. The should now be runnable on big-endian machines, if the define.h file is first modified. The #define LITTLE_ENDIAN must be commented out, and the #define BIG_ENDIAN must be uncommented. More verbose error messages have been added. Apparently people got confused when the program stopped for no visible reason. This has now been resolved. Thanks for the continued support of all people involved. Dave Smith Libpst v0.3.4 ============= Several more fixes. An Infinite loop and incorrect interpreting of item index attributes. Work has started on making the code executable on big endian CPUs. At present it should work with Linux on these CPUs, but I would appreciate it if you could provide feedback with regard to it's performance. I am also working with some other people at make it operate on Solaris. A whole load more items are now recognized by the Item records. With more items in Emails and Folders. I haven't got to the Contacts yet. Anyway, this is what I would call a minor feature enhancement and bugfix release. Dave Smith LibPST v0.3.3 ============= Fixed several items. Mainly memory leaks. Loads of them! oops.. I have added a new program, mainly of debugging, which when passed an ID value and a pst file, will extract and decrypt that ID from the pst file. I don't see it being a huge attraction, or of much use to most people, but it is another example of writing an application to use the libpst interface. Another fix was in the reading of the item index. This has hopefully now been corrected. The result of this bug was that not all the emails in a folder were converted. Hopefully you should have more luck now. Dave Smith LibPST v0.3.2 ============= Quick bugfix release. There was a bug in the decryption of the basic encryption that outlook uses. One byte, 0x6c, was incorrectly decrypted to 0x6c instead of 0xcd. This release fixes this bug. Sorry... LibPST v0.3.1 ============= Minor improvements. Fixed bug when linking multiple blocks together, so now the linking blocks are not "encrypted" when trying to read them. LibPST v0.3 =========== A lot of bug fixing has been done for this release. Testing has been done on the creation of the files by readpst. Better handling of large binaries being extracted from the PST file has been implemented. Quite a few reports have come in about not being able to compile on Darwin. This could be down to using macros with variable parameter lists. This has now been changed to use C functions with variable parameters. I hope this fixes a lot of problems. Added support for recreating the folder structure into normal directories. For Instance: Personal Folders |-Inbox | |-Jokes | |-Meetings |-Send Items each folder containing an mbox file with the correct emails for that folder. Dave Smith LibPST v0.3 beta1 ================= Again, a shed load of enhancements. More work has been done on the mime creation. A bug has been fixed that was letting part of the attachments that were created disappear. A major enhancement is that "compressible encryption" support has been added. This was an incredibly simple method to use. It is basically a caesar cipher. It has been noted by several users already that the PST password that Outlook uses, serves *no purpose*. It is not used to encrypt the PST, it is merely stored there. This means that the readpst application is able to convert PST files without knowing the password. Microsoft have some explaining to do! Output files are now not overwritten if they already exist. This means that if you have two folders in your PST file named "fred", the first one encountered will be named "fred" and the second one will be named "fred00000001". As you can see, there is enough room there for many duplicate names! Output filenames are now restricted. Any "/" or "\" characters in the name are replaced with "_". If you find that there are any other characters that need to be changed, could you please make me aware! Thanks to Berry Wizard for help with supporting the encryption. Thanks to Auke Kok, Carolus Walraven and Yogesh Kumar Guatam for providing debugging information and testing. Dave Smith LibPST v0.2 beta1 ================= Hello once more... Attachments are now re-created in mime format. The method is very crude and could be prone to over generalisation. Please test this version, and if attachments are not recreated correctly, please send me the email (complete message source) of the original and converted. Cheers. I hope this will work for everyone who uses this program, but reality can be very different! Let us see how it goes... Dave Smith LibPST v0.2 alpha1 =========== Hello! Some improvements. The internal code has been changed so that attachments are now processed and loaded into the structures. The readpst program is not finished yet. It needs to convert these binary structs into mime data. At present it just saves them to the current directory, overwriting any previous files with the attachment name. Improvements over previous version: * KMail output is supported - if the "-k" flag is specified, all the directory hierarchy is created using the KMail standard * Lots of bugs and memory leaks fixed Usage: ReadPST v0.2alpha1 implementing LibPST v0.2alpha1 Usage: ./readpst [OPTIONS] {PST FILENAME} OPTIONS: -h - Help. This screen -k - KMail. Output in kmail format -o - Output Dir. Directory to write files to. CWD is changed *after* opening pst file -V - Version. Display program version If you want to view lots of debug output, modify a line in "define.h" from "//#define DEBUG_ALL" to "#define DEBUG_ALL". It would then be advisable to pipe all output to a log file: ./readpst -o out pst_file &> logfile Dave Smith LibPST v0.1 =========== Hi Folks! This has been a long, hard slog, but I now feel that I have got somewhere useful. The included program "main" is able to read an Outlook PST file and dump the emails into mbox files, separating each folder into a different mbox file. All the mbox files are stored in the current directory and no attempt is yet made to organise these files into a directory hierarchy. This would not be too difficult to achieve though. Email attachments are not yet handled, neither are Contacts. There is no pretty interface yet, but you can convert a PST file in the following manner ./main {path to PST file} This is very much a work in progress, but I thought I should release this code so that people can lose their conception that outlook files will never be converted to Linux. I am intending that the code I am writing will be developed into greater applications to provide USEFUL tools for accessing and converting PST files into a variety of formats. One point I feel I should make is that Outlook, by default, creates "Compressible Encryption" PST files. I have not, as yet, attempted to write any decryption routines, so you will not be able to convert these files. However, if you create a new PST file and choose not to make an encrypted one, you can copy all your emails into this new one and then convert the unencrypted one. I hope you enjoy, Dave Smith diff --git a/NEWS b/NEWS index 174e30e..07f27fa 100644 --- a/NEWS +++ b/NEWS @@ -1,87 +1,88 @@ +0.6.76 2021-03-27 Stuart C. Naifeh - fix rfc2231 encoding when saving messages to .eml and .msg formats. 0.6.75 2020-03-22 Markus Schnalke - fix from Debian for vcard version format. 0.6.74 2020-01-12 Paul Wise - many changes for Debian. 0.6.73 2019-07-25 Tim Dufrane - fix segfault in pst_close() 0.6.72 2018-08-01 Alfredo Esteban - add -l and -f options to lspst. 0.6.71 2017-07-21 Zachary Travis - Add support for the OST 2013 format, and Content-Disposition filename key fix for outlook compatibility 0.6.70 2017-02-08 Jeffrey Morlan - pst_getID2 must not recurse into children 0.6.69 2016-10-29 fix bugs in code allowing folders containing multiple item types 0.6.68 2016-08-29 allow folders containing multiple item types; better detection of valid internet headers 0.6.67 2016-07-06 Jeffrey Morlan - multiple bug fixes and an optimization 0.6.66 2015-12-21 Igor Stroh - Added Content-ID header support 0.6.65 2015-09-11 Jeffrey Morlan - fix multiple Content-Type headers; Hans Liss - debug level output 0.6.64 2015-03-09 AJ Shankar fixes for attachment processing and body encodings that contain embedded null chars 0.6.63 2013-12-27 Daniel Gryniewicz found buffer overrun in LIST_COPY_TIME 0.6.62 2013-09-22 983596 - Old dependency filter breaks file coloring 0.6.61 2013-08-06 move documentation to unversioned directory 0.6.60 2013-06-12 add AC_USE_SYSTEM_EXTENSIONS, add readpst -a option for attachment stripping 0.6.59 2013-05-17 add autoconf checking for libgsf 0.6.58 2012-12-28 fix From quoting on embedded rfc/822 messages 0.6.57 2012-12-27 remove useless dependencies 0.6.56 2012-12-24 merge -m .msg files code into main branch 0.6.55 2012-05-08 preserve bcc headers, space after colon is not required in header fields 0.6.54 2011-11-04 embedded rfc822 messages might contain rtf encoded bodies 0.6.53 2011-07-10 allow fork for parallel processing of individual email folders in separate mode 0.6.52 2011-05-22 fix dangling freed pointer; allow broken outlook internet header field 0.6.51 2011-04-17 fix for buffer overrun; attachment size fetched twice 0.6.50 2010-12-24 rfc2047 and rfc2231 encoding for non-ascii headers and attachment filenames 0.6.49 2010-09-13 fix to ignore embedded objects that are not email messages 0.6.48 2010-09-02 fix for broken internet headers from Outlook, change to mboxrd quoting 0.6.47 2010-05-07 patches from Kenneth Berland for solaris 0.6.46 2010-02-13 fixes for fedora 13 change in implicit dso linking semantics 0.6.45 2009-11-18 patch from Hugo DesRosiers to export categories and notes into vcards 0.6.44 2009-09-20 patch from Lee Ayres to add file name extensions in separate mode 0.6.43 2009-09-12 patches from Justin Greer, Chris White, Roberto Polli; better rfc822 embedded message decoding 0.6.42 2009-09-03 patch from Fridrich Strba to build with DJGPP DOS cross-compiler 0.6.41 2009-06-23 fix ax_python detection - should not use locate command 0.6.40 2009-06-23 fedora 11 has python2.6, remove pdf version of the man pages 0.6.39 2009-06-21 fedora > 10 moved to boost-python-devel 0.6.39 2009-06-21 fedora > 10 moved to boost-python-devel 0.6.38 2009-06-21 many changes including shared library soname 0.6.37 2009-04-17 add pst_attach_to_mem() back into the shared library interface 0.6.36 2009-04-14 build separate -doc and -devel-doc subpackages 0.6.35 2009-04-08 properly add trailing mime boundary in all modes, build separate rpms with libpst.so shared. 0.6.34 2009-03-19 avoid putting mixed item types into the same output folder 0.6.33 2009-03-17 fix utf-7 conversions, don't produce empty attachment files in separate mode 0.6.32 2009-03-14 fix ppc64 compile error 0.6.31 2009-03-14 bump version for fedora cvs tagging mistake 0.6.30 2009-03-14 track character set individually for each mapi element, avoid emitting bogus empty email messages into contacts and calendar files. 0.6.29 2009-02-24 fix for 64bit on Fedora 11 0.6.28 2009-02-24 improve decoding of multipart/report and message/rfc822 mime types 0.6.27 2009-02-07 fix for const correctness on Fedora 11 0.6.26 2009-02-07 patch from Fridrich Strba for building on mingw, and autoconf cleanup, better mime headers 0.6.25 2009-01-16 improve handling of content-type charset values in mime parts 0.6.24 2008-12-11 patch from Chris Eagle to build on cygwin 0.6.23 2008-12-04 bump version to avoid cvs tagging mistake in fedora 0.6.22 2008-11-28 process emails with type PST_TYPE_OTHER, fix malloc error and possible segfault 0.6.21 2008-10-21 fix title bug with old schema in pst2ldif, also escape commas in distinguished names per rfc4514. 0.6.20 2008-10-09 add configure option --enable-dii=no, fixes from Robert Harris for pst2ldif. 0.6.19 2008-09-14 Initial work on a .so shared library from Bharath Acharya. 0.6.18 2008-08-28 Fixes for iconv on Mac from Justin Greer. 0.6.17 2008-08-05 More fixes for 32/64 bit portability on big endian ppc 0.6.16 2008-08-05 Use inttypes.h for portable printing of 64 bit items 0.6.15 2008-07-30 Fix file handle leak in error case, missing length on lz decompression 0.6.14 2008-06-15 Fix my mistake in debian packaging 0.6.13 2008-06-13 Patch from Robert Simpson for encryption type 2. 0.6.12 2008-06-10 Patch from Joachim Metz for debian packaging, and fix for incorrect length on lz decompression. 0.6.11 2008-06-03 Use ftello/fseeko to properly handle large files. 0.6.10 2008-05-29 Patch from Robert Simpson for doubly-linked list and arrays of unicode strings. 0.6.9 2008-05-16 Patch from Joachim Metz for 64 bit compile. 0.6.8 2008-03-05 Initial version of pst2dii to convert to Summation dii load file format. 0.6.7 2008-02-16 Ignore unknown attachments on some read messages; autoconf cleanup. 0.6.6 2008-01-31 Code cleanup, switch from cvs to mercurial source control. 0.6.5 2008-01-22 Code cleanup, rpm group Applications/Productivity. 0.6.4 2008-01-19 More fixes for 64 bit format, merge changes from svn Alioth. 0.6.3 2008-01-13 More type consistency issues found by splint. 0.6.2 2008-01-12 More fixes for 64 bit format, consistent types size_t, off_t, etc. 0.6.1 2008-01-06 Outlook 2003 64 bit format and fix for bogus contacts. 0.5.12 2007-10-02 security fix for possible buffer overruns in liv-zemple decoding 0.5.11 2007-08-24 fix for uninitialized variable 0.5.10 2007-08-20 fix yet more valgrind errors, restructure readpst recursive walk, backwards overrun test 0.5.9 2007-08-12 fix more valgrind errors, pst2ldif wrote undefined data 0.5.8 2007-08-10 lzfu_decompress/base64_encode encoded random data into attachment 0.5.7 2007-08-09 fix valgrind errors, using uninitialized data 0.5.6 2007-07-15 handle small pst files, better decoding of 7c blocks 0.5.5 2007-07-10 merge changes from Joe Nahmias version 0.5.4 2006-02-25 add MH mode, generated filenames with no leading zeros 0.5.3 2006-02-20 switch to gnu autoconf/automake 0.5.2 2006-02-18 add pst2ldif, fix btree processing in libpst.c diff --git a/configure.ac b/configure.ac index 2b639f9..2ea2315 100644 --- a/configure.ac +++ b/configure.ac @@ -1,394 +1,395 @@ AC_PREREQ(2.60) -AC_INIT(libpst,0.6.75,carl@five-ten-sg.com) +AC_INIT(libpst,0.6.76,carl@five-ten-sg.com) AC_CONFIG_SRCDIR([src/libpst.c]) AC_CONFIG_HEADER([config.h]) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE AC_CANONICAL_HOST AC_USE_SYSTEM_EXTENSIONS # # 1. Remember that version-info is current:revision:age, and age <= current. # 2. If the source code has changed at all since the last public release, # then increment revision (`c:r:a' becomes `c:r+1:a'). # 3. If any interfaces have been added, removed, or changed since the last # update, increment current, and set revision to 0. # 4. If any interfaces have been added since the last public release, then # increment age, since we should be backward compatible with the previous # version. # 5. If any interfaces have been removed or changed since the last public # release, then set age to 0, since we are not backward compatible. # 6. libtool will build libpst.so.x.y.z where the SONAME is libpst.so.x # and x=current-age, y=age, z=revision -libpst_version_info='5:17:1' +libpst_version_info='5:18:1' AC_SUBST(LIBPST_VERSION_INFO, [$libpst_version_info]) libpst_so_major='4' AC_SUBST(LIBPST_SO_MAJOR, [$libpst_so_major]) # libpst # version soname so library name # 0.6.35 libpst.so.2 libpst.so.2.0.0 # 0.6.37 libpst.so.2 libpst.so.2.1.0 # 0.6.38 libpst.so.2 libpst.so.2.1.0 # 0.6.40 libpst.so.4 libpst.so.4.0.0 # 0.6.43 libpst.so.4 libpst.so.4.0.1 # 0.6.47 libpst.so.4 libpst.so.4.0.2 # 0.6.48 libpst.so.4 libpst.so.4.0.3 # 0.6.49 libpst.so.4 libpst.so.4.0.4 # 0.6.50 libpst.so.4 libpst.so.4.1.0 # 0.6.51 libpst.so.4 libpst.so.4.1.1 # 0.6.52 libpst.so.4 libpst.so.4.1.2 # 0.6.53 libpst.so.4 libpst.so.4.1.3 # 0.6.54 libpst.so.4 libpst.so.4.1.4 # 0.6.55 libpst.so.4 libpst.so.4.1.5 # 0.6.56 libpst.so.4 libpst.so.4.1.6 # 0.6.57 libpst.so.4 libpst.so.4.1.6 # 0.6.58 libpst.so.4 libpst.so.4.1.7 # 0.6.59 libpst.so.4 libpst.so.4.1.8 # 0.6.60 libpst.so.4 libpst.so.4.1.9 # 0.6.61 libpst.so.4 libpst.so.4.1.9 # 0.6.62 libpst.so.4 libpst.so.4.1.9 # 0.6.63 libpst.so.4 libpst.so.4.1.10 # 0.6.66 libpst.so.4 libpst.so.4.1.11 # 0.6.67 libpst.so.4 libpst.so.4.1.12 # 0.6.68 libpst.so.4 libpst.so.4.1.13 # 0.6.69 libpst.so.4 libpst.so.4.1.14 # 0.6.73 libpst.so.4 libpst.so.4.1.15 # 0.6.74 libpst.so.4 libpst.so.4.1.16 # 0.6.75 libpst.so.4 libpst.so.4.1.17 +# 0.6.76 libpst.so.4 libpst.so.4.1.18 # Check for solaris AC_MSG_CHECKING([for Solaris]) case "$host" in *solaris*) os_solaris=yes ;; *) os_solaris=no ;; esac AC_MSG_RESULT($os_solaris) AM_CONDITIONAL(OS_SOLARIS, [test "$os_solaris" = "yes"]) # Check for win32 AC_MSG_CHECKING([for Win32]) case "$host" in *-mingw*) os_win32=yes ;; *) os_win32=no ;; esac AC_MSG_RESULT($os_win32) AM_CONDITIONAL(OS_WIN32, [test "$os_win32" = "yes"]) # Check for Win32 platform AC_MSG_CHECKING([for Win32 platform in general]) case "$host" in *-cygwin*) platform_win32=yes ;; *) platform_win32=$os_win32 ;; esac AC_MSG_RESULT($platform_win32) AM_CONDITIONAL(PLATFORM_WIN32, [test "$platform_win32" = "yes"]) # Checks for programs. # The following lines adds the --enable-dii option to configure: # # Give the user the choice to enter one of these: # --enable-dii # --enable-dii=yes # --enable-dii=no # AC_MSG_CHECKING([whether we are enabling dii utility]) AC_ARG_ENABLE(dii, AC_HELP_STRING([--enable-dii], [enable dii utility]), [ case "${enableval}" in yes) ;; no) ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-dii) ;; esac ], # default if not specified enable_dii=yes ) AC_MSG_RESULT([$enable_dii]) AC_PATH_PROG(CONVERT, convert) if test "x$CONVERT" = "x" ; then if test "$enable_dii" = "yes"; then enable_dii=no AC_MSG_WARN([convert program not found. pst2dii disabled]) fi else if test "x`$CONVERT --version 2>&1 | grep -i imagemagick >/dev/null ; echo $?`" != "x0"; then if test "$enable_dii" = "yes"; then enable_dii=no AC_MSG_WARN([wrong convert program found. pst2dii disabled]) fi fi fi AC_CHECK_HEADER([gd.h], [ AC_DEFINE([HAVE_GD_H], [1], [Define to 1 if you have the header file.]) ], [ if test "$enable_dii" = "yes"; then enable_dii=no AC_MSG_WARN([gd.h not found. pst2dii disabled]) fi ]) AM_CONDITIONAL(BUILD_DII, [test "$enable_dii" = "yes"]) # Checks for programs. AC_PROG_CXX AC_PROG_CC AM_PROG_CC_C_O AC_PROG_CPP AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_LIBTOOL AC_PROG_MAKE_SET # make sure we get large file support AC_SYS_LARGEFILE AC_CHECK_SIZEOF(off_t) # Checks for header files. AC_CHECK_HEADER([unistd.h], AM_CONDITIONAL(NEED_XGETOPT, [test yes = no]), AM_CONDITIONAL(NEED_XGETOPT, [test yes = yes]) ) AC_HEADER_DIRENT AC_HEADER_STDC AC_CHECK_HEADERS([ctype.h dirent.h errno.h fcntl.h inttypes.h limits.h regex.h semaphore.h signal.h stdarg.h stdint.h stdio.h stdlib.h string.h sys/param.h sys/shm.h sys/stat.h sys/types.h time.h unistd.h wchar.h]) save_libs="$LIBS" ; LIBS="" AC_SEARCH_LIBS([sem_init], [pthread rt], [SEM_LIBS="$LIBS"], [AC_MSG_ERROR([sem_init missing])]) AC_SUBST([SEM_LIBS]) LIBS="$save_libs" # Checks for typedefs, structures, and compiler characteristics. AC_HEADER_STDBOOL AC_HEADER_SYS_WAIT AC_C_CONST AC_C_INLINE AC_TYPE_OFF_T AC_TYPE_SIZE_T AC_TYPE_PID_T AC_STRUCT_TM # Checks for library functions. AC_FUNC_FORK AC_FUNC_FSEEKO AC_FUNC_STAT AC_FUNC_LSTAT AC_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK if test "$cross_compiling" != "yes"; then AC_FUNC_MALLOC AC_FUNC_MKTIME AC_FUNC_REALLOC fi AC_FUNC_STRFTIME AC_FUNC_VPRINTF AC_CHECK_FUNCS([chdir getcwd memchr memmove memset regcomp strcasecmp strncasecmp strchr strdup strerror strpbrk strrchr strstr strtol get_current_dir_name]) AM_GNU_GETTEXT AM_GNU_GETTEXT_VERSION([0.17]) AM_ICONV if test "$am_cv_func_iconv" != "yes"; then AC_MSG_ERROR([libpst requires iconv which is missing]) fi AC_CHECK_FUNCS(regexec,,[AC_CHECK_LIB(regex,regexec, [REGEXLIB=-lregex AC_DEFINE(HAVE_REGEXEC,1,[Define to 1 if you have the regexec function.])], [AC_MSG_ERROR([No regex library found])])]) AC_SUBST(REGEXLIB) # The following lines adds the --enable-pst-debug option to configure: # # Give the user the choice to enter one of these: # --enable-pst-debug # --enable-pst-debug=yes # --enable-pst-debug=no # AC_MSG_CHECKING([whether we are forcing debug dump file creation]) AC_ARG_ENABLE(pst-debug, AC_HELP_STRING([--enable-pst-debug], [force debug dump file creation]), [ case "${enableval}" in yes) ;; no) ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-pst-debug) ;; esac ], # default if not specified enable_pst_debug=no ) AC_MSG_RESULT([$enable_pst_debug]) if test "$enable_pst_debug" = "yes"; then AC_DEFINE(DEBUG_ALL, 1, Define to 1 to force debug dump file creation) fi # The following lines adds the --enable-libpst-shared option to configure: # # Give the user the choice to enter one of these: # --enable-libpst-shared # --enable-libpst-shared=yes # --enable-libpst-shared=no # AC_MSG_CHECKING([whether we are building libpst shared object]) AC_ARG_ENABLE(libpst-shared, AC_HELP_STRING([--enable-libpst-shared], [build libpst shared object]), [ case "${enableval}" in yes) ;; no) ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-libpst-shared) ;; esac ], # default if not specified enable_libpst_shared=no ) AC_MSG_RESULT([$enable_libpst_shared]) enable_static_tools=yes if test "$enable_libpst_shared" = "yes"; then enable_shared=yes enable_static_tools=no fi # needed by STATIC_TOOLS in src/Makefile.am AC_SUBST(PST_OBJDIR, [$objdir]) # The following lines adds the --enable-static-tools option to configure: # # Give the user the choice to enter one of these: # --enable-static-tools # --enable-static-tools=yes # --enable-static-tools=no # AC_MSG_CHECKING([whether to link command line tools with libpst statically]) AC_ARG_ENABLE([static-tools], AC_HELP_STRING([--enable-static-tools], [link command line tools with libpst statically]), [ case "${enableval}" in yes) ;; no) ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-static-tools) ;; esac ], [ enable_static_tools=no ]) AC_MSG_RESULT([$enable_static_tools]) AM_CONDITIONAL(STATIC_TOOLS, [test "$enable_static_tools" = "yes"]) if test "$enable_static_tools" = "yes"; then enable_static="yes" fi # The following lines adds the --enable-python option to configure: # # Give the user the choice to enter one of these: # --enable-python # --enable-python=yes # --enable-python=no # AC_MSG_CHECKING([whether to build the libpst python interface]) AC_ARG_ENABLE([python], AC_HELP_STRING([--enable-python], [build libpst python interface]), [ case "${enableval}" in yes) ;; no) ;; *) AC_MSG_ERROR(bad value ${enableval} for --python) ;; esac ], [ enable_python=yes ]) AC_MSG_RESULT([$enable_python]) AM_CONDITIONAL(PYTHON_INTERFACE, [test "$enable_python" = "yes"]) if test "$enable_python" = "yes"; then enable_shared="yes" # get the version of installed python AX_PYTHON if test "$ax_python_bin" = "no"; then AC_MSG_ERROR(python binary not found) fi PYTHON_VERSION=`echo $ax_python_bin | cut -c7-` # find the flags for that version AX_PYTHON_DEVEL([$PYTHON_VERSION]) # do we have Boost Python? AX_BOOST_PYTHON if test "$ac_cv_boost_python" = "no"; then AC_MSG_ERROR(Boost Python not found) fi AC_SUBST(PYTHON_VERSION, [$ax_python_bin]) fi # The following lines adds the --enable-profiling option to configure: # # Give the user the choice to enter one of these: # --enable-profiling # --enable-profiling=yes # --enable-profiling=no # AC_MSG_CHECKING([whether to link with gprof profiling]) AC_ARG_ENABLE([profiling], AC_HELP_STRING([--enable-profiling], [link with gprof profiling]), [ case "${enableval}" in yes) CFLAGS="$CFLAGS -pg" CPPFLAGS="$CPPFLAGS -pg" CXXFLAGS="$CXXFLAGS -pg" ;; no) ;; *) AC_MSG_ERROR(bad value ${enableval} for --profiling) ;; esac ], [ enable_profiling=no ]) AC_MSG_RESULT([$enable_profiling]) AM_CONDITIONAL(GPROF_PROFILING, [test "$enable_profiling" = "yes"]) PKG_CHECK_MODULES([GSF], [libgsf-1]) PKG_CHECK_MODULES([ZLIB], [zlib]) AC_OUTPUT( \ Makefile \ html/Makefile \ libpst.pc \ libpst.spec \ man/Makefile \ src/Makefile \ src/pst2dii.cpp \ python/Makefile \ xml/Makefile \ xml/libpst \ ) diff --git a/libpst.spec.in b/libpst.spec.in index f83fdda..615d384 100644 --- a/libpst.spec.in +++ b/libpst.spec.in @@ -1,638 +1,697 @@ +%if "%{?dist}" == ".el8" + %define fedora 32 +%endif + +%if 0%{?fedora} > 27 || 0%{?rhel} >= 9 +%global use_python3 1 +%define __python %{__python3} +%endif + +%if 0%{?rhel} >= 9 +%global with_dii 0 +%else +%global with_dii 1 +%endif Summary: Utilities to convert Outlook .pst files to other formats Name: @PACKAGE@ Version: @VERSION@ Release: 1%{?dist} License: GPLv2+ URL: http://www.five-ten-sg.com/%{name}/ Source: %{url}/packages/%{name}-%{version}.tar.gz +BuildRequires: make BuildRequires: libtool gcc-c++ -BuildRequires: ImageMagick gd-devel zlib-devel boost-devel libgsf-devel gettext-devel +BuildRequires: gd-devel zlib-devel boost-devel libgsf-devel gettext-devel -%if "%{?dist}" == ".el8" - %define fedora 32 +%if 0%{with_dii} +BuildRequires: ImageMagick %endif -%if 0%{?fedora} > 27 + +%if 0%{?use_python3} BuildRequires: python3 python3-devel boost-python3 boost-python3-devel Requires: boost-python3 %else BuildRequires: python-devel %endif -Requires: ImageMagick%{?_isa} libgsf gettext +Requires: libgsf gettext Requires: %{name}-libs%{?_isa} = %{version}-%{release} -%if "%{?dist}" == ".el8" - %define __python /usr/bin/python3 - %{!?python_sitelib: %global python_sitelib %(python3 -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")} - %{!?python_sitearch: %global python_sitearch %(python3 -c "from distutils.sysconfig import get_python_lib; print get_python_lib(1)")} -%else - %{!?python_sitelib: %global python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")} - %{!?python_sitearch: %global python_sitearch %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib(1)")} +%if 0%{with_dii} +Requires: ImageMagick%{?_isa} %endif +%{!?python_sitelib: %global python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")} +%{!?python_sitearch: %global python_sitearch %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib(1)")} + + +%if 0%{with_dii} %description The Libpst utilities include readpst which can convert email messages to both mbox and MH mailbox formats, pst2ldif which can convert the contacts to .ldif format for import into ldap databases, and pst2dii which can convert email messages to the DII load file format used by Summation. +%else +%description +The Libpst utilities include readpst which can convert email messages +to both mbox and MH mailbox formats, pst2ldif which can convert the +contacts to .ldif format for import into ldap databases. +%endif %package libs Summary: Shared library used by the pst utilities %description libs The libpst-libs package contains the shared library used by the pst utilities. -%if 0%{?fedora} > 27 +%if 0%{?use_python3} %package -n python3-%{name} Requires: python3 Provides: %{name}-python = %{version}-%{release} %else %package python Requires: python %endif Summary: Python bindings for libpst Requires: %{name}-libs%{?_isa} = %{version}-%{release} -%if 0%{?fedora} >= 20 +%if 0%{?fedora} >= 20 || 0%{?rhel} >= 9 %global __provides_exclude_from %{?__provides_exclude_from:%__provides_exclude_from|}^%{python_sitearch}/_.*\.so$ %else %{?filter_setup: %filter_provides_in %{python_sitearch}/_.*\.so$ %filter_setup } %endif -%if 0%{?fedora} > 27 +%if 0%{?use_python3} %description -n python3-%{name} %else %description python %endif The libpst-python package allows you to use the libpst shared object from Python code. %package devel Summary: Library links and header files for libpst application development Requires: pkgconfig Requires: %{name}-libs%{?_isa} = %{version}-%{release} %description devel The libpst-devel package contains the library links and header files you'll need to develop applications using the libpst shared library. You do not need to install it if you just want to use the libpst utilities. %package devel-doc Summary: Documentation for libpst.so for libpst application development Requires: %{name}-doc = %{version}-%{release} %description devel-doc The libpst-devel-doc package contains the doxygen generated documentation for the libpst.so shared library. %package doc Summary: Documentation for the pst utilities in html format %description doc The libpst-doc package contains the html documentation for the pst utilities. You do not need to install it if you just want to use the libpst utilities. %prep %setup -q %build autoreconf -fiv %configure --enable-libpst-shared \ +%if 0%{with_dii} + --enable-dii \ +%else + --disable-dii \ +%endif --with-boost-python=boost_python%{python3_version_nodots} -%if 0%{?fedora} > 27 +%if 0%{?use_python3} %make_build %else make %{?_smp_mflags} %endif %install -%if 0%{?fedora} > 27 +%if 0%{?use_python3} %make_install %else rm -rf $RPM_BUILD_ROOT make DESTDIR=$RPM_BUILD_ROOT install %endif #Remove libtool archives. find %{buildroot} -name '*.la' -or -name '*.a' | xargs rm -f mv %{buildroot}%{_datadir}/doc/%{name}-%{version} %{buildroot}%{_datadir}/doc/%{name} +# Remove pst2dii man page, when it's not built +%if !0%{with_dii} +rm %{buildroot}%{_mandir}/man1/pst2dii.1* +%endif -%if 0%{?fedora} > 27 +%if 0%{?use_python3} %ldconfig_scriptlets libs %else %post libs -p /sbin/ldconfig %postun libs -p /sbin/ldconfig %endif %files %{_bindir}/* %{_mandir}/man1/* %{_mandir}/man5/* %files libs %{_libdir}/libpst.so.* %doc COPYING -%if 0%{?fedora} > 27 +%if 0%{?use_python3} %files -n python3-%{name} %defattr(-,root,root,-) %{python3_sitearch}/_*.so %else %files python %{python_sitearch}/_*.so %endif %files devel %{_libdir}/libpst.so %{_includedir}/%{name}-@LIBPST_SO_MAJOR@/ %{_libdir}/pkgconfig/libpst.pc %files devel-doc %{_datadir}/doc/%{name}/devel/ %files doc %dir %{_datadir}/doc/%{name}/ %{_datadir}/doc/%{name}/*.html %{_datadir}/doc/%{name}/AUTHORS %{_datadir}/doc/%{name}/COPYING %{_datadir}/doc/%{name}/ChangeLog %{_datadir}/doc/%{name}/NEWS %{_datadir}/doc/%{name}/README %changelog +* Sat Mar 27 2021 Carl Byington 0.6.76-1 +- Stuart C. Naifeh - fix rfc2231 encoding when saving messages to + both .eml and .msg formats. + +* Tue Feb 02 2021 Milan Crha - 0.6.75-9 +- Resolves: #1913613 (Disable DII (and ImageMagic dependency) for RHEL 9) + +* Tue Jan 26 2021 Fedora Release Engineering - 0.6.75-8 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild + +* Fri Jan 22 2021 Jonathan Wakely - 0.6.75-7 +- Rebuilt for Boost 1.75 + +* Tue Jul 28 2020 Merlin Mathesius - 0.6.75-6 +- FTBFS fix: %%{__python} must now be explicitly defined + +* Tue Jul 28 2020 Fedora Release Engineering - 0.6.75-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Thu Jul 16 2020 Merlin Mathesius - 0.6.75-4 +- Cleanup conditionals for using python3 + +* Fri May 29 2020 Jonathan Wakely - 0.6.75-3 +- Rebuilt for Boost 1.73 + +* Tue May 26 2020 Miro Hrončok - 0.6.75-2 +- Rebuilt for Python 3.9 + * Sun Mar 22 2020 Carl Byington 0.6.75-1 - Markus Schnalke - fix from Debian for vcard version format. * Wed Jan 29 2020 Fedora Release Engineering - 0.6.74-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild * Sun Jan 12 2020 Carl Byington 0.6.74-1 - Paul Wise - many changes for debian: - Add missing linking with zlib and libpthread/librt - Use PKG_CHECK_MODULES to find the gsf-1 library - Fix usage of indefinite articles - Fix a number of spelling mistakes - Use plain make when building from Mercurial - Add operator and quotes to the AX_PYTHON_DEVEL parameter - Remove files copied in by autotools - Add AM_GNU_GETTEXT macros - Rename configure.in to configure.ac - add extern "C" to header for use with C++ code * Mon Aug 19 2019 Miro Hrončok - 0.6.72-6 - Rebuilt for Python 3.8 * Thu Jul 25 2019 Fedora Release Engineering - 0.6.72-5 - Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild * Thu Jul 25 2019 Carl Byington 0.6.73-1 - Tim Dufrane - fix segfault in pst_close() * Sat Jun 08 2019 Leigh Scott - 0.6.72-4 - Add configure option for boost-python - Remove all old fedora conditionals - Update spec file to comply with packaging guidelines * Fri Feb 01 2019 Fedora Release Engineering - 0.6.72-3 - Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild * Wed Jan 30 2019 Jonathan Wakely - 0.6.72-2 - Rebuilt for Boost 1.69 * Wed Aug 01 2018 Carl Byington 0.6.72-1 - allow all 7 days in bydays recurring appointment - update for Fedora Python packaging - Alfredo Esteban - add -l and -f options to lspst * Fri Jul 13 2018 Fedora Release Engineering - 0.6.71-8 - Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild * Wed Feb 07 2018 Fedora Release Engineering - 0.6.71-7 - Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild * Sun Aug 20 2017 Zbigniew Jędrzejewski-Szmek - 0.6.71-6 - Add Provides for the old name without %%_isa * Sat Aug 19 2017 Zbigniew Jędrzejewski-Szmek - 0.6.71-5 - Python 2 binary package renamed to python2-libpst See https://fedoraproject.org/wiki/FinalizingFedoraSwitchtoPython3 * Sat Aug 19 2017 Zbigniew Jędrzejewski-Szmek - 0.6.71-4 - Python 2 binary package renamed to python2-libpst See https://fedoraproject.org/wiki/FinalizingFedoraSwitchtoPython3 * Thu Aug 03 2017 Fedora Release Engineering - 0.6.71-3 - Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild * Wed Jul 26 2017 Fedora Release Engineering - 0.6.71-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild * Fri Jul 21 2017 Carl Byington 0.6.71-1 - Fedora Python naming scheme changes - Zachary Travis - Add support for the OST 2013 format, and Content-Disposition filename key fix for outlook compatibility * Thu Jul 20 2017 Kalev Lember - 0.6.70-3 - Rebuilt for Boost 1.64 * Fri Jul 07 2017 Igor Gnatenko - 0.6.70-2 - Rebuild due to bug in RPM (RHBZ #1468476) * Wed Feb 08 2017 Carl Byington 0.6.70-1 - Jeffrey Morlan - pst_getID2 must not recurse into children * Fri Jan 27 2017 Jonathan Wakely - 0.6.69-2 - Rebuilt for Boost 1.63 * Sat Oct 29 2016 Carl Byington 0.6.69-1 - fix bugs in code allowing folders containing multiple item types * Mon Aug 29 2016 Carl Byington 0.6.68-1 - allow folders containing multiple item types, e.g. email and calendar - better detection of valid internet headers * Tue Jul 19 2016 Fedora Release Engineering - 0.6.67-2 - https://fedoraproject.org/wiki/Changes/Automatic_Provides_for_Python_RPM_Packages * Wed Jul 06 2016 Carl Byington 0.6.67-1 - Jeffrey Morlan - multiple bug fixes and an optimization * Thu Feb 04 2016 Fedora Release Engineering - 0.6.66-3 - Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild * Fri Jan 15 2016 Jonathan Wakely - 0.6.66-2 - Rebuilt for Boost 1.60 * Mon Dec 21 2015 Carl Byington 0.6.66-1 - Igor Stroh - Added Content-ID header support * Fri Sep 11 2015 Carl Byington 0.6.65-1 - Jeffrey Morlan - fix multiple Content-Type headers - Hans Liss - debug level output * Thu Aug 27 2015 Jonathan Wakely - 0.6.64-6 - Rebuilt for Boost 1.59 * Wed Jul 29 2015 Fedora Release Engineering - 0.6.64-5 - Rebuilt for https://fedoraproject.org/wiki/Changes/F23Boost159 * Wed Jul 22 2015 David Tardon - 0.6.64-4 - rebuild for Boost 1.58 * Wed Jun 17 2015 Fedora Release Engineering - 0.6.64-3 - Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild * Sat May 02 2015 Kalev Lember - 0.6.64-2 - Rebuilt for GCC 5 C++11 ABI change * Mon Mar 09 2015 Carl Byington 0.6.64-1 - fix line wrap on Python provides_exclude_from - fix unchecked errors found by cppcheck - AJ Shankar fixes for attachment processing and body encodings that contain embedded null chars. * Mon Jan 26 2015 Petr Machata - 0.6.63-5 - Rebuild for boost 1.57.0 * Sun Aug 17 2014 Fedora Release Engineering - 0.6.63-4 - Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild * Sat Jun 07 2014 Fedora Release Engineering - 0.6.63-3 - Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild * Fri May 23 2014 David Tardon - 0.6.63-2 - rebuild for boost 1.55.0 * Fri Dec 27 2013 Carl Byington 0.6.63-1 - Daniel Gryniewicz found buffer overrun in LIST_COPY_TIME * Sun Sep 22 2013 Carl Byington 0.6.62-1 - 983596 - Old dependency filter breaks file coloring * Tue Aug 06 2013 Carl Byington 0.6.61-1 - move documentation to unversioned directory * Sat Aug 03 2013 Fedora Release Engineering - 0.6.59-4 - Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild * Sat Jul 27 2013 pmachata@redhat.com - 0.6.59-3 - Rebuild for boost 1.54.0 * Wed Jun 12 2013 Carl Byington 0.6.60-1 - patch from Dominique Leuenberger to add AC_USE_SYSTEM_EXTENSIONS - add readpst -a option for attachment stripping * Tue Jun 11 2013 Remi Collet - 0.6.59-2 - rebuild for new GD 2.1.0 * Fri May 17 2013 Carl Byington 0.6.59-1 - add autoconf checking for libgsf * Fri Mar 29 2013 Carl Byington 0.6.58-4 - add autoreconf for aarch64 * Sun Feb 10 2013 Denis Arnaud - 0.6.58-3 - Rebuild for Boost-1.53.0 * Sat Feb 09 2013 Denis Arnaud - 0.6.58-2 - Rebuild for Boost-1.53.0 * Fri Dec 28 2012 Carl Byington - 0.6.58-1 - fix From quoting on embedded rfc/822 messages * Wed Dec 26 2012 Carl Byington - 0.6.57-1 - bugzilla 852414, remove unnecessary dependencies * Mon Dec 24 2012 Carl Byington - 0.6.56-1 - filter private provides from rpm - merge -m .msg files code into main branch * Thu Aug 09 2012 Carl Byington - 0.6.55-2 - rebuild for Python * Thu Jul 19 2012 Fedora Release Engineering - 0.6.54-6 - Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild * Tue May 08 2012 Carl Byington - 0.6.55-1 - preserve bcc headers - document -C switch to set default character set - space after colon is not required in header fields * Tue Feb 28 2012 Fedora Release Engineering - 0.6.54-5 - Rebuilt for c++ ABI breakage * Fri Jan 13 2012 Fedora Release Engineering - 0.6.54-4 - Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild * Sat Dec 24 2011 Carl Byington - 0.6.54-3 - bump versions and prep for Fedora build * Wed Nov 30 2011 Petr Pisar - 0.6.53-3 - Rebuild against boost-1.48 * Mon Nov 14 2011 Carl Byington - 0.6.54-2 - failed to bump version number * Fri Nov 04 2011 Carl Byington - 0.6.54-1 - embedded rfc822 messages might contain rtf encoded bodies * Fri Sep 02 2011 Petr Pisar - 0.6.53-2 - Rebuild against boost-1.47 * Sun Jul 10 2011 Carl Byington - 0.6.53-1 - add Status: header in output - allow fork for parallel processing of individual email folders in separate mode - proper handling of --with-boost-python option * Sun May 22 2011 Carl Byington - 0.6.52-1 - fix dangling freed pointer in embedded rfc822 message processing - allow broken outlook internet header field - it sometimes contains fragments of the message body rather than headers * Sun Apr 17 2011 Carl Byington - 0.6.51-1 - fix for buffer overrun; attachment size from the secondary list of mapi elements overwrote proper size from the primary list of mapi elements. Fedora bugzilla 696263 * Tue Feb 08 2011 Fedora Release Engineering - 0.6.49-4 - Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild * Mon Feb 07 2011 Thomas Spura - 0.6.49-3 - rebuild for new boost * Fri Dec 24 2010 Carl Byington - 0.6.50-1 - rfc2047 and rfc2231 encoding for non-ascii headers and attachment filenames. * Wed Sep 29 2010 jkeating - 0.6.49-2 - Rebuilt for gcc bug 634757 * Mon Sep 13 2010 Carl Byington - 0.6.49-1 - fix to ignore embedded objects that are not email messages Fedora bugzilla 633498 * Thu Sep 02 2010 Carl Byington - 0.6.48-1 - fix for broken internet headers from Outlook - fix ax_python.m4 to look for python2.7 - use mboxrd from quoting for output formats with multiple messages per file - use no from quoting for output formats with single message per file * Sat Jul 31 2010 Carl Byington - 0.6.47-6 - rebuild for Python dependencies * Mon Jul 26 2010 David Malcolm - 0.6.47-4 - hack up configure so that it looks for Python 2.7 * Wed Jul 21 2010 David Malcolm - 0.6.47-3 - Rebuilt for https://fedoraproject.org/wiki/Features/Python_2.7/MassRebuild * Wed Jul 07 2010 Carl Byington - 0.6.47-2 - Subpackage Licensing, add COPYING to -libs. - patches from Kenneth Berland for solaris * Fri May 07 2010 Carl Byington - 0.6.47-1 - patches from Kenneth Berland for solaris * Thu Jan 21 2010 Carl Byington - 0.6.46-1 - prefer libpthread over librt for finding sem_init function. * Thu Jan 21 2010 Carl Byington - 0.6.45-2 - rebuild for new boost package * Wed Nov 18 2009 Carl Byington - 0.6.45-1 - patch from Hugo DesRosiers to export categories and notes into vcards. - extend that patch to export categories into vcalendar appointments also. * Sun Sep 20 2009 Carl Byington - 0.6.44-1 - patch from Lee Ayres to add file name extensions in separate mode. - allow mixed items types in a folder in separate mode. * Sat Sep 12 2009 Carl Byington - 0.6.43-1 - decode more of the pst format, some minor bug fixes - add support for code pages 1200 and 1201. - add readpst -t option to select output item types, which can now be used to process folders containing mixed item types. - fix segfault with embedded appointments - add readpst -u option for Thunderbird mode .size and .type files - better detection of embedded rfc822 message attachments * Thu Sep 03 2009 Carl Byington - 0.6.42-1 - patch from Fridrich Strba to build with DJGPP DOS cross-compiler. * Sat Jul 25 2009 Fedora Release Engineering - 0.6.41-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild * Tue Jun 23 2009 Carl Byington - 0.6.41-1 - fix ax_python detection - should not use locate command - checking for Fedora versions is not needed * Tue Jun 23 2009 Carl Byington - 0.6.40-1 - Fedora 11 has python2.6 - remove pdf version of the man pages * Sun Jun 21 2009 Carl Byington - 0.6.39-1 - Fedora > 10 moved to boost-python-devel * Sun Jun 21 2009 Carl Byington - 0.6.38-1 - add Python interface to the shared library. - bump soname to version 4 for many changes to the interface. - better decoding of recurrence data in appointments. - remove readpstlog since debug log files are now plain text. - add readpst -j option for parallel jobs for each folder. - make nested mime multipart/alternative to hold the text/html parts. * Fri Apr 17 2009 Carl Byington - 0.6.37-1 - add pst_attach_to_mem() back into the shared library interface. - fix memory leak caught by valgrind. * Tue Apr 14 2009 Carl Byington - 0.6.36-1 - build separate -doc and -devel-doc subpackages. - other spec file cleanup * Wed Apr 08 2009 Carl Byington - 0.6.35-1 - properly add trailing mime boundary in all modes. - build separate libpst, libpst-libs, libpst-devel rpms. * Thu Mar 19 2009 Carl Byington - 0.6.34-1 - avoid putting mixed item types into the same output folder. * Tue Mar 17 2009 Carl Byington - 0.6.33-1 - compensate for iconv conversion to utf-7 that produces strings that are not null terminated. - don't produce empty attachment files in separate mode. * Sat Mar 14 2009 Carl Byington - 0.6.32-1 - fix ppc64 compile error * Sat Mar 14 2009 Carl Byington - 0.6.31-1 - bump version for Fedora cvs tagging mistake * Sat Mar 14 2009 Carl Byington - 0.6.30-1 - track character set individually for each mapi element. - remove charset option from pst2ldif since we get that from each object now. - avoid emitting bogus empty email messages into contacts and calendar files. * Tue Feb 24 2009 Carl Byington - 0.6.29-1 - fix for 64bit on Fedora 11 * Tue Feb 24 2009 Carl Byington - 0.6.28-1 - improve decoding of multipart/report and message/rfc822 mime types. - improve character set handling. - fix embedded rfc822 messages with attachments. * Sat Feb 07 2009 Carl Byington - 0.6.27-1 - fix for const correctness on Fedora 11 * Sat Feb 07 2009 Carl Byington - 0.6.26-1 - patch from Fridrich Strba for building on mingw and general - cleanup of autoconf files. - add processing for pst files of type 0x0f. - strip and regenerate all MIME headers to avoid duplicates. - do a better job of making unique MIME boundaries. - only use base64 coding when strictly necessary. * Fri Jan 16 2009 Carl Byington - 0.6.25-1 - improve handling of content-type charset values in mime parts * Thu Dec 11 2008 Carl Byington - 0.6.24-1 - patch from Chris Eagle to build on cygwin * Thu Dec 04 2008 Carl Byington - 0.6.23-1 - bump version to avoid cvs tagging mistake in fedora * Fri Nov 28 2008 Carl Byington - 0.6.22-1 - patch from David Cuadrado to process emails with type PST_TYPE_OTHER - base64_encode_multiple() may insert newline, needs larger malloc - subject lines shorter than 2 bytes could segfault * Tue Oct 21 2008 Carl Byington - 0.6.21-1 - fix title bug with old schema in pst2ldif. - also escape commas in distinguished names per rfc4514. * Thu Oct 09 2008 Carl Byington - 0.6.20-1 - add configure option --enable-dii=no to remove dependency on libgd. - many fixes in pst2ldif by Robert Harris. - add -D option to include deleted items, from Justin Greer - fix from Justin Greer to add missing email headers - fix from Justin Greer for my_stristr() - fix for orphan children when building descriptor tree - avoid writing uninitialized data to debug log file - remove unreachable code - create dummy top-of-folder descriptor if needed for corrupt pst files * Sun Sep 14 2008 Carl Byington - 0.6.19-1 - Fix base64 encoding that could create long lines. - Initial work on a .so shared library from Bharath Acharya. * Thu Aug 28 2008 Carl Byington - 0.6.18-1 - Fixes for iconv on Mac from Justin Greer. * Tue Aug 05 2008 Carl Byington - 0.6.17-1 - More fixes for 32/64 bit portability on big endian ppc. * Tue Aug 05 2008 Carl Byington - 0.6.16-1 - Use inttypes.h for portable printing of 64 bit items. * Wed Jul 30 2008 Carl Byington - 0.6.15-1 - Patch from Robert Simpson for file handle leak in error case. - Fix for missing length on lz decompression, bug found by Chris White. * Sun Jun 15 2008 Carl Byington - 0.6.14-1 - Fix my mistake in Debian packaging. * Fri Jun 13 2008 Carl Byington - 0.6.13-1 - Patch from Robert Simpson for encryption type 2. * Tue Jun 10 2008 Carl Byington - 0.6.12-1 - Patch from Joachim Metz for Debian packaging and - fix for incorrect length on lz decompression * Tue Jun 03 2008 Carl Byington - 0.6.11-1 - Use ftello/fseeko to properly handle large files. - Document and properly use datasize field in b5 blocks. - Fix some MSVC compile issues and collect MSVC dependencies into one place. * Thu May 29 2008 Carl Byington - 0.6.10-1 - Patch from Robert Simpson for doubly-linked list code and arrays of unicode strings. * Fri May 16 2008 Carl Byington - 0.6.9 - Patch from Joachim Metz for 64 bit compile. - Fix pst format documentation for 8 byte backpointers. * Wed Mar 05 2008 Carl Byington - 0.6.8 - Initial version of pst2dii to convert to Summation dii load file format - changes for Fedora packaging guidelines (#434727) * Tue Jul 10 2007 Carl Byington - 0.5.5 - merge changes from Joe Nahmias version * Sun Feb 19 2006 Carl Byington - 0.5.3 - initial spec file using autoconf and http://www.fedora.us/docs/rpm-packaging-guidelines.html diff --git a/src/readpst.c b/src/readpst.c index 53224fe..6d94f15 100644 --- a/src/readpst.c +++ b/src/readpst.c @@ -1,2334 +1,2371 @@ /*** * readpst.c * Part of the LibPST project * Written by David Smith * dave.s@earthcorp.com */ #include "define.h" #include "lzfu.h" #include "msg.h" #define OUTPUT_TEMPLATE "%s.%s" #define OUTPUT_KMAIL_DIR_TEMPLATE ".%s.directory" #define KMAIL_INDEX "../.%s.index" #define SEP_MAIL_FILE_TEMPLATE "%i%s" // max size of the c_time char*. It will store the date of the email #define C_TIME_SIZE 500 struct file_ll { char *name[PST_TYPE_MAX]; char *dname; FILE * output[PST_TYPE_MAX]; int32_t stored_count; int32_t item_count; int32_t skip_count; }; int grim_reaper(); pid_t try_fork(char* folder); void process(pst_item *outeritem, pst_desc_tree *d_ptr); void write_email_body(FILE *f, char *body); void removeCR(char *c); void usage(); void version(); void mk_kmail_dir(char* fname); int close_kmail_dir(); void mk_recurse_dir(char* dir); int close_recurse_dir(); void mk_separate_dir(char *dir); int close_separate_dir(); void mk_separate_file(struct file_ll *f, int32_t t, char *extension, int openit); void close_separate_file(struct file_ll *f); char* my_stristr(char *haystack, char *needle); void check_filename(char *fname); int acceptable_ext(pst_item_attach* attach); void write_separate_attachment(char f_name[], pst_item_attach* attach, int attach_num, pst_file* pst); void write_embedded_message(FILE* f_output, pst_item_attach* attach, char *boundary, pst_file* pf, int save_rtf, char** extra_mime_headers); void write_inline_attachment(FILE* f_output, pst_item_attach* attach, char *boundary, pst_file* pst); int valid_headers(char *header); void header_has_field(char *header, char *field, int *flag); void header_get_subfield(char *field, const char *subfield, char *body_subfield, size_t size_subfield); char* header_get_field(char *header, char *field); char* header_end_field(char *field); void header_strip_field(char *header, char *field); int test_base64(char *body, size_t len); void find_html_charset(char *html, char *charset, size_t charsetlen); void find_rfc822_headers(char** extra_mime_headers); void write_body_part(FILE* f_output, pst_string *body, char *mime, char *charset, char *boundary, pst_file* pst); void write_schedule_part_data(FILE* f_output, pst_item* item, const char* sender, const char* method); void write_schedule_part(FILE* f_output, pst_item* item, const char* sender, const char* boundary); void write_normal_email(FILE* f_output, char f_name[], pst_item* item, int mode, int mode_MH, pst_file* pst, int save_rtf, int embedding, char** extra_mime_headers); void write_vcard(FILE* f_output, pst_item *item, pst_item_contact* contact, char comment[]); int write_extra_categories(FILE* f_output, pst_item* item); void write_journal(FILE* f_output, pst_item* item); void write_appointment(FILE* f_output, pst_item *item); void create_enter_dir(struct file_ll* f, pst_item *item); void close_enter_dir(struct file_ll *f); char* quote_string(char *inp); const char* prog_name; char* output_dir = "."; // Normal mode just creates mbox format files in the current directory. Each file is named // the same as the folder's name that it represents #define MODE_NORMAL 0 // KMail mode creates a directory structure suitable for being used directly // by the KMail application #define MODE_KMAIL 1 // recurse mode creates a directory structure like the PST file. Each directory // contains only one file which stores the emails in mboxrd format. #define MODE_RECURSE 2 // separate mode creates the same directory structure as recurse. The emails are stored in // separate files, numbering from 1 upward. Attachments belonging to the emails are // saved as email_no-filename (e.g. 1-samplefile.doc or 1-Attachment2.zip) #define MODE_SEPARATE 3 // Output Normal just prints the standard information about what is going on #define OUTPUT_NORMAL 0 // Output Quiet is provided so that only errors are printed #define OUTPUT_QUIET 1 // default mime-type for attachments that have a null mime-type #define MIME_TYPE_DEFAULT "application/octet-stream" #define RFC822 "message/rfc822" // output mode for contacts #define CMODE_VCARD 0 #define CMODE_LIST 1 // output mode for deleted items #define DMODE_EXCLUDE 0 #define DMODE_INCLUDE 1 // Output type mode flags #define OTMODE_EMAIL 1 #define OTMODE_APPOINTMENT 2 #define OTMODE_JOURNAL 4 #define OTMODE_CONTACT 8 // output settings for RTF bodies // filename for the attachment #define RTF_ATTACH_NAME "rtf-body.rtf" // mime type for the attachment #define RTF_ATTACH_TYPE "application/rtf" // global settings int mode = MODE_NORMAL; int mode_MH = 0; // a submode of MODE_SEPARATE int mode_EX = 0; // a submode of MODE_SEPARATE int mode_MSG = 0; // a submode of MODE_SEPARATE int mode_thunder = 0; // a submode of MODE_RECURSE int output_mode = OUTPUT_NORMAL; int contact_mode = CMODE_VCARD; int deleted_mode = DMODE_EXCLUDE; int output_type_mode = 0xff; // Default to all. int contact_mode_specified = 0; int overwrite = 0; int prefer_utf8 = 0; int save_rtf_body = 1; int file_name_len = 10; // enough room for MODE_SPEARATE file name pst_file pstfile; regex_t meta_charset_pattern; char* default_charset = NULL; char* acceptable_extensions = NULL; int number_processors = 1; // number of cpus we have int max_children = 0; // based on number of cpus and command line args int max_child_specified = 0;// have command line arg -j int active_children; // number of children of this process, cannot be larger than max_children pid_t* child_processes; // setup by main(), and at the start of new child process #ifdef HAVE_SEMAPHORE_H int shared_memory_id; sem_t* global_children = NULL; sem_t* output_mutex = NULL; #endif int grim_reaper(int waitall) { int available = 0; #ifdef HAVE_FORK #ifdef HAVE_SEMAPHORE_H if (global_children) { //sem_getvalue(global_children, &available); //printf("grim reaper %s for pid %d (parent %d) with %d children, %d available\n", (waitall) ? "all" : "", getpid(), getppid(), active_children, available); //fflush(stdout); int i,j; for (i=0; i 0 but active_children == max_children if (available && active_children < max_children) { sem_wait(global_children); pid_t child = fork(); if (child < 0) { // fork failed, pretend it worked and we are the child return 0; } else if (child == 0) { // fork worked, and we are the child, reinitialize *our* list of children active_children = 0; memset(child_processes, 0, sizeof(pid_t) * max_children); pst_reopen(&pstfile); // close and reopen the pst file to get an independent file position pointer } else { // fork worked, and we are the parent, record this child that we need to wait for //pid_t me = getpid(); //printf("parent %d forked child pid %d to process folder %s\n", me, child, folder); //fflush(stdout); child_processes[active_children++] = child; } return child; } else { return 0; // pretend to have forked and we are the child } #endif #endif return 0; } void process(pst_item *outeritem, pst_desc_tree *d_ptr) { struct file_ll ff; pst_item *item = NULL; DEBUG_ENT("process"); create_enter_dir(&ff, outeritem); for (; d_ptr; d_ptr = d_ptr->next) { DEBUG_INFO(("New item record\n")); if (!d_ptr->desc) { ff.skip_count++; DEBUG_WARN(("ERROR item's desc record is NULL\n")); continue; } DEBUG_INFO(("Desc Email ID %#"PRIx64" [d_ptr->d_id = %#"PRIx64"]\n", d_ptr->desc->i_id, d_ptr->d_id)); item = pst_parse_item(&pstfile, d_ptr, NULL); DEBUG_INFO(("About to process item\n")); if (!item) { ff.skip_count++; DEBUG_INFO(("A NULL item was seen\n")); continue; } if (item->subject.str) { DEBUG_INFO(("item->subject = %s\n", item->subject.str)); } if (item->folder && item->file_as.str) { DEBUG_INFO(("Processing Folder \"%s\"\n", item->file_as.str)); if (output_mode != OUTPUT_QUIET) { pst_debug_lock(); printf("Processing Folder \"%s\"\n", item->file_as.str); fflush(stdout); pst_debug_unlock(); } ff.item_count++; if (d_ptr->child && (deleted_mode == DMODE_INCLUDE || strcasecmp(item->file_as.str, "Deleted Items"))) { //if this is a non-empty folder other than deleted items, we want to recurse into it pid_t parent = getpid(); pid_t child = try_fork(item->file_as.str); if (child == 0) { // we are the child process, or the original parent if no children were available pid_t me = getpid(); process(item, d_ptr->child); #ifdef HAVE_FORK #ifdef HAVE_SEMAPHORE_H if (me != parent) { // we really were a child, forked for the sole purpose of processing this folder // free my child count slot before really exiting, since // all I am doing here is waiting for my children to exit sem_post(global_children); grim_reaper(1); // wait for all my child processes to exit exit(0); // really exit } #endif #endif } } } else if (item->contact && (item->type == PST_TYPE_CONTACT)) { DEBUG_INFO(("Processing Contact\n")); if (!(output_type_mode & OTMODE_CONTACT)) { ff.skip_count++; DEBUG_INFO(("skipping contact: not in output type list\n")); } else { ff.item_count++; if (mode == MODE_SEPARATE) mk_separate_file(&ff, PST_TYPE_CONTACT, (mode_EX) ? ".vcf" : "", 1); if (contact_mode == CMODE_VCARD) { pst_convert_utf8_null(item, &item->comment); write_vcard(ff.output[PST_TYPE_CONTACT], item, item->contact, item->comment.str); } else { pst_convert_utf8(item, &item->contact->fullname); pst_convert_utf8(item, &item->contact->address1); fprintf(ff.output[PST_TYPE_CONTACT], "%s <%s>\n", item->contact->fullname.str, item->contact->address1.str); } if (mode == MODE_SEPARATE) close_separate_file(&ff); } } else if (item->email && ((item->type == PST_TYPE_NOTE) || (item->type == PST_TYPE_SCHEDULE) || (item->type == PST_TYPE_REPORT))) { DEBUG_INFO(("Processing Email\n")); if (!(output_type_mode & OTMODE_EMAIL)) { ff.skip_count++; DEBUG_INFO(("skipping email: not in output type list\n")); } else { char *extra_mime_headers = NULL; ff.item_count++; if (mode == MODE_SEPARATE) { // process this single email message, possibly forking pid_t parent = getpid(); pid_t child = try_fork(item->file_as.str); if (child == 0) { // we are the child process, or the original parent if no children were available pid_t me = getpid(); mk_separate_file(&ff, PST_TYPE_NOTE, (mode_EX) ? ".eml" : "", 1); write_normal_email(ff.output[PST_TYPE_NOTE], ff.name[PST_TYPE_NOTE], item, mode, mode_MH, &pstfile, save_rtf_body, PST_TYPE_NOTE, &extra_mime_headers); close_separate_file(&ff); if (mode_MSG) { mk_separate_file(&ff, PST_TYPE_NOTE, ".msg", 0); write_msg_email(ff.name[PST_TYPE_NOTE], item, &pstfile); } #ifdef HAVE_FORK #ifdef HAVE_SEMAPHORE_H if (me != parent) { // we really were a child, forked for the sole purpose of processing this message // free my child count slot before really exiting, since // all I am doing here is waiting for my children to exit sem_post(global_children); grim_reaper(1); // wait for all my child processes to exit - there should not be any exit(0); // really exit } #endif #endif } } else { // process this single email message, cannot fork since not separate mode write_normal_email(ff.output[PST_TYPE_NOTE], ff.name[PST_TYPE_NOTE], item, mode, mode_MH, &pstfile, save_rtf_body, 0, &extra_mime_headers); } } } else if (item->journal && (item->type == PST_TYPE_JOURNAL)) { DEBUG_INFO(("Processing Journal Entry\n")); if (!(output_type_mode & OTMODE_JOURNAL)) { ff.skip_count++; DEBUG_INFO(("skipping journal entry: not in output type list\n")); } else { ff.item_count++; if (mode == MODE_SEPARATE) mk_separate_file(&ff, PST_TYPE_JOURNAL, (mode_EX) ? ".ics" : "", 1); write_journal(ff.output[PST_TYPE_JOURNAL], item); fprintf(ff.output[PST_TYPE_JOURNAL], "\n"); if (mode == MODE_SEPARATE) close_separate_file(&ff); } } else if (item->appointment && (item->type == PST_TYPE_APPOINTMENT)) { DEBUG_INFO(("Processing Appointment Entry\n")); if (!(output_type_mode & OTMODE_APPOINTMENT)) { ff.skip_count++; DEBUG_INFO(("skipping appointment: not in output type list\n")); } else { ff.item_count++; if (mode == MODE_SEPARATE) mk_separate_file(&ff, PST_TYPE_APPOINTMENT, (mode_EX) ? ".ics" : "", 1); write_schedule_part_data(ff.output[PST_TYPE_APPOINTMENT], item, NULL, NULL); fprintf(ff.output[PST_TYPE_APPOINTMENT], "\n"); if (mode == MODE_SEPARATE) close_separate_file(&ff); } } else if (item->message_store) { // there should only be one message_store, and we have already done it ff.skip_count++; DEBUG_WARN(("item with message store content, type %i %s, skipping it\n", item->type, item->ascii_type)); } else { ff.skip_count++; DEBUG_WARN(("Unknown item type %i (%s) name (%s)\n", item->type, item->ascii_type, item->file_as.str)); } pst_freeItem(item); } close_enter_dir(&ff); DEBUG_RET(); } int main(int argc, char* const* argv) { pst_item *item = NULL; pst_desc_tree *d_ptr; char * fname = NULL; char *d_log = NULL; int c,x; char *temp = NULL; //temporary char pointer prog_name = argv[0]; time_t now = time(NULL); srand((unsigned)now); if (regcomp(&meta_charset_pattern, "]*content=\"[^>]*charset=([^>\";]*)[\";]", REG_ICASE | REG_EXTENDED)) { printf("cannot compile regex pattern to find content charset in html bodies\n"); exit(3); } // command-line option handling while ((c = getopt(argc, argv, "a:bC:c:Dd:emhj:kMo:qrSt:uVwL:8"))!= -1) { switch (c) { case 'a': if (optarg) { int n = strlen(optarg); acceptable_extensions = (char*)pst_malloc(n+2); strcpy(acceptable_extensions, optarg); acceptable_extensions[n+1] = '\0'; // double null terminates array of non-empty null terminated strings. char *p = acceptable_extensions; while (*p) { if (*p == ',') *p = '\0'; p++; } } break; case 'b': save_rtf_body = 0; break; case 'C': if (optarg) { default_charset = optarg; } else { usage(); exit(0); } break; case 'c': if (optarg && optarg[0]=='v') { contact_mode=CMODE_VCARD; contact_mode_specified = 1; } else if (optarg && optarg[0]=='l') { contact_mode=CMODE_LIST; contact_mode_specified = 1; } else { usage(); exit(0); } break; case 'D': deleted_mode = DMODE_INCLUDE; break; case 'd': d_log = optarg; break; case 'h': usage(); exit(0); break; case 'j': max_children = atoi(optarg); max_child_specified = 1; break; case 'k': mode = MODE_KMAIL; break; case 'M': mode = MODE_SEPARATE; mode_MH = 1; mode_EX = 0; mode_MSG = 0; break; case 'e': mode = MODE_SEPARATE; mode_MH = 1; mode_EX = 1; mode_MSG = 0; file_name_len = 14; break; case 'L': pst_debug_setlevel(atoi(optarg)); break; case 'm': mode = MODE_SEPARATE; mode_MH = 1; mode_EX = 1; mode_MSG = 1; file_name_len = 14; break; case 'o': output_dir = optarg; break; case 'q': output_mode = OUTPUT_QUIET; break; case 'r': mode = MODE_RECURSE; mode_thunder = 0; break; case 'S': mode = MODE_SEPARATE; mode_MH = 0; mode_EX = 0; mode_MSG = 0; break; case 't': // email, appointment, contact, other if (!optarg) { usage(); exit(0); } temp = optarg; output_type_mode = 0; while (*temp > 0) { switch (temp[0]) { case 'e': output_type_mode |= OTMODE_EMAIL; break; case 'a': output_type_mode |= OTMODE_APPOINTMENT; break; case 'j': output_type_mode |= OTMODE_JOURNAL; break; case 'c': output_type_mode |= OTMODE_CONTACT; break; default: usage(); exit(0); break; } temp++; } break; case 'u': mode = MODE_RECURSE; mode_thunder = 1; break; case 'V': version(); exit(0); break; case 'w': overwrite = 1; break; case '8': prefer_utf8 = 1; break; default: usage(); exit(1); break; } } if (argc > optind) { fname = argv[optind]; } else { usage(); exit(2); } #ifdef _SC_NPROCESSORS_ONLN number_processors = sysconf(_SC_NPROCESSORS_ONLN); #endif max_children = (max_child_specified) ? max_children : number_processors * 4; active_children = 0; child_processes = (pid_t *)pst_malloc(sizeof(pid_t) * max_children); memset(child_processes, 0, sizeof(pid_t) * max_children); #ifdef HAVE_SEMAPHORE_H if (max_children) { shared_memory_id = shmget(IPC_PRIVATE, sizeof(sem_t)*2, 0777); if (shared_memory_id >= 0) { global_children = (sem_t *)shmat(shared_memory_id, NULL, 0); if (global_children == (sem_t *)-1) global_children = NULL; if (global_children) { output_mutex = &(global_children[1]); sem_init(global_children, 1, max_children); sem_init(output_mutex, 1, 1); } shmctl(shared_memory_id, IPC_RMID, NULL); } } #endif #ifdef DEBUG_ALL // force a log file if (!d_log) d_log = "readpst.log"; #endif // defined DEBUG_ALL #ifdef HAVE_SEMAPHORE_H DEBUG_INIT(d_log, output_mutex); #else DEBUG_INIT(d_log, NULL); #endif DEBUG_ENT("main"); if (output_mode != OUTPUT_QUIET) printf("Opening PST file and indexes...\n"); RET_DERROR(pst_open(&pstfile, fname, default_charset), 1, ("Error opening File\n")); RET_DERROR(pst_load_index(&pstfile), 2, ("Index Error\n")); pst_load_extended_attributes(&pstfile); if (chdir(output_dir)) { x = errno; pst_close(&pstfile); DEBUG_RET(); DIE(("Cannot change to output dir %s: %s\n", output_dir, strerror(x))); } d_ptr = pstfile.d_head; // first record is main record item = pst_parse_item(&pstfile, d_ptr, NULL); if (!item || !item->message_store) { DEBUG_RET(); DIE(("Could not get root record\n")); } // default the file_as to the same as the main filename if it doesn't exist if (!item->file_as.str) { if (!(temp = strrchr(fname, '/'))) if (!(temp = strrchr(fname, '\\'))) temp = fname; else temp++; // get past the "\\" else temp++; // get past the "/" item->file_as.str = (char*)pst_malloc(strlen(temp)+1); strcpy(item->file_as.str, temp); item->file_as.is_utf8 = 1; DEBUG_INFO(("file_as was blank, so am using %s\n", item->file_as.str)); } DEBUG_INFO(("Root Folder Name: %s\n", item->file_as.str)); d_ptr = pst_getTopOfFolders(&pstfile, item); if (!d_ptr) { DEBUG_RET(); DIE(("Top of folders record not found. Cannot continue\n")); } process(item, d_ptr->child); // do the children of TOPF grim_reaper(1); // wait for all child processes pst_freeItem(item); pst_close(&pstfile); DEBUG_RET(); #ifdef HAVE_SEMAPHORE_H if (global_children) { sem_destroy(global_children); sem_destroy(output_mutex); shmdt(global_children); } #endif regfree(&meta_charset_pattern); return 0; } void write_email_body(FILE *f, char *body) { char *n = body; DEBUG_ENT("write_email_body"); if (mode != MODE_SEPARATE) { while (n) { char *p = body; while (*p == '>') p++; if (strncmp(p, "From ", 5) == 0) fprintf(f, ">"); if ((n = strchr(body, '\n'))) { n++; pst_fwrite(body, n-body, 1, f); //write just a line body = n; } } } pst_fwrite(body, strlen(body), 1, f); DEBUG_RET(); } void removeCR (char *c) { // converts \r\n to \n char *a, *b; DEBUG_ENT("removeCR"); a = b = c; while (*a != '\0') { *b = *a; if (*a != '\r') b++; a++; } *b = '\0'; DEBUG_RET(); } void usage() { DEBUG_ENT("usage"); version(); printf("Usage: %s [OPTIONS] {PST FILENAME}\n", prog_name); printf("OPTIONS:\n"); printf("\t-V\t- Version. Display program version\n"); printf("\t-C charset\t- character set for items with an unspecified character set\n"); printf("\t-D\t- Include deleted items in output\n"); printf("\t-L \t- Set debug level; 1=debug,2=info,3=warn.\n"); printf("\t-M\t- Write emails in the MH (rfc822) format\n"); printf("\t-S\t- Separate. Write emails in the separate format\n"); printf("\t-a \t- Discard any attachment without an extension on the list\n"); printf("\t-b\t- Don't save RTF-Body attachments\n"); printf("\t-c[v|l]\t- Set the Contact output mode. -cv = VCard, -cl = EMail list\n"); printf("\t-d \t- Debug to file.\n"); printf("\t-e\t- As with -M, but include extensions on output files\n"); printf("\t-h\t- Help. This screen\n"); printf("\t-j \t- Number of parallel jobs to run\n"); printf("\t-k\t- KMail. Output in kmail format\n"); printf("\t-m\t- As with -e, but write .msg files also\n"); printf("\t-o \t- Output directory to write files to. CWD is changed *after* opening pst file\n"); printf("\t-q\t- Quiet. Only print error messages\n"); printf("\t-r\t- Recursive. Output in a recursive format\n"); printf("\t-t[eajc]\t- Set the output type list. e = email, a = attachment, j = journal, c = contact\n"); printf("\t-u\t- Thunderbird mode. Write two extra .size and .type files\n"); printf("\t-w\t- Overwrite any output mbox files\n"); printf("\t-8\t- Output bodies in UTF-8, rather than original encoding, if UTF-8 version is available\n"); printf("\n"); printf("Only one of -M -S -e -k -m -r should be specified\n"); DEBUG_RET(); } void version() { DEBUG_ENT("version"); printf("ReadPST / LibPST v%s\n", VERSION); #if BYTE_ORDER == BIG_ENDIAN printf("Big Endian implementation being used.\n"); #elif BYTE_ORDER == LITTLE_ENDIAN printf("Little Endian implementation being used.\n"); #else # error "Byte order not supported by this library" #endif DEBUG_RET(); } void mk_kmail_dir(char *fname) { //make a directory based on OUTPUT_KMAIL_DIR_TEMPLATE //change to that directory char *dir, *index; int x; DEBUG_ENT("mk_kmail_dir"); dir = pst_malloc(strlen(fname)+strlen(OUTPUT_KMAIL_DIR_TEMPLATE)+1); sprintf(dir, OUTPUT_KMAIL_DIR_TEMPLATE, fname); check_filename(dir); if (D_MKDIR(dir)) { if (errno != EEXIST) { // not an error because it exists x = errno; DIE(("mk_kmail_dir: Cannot create directory %s: %s\n", dir, strerror(x))); } } if (chdir(dir)) { x = errno; DIE(("mk_kmail_dir: Cannot change to directory %s: %s\n", dir, strerror(x))); } free (dir); //we should remove any existing indexes created by KMail, cause they might be different now index = pst_malloc(strlen(fname)+strlen(KMAIL_INDEX)+1); sprintf(index, KMAIL_INDEX, fname); unlink(index); free(index); DEBUG_RET(); } int close_kmail_dir() { int x; DEBUG_ENT("close_kmail_dir"); if (chdir("..")) { x = errno; DIE(("close_kmail_dir: Cannot move up dir (..): %s\n", strerror(x))); } DEBUG_RET(); return 0; } char *item_type_to_name(int32_t item_type) { char *name; switch (item_type) { case PST_TYPE_APPOINTMENT: name = "calendar"; break; case PST_TYPE_CONTACT: name = "contacts"; break; case PST_TYPE_JOURNAL: name = "journal"; break; case PST_TYPE_STICKYNOTE: case PST_TYPE_TASK: case PST_TYPE_NOTE: case PST_TYPE_OTHER: case PST_TYPE_REPORT: default: name = "mbox"; break; } return name; } int32_t reduced_item_type(int32_t item_type) { int32_t reduced; switch (item_type) { case PST_TYPE_APPOINTMENT: case PST_TYPE_CONTACT: case PST_TYPE_JOURNAL: reduced = item_type; break; case PST_TYPE_STICKYNOTE: case PST_TYPE_TASK: case PST_TYPE_NOTE: case PST_TYPE_OTHER: case PST_TYPE_REPORT: default: reduced = PST_TYPE_NOTE; break; } return reduced; } // this will create a directory by that name void mk_recurse_dir(char *dir) { int x; DEBUG_ENT("mk_recurse_dir"); check_filename(dir); if (D_MKDIR (dir)) { if (errno != EEXIST) { // not an error because it exists x = errno; DIE(("mk_recurse_dir: Cannot create directory %s: %s\n", dir, strerror(x))); } } if (chdir(dir)) { x = errno; DIE(("mk_recurse_dir: Cannot change to directory %s: %s\n", dir, strerror(x))); } DEBUG_RET(); } int close_recurse_dir() { int x; DEBUG_ENT("close_recurse_dir"); if (chdir("..")) { x = errno; DIE(("close_recurse_dir: Cannot go up dir (..): %s\n", strerror(x))); } DEBUG_RET(); return 0; } void mk_separate_dir(char *dir) { size_t dirsize = strlen(dir) + 10; char dir_name[dirsize]; int x = 0, y = 0; DEBUG_ENT("mk_separate_dir"); do { if (y == 0) snprintf(dir_name, dirsize, "%s", dir); else snprintf(dir_name, dirsize, "%s" SEP_MAIL_FILE_TEMPLATE, dir, y, ""); // enough for 9 digits allocated above check_filename(dir_name); DEBUG_INFO(("about to try creating %s\n", dir_name)); if (D_MKDIR(dir_name)) { if (errno != EEXIST) { // if there is an error, and it doesn't already exist x = errno; DIE(("mk_separate_dir: Cannot create directory %s: %s\n", dir, strerror(x))); } } else { break; } y++; } while (overwrite == 0); if (chdir(dir_name)) { x = errno; DIE(("mk_separate_dir: Cannot change to directory %s: %s\n", dir, strerror(x))); } if (overwrite) { // we should probably delete all files from this directory #if !defined(WIN32) && !defined(__CYGWIN__) DIR * sdir = NULL; struct dirent *dirent = NULL; struct stat filestat; if (!(sdir = opendir("./"))) { DEBUG_WARN(("mk_separate_dir: Cannot open dir \"%s\" for deletion of old contents\n", "./")); } else { while ((dirent = readdir(sdir))) { if (lstat(dirent->d_name, &filestat) != -1) if (S_ISREG(filestat.st_mode)) { if (unlink(dirent->d_name)) { y = errno; DIE(("mk_separate_dir: unlink returned error on file %s: %s\n", dirent->d_name, strerror(y))); } } } closedir(sdir); // cppcheck detected leak } #endif } DEBUG_RET(); } int close_separate_dir() { int x; DEBUG_ENT("close_separate_dir"); if (chdir("..")) { x = errno; DIE(("close_separate_dir: Cannot go up dir (..): %s\n", strerror(x))); } DEBUG_RET(); return 0; } void mk_separate_file(struct file_ll *f, int32_t t, char *extension, int openit) { DEBUG_ENT("mk_separate_file"); DEBUG_INFO(("opening next file to save email type %s\n", item_type_to_name(t))); if (f->item_count > 999999999) { // bigger than nine 9's DIE(("mk_separate_file: The number of emails in this folder has become too high to handle\n")); } sprintf(f->name[t], SEP_MAIL_FILE_TEMPLATE, f->item_count, extension); check_filename(f->name[t]); if (openit) { if (!(f->output[t] = fopen(f->name[t], "w"))) { DIE(("mk_separate_file: Cannot open file to save email \"%s\"\n", f->name[t])); } } DEBUG_RET(); } void close_separate_file(struct file_ll *f) { int32_t t; DEBUG_ENT("close_separate_file"); for (t=0; toutput[t]) { struct stat st; fclose(f->output[t]); stat(f->name[t], &st); if (!st.st_size) { DEBUG_WARN(("removing empty output file %s\n", f->name[t])); remove(f->name[t]); } f->output[t] = NULL; } } DEBUG_RET(); } char *my_stristr(char *haystack, char *needle) { // my_stristr varies from strstr in that its searches are case-insensitive char *x=haystack, *y=needle, *z = NULL; if (!haystack || !needle) { return NULL; } while (*y != '\0' && *x != '\0') { if (tolower(*y) == tolower(*x)) { // move y on one y++; if (!z) { z = x; // store first position in haystack where a match is made } } else { y = needle; // reset y to the beginning of the needle z = NULL; // reset the haystack storage point } x++; // advance the search in the haystack } // If the haystack ended before our search finished, it's not a match. if (*y != '\0') return NULL; return z; } void check_filename(char *fname) { char *t = fname; DEBUG_ENT("check_filename"); if (!t) { DEBUG_RET(); return; } while ((t = strpbrk(t, "/\\:"))) { // while there are characters in the second string that we don't want *t = '_'; //replace them with an underscore } DEBUG_RET(); } /** * check if the file name extension is acceptable. If not, the attachment * will be discarded * @param attach pst attachment object * @return true if the attachment filename contains an extension that we want. */ int acceptable_ext(pst_item_attach* attach) { if (!acceptable_extensions || *acceptable_extensions == '\0') return 1; // acceptable list missing or empty char *attach_filename = (attach->filename2.str) ? attach->filename2.str : attach->filename1.str; if (!attach_filename) return 1; // attachment with no name is always acceptable char *e = strrchr(attach_filename, '.'); if (!e) return 1; // attachment with no extension is always acceptable. DEBUG_ENT("acceptable_ext"); DEBUG_INFO(("attachment extension %s\n", e)); int rc = 0; char *a = acceptable_extensions; while (*a) { if (pst_stricmp(a, e) == 0) { rc = 1; break; } a += strlen(a) + 1; } DEBUG_INFO(("attachment acceptable returns %d\n", rc)); DEBUG_RET(); return rc; } void write_separate_attachment(char f_name[], pst_item_attach* attach, int attach_num, pst_file* pst) { FILE *fp = NULL; int x = 0; char *temp = NULL; // If there is a long filename (filename2) use that, otherwise // use the 8.3 filename (filename1) char *attach_filename = (attach->filename2.str) ? attach->filename2.str : attach->filename1.str; DEBUG_ENT("write_separate_attachment"); DEBUG_INFO(("Attachment %s Size is %#"PRIx64", data = %#"PRIxPTR", id %#"PRIx64"\n", attach_filename, (uint64_t)attach->data.size, attach->data.data, attach->i_id)); if (!attach->data.data) { // make sure we can fetch data from the id pst_index_ll *ptr = pst_getID(pst, attach->i_id); if (!ptr) { DEBUG_WARN(("Couldn't find i_id %#"PRIx64". Cannot save attachment to file\n", attach->i_id)); DEBUG_RET(); return; } } check_filename(f_name); if (!attach_filename) { // generate our own (dummy) filename for the attachment temp = pst_malloc(strlen(f_name)+15); sprintf(temp, "%s-attach%i", f_name, attach_num); } else { // have an attachment name, make sure it's unique temp = pst_malloc(strlen(f_name)+strlen(attach_filename)+15); do { if (fp) fclose(fp); if (x == 0) sprintf(temp, "%s-%s", f_name, attach_filename); else sprintf(temp, "%s-%s-%i", f_name, attach_filename, x); } while ((fp = fopen(temp, "r")) && ++x < 99999999); if (x > 99999999) { DIE(("error finding attachment name. exhausted possibilities to %s\n", temp)); } } DEBUG_INFO(("Saving attachment to %s\n", temp)); if (!(fp = fopen(temp, "w"))) { DEBUG_WARN(("write_separate_attachment: Cannot open attachment save file \"%s\"\n", temp)); } else { (void)pst_attach_to_file(pst, attach, fp); fclose(fp); } if (temp) free(temp); DEBUG_RET(); } void write_embedded_message(FILE* f_output, pst_item_attach* attach, char *boundary, pst_file* pf, int save_rtf, char** extra_mime_headers) { pst_index_ll *ptr; DEBUG_ENT("write_embedded_message"); ptr = pst_getID(pf, attach->i_id); pst_desc_tree d_ptr; d_ptr.d_id = 0; d_ptr.parent_d_id = 0; d_ptr.assoc_tree = NULL; d_ptr.desc = ptr; d_ptr.no_child = 0; d_ptr.prev = NULL; d_ptr.next = NULL; d_ptr.parent = NULL; d_ptr.child = NULL; d_ptr.child_tail = NULL; pst_item *item = pst_parse_item(pf, &d_ptr, attach->id2_head); // It appears that if the embedded message contains an appointment/ // calendar item, pst_parse_item returns NULL due to the presence of // an unexpected reference type of 0x1048, which seems to represent // an array of GUIDs representing a CLSID. It's likely that this is // a reference to an internal Outlook COM class. // Log the skipped item and continue on. if (!item) { DEBUG_WARN(("write_embedded_message: pst_parse_item was unable to parse the embedded message in attachment ID %llu", attach->i_id)); } else { if (!item->email) { DEBUG_WARN(("write_embedded_message: pst_parse_item returned type %d, not an email message", item->type)); } else { fprintf(f_output, "\n--%s\n", boundary); fprintf(f_output, "Content-Type: %s\n\n", attach->mimetype.str); write_normal_email(f_output, "", item, MODE_NORMAL, 0, pf, save_rtf, 1, extra_mime_headers); } pst_freeItem(item); } DEBUG_RET(); } /** * Backslash-escape quotes and backslashes in the given string. */ char *quote_string(char *inp) { int i = 0; int count = 0; char *curr = inp; while (*curr) { if (*curr == '\"' || *curr == '\\') { count++; } curr++; i++; } char *res = malloc(i + count + 1); char *curr_in = inp; char *curr_out = res; while (*curr_in) { if (*curr_in == '\"' || *curr_in == '\\') { *curr_out++ = '\\'; } *curr_out++ = *curr_in++; } *curr_out = '\0'; return res; } +/** Convert inp to rfc2231 encoding of string + * + * @param inp pointer to the string of interest + * @return pointer to converted string -- caller must free + */ +char *rfc2231_string(char *inp) { + int needs = 0; + const int8_t *x = (int8_t *)inp; + while (*x) { + if (*x <= 32) needs++; + x++; + } + int n = strlen(inp) + 2*needs + 15; + char *buffer = pst_malloc(n); + strcpy(buffer, "utf-8''"); + x = (int8_t *)inp; + const uint8_t *y = (uint8_t *)inp; + uint8_t *z = (uint8_t *)buffer; + z += strlen(buffer); // skip the utf8 prefix + while (*y) { + if (*x <= 32) { + *(z++) = (uint8_t)'%'; + snprintf(z, 3, "%2x", *y); + z += 2; + } + else { + *(z++) = *y; + } + x++; + y++; + } + *z = '\0'; + return buffer; +} + void write_inline_attachment(FILE* f_output, pst_item_attach* attach, char *boundary, pst_file* pst) { DEBUG_ENT("write_inline_attachment"); DEBUG_INFO(("Attachment Size is %#"PRIx64", data = %#"PRIxPTR", id %#"PRIx64"\n", (uint64_t)attach->data.size, attach->data.data, attach->i_id)); if (!attach->data.data) { // make sure we can fetch data from the id pst_index_ll *ptr = pst_getID(pst, attach->i_id); if (!ptr) { DEBUG_WARN(("Couldn't find ID pointer. Cannot save attachment to file\n")); DEBUG_RET(); return; } } fprintf(f_output, "\n--%s\n", boundary); if (!attach->mimetype.str) { fprintf(f_output, "Content-Type: %s\n", MIME_TYPE_DEFAULT); } else { fprintf(f_output, "Content-Type: %s\n", attach->mimetype.str); } fprintf(f_output, "Content-Transfer-Encoding: base64\n"); if (attach->content_id.str) { fprintf(f_output, "Content-ID: <%s>\n", attach->content_id.str); } if (attach->filename2.str) { // use the long filename, converted to proper encoding if needed. // it is already utf8 char *escaped = quote_string(attach->filename2.str); - pst_rfc2231(&attach->filename2); - fprintf(f_output, "Content-Disposition: attachment; \n filename*=%s;\n", attach->filename2.str); + // encode long filename as rfc2231 without modifying original -- we may still need the original long filename + char *rfc2231 = rfc2231_string(attach->filename2.str); + fprintf(f_output, "Content-Disposition: attachment; \n filename*=%s;\n", rfc2231); + free (rfc2231); // Also include the (escaped) utf8 filename in the 'filename' header directly - this is not strictly valid // (since this header should be ASCII) but is almost always handled correctly (and in fact this is the only // way to get MS Outlook to correctly read a UTF8 filename, AFAICT, which is why we're doing it). fprintf(f_output, " filename=\"%s\"\n\n", escaped); free(escaped); } else if (attach->filename1.str) { // short filename never needs encoding fprintf(f_output, "Content-Disposition: attachment; filename=\"%s\"\n\n", attach->filename1.str); } else { // no filename is inline fprintf(f_output, "Content-Disposition: inline\n\n"); } (void)pst_attach_to_file_base64(pst, attach, f_output); fprintf(f_output, "\n\n"); DEBUG_RET(); } int header_match(char *header, char*field) { int n = strlen(field); if (strncasecmp(header, field, n) == 0) return 1; // tag:{space} if ((field[n-1] == ' ') && (strncasecmp(header, field, n-1) == 0)) { char *crlftab = "\r\n\t"; DEBUG_INFO(("Possible wrapped header = %s\n", header)); if (strncasecmp(header+n-1, crlftab, 3) == 0) return 1; // tag:{cr}{lf}{tab} } return 0; } int valid_headers(char *header) { // headers are sometimes really bogus - they seem to be fragments of the // message body, so we only use them if they seem to be real rfc822 headers. // this list is composed of ones that we have seen in real pst files. // there are surely others. the problem is - given an arbitrary character // string, is it a valid (or even reasonable) set of rfc822 headers? if (header) { if (header_match(header, "Content-Type: " )) return 1; if (header_match(header, "Date: " )) return 1; if (header_match(header, "From: " )) return 1; if (header_match(header, "MIME-Version: " )) return 1; if (header_match(header, "Microsoft Mail Internet Headers")) return 1; if (header_match(header, "Received: " )) return 1; if (header_match(header, "Return-Path: " )) return 1; if (header_match(header, "Subject: " )) return 1; if (header_match(header, "To: " )) return 1; if (header_match(header, "X-ASG-Debug-ID: " )) return 1; if (header_match(header, "X-Barracuda-URL: " )) return 1; if (header_match(header, "X-x: " )) return 1; if (strlen(header) > 2) { DEBUG_INFO(("Ignore bogus headers = %s\n", header)); } return 0; } else return 0; } void header_has_field(char *header, char *field, int *flag) { DEBUG_ENT("header_has_field"); if (my_stristr(header, field) || (strncasecmp(header, field+1, strlen(field)-1) == 0)) { DEBUG_INFO(("header block has %s header\n", field+1)); *flag = 1; } DEBUG_RET(); } void header_get_subfield(char *field, const char *subfield, char *body_subfield, size_t size_subfield) { if (!field) return; DEBUG_ENT("header_get_subfield"); char search[60]; snprintf(search, sizeof(search), " %s=", subfield); field++; char *n = header_end_field(field); char *s = my_stristr(field, search); if (n && s && (s < n)) { char *e, *f, save; s += strlen(search); // skip over subfield= if (*s == '"') { s++; e = strchr(s, '"'); } else { e = strchr(s, ';'); f = strchr(s, '\n'); if (e && f && (f < e)) e = f; } if (!e || (e > n)) e = n; // use the trailing lf as terminator if nothing better save = *e; *e = '\0'; snprintf(body_subfield, size_subfield, "%s", s); // copy the subfield to our buffer *e = save; DEBUG_INFO(("body %s %s from headers\n", subfield, body_subfield)); } DEBUG_RET(); } char* header_get_field(char *header, char *field) { char *t = my_stristr(header, field); if (!t && (strncasecmp(header, field+1, strlen(field)-1) == 0)) t = header; return t; } // return pointer to \n at the end of this header field, // or NULL if this field goes to the end of the string. char *header_end_field(char *field) { char *e = strchr(field+1, '\n'); while (e && ((e[1] == ' ') || (e[1] == '\t'))) { e = strchr(e+1, '\n'); } return e; } void header_strip_field(char *header, char *field) { char *t; while ((t = header_get_field(header, field))) { char *e = header_end_field(t); if (e) { if (t == header) e++; // if *t is not \n, we don't want to keep the \n at *e either. while (*e != '\0') { *t = *e; t++; e++; } *t = '\0'; } else { // this was the last header field, truncate the headers *t = '\0'; } } } int test_base64(char *body, size_t len) { int b64 = 0; uint8_t *b = (uint8_t *)body; DEBUG_ENT("test_base64"); while (len--) { if ((*b < 32) && (*b != 9) && (*b != 10)) { DEBUG_INFO(("found base64 byte %d\n", (int)*b)); DEBUG_HEXDUMPC(body, strlen(body), 0x10); b64 = 1; break; } b++; } DEBUG_RET(); return b64; } void find_html_charset(char *html, char *charset, size_t charsetlen) { const int index = 1; const int nmatch = index+1; regmatch_t match[nmatch]; DEBUG_ENT("find_html_charset"); int rc = regexec(&meta_charset_pattern, html, nmatch, match, 0); if (rc == 0) { int s = match[index].rm_so; int e = match[index].rm_eo; if (s != -1) { char save = html[e]; html[e] = '\0'; snprintf(charset, charsetlen, "%s", html+s); // copy the html charset html[e] = save; DEBUG_INFO(("charset %s from html text\n", charset)); } else { DEBUG_INFO(("matching %d %d %d %d\n", match[0].rm_so, match[0].rm_eo, match[1].rm_so, match[1].rm_eo)); DEBUG_HEXDUMPC(html, strlen(html), 0x10); } } else { DEBUG_INFO(("regexec returns %d\n", rc)); } DEBUG_RET(); } void find_rfc822_headers(char** extra_mime_headers) { DEBUG_ENT("find_rfc822_headers"); char *headers = *extra_mime_headers; if (headers) { char *temp, *t; while ((temp = strstr(headers, "\n\n"))) { temp[1] = '\0'; t = header_get_field(headers, "\nContent-Type:"); if (t) { t++; DEBUG_INFO(("found content type header\n")); char *n = strchr(t, '\n'); char *s = strstr(t, ": "); char *e = strchr(t, ';'); if (!e || (e > n)) e = n; if (s && (s < e)) { s += 2; if (!strncasecmp(s, RFC822, e-s)) { headers = temp+2; // found rfc822 header DEBUG_INFO(("found 822 headers\n%s\n", headers)); break; } } } //DEBUG_INFO(("skipping to next block after\n%s\n", headers)); headers = temp+2; // skip to next chunk of headers } *extra_mime_headers = headers; } DEBUG_RET(); } void write_body_part(FILE* f_output, pst_string *body, char *mime, char *charset, char *boundary, pst_file* pst) { DEBUG_ENT("write_body_part"); removeCR(body->str); size_t body_len = strlen(body->str); if (body->is_utf8 && (strcasecmp("utf-8", charset))) { if (prefer_utf8) { charset = "utf-8"; } else { // try to convert to the specified charset since the target // is not utf-8, and the data came from a unicode (utf16) field // and is now in utf-8. size_t rc; DEBUG_INFO(("Convert %s utf-8 to %s\n", mime, charset)); pst_vbuf *newer = pst_vballoc(2); rc = pst_vb_utf8to8bit(newer, body->str, body_len, charset); if (rc == (size_t)-1) { // unable to convert, change the charset to utf8 free(newer->b); DEBUG_INFO(("Failed to convert %s utf-8 to %s\n", mime, charset)); charset = "utf-8"; } else { // null terminate the output string pst_vbgrow(newer, 1); newer->b[newer->dlen] = '\0'; free(body->str); body->str = newer->b; body_len = newer->dlen; } free(newer); } } int base64 = test_base64(body->str, body_len); fprintf(f_output, "\n--%s\n", boundary); fprintf(f_output, "Content-Type: %s; charset=\"%s\"\n", mime, charset); if (base64) fprintf(f_output, "Content-Transfer-Encoding: base64\n"); fprintf(f_output, "\n"); // Any body that uses an encoding with NULLs, e.g. UTF16, will be base64-encoded here. if (base64) { char *enc = pst_base64_encode(body->str, body_len); if (enc) { write_email_body(f_output, enc); fprintf(f_output, "\n"); free(enc); } } else { write_email_body(f_output, body->str); } DEBUG_RET(); } void write_schedule_part_data(FILE* f_output, pst_item* item, const char* sender, const char* method) { fprintf(f_output, "BEGIN:VCALENDAR\n"); fprintf(f_output, "VERSION:2.0\n"); fprintf(f_output, "PRODID:LibPST v%s\n", VERSION); if (method) fprintf(f_output, "METHOD:%s\n", method); fprintf(f_output, "BEGIN:VEVENT\n"); if (sender) { if (item->email->outlook_sender_name.str) { fprintf(f_output, "ORGANIZER;CN=\"%s\":MAILTO:%s\n", item->email->outlook_sender_name.str, sender); } else { fprintf(f_output, "ORGANIZER;CN=\"\":MAILTO:%s\n", sender); } } write_appointment(f_output, item); fprintf(f_output, "END:VCALENDAR\n"); } void write_schedule_part(FILE* f_output, pst_item* item, const char* sender, const char* boundary) { const char* method = "REQUEST"; const char* charset = "utf-8"; char fname[30]; if (!item->appointment) return; // inline appointment request fprintf(f_output, "\n--%s\n", boundary); fprintf(f_output, "Content-Type: %s; method=\"%s\"; charset=\"%s\"\n\n", "text/calendar", method, charset); write_schedule_part_data(f_output, item, sender, method); fprintf(f_output, "\n"); // attachment appointment request snprintf(fname, sizeof(fname), "i%i.ics", rand()); fprintf(f_output, "\n--%s\n", boundary); fprintf(f_output, "Content-Type: %s; charset=\"%s\"; name=\"%s\"\n", "text/calendar", "utf-8", fname); fprintf(f_output, "Content-Disposition: attachment; filename=\"%s\"\n\n", fname); write_schedule_part_data(f_output, item, sender, method); fprintf(f_output, "\n"); } void write_normal_email(FILE* f_output, char f_name[], pst_item* item, int mode, int mode_MH, pst_file* pst, int save_rtf, int embedding, char** extra_mime_headers) { char boundary[60]; char altboundary[66]; char *altboundaryp = NULL; char body_charset[30]; char buffer_charset[30]; char body_report[60]; char sender[60]; int sender_known = 0; char *temp = NULL; time_t em_time; char *c_time; char *headers = NULL; int has_from, has_subject, has_to, has_cc, has_date, has_msgid; has_from = has_subject = has_to = has_cc = has_date = has_msgid = 0; DEBUG_ENT("write_normal_email"); pst_convert_utf8_null(item, &item->email->header); headers = valid_headers(item->email->header.str) ? item->email->header.str : valid_headers(*extra_mime_headers) ? *extra_mime_headers : NULL; // setup default body character set and report type strncpy(body_charset, pst_default_charset(item, sizeof(buffer_charset), buffer_charset), sizeof(body_charset)); body_charset[sizeof(body_charset)-1] = '\0'; strncpy(body_report, "delivery-status", sizeof(body_report)); body_report[sizeof(body_report)-1] = '\0'; // setup default sender pst_convert_utf8(item, &item->email->sender_address); if (item->email->sender_address.str && strchr(item->email->sender_address.str, '@')) { temp = item->email->sender_address.str; sender_known = 1; } else { temp = "MAILER-DAEMON"; } strncpy(sender, temp, sizeof(sender)); sender[sizeof(sender)-1] = '\0'; // convert the sent date if it exists, or set it to a fixed date if (item->email->sent_date) { em_time = pst_fileTimeToUnixTime(item->email->sent_date); c_time = ctime(&em_time); if (c_time) c_time[strlen(c_time)-1] = '\0'; //remove end \n else c_time = "Thu Jan 1 00:00:00 1970"; } else c_time = "Thu Jan 1 00:00:00 1970"; // create our MIME boundaries here. snprintf(boundary, sizeof(boundary), "--boundary-LibPST-iamunique-%i_-_-", rand()); snprintf(altboundary, sizeof(altboundary), "alt-%s", boundary); // we will always look at the headers to discover some stuff if (headers ) { char *t; removeCR(headers); temp = strstr(headers, "\n\n"); if (temp) { // cut off our real rfc822 headers here temp[1] = '\0'; // pointer to all the embedded MIME headers. // we use these to find the actual rfc822 headers for embedded message/rfc822 mime parts // but only for the outermost message if (!*extra_mime_headers) *extra_mime_headers = temp+2; DEBUG_INFO(("Found extra mime headers\n%s\n", temp+2)); } // Check if the headers have all the necessary fields header_has_field(headers, "\nFrom:", &has_from); header_has_field(headers, "\nTo:", &has_to); header_has_field(headers, "\nSubject:", &has_subject); header_has_field(headers, "\nDate:", &has_date); header_has_field(headers, "\nCC:", &has_cc); header_has_field(headers, "\nMessage-Id:", &has_msgid); // look for charset and report-type in Content-Type header t = header_get_field(headers, "\nContent-Type:"); header_get_subfield(t, "charset", body_charset, sizeof(body_charset)); header_get_subfield(t, "report-type", body_report, sizeof(body_report)); // derive a proper sender email address if (!sender_known) { t = header_get_field(headers, "\nFrom:"); if (t) { // assume address is on the first line, rather than on a continuation line t++; char *n = strchr(t, '\n'); char *s = strchr(t, '<'); char *e = strchr(t, '>'); if (s && e && n && (s < e) && (e < n)) { char save = *e; *e = '\0'; snprintf(sender, sizeof(sender), "%s", s+1); *e = save; } } } // Strip out the mime headers and some others that we don't want to emit header_strip_field(headers, "\nMicrosoft Mail Internet Headers"); header_strip_field(headers, "\nMIME-Version:"); header_strip_field(headers, "\nContent-Type:"); header_strip_field(headers, "\nContent-Transfer-Encoding:"); header_strip_field(headers, "\nContent-class:"); header_strip_field(headers, "\nX-MimeOLE:"); header_strip_field(headers, "\nX-From_:"); } DEBUG_INFO(("About to print Header\n")); if (item && item->subject.str) { pst_convert_utf8(item, &item->subject); DEBUG_INFO(("item->subject = %s\n", item->subject.str)); } if (mode != MODE_SEPARATE) { // most modes need this separator line. // procmail produces this separator without the quotes around the // sender email address, but apparently some Mac email client needs // those quotes, and they don't seem to cause problems for anyone else. char *quo = (embedding) ? ">" : ""; fprintf(f_output, "%sFrom \"%s\" %s\n", quo, sender, c_time); } // print the supplied email headers if (headers) { int len = strlen(headers); if (len > 0) { fprintf(f_output, "%s", headers); // make sure the headers end with a \n if (headers[len-1] != '\n') fprintf(f_output, "\n"); //char *h = headers; //while (*h) { // char *e = strchr(h, '\n'); // int d = 1; // normally e points to trailing \n // if (!e) { // e = h + strlen(h); // e points to trailing null // d = 0; // } // // we could do rfc2047 encoding here if needed // fprintf(f_output, "%.*s\n", (int)(e-h), h); // h = e + d; //} } } // record read status if ((item->flags & PST_FLAG_READ) == PST_FLAG_READ) { fprintf(f_output, "Status: RO\n"); } // create required header fields that are not already written if (!has_from) { if (item->email->outlook_sender_name.str){ pst_rfc2047(item, &item->email->outlook_sender_name, 1); fprintf(f_output, "From: %s <%s>\n", item->email->outlook_sender_name.str, sender); } else { fprintf(f_output, "From: <%s>\n", sender); } } if (!has_subject) { if (item->subject.str) { pst_rfc2047(item, &item->subject, 0); fprintf(f_output, "Subject: %s\n", item->subject.str); } else { fprintf(f_output, "Subject: \n"); } } if (!has_to && item->email->sentto_address.str) { pst_rfc2047(item, &item->email->sentto_address, 0); fprintf(f_output, "To: %s\n", item->email->sentto_address.str); } if (!has_cc && item->email->cc_address.str) { pst_rfc2047(item, &item->email->cc_address, 0); fprintf(f_output, "Cc: %s\n", item->email->cc_address.str); } if (!has_date && item->email->sent_date) { char c_time[C_TIME_SIZE]; struct tm stm; gmtime_r(&em_time, &stm); strftime(c_time, C_TIME_SIZE, "%a, %d %b %Y %H:%M:%S %z", &stm); fprintf(f_output, "Date: %s\n", c_time); } if (!has_msgid && item->email->messageid.str) { pst_convert_utf8(item, &item->email->messageid); fprintf(f_output, "Message-Id: %s\n", item->email->messageid.str); } // add forensic headers to capture some .pst stuff that is not really // needed or used by mail clients pst_convert_utf8_null(item, &item->email->sender_address); if (item->email->sender_address.str && !strchr(item->email->sender_address.str, '@') && strcmp(item->email->sender_address.str, ".") && (strlen(item->email->sender_address.str) > 0)) { fprintf(f_output, "X-libpst-forensic-sender: %s\n", item->email->sender_address.str); } if (item->email->bcc_address.str) { pst_convert_utf8(item, &item->email->bcc_address); fprintf(f_output, "X-libpst-forensic-bcc: %s\n", item->email->bcc_address.str); } // add our own mime headers fprintf(f_output, "MIME-Version: 1.0\n"); if (item->type == PST_TYPE_REPORT) { // multipart/report for DSN/MDN reports fprintf(f_output, "Content-Type: multipart/report; report-type=%s;\n\tboundary=\"%s\"\n", body_report, boundary); } else { fprintf(f_output, "Content-Type: multipart/mixed;\n\tboundary=\"%s\"\n", boundary); } fprintf(f_output, "\n"); // end of headers, start of body // now dump the body parts if ((item->type == PST_TYPE_REPORT) && (item->email->report_text.str)) { write_body_part(f_output, &item->email->report_text, "text/plain", body_charset, boundary, pst); fprintf(f_output, "\n"); } if (item->body.str && item->email->htmlbody.str) { // start the nested alternative part fprintf(f_output, "\n--%s\n", boundary); fprintf(f_output, "Content-Type: multipart/alternative;\n\tboundary=\"%s\"\n", altboundary); altboundaryp = altboundary; } else { altboundaryp = boundary; } if (item->body.str) { write_body_part(f_output, &item->body, "text/plain", body_charset, altboundaryp, pst); } if (item->email->htmlbody.str) { find_html_charset(item->email->htmlbody.str, body_charset, sizeof(body_charset)); write_body_part(f_output, &item->email->htmlbody, "text/html", body_charset, altboundaryp, pst); } if (item->body.str && item->email->htmlbody.str) { // end the nested alternative part fprintf(f_output, "\n--%s--\n", altboundary); } if (item->email->rtf_compressed.data && save_rtf) { pst_item_attach* attach = (pst_item_attach*)pst_malloc(sizeof(pst_item_attach)); DEBUG_INFO(("Adding RTF body as attachment\n")); memset(attach, 0, sizeof(pst_item_attach)); attach->next = item->attach; item->attach = attach; attach->data.data = pst_lzfu_decompress(item->email->rtf_compressed.data, item->email->rtf_compressed.size, &attach->data.size); attach->filename2.str = strdup(RTF_ATTACH_NAME); attach->filename2.is_utf8 = 1; attach->mimetype.str = strdup(RTF_ATTACH_TYPE); attach->mimetype.is_utf8 = 1; } if (item->email->encrypted_body.data) { pst_item_attach* attach = (pst_item_attach*)pst_malloc(sizeof(pst_item_attach)); DEBUG_INFO(("Adding encrypted text body as attachment\n")); memset(attach, 0, sizeof(pst_item_attach)); attach->next = item->attach; item->attach = attach; attach->data.data = item->email->encrypted_body.data; attach->data.size = item->email->encrypted_body.size; item->email->encrypted_body.data = NULL; } if (item->email->encrypted_htmlbody.data) { pst_item_attach* attach = (pst_item_attach*)pst_malloc(sizeof(pst_item_attach)); DEBUG_INFO(("Adding encrypted HTML body as attachment\n")); memset(attach, 0, sizeof(pst_item_attach)); attach->next = item->attach; item->attach = attach; attach->data.data = item->email->encrypted_htmlbody.data; attach->data.size = item->email->encrypted_htmlbody.size; item->email->encrypted_htmlbody.data = NULL; } if (item->type == PST_TYPE_SCHEDULE) { write_schedule_part(f_output, item, sender, boundary); } // other attachments { pst_item_attach* attach; int attach_num = 0; for (attach = item->attach; attach; attach = attach->next) { pst_convert_utf8_null(item, &attach->filename1); pst_convert_utf8_null(item, &attach->filename2); pst_convert_utf8_null(item, &attach->mimetype); DEBUG_INFO(("Attempting Attachment encoding\n")); if (attach->method == PST_ATTACH_EMBEDDED) { DEBUG_INFO(("have an embedded rfc822 message attachment\n")); if (attach->mimetype.str) { DEBUG_INFO(("which already has a mime-type of %s\n", attach->mimetype.str)); free(attach->mimetype.str); } attach->mimetype.str = strdup(RFC822); attach->mimetype.is_utf8 = 1; find_rfc822_headers(extra_mime_headers); write_embedded_message(f_output, attach, boundary, pst, save_rtf, extra_mime_headers); } else if (attach->data.data || attach->i_id) { if (acceptable_ext(attach)) { if (mode == MODE_SEPARATE && !mode_MH) write_separate_attachment(f_name, attach, ++attach_num, pst); else write_inline_attachment(f_output, attach, boundary, pst); } } } } fprintf(f_output, "\n--%s--\n\n", boundary); DEBUG_RET(); } void write_vcard(FILE* f_output, pst_item* item, pst_item_contact* contact, char comment[]) { char* result = NULL; size_t resultlen = 0; char time_buffer[30]; // We can only call rfc escape once per printf, since the second call // may free the buffer returned by the first call. // I had tried to place those into a single printf - Carl. DEBUG_ENT("write_vcard"); // make everything utf8 pst_convert_utf8_null(item, &contact->fullname); pst_convert_utf8_null(item, &contact->surname); pst_convert_utf8_null(item, &contact->first_name); pst_convert_utf8_null(item, &contact->middle_name); pst_convert_utf8_null(item, &contact->display_name_prefix); pst_convert_utf8_null(item, &contact->suffix); pst_convert_utf8_null(item, &contact->nickname); pst_convert_utf8_null(item, &contact->address1); pst_convert_utf8_null(item, &contact->address2); pst_convert_utf8_null(item, &contact->address3); pst_convert_utf8_null(item, &contact->home_po_box); pst_convert_utf8_null(item, &contact->home_street); pst_convert_utf8_null(item, &contact->home_city); pst_convert_utf8_null(item, &contact->home_state); pst_convert_utf8_null(item, &contact->home_postal_code); pst_convert_utf8_null(item, &contact->home_country); pst_convert_utf8_null(item, &contact->home_address); pst_convert_utf8_null(item, &contact->business_po_box); pst_convert_utf8_null(item, &contact->business_street); pst_convert_utf8_null(item, &contact->business_city); pst_convert_utf8_null(item, &contact->business_state); pst_convert_utf8_null(item, &contact->business_postal_code); pst_convert_utf8_null(item, &contact->business_country); pst_convert_utf8_null(item, &contact->business_address); pst_convert_utf8_null(item, &contact->other_po_box); pst_convert_utf8_null(item, &contact->other_street); pst_convert_utf8_null(item, &contact->other_city); pst_convert_utf8_null(item, &contact->other_state); pst_convert_utf8_null(item, &contact->other_postal_code); pst_convert_utf8_null(item, &contact->other_country); pst_convert_utf8_null(item, &contact->other_address); pst_convert_utf8_null(item, &contact->business_fax); pst_convert_utf8_null(item, &contact->business_phone); pst_convert_utf8_null(item, &contact->business_phone2); pst_convert_utf8_null(item, &contact->car_phone); pst_convert_utf8_null(item, &contact->home_fax); pst_convert_utf8_null(item, &contact->home_phone); pst_convert_utf8_null(item, &contact->home_phone2); pst_convert_utf8_null(item, &contact->isdn_phone); pst_convert_utf8_null(item, &contact->mobile_phone); pst_convert_utf8_null(item, &contact->other_phone); pst_convert_utf8_null(item, &contact->pager_phone); pst_convert_utf8_null(item, &contact->primary_fax); pst_convert_utf8_null(item, &contact->primary_phone); pst_convert_utf8_null(item, &contact->radio_phone); pst_convert_utf8_null(item, &contact->telex); pst_convert_utf8_null(item, &contact->job_title); pst_convert_utf8_null(item, &contact->profession); pst_convert_utf8_null(item, &contact->assistant_name); pst_convert_utf8_null(item, &contact->assistant_phone); pst_convert_utf8_null(item, &contact->company_name); pst_convert_utf8_null(item, &item->body); // the specification I am following is (hopefully) RFC2426 vCard Mime Directory Profile fprintf(f_output, "BEGIN:VCARD\n"); fprintf(f_output, "FN:%s\n", pst_rfc2426_escape(contact->fullname.str, &result, &resultlen)); //fprintf(f_output, "N:%s;%s;%s;%s;%s\n", fprintf(f_output, "N:%s;", (!contact->surname.str) ? "" : pst_rfc2426_escape(contact->surname.str, &result, &resultlen)); fprintf(f_output, "%s;", (!contact->first_name.str) ? "" : pst_rfc2426_escape(contact->first_name.str, &result, &resultlen)); fprintf(f_output, "%s;", (!contact->middle_name.str) ? "" : pst_rfc2426_escape(contact->middle_name.str, &result, &resultlen)); fprintf(f_output, "%s;", (!contact->display_name_prefix.str) ? "" : pst_rfc2426_escape(contact->display_name_prefix.str, &result, &resultlen)); fprintf(f_output, "%s\n", (!contact->suffix.str) ? "" : pst_rfc2426_escape(contact->suffix.str, &result, &resultlen)); if (contact->nickname.str) fprintf(f_output, "NICKNAME:%s\n", pst_rfc2426_escape(contact->nickname.str, &result, &resultlen)); if (contact->address1.str) fprintf(f_output, "EMAIL:%s\n", pst_rfc2426_escape(contact->address1.str, &result, &resultlen)); if (contact->address2.str) fprintf(f_output, "EMAIL:%s\n", pst_rfc2426_escape(contact->address2.str, &result, &resultlen)); if (contact->address3.str) fprintf(f_output, "EMAIL:%s\n", pst_rfc2426_escape(contact->address3.str, &result, &resultlen)); if (contact->birthday) fprintf(f_output, "BDAY:%s\n", pst_rfc2425_datetime_format(contact->birthday, sizeof(time_buffer), time_buffer)); if (contact->home_address.str) { //fprintf(f_output, "ADR;TYPE=home:%s;%s;%s;%s;%s;%s;%s\n", fprintf(f_output, "ADR;TYPE=home:%s;", (!contact->home_po_box.str) ? "" : pst_rfc2426_escape(contact->home_po_box.str, &result, &resultlen)); fprintf(f_output, "%s;", ""); // extended Address fprintf(f_output, "%s;", (!contact->home_street.str) ? "" : pst_rfc2426_escape(contact->home_street.str, &result, &resultlen)); fprintf(f_output, "%s;", (!contact->home_city.str) ? "" : pst_rfc2426_escape(contact->home_city.str, &result, &resultlen)); fprintf(f_output, "%s;", (!contact->home_state.str) ? "" : pst_rfc2426_escape(contact->home_state.str, &result, &resultlen)); fprintf(f_output, "%s;", (!contact->home_postal_code.str) ? "" : pst_rfc2426_escape(contact->home_postal_code.str, &result, &resultlen)); fprintf(f_output, "%s\n", (!contact->home_country.str) ? "" : pst_rfc2426_escape(contact->home_country.str, &result, &resultlen)); fprintf(f_output, "LABEL;TYPE=home:%s\n", pst_rfc2426_escape(contact->home_address.str, &result, &resultlen)); } if (contact->business_address.str) { //fprintf(f_output, "ADR;TYPE=work:%s;%s;%s;%s;%s;%s;%s\n", fprintf(f_output, "ADR;TYPE=work:%s;", (!contact->business_po_box.str) ? "" : pst_rfc2426_escape(contact->business_po_box.str, &result, &resultlen)); fprintf(f_output, "%s;", ""); // extended Address fprintf(f_output, "%s;", (!contact->business_street.str) ? "" : pst_rfc2426_escape(contact->business_street.str, &result, &resultlen)); fprintf(f_output, "%s;", (!contact->business_city.str) ? "" : pst_rfc2426_escape(contact->business_city.str, &result, &resultlen)); fprintf(f_output, "%s;", (!contact->business_state.str) ? "" : pst_rfc2426_escape(contact->business_state.str, &result, &resultlen)); fprintf(f_output, "%s;", (!contact->business_postal_code.str) ? "" : pst_rfc2426_escape(contact->business_postal_code.str, &result, &resultlen)); fprintf(f_output, "%s\n", (!contact->business_country.str) ? "" : pst_rfc2426_escape(contact->business_country.str, &result, &resultlen)); fprintf(f_output, "LABEL;TYPE=work:%s\n", pst_rfc2426_escape(contact->business_address.str, &result, &resultlen)); } if (contact->other_address.str) { //fprintf(f_output, "ADR;TYPE=postal:%s;%s;%s;%s;%s;%s;%s\n", fprintf(f_output, "ADR;TYPE=postal:%s;",(!contact->other_po_box.str) ? "" : pst_rfc2426_escape(contact->other_po_box.str, &result, &resultlen)); fprintf(f_output, "%s;", ""); // extended Address fprintf(f_output, "%s;", (!contact->other_street.str) ? "" : pst_rfc2426_escape(contact->other_street.str, &result, &resultlen)); fprintf(f_output, "%s;", (!contact->other_city.str) ? "" : pst_rfc2426_escape(contact->other_city.str, &result, &resultlen)); fprintf(f_output, "%s;", (!contact->other_state.str) ? "" : pst_rfc2426_escape(contact->other_state.str, &result, &resultlen)); fprintf(f_output, "%s;", (!contact->other_postal_code.str) ? "" : pst_rfc2426_escape(contact->other_postal_code.str, &result, &resultlen)); fprintf(f_output, "%s\n", (!contact->other_country.str) ? "" : pst_rfc2426_escape(contact->other_country.str, &result, &resultlen)); fprintf(f_output, "LABEL;TYPE=postal:%s\n", pst_rfc2426_escape(contact->other_address.str, &result, &resultlen)); } if (contact->business_fax.str) fprintf(f_output, "TEL;TYPE=work,fax:%s\n", pst_rfc2426_escape(contact->business_fax.str, &result, &resultlen)); if (contact->business_phone.str) fprintf(f_output, "TEL;TYPE=work,voice:%s\n", pst_rfc2426_escape(contact->business_phone.str, &result, &resultlen)); if (contact->business_phone2.str) fprintf(f_output, "TEL;TYPE=work,voice:%s\n", pst_rfc2426_escape(contact->business_phone2.str, &result, &resultlen)); if (contact->car_phone.str) fprintf(f_output, "TEL;TYPE=car,voice:%s\n", pst_rfc2426_escape(contact->car_phone.str, &result, &resultlen)); if (contact->home_fax.str) fprintf(f_output, "TEL;TYPE=home,fax:%s\n", pst_rfc2426_escape(contact->home_fax.str, &result, &resultlen)); if (contact->home_phone.str) fprintf(f_output, "TEL;TYPE=home,voice:%s\n", pst_rfc2426_escape(contact->home_phone.str, &result, &resultlen)); if (contact->home_phone2.str) fprintf(f_output, "TEL;TYPE=home,voice:%s\n", pst_rfc2426_escape(contact->home_phone2.str, &result, &resultlen)); if (contact->isdn_phone.str) fprintf(f_output, "TEL;TYPE=isdn:%s\n", pst_rfc2426_escape(contact->isdn_phone.str, &result, &resultlen)); if (contact->mobile_phone.str) fprintf(f_output, "TEL;TYPE=cell,voice:%s\n", pst_rfc2426_escape(contact->mobile_phone.str, &result, &resultlen)); if (contact->other_phone.str) fprintf(f_output, "TEL;TYPE=msg:%s\n", pst_rfc2426_escape(contact->other_phone.str, &result, &resultlen)); if (contact->pager_phone.str) fprintf(f_output, "TEL;TYPE=pager:%s\n", pst_rfc2426_escape(contact->pager_phone.str, &result, &resultlen)); if (contact->primary_fax.str) fprintf(f_output, "TEL;TYPE=fax,pref:%s\n", pst_rfc2426_escape(contact->primary_fax.str, &result, &resultlen)); if (contact->primary_phone.str) fprintf(f_output, "TEL;TYPE=phone,pref:%s\n", pst_rfc2426_escape(contact->primary_phone.str, &result, &resultlen)); if (contact->radio_phone.str) fprintf(f_output, "TEL;TYPE=pcs:%s\n", pst_rfc2426_escape(contact->radio_phone.str, &result, &resultlen)); if (contact->telex.str) fprintf(f_output, "TEL;TYPE=bbs:%s\n", pst_rfc2426_escape(contact->telex.str, &result, &resultlen)); if (contact->job_title.str) fprintf(f_output, "TITLE:%s\n", pst_rfc2426_escape(contact->job_title.str, &result, &resultlen)); if (contact->profession.str) fprintf(f_output, "ROLE:%s\n", pst_rfc2426_escape(contact->profession.str, &result, &resultlen)); if (contact->assistant_name.str || contact->assistant_phone.str) { fprintf(f_output, "AGENT:BEGIN:VCARD\n"); if (contact->assistant_name.str) fprintf(f_output, "FN:%s\n", pst_rfc2426_escape(contact->assistant_name.str, &result, &resultlen)); if (contact->assistant_phone.str) fprintf(f_output, "TEL:%s\n", pst_rfc2426_escape(contact->assistant_phone.str, &result, &resultlen)); } if (contact->company_name.str) fprintf(f_output, "ORG:%s\n", pst_rfc2426_escape(contact->company_name.str, &result, &resultlen)); if (comment) fprintf(f_output, "NOTE:%s\n", pst_rfc2426_escape(comment, &result, &resultlen)); if (item->body.str) fprintf(f_output, "NOTE:%s\n", pst_rfc2426_escape(item->body.str, &result, &resultlen)); write_extra_categories(f_output, item); fprintf(f_output, "VERSION:3.0\n"); fprintf(f_output, "END:VCARD\n\n"); if (result) free(result); DEBUG_RET(); } /** * write extra vcard or vcalendar categories from the extra keywords fields * * @param f_output open file pointer * @param item pst item containing the keywords * @return true if we write a categories line */ int write_extra_categories(FILE* f_output, pst_item* item) { char* result = NULL; size_t resultlen = 0; pst_item_extra_field *ef = item->extra_fields; const char *fmt = "CATEGORIES:%s"; int category_started = 0; while (ef) { if (strcmp(ef->field_name, "Keywords") == 0) { fprintf(f_output, fmt, pst_rfc2426_escape(ef->value, &result, &resultlen)); fmt = ", %s"; category_started = 1; } ef = ef->next; } if (category_started) fprintf(f_output, "\n"); if (result) free(result); return category_started; } void write_journal(FILE* f_output, pst_item* item) { char* result = NULL; size_t resultlen = 0; char time_buffer[30]; pst_item_journal* journal = item->journal; // make everything utf8 pst_convert_utf8_null(item, &item->subject); pst_convert_utf8_null(item, &item->body); fprintf(f_output, "BEGIN:VJOURNAL\n"); fprintf(f_output, "DTSTAMP:%s\n", pst_rfc2445_datetime_format_now(sizeof(time_buffer), time_buffer)); if (item->create_date) fprintf(f_output, "CREATED:%s\n", pst_rfc2445_datetime_format(item->create_date, sizeof(time_buffer), time_buffer)); if (item->modify_date) fprintf(f_output, "LAST-MOD:%s\n", pst_rfc2445_datetime_format(item->modify_date, sizeof(time_buffer), time_buffer)); if (item->subject.str) fprintf(f_output, "SUMMARY:%s\n", pst_rfc2426_escape(item->subject.str, &result, &resultlen)); if (item->body.str) fprintf(f_output, "DESCRIPTION:%s\n", pst_rfc2426_escape(item->body.str, &result, &resultlen)); if (journal && journal->start) fprintf(f_output, "DTSTART;VALUE=DATE-TIME:%s\n", pst_rfc2445_datetime_format(journal->start, sizeof(time_buffer), time_buffer)); fprintf(f_output, "END:VJOURNAL\n"); if (result) free(result); } void write_appointment(FILE* f_output, pst_item* item) { char* result = NULL; size_t resultlen = 0; char time_buffer[30]; pst_item_appointment* appointment = item->appointment; // make everything utf8 pst_convert_utf8_null(item, &item->subject); pst_convert_utf8_null(item, &item->body); pst_convert_utf8_null(item, &appointment->location); fprintf(f_output, "UID:%#"PRIx64"\n", item->block_id); fprintf(f_output, "DTSTAMP:%s\n", pst_rfc2445_datetime_format_now(sizeof(time_buffer), time_buffer)); if (item->create_date) fprintf(f_output, "CREATED:%s\n", pst_rfc2445_datetime_format(item->create_date, sizeof(time_buffer), time_buffer)); if (item->modify_date) fprintf(f_output, "LAST-MOD:%s\n", pst_rfc2445_datetime_format(item->modify_date, sizeof(time_buffer), time_buffer)); if (item->subject.str) fprintf(f_output, "SUMMARY:%s\n", pst_rfc2426_escape(item->subject.str, &result, &resultlen)); if (item->body.str) fprintf(f_output, "DESCRIPTION:%s\n", pst_rfc2426_escape(item->body.str, &result, &resultlen)); if (appointment && appointment->start) fprintf(f_output, "DTSTART;VALUE=DATE-TIME:%s\n", pst_rfc2445_datetime_format(appointment->start, sizeof(time_buffer), time_buffer)); if (appointment && appointment->end) fprintf(f_output, "DTEND;VALUE=DATE-TIME:%s\n", pst_rfc2445_datetime_format(appointment->end, sizeof(time_buffer), time_buffer)); if (appointment && appointment->location.str) fprintf(f_output, "LOCATION:%s\n", pst_rfc2426_escape(appointment->location.str, &result, &resultlen)); if (appointment) { switch (appointment->showas) { case PST_FREEBUSY_TENTATIVE: fprintf(f_output, "STATUS:TENTATIVE\n"); break; case PST_FREEBUSY_FREE: // mark as transparent and as confirmed fprintf(f_output, "TRANSP:TRANSPARENT\n"); case PST_FREEBUSY_BUSY: case PST_FREEBUSY_OUT_OF_OFFICE: fprintf(f_output, "STATUS:CONFIRMED\n"); break; } if (appointment->is_recurring) { const char* rules[] = {"DAILY", "WEEKLY", "MONTHLY", "YEARLY"}; const char* days[] = {"SU", "MO", "TU", "WE", "TH", "FR", "SA"}; pst_recurrence *rdata = pst_convert_recurrence(appointment); fprintf(f_output, "RRULE:FREQ=%s", rules[rdata->type]); if (rdata->count) fprintf(f_output, ";COUNT=%u", rdata->count); if ((rdata->interval != 1) && (rdata->interval)) fprintf(f_output, ";INTERVAL=%u", rdata->interval); if (rdata->dayofmonth) fprintf(f_output, ";BYMONTHDAY=%d", rdata->dayofmonth); if (rdata->monthofyear) fprintf(f_output, ";BYMONTH=%d", rdata->monthofyear); if (rdata->position) fprintf(f_output, ";BYSETPOS=%d", rdata->position); if (rdata->bydaymask) { char byday[40]; int empty = 1; int i=0; memset(byday, 0, sizeof(byday)); for (i=0; i<7; i++) { int bit = 1 << i; if (bit & rdata->bydaymask) { char temp[40]; snprintf(temp, sizeof(temp), "%s%s%s", byday, (empty) ? ";BYDAY=" : ";", days[i]); strcpy(byday, temp); empty = 0; } } fprintf(f_output, "%s", byday); } fprintf(f_output, "\n"); pst_free_recurrence(rdata); } switch (appointment->label) { case PST_APP_LABEL_NONE: if (!write_extra_categories(f_output, item)) fprintf(f_output, "CATEGORIES:NONE\n"); break; case PST_APP_LABEL_IMPORTANT: fprintf(f_output, "CATEGORIES:IMPORTANT\n"); break; case PST_APP_LABEL_BUSINESS: fprintf(f_output, "CATEGORIES:BUSINESS\n"); break; case PST_APP_LABEL_PERSONAL: fprintf(f_output, "CATEGORIES:PERSONAL\n"); break; case PST_APP_LABEL_VACATION: fprintf(f_output, "CATEGORIES:VACATION\n"); break; case PST_APP_LABEL_MUST_ATTEND: fprintf(f_output, "CATEGORIES:MUST-ATTEND\n"); break; case PST_APP_LABEL_TRAVEL_REQ: fprintf(f_output, "CATEGORIES:TRAVEL-REQUIRED\n"); break; case PST_APP_LABEL_NEEDS_PREP: fprintf(f_output, "CATEGORIES:NEEDS-PREPARATION\n"); break; case PST_APP_LABEL_BIRTHDAY: fprintf(f_output, "CATEGORIES:BIRTHDAY\n"); break; case PST_APP_LABEL_ANNIVERSARY: fprintf(f_output, "CATEGORIES:ANNIVERSARY\n"); break; case PST_APP_LABEL_PHONE_CALL: fprintf(f_output, "CATEGORIES:PHONE-CALL\n"); break; } // ignore bogus alarms if (appointment->alarm && (appointment->alarm_minutes >= 0) && (appointment->alarm_minutes < 1440)) { fprintf(f_output, "BEGIN:VALARM\n"); fprintf(f_output, "TRIGGER:-PT%dM\n", appointment->alarm_minutes); fprintf(f_output, "ACTION:DISPLAY\n"); fprintf(f_output, "DESCRIPTION:Reminder\n"); fprintf(f_output, "END:VALARM\n"); } } fprintf(f_output, "END:VEVENT\n"); if (result) free(result); } void create_enter_dir(struct file_ll* f, pst_item *item) { memset(f, 0, sizeof(*f)); f->stored_count = (item->folder) ? item->folder->item_count : 0; pst_convert_utf8(item, &item->file_as); f->dname = (char*) pst_malloc(strlen(item->file_as.str)+1); strcpy(f->dname, item->file_as.str); DEBUG_ENT("create_enter_dir"); if (mode == MODE_KMAIL) { int32_t t; mk_kmail_dir(item->file_as.str); for (t=0; tname[t] = (char*) pst_malloc(strlen(item->file_as.str)+strlen(OUTPUT_TEMPLATE)+30); sprintf(f->name[t], OUTPUT_TEMPLATE, item->file_as.str, item_type_to_name(t)); } } } else if (mode == MODE_RECURSE) { int32_t t; mk_recurse_dir(item->file_as.str); for (t=0; tname[t] = strdup(item_type_to_name(t)); } } if (mode_thunder) { FILE *type_file = fopen(".type", "w"); fprintf(type_file, "%d\n", item->type); fclose(type_file); } } else if (mode == MODE_SEPARATE) { // do similar stuff to recurse here. int32_t t; mk_separate_dir(item->file_as.str); for (t=0; tname[t] = (char*) pst_malloc(file_name_len); memset(f->name[t], 0, file_name_len); } } } else { // MODE_NORMAL int32_t t; for (t=0; tname[t] = (char*) pst_malloc(strlen(item->file_as.str)+strlen(OUTPUT_TEMPLATE)+30); sprintf(f->name[t], OUTPUT_TEMPLATE, item->file_as.str, item_type_to_name(t)); } } } if (mode != MODE_SEPARATE) { int32_t t; for (t=0; tname[t]) { if (!overwrite) { int x = 0; char *temp = (char*) pst_malloc (strlen(f->name[t])+10); //enough room for 10 digits sprintf(temp, "%s", f->name[t]); check_filename(temp); while ((f->output[t] = fopen(temp, "r"))) { DEBUG_INFO(("need to increase filename because one already exists with that name\n")); x++; sprintf(temp, "%s%08d", f->name[t], x); DEBUG_INFO(("- bump file name and try \"%s\"\n", temp)); if (x == 99999999) { DIE(("create_enter_dir: Why can I not create a folder %s? I have tried %i extensions...\n", f->name[t], x)); } fclose(f->output[t]); } if (x > 0) { //then the f->name should change free (f->name[t]); f->name[t] = temp; } else { free(temp); } } check_filename(f->name[t]); if (!(f->output[t] = fopen(f->name[t], "w"))) { DIE(("create_enter_dir: Could not open file \"%s\" for write\n", f->name[t])); } DEBUG_INFO(("f->name = %s\nitem->folder_name = %s\n", f->name[t], item->file_as.str)); } } } DEBUG_RET(); } void close_enter_dir(struct file_ll *f) { int32_t t; DEBUG_INFO(("processed item count for folder %s is %i, skipped %i, total %i \n", f->dname, f->item_count, f->skip_count, f->stored_count)); if (output_mode != OUTPUT_QUIET) { pst_debug_lock(); printf("\t\"%s\" - %i items done, %i items skipped.\n", f->dname, f->item_count, f->skip_count); fflush(stdout); pst_debug_unlock(); } for (t=0; toutput[t]) { if (mode == MODE_SEPARATE) DEBUG_WARN(("close_enter_dir finds open separate file\n")); fclose(f->output[t]); f->output[t] = NULL; } if (f->name[t]) { struct stat st; stat(f->name[t], &st); if (!st.st_size) { DEBUG_WARN(("removing empty output file %s\n", f->name[t])); remove(f->name[t]); } free(f->name[t]); f->name[t] = NULL; } } free(f->dname); if (mode == MODE_KMAIL) close_kmail_dir(); else if (mode == MODE_RECURSE) { if (mode_thunder) { FILE *type_file = fopen(".size", "w"); fprintf(type_file, "%i %i\n", f->item_count, f->stored_count); fclose(type_file); } close_recurse_dir(); } else if (mode == MODE_SEPARATE) close_separate_dir(); }