Page MenuHomePhorge

No OneTemporary

Authored By
Unknown
Size
160 KB
Referenced Files
None
Subscribers
None
diff --git a/ChangeLog b/ChangeLog
index d1f5f3b..4125f81 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,869 +1,873 @@
+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 sytems 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 <rsimpson@idiscoverglobal.com>
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 <joachim.metz@gmail.com> 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 thru 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
<http://msdn2.microsoft.com/en-us/library/aa454925.aspx>
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 mesage 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
<alexandergrau@gmx.de> and patches from Sean Loaring <sloaring@tec-man.com>.
* fix from Antonio Palama <palama@inwind.it> 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 <bradh@frogmouth.net> for buffer
overruns in liv-zemple decoding for corrupted or malicious pst files.
LibPST 0.5.11 (2007-08-24)
===============================
* fix from Stevens Miller <smiller@novadatalabs.com>
for unitialized 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, alot 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
<http://lists.alioth.debian.org/pipermail/libpst-devel/2004-November/000000.html>
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 <bakker@fox-it.com>.
* ensure there's a blank line between header and body
patch from <johnh@aproposretail.com> (SourceForge #890745).
* Apply accumulated endian-related patches
* Removed unused files, upstream's debian/ dir
-- Joe Nahmias <joe@nahmias.net>
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
<dave.s@earthcorp.com>
LibPST v0.4.3
=============
Bug fix release. No extra functionality
Dave Smith
<dave.s@earthcorp.com>
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 embeded
items. They are not fully re-created using Mime-types, but are
accessible with the -S switch (which saves everything as seperate
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
<dave.s@earthcorp.com>
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 "Seperate". 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 seperate files counting from 0 upwards,
in a folder named as the PST folder.
When an email has an attachment, it is saved as a seperate 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
<dave.s@earthcorp.com>
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 enhancment and
bugfix release.
Dave Smith
<dave.s@earthcorp.com>
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
<dave.s@earthcorp.com>
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
<dave.s@earthcorp.com>
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
ceasar 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 mearly stored there. This means that the
readpst application is able to convert PST files without knowing the
password. Microsoft have some explaning 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
<dave.s@earthcorp.com>
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
<dave.s@earthcorp.com>
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 dd77004..b451044 100644
--- a/NEWS
+++ b/NEWS
@@ -1,69 +1,70 @@
+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 unitialized 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.in b/configure.in
index d2cbb8c..590138a 100644
--- a/configure.in
+++ b/configure.in
@@ -1,377 +1,378 @@
AC_PREREQ(2.59)
-AC_INIT(libpst,0.6.57,carl@five-ten-sg.com)
+AC_INIT(libpst,0.6.58,carl@five-ten-sg.com)
AC_CONFIG_SRCDIR([src/libpst.c])
AC_CONFIG_HEADER([config.h])
AM_INIT_AUTOMAKE
AC_CANONICAL_HOST
#
# 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:6:1'
+libpst_version_info='5:7: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
# 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 <gd.h> 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
AC_PROG_RANLIB
# 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])
AC_SEARCH_LIBS([sem_init],[pthread rt])
# 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])
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
py_ver=`echo $ax_python_bin | cut -c7-`
# find the flags for that version
AC_PYTHON_DEVEL([$py_ver])
PYTHON_INCLUDE_DIR=`echo $python_path | cut -c3-`
AC_SUBST([PYTHON_INCLUDE_DIR])
# 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"])
gsf_flags="`pkg-config libgsf-1 --cflags`"
gsf_libs="`pkg-config libgsf-1 --libs`"
AC_SUBST(GSF_FLAGS, [$gsf_flags])
AC_SUBST(GSF_LIBS, [$gsf_libs])
AC_OUTPUT( \
Makefile \
debian/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 4ed1054..3761449 100644
--- a/libpst.spec.in
+++ b/libpst.spec.in
@@ -1,427 +1,430 @@
Summary: Utilities to convert Outlook .pst files to other formats
Name: @PACKAGE@
Version: @VERSION@
Release: 1%{?dist}
License: GPLv2+
Group: Applications/Productivity
Source: http://www.five-ten-sg.com/%{name}/packages/%{name}-%{version}.tar.gz
BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
URL: http://www.five-ten-sg.com/%{name}/
Requires: ImageMagick libgsf
Requires: %{name}-libs = %{version}-%{release}
BuildRequires: ImageMagick gd-devel zlib-devel python-devel boost-devel libgsf-devel
%{!?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)")}
%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.
%package libs
Summary: Shared library used by the pst utilities
Group: Development/Libraries
%description libs
The libpst-libs package contains the shared library used by the pst
utilities.
%package python
Summary: Python bindings for libpst
Group: Development/Libraries
Requires: python
Requires: %{name}-libs = %{version}-%{release}
%{?filter_setup:
%filter_provides_in %{python_sitearch}/_.*\.so$
%filter_setup
}
%description python
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
Group: Development/Libraries
Requires: pkgconfig
Requires: %{name}-libs = %{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
Group: Documentation
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
Group: Documentation
%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
%configure --enable-libpst-shared
make %{?_smp_mflags}
%install
rm -rf $RPM_BUILD_ROOT
make DESTDIR=$RPM_BUILD_ROOT install
rm $RPM_BUILD_ROOT%{_libdir}/libpst.la
rm $RPM_BUILD_ROOT%{_libdir}/libpst.a
%clean
rm -rf $RPM_BUILD_ROOT
%post libs -p /sbin/ldconfig
%postun libs -p /sbin/ldconfig
%files
%defattr(-,root,root,-)
%{_bindir}/*
%{_mandir}/man1/*
%{_mandir}/man5/*
%files libs
%defattr(-,root,root,-)
%{_libdir}/libpst.so.*
%doc COPYING
%files python
%defattr(-,root,root,-)
%{python_sitearch}/_*.so
%exclude %{python_sitearch}/*.a
%exclude %{python_sitearch}/*.la
%files devel
%defattr(-,root,root,-)
%{_libdir}/libpst.so
%{_includedir}/%{name}-@LIBPST_SO_MAJOR@/
%{_libdir}/pkgconfig/libpst.pc
%files devel-doc
%defattr(-,root,root,-)
%{_datadir}/doc/%{name}-%{version}/devel/
%files doc
%defattr(-,root,root,-)
%dir %{_datadir}/doc/%{name}-%{version}/
%{_datadir}/doc/%{name}-%{version}/*.html
%{_datadir}/doc/%{name}-%{version}/AUTHORS
%{_datadir}/doc/%{name}-%{version}/COPYING
%{_datadir}/doc/%{name}-%{version}/ChangeLog
%{_datadir}/doc/%{name}-%{version}/NEWS
%{_datadir}/doc/%{name}-%{version}/README
%changelog
+* Wed Dec 28 2012 Carl Byington <carl@five-ten-sg.com> - 0.6.58-1
+- fix From quoting on embedded rfc/822 messages
+
* Wed Dec 26 2012 Carl Byington <carl@five-ten-sg.com> - 0.6.57-1
- bugzilla 852414, remove unnecessary dependencies
* Mon Dec 24 2012 Carl Byington <carl@five-ten-sg.com> - 0.6.56-1
- filter private provides from rpm
- merge -m .msg files code into main branch
* Tue Aug 09 2012 Carl Byington <carl@five-ten-sg.com> - 0.6.55-2
- rebuild for python
* Thu Jul 19 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.6.54-6
- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild
* Tue May 08 2012 Carl Byington <carl@five-ten-sg.com> - 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 <rel-eng@lists.fedoraproject.org> - 0.6.54-5
- Rebuilt for c++ ABI breakage
* Fri Jan 13 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.6.54-4
- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild
* Sat Dec 24 2011 Carl Byington <carl@five-ten-sg.com> - 0.6.54-3
- bump versions and prep for fedora build
* Wed Nov 30 2011 Petr Pisar <ppisar@redhat.com> - 0.6.53-3
- Rebuild against boost-1.48
* Wed Nov 14 2011 Carl Byington <carl@five-ten-sg.com> - 0.6.54-2
- failed to bump version number
* Fri Nov 04 2011 Carl Byington <carl@five-ten-sg.com> - 0.6.54-1
- embedded rfc822 messages might contain rtf encoded bodies
* Fri Sep 02 2011 Petr Pisar <ppisar@redhat.com> - 0.6.53-2
- Rebuild against boost-1.47
* Sun Jul 10 2011 Carl Byington <carl@five-ten-sg.com> - 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 <carl@five-ten-sg.com> - 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 <carl@five-ten-sg.com> - 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 <rel-eng@lists.fedoraproject.org> - 0.6.49-4
- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild
* Mon Feb 07 2011 Thomas Spura <tomspur@fedoraproject.org> - 0.6.49-3
- rebuild for new boost
* Fri Dec 24 2010 Carl Byington <carl@five-ten-sg.com> - 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 <carl@five-ten-sg.com> - 0.6.49-1
- fix to ignore embedded objects that are not email messages
fedora bugzilla 633498
* Thu Sep 02 2010 Carl Byington <carl@five-ten-sg.com> - 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 <carl@five-ten-sg.com> - 0.6.47-6
- rebuild for python dependencies
* Mon Jul 26 2010 David Malcolm <dmalcolm@redhat.com> - 0.6.47-4
- hack up configure so that it looks for python 2.7
* Wed Jul 21 2010 David Malcolm <dmalcolm@redhat.com> - 0.6.47-3
- Rebuilt for https://fedoraproject.org/wiki/Features/Python_2.7/MassRebuild
* Wed Jul 07 2010 Carl Byington <carl@five-ten-sg.com> - 0.6.47-2
- Subpackage Licensing, add COPYING to -libs.
- patches from Kenneth Berland for solaris
* Fri May 07 2010 Carl Byington <carl@five-ten-sg.com> - 0.6.47-1
- patches from Kenneth Berland for solaris
* Thu Jan 21 2010 Carl Byington <carl@five-ten-sg.com> - 0.6.46-1
- prefer libpthread over librt for finding sem_init function.
* Thu Jan 21 2010 Carl Byington <carl@five-ten-sg.com> - 0.6.45-2
- rebuild for new boost package
* Wed Nov 18 2009 Carl Byington <carl@five-ten-sg.com> - 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 <carl@five-ten-sg.com> - 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.
* Thu Sep 12 2009 Carl Byington <carl@five-ten-sg.com> - 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 <carl@five-ten-sg.com> - 0.6.42-1
- patch from Fridrich Strba to build with DJGPP DOS cross-compiler.
* Sat Jul 25 2009 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.6.41-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild
* Tue Jun 23 2009 Carl Byington <carl@five-ten-sg.com> - 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 <carl@five-ten-sg.com> - 0.6.40-1
- fedora 11 has python2.6
- remove pdf version of the man pages
* Sun Jun 21 2009 Carl Byington <carl@five-ten-sg.com> - 0.6.39-1
- fedora > 10 moved to boost-python-devel
* Sun Jun 21 2009 Carl Byington <carl@five-ten-sg.com> - 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 <carl@five-ten-sg.com> - 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 <carl@five-ten-sg.com> - 0.6.36-1
- build separate -doc and -devel-doc subpackages.
- other spec file cleanup
* Wed Apr 08 2009 Carl Byington <carl@five-ten-sg.com> - 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 <carl@five-ten-sg.com> - 0.6.34-1
- avoid putting mixed item types into the same output folder.
* Tue Mar 17 2009 Carl Byington <carl@five-ten-sg.com> - 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 <carl@five-ten-sg.com> - 0.6.32-1
- fix ppc64 compile error
* Sat Mar 14 2009 Carl Byington <carl@five-ten-sg.com> - 0.6.31-1
- bump version for fedora cvs tagging mistake
* Sat Mar 14 2009 Carl Byington <carl@five-ten-sg.com> - 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 <carl@five-ten-sg.com> - 0.6.29-1
- fix for 64bit on Fedora 11
* Tue Feb 24 2009 Carl Byington <carl@five-ten-sg.com> - 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 <carl@five-ten-sg.com> - 0.6.27-1
- fix for const correctness on Fedora 11
* Sat Feb 07 2009 Carl Byington <carl@five-ten-sg.com> - 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 <carl@five-ten-sg.com> - 0.6.25-1
- improve handling of content-type charset values in mime parts
* Thu Dec 11 2008 Carl Byington <carl@five-ten-sg.com> - 0.6.24-1
- patch from Chris Eagle to build on cygwin
* Thu Dec 04 2008 Carl Byington <carl@five-ten-sg.com> - 0.6.23-1
- bump version to avoid cvs tagging mistake in fedora
* Fri Nov 28 2008 Carl Byington <carl@five-ten-sg.com> - 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 <carl@five-ten-sg.com> - 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 <carl@five-ten-sg.com> - 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 <carl@five-ten-sg.com> - 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 <carl@five-ten-sg.com> - 0.6.18-1
- Fixes for iconv on Mac from Justin Greer.
* Tue Aug 05 2008 Carl Byington <carl@five-ten-sg.com> - 0.6.17-1
- More fixes for 32/64 bit portability on big endian ppc.
* Tue Aug 05 2008 Carl Byington <carl@five-ten-sg.com> - 0.6.16-1
- Use inttypes.h for portable printing of 64 bit items.
* Wed Jul 30 2008 Carl Byington <carl@five-ten-sg.com> - 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 <carl@five-ten-sg.com> - 0.6.14-1
- Fix my mistake in debian packaging.
* Fri Jun 13 2008 Carl Byington <carl@five-ten-sg.com> - 0.6.13-1
- Patch from Robert Simpson for encryption type 2.
* Tue Jun 10 2008 Carl Byington <carl@five-ten-sg.com> - 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 <carl@five-ten-sg.com> - 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 <carl@five-ten-sg.com> - 0.6.10-1
- Patch from Robert Simpson for doubly-linked list code and arrays of unicode strings.
* Fri May 16 2008 Carl Byington <carl@five-ten-sg.com> - 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 <carl@five-ten-sg.com> - 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 <carl@five-ten-sg.com> - 0.5.5
- merge changes from Joe Nahmias version
* Sun Feb 19 2006 Carl Byington <carl@five-ten-sg.com> - 0.5.3
- initial spec file using autoconf and http://www.fedora.us/docs/rpm-packaging-guidelines.html
diff --git a/regression/regression-tests.bash b/regression/regression-tests.bash
index a23e07a..fb639d8 100644
--- a/regression/regression-tests.bash
+++ b/regression/regression-tests.bash
@@ -1,148 +1,148 @@
#!/bin/bash
function consistency()
{
# check source and xml documentation for consistency
(
cd .. # back to top level of project
f1=/tmp/f1$$
f2=/tmp/f2$$
grep 'case 0x' src/libpst.c | awk '{print $2}' | tr A-Z a-z | sed -e 's/://g' | sort >$f1
grep '^0x' xml/libpst.in | awk '{print $1}' | (for i in {1..19}; do read a; done; cat) | sort >$f2
diff $f1 $f2
less $f1
rm -f $f1 $f2
)
}
function dodii()
{
n="$1"
fn="$2"
ba=$(basename "$fn" .pst)
size=$(stat -c %s $fn)
rm -rf output$n
if [ -z "$val" ] || [ $size -lt 10000000 ]; then
echo $fn
mkdir output$n
$val ../src/pst2dii -f /usr/share/fonts/bitstream-vera/VeraMono.ttf -B "bates-" -o output$n -O $ba.mydii -d $fn.log $fn >$fn.dii.err 2>&1
fi
}
function doldif()
{
n="$1"
fn="$2"
ba=$(basename "$fn" .pst)
size=$(stat -c %s $fn)
rm -rf output$n
if [ -z "$val" ] || [ $size -lt 10000000 ]; then
echo $fn
mkdir output$n
$val ../src/pst2ldif -d $ba.ldif.log -b 'o=ams-cc.com, c=US' -c 'inetOrgPerson' $fn >$ba.ldif.err 2>&1
fi
}
function dopst()
{
n="$1"
fn="$2"
ba=$(basename "$fn" .pst)
size=$(stat -c %s $fn)
jobs=""
[ -n "$val" ] && jobs="-j 0"
rm -rf output$n
if [ -z "$val" ] || [ $size -lt 100000000 ]; then
echo $fn
mkdir output$n
if [ "$regression" == "yes" ]; then
$val ../src/readpst $jobs -te -r -cv -o output$n $fn >$ba.err 2>&1
else
## only email and include deleted items, have a deleted items folder with multiple item types
#$val ../src/readpst $jobs -te -r -D -cv -o output$n -d $ba.log $fn >$ba.err 2>&1
## normal recursive dump
- #char='us-ascii'
+ char='us-ascii'
#char='BIG-5'
- #echo $val ../src/readpst -C $char -j 0 -r -cv -o output$n -d $ba.log $fn
- # $val ../src/readpst -C $char -j 0 -r -cv -o output$n -d $ba.log $fn >$ba.err 2>&1
+ echo $val ../src/readpst -C $char -j 0 -r -cv -o output$n -d $ba.log $fn
+ $val ../src/readpst -C $char -j 0 -r -cv -o output$n -d $ba.log $fn >$ba.err 2>&1
## separate mode with filename extensions and .msg files
- echo $val ../src/readpst $jobs -r -m -D -cv -o output$n -d $ba.log $fn
- $val ../src/readpst $jobs -r -m -D -cv -o output$n -d $ba.log $fn >$ba.err 2>&1
+ #echo $val ../src/readpst $jobs -r -m -D -cv -o output$n -d $ba.log $fn
+ # $val ../src/readpst $jobs -r -m -D -cv -o output$n -d $ba.log $fn >$ba.err 2>&1
## separate mode where we decode all attachments to binary files
#echo $val ../src/readpst $jobs -r -S -D -cv -o output$n -d $ba.log $fn
# $val ../src/readpst $jobs -r -S -D -cv -o output$n -d $ba.log $fn >$ba.err 2>&1
## testing idblock
#../src/getidblock -p $fn 0 >$ba.fulldump
fi
fi
}
pushd ..
make || exit
popd
rm -rf output* *.err *.log
v="valgrind --leak-check=full"
val=""
func="dopst"
[ "$1" == "pst" ] && func="dopst"
[ "$1" == "pstv" ] && func="dopst" && val=$v
[ "$1" == "ldif" ] && func="doldif"
[ "$1" == "dii" ] && func="dodii"
regression=""
[ "$2" == "reg" ] && regression="yes"
[ "$regression" == "yes" ] && val=""
#$func 1 ams.pst
#$func 2 sample_64.pst
#$func 3 test.pst
#$func 4 big_mail.pst
#$func 5 mbmg.archive.pst
#$func 6 Single2003-read.pst
#$func 7 Single2003-unread.pst
#$func 8 ol2k3high.pst
#$func 9 ol97high.pst
#$func 10 returned_message.pst
#$func 11 flow.pst
#$func 12 test-html.pst
#$func 13 test-text.pst
#$func 14 joe.romanowski.pst
#$func 15 hourig1.pst
#$func 16 test-mac.pst
#$func 18 spam.pst
#$func 19 rendgen.pst # single email appointment
#$func 20 rendgen2.pst # email appointment with no termination date
#$func 21 rendgen3.pst # mime signed email
#$func 22 rendgen4.pst # appointment test cases
#$func 23 rendgen5.pst # appointment test cases
-#$func 24 paul.sheer.pst # embedded rfc822 attachment
+$func 24 paul.sheer.pst # embedded rfc822 attachment
#$func 25 jerry.pst # non ascii subject lines
#$func 26 phill.bertolus.pst # possible segfault in forked process, cannot reproduce
#$func 27 kaiser.pst # appointments with other character sets
#$func 28 pstsample.pst # character set issue
#$func 29 pstsample2.pst # embedded image in rtf data
$func 30 pstsample3.pst # exports of rtf and html
[ -n "$val" ] && grep 'lost:' *err | grep -v 'lost: 0 '
if [ "$regression" == "yes" ]; then
(
(for i in output*; do find $i -type f; done) | while read a; do
grep -v iamunique "$a"
rm -f "$a"
done
) >regression.txt
fi
diff --git a/src/readpst.c b/src/readpst.c
index 899ae72..38e3ac7 100644
--- a/src/readpst.c
+++ b/src/readpst.c
@@ -1,2205 +1,2206 @@
/***
* 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"
#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;
char *dname;
FILE * output;
int32_t stored_count;
int32_t item_count;
int32_t skip_count;
int32_t type;
};
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();
char* mk_kmail_dir(char* fname);
int close_kmail_dir();
char* mk_recurse_dir(char* dir, int32_t folder_type);
int close_recurse_dir();
char* mk_separate_dir(char *dir);
int close_separate_dir();
void mk_separate_file(struct file_ll *f, char *extension, int openit);
void close_separate_file(struct file_ll *f);
char* my_stristr(char *haystack, char *needle);
void check_filename(char *fname);
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);
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, char** extra_mime_headers);
+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);
const char* prog_name;
char* output_dir = ".";
char* kmail_chdir = NULL;
// 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 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;
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<active_children; i++) {
int status;
pid_t child = child_processes[i];
pid_t ch = waitpid(child, &status, ((waitall) ? 0 : WNOHANG));
if (ch == child) {
// check termination status
//if (WIFEXITED(status)) {
// int ext = WEXITSTATUS(status);
// printf("Process %d exited with status %d\n", child, ext);
// fflush(stdout);
//}
if (WIFSIGNALED(status)) {
int sig = WTERMSIG(status);
DEBUG_INFO(("Process %d terminated with signal %d\n", child, sig));
//printf("Process %d terminated with signal %d\n", child, sig);
//fflush(stdout);
}
// this has terminated, remove it from the list
for (j=i; j<active_children-1; j++) {
child_processes[j] = child_processes[j+1];
}
active_children--;
i--;
}
}
sem_getvalue(global_children, &available);
//printf("grim reaper %s for pid %d with %d children, %d available\n", (waitall) ? "all" : "", getpid(), active_children, available);
//fflush(stdout);
}
#endif
#endif
return available;
}
pid_t try_fork(char *folder)
{
#ifdef HAVE_FORK
#ifdef HAVE_SEMAPHORE_H
int available = grim_reaper(0);
if (available) {
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");
memset(&ff, 0, sizeof(ff));
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 {
if (!ff.type) ff.type = item->type;
if ((ff.type != PST_TYPE_CONTACT) && (mode != MODE_SEPARATE)) {
ff.skip_count++;
DEBUG_INFO(("I have a contact, but the folder type %"PRIi32" isn't a contacts folder. Skipping it\n", ff.type));
}
else {
ff.item_count++;
if (mode == MODE_SEPARATE) mk_separate_file(&ff, (mode_EX) ? ".vcf" : "", 1);
if (contact_mode == CMODE_VCARD) {
pst_convert_utf8_null(item, &item->comment);
write_vcard(ff.output, item, item->contact, item->comment.str);
}
else {
pst_convert_utf8(item, &item->contact->fullname);
pst_convert_utf8(item, &item->contact->address1);
fprintf(ff.output, "%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 {
if (!ff.type) ff.type = item->type;
if ((ff.type != PST_TYPE_NOTE) && (ff.type != PST_TYPE_SCHEDULE) && (ff.type != PST_TYPE_REPORT) && (mode != MODE_SEPARATE)) {
ff.skip_count++;
DEBUG_INFO(("I have an email type %"PRIi32", but the folder type %"PRIi32" isn't an email folder. Skipping it\n", item->type, ff.type));
}
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, (mode_EX) ? ".eml" : "", 1);
- write_normal_email(ff.output, ff.name, item, mode, mode_MH, &pstfile, save_rtf_body, &extra_mime_headers);
+ write_normal_email(ff.output, ff.name, item, mode, mode_MH, &pstfile, save_rtf_body, 0, &extra_mime_headers);
close_separate_file(&ff);
if (mode_MSG) {
mk_separate_file(&ff, ".msg", 0);
write_msg_email(ff.name, 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, ff.name, item, mode, mode_MH, &pstfile, save_rtf_body, &extra_mime_headers);
+ write_normal_email(ff.output, ff.name, 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 {
if (!ff.type) ff.type = item->type;
if ((ff.type != PST_TYPE_JOURNAL) && (mode != MODE_SEPARATE)) {
ff.skip_count++;
DEBUG_INFO(("I have a journal entry, but the folder type %"PRIi32" isn't a journal folder. Skipping it\n", ff.type));
}
else {
ff.item_count++;
if (mode == MODE_SEPARATE) mk_separate_file(&ff, (mode_EX) ? ".ics" : "", 1);
write_journal(ff.output, item);
fprintf(ff.output, "\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 {
if (!ff.type) ff.type = item->type;
if ((ff.type != PST_TYPE_APPOINTMENT) && (mode != MODE_SEPARATE)) {
ff.skip_count++;
DEBUG_INFO(("I have an appointment, but the folder type %"PRIi32" isn't an appointment folder. Skipping it\n", ff.type));
}
else {
ff.item_count++;
if (mode == MODE_SEPARATE) mk_separate_file(&ff, (mode_EX) ? ".ics" : "", 1);
write_schedule_part_data(ff.output, item, NULL, NULL);
fprintf(ff.output, "\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_INFO(("item with message store content, type %i %s folder type %i, skipping it\n", item->type, item->ascii_type, ff.type));
} else {
ff.skip_count++;
DEBUG_INFO(("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, "<meta[^>]*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, "bC:c:Dd:emhj:kMo:qrSt:uVw"))!= -1) {
switch (c) {
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 '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;
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-M\t- Write emails in the MH (rfc822) format\n");
printf("\t-S\t- Separate. Write emails in the separate format\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 <filename> \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 <integer>\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 <dirname>\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("\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();
}
char *mk_kmail_dir(char *fname) {
//change to that directory
//make a directory based on OUTPUT_KMAIL_DIR_TEMPLATE
//allocate space for OUTPUT_TEMPLATE and form a char* with fname
//return that value
char *dir, *out_name, *index;
int x;
DEBUG_ENT("mk_kmail_dir");
if (kmail_chdir && chdir(kmail_chdir)) {
x = errno;
DIE(("mk_kmail_dir: Cannot change to directory %s: %s\n", kmail_chdir, strerror(x)));
}
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)));
}
}
kmail_chdir = pst_realloc(kmail_chdir, strlen(dir)+1);
strcpy(kmail_chdir, dir);
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);
out_name = pst_malloc(strlen(fname)+strlen(OUTPUT_TEMPLATE)+1);
sprintf(out_name, OUTPUT_TEMPLATE, fname);
DEBUG_RET();
return out_name;
}
int close_kmail_dir() {
// change ..
int x;
DEBUG_ENT("close_kmail_dir");
if (kmail_chdir) { //only free kmail_chdir if not NULL. do not change directory
free(kmail_chdir);
kmail_chdir = NULL;
} else {
if (chdir("..")) {
x = errno;
DIE(("close_kmail_dir: Cannot move up dir (..): %s\n", strerror(x)));
}
}
DEBUG_RET();
return 0;
}
// this will create a directory by that name,
// then make an mbox file inside that directory.
char *mk_recurse_dir(char *dir, int32_t folder_type) {
int x;
char *out_name;
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)));
}
switch (folder_type) {
case PST_TYPE_APPOINTMENT:
out_name = strdup("calendar");
break;
case PST_TYPE_CONTACT:
out_name = strdup("contacts");
break;
case PST_TYPE_JOURNAL:
out_name = strdup("journal");
break;
case PST_TYPE_STICKYNOTE:
case PST_TYPE_TASK:
case PST_TYPE_NOTE:
case PST_TYPE_OTHER:
case PST_TYPE_REPORT:
default:
out_name = strdup("mbox");
break;
}
DEBUG_RET();
return out_name;
}
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;
}
char *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)));
}
}
}
}
#endif
}
// we don't return a filename here cause it isn't necessary.
DEBUG_RET();
return NULL;
}
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, char *extension, int openit) {
DEBUG_ENT("mk_separate_file");
DEBUG_INFO(("opening next file to save email\n"));
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, SEP_MAIL_FILE_TEMPLATE, f->item_count, extension);
check_filename(f->name);
if (openit) {
if (!(f->output = fopen(f->name, "w"))) {
DIE(("mk_separate_file: Cannot open file to save email \"%s\"\n", f->name));
}
}
DEBUG_RET();
}
void close_separate_file(struct file_ll *f) {
DEBUG_ENT("close_separate_file");
if (f->output) {
struct stat st;
fclose(f->output);
stat(f->name, &st);
if (!st.st_size) {
DEBUG_WARN(("removing empty output file %s\n", f->name));
remove(f->name);
}
f->output = 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();
}
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 attachement
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, extra_mime_headers);
+ write_normal_email(f_output, "", item, MODE_NORMAL, 0, pf, save_rtf, 1, extra_mime_headers);
}
pst_freeItem(item);
}
DEBUG_RET();
}
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->filename2.str) {
// use the long filename, converted to proper encoding if needed.
// it is already utf8
pst_rfc2231(&attach->filename2);
fprintf(f_output, "Content-Disposition: attachment; \n filename*=%s\n\n", attach->filename2.str);
}
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 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 ((strncasecmp(header, "X-Barracuda-URL: ", 17) == 0) ||
(strncasecmp(header, "X-ASG-Debug-ID: ", 16) == 0) ||
(strncasecmp(header, "Return-Path: ", 13) == 0) ||
(strncasecmp(header, "Received: ", 10) == 0) ||
(strncasecmp(header, "Subject: ", 9) == 0) ||
(strncasecmp(header, "Date: ", 6) == 0) ||
(strncasecmp(header, "From: ", 6) == 0) ||
(strncasecmp(header, "X-x: ", 5) == 0) ||
(strncasecmp(header, "Microsoft Mail Internet Headers", 31) == 0)) {
return 1;
}
else {
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 = header_get_field(header, field);
if (t) {
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)
{
int b64 = 0;
uint8_t *b = (uint8_t *)body;
DEBUG_ENT("test_base64");
while (*b) {
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");
if (body->is_utf8 && (strcasecmp("utf-8", charset))) {
// 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, strlen(body->str), 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;
}
free(newer);
}
removeCR(body->str);
int base64 = test_base64(body->str);
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");
if (base64) {
char *enc = pst_base64_encode(body->str, strlen(body->str));
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, char** extra_mime_headers)
+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 = "Fri Dec 28 12:06:21 2001";
} else
c_time = "Fri Dec 28 12:06:21 2001";
// 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.
- fprintf(f_output, "From \"%s\" %s\n", sender, c_time);
+ 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"));
attach = (pst_item_attach*) pst_malloc(sizeof(pst_item_attach));
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"));
attach = (pst_item_attach*) pst_malloc(sizeof(pst_item_attach));
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 (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<6; 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)
{
pst_convert_utf8(item, &item->file_as);
f->type = item->type;
f->stored_count = (item->folder) ? item->folder->item_count : 0;
DEBUG_ENT("create_enter_dir");
if (mode == MODE_KMAIL)
f->name = mk_kmail_dir(item->file_as.str);
else if (mode == MODE_RECURSE) {
f->name = mk_recurse_dir(item->file_as.str, f->type);
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.
mk_separate_dir(item->file_as.str);
f->name = (char*) pst_malloc(file_name_len);
memset(f->name, 0, file_name_len);
} else {
f->name = (char*) pst_malloc(strlen(item->file_as.str)+strlen(OUTPUT_TEMPLATE)+1);
sprintf(f->name, OUTPUT_TEMPLATE, item->file_as.str);
}
f->dname = (char*) pst_malloc(strlen(item->file_as.str)+1);
strcpy(f->dname, item->file_as.str);
if (overwrite != 1) {
int x = 0;
char *temp = (char*) pst_malloc (strlen(f->name)+10); //enough room for 10 digits
sprintf(temp, "%s", f->name);
check_filename(temp);
while ((f->output = fopen(temp, "r"))) {
DEBUG_INFO(("need to increase filename because one already exists with that name\n"));
DEBUG_INFO(("- increasing it to %s%d\n", f->name, x));
x++;
sprintf(temp, "%s%08d", f->name, x);
DEBUG_INFO(("- trying \"%s\"\n", f->name));
if (x == 99999999) {
DIE(("create_enter_dir: Why can I not create a folder %s? I have tried %i extensions...\n", f->name, x));
}
fclose(f->output);
}
if (x > 0) { //then the f->name should change
free (f->name);
f->name = temp;
} else {
free(temp);
}
}
DEBUG_INFO(("f->name = %s\nitem->folder_name = %s\n", f->name, item->file_as.str));
if (mode != MODE_SEPARATE) {
check_filename(f->name);
if (!(f->output = fopen(f->name, "w"))) {
DIE(("create_enter_dir: Could not open file \"%s\" for write\n", f->name));
}
}
DEBUG_RET();
}
void close_enter_dir(struct file_ll *f)
{
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();
}
if (f->output) {
if (mode == MODE_SEPARATE) DEBUG_WARN(("close_enter_dir finds open separate file\n"));
struct stat st;
fclose(f->output);
stat(f->name, &st);
if (!st.st_size) {
DEBUG_WARN(("removing empty output file %s\n", f->name));
remove(f->name);
}
f->output = NULL;
}
free(f->name);
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();
}

File Metadata

Mime Type
text/x-diff
Expires
Fri, Apr 24, 10:26 AM (1 d, 6 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18896508
Default Alt Text
(160 KB)

Event Timeline