diff --git a/src/define.h b/src/define.h index df6fcc3..8d1e109 100644 --- a/src/define.h +++ b/src/define.h @@ -1,261 +1,290 @@ /*** * define.h * Part of the LibPST project * Written by David Smith * dave.s@earthcorp.com */ #ifndef DEFINEH_H #define DEFINEH_H #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "libpst.h" #include "timeconv.h" #include "libstrfunc.h" #include "vbuf.h" #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_CTYPE_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_WCHAR_H #include #endif #ifdef HAVE_SIGNAL_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_ICONV #include #endif #ifdef HAVE_REGEX_H #include #endif #ifdef HAVE_GD_H #include #endif #define PERM_DIRS 0777 #ifdef _WIN32 #include + #ifdef __cplusplus + extern "C" { + #endif + #define D_MKDIR(x) mkdir(x) #define chdir _chdir #define strcasecmp _stricmp #define vsnprintf _vsnprintf #define snprintf _snprintf #ifdef _MSC_VER #define ftello _ftelli64 #define fseeko _fseeki64 #elif defined (__MINGW32__) #define ftello ftello64 #define fseeko fseeko64 #else #error Only MSC and mingw supported for Windows #endif #ifndef UINT64_MAX #define UINT64_MAX ((uint64_t)0xffffffffffffffff) #endif #ifndef PRIx64 #define PRIx64 "I64x" #endif int __cdecl _fseeki64(FILE *, __int64, int); __int64 __cdecl _ftelli64(FILE *); + #ifdef __cplusplus + } + #endif + #ifdef __MINGW32__ #include #else + #ifdef __cplusplus + extern "C" { + #endif #include "XGetopt.h" + #ifdef __cplusplus + } + #endif #endif #include #undef gmtime_r #define gmtime_r(tp,tmp) (gmtime(tp)?(*(tmp)=*gmtime(tp),(tmp)):0) #define ctime_r(tp,tmp) (ctime(tp)?(strcpy((tmp),ctime((tp))),(tmp)):0) #else #ifdef __DJGPP__ #define gmtime_r(tp,tmp) (gmtime(tp)?(*(tmp)=*gmtime(tp),(tmp)):0) #define ctime_r(tp,tmp) (ctime(tp)?(strcpy((tmp),ctime((tp))),(tmp)):0) #define fseeko(stream, offset, whence) fseek(stream, (long)offset, whence) #define ftello ftell #endif #ifdef HAVE_UNISTD_H #include #else + #ifdef __cplusplus + extern "C" { + #endif + #include "XGetopt.h" + + #ifdef __cplusplus + } + #endif #endif #define D_MKDIR(x) mkdir(x, PERM_DIRS) #endif #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_SHM_H #include #endif #ifdef HAVE_SYS_WAIT_H #include #endif #ifdef HAVE_DIRENT_H #include #endif #ifdef HAVE_SEMAPHORE_H #include #endif +#ifdef __cplusplus +extern "C" { +#endif void pst_debug_lock(); void pst_debug_unlock(); void pst_debug_setlevel(int level); void pst_debug_init(const char* fname, void* output_mutex); void pst_debug_func(int level, const char* function); void pst_debug_func_ret(int level); void pst_debug(int level, int line, const char *file, const char *fmt, ...); void pst_debug_hexdump(int level, int line, const char *file, const char* buf, size_t size, int cols, int delta); void pst_debug_hexdumper(FILE* out, const char* buf, size_t size, int cols, int delta); void pst_debug_close(); void* pst_malloc(size_t size); void *pst_realloc(void *ptr, size_t size); #define MESSAGEPRINT1(...) pst_debug(1, __LINE__, __FILE__, __VA_ARGS__) #define MESSAGEPRINT2(...) pst_debug(2, __LINE__, __FILE__, __VA_ARGS__) #define MESSAGEPRINT3(...) pst_debug(3, __LINE__, __FILE__, __VA_ARGS__) #define WARN(x) { \ MESSAGEPRINT3 x; \ pst_debug_lock(); \ printf x; \ fflush(stdout); \ pst_debug_unlock(); \ } #define DIE(x) { \ WARN(x); \ exit(EXIT_FAILURE); \ } #define DEBUG_WARN(x) MESSAGEPRINT3 x #define DEBUG_INFO(x) MESSAGEPRINT2 x #define DEBUG_HEXDUMP(x, s) pst_debug_hexdump(1, __LINE__, __FILE__, (char*)x, s, 0x10, 0) #define DEBUG_HEXDUMPC(x, s, c) pst_debug_hexdump(1, __LINE__, __FILE__, (char*)x, s, c, 0) #define DEBUG_ENT(x) \ { \ pst_debug_func(1, x); \ pst_debug(1, __LINE__, __FILE__, "Entering function\n"); \ } #define DEBUG_RET() \ { \ pst_debug(1, __LINE__, __FILE__, "Leaving function\n"); \ pst_debug_func_ret(1); \ } #define DEBUG_INIT(fname,mutex) {pst_debug_init(fname,mutex);} #define DEBUG_CLOSE() {pst_debug_close();} #define RET_DERROR(res, ret_val, x) if (res) { DIE(x);} #if BYTE_ORDER == BIG_ENDIAN # define LE64_CPU(x) \ x = ((((x) & UINT64_C(0xff00000000000000)) >> 56) | \ (((x) & UINT64_C(0x00ff000000000000)) >> 40) | \ (((x) & UINT64_C(0x0000ff0000000000)) >> 24) | \ (((x) & UINT64_C(0x000000ff00000000)) >> 8 ) | \ (((x) & UINT64_C(0x00000000ff000000)) << 8 ) | \ (((x) & UINT64_C(0x0000000000ff0000)) << 24) | \ (((x) & UINT64_C(0x000000000000ff00)) << 40) | \ (((x) & UINT64_C(0x00000000000000ff)) << 56)); # define LE32_CPU(x) \ x = ((((x) & 0xff000000) >> 24) | \ (((x) & 0x00ff0000) >> 8 ) | \ (((x) & 0x0000ff00) << 8 ) | \ (((x) & 0x000000ff) << 24)); # define LE16_CPU(x) \ x = ((((x) & 0xff00) >> 8) | \ (((x) & 0x00ff) << 8)); #elif BYTE_ORDER == LITTLE_ENDIAN # define LE64_CPU(x) {} # define LE32_CPU(x) {} # define LE16_CPU(x) {} #else # error "Byte order not supported by this library" #endif // BYTE_ORDER #define PST_LE_GET_UINT64(p) \ (uint64_t)((((uint8_t const *)(p))[0] << 0) | \ (((uint8_t const *)(p))[1] << 8) | \ (((uint8_t const *)(p))[2] << 16) | \ (((uint8_t const *)(p))[3] << 24) | \ (((uint8_t const *)(p))[4] << 32) | \ (((uint8_t const *)(p))[5] << 40) | \ (((uint8_t const *)(p))[6] << 48) | \ (((uint8_t const *)(p))[7] << 56)) #define PST_LE_GET_INT64(p) \ (int64_t)((((uint8_t const *)(p))[0] << 0) | \ (((uint8_t const *)(p))[1] << 8) | \ (((uint8_t const *)(p))[2] << 16) | \ (((uint8_t const *)(p))[3] << 24) | \ (((uint8_t const *)(p))[4] << 32) | \ (((uint8_t const *)(p))[5] << 40) | \ (((uint8_t const *)(p))[6] << 48) | \ (((uint8_t const *)(p))[7] << 56)) #define PST_LE_GET_UINT32(p) \ (uint32_t)((((uint8_t const *)(p))[0] << 0) | \ (((uint8_t const *)(p))[1] << 8) | \ (((uint8_t const *)(p))[2] << 16) | \ (((uint8_t const *)(p))[3] << 24)) #define PST_LE_GET_INT32(p) \ (int32_t)((((uint8_t const *)(p))[0] << 0) | \ (((uint8_t const *)(p))[1] << 8) | \ (((uint8_t const *)(p))[2] << 16) | \ (((uint8_t const *)(p))[3] << 24)) #define PST_LE_GET_UINT16(p) \ (uint16_t)((((uint8_t const *)(p))[0] << 0) | \ (((uint8_t const *)(p))[1] << 8)) #define PST_LE_GET_INT16(p) \ (int16_t)((((uint8_t const *)(p))[0] << 0) | \ (((uint8_t const *)(p))[1] << 8)) #define PST_LE_GET_UINT8(p) (*(uint8_t const *)(p)) #define PST_LE_GET_INT8(p) (*(int8_t const *)(p)) #define MAXDATEFMTLEN 40 +#ifdef __cplusplus +} /* extern "C" */ +#endif + #endif //DEFINEH_H diff --git a/src/deltasearch.cpp b/src/deltasearch.cpp index 5f13325..d9b47c9 100644 --- a/src/deltasearch.cpp +++ b/src/deltasearch.cpp @@ -1,68 +1,66 @@ -extern "C" { - #include "define.h" -}; +#include "define.h" #include #include #include using namespace std; unsigned char comp_enc [] = { 0x47, 0xf1, 0xb4, 0xe6, 0x0b, 0x6a, 0x72, 0x48, 0x85, 0x4e, 0x9e, 0xeb, 0xe2, 0xf8, 0x94, 0x53, /*0x0f*/ 0xe0, 0xbb, 0xa0, 0x02, 0xe8, 0x5a, 0x09, 0xab, 0xdb, 0xe3, 0xba, 0xc6, 0x7c, 0xc3, 0x10, 0xdd, /*0x1f*/ 0x39, 0x05, 0x96, 0x30, 0xf5, 0x37, 0x60, 0x82, 0x8c, 0xc9, 0x13, 0x4a, 0x6b, 0x1d, 0xf3, 0xfb, /*0x2f*/ 0x8f, 0x26, 0x97, 0xca, 0x91, 0x17, 0x01, 0xc4, 0x32, 0x2d, 0x6e, 0x31, 0x95, 0xff, 0xd9, 0x23, /*0x3f*/ 0xd1, 0x00, 0x5e, 0x79, 0xdc, 0x44, 0x3b, 0x1a, 0x28, 0xc5, 0x61, 0x57, 0x20, 0x90, 0x3d, 0x83, /*0x4f*/ 0xb9, 0x43, 0xbe, 0x67, 0xd2, 0x46, 0x42, 0x76, 0xc0, 0x6d, 0x5b, 0x7e, 0xb2, 0x0f, 0x16, 0x29, /*0x5f*/ 0x3c, 0xa9, 0x03, 0x54, 0x0d, 0xda, 0x5d, 0xdf, 0xf6, 0xb7, 0xc7, 0x62, 0xcd, 0x8d, 0x06, 0xd3, /*0x6f*/ 0x69, 0x5c, 0x86, 0xd6, 0x14, 0xf7, 0xa5, 0x66, 0x75, 0xac, 0xb1, 0xe9, 0x45, 0x21, 0x70, 0x0c, /*0x7f*/ 0x87, 0x9f, 0x74, 0xa4, 0x22, 0x4c, 0x6f, 0xbf, 0x1f, 0x56, 0xaa, 0x2e, 0xb3, 0x78, 0x33, 0x50, /*0x8f*/ 0xb0, 0xa3, 0x92, 0xbc, 0xcf, 0x19, 0x1c, 0xa7, 0x63, 0xcb, 0x1e, 0x4d, 0x3e, 0x4b, 0x1b, 0x9b, /*0x9f*/ 0x4f, 0xe7, 0xf0, 0xee, 0xad, 0x3a, 0xb5, 0x59, 0x04, 0xea, 0x40, 0x55, 0x25, 0x51, 0xe5, 0x7a, /*0xaf*/ 0x89, 0x38, 0x68, 0x52, 0x7b, 0xfc, 0x27, 0xae, 0xd7, 0xbd, 0xfa, 0x07, 0xf4, 0xcc, 0x8e, 0x5f, /*0xbf*/ 0xef, 0x35, 0x9c, 0x84, 0x2b, 0x15, 0xd5, 0x77, 0x34, 0x49, 0xb6, 0x12, 0x0a, 0x7f, 0x71, 0x88, /*0xcf*/ 0xfd, 0x9d, 0x18, 0x41, 0x7d, 0x93, 0xd8, 0x58, 0x2c, 0xce, 0xfe, 0x24, 0xaf, 0xde, 0xb8, 0x36, /*0xdf*/ 0xc8, 0xa1, 0x80, 0xa6, 0x99, 0x98, 0xa8, 0x2f, 0x0e, 0x81, 0x65, 0x73, 0xe4, 0xc2, 0xa2, 0x8a, /*0xef*/ 0xd4, 0xe1, 0x11, 0xd0, 0x08, 0x8b, 0x2a, 0xf2, 0xed, 0x9a, 0x64, 0x3f, 0xc1, 0x6c, 0xf9, 0xec}; /*0xff*/ int main(int argc, char* const* argv) { if (argc < 4) { printf("usage: %s filename.pst integer-delta search-string\n", argv[0]); return 0; } int fd = open(argv[1], O_RDONLY); int d = atoi(argv[2]); string search(argv[3]); printf("using file %s with delta %d looking for %s\n", argv[1], d, argv[3]); if (fd) { struct stat st; fstat(fd, &st); off_t size = st.st_size; vector buf(size); size_t s = read(fd, &buf[0], size); pst_debug_hexdumper(stdout, &buf[0], s, 16, 0); printf("\n\n dump decrypted data \n"); for (off_t i=0; i #include #include #include #include #include #include #include #include #include using namespace std; struct property { uint32_t tag; uint32_t flags; uint32_t length; // or value uint32_t reserved; }; typedef list property_list; /** Convert str to an 8 bit charset if it is utf8, null strings are preserved. * * @param str reference to the mapi string of interest * @param charset pointer to the 8 bit charset to use */ static void convert_8bit(pst_string &str, const char *charset); static void convert_8bit(pst_string &str, const char *charset) { if (!str.str) return; // null if (!str.is_utf8) return; // not utf8 DEBUG_ENT("convert_8bit"); pst_vbuf *newer = pst_vballoc(2); size_t strsize = strlen(str.str); size_t rc = pst_vb_utf8to8bit(newer, str.str, strsize, charset); if (rc == (size_t)-1) { // unable to convert, change the charset to utf8 free(newer->b); DEBUG_INFO(("Failed to convert utf-8 to %s\n", charset)); DEBUG_HEXDUMPC(str.str, strsize, 0x10); } else { // null terminate the output string pst_vbgrow(newer, 1); newer->b[newer->dlen] = '\0'; free(str.str); str.str = newer->b; } free(newer); DEBUG_RET(); } static void empty_property(GsfOutfile *out, uint32_t tag); static void empty_property(GsfOutfile *out, uint32_t tag) { vector n(50); snprintf(&n[0], n.size(), "__substg1.0_%08X", tag); GsfOutput* dst = gsf_outfile_new_child(out, &n[0], false); gsf_output_close(dst); g_object_unref(G_OBJECT(dst)); } static void string_property(GsfOutfile *out, property_list &prop, uint32_t tag, const char *contents, size_t size); static void string_property(GsfOutfile *out, property_list &prop, uint32_t tag, const char *contents, size_t size) { if (!contents) return; size_t term = ((tag & 0x0000ffff) == 0x001e) ? 1 : ((tag & 0x0000ffff) == 0x001f) ? 2 : 0; // null terminator vector n(50); snprintf(&n[0], n.size(), "__substg1.0_%08X", tag); GsfOutput* dst = gsf_outfile_new_child(out, &n[0], false); gsf_output_write(dst, size, (const guint8*)contents); if (term) { memset(&n[0], 0, term); gsf_output_write(dst, term, (const guint8*)&n[0]); size += term; } gsf_output_close(dst); g_object_unref(G_OBJECT(dst)); property p; p.tag = tag; p.flags = 0x6; // make all the properties writable p.length = size; p.reserved = 0; prop.push_back(p); } static void string_property(GsfOutfile *out, property_list &prop, uint32_t tag, FILE *fp); static void string_property(GsfOutfile *out, property_list &prop, uint32_t tag, FILE *fp) { vector n(50); snprintf(&n[0], n.size(), "__substg1.0_%08X", tag); GsfOutput* dst = gsf_outfile_new_child(out, &n[0], false); size_t size = 0; const size_t bsize = 10000; char buf[bsize]; while (1) { size_t s = fread(buf, 1, bsize, fp); if (!s) break; gsf_output_write(dst, s, (const guint8*)buf); } gsf_output_close(dst); g_object_unref(G_OBJECT(dst)); property p; p.tag = tag; p.flags = 0x6; // make all the properties writable p.length = size; p.reserved = 0; prop.push_back(p); } static void string_property(GsfOutfile *out, property_list &prop, uint32_t tag, const char* charset, pst_string &contents); static void string_property(GsfOutfile *out, property_list &prop, uint32_t tag, const char* charset, pst_string &contents) { if (contents.str) { convert_8bit(contents, charset); string_property(out, prop, tag, contents.str, strlen(contents.str)); } } static void strin0_property(GsfOutfile *out, property_list &prop, uint32_t tag, const char* charset, pst_string &contents); static void strin0_property(GsfOutfile *out, property_list &prop, uint32_t tag, const char* charset, pst_string &contents) { if (contents.str) { convert_8bit(contents, charset); string_property(out, prop, tag, contents.str, strlen(contents.str)+1); } } static void string_property(GsfOutfile *out, property_list &prop, uint32_t tag, const string &contents); static void string_property(GsfOutfile *out, property_list &prop, uint32_t tag, const string &contents) { string_property(out, prop, tag, contents.c_str(), contents.size()); } static void string_property(GsfOutfile *out, property_list &prop, uint32_t tag, pst_binary &contents); static void string_property(GsfOutfile *out, property_list &prop, uint32_t tag, pst_binary &contents) { if (contents.size) string_property(out, prop, tag, contents.data, contents.size); } static void write_properties(GsfOutfile *out, property_list &prop, const guint8* header, size_t hlen); static void write_properties(GsfOutfile *out, property_list &prop, const guint8* header, size_t hlen) { GsfOutput* dst = gsf_outfile_new_child(out, "__properties_version1.0", false); gsf_output_write(dst, hlen, header); for (property_list::iterator i=prop.begin(); i!=prop.end(); i++) { property &p = *i; gsf_output_write(dst, sizeof(property), (const guint8*)&p); } gsf_output_close(dst); g_object_unref(G_OBJECT(dst)); } static void int_property(property_list &prop_list, uint32_t tag, uint32_t flags, uint32_t value); static void int_property(property_list &prop_list, uint32_t tag, uint32_t flags, uint32_t value) { property p; p.tag = tag; p.flags = flags; p.length = value; p.reserved = 0; prop_list.push_back(p); } static void i64_property(property_list &prop_list, uint32_t tag, uint32_t flags, FILETIME *value); static void i64_property(property_list &prop_list, uint32_t tag, uint32_t flags, FILETIME *value) { if (value) { property p; p.tag = tag; p.flags = flags; p.length = value->dwLowDateTime; p.reserved = value->dwHighDateTime; prop_list.push_back(p); } } static void nzi_property(property_list &prop_list, uint32_t tag, uint32_t flags, uint32_t value); static void nzi_property(property_list &prop_list, uint32_t tag, uint32_t flags, uint32_t value) { if (value) int_property(prop_list, tag, flags, value); } void write_msg_email(char *fname, pst_item* item, pst_file* pst) { // this is not an email item if (!item->email) return; DEBUG_ENT("write_msg_email"); pst_item_email &email = *(item->email); char charset[30]; const char* body_charset = pst_default_charset(item, sizeof(charset), charset); DEBUG_INFO(("%s body charset seems to be %s\n", fname, body_charset)); body_charset = "iso-8859-1//TRANSLIT//IGNORE"; gsf_init(); GsfOutfile *outfile; GsfOutput *output; GError *err = NULL; output = gsf_output_stdio_new(fname, &err); if (output == NULL) { gsf_shutdown(); DEBUG_INFO(("unable to open output .msg file %s\n", fname)); DEBUG_RET(); return; } struct top_property_header { uint32_t reserved1; uint32_t reserved2; uint32_t next_recipient; // same as recipient count uint32_t next_attachment; // same as attachment count uint32_t recipient_count; uint32_t attachment_count; uint32_t reserved3; uint32_t reserved4; }; top_property_header top_head; memset(&top_head, 0, sizeof(top_head)); outfile = gsf_outfile_msole_new(output); g_object_unref(G_OBJECT(output)); output = GSF_OUTPUT(outfile); property_list prop_list; int_property(prop_list, 0x00170003, 0x6, email.importance); nzi_property(prop_list, 0x0023000B, 0x6, email.delivery_report); nzi_property(prop_list, 0x00260003, 0x6, email.priority); nzi_property(prop_list, 0x0029000B, 0x6, email.read_receipt); nzi_property(prop_list, 0x002E0003, 0x6, email.original_sensitivity); nzi_property(prop_list, 0x00360003, 0x6, email.sensitivity); nzi_property(prop_list, 0x0C17000B, 0x6, email.reply_requested); nzi_property(prop_list, 0x0E01000B, 0x6, email.delete_after_submit); int_property(prop_list, 0x0E070003, 0x6, item->flags); i64_property(prop_list, 0x00390040, 0x6, email.sent_date); GsfOutfile *out = GSF_OUTFILE (output); string_property(out, prop_list, 0x001A001E, item->ascii_type); string_property(out, prop_list, 0x0037001E, body_charset, item->subject); strin0_property(out, prop_list, 0x003B0102, body_charset, email.outlook_sender); string_property(out, prop_list, 0x003D001E, string("")); string_property(out, prop_list, 0x0040001E, body_charset, email.outlook_received_name1); string_property(out, prop_list, 0x0042001E, body_charset, email.outlook_sender_name); string_property(out, prop_list, 0x0044001E, body_charset, email.outlook_recipient_name); string_property(out, prop_list, 0x0050001E, body_charset, email.reply_to); strin0_property(out, prop_list, 0x00510102, body_charset, email.outlook_recipient); strin0_property(out, prop_list, 0x00520102, body_charset, email.outlook_recipient2); string_property(out, prop_list, 0x0064001E, body_charset, email.sender_access); string_property(out, prop_list, 0x0065001E, body_charset, email.sender_address); string_property(out, prop_list, 0x0070001E, body_charset, email.processed_subject); string_property(out, prop_list, 0x00710102, email.conversation_index); string_property(out, prop_list, 0x0072001E, body_charset, email.original_bcc); string_property(out, prop_list, 0x0073001E, body_charset, email.original_cc); string_property(out, prop_list, 0x0074001E, body_charset, email.original_to); string_property(out, prop_list, 0x0075001E, body_charset, email.recip_access); string_property(out, prop_list, 0x0076001E, body_charset, email.recip_address); string_property(out, prop_list, 0x0077001E, body_charset, email.recip2_access); string_property(out, prop_list, 0x0078001E, body_charset, email.recip2_address); string_property(out, prop_list, 0x007D001E, body_charset, email.header); string_property(out, prop_list, 0x0C1A001E, body_charset, email.outlook_sender_name2); strin0_property(out, prop_list, 0x0C1D0102, body_charset, email.outlook_sender2); string_property(out, prop_list, 0x0C1E001E, body_charset, email.sender2_access); string_property(out, prop_list, 0x0C1F001E, body_charset, email.sender2_address); string_property(out, prop_list, 0x0E02001E, body_charset, email.bcc_address); string_property(out, prop_list, 0x0E03001E, body_charset, email.cc_address); string_property(out, prop_list, 0x0E04001E, body_charset, email.sentto_address); string_property(out, prop_list, 0x0E1D001E, body_charset, email.outlook_normalized_subject); string_property(out, prop_list, 0x1000001E, body_charset, item->body); string_property(out, prop_list, 0x1013001E, body_charset, email.htmlbody); string_property(out, prop_list, 0x1035001E, body_charset, email.messageid); string_property(out, prop_list, 0x1042001E, body_charset, email.in_reply_to); string_property(out, prop_list, 0x1046001E, body_charset, email.return_path_address); // any property over 0x8000 needs entries in the __nameid to make them // either string named or numerical named properties. { vector n(50); { snprintf(&n[0], n.size(), "__recip_version1.0_#%08X", top_head.recipient_count); GsfOutput *output = gsf_outfile_new_child(out, &n[0], true); { int v = 1; // to property_list prop_list; int_property(prop_list, 0x0C150003, 0x6, v); // PidTagRecipientType int_property(prop_list, 0x30000003, 0x6, top_head.recipient_count); // PR_ROWID GsfOutfile *out = GSF_OUTFILE (output); string_property(out, prop_list, 0x3001001E, body_charset, item->file_as); if (item->contact) { string_property(out, prop_list, 0x3002001E, body_charset, item->contact->address1_transport); string_property(out, prop_list, 0x3003001E, body_charset, item->contact->address1); string_property(out, prop_list, 0x5ff6001E, body_charset, item->contact->address1); } strin0_property(out, prop_list, 0x300B0102, body_charset, email.outlook_search_key); write_properties(out, prop_list, (const guint8*)&top_head, 8); // convenient 8 bytes of reserved zeros gsf_output_close(output); g_object_unref(G_OBJECT(output)); top_head.next_recipient++; top_head.recipient_count++; } } if (email.cc_address.str) { snprintf(&n[0], n.size(), "__recip_version1.0_#%08X", top_head.recipient_count); GsfOutput *output = gsf_outfile_new_child(out, &n[0], true); { int v = 2; // cc property_list prop_list; int_property(prop_list, 0x0C150003, 0x6, v); // PidTagRecipientType int_property(prop_list, 0x30000003, 0x6, top_head.recipient_count); // PR_ROWID GsfOutfile *out = GSF_OUTFILE (output); string_property(out, prop_list, 0x3001001E, body_charset, email.cc_address); string_property(out, prop_list, 0x3003001E, body_charset, email.cc_address); string_property(out, prop_list, 0x5ff6001E, body_charset, email.cc_address); write_properties(out, prop_list, (const guint8*)&top_head, 8); // convenient 8 bytes of reserved zeros gsf_output_close(output); g_object_unref(G_OBJECT(output)); top_head.next_recipient++; top_head.recipient_count++; } } if (email.bcc_address.str) { snprintf(&n[0], n.size(), "__recip_version1.0_#%08X", top_head.recipient_count); GsfOutput *output = gsf_outfile_new_child(out, &n[0], true); { int v = 3; // bcc property_list prop_list; int_property(prop_list, 0x0C150003, 0x6, v); // PidTagRecipientType int_property(prop_list, 0x30000003, 0x6, top_head.recipient_count); // PR_ROWID GsfOutfile *out = GSF_OUTFILE (output); string_property(out, prop_list, 0x3001001E, body_charset, email.bcc_address); string_property(out, prop_list, 0x3003001E, body_charset, email.bcc_address); string_property(out, prop_list, 0x5ff6001E, body_charset, email.bcc_address); write_properties(out, prop_list, (const guint8*)&top_head, 8); // convenient 8 bytes of reserved zeros gsf_output_close(output); g_object_unref(G_OBJECT(output)); top_head.next_recipient++; top_head.recipient_count++; } } } pst_item_attach *a = item->attach; while (a) { if (a->method == PST_ATTACH_EMBEDDED) { // not implemented yet } else if (a->data.data || a->i_id) { vector n(50); snprintf(&n[0], n.size(), "__attach_version1.0_#%08X", top_head.attachment_count); GsfOutput *output = gsf_outfile_new_child(out, &n[0], true); { FILE *fp = fopen("temp_file_attachment", "w+b"); if (fp) { pst_attach_to_file(pst, a, fp); // data is now in the file fseek(fp, 0, SEEK_SET); property_list prop_list; int_property(prop_list, 0x0E210003, 0x2, top_head.attachment_count); // MAPI_ATTACH_NUM int_property(prop_list, 0x0FF40003, 0x2, 2); // PR_ACCESS read int_property(prop_list, 0x0FF70003, 0x2, 0); // PR_ACCESS_LEVEL read only int_property(prop_list, 0x0FFE0003, 0x2, 7); // PR_OBJECT_TYPE attachment int_property(prop_list, 0x37050003, 0x7, 1); // PR_ATTACH_METHOD by value int_property(prop_list, 0x370B0003, 0x7, a->position); // PR_RENDERING_POSITION int_property(prop_list, 0x37100003, 0x6, a->sequence); // PR_ATTACH_MIME_SEQUENCE GsfOutfile *out = GSF_OUTFILE (output); string_property(out, prop_list, 0x0FF90102, item->record_key); string_property(out, prop_list, 0x37010102, fp); if (a->filename2.str) { // have long file name string_property(out, prop_list, 0x3707001E, body_charset, a->filename2); } else if (a->filename1.str) { // have short file name string_property(out, prop_list, 0x3704001E, body_charset, a->filename1); } else { // make up a name const char *n = "inline"; string_property(out, prop_list, 0x3704001E, n, strlen(n)); } string_property(out, prop_list, 0x370E001E, body_charset, a->mimetype); write_properties(out, prop_list, (const guint8*)&top_head, 8); // convenient 8 bytes of reserved zeros gsf_output_close(output); g_object_unref(G_OBJECT(output)); top_head.next_attachment++; top_head.attachment_count++; fclose(fp); } } } a = a->next; } write_properties(out, prop_list, (const guint8*)&top_head, sizeof(top_head)); { GsfOutput *output = gsf_outfile_new_child(out, "__nameid_version1.0", true); { GsfOutfile *out = GSF_OUTFILE (output); empty_property(out, 0x00020102); empty_property(out, 0x00030102); empty_property(out, 0x00040102); gsf_output_close(output); g_object_unref(G_OBJECT(output)); } } gsf_output_close(output); g_object_unref(G_OBJECT(output)); gsf_shutdown(); DEBUG_RET(); } diff --git a/src/msg.h b/src/msg.h index fa3cd72..7899829 100644 --- a/src/msg.h +++ b/src/msg.h @@ -1,2 +1,10 @@ +#ifdef __cplusplus +extern "C" { +#endif + void write_msg_email(char *fname, pst_item* item, pst_file* pst); + +#ifdef __cplusplus +} +#endif diff --git a/src/nick2ldif.cpp b/src/nick2ldif.cpp index 690f7b7..adadc4d 100644 --- a/src/nick2ldif.cpp +++ b/src/nick2ldif.cpp @@ -1,84 +1,82 @@ /* Copyright (c) 2004 Carl Byington - 510 Software Group, released under the GPL version 2 or any later version at your choice available at https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt */ #include -extern "C" { - #include "define.h" -} +#include "define.h" char *ldap_base = NULL; char *ldap_org = NULL; char *ldap_class = NULL; using namespace std; int main(int argc, char* const* argv) { char c; char *temp; while ((c = getopt(argc, argv, "b:c:"))!= -1) { switch (c) { case 'b': ldap_base = optarg; temp = strchr(ldap_base, ','); if (temp) { *temp = '\0'; ldap_org = strdup(ldap_base); *temp = ','; } break; case 'c': ldap_class = optarg; break; default: break; } } const int LINE_SIZE = 2000; char line[LINE_SIZE]; while (!cin.eof()) { cin.getline(line, LINE_SIZE); int n = strlen(line); if (!n) continue; if (strncmp(line, "alias", 5) != 0) continue; // not alias char *f = line + 6; // skip alias keyword char *e; if (*f == '"') { f++; e = strchr(f, '"'); } else { e = strchr(f, ' '); } if (!e) continue; *e = '\0'; char *m = e+1; while (*m == ' ') m++; if (*m != '\0') { char cn[1000], givenName[1000], sn[1000]; snprintf(cn, sizeof(cn), "%s", f); char *ff = strchr(f, ' '); if (ff) { strncpy(givenName, ff+1, sizeof(givenName)-1); *ff = '\0'; strncpy(sn, f, sizeof(sn)-1); } else { strcpy(givenName, cn); strcpy(sn, cn); } printf("dn: cn=%s, %s\n", cn, ldap_base); printf("cn: %s\n", cn); printf("givenName: %s\n", givenName); printf("sn: %s\n", sn); printf("mail: %s\n", m); printf("objectClass: %s\n\n", ldap_class); } } } diff --git a/src/pst2dii.cpp.in b/src/pst2dii.cpp.in index 0b2dcf5..f83cb0e 100644 --- a/src/pst2dii.cpp.in +++ b/src/pst2dii.cpp.in @@ -1,730 +1,728 @@ /* Copyright (c) 2008 Carl Byington - 510 Software Group, released under the GPL version 2 or any later version at your choice available at https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt Based on readpst.c by David Smith */ #include #include #include using namespace std; -extern "C" { - #include "define.h" - #include "lzfu.h" -} +#include "define.h" +#include "lzfu.h" struct file_ll { string name; int32_t stored_count; int32_t email_count; int32_t skip_count; int32_t type; file_ll() { stored_count = 0; email_count = 0; skip_count = 0; type = 0; }; }; // global settings const char* convert = "@CONVERT@"; // fully qualified path of the convert program from image magick const char* prog_name = NULL; // our arg0 name const char* bates_prefix = ""; // string to prefix bates numbers int bates_index = 0; // current bates sequence const char* output_directory = "."; const char* output_file = "load.dii"; char* font_file = NULL; int bates_color = 0xff0000; // color of bates header stamp int email_sequence = 0; // current pdf sequence number char* pdf_name = NULL; // current pdf file name FILE* dii_file = NULL; // the output dii load file pst_file pstfile; // the input pst file // pdf writer globals bool pdf_open = false; // is pdf writer started char* pst_folder; // current folder name int page_sequence; // current page number string conversion; // conversion command vector png_names; // png writer globals bool png_open = false; // is current page open int line_height; // in pixels int char_width; // in pixels int col_number, col_max; // in characters int line_number, line_max; // lines per page int x_position, y_position; // in pixels int black, red; // text colors gdImagePtr image; // current gd image const int DPI = 300; const double sz = 10.0; const int margin = DPI/2; const int LINE_SIZE = 2000; const int PAGE_WIDTH = DPI*17/2; const int PAGE_HEIGHT = DPI*11; // max size of the c_time char*. It will store the date of the email #define C_TIME_SIZE 500 static void open_png(); static void close_png(); static void version(); static void version() { printf("pst2dii 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 } static void usage(); static void usage() { version(); printf("Usage: %s -f ttf-font-file [OPTIONS] {PST FILENAME}\n", prog_name); printf("\t-f ttf-font-file \t- Set the font file\n"); printf("OPTIONS:\n"); printf("\t-B bates-prefix \t- Set the bates prefix string\n"); printf("\t-O dii-output-file\t- Set the dii load file output filename\n"); printf("\t-V \t- Version. Display program version\n"); printf("\t-b bates-number \t- Set the starting bates sequence number\n"); printf("\t-c bates-color \t- Specify the color of the bates stamps as 6 digit hex\n"); printf("\t-d filename \t- Debug to file.\n"); printf("\t-h \t- Help. This screen\n"); printf("\t-o dirname \t- Output directory to write files to.\n"); } static char *removeCR (char *c); static char *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(); return c; } // The sole purpose of this function is to bypass the pseudo-header prologue // that Microsoft Outlook inserts at the beginning of the internet email // headers for emails stored in their "Personal Folders" files. static char *skip_header_prologue(char *headers); static char *skip_header_prologue(char *headers) { const char *bad = "Microsoft Mail Internet Headers"; if (strncmp(headers, bad, strlen(bad)) == 0) { // Found the offensive header prologue char *pc = strchr(headers, '\n'); return pc + 1; } return headers; } static void check_filename(string &fname); static void check_filename(string &fname) { char *t = strdup(fname.c_str()); DEBUG_ENT("check_filename"); if (!t) { DEBUG_RET(); return; } char *tt = t; bool fixed = false; while ((t = strpbrk(t, " /\\:"))) { // while there are characters in the second string that we don't want *t = '_'; //replace them with an underscore fixed = true; } if (fixed) fname = string(tt); free(tt); DEBUG_RET(); } static string write_separate_attachment(string fname, pst_item_attach* current_attach, int attach_num, pst_file* pst); static string write_separate_attachment(string fname, pst_item_attach* current_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 = (current_attach->filename2.str) ? current_attach->filename2.str : current_attach->filename1.str; DEBUG_ENT("write_separate_attachment"); check_filename(fname); const char* f_name = fname.c_str(); DEBUG_INFO(("dirname=%s, pathname=%s, filename=%s\n", output_directory, f_name, attach_filename)); int len = strlen(output_directory) + 1 + strlen(f_name) + 15; if (!attach_filename) { // generate our own (dummy) filename for the attachment temp = (char*)pst_malloc(len); sprintf(temp, "%s/%s_attach%i", output_directory, f_name, attach_num); } else { // have an attachment name, make sure it's unique temp = (char*)pst_malloc(len+strlen(attach_filename)); do { if (fp) fclose(fp); if (x == 0) sprintf(temp, "%s/%s_%s", output_directory, f_name, attach_filename); else sprintf(temp, "%s/%s_%s-%i", output_directory, 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, "wb"))) { DEBUG_WARN(("write_separate_attachment: Cannot open attachment save file \"%s\"\n", temp)); } else { (void)pst_attach_to_file(pst, current_attach, fp); fclose(fp); } string rc(temp); if (temp) free(temp); DEBUG_RET(); return rc; } static void print_pdf_short(const char *line, int len, int color); static void print_pdf_short(const char *line, int len, int color) { if (line_number >= line_max) { close_png(); open_png(); } int brect[8]; gdFTStringExtra strex; strex.flags = gdFTEX_RESOLUTION; strex.linespacing = 1.20; strex.charmap = 0; strex.hdpi = DPI; strex.vdpi = DPI; char xline[len+1]; memcpy(xline, line, len); xline[len] = '\0'; char *p; char *l = xline; while ((p = strchr(l, '&'))) { *p = '\0'; char *err = gdImageStringFTEx(image, &brect[0], color, font_file, sz, 0.0, x_position, y_position, l, &strex); if (err) printf("%s", err); x_position += (brect[2]-brect[6]); l = p+1; err = gdImageStringFTEx(image, &brect[0], color, font_file, sz, 0.0, x_position, y_position, (char*)"&", &strex); if (err) printf("%s", err); x_position += (brect[2]-brect[6]); } char *err = gdImageStringFTEx(image, &brect[0], color, font_file, sz, 0.0, x_position, y_position, l, &strex); if (err) printf("%s", err); x_position += (brect[2]-brect[6]); col_number += len; } static void new_line(); static void new_line() { y_position += line_height; line_number += 1; x_position = margin; col_number = 0; } static void print_pdf_single(const char *line, int color); static void print_pdf_single(const char *line, int color) { while (*line == '\t') { char blanks[5]; memset(blanks, ' ', 5); print_pdf_short(blanks, 4, color); line++; if (col_number >= col_max) new_line(); } int n = strlen(line); while (n) { int m = col_max - col_number; // number of chars that will fit on this line m = (n > m) ? m : n; print_pdf_short(line, m, color); line += m; n -= m; if (n) new_line(); } } static void print_pdf_only(char *line, int color); static void print_pdf_only(char *line, int color) { char *p; while ((p = strchr(line, '\n'))) { *p = '\0'; print_pdf_single(line, color); *p = '\n'; line = p+1; new_line(); } print_pdf_single(line, color); } static void print_pdf(char *line); static void print_pdf(char *line) { pst_fwrite(line, 1, strlen(line), dii_file); print_pdf_only(line, black); } static void open_png() { if (!png_open) { png_open = true; int brect[8]; image = gdImageCreate(PAGE_WIDTH, PAGE_HEIGHT); gdImageColorAllocate(image, 255, 255, 255); // background color first one allocated black = gdImageColorAllocate(image, 0, 0, 0); int r = (bates_color & 0xff0000) >> 16; int g = (bates_color & 0x00ff00) >> 8; int b = (bates_color & 0x0000ff); red = gdImageColorAllocate(image, r, g, b); gdFTStringExtra strex; strex.flags = gdFTEX_RESOLUTION; strex.linespacing = 1.20; strex.charmap = 0; strex.hdpi = DPI; strex.vdpi = DPI; char line[LINE_SIZE]; char *err = gdImageStringFTEx(NULL, &brect[0], black, font_file, sz, 0.0, margin, margin, (char*)"LMgqQ", &strex); if (err) printf("%s", err); line_height = (brect[3]-brect[7]) * 12/10; char_width = (brect[2]-brect[6]) / 5; col_number = 0; col_max = (PAGE_WIDTH - margin*2) / char_width; line_number = 0; line_max = (PAGE_HEIGHT - margin*2) / line_height; x_position = margin; y_position = margin + line_height; snprintf(line, sizeof(line), "%s%06d\n", bates_prefix, bates_index++); print_pdf_only(line, red); print_pdf_only(pst_folder, red); } } static void close_png() { if (png_open) { png_open = false; int len = 4 + 11 + 4 +1; char *fn = (char*)pst_malloc(len); snprintf(fn, len, "page%d.png", ++page_sequence); FILE *pngout = fopen(fn, "wb"); if (pngout) { gdImagePng(image, pngout); fclose(pngout); } gdImageDestroy(image); // free memory png_names.push_back(fn); conversion += string(" ") + fn; free(fn); } } static void open_pdf(char *line); static void open_pdf(char *line) { pst_folder = line; page_sequence = 0; conversion = string(convert); png_names.clear(); open_png(); /* Note; allocating the largest string length to pdf_name */ int len = strlen(output_directory) + 4 + 6 + 4 + 1; pdf_name = (char*)pst_malloc(len); snprintf(pdf_name, 3 + 6 + 1, "dii%06d", ++email_sequence); fprintf(dii_file, "\n@T %s\n", pdf_name); snprintf(pdf_name, len, "%s/dii%06d.pdf", output_directory, email_sequence); } static void close_pdf(); static void close_pdf() { close_png(); conversion += string(" ") + pdf_name; (void)system(conversion.c_str()); for (vector::iterator i=png_names.begin(); i!=png_names.end(); i++) { remove((*i).c_str()); } fprintf(dii_file, "@D %s\n", pdf_name); free(pdf_name); } static void write_simple(const char *tag, const char *value); static void write_simple(const char *tag, const char *value) { if (value) fprintf(dii_file, "@%s %s\n", tag, value); } static void write_simple(const char *tag, string value); static void write_simple(const char *tag, string value) { fprintf(dii_file, "@%s %s\n", tag, value.c_str()); } static void write_simple(const char *tag, const char *value, const char *value2); static void write_simple(const char *tag, const char *value, const char *value2) { if (value) { if (value2) fprintf(dii_file, "@%s \"%s\" <%s>\n", tag, value, value2); else fprintf(dii_file, "@%s \"%s\"\n", tag, value); } } static string extract_header(char *headers, const char *field); static string extract_header(char *headers, const char *field) { string rc; int len = strlen(field) + 4; char f[len]; snprintf(f, len, "\n%s: ", field); char *p = strstr(headers, f); if (p) { p += strlen(f); char *n = strchr(p, '\n'); if (n) { *n = '\0'; rc = string(p); *n = '\n'; } else { rc = string(p); } } return rc; } static void write_normal_email(file_ll &f, pst_item* item, pst_file* pst); static void write_normal_email(file_ll &f, pst_item* item, pst_file* pst) { DEBUG_ENT("write_normal_email"); char *soh = NULL; // real start of headers. if (item->email->header.str) { // some of the headers we get from the file are not properly defined. // they can contain some email stuff too. We will cut off the header // when we see a \n\n or \r\n\r\n removeCR(item->email->header.str); char *temp = strstr(item->email->header.str, "\n\n"); if (temp) { DEBUG_INFO(("Found body text in header\n")); temp[1] = '\0'; // stop after first \n } soh = skip_header_prologue(item->email->header.str); } char folder_line[LINE_SIZE]; char line[LINE_SIZE]; // reset pdf writer to new file int bates = bates_index; // save starting index snprintf(folder_line, sizeof(folder_line), "pst folder = %s\n", f.name.c_str()); open_pdf(folder_line); // start printing this email fprintf(dii_file, "@FOLDERNAME %s\n", f.name.c_str()); string myfrom = extract_header(soh, "From"); string myto = extract_header(soh, "To"); string mycc = extract_header(soh, "Cc"); string mybcc = extract_header(soh, "Bcc"); if (myfrom.empty()) write_simple("FROM", item->email->outlook_sender_name.str, item->email->sender_address.str); else write_simple("FROM", myfrom); if (myto.empty()) write_simple("TO", item->email->sentto_address.str, item->email->recip_address.str); else write_simple("TO", myto); if (mycc.empty()) write_simple("CC", item->email->cc_address.str); else write_simple("CC", mycc); if (mybcc.empty()) write_simple("BCC", item->email->bcc_address.str); else write_simple("BCC", mybcc); if (item->email->sent_date) { time_t t = pst_fileTimeToUnixTime(item->email->sent_date); char c_time[C_TIME_SIZE]; strftime(c_time, C_TIME_SIZE, "%F", gmtime(&t)); write_simple("DATESENT", c_time); strftime(c_time, C_TIME_SIZE, "%T+0000", gmtime(&t)); write_simple("TIMESENT", c_time); } if (item->email->arrival_date) { time_t t = pst_fileTimeToUnixTime(item->email->arrival_date); char c_time[C_TIME_SIZE]; strftime(c_time, C_TIME_SIZE, "%F", gmtime(&t)); write_simple("DATERCVD", c_time); strftime(c_time, C_TIME_SIZE, "%T+0000", gmtime(&t)); write_simple("TIMERCVD", c_time); } if (item->subject.str) { write_simple("SUBJECT", item->subject.str); } write_simple("MSGID", item->email->messageid.str); write_simple("READ", (item->flags & 1) ? "Y" : "N"); DEBUG_INFO(("About to print Header\n")); fprintf(dii_file, "@HEADER\n"); if (item && item->subject.str) { DEBUG_INFO(("item->subject = %s\n", item->subject.str)); } if (soh) { // Now, write out the header... print_pdf(soh); int len = strlen(soh); if (!len || (soh[len-1] != '\n')) { snprintf(line, sizeof(line), "\n"); print_pdf(line); } } else { //make up our own headers const char *temp = item->email->outlook_sender.str; if (!temp) temp = ""; snprintf(line, sizeof(line), "From: \"%s\" <%s>\n", item->email->outlook_sender_name.str, temp); print_pdf(line); if (item->subject.str) { snprintf(line, sizeof(line), "Subject: %s\n", item->subject.str); } else { snprintf(line, sizeof(line), "Subject: \n"); } print_pdf(line); snprintf(line, sizeof(line), "To: %s\n", item->email->sentto_address.str); print_pdf(line); if (item->email->cc_address.str) { snprintf(line, sizeof(line), "Cc: %s\n", item->email->cc_address.str); print_pdf(line); } if (item->email->sent_date) { time_t em_time = pst_fileTimeToUnixTime(item->email->sent_date); char c_time[C_TIME_SIZE]; strftime(c_time, C_TIME_SIZE, "%a, %d %b %Y %H:%M:%S %z", gmtime(&em_time)); snprintf(line, sizeof(line), "Date: %s\n", c_time); print_pdf(line); } } snprintf(line, sizeof(line), "\n"); print_pdf_only(line, black); fprintf(dii_file, "@HEADER-END\n"); DEBUG_INFO(("About to print Body\n")); fprintf(dii_file, "@EMAIL-BODY\n"); if (item->body.str) { removeCR(item->body.str); print_pdf(item->body.str); } else if (item->email->htmlbody.str) { removeCR(item->email->htmlbody.str); print_pdf(item->email->htmlbody.str); } else if (item->email->encrypted_body.data || item->email->encrypted_htmlbody.data) { char ln[LINE_SIZE]; snprintf(ln, sizeof(ln), "%s", "The body of this email is encrypted. This isn't supported yet, but the body is now an attachment\n"); print_pdf(ln); } fprintf(dii_file, "@EMAIL-END\n"); int attach_num = 0; for (pst_item_attach* 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->data.data || attach->i_id) { string an = write_separate_attachment(f.name, attach, ++attach_num, pst); fprintf(dii_file, "@EATTACH %s\n", an.c_str()); } } close_pdf(); fprintf(dii_file, "@BATESBEG %d\n", bates); fprintf(dii_file, "@BATESEND %d\n", bates_index-1); DEBUG_RET(); } static void create_enter_dir(file_ll &f, file_ll *parent, pst_item *item); static void create_enter_dir(file_ll &f, file_ll *parent, pst_item *item) { pst_convert_utf8(item, &item->file_as); f.type = item->type; f.stored_count = (item->folder) ? item->folder->item_count : 0; f.name = ((parent) ? parent->name + "/" : "") + string(item->file_as.str); } static void close_enter_dir(file_ll &f); static void close_enter_dir(file_ll &f) { } static void process(pst_item *outeritem, file_ll *parent, pst_desc_tree *d_ptr); static void process(pst_item *outeritem, file_ll *parent, pst_desc_tree *d_ptr) { file_ll ff; pst_item *item = NULL; DEBUG_ENT("process"); create_enter_dir(ff, parent, outeritem); for (; d_ptr; d_ptr = d_ptr->next) { if (d_ptr->desc) { item = pst_parse_item(&pstfile, d_ptr, NULL); DEBUG_INFO(("item pointer is %p\n", item)); if (item) { if (item->folder && item->file_as.str && d_ptr->child ) { //if this is a non-empty folder, we want to recurse into it fprintf(stderr, "entering folder %s\n", item->file_as.str); process(item, &ff, d_ptr->child); } else if (item->email && (item->type == PST_TYPE_NOTE || item->type == PST_TYPE_SCHEDULE || item->type == PST_TYPE_REPORT)) { ff.email_count++; write_normal_email(ff, item, &pstfile); } else { ff.skip_count++; // other mapi objects } pst_freeItem(item); } else { ff.skip_count++; DEBUG_INFO(("A NULL item was seen\n")); } } } close_enter_dir(ff); DEBUG_RET(); } int main(int argc, char* const* argv) { pst_desc_tree *d_ptr; char *fname = NULL; char c; char *d_log = NULL; prog_name = argv[0]; pst_item *item = NULL; while ((c = getopt(argc, argv, "B:b:c:d:f:o:O:Vh"))!= -1) { switch (c) { case 'B': bates_prefix = optarg; break; case 'b': bates_index = atoi(optarg); break; case 'c': bates_color = (int)strtol(optarg, (char**)NULL, 16); break; case 'f': font_file = optarg; break; case 'o': output_directory = optarg; break; case 'O': output_file = optarg; break; case 'd': d_log = optarg; break; case 'h': usage(); exit(0); break; case 'V': version(); exit(0); break; default: usage(); exit(1); break; } } if (!font_file) { usage(); exit(1); } if (argc > optind) { fname = argv[optind]; } else { usage(); exit(2); } #ifdef DEBUG_ALL // force a log file if (!d_log) d_log = "pst2dii.log"; #endif DEBUG_INIT(d_log, NULL); DEBUG_ENT("main"); RET_DERROR(pst_open(&pstfile, fname, NULL), 1, ("Error opening File\n")); RET_DERROR(pst_load_index(&pstfile), 2, ("Index Error\n")); pst_load_extended_attributes(&pstfile); d_ptr = pstfile.d_head; // first record is main record item = (pst_item*)pst_parse_item(&pstfile, d_ptr, NULL); if (!item || !item->message_store) { DEBUG_RET(); DIE(("Could not get root record\n")); } d_ptr = pst_getTopOfFolders(&pstfile, item); if (!d_ptr) { DEBUG_RET(); DIE(("Top of folders record not found. Cannot continue\n")); } dii_file = fopen(output_file, "wb"); if (dii_file) { process(item, NULL, d_ptr->child); // do the children of TOPF pst_freeItem(item); pst_close(&pstfile); fclose(dii_file); } DEBUG_RET(); return 0; } diff --git a/src/pst2ldif.cpp b/src/pst2ldif.cpp index 119bd72..ac6ed35 100644 --- a/src/pst2ldif.cpp +++ b/src/pst2ldif.cpp @@ -1,765 +1,763 @@ /* Copyright (c) 2004 Carl Byington - 510 Software Group, released under the GPL version 2 or any later version at your choice available at https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt Based on readpst.c by David Smith */ using namespace std; // needed for std c++ collections #include #include #include -extern "C" { - #include "define.h" - #include "lzfu.h" -} +#include "define.h" +#include "lzfu.h" void usage(void); void version(void); char *check_filename(char *fname); void print_ldif_single(const char *attr, const char *value); void print_ldif_single(const char *attr, pst_string value); void print_ldif_address(const char *attr, int nvalues, pst_string value, ...); void print_ldif_dn(const char *attr, pst_string value, const char *base); void print_ldif_multi(const char *dn, pst_string value); void print_ldif_two(const char *attr, pst_string value1, pst_string value2); void print_escaped_dn(const char *value); void build_cn(char *cn, size_t len, int nvalues, pst_string value, ...); char *prog_name; pst_file pstfile; bool old_schema = false; char *ldap_base = NULL; // 'o=some.domain.tld,c=US' int ldif_extra_line_count = 0; vector ldap_class; // 'newPerson' or 'inetOrgPerson' vector ldif_extra_line; // 'o: myorg' //////////////////////////////////////////////// // define our ordering struct ltstr { bool operator()(const char* s1, const char* s2) const { return strcasecmp(s1, s2) < 0; } }; // define our set typedef set string_set; // make a static set to hold the cn values static string_set all_strings; //////////////////////////////////////////////// // helper to free all the strings in a set // static void free_strings(string_set &s); static void free_strings(string_set &s) { if (s.empty()) return; for (string_set::iterator i=s.begin(); i!=s.end(); i++) { free((void*)*i); } s.clear(); } //////////////////////////////////////////////// // helper to register a string in a string set // static const char* register_string(string_set &s, const char *name); static const char* register_string(string_set &s, const char *name) { string_set::const_iterator i = s.find(name); if (i != s.end()) return *i; char *x = strdup(name); s.insert(x); return x; } //////////////////////////////////////////////// // register a global string // static const char* register_string(const char *name); static const char* register_string(const char *name) { return register_string(all_strings, name); } //////////////////////////////////////////////// // make a unique string // static const char* unique_string(const char *name); static const char* unique_string(const char *name) { int unique = 2; string_set::iterator i = all_strings.find(name); if (i == all_strings.end()) return register_string(name); while (true) { vector n(strlen(name)+10); snprintf(&n[0], n.size(), "%s %d", name, unique++); string_set::iterator i = all_strings.find(&n[0]); if (i == all_strings.end()) return register_string(&n[0]); } } static void process(pst_desc_tree *d_ptr); static void process(pst_desc_tree *d_ptr) { DEBUG_ENT("process"); pst_item *item = NULL; while (d_ptr) { if (d_ptr->desc) { item = pst_parse_item(&pstfile, d_ptr, NULL); DEBUG_INFO(("item pointer is %p\n", item)); if (item) { if (item->folder && d_ptr->child && item->file_as.str && strcasecmp(item->file_as.str, "Deleted Items")) { //if this is a non-empty folder other than deleted items, we want to recurse into it fprintf(stderr, "entering folder %s\n", item->file_as.str); process(d_ptr->child); } else if (item->contact && (item->type == PST_TYPE_CONTACT)) { // deal with a contact char cn[1000]; // convert everything to utf8 pst_convert_utf8_null(item, &item->contact->display_name_prefix); pst_convert_utf8_null(item, &item->contact->first_name); pst_convert_utf8_null(item, &item->contact->surname); pst_convert_utf8_null(item, &item->contact->suffix); pst_convert_utf8_null(item, &item->contact->company_name); pst_convert_utf8_null(item, &item->contact->job_title); pst_convert_utf8_null(item, &item->contact->address1); pst_convert_utf8_null(item, &item->contact->address2); pst_convert_utf8_null(item, &item->contact->address3); pst_convert_utf8_null(item, &item->contact->address1a); pst_convert_utf8_null(item, &item->contact->address2a); pst_convert_utf8_null(item, &item->contact->address3a); pst_convert_utf8_null(item, &item->contact->business_address); pst_convert_utf8_null(item, &item->contact->business_po_box); pst_convert_utf8_null(item, &item->contact->business_street); pst_convert_utf8_null(item, &item->contact->business_city); pst_convert_utf8_null(item, &item->contact->business_state); pst_convert_utf8_null(item, &item->contact->business_postal_code); pst_convert_utf8_null(item, &item->contact->home_address); pst_convert_utf8_null(item, &item->contact->home_po_box); pst_convert_utf8_null(item, &item->contact->home_street); pst_convert_utf8_null(item, &item->contact->home_city); pst_convert_utf8_null(item, &item->contact->home_state); pst_convert_utf8_null(item, &item->contact->home_postal_code); pst_convert_utf8_null(item, &item->contact->other_address); pst_convert_utf8_null(item, &item->contact->other_po_box); pst_convert_utf8_null(item, &item->contact->other_street); pst_convert_utf8_null(item, &item->contact->other_city); pst_convert_utf8_null(item, &item->contact->other_state); pst_convert_utf8_null(item, &item->contact->other_postal_code); pst_convert_utf8_null(item, &item->contact->business_fax); pst_convert_utf8_null(item, &item->contact->home_fax); pst_convert_utf8_null(item, &item->contact->business_phone); pst_convert_utf8_null(item, &item->contact->home_phone); pst_convert_utf8_null(item, &item->contact->car_phone); pst_convert_utf8_null(item, &item->contact->mobile_phone); pst_convert_utf8_null(item, &item->contact->other_phone); pst_convert_utf8_null(item, &item->contact->business_homepage); pst_convert_utf8_null(item, &item->contact->personal_homepage); pst_convert_utf8_null(item, &item->comment); build_cn(cn, sizeof(cn), 4, item->contact->display_name_prefix, item->contact->first_name, item->contact->surname, item->contact->suffix); if (cn[0] != 0) { // have a valid cn pst_string ucn; ucn.str = (char*)unique_string(cn); ucn.is_utf8 = 1; // all the components are already utf8 print_ldif_dn("dn", ucn, ldap_base); print_ldif_single("cn", ucn); if (item->contact->first_name.str) { print_ldif_two("givenName", item->contact->display_name_prefix, item->contact->first_name); } if (item->contact->surname.str) { print_ldif_two("sn", item->contact->surname, item->contact->suffix); } else if (item->contact->company_name.str) { print_ldif_single("sn", item->contact->company_name); } else print_ldif_single("sn", ucn); // use cn as sn if we cannot find something better if (old_schema) { if (item->contact->job_title.str) print_ldif_single("personalTitle", item->contact->job_title); if (item->contact->company_name.str) print_ldif_single("company", item->contact->company_name); } else { // new schema if (item->contact->job_title.str) print_ldif_single("title", item->contact->job_title); if (item->contact->company_name.str) print_ldif_single("o", item->contact->company_name); } if (item->contact->address1.str && *item->contact->address1.str) print_ldif_single("mail", item->contact->address1); if (item->contact->address2.str && *item->contact->address2.str) print_ldif_single("mail", item->contact->address2); if (item->contact->address3.str && *item->contact->address3.str) print_ldif_single("mail", item->contact->address3); if (item->contact->address1a.str && *item->contact->address1a.str) print_ldif_single("mail", item->contact->address1a); if (item->contact->address2a.str && *item->contact->address2a.str) print_ldif_single("mail", item->contact->address2a); if (item->contact->address3a.str && *item->contact->address3a.str) print_ldif_single("mail", item->contact->address3a); if (old_schema) { if (item->contact->business_address.str) { if (item->contact->business_po_box.str) print_ldif_single("postalAddress", item->contact->business_po_box); if (item->contact->business_street.str) print_ldif_multi("postalAddress", item->contact->business_street); if (item->contact->business_city.str) print_ldif_single("l", item->contact->business_city); if (item->contact->business_state.str) print_ldif_single("st", item->contact->business_state); if (item->contact->business_postal_code.str) print_ldif_single("postalCode", item->contact->business_postal_code); } else if (item->contact->home_address.str) { if (item->contact->home_po_box.str) print_ldif_single("postalAddress", item->contact->home_po_box); if (item->contact->home_street.str) print_ldif_multi("postalAddress", item->contact->home_street); if (item->contact->home_city.str) print_ldif_single("l", item->contact->home_city); if (item->contact->home_state.str) print_ldif_single("st", item->contact->home_state); if (item->contact->home_postal_code.str) print_ldif_single("postalCode", item->contact->home_postal_code); } else if (item->contact->other_address.str) { if (item->contact->other_po_box.str) print_ldif_single("postalAddress", item->contact->other_po_box); if (item->contact->other_street.str) print_ldif_multi("postalAddress", item->contact->other_street); if (item->contact->other_city.str) print_ldif_single("l", item->contact->other_city); if (item->contact->other_state.str) print_ldif_single("st", item->contact->other_state); if (item->contact->other_postal_code.str) print_ldif_single("postalCode", item->contact->other_postal_code); } } else { // new schema, with proper RFC4517 postal addresses if (item->contact->business_address.str) { print_ldif_address("postalAddress", 6, item->contact->business_po_box, item->contact->business_street, item->contact->business_city, item->contact->business_state, item->contact->business_postal_code, item->contact->business_country); if (item->contact->business_city.str) print_ldif_single("l", item->contact->business_city); if (item->contact->business_state.str) print_ldif_single("st", item->contact->business_state); if (item->contact->business_postal_code.str) print_ldif_single("postalCode", item->contact->business_postal_code); } else if (item->contact->home_address.str) { if (item->contact->home_city.str) print_ldif_single("l", item->contact->home_city); if (item->contact->home_state.str) print_ldif_single("st", item->contact->home_state); if (item->contact->home_postal_code.str) print_ldif_single("postalCode", item->contact->home_postal_code); } else if (item->contact->other_address.str) { print_ldif_address("postalAddress", 6, item->contact->other_po_box, item->contact->other_street, item->contact->other_city, item->contact->other_state, item->contact->other_postal_code, item->contact->other_country); if (item->contact->other_city.str) print_ldif_single("l", item->contact->other_city); if (item->contact->other_state.str) print_ldif_single("st", item->contact->other_state); if (item->contact->other_postal_code.str) print_ldif_single("postalCode", item->contact->other_postal_code); } if (item->contact->home_address.str) { print_ldif_address("homePostalAddress", 6, item->contact->home_po_box, item->contact->home_street, item->contact->home_city, item->contact->home_state, item->contact->home_postal_code, item->contact->home_country); } } if (item->contact->business_fax.str) print_ldif_single("facsimileTelephoneNumber", item->contact->business_fax); else if (item->contact->home_fax.str) print_ldif_single("facsimileTelephoneNumber", item->contact->home_fax); if (item->contact->business_phone.str) print_ldif_single("telephoneNumber", item->contact->business_phone); if (item->contact->home_phone.str) print_ldif_single("homePhone", item->contact->home_phone); if (item->contact->car_phone.str) print_ldif_single("mobile", item->contact->car_phone); else if (item->contact->mobile_phone.str) print_ldif_single("mobile", item->contact->mobile_phone); else if (item->contact->other_phone.str) print_ldif_single("mobile", item->contact->other_phone); if (!old_schema) { if (item->contact->business_homepage.str) print_ldif_single("labeledURI", item->contact->business_homepage); if (item->contact->personal_homepage.str) print_ldif_single("labeledURI", item->contact->personal_homepage); } if (item->comment.str) print_ldif_single("description", item->comment); for (vector::size_type i=0; inext; } DEBUG_RET(); } void print_ldif_single(const char *attr, pst_string value) { print_ldif_single(attr, value.str); } // Prints an attribute together with its value. // If the value isn't a "SAFE STRING" (as defined in RFC2849), // then it is output as a BASE-64 encoded value void print_ldif_single(const char *attr, const char *value) { size_t len; bool is_safe_string = true; bool space_flag = false; // Strip leading spaces while (*value == ' ') value++; len = strlen(value) + 1; vector buffer(len); char *p = &buffer[0]; // See if "value" is a "SAFE STRING" // First check characters that are safe but not safe as initial characters if (*value == ':' || *value == '<') is_safe_string = false; for (;;) { char ch = *value++; if (ch == 0 || ch == '\n') break; else if (ch == '\r') continue; else if (ch == ' ') { space_flag = true; continue; } else { if ((ch & 0x80) == 0x80) { is_safe_string = false; } if (space_flag) { *p++ = ' '; space_flag = false; } *p++ = ch; } } *p = 0; if (is_safe_string) { printf("%s: %s\n", attr, &buffer[0]); } else { p = pst_base64_encode(&buffer[0], buffer.size()); printf("%s:: %s\n", attr, p); free(p); } } // Combines values representing address lines into an address,i // lines separated with "$" as per PostalAddress syntax in RFC4517 void print_ldif_address(const char *attr, int nvalues, pst_string value, ...) { DEBUG_ENT("print_ldif_address"); bool space_flag = false; bool newline_flag = false; char *address = NULL; // Buffer where address is built up int len = 0; // Length of buffer int i = 0; // Index of next character position in buffer va_list ap; va_start(ap, value); while (!value.str) { nvalues--; if (nvalues == 0) { // Nothing at all to do! va_end(ap); DEBUG_RET(); return; } value = va_arg(ap, pst_string); } for (;;) { char ch = *(value.str)++; if (ch == 0) { do { nvalues--; if (nvalues == 0) break; value = va_arg(ap, pst_string); } while (!value.str); if (!nvalues || !value.str) break; space_flag = true; newline_flag = true; } else if (ch == '\r') continue; else if (ch == '\n') { newline_flag = true; continue; } else if (ch == ' ') { space_flag = true; continue; } else { if (i > (len-5)) { len += 256; char *addr = (char *)realloc(address, len); // cppcheck found unchecked error if (!addr) exit(3); address = addr; } if (newline_flag) { address[i++] = '$'; newline_flag = false; space_flag = false; } else if (space_flag) { address[i++] = ' '; space_flag = false; } if (ch == '$' || ch == '\\') address[i++] = '\\'; address[i++] = ch; } } va_end(ap); if (i == 0) return; // Nothing to do address[i] = 0; print_ldif_single(attr, address); free(address); DEBUG_RET(); } void print_ldif_multi(const char *dn, pst_string value) { char *n; char *valuestr = value.str; while ((n = strchr(valuestr, '\n'))) { print_ldif_single(dn, valuestr); valuestr = n + 1; } print_ldif_single(dn, valuestr); } void print_ldif_two(const char *attr, pst_string value1, pst_string value2) { size_t len1, len2; if (value1.str && *value1.str) len1 = strlen(value1.str); else { print_ldif_single(attr, value2); return; } if (value2.str && *value2.str) len2 = strlen(value2.str); else { print_ldif_single(attr, value1); return; } vector value(len1 + len2 + 2); memcpy(&value[0], value1.str, len1); value[len1] = ' '; memcpy(&value[0] + len1 + 1, value2.str, len2 + 1); print_ldif_single(attr, &value[0]); } void build_cn(char *cn, size_t len, int nvalues, pst_string value, ...) { bool space_flag = false; size_t i = 0; va_list ap; va_start(ap, value); while (!value.str) { nvalues--; if (nvalues == 0) { cn[0] = 0; // Just a terminating NUL va_end(ap); return; } value = va_arg(ap, pst_string); } for (;;) { char ch = *(value.str)++; if (ch == 0 || ch == '\n') { do { nvalues--; if (nvalues == 0) break; value = va_arg(ap, pst_string); } while (!value.str); if (!nvalues || !value.str) break; space_flag = true; } else if (ch == '\r') continue; else if (ch == ' ') { space_flag = true; continue; } else { if (space_flag) { if (i > 0) { if (i < (len - 2)) cn[i++] = ' '; else break; } space_flag = false; } if (i < (len - 1)) cn[i++] = ch; else break; } } cn[i] = 0; va_end(ap); } int main(int argc, char* const* argv) { pst_desc_tree *d_ptr; char *fname = NULL; int c; char *d_log = NULL; prog_name = argv[0]; pst_item *item = NULL; while ((c = getopt(argc, argv, "b:c:d:l:oVh"))!= -1) { switch (c) { case 'b': ldap_base = optarg; break; case 'c': ldap_class.push_back(string(optarg)); break; case 'd': d_log = optarg; break; case 'h': usage(); exit(0); break; case 'l': ldif_extra_line.push_back(string(optarg)); break; case 'o': old_schema = true; break; case 'V': version(); exit(0); break; default: usage(); exit(1); break; } } if ((argc > optind) && (ldap_base)) { fname = argv[optind]; } else { usage(); exit(2); } #ifdef DEBUG_ALL // force a log file if (!d_log) d_log = "pst2ldif.log"; #endif DEBUG_INIT(d_log, NULL); DEBUG_ENT("main"); RET_DERROR(pst_open(&pstfile, fname, NULL), 1, ("Error opening File\n")); RET_DERROR(pst_load_index(&pstfile), 2, ("Index Error\n")); pst_load_extended_attributes(&pstfile); d_ptr = pstfile.d_head; // first record is main record item = (pst_item*)pst_parse_item(&pstfile, d_ptr, NULL); if (!item || !item->message_store) { DEBUG_RET(); DIE(("main: Could not get root record\n")); } d_ptr = pst_getTopOfFolders(&pstfile, item); if (!d_ptr) { DEBUG_RET(); DIE(("Top of folders record not found. Cannot continue\n")); } pst_freeItem(item); if (old_schema && (strlen(ldap_base) > 2)) { char *ldap_org = strdup(ldap_base+2); // assume first 2 chars are o= char *temp = strchr(ldap_org, ','); if (temp) { *temp = '\0'; // write the ldap header printf("dn: %s\n", ldap_base); printf("o: %s\n", ldap_org); printf("objectClass: organization\n\n"); printf("dn: cn=root, %s\n", ldap_base); printf("cn: root\n"); printf("sn: root\n"); for (vector::size_type i=0; ichild); // do the children of TOPF pst_close(&pstfile); DEBUG_RET(); free_strings(all_strings); return 0; } void usage(void) { version(); printf("Usage: %s [OPTIONS] {PST FILENAME}\n", prog_name); printf("OPTIONS:\n"); printf("\t-V\t- Version. Display program version\n"); printf("\t-b ldapbase\t- set the LDAP base value\n"); printf("\t-c class\t- set the class of the LDAP objects (may contain more than one)\n"); printf("\t-d \t- Debug to file.\n"); printf("\t-h\t- Help. This screen\n"); printf("\t-l line\t- extra line to insert in the LDIF file for each contact\n"); printf("\t-o\t- use old schema, default is new schema\n"); } void version(void) { printf("pst2ldif 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 } char *check_filename(char *fname) { char *t = fname; if (t == NULL) { return fname; } while ((t = strpbrk(t, "/\\:"))) { // while there are characters in the second string that we don't want *t = '_'; //replace them with an underscore } return fname; } // This function escapes Distinguished Names (as per RFC4514) void print_ldif_dn(const char *attr, pst_string value, const char *base) { printf("dn: cn="); const char *valuestr = value.str; // remove leading spaces (RFC says escape them) while (*valuestr == ' ') valuestr++; print_escaped_dn(valuestr); if (base && base[0]) { printf(", %s", base); } printf("\n"); return; } void print_escaped_dn(const char *value) { char ch; // escape initial '#' and space if (*value == '#' || *value == ' ') putchar('\\'); while ((ch = *value++) != 0) { if (((ch & 0x80) != 0) || (ch <= 0x1F)) // Print as escaped hex digits printf("\\%2.2X", ch & 0xFF); else switch (ch) { case '\\': case '"' : case '+' : case ',' : case ';' : case '<' : case '>' : putchar('\\'); // Fall through default: putchar(ch); } } return; } diff --git a/src/vbuf.h b/src/vbuf.h index 55f2bd1..32233e9 100644 --- a/src/vbuf.h +++ b/src/vbuf.h @@ -1,29 +1,35 @@ #ifndef __PST_VBUF_H #define __PST_VBUF_H #include "common.h" +#ifdef __cplusplus +extern "C" { +#endif // Variable-length buffers struct pst_varbuf { size_t dlen; //length of data stored in buffer size_t blen; //length of buffer char *buf; //buffer char *b; //start of stored data }; typedef struct pst_varbuf pst_vbuf; pst_vbuf *pst_vballoc(size_t len); void pst_vbgrow(pst_vbuf *vb, size_t len); // grow buffer by len bytes, data are preserved void pst_vbset(pst_vbuf *vb, void *data, size_t len); void pst_vbappend(pst_vbuf *vb, void *data, size_t length); void pst_unicode_init(); size_t pst_vb_utf16to8(pst_vbuf *dest, const char *inbuf, int iblen); size_t pst_vb_utf8to8bit(pst_vbuf *dest, const char *inbuf, int iblen, const char* charset); size_t pst_vb_8bit2utf8(pst_vbuf *dest, const char *inbuf, int iblen, const char* charset); +#ifdef __cplusplus +} +#endif #endif