#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <Application.h>
#include <E-mail.h>
#include <StorageKit.h>
#include <SupportKit.h>
#include <MailMessage.h>
#include <mail_util.h>
extern const char* __progname;
static const char* kProgramName = __progname;
char InputPathName [B_PATH_NAME_LENGTH];
FILE *InputFile;
BDirectory OutputDir;
typedef enum StandardHeaderEnum
{
STD_HDR_DATE = 0,
STD_HDR_FROM,
STD_HDR_TO,
STD_HDR_CC,
STD_HDR_REPLY,
STD_HDR_SUBJECT,
STD_HDR_PRIORITY,
STD_HDR_LABEL,
STD_HDR_STATUS,
STD_HDR_THREAD,
STD_HDR_NAME,
STD_HDR_MAX
} StandardHeaderCodes;
const char *g_StandardAttributeNames [STD_HDR_MAX] =
{
B_MAIL_ATTR_WHEN,
B_MAIL_ATTR_FROM,
B_MAIL_ATTR_TO,
B_MAIL_ATTR_CC,
B_MAIL_ATTR_REPLY,
B_MAIL_ATTR_SUBJECT,
B_MAIL_ATTR_PRIORITY,
B_MAIL_ATTR_LABEL,
B_MAIL_ATTR_STATUS,
B_MAIL_ATTR_THREAD,
B_MAIL_ATTR_NAME
};
static void DisplayErrorMessage (
const char *MessageString = NULL,
int ErrorNumber = 0,
const char *TitleString = NULL)
{
char ErrorBuffer [B_PATH_NAME_LENGTH + 80 + 80];
if (TitleString == NULL)
TitleString = "Error Message:";
if (MessageString == NULL)
{
if (ErrorNumber == 0)
MessageString = "No error, no message, why bother?";
else
MessageString = "Something went wrong";
}
if (ErrorNumber != 0)
{
sprintf (ErrorBuffer, "%s, error code $%X/%d (%s) has occured.",
MessageString, ErrorNumber, ErrorNumber, strerror (ErrorNumber));
MessageString = ErrorBuffer;
}
fputs (TitleString, stderr);
fputc ('\n', stderr);
fputs (MessageString, stderr);
fputc ('\n', stderr);
}
bool IsStartOfMailMessage (char *LineString)
{
char *StringPntr;
if (memcmp ("From ", LineString, 5) != 0)
return false;
StringPntr = LineString + 4;
while (*StringPntr == ' ')
StringPntr++;
while (*StringPntr != ' ' && *StringPntr != 0)
StringPntr++;
while (*StringPntr == ' ')
StringPntr++;
if (memcmp (StringPntr, "Mon", 3) != 0 &&
memcmp (StringPntr, "Tue", 3) != 0 &&
memcmp (StringPntr, "Wed", 3) != 0 &&
memcmp (StringPntr, "Thu", 3) != 0 &&
memcmp (StringPntr, "Fri", 3) != 0 &&
memcmp (StringPntr, "Sat", 3) != 0 &&
memcmp (StringPntr, "Sun", 3) != 0)
{
printf ("False alarm, not a valid day of the week in \"%s\".\n",
LineString);
return false;
}
StringPntr += 3;
while (*StringPntr == ' ')
StringPntr++;
if (memcmp (StringPntr, "Jan", 3) != 0 &&
memcmp (StringPntr, "Feb", 3) != 0 &&
memcmp (StringPntr, "Mar", 3) != 0 &&
memcmp (StringPntr, "Apr", 3) != 0 &&
memcmp (StringPntr, "May", 3) != 0 &&
memcmp (StringPntr, "Jun", 3) != 0 &&
memcmp (StringPntr, "Jul", 3) != 0 &&
memcmp (StringPntr, "Aug", 3) != 0 &&
memcmp (StringPntr, "Sep", 3) != 0 &&
memcmp (StringPntr, "Oct", 3) != 0 &&
memcmp (StringPntr, "Nov", 3) != 0 &&
memcmp (StringPntr, "Dec", 3) != 0)
{
printf ("False alarm, not a valid month name in \"%s\".\n",
LineString);
return false;
}
StringPntr += 3;
while (*StringPntr == ' ')
StringPntr++;
if (*StringPntr < '0' || *StringPntr > '9')
{
printf ("False alarm, not a valid day of the month number in \"%s\".\n",
LineString);
return false;
}
while (*StringPntr >= '0' && *StringPntr <= '9')
StringPntr++;
while (*StringPntr == ' ')
StringPntr++;
if (StringPntr[0] < '0' || StringPntr[0] > '9' ||
StringPntr[1] < '0' || StringPntr[1] > '9' ||
StringPntr[2] != ':' ||
StringPntr[3] < '0' || StringPntr[3] > '9' ||
StringPntr[4] < '0' || StringPntr[4] > '9' ||
StringPntr[5] != ':' ||
StringPntr[6] < '0' || StringPntr[6] > '9' ||
StringPntr[7] < '0' || StringPntr[7] > '9')
{
printf ("False alarm, not a valid time value in \"%s\".\n",
LineString);
return false;
}
StringPntr += 8;
while (*StringPntr == ' ')
StringPntr++;
if (StringPntr[0] >= 'A' && StringPntr[0] <= 'Z' &&
StringPntr[1] >= 'A' && StringPntr[1] <= 'Z' &&
StringPntr[2] >= 'A' && StringPntr[2] <= 'Z')
{
StringPntr += 3;
while (*StringPntr == ' ')
StringPntr++;
}
if (StringPntr[0] < '0' || StringPntr[0] > '9' ||
StringPntr[1] < '0' || StringPntr[1] > '9' ||
StringPntr[2] < '0' || StringPntr[2] > '9' ||
StringPntr[3] < '0' || StringPntr[3] > '9')
{
printf ("False alarm, not a valid 4 digit year in \"%s\".\n",
LineString);
return false;
}
StringPntr += 4;
while (*StringPntr == ' ')
StringPntr++;
if ((StringPntr[0] == '+' || StringPntr[0] == '-') &&
StringPntr[1] >= '0' && StringPntr[1] <= '9' &&
StringPntr[2] >= '0' && StringPntr[2] <= '9' &&
StringPntr[3] >= '0' && StringPntr[3] <= '9' &&
StringPntr[4] >= '0' && StringPntr[4] <= '9')
{
StringPntr += 5;
while (*StringPntr == ' ')
StringPntr++;
}
if (*StringPntr != 0)
{
printf ("False alarm, extra stuff after the year/time zone in \"%s\".\n",
LineString);
return false;
}
return true;
}
bool IsStartOfUsenetArticle (char *LineString)
{
char *StringPntr;
if (memcmp ("Article ", LineString, 8) != 0)
return false;
StringPntr = LineString + 7;
while (*StringPntr == ' ')
StringPntr++;
if (*StringPntr < '0' || *StringPntr > '9')
{
printf ("False alarm, not a valid article number in \"%s\".\n",
LineString);
return false;
}
while (*StringPntr >= '0' && *StringPntr <= '9')
StringPntr++;
while (*StringPntr == ' ')
StringPntr++;
if (memcmp ("of ", StringPntr, 3) != 0)
{
printf ("False alarm, article line \"of\" misssing in \"%s\".\n",
LineString);
return false;
}
StringPntr += 2;
while (*StringPntr == ' ')
StringPntr++;
while (*StringPntr != ':' && *StringPntr != ' ' && *StringPntr != 0)
StringPntr++;
if (StringPntr[0] != ':' || StringPntr[1] != 0)
{
printf ("False alarm, article doesn't end with a colon in \"%s\".\n",
LineString);
return false;
}
return true;
}
status_t SaveMessage (BString &MessageText)
{
time_t DateInSeconds;
status_t ErrorCode;
BString FileName;
BString HeaderValues [STD_HDR_MAX];
int i;
int Length;
BEmailMessage MailMessage;
BFile OutputFile;
char TempString [80];
struct tm TimeFields;
BString UniqueFileName;
int32 Uniquer;
i = MessageText.Length ();
while (i > 0 && (MessageText[i-1] == '\n' || MessageText[i-1] == '\r'))
i--;
MessageText.Truncate (i);
MessageText.Append ("\r\n");
BMemoryIO FakeFile (MessageText.String (), MessageText.Length ());
ErrorCode = MailMessage.SetToRFC822 (&FakeFile,
MessageText.Length (), false );
if (ErrorCode != B_OK)
{
DisplayErrorMessage ("Mail library was unable to process a mail "
"message for some reason", ErrorCode);
return ErrorCode;
}
HeaderValues [STD_HDR_TO] = MailMessage.To ();
HeaderValues [STD_HDR_FROM] = MailMessage.From ();
HeaderValues [STD_HDR_CC] = MailMessage.CC ();
HeaderValues [STD_HDR_DATE] = MailMessage.Date ();
HeaderValues [STD_HDR_REPLY] = MailMessage.ReplyTo ();
HeaderValues [STD_HDR_SUBJECT] = MailMessage.Subject ();
HeaderValues [STD_HDR_STATUS] = "Read";
if (MailMessage.Priority () != 3 )
{
sprintf (TempString, "%d", MailMessage.Priority ());
HeaderValues [STD_HDR_PRIORITY] = TempString;
}
HeaderValues[STD_HDR_THREAD] = HeaderValues[STD_HDR_SUBJECT];
SubjectToThread (HeaderValues[STD_HDR_THREAD]);
if (HeaderValues[STD_HDR_THREAD].Length() <= 0)
HeaderValues[STD_HDR_THREAD] = "No Subject";
HeaderValues[STD_HDR_NAME] = HeaderValues[STD_HDR_FROM];
extract_address_name (HeaderValues[STD_HDR_NAME]);
FileName = HeaderValues [STD_HDR_THREAD];
if (FileName[0] == '.')
FileName.Prepend ("_");
DateInSeconds =
ParseDateWithTimeZone (HeaderValues[STD_HDR_DATE].String());
if (DateInSeconds == -1)
DateInSeconds = 0;
localtime_r (&DateInSeconds, &TimeFields);
sprintf (TempString, "%04d%02d%02d%02d%02d%02d",
TimeFields.tm_year + 1900,
TimeFields.tm_mon + 1,
TimeFields.tm_mday,
TimeFields.tm_hour,
TimeFields.tm_min,
TimeFields.tm_sec);
FileName << " " << TempString << " " << HeaderValues[STD_HDR_NAME];
FileName.Truncate (240);
FileName.ReplaceAll('/','_');
FileName.ReplaceAll('\'','_');
FileName.ReplaceAll('"','_');
FileName.ReplaceAll('!','_');
FileName.ReplaceAll('<','_');
FileName.ReplaceAll('>','_');
while (FileName.FindFirst(" ") >= 0)
FileName.Replace(" " , " " , 1024 );
Uniquer = 0;
UniqueFileName = FileName;
while (true)
{
ErrorCode = OutputFile.SetTo (&OutputDir,
const_cast<const char *> (UniqueFileName.String ()),
B_READ_WRITE | B_CREATE_FILE | B_FAIL_IF_EXISTS);
if (ErrorCode == B_OK)
break;
if (ErrorCode != B_FILE_EXISTS)
{
UniqueFileName.Prepend ("Unable to create file \"");
UniqueFileName.Append ("\" for writing a message to");
DisplayErrorMessage (UniqueFileName.String (), ErrorCode);
return ErrorCode;
}
Uniquer++;
UniqueFileName = FileName;
UniqueFileName << " " << Uniquer;
}
ErrorCode = OutputFile.Write (MessageText.String (), MessageText.Length ());
if (ErrorCode < 0)
{
UniqueFileName.Prepend ("Error while writing file \"");
UniqueFileName.Append ("\"");
DisplayErrorMessage (UniqueFileName.String (), ErrorCode);
return ErrorCode;
}
OutputFile.WriteAttr ("BEOS:TYPE", B_MIME_STRING_TYPE, 0,
"text/x-email", 13);
OutputFile.WriteAttr (g_StandardAttributeNames[STD_HDR_DATE],
B_TIME_TYPE, 0, &DateInSeconds, sizeof (DateInSeconds));
for (i = 1 ; i < STD_HDR_MAX; i++)
{
if ((Length = HeaderValues[i].Length()) > 0)
OutputFile.WriteAttr (g_StandardAttributeNames[i], B_STRING_TYPE, 0,
HeaderValues[i].String(), Length + 1);
}
return 0;
}
int main (int argc, char** argv)
{
char ErrorMessage [B_PATH_NAME_LENGTH + 80];
bool HaveOldMessage = false;
int MessagesDoneCount = 0;
BString MessageText;
BApplication MyApp ("application/x-vnd.Haiku-mbox2mail");
int NextArgIndex;
char OutputDirectoryPathName [B_PATH_NAME_LENGTH];
status_t ReturnCode = -1;
bool SaveSeparatorLine = false;
char *StringPntr;
char TempString [102400];
if (argc <= 1)
{
printf ("%s is a utility for converting Pine e-mail\n",
argv[0]);
printf ("files (mbox files) to Mail e-mail files with attributes. It\n");
printf ("could well work with other Unix style mailbox files, and\n");
printf ("saved Usenet article files. Each message in the input\n");
printf ("mailbox is converted into a separate file. You can\n");
printf ("optionally specify a directory (will be created if needed) to\n");
printf ("put all the output files in, otherwise it scatters them into\n");
printf ("the current directory. The -s option makes it leave in the\n");
printf ("separator text line at the top of each message, the default\n");
printf ("is to lose it.\n\n");
printf ("Usage:\n\n");
printf ("%s [-s] InputFile [OutputDirectory]\n\n", kProgramName);
printf ("Public domain, by Alexander G. M. Smith.\n");
return -10;
}
NextArgIndex = 1;
if (strcmp (argv[NextArgIndex], "-s") == 0)
{
SaveSeparatorLine = true;
NextArgIndex++;
}
if (NextArgIndex >= argc)
{
ReturnCode = -20;
DisplayErrorMessage ("Missing the input file (mbox file) name argument.");
goto ErrorExit;
}
strncpy (InputPathName, argv[NextArgIndex], sizeof (InputPathName) - 1);
NextArgIndex++;
InputFile = fopen (InputPathName, "rb");
if (InputFile == NULL)
{
ReturnCode = errno;
sprintf (ErrorMessage, "Unable to open file \"%s\" for reading",
InputPathName);
DisplayErrorMessage (ErrorMessage, ReturnCode);
goto ErrorExit;
}
if (NextArgIndex < argc)
{
strncpy (OutputDirectoryPathName, argv[NextArgIndex],
sizeof (OutputDirectoryPathName) - 2
);
NextArgIndex++;
}
else
strcpy (OutputDirectoryPathName, ".");
StringPntr =
OutputDirectoryPathName + (strlen (OutputDirectoryPathName) - 1);
while (StringPntr >= OutputDirectoryPathName)
{
if (*StringPntr != '/')
break;
StringPntr--;
}
*(++StringPntr) = 0;
if (StringPntr - OutputDirectoryPathName > 0 &&
strcmp (OutputDirectoryPathName, ".") != 0)
{
if (mkdir (OutputDirectoryPathName, 0777))
{
ReturnCode = errno;
if (ReturnCode != B_FILE_EXISTS)
{
sprintf (ErrorMessage, "Unable to make output directory \"%s\"",
OutputDirectoryPathName);
DisplayErrorMessage (ErrorMessage, ReturnCode);
goto ErrorExit;
}
}
}
ReturnCode = OutputDir.SetTo (OutputDirectoryPathName);
if (ReturnCode != B_OK)
{
sprintf (ErrorMessage, "Unable to set output BDirectory to \"%s\"",
OutputDirectoryPathName);
DisplayErrorMessage (ErrorMessage, ReturnCode);
goto ErrorExit;
}
printf ("Input file: \"%s\", Output directory: \"%s\", ",
InputPathName, OutputDirectoryPathName);
printf ("%ssaving separator text line at the top of each message. Working",
SaveSeparatorLine ? "" : "not ");
while (!feof (InputFile))
{
if (!fgets (TempString, sizeof (TempString), InputFile))
{
ReturnCode = errno;
if (ferror (InputFile))
{
sprintf (ErrorMessage,
"Error while reading from \"%s\"", InputPathName);
DisplayErrorMessage (ErrorMessage, ReturnCode);
goto ErrorExit;
}
break;
}
StringPntr = TempString + strlen (TempString) - 1;
while (StringPntr >= TempString && *StringPntr < 32)
StringPntr--;
*(++StringPntr) = 0;
if (IsStartOfUsenetArticle (TempString) ||
IsStartOfMailMessage (TempString))
{
if (HaveOldMessage)
{
if ((ReturnCode = SaveMessage (MessageText)) != 0)
goto ErrorExit;
putchar ('.');
fflush (stdout);
MessagesDoneCount++;
}
HaveOldMessage = true;
MessageText.SetTo (SaveSeparatorLine ? TempString : "");
}
else
{
if (MessageText.Length () > 0)
MessageText.Append ("\r\n");
MessageText.Append (TempString);
}
}
if (HaveOldMessage)
{
if ((ReturnCode = SaveMessage (MessageText)) != 0)
goto ErrorExit;
putchar ('.');
MessagesDoneCount++;
}
printf (" Did %d messages.\n", MessagesDoneCount);
ReturnCode = 0;
ErrorExit:
if (InputFile != NULL)
fclose (InputFile);
OutputDir.Unset ();
return ReturnCode;
}