diff --git a/src/main.cpp b/src/main.cpp index 3ba124b8..ffda0a0b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -88,6 +88,7 @@ int main(int argc, char ** argv) printf("? show this help\n"); printf("open open/create database\n"); printf("close close database\n"); + printf("import [] import file/folder/last EML-file(s)\n"); printf("set [] set/delete configuration value\n"); printf("get show configuration value\n"); printf("connect connect to mailbox server\n"); @@ -125,6 +126,13 @@ int main(int argc, char ** argv) printf("ERROR: no database opened.\n"); } } + else if( strncmp(cmd, "import", 6)==0 ) + { + const char* arg1 = strstr(cmd, " "); + if( !mailbox->ImportSpec(arg1? ++arg1 : NULL) ) { + print_error(); + } + } else if( strcmp(cmd, "connect")==0 ) { if( !mailbox->Connect() ) { diff --git a/src/mrimfparser.cpp b/src/mrimfparser.cpp index 1d7766e2..95d79b1a 100644 --- a/src/mrimfparser.cpp +++ b/src/mrimfparser.cpp @@ -201,7 +201,7 @@ void MrImfParser::AddOrLookupContacts(mailimf_address_list* adr_list, carray* id ******************************************************************************/ -int32_t MrImfParser::Imf2Msg(const char* imf_raw, size_t imf_len) +int32_t MrImfParser::Imf2Msg(const char* imf_raw_not_terminated, size_t imf_raw_bytes) { carray* contact_ids_from = NULL; carray* contact_ids_to = NULL; @@ -234,7 +234,7 @@ int32_t MrImfParser::Imf2Msg(const char* imf_raw, size_t imf_len) // normally, this is done by mailimf_message_parse(), however, as we also need the MIME data, // we use mailmime_parse() through MrMimeParser (both call mailimf_struct_multiple_parse() somewhen, I did not found out anything // that speaks against this approach yet) - mime_parser.Parse(imf_raw); + mime_parser.Parse(imf_raw_not_terminated, imf_raw_bytes); if( mime_parser.m_header == NULL ) { goto Imf2Msg_Done; // Error - even adding an empty record won't help as we do not know the message ID } diff --git a/src/mrimfparser.h b/src/mrimfparser.h index 92805320..a714a137 100644 --- a/src/mrimfparser.h +++ b/src/mrimfparser.h @@ -39,7 +39,7 @@ public: // Imf2Msg() takes an IMF, convers into one or more messages and stores them in the database. // the function returns the number of new created messages. - int32_t Imf2Msg (const char* imf, size_t imf_len); + int32_t Imf2Msg (const char* imf_raw_not_terminated, size_t imf_raw_bytes); private: char* DecodeHeaderString (const char* in); // can e NULL, result must be free()'s by the caller diff --git a/src/mrmailbox.cpp b/src/mrmailbox.cpp index 3f8ba05b..dfce430a 100644 --- a/src/mrmailbox.cpp +++ b/src/mrmailbox.cpp @@ -28,6 +28,7 @@ #include #include +#include #include #include "mrmailbox.h" #include "mrimfparser.h" @@ -84,6 +85,105 @@ void MrMailbox::Close() } +/******************************************************************************* + * Import EML-files + ******************************************************************************/ + + +bool MrMailbox::ImportFile(const char* filename) +{ + bool success = false; + FILE* f = NULL; + struct stat stat_info; + char* data = NULL; + + // read file content to `data` + if( (f=fopen(filename, "r")) == NULL ) { + MrLogError("MrMailbox::ImportFile(): Cannot open file.", filename); + goto ImportFile_Cleanup; + } + + if( stat(filename, &stat_info) != 0 || stat_info.st_size == 0 ) { + MrLogError("MrMailbox::ImportFile(): Cannot find out file size or file is empty.", filename); + goto ImportFile_Cleanup; + } + + if( (data=(char*)malloc(stat_info.st_size))==NULL ) { + MrLogError("MrMailbox::ImportFile(): Out of memory.", filename); + goto ImportFile_Cleanup; + } + + if( fread(data, 1, stat_info.st_size, f)!=(size_t)stat_info.st_size ) { + MrLogError("MrMailbox::ImportFile(): Read error.", filename); + goto ImportFile_Cleanup; + } + + fclose(f); + f = NULL; + + // import `data` + ReceiveImf(data, stat_info.st_size); + + // success + success = true; + + // cleanup: +ImportFile_Cleanup: + free(data); + if( f ) { + fclose(f); + } + return success; +} + + +bool MrMailbox::ImportSpec(const char* spec) +{ + bool success = false; + char* spec_memory = NULL; + + if( !m_sql.Ok() ) { + MrLogError("MrMailbox::ImportSpec(): Datebase not opened.", spec); + goto ImportSpec_Cleanup; + } + + // if `spec` is not given, try to use the last one + if( spec == NULL ) { + MrSqlite3Locker locker(m_sql); + spec_memory = m_sql.GetConfig("import_spec", NULL); + spec = spec_memory; // may still be NULL + if( spec == NULL ) { + MrLogError("MrMailbox::ImportSpec(): No file or folder given.", spec); + goto ImportSpec_Cleanup; + } + } + + if( strlen(spec)>=4 && strcmp(&spec[strlen(spec)-4], ".eml")==0 ) { + // import a single file + if( !ImportFile(spec) ) { + goto ImportSpec_Cleanup; // error already logged + } + } + else { + // import a directory + MrLogError("MrMailbox::ImportSpec(): Directory import not yet implemented.", spec); + goto ImportSpec_Cleanup; + } + + // success + { + MrSqlite3Locker locker(m_sql); + m_sql.SetConfig("import_spec", spec); + } + success = true; + + // cleanup +ImportSpec_Cleanup: + free(spec_memory); + return success; +} + + /******************************************************************************* * Connect ******************************************************************************/ @@ -135,17 +235,17 @@ bool MrMailbox::Fetch() /******************************************************************************* - * Receive an IMF as an result to calling Fetch() + * Receive an IMF as an result to calling Fetch() or Import*() * the new IMF may be old or new and should be parsed, contacts created etc. * However, the caller should make sure, it does not exist in the database. ******************************************************************************/ -void MrMailbox::ReceiveImf(const char* imf, size_t imf_len) +void MrMailbox::ReceiveImf(const char* imf_raw_not_terminated, size_t imf_raw_bytes) { MrImfParser parser(this); - if( !parser.Imf2Msg(imf, imf_len) ) { + if( !parser.Imf2Msg(imf_raw_not_terminated, imf_raw_bytes) ) { return; // error already logged } } diff --git a/src/mrmailbox.h b/src/mrmailbox.h index 4cafbc3a..817cc01b 100644 --- a/src/mrmailbox.h +++ b/src/mrmailbox.h @@ -64,6 +64,12 @@ public: bool Open (const char* dbfile); void Close (); + // ImportSpec() imports data from EML-files. if `spec` is a folder, all EML-files are imported, if `spec` is a file, + // a single EML-file is imported, if `spec` is NULL, the last import is done again (you may want to call Empty() before) + // ImportFile() always imports a single file, + bool ImportSpec (const char* spec); + bool ImportFile (const char* file); + // empty all tables but leaves server configuration bool Empty (); diff --git a/src/mrmimeparser.cpp b/src/mrmimeparser.cpp index 9ebdf818..e3601d1c 100644 --- a/src/mrmimeparser.cpp +++ b/src/mrmimeparser.cpp @@ -642,7 +642,7 @@ void MrMimeParser::ParseMimeRecursive(mailmime* mime) } -carray* MrMimeParser::Parse(const char* body) +carray* MrMimeParser::Parse(const char* body_not_terminated, size_t body_bytes) { int r; size_t index = 0; @@ -651,7 +651,7 @@ carray* MrMimeParser::Parse(const char* body) Empty(); // parse body - r = mailmime_parse(body, strlen(body), &index, &m_mimeroot); + r = mailmime_parse(body_not_terminated, body_bytes, &index, &m_mimeroot); if(r != MAILIMF_NO_ERROR || m_mimeroot == NULL ) { goto Parse_Cleanup; } diff --git a/src/mrmimeparser.h b/src/mrmimeparser.h index d7acb42c..da8bc7af 100644 --- a/src/mrmimeparser.h +++ b/src/mrmimeparser.h @@ -55,7 +55,7 @@ public: // Unless memory-allocation-errors occur, Parse() returns at least one empty part. // (this is because we want to add even these message to our database to avoid reading them several times. // of course, these empty messages are not added to any chat) - carray* Parse (const char* body); + carray* Parse (const char* body_not_terminated, size_t body_bytes); // data, read-only carray* m_parts;