/*****************************************************************************/
/* FA Partners - Created by Flemming Bregnvig bregnvig@stealthmail.com       */
/* Copyright (c) FA Partners 1997                                            */
/*---------------------------------------------------------------------------*/
/* Name        : mailmain.cpp                                                */
/* Description : This does the actual work of analysing the mail & inserting */
/*                                                                           */
/*---------------------------------------------------------------------------*/
/* History                                                                   */
/*---------------------------------------------------------------------------*/
/* Date        | Event                                   | Person            */
/*---------------------------------------------------------------------------*/
/* 1997-05-30  | Created                                 | FB                */
/* 1997-06-05  | Add member headers.                     | FB                */
/*---------------------------------------------------------------------------*/
/* Compile     :                                                             */
/*****************************************************************************/

#ifdef __TOS_OS2__
  #define INCL_DOS
  #define INCL_DOSFILEMGR
  #define INCL_DOSERRORS
  #define INCL_WIN
  #include <os2.h>

  extern "C" {

  }

#else
  #include <windows.h>
#endif
#include <stdlib.h>
#include <ctype.h>
#include <fstream.h>
#include <imsgbox.hpp>

#include <itime.hpp>

#include "..\..\BanditTagger\Tagger\mailmain.hpp"
#include "..\..\BanditTagger\Tagger\tagini.hpp"
#include "..\..\BanditTagger\Tagger\commonDef.hpp"
#include "..\..\BanditTagger\PM_Interface\pmBandit.hpp"
//const IString TAG_MASK   ("!*.TAG");
const IString TAG_MASK      ("!*");
const IString SEARCH        ("!");
const IString TAG_EXTENSION (".TAG");

extern Boolean debug;
//****************************************************************************/
// Procedure name   : Mail Tagger constructor                                */
// Input parameters : IString                                                */
// The member function constructs the object.                                */
//****************************************************************************/

MailTagger::MailTagger(TAGUserIni &aUserIni)
: _userIni(aUserIni)
, _inifile(aUserIni.profile())
, _xmailer(aUserIni.xMailer())
, _xnews  (aUserIni.xNews())
{
  #ifdef __TOS_OS2__
  if (_inifile.handle())
  #endif
    _inifile.setDefaultApplicationName(APP_FILE);

  if (!_userIni.tagfiles().upperCase()
                          .indexOf(TAG_FILE_MASK))
      _userIni.tagfiles() += TAG_FILE_MASK;

  _filepath = _userIni.tagfiles()
                      .subString(1, _userIni.tagfiles().lastIndexOf("\\"));

  #ifdef __TOS_OS2__
  HDIR          hdir = HDIR_SYSTEM;
  FILEFINDBUF3  fileBuffer  = {0};
  ULONG         ulFileCount = 1;
  APIRET        rc = NO_ERROR;

  for (rc = DosFindFirst(_userIni.tagfiles(),
                         &hdir,
                         FILE_NORMAL,
                         &fileBuffer,
                         sizeof(fileBuffer),
                         &ulFileCount,
                         FIL_STANDARD);
       rc == NO_ERROR;
       rc = DosFindNext(hdir,
                        &fileBuffer,
                        sizeof(fileBuffer),
                        &ulFileCount))
  {
    IString tempString(fileBuffer.achName);
    tempString.upperCase();
    _tagProspects.insertAsLast(new IString(tempString));
    if (!this->isProspectUpdated(&fileBuffer))
      this->updateProspect(&fileBuffer);
    ulFileCount = 1;
  } // endfor
  #else
  HANDLE            filehandle = 0;
  LPCTSTR           w32Filemask = _userIni.tagfiles();
  WIN32_FIND_DATA   fileBuffer;
  Boolean           moreFiles(true);
  memset(&fileBuffer, 0, sizeof(WIN32_FIND_DATA));
  for (filehandle = FindFirstFile(w32Filemask, &fileBuffer);
       filehandle != INVALID_HANDLE_VALUE && moreFiles;
       moreFiles = FindNextFile(filehandle, &fileBuffer))
  {
    IString tempString(fileBuffer.cFileName);
    tempString.upperCase();
    _tagProspects.insertAsLast(new IString(tempString));
    if (!this->isProspectUpdated(&fileBuffer))
      this->updateProspect(&fileBuffer, filehandle);
  } // endfor
  #endif
  // Always add the manual tag option.
  _tagProspects.insertAsLast(new IString(_userIni.manualTag()));

  // Set the srand once.
  srand(ITime::now().asSeconds());

  return;
}

/*****************************************************************************/
/* Member function  : isProspectUpdated (private)                            */
/* Input parameters : FILEFINDBUF3                                           */
/* The member function check to see if the file & the inifile has the same   */
/* information regarding the date, time & size.                              */
/* Returncodes : Boolean                                                     */
/*****************************************************************************/
#ifdef __TOS_OS2__
Boolean MailTagger::isProspectUpdated(FILEFINDBUF3* fileBuffer)
{
  Boolean   rc = false;

  if (_inifile.containsKeyName(_filepath+fileBuffer->achName)) {
    IString aString(_inifile.elementWithKey(_filepath+fileBuffer->achName));
    FILESTATUS3 *oldBuffer = (FILESTATUS3*) (char*)aString;
    if (fileBuffer->fdateLastWrite.day     == oldBuffer->fdateLastWrite.day     &&
        fileBuffer->fdateLastWrite.month   == oldBuffer->fdateLastWrite.month   &&
        fileBuffer->fdateLastWrite.year    == oldBuffer->fdateLastWrite.year    &&
        fileBuffer->ftimeLastWrite.twosecs == oldBuffer->ftimeLastWrite.twosecs &&
        fileBuffer->ftimeLastWrite.minutes == oldBuffer->ftimeLastWrite.minutes &&
        fileBuffer->ftimeLastWrite.hours   == oldBuffer->ftimeLastWrite.hours   &&
        fileBuffer->cbFile                 == oldBuffer->cbFile)
        rc = true;
  }
  return rc;
}
#else // The Windows version starts here **********************************************
Boolean MailTagger::isProspectUpdated(WIN32_FIND_DATA* fileBuffer)
{
  Boolean   rc = false;

  if (_inifile.containsKeyName(_filepath+fileBuffer->cFileName)) {
    IString aString(_inifile.elementWithKey(_filepath+fileBuffer->cFileName));
    WIN32_FIND_DATA *oldBuffer = (WIN32_FIND_DATA*) (char*)aString;
    if (CompareFileTime(&fileBuffer->ftLastWriteTime, &oldBuffer->ftLastWriteTime) == 0)
        rc = true;
  }
  return rc;
}
#endif
/*****************************************************************************/
/* Member function  : updateProspect    (private)                            */
/* Input parameters : FILEFINDBUF3                                           */
/* The member function updates the tag file & the ini file.                  */
/* Returncodes : void                                                        */
/*****************************************************************************/
#ifdef __TOS_OS2__
Boolean MailTagger::updateProspect(FILEFINDBUF3* fileBuffer)
{
  ifstream  in(_filepath + fileBuffer->achName);

  IString aString(_userIni.wysiwyg() ? this->readWysiwyg(in)
                                     : this->readOnelines(in));

  in.close();

  ofstream  out(_filepath + fileBuffer->achName);

  if (out.good()) {
    out.write((char *)aString, aString.size());
    out.close();
  }

  // Create the new file buffer.
  HFILE         hfile = NULLHANDLE;
  FILESTATUS3   fileStatus;
  ULONG         ulAction = 1;
  APIRET        rc = NO_ERROR;

  rc = DosOpen(_filepath + fileBuffer->achName,
               &hfile,
               &ulAction,
               0,
               FILE_NORMAL,
               OPEN_ACTION_OPEN_IF_EXISTS,
               OPEN_SHARE_DENYNONE,
               NULL);
  if (rc == NO_ERROR)
     rc = DosQueryFileInfo(hfile,
                           FIL_STANDARD,
                           &fileStatus,
                           sizeof(FILESTATUS3));
  if (rc == NO_ERROR)
    rc = DosClose(hfile);

  _inifile.addOrReplaceElementWithKey(_filepath + fileBuffer->achName,
                                      IString(&fileStatus, sizeof(FILESTATUS3)));
  return rc == NO_ERROR ? true : false;
}
#else  // The Windows version starts here **********************************************
Boolean MailTagger::updateProspect(WIN32_FIND_DATA* fileBuffer, HANDLE filehandle)
{
  ifstream  in(_filepath + fileBuffer->cFileName);

  IString aString(_userIni.wysiwyg() ? this->readWysiwyg(in)
                                     : this->readOnelines(in));

  in.close();

  ofstream  out(_filepath + fileBuffer->cFileName);

  if (out.good()) {
    out.write((char *)aString, aString.size());
    out.close();
  }

  // Create the new file buffer.
  FILETIME      creationTime;
  FILETIME      lastAccessTime;
  FILETIME      lastWriteTime;
  Boolean       rc (true);

  rc = GetFileTime(filehandle,
                   &creationTime,
                   &lastAccessTime,
                   &lastWriteTime);

  if (rc == true)
  _inifile.addOrReplaceElementWithKey(_filepath + fileBuffer->cFileName,
                                      IString(&lastAccessTime, sizeof(FILETIME)));
  return rc == NO_ERROR ? true : false;
}

#endif
/*****************************************************************************/
/* Member function  : readOnelines       (Private)                           */
/* Input parameters : ifstream                                               */
/* The member function updates the tag file                                  */
/* Returncodes : IString                                                     */
/*****************************************************************************/

IString MailTagger::readOnelines(ifstream& in)
{
  long    counter=0;
  IString aString;
  IString temp;

  for (temp = IString::lineFrom(in);
       temp != IString() || in.good();
       temp = IString::lineFrom(in)) {
    if (counter != 0)
       aString += temp + "\n";
    else if (!temp.strip().isDigits())
       aString += temp + "\n";
    counter++;
  }
  //Remove the tailing newline
  aString.stripTrailing('\n');
  aString = IString(counter)+ "\n" + aString;

  return aString;
}



/*****************************************************************************/
/* Member function  : readWysiwyg        (Private)                           */
/* Input parameters : ifstream                                               */
/* The member function updates the tag file                                  */
/* Returncodes : void                                                        */
/*****************************************************************************/

IString MailTagger::readWysiwyg(ifstream& in)
{
  long    counter=1;
  IString aString;
  IString temp;

  for (temp = IString::lineFrom(in);
       temp != IString() || in.good();
       temp = IString::lineFrom(in)) {
    if (temp != IString()) {
      if (counter != 1)
         aString += temp + "\n";
      else if (!IString::stripBlanks(temp).isDigits())
         aString += temp + "\n";
    } else {
      aString += "\n";
      counter++;
    }
  }

  //Remove the tailing newline
  aString.stripTrailing('\n');
  aString = IString(counter)+ "\n" + aString;

  return aString;
}

/*****************************************************************************/
/* Member function  : isTagline         (public)                             */
/* Input parameters : MList<PTagMarker>                                      */
/* The member function check to see if a given Tag class should be inserted  */
/* in the mail.                                                              */
/* Returncodes : Boolean                                                     */
/*****************************************************************************/

void MailTagger::isTaglines(MList<TagMarker>& tagMarkers)
{
  IString*   pstring;
  PTagMarker pmarker;
  for (pstring = _tagProspects.getFirst();
       pstring;
       pstring = _tagProspects.getNext())
  {
    IString& tagFile(*pstring);
    for (pmarker = tagMarkers.getFirst();
         pmarker;
         pmarker = tagMarkers.getNext())
    {
      TagMarker& tagMark(*pmarker);
      IString tempName(tagMark.tagname()); // In case it only says !os2 instaed of os2.tag
      if (!tempName.lastIndexOf(TAG_EXTENSION))
         tempName += TAG_EXTENSION;
      if ((tempName == IString("!") + tagFile) || (tempName == RANDOM_TAG) || (tagMark.tagname() == IString("!")+_userIni.manualTag()))
        tagMark.insert(true);
    } // endfor
  } // endfor
  return;
}


/*****************************************************************************/
/* Member function  : insertTagline     (public)                             */
/* Input parameters : const IString& const IString&                          */
/* The member function changes the mail tag with the actual tagline.         */
/* Returncodes : IString&                                                    */
/*****************************************************************************/

Boolean MailHandler::insertTaglines(MailTagger& tagger)
{
  long     offset(0);
  long     htmlOffset(0);
  _newMail = _orgMail;
  PTagMarker pmarker(0);
  for (pmarker = _tagMarkers.getFirst();
       pmarker;
       pmarker = _tagMarkers.getNext())
  {
    TagMarker& tagMark(*pmarker);
    if(tagMark.insert()) {
      IString tag;
      if (tagMark.tagname() != RANDOM_TAG && tagMark.tagname()+TAG_EXTENSION != RANDOM_TAG)
        tag = tagger.tagline(tagMark.tagname().subString(2));
      else {
        tag = tagger.tagline(tagger.getRandomTagProspect());
      } // endif
      _newMail.remove(tagMark.index()+offset, tagMark.tagname().size()); // Remove the !*.tag
      _newMail.insert(tag, tagMark.index()+offset-1); // -1 bescause it inserts after.
      offset += tag.size() - tagMark.tagname().size();
      if (_htmlEnabled) { // Assumtion : htmlIndex should never be zero.
      	IString htmlTag(tag); // Create a html, since the newline needs to be changed.
      	htmlTag.change("\n", "<BR>");
        _newMail.remove(tagMark.htmlIndex()+offset+htmlOffset, tagMark.tagname().size()); // Remove the !*.tag
        _newMail.insert(htmlTag, tagMark.htmlIndex()+offset+htmlOffset-1); // -1 bescause it inserts after.
        htmlOffset += htmlTag.size() - tagMark.tagname().size();
      } // endif
      _changed = true;
    }
  } // endfor
  #ifdef __TOS_OS2__
  if (_changed && (_userIni.advertise() || (!_userIni.registered() && _usingNetscape == true))) {
  #else // Only the Windows users.
  if (_changed && (_userIni.advertise() || !_userIni.registered())) {
  #endif
    if (!_htmlEnabled)
      _newMail += BANDIT_ADVERTISE;
    else {
      _newMail.insert(BANDIT_ADVERTISE, _newMail.lastIndexOf("\n", _newMail.lastIndexOf(IMA_ID, _newMail.indexOf(HTML_START)))-1);
      #ifdef __TOS_OS2__
      _newMail.insert(HTML_BANDIT_ADVERTISE, _newMail.indexOf(HTML_END)-1);
      #else
      _newMail.insert(HTML_BANDIT_ADVERTISE, _newMail.indexOf(HTML_END)-1);
      #endif
    } // endif
  } // endif
  return true;
}

//***************************************************************************
// MailHandler::handleX
// Input parameters : const IString& const IString&
// This member function select whether to remove or change the X-Whatever
// (Only registered)
// Returncodes : Boolean.
//***************************************************************************

Boolean MailHandler::handleX()
{
  Boolean isMail(_newMail.indexOf(XMAILER) ? true : false);
  Boolean rc(true);
  Boolean remove(isMail ? _userIni.removeXMailer() : _userIni.removeXNews());
  IString xtext(isMail ? _userIni.xMailer() : _userIni.xNews());
  IString xtype(isMail ? XMAILER : XNEWS);

  if (_userIni.noReplace() == false) {
    if (remove)
      rc = this->removeX(xtype);
    if (!rc || !remove) { // If remove failed try to insert x-whatever
      this->insertX(xtext, xtype);
      this->insertXTag();
    }
  } // endif
  return true;
}

//***************************************************************************
// MailHandler::removeX
// Input parameters : const IString&
// This member function select whether to remove or change the X-Mailer.
// (Only registered)
// Returncodes : Boolean.
//***************************************************************************

Boolean MailHandler::removeX(const IString& xtype)
{
  long    xStart    (_newMail.indexOf(xtype));
  long    numOfChars(_newMail.indexOf("\n", xStart) - xStart);

  Boolean rc(true);
  // Error if not found.
  if (xStart == 0) rc = false;

  if (rc) {
    IString unregistered(_newMail.subString(xStart, numOfChars).words(2).upperCase());

    if ((unregistered.indexOf(UNREGISTRED) == 0 && unregistered.indexOf(DEMONSTRATION) == 0) || _forceX)
        _newMail.remove(xStart, numOfChars+1);
    else rc = false;
  }
  return rc;
}

//***************************************************************************
// MailHandler::insertX
// Input parameters : const IString&
// This member function inserts the new x, but only if it a registered
// version
// Returncodes : Boolean.
//***************************************************************************

Boolean MailHandler::insertX(const IString& xtext, const IString& xtype)
{
  long    searchStart(_newMail.indexOf(xtype));
  long    searchLen  (_newMail.indexOf("\n", searchStart) - searchStart);

  if (searchStart) {
    IString temp(_newMail.subString(searchStart, searchLen));
    IString unregistered(temp = temp.words(2));

    unregistered.upperCase();

    if ((unregistered.indexOf(UNREGISTRED) == 0 && unregistered.indexOf(DEMONSTRATION) == 0)  || _forceX)
      _newMail.change(temp, xtext);
    else
      _newMail.change(temp, temp + WITH_BANDIT_TAGGER);
  }
  return true;
}

//***************************************************************************
// MailHandler::insertXTag
// Input parameters :
// This member function inserts the X-Tag, it inserts it before Subject, so that
// MR ICE/2 also get it. (Only registered)
// Returncodes : Boolean.
//***************************************************************************

Boolean MailHandler::insertXTag()
{
  long    insertAt(_newMail.indexOf(SUBJECT));
  long    isInserted(_newMail.indexOf(X_TAG));
  Boolean rc(true);

  if (insertAt && isInserted == 0) {
    IString xtag(X_TAG+(_userIni.registered() ? _userIni.registeredName()  : IString()));
    #ifndef __TOS_OS2__ // Windows only
    xtag.remove(xtag.indexOf(X_TAG_REMOVE), IString(X_TAG_REMOVE).length());
    #endif
    insertAt--; // Remove the index from the 'S'
    _newMail.insert(xtag + IString("\n"), insertAt);
  } else
    rc = false;

  return rc;
}

//***************************************************************************
// MailHandler::anonymous
// Input parameters :
// This removes the identity of the sender.
// Returncodes : Boolean.
//***************************************************************************

Boolean MailHandler::anonymous()
{
  IString tempMail(_newMail);

  long    startPos(tempMail.upperCase().indexOf(ANONYMOUS));
  if (startPos != 0) { // The user wants to be anonymous
    _newMail.remove(startPos, 10);

    // Find the X-Tag and change it. But only if registered.
    if (_userIni.registered()) {
      long xtagStart (_newMail.indexOf("X-Tag:"));
      long xtagLength(_newMail.indexOf("\n", xtagStart) - xtagStart);
      _newMail.change(_newMail.subString(xtagStart, xtagLength), X_TAG_ANONYMOUS);
    }
    // Find the From and change it.
    long fromStart (_newMail.indexOf("From: ")+6); // 6 move away from the From:
    long fromLength(_newMail.indexOf("\n", fromStart) - fromStart);
    IString anonymousFrom;
    if (_userIni.anonymousName().size())
      anonymousFrom = IString("\"") + _userIni.anonymousName() + IString("\"");
    if (_userIni.anonymousName().size()) // No matter what, just insert space.
      anonymousFrom += IString(" <") + _userIni.anonymousEmail() + IString(">");
    _newMail.change(_newMail.subString(fromStart, fromLength), anonymousFrom);
  }
  return true;
}

/*****************************************************************************/
/* Member function  : tagline           (public)                             */
/* Input parameters : const IString&                                         */
/* The member function extract a tagline based on a tagfilename.             */
/* Returncodes : const IString&                                              */
/*****************************************************************************/

const IString& MailTagger::tagline(IString aString)
{
  if (aString == _userIni.manualTag()) {
    TAGNotebook tagNotebook(_tagProspects, _filepath, _userIni);
    tagNotebook.layout();
    tagNotebook.setFocus();
    tagNotebook.showModally();
    if(tagNotebook.result() == DID_OK)
      _tagline = tagNotebook.itemText();
    else
      _tagline = "";
  } else {
    if (!aString.lastIndexOf(TAG_EXTENSION))
      aString += TAG_EXTENSION;
    ifstream      in(_filepath+aString); // Cut away the !
    if (!in.good())
      return _tagline;
    unsigned long noOfLines = 0;
    _tagline = IString::lineFrom(in);
    if (_userIni.sequential())
      noOfLines = this->nextInSequence(aString, _tagline.asInt()- (_userIni.wysiwyg() ? 0 : 1));
    else
      noOfLines = (rand() % _tagline.asInt())+1;
    if (in.good()) {
      if (_userIni.wysiwyg())
        _tagline = this->getWysiwyg(in, noOfLines);
      else
        _tagline = this->getOnelines(in, noOfLines);
    }
  } // end if
  // Add the tag to the list of tags.
  _tagList.insertAsLast(new IString(_tagline));

  return _tagline;
}

/*****************************************************************************/
/* Member function  : randomOnelines    (private)                            */
/* Input parameters : ifstream& unsigned long                                */
/* The member function extract a tagline from a onelines tagfile.            */
/* Returncodes : const IString&                                              */
/*****************************************************************************/

const IString& MailTagger::getOnelines(ifstream& in, unsigned long noOfLines)
{
//  if (noOfLines == 1) noOfLines++; // To avoid reading the number.

  while (!in.fail() && !in.eof() && noOfLines--)
    _tagline = IString::lineFrom(in);

  _tagline.change(_userIni.newline(), "\n");
  return _tagline;
}

/*****************************************************************************/
/* Member function  : randomWysiwyg     (private)                            */
/* Input parameters : ifstream& unsigned long                                */
/* The member function extract a tagline from a wysiwyg tagfile.             */
/* Returncodes : const IString&                                              */
/*****************************************************************************/

const IString& MailTagger::getWysiwyg(ifstream& in, unsigned long noOfTags)
{
  _tagline = IString();

  while (!in.fail() && !in.eof() && noOfTags) {
    IString temp(IString::lineFrom(in));
    if (noOfTags == 1)
      _tagline += temp + "\n";
    if (temp == IString())
      noOfTags--;
  }
  _tagline.stripTrailing("\n");

  return _tagline;
}

/*****************************************************************************/
/* Member function  : getRandomTagPreospect   (public)                       */
/* Input parameters :                                                        */
/* The member function returns a random filename of a tag file.              */
/* Returncodes : IString&                                                    */
/*****************************************************************************/

IString MailTagger::getRandomTagProspect()
{
  IString tagProspect;
  unsigned short  randomNum(rand() % _tagProspects.numOfItems()+1);
  // To avoid having the last number, since it is the MANUAL tag - check
  if (randomNum == _tagProspects.numOfItems() && randomNum != 0)
    randomNum--;
  for(unsigned short i(0); i < randomNum; i++)
    tagProspect = *(i != 0 ? _tagProspects.getNext() : _tagProspects.getFirst());
  return tagProspect;
}

/*****************************************************************************/
/* Member function  : nextInSequence    (public)                             */
/* Input parameters : const IString&, unsigned long                          */
/* The member get the next in line. Resets if the number is to high.         */
/* Returncodes : unsigned long                                               */
/*****************************************************************************/

unsigned long MailTagger::nextInSequence(const IString& keyname, long maxNoOfTags)
{
  _inifile.setDefaultApplicationName(APP_SEQUENCE);

  unsigned long lineNo(0);
  if (_inifile.containsKeyName(keyname))
    lineNo = _inifile.integerWithKey(keyname);

  if (lineNo >=  maxNoOfTags)
    lineNo = 0;

  _inifile.addOrReplaceElementWithKey(keyname, ++lineNo);

  return lineNo;
}

//****************************************************************************/
// Procedure name   : MailHandler constructor                                */
// Input parameters : const IString&                                         */
// The member function constructs the object.                                */
//****************************************************************************/

MailHandler::MailHandler(TAGUserIni& userIni, const IString& aString, Boolean debug, Boolean forceX, Boolean usingNetscape)
: _userIni(userIni)
, _changed(false)
, _isValid(true)
, _mailFilename(aString)
, _debug(debug)
, _forceX(forceX)
, _usingNetscape(usingNetscape)
{

  ifstream in(aString);

  if (in.good() || _usingNetscape == true) {
    if (_usingNetscape == false) {
      in.seekg(0, ios::end);
      unsigned long ulFileSize(in.tellg());
      in.seekg(0, ios::beg);
      char          *pszFileBuffer = new char[ulFileSize+1];
      memset(pszFileBuffer, 0, ulFileSize + 1);
      // Read the file & closes the file.
      in.read(pszFileBuffer, ulFileSize);
      in.close();
      _orgMail = pszFileBuffer;
      delete []pszFileBuffer;
    } else
      _orgMail = aString;

    _htmlEnabled = _orgMail.includes(HTML_START);
    Boolean     htmlOffset   (_orgMail.indexOf(HTML_START));
    Boolean     htmlIndex    (_orgMail.indexOf(HTML_START));
    PTagMarker  marker(NULL);
    long  index = 0;
    for (index = _orgMail.indexOf(SEARCH);
         index && (!_htmlEnabled || htmlOffset > index);
         index = _orgMail.indexOf(SEARCH, index))
    {
      for (long wordlen = index;
           !isspace(_orgMail[wordlen]); //Performance consideration.
           wordlen++);
      if (!_htmlEnabled || (_htmlEnabled && htmlOffset > index)) {
        IString temp(_orgMail.subString(index, wordlen-index));
        if (temp.upperCase().isLike(TAG_MASK))
          _tagMarkers.insertAsLast(marker = new TagMarker(temp, index));
        index+=wordlen-index;
      }
      if (_htmlEnabled) { // There should be HTML tags in the file.
        for (Boolean foundMask = false; foundMask == false; htmlIndex++) {
          htmlIndex = _orgMail.indexOf(SEARCH, htmlIndex);
          IString mask(_orgMail.subString(htmlIndex).word(1));
          if (foundMask = mask.upperCase().isLike(TAG_MASK))
            marker->htmlIndex(htmlIndex);
        } // endfor
        htmlIndex++;
      } // endif
    }
  } else
    _isValid = false;
  return;
}

/*****************************************************************************/
/* Member function  : save              (public)                             */
/* Input parameters :                                                        */
/* The member function saves the new mail.                                   */
/* Returncodes : Boolean                                                     */
/*****************************************************************************/

Boolean MailHandler::save()
{
  ofstream out(_mailFilename);
  long     rc = out.good();

  if (!rc) {
    IMessageBox(IWindow::desktopWindow())
               .setTitle("Error")
               .show("Unable to open mail.", IMessageBox::catastrophic);
    return false;
  }
  if (rc) {
    out.write((char *)_newMail, _newMail.size());
    rc = out.good();
  }
  if (!rc) {
    IMessageBox(IWindow::desktopWindow())
               .setTitle("Error")
               .show("Unable to write mail.", IMessageBox::catastrophic);
    return false;
  } else {
    out.close();
    rc = out.good();
  }
  if (!rc) {
    IMessageBox(IWindow::desktopWindow())
               .setTitle("Error")
               .show("Unable to close new mail.", IMessageBox::catastrophic);
    return false;
  } else {
    out.close();
    rc = out.good();
  }
  if (_debug) {
    ofstream outDebug("debug.txt");
    outDebug.write((char *)_newMail, _newMail.size());
    outDebug.close();
  }

  return out.good();
}
