* Copyright 2005-2009, Haiku Inc.
* This file may be used under the terms of the MIT License.
*
* Originally public domain written by Alexander G. M. Smith.
*/
converts BeOS e-mail files into Unix mailbox files (the kind that Pine
uses). All the files in the input directory are concatenated with the
appropriate mbox header lines added between them, and trailing blank lines
reduced. The resulting text is written to standard output. Command line
driven.
*/
#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include <Application.h>
#include <StorageKit.h>
#include <SupportKit.h>
extern const char* __progname;
static const char* kProgramName = __progname;
time_t gDateStampTime;
part describes the error, and if errorNumber is non-zero, gets the string
", error code $X (standard description)." appended to it. If the message
is NULL then it gets defaulted to "Something went wrong".
*/
static void
DisplayErrorMessage(const char* messageString = NULL, status_t errorNumber = 0,
const char* titleString = NULL)
{
char errorBuffer[2048];
if (titleString == NULL)
titleString = "Error Message:";
if (messageString == NULL) {
if (errorNumber == B_OK)
messageString = "No error, no message, why bother?";
else
messageString = "Error";
}
if (errorNumber != 0) {
snprintf(errorBuffer, sizeof(errorBuffer), "%s: %s (%" B_PRIx32 ")"
"has occured.", messageString, strerror(errorNumber), errorNumber);
messageString = errorBuffer;
}
fputs(titleString, stderr);
fputc('\n', stderr);
fputs(messageString, stderr);
fputc('\n', stderr);
}
files have messages that start with a line that could say something like
"From agmsmith@achilles.net Fri Oct 31 21:19:36 EST 1997" or maybe something
like "From POPmail Mon Oct 20 21:12:36 1997" or in a more modern format,
"From agmsmith@achilles.net Tue Sep 4 09:04:11 2001 -0400". I generalise it
to "From blah Day MMM NN XX:XX:XX TZONE1 YYYY TZONE2". Blah is an e-mail
address you can ignore (just treat it as a word separated by spaces). Day
is a 3 letter day of the week. MMM is a 3 letter month name. NN is the two
digit day of the week, has a leading space if the day is less than 10.
XX:XX:XX is the time, the X's are digits. TZONE1 is the old style optional
time zone of 3 capital letters. YYYY is the four digit year. TZONE2 is the
optional modern time zone info, a plus or minus sign and 4 digits. Returns
true if the line of text (ended with a NUL byte, no line feed or carriage
returns at the end) is the start of a message.
*/
bool
IsStartOfMailMessage(char* lineString)
{
if (memcmp("From ", lineString, 5) != 0)
return false;
char* string = lineString + 4;
while (*string == ' ')
string++;
while (*string != ' ' && *string != 0)
string++;
while (*string == ' ')
string++;
if (memcmp(string, "Mon", 3) != 0 && memcmp(string, "Tue", 3) != 0
&& memcmp(string, "Wed", 3) != 0 && memcmp(string, "Thu", 3) != 0
&& memcmp(string, "Fri", 3) != 0 && memcmp(string, "Sat", 3) != 0
&& memcmp(string, "Sun", 3) != 0) {
fprintf(stderr, "False alarm, not a valid day of the week in \"%s\""
".\n", lineString);
return false;
}
string += 3;
while (*string == ' ')
string++;
if (memcmp(string, "Jan", 3) != 0 && memcmp(string, "Feb", 3) != 0
&& memcmp(string, "Mar", 3) != 0 && memcmp(string, "Apr", 3) != 0
&& memcmp(string, "May", 3) != 0 && memcmp(string, "Jun", 3) != 0
&& memcmp(string, "Jul", 3) != 0 && memcmp(string, "Aug", 3) != 0
&& memcmp(string, "Sep", 3) != 0 && memcmp(string, "Oct", 3) != 0
&& memcmp(string, "Nov", 3) != 0 && memcmp(string, "Dec", 3) != 0) {
fprintf(stderr, "False alarm, not a valid month name in \"%s\".\n",
lineString);
return false;
}
string += 3;
while (*string == ' ')
string++;
if (*string < '0' || *string > '9') {
fprintf(stderr, "False alarm, not a valid day of the "
"month number in \"%s\".\n", lineString);
return false;
}
while (*string >= '0' && *string <= '9')
string++;
while (*string == ' ')
string++;
if (string[0] < '0' || string[0] > '9'
|| string[1] < '0' || string[1] > '9'
|| string[2] != ':'
|| string[3] < '0' || string[3] > '9'
|| string[4] < '0' || string[4] > '9'
|| string[5] != ':'
|| string[6] < '0' || string[6] > '9'
|| string[7] < '0' || string[7] > '9') {
fprintf(stderr, "False alarm, not a valid time value in \"%s\".\n",
lineString);
return false;
}
string += 8;
while (*string == ' ')
string++;
if (string[0] >= 'A' && string[0] <= 'Z'
&& string[1] >= 'A' && string[1] <= 'Z'
&& string[2] >= 'A' && string[2] <= 'Z') {
string += 3;
while (*string == ' ')
string++;
}
if (string[0] < '0' || string[0] > '9'
|| string[1] < '0' || string[1] > '9'
|| string[2] < '0' || string[2] > '9'
|| string[3] < '0' || string[3] > '9') {
fprintf(stderr, "False alarm, not a valid 4 digit year in \"%s\".\n",
lineString);
return false;
}
string += 4;
while (*string == ' ')
string++;
if ((string[0] == '+' || string[0] == '-')
&& string[1] >= '0' && string[1] <= '9'
&& string[2] >= '0' && string[2] <= '9'
&& string[3] >= '0' && string[3] <= '9'
&& string[4] >= '0' && string[4] <= '9') {
string += 5;
while (*string == ' ')
string++;
}
if (*string != 0) {
fprintf(stderr, "False alarm, extra stuff after the "
"year/time zone in \"%s\".\n", lineString);
return false;
}
return true;
}
output. Returns zero if successful, a negative error code if an error
occured.
*/
status_t
ProcessMessageFile(char* fileName)
{
fprintf(stdout, "Now processing: \"%s\"\n", fileName);
FILE* inputFile = fopen(fileName, "rb");
if (inputFile == NULL) {
DisplayErrorMessage("Unable to open file", errno);
return errno;
}
BString messageText;
int lineNumber = 0;
while (!feof(inputFile)) {
char line[102400];
if (fgets(line, sizeof(line), inputFile) == NULL) {
if (ferror(inputFile)) {
char errorString[2048];
snprintf(errorString, sizeof(errorString),
"Error while reading from \"%s\"", fileName);
DisplayErrorMessage(errorString, errno);
fclose(inputFile);
return errno;
}
break;
}
char* string = line + strlen(line) - 1;
while (string >= line && *string < 32)
string--;
*(++string) = 0;
if (lineNumber == 0 && line[0] == 0) {
continue;
}
lineNumber++;
if (lineNumber == 1 && !IsStartOfMailMessage(line)) {
time_t timestamp = gDateStampTime++;
messageText.Append("From baron@be.com ");
messageText.Append(ctime(×tamp));
}
messageText.Append(line);
messageText.Append("\n");
}
int i = messageText.Length();
while (i > 0 && (messageText[i - 1] == '\n' || messageText[i - 1] == '\r'))
i--;
messageText.Truncate(i);
messageText.Append("\n\n");
status_t status = B_OK;
if (puts(messageText.String()) < 0) {
DisplayErrorMessage ("Error while writing the message", errno);
status = errno;
}
fclose(inputFile);
return status;
}
int
main(int argc, char** argv)
{
BApplication app("application/x-vnd.Haiku-mail2mbox");
if (argc <= 1 || argc >= 3) {
printf("%s is a utility for converting Mail e-mail\n", argv[0]);
printf("files to Unix Pine style e-mail files. It could well\n");
printf("work with other Unix style mailbox files. Each message in\n");
printf("the input directory is converted and sent to the standard\n");
printf("output. Usage:\n\n");
printf("%s InputDirectory >OutputFile\n\n", kProgramName);
printf("Public domain, by Alexander G. M. Smith.\n");
return -10;
}
gDateStampTime = time (NULL);
char inputPathName[B_PATH_NAME_LENGTH];
strlcpy(inputPathName, argv[1], sizeof(inputPathName) - 2);
char tempString[2048];
DIR* dir = opendir(inputPathName);
if (dir == NULL) {
sprintf(tempString, "Problems opening directory named \"%s\".",
inputPathName);
DisplayErrorMessage(tempString, errno);
return 1;
}
if (inputPathName[strlen(inputPathName) - 1] != '/')
strcat(inputPathName, "/");
int messagesDoneCount = 0;
status_t status = B_OK;
while (dirent_t* entry = readdir(dir)) {
if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
break;
strlcpy(tempString, inputPathName, sizeof(tempString));
strlcat(tempString, entry->d_name, sizeof(tempString));
status = ProcessMessageFile(tempString);
if (status != B_OK)
break;
messagesDoneCount++;
}
closedir(dir);
if (status != B_OK) {
DisplayErrorMessage("Stopping early because an error occured", status);
return status;
}
fprintf(stderr, "Did %d messages successfully.\n", messagesDoneCount);
return 0;
}