/***************************************************************************
                          filter_pmail.cxx  -  Pegasus-Mail import
                             -------------------
    begin                : Sat Jan 6 2001
    copyright            : (C) 2001 by Holger Schurig
    email                : holgerschurig@gmx.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include <config.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <klocale.h>
#include <kfiledialog.h>
#include <qregexp.h>

#include "filter_pmail.hxx"


filter_pmail::filter_pmail() :
   filter(i18n("Import Folders From Pegasus-Mail (*.CNM, *.PMM, *.MBX)"),"Holger Schurig")
{
  CAP=i18n("Import Pegasus-Mail");
}



filter_pmail::~filter_pmail()
{
}



void filter_pmail::import(filterInfo *info)
{
   QString  choosen;
   QString  msg;

   if (!kmailStart(info)) { return; }
   inf = info;
   par = info->parent();

   msg = i18n("Select the Pegasus-Mail directory on your system.\n\n"
              "This import filter will import your folders, but not\n"
              "the folder structure. But you will probably only do\n"
              "this one time.\n\n"
              "NOTE: Kmailcvt creates folders with the prefix 'pmail-'.\n"
              "If this causes problems for you (you have KMail folders\n"
              "with that prefix), cancel this import function (the next dialog\n"
              "will allow you to do that) and rename the existing KMail\n"
              "folders."
             );
   info->alert(CAP,msg);

   // Select directory from where I have to import files
   sprintf(dir,getenv("HOME"));	//lukas: noooooo! this breaks i18n, no sprintf please! use QDir instead
   choosen=KFileDialog::getExistingDirectory(dir,par);
   if (choosen.length()==0) { return; } // No directory choosen here!
   strcpy(dir,choosen.latin1());

   // Count total number of files to be processed
   info->log(i18n("Counting files..."));
   totalFiles = countFiles(".cnm");
   totalFiles += countFiles(".pmm");
   totalFiles += countFiles(".mbx");

   //msg=i18n("Searching for distribution lists ('.pml')...");
   //info->log(msg);
   //totalFiles += countFiles(dir, "*.pml");

   if (!kmailStart(info))
      return;
   info->log(i18n("Importing new mail files ('.cnm')..."));
   processFiles(".cnm", &filter_pmail::importNewMessage);
   info->log(i18n("Importing mail folders ('.pmm')..."));
   processFiles(".pmm", &filter_pmail::importMailFolder);
   info->log(i18n("Importing 'UNIX' mail folders ('.mbx')..."));
   processFiles(".mbx", &filter_pmail::importUnixMailFolder);
   kmailStop(info);
}


/** counts all files with mask (e.g. '*.cnm') in
in a directory */
int filter_pmail::countFiles(const char *mask)
{
   DIR *d;
   struct dirent *entry;
   d = opendir(dir);
   int n = 0;

   entry=readdir(d);
   while (entry!=NULL) {
      char *file=entry->d_name;
      if (strlen(file)>4 && strcasecmp(&file[strlen(file)-4],mask)==0)
         n++;
      entry=readdir(d);
   }
   closedir(d);

   return n;
}


/** updates currentFile and the progress bar */
void filter_pmail::nextFile()
{
   float perc;

   currentFile++;
   perc=(((float) currentFile)/((float) totalFiles))*100.0;
   inf->overall(perc);
}


/** this looks for all files with the filemask 'mask' and calls the 'workFunc' on each of them */
void filter_pmail::processFiles(const char *mask, void(filter_pmail::* workFunc)(const char*) )
{
   DIR *d;
   struct dirent *entry;
   d = opendir(dir);

   entry=readdir(d);
   while (entry!=NULL) {
      char *file=entry->d_name;
      if (strlen(file)>4 && strcasecmp(&file[strlen(file)-4],mask)==0) {

         // Notify current file
         QString msg;
         msg = i18n("From: %1").arg(file);
         inf->from(msg);

         // Clear the other fields
         msg = "";
         inf->to(msg);
         inf->current(msg);
         inf->current(-1);

         // combine dir and filename into a path
         QString path = dir;
         path.append("/");
         path.append(file);

         // call worker function, increase progressbar
         (this->*workFunc)(path.latin1());	//lukas: noooooo! no .latin1() in paths!!!
         nextFile();
      }
      entry=readdir(d);
   }
   closedir(d);
}


/** this function imports one *.CNM message */
void filter_pmail::importNewMessage(const char *file)
{
   unsigned long added;
   const char* destFolder = "PMail-New Messages";
   QString msg;

   msg = i18n("To: %1").arg(destFolder);
   inf->to(msg);

   kmailMessage((filterInfo *) inf, (char *)destFolder, (char *)file, added);
}


/** this function imports one mail folder file (*.PMM) */
void filter_pmail::importMailFolder(const char *file)
{
   struct {
      char folder[86];
      char id[42];
   } pmm_head;

   FILE *f;
   QString msg;
   QString folder;
   int ch = 0;
   int state = 0;
   int n = 0;
   FILE *temp = NULL;
   char tempname[128];
   unsigned long added;


   // Format of a PMM file:
   // First comes a header with 128 bytes. At the beginning is the name of
   // the folder. Then there are some unknown bytes (strings). At offset 128
   // the first message starts.
   //
   // Each message is terminated by a 0x1A byte. The next message follows
   // immediately.
   //
   // The last message is followed by a 0x1A, too.
   //
   // 000000 6d 61 69 6c 73 65 72 76 65 72 2d 70 72 6f 6a 65    mailserver-proje
   // 000010 63 74 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ct..............
   // 000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
   // 000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
   // 000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
   // 000050 00 00 00 00 00 00 36 30 34 37 35 37 32 45 3a 36    ......6047572E:6
   // 000060 46 34 39 3a 46 4f 4c 30 31 33 35 35 00 00 00 00    F49:FOL01355....
   // 000070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
   // 000080 52 65 74 75 72 6e 2d 50 61 74 68 3a 20 3c 75 72    Return-Path: <ur
   // ...
   // 000cb0 2d 2d 2d 2d 2d 2d 2d 2d 2d 2b 0d 0a 1a 52 65 74    ---------+...Ret
   // 000cc0 75 72 6e 2d 50 61 74 68 3a 20 3c 62 6f 75 6e 63    urn-Path: <bounc
   // ...
   // 04dc50 46 30 33 38 44 2e 36 31 35 44 37 34 44 30 2d 2d    F038D.615D74D0--
   // 04dc60 0d 0a 1a

   // open the message
   f = fopen(file, "rb");

   // Get folder name
   fread(&pmm_head, sizeof(pmm_head), 1, f);
   folder = "PMail-";
   folder.append(pmm_head.folder);
   msg = i18n("To: %1").arg(folder);
   inf->to(msg);
   // The folder name might contain weird characters ...
   folder.replace(QRegExp("[^a-zA-Z0-9:.-]"), ":");

   // State machine to read the data in. The fgetc usage is probably terribly slow ...
   while ((ch = fgetc(f)) >= 0) {
      switch (state) {

      // new message state
      case 0:
         // open temp output file

         // XXX this is UGLY. Some people have the stuff in /var/tmp ...
         // However, KTempFile is also ugly, because it does not return
         // the full path of the created file (and creates it in the
         // current directory) :-(
         snprintf(tempname, sizeof(tempname), "/tmp/pmail_pmm.%d",getpid());
         temp = fopen(tempname,"wb");
         state = 1;
         n++;
         msg.sprintf("Message %d", n);
         inf->current(msg);
         // fall throught

      // inside a message state
      case 1:
         if (ch == 0x1a) {
            // close file, send it
            fclose(temp);
            kmailMessage((filterInfo *) inf, (char *)folder.latin1(), tempname, added);
            unlink(tempname);
            state = 0;
            break;
         }
         if (ch == 0x0d) {
             break;
         }
         fputc(ch, temp);
         break;
      }
   }

   // did Folder end without 0x1a at the end?
   if (state != 0) {
      fclose(temp);
      kmailMessage((filterInfo *) inf, (char *)folder.latin1(), tempname, added);
      unlink(tempname);
   }

   fclose(f);
}


/** imports a 'unix' format mail folder (*.MBX) */
void filter_pmail::importUnixMailFolder(const char *file)
{
   #define MAX_LINE 4096
   #define MSG_SEPERATOR_START "From "
   #define MSG_SEPERATOR_REGEX "^From .*..:...*$"
   static short msgSepLen = strlen(MSG_SEPERATOR_START);

   struct {
      char folder[58];
   } pmg_head;
   FILE *f;
   QString s(file);
   QString folder;

   char line[MAX_LINE];
   FILE *temp = NULL;
   char tempname[128];
   int n = 0;
   QRegExp regexp(MSG_SEPERATOR_REGEX);

   unsigned long added;

   // Get folder name
   s.replace( QRegExp("mbx$"), "pmg");
   s.replace( QRegExp("MBX$"), "PMG");
   f = fopen(s.latin1(), "rb");		//lukas: noooooo! no fopen nor .latin1() for files!!!
   fread(&pmg_head, sizeof(pmg_head), 1, f);
   fclose(f);
   folder = "PMail-";
   folder.append(pmg_head.folder);
   inf->to(i18n("To: %1").arg(folder));
   // The folder name might contain weird characters ...
   folder.replace(QRegExp("[^a-zA-Z0-9:.-]"), ":");

   // Read in the folder
   //
   // Messages are separated by "From " lines. We use a logic similar
   // to KMail's kmfolder.cpp to find the boundaries.

   snprintf(tempname, sizeof(tempname), "/tmp/pmail_mbx.%d",getpid());
   f = fopen(file, "rb");
   while (fgets(line, MAX_LINE, f)) {
      // Look for separator
      if (temp &&                                             // when we wrote to outfile
         (strncmp(line,MSG_SEPERATOR_START, msgSepLen)==0 &&  // quick compar
         regexp.search(line) >= 0))                            // slower regexp
      {
         fclose(temp);
         kmailMessage((filterInfo *) inf, (char *)folder.latin1(), tempname, added);
         unlink(tempname);
         temp = NULL;
      }

      // Do we need to open/reopen output file?
      if (!temp) {
         // XXX again, direct usage of '/tmp' is ugly, but again KTempFile is not good
         // enought for us -- as of early Januar 2001
         temp = fopen(tempname,"w");
         // Notify progress
         n++;
         s.sprintf("Message %d", n);
         inf->current(s);
      }

      fputs(line, temp);
   }

   if (temp) {
      fclose(temp);
      kmailMessage((filterInfo *) inf, (char *)folder.latin1(), tempname, added);
      unlink(tempname);
   }

   fclose(f);
}
