// --------------------------------------------------------------------
//        S c a n d i r . c p p 
//                                                                     
//        Fido messages tracker                            
//        Scan dir class.
// --------------------------------------------------------------------
//        Copyright (c) 1998-2000 by Fyodor Ustinov                         
//                                FIDONet 2:5020/79                      
//                                                                     
//        All rights reserved.                                         
// --------------------------------------------------------------------

#ifndef __GNUC__
#include <io.h>
#include <direct.h>
#else
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <errno.h>
#endif                                                                                                          

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <ctype.h>

#include "constant.hpp"
#include "vars.hpp"
#include "nodelist.hpp"
#include "utils.hpp"
#include "msg.hpp"
#include "msgbase.hpp"
#include "age.hpp"
#include "scandir.hpp"
#include "attach.hpp"
#include "aka.hpp"
#include "wildmat.hpp"
#include "script.hpp"
#include <smapi/msgapi.h>
#include <smapi/progprot.h>

// ---------------------------------------------------------
// ---------------------------------------------------------

Action::Action() {
   _Act = ACT_ERROR;
   _TplName = NULL;
   _OutDir = NULL;
   _Base = NULL;
   _Mask = NULL;
   _Tpl = NULL;
   _Times.Clear();
   sd = NULL;
   _f.Clean();
}
// --------------------------------------------------------------------

Action::~Action() {
   CHP = 1;
   _Times.Clear();
   if (_TplName != NULL) free(_TplName);
   if (_OutDir != NULL) free(_OutDir);
   if (_Base != NULL) delete _Base;
   if (_Mask != NULL) delete _Mask;
   if (_Tpl != NULL) delete _Tpl;
}

// --------------------------------------------------------------------

#include "scd_do.cpp"

// --------------------------------------------------------------------

void Action::Print(void) {
char *tmt2;
char Buff[1024];
IndBiList<tTimes>::ElemPtr tmt;

   if (LogLevel < 5) return;

   CHP = 22;
   switch(_Act) {
      case ACT_ERROR:
         tmt2 = "Error!";
         break;
      case ACT_IGNORE:
         tmt2 = "Ignore";
         break;
      default:
         tmt2 = "Unknown";
         break;
   }
   sprintf(Buff,"Action: %s",tmt2);
   Log.Level(LOGD) << Buff << EOL;
   Log.Level(LOGD) << "--" << EOL;
   if (_Times.IsEmpty()) {
      Log.Level(LOGD) << "Always";
   } else {
      for (tmt = _Times.GetFirst(); tmt != NULL; tmt++) {
         tmt->Print();
         Log.Level(LOGD) << " ";
      }   
   }
   Log.Level(LOGD) << EOL;
   Log.Level(LOGD) << "--" << EOL;
}

// ---------------------------------------------------------
// ---------------------------------------------------------

void DoList::AddAction(Action &a) {
   CHP = 30;
   Actions.AddToEnd(&a);
}

// ---------------------------------------------------------

void DoList::AddMask(Mask &m) {
   CHP = 31;
   Masks.AddToEnd(&m);
}

// ---------------------------------------------------------

DoList::DoList() {
   CHP = 32;
}

// ---------------------------------------------------------

DoList::~DoList() {
   CHP = 33;
   Actions.Clear();
   Masks.Clear();
}

// ---------------------------------------------------------

int DoList::Do(MSGBASE &b, cMSG &m) {
IndBiList<Action>::ElemPtr tmt;
 
   CHP = 34;
   for (tmt = Actions.GetFirst(); tmt != NULL; tmt++) {
      if (InTime(tmt->_Times.GetFirst())) {
         if (!tmt->Do(b,m)) {
            return FALSE;
         }
      }
   }   
   return TRUE;
}

// ---------------------------------------------------------

MaskType DoList::InMask(cMSG &m) {
IndBiList<Mask>::ElemPtr tmt;
int SkipFlag;

   CHP = 35;
   SkipFlag = FALSE;
   tmt = Masks.GetFirst();

   while (tmt != NULL) {
      if (*tmt == m) {
         SkipFlag = (tmt->_Type == MASK_SKIP ? TRUE : FALSE);
         tmt++;
         while (tmt != NULL && tmt->_Type == MASK_ADD && *tmt == m) {
            tmt++;
         }
         if (tmt == NULL || tmt->_Type != MASK_ADD) {
            return (SkipFlag ? MASK_SKIP : MASK_NORMAL);
         }
         while (tmt != NULL && tmt->_Type == MASK_ADD) {
            tmt++;
         }
      } else {
         tmt++;
         while (tmt != NULL && tmt->_Type == MASK_ADD) {
            tmt++;
         }
      }
   }
   return MASK_ERROR;
}

// ---------------------------------------------------------

void DoList::Print(void) {
IndBiList<Mask>::ElemPtr tmt;
IndBiList<Action>::ElemPtr dtmt;

   if (LogLevel < 5) return;
   CHP = 36;
   for (tmt = Masks.GetFirst(); tmt != NULL; tmt++) {
      tmt->Print();
      Log.Level(LOGD) << EOL;
   }
   for (dtmt = Actions.GetFirst(); dtmt != NULL; dtmt++) {
      dtmt->Print();
   }
}

// ---------------------------------------------------------
// ---------------------------------------------------------

ScanDir::ScanDir() {
   CHP = 37;
   _Renumber = FALSE;
   _Unpack   = FALSE;
   _Fresh    = FALSE;
   _MaxAge   = 0;
   _MaxAttachSize = 0;
   _LoopStr = NULL;
   _FlagFile = NULL;
   _FileInbound = NULL;
   _ScriptBefore = NULL;
   _ScriptAfter = NULL;
   _Base = NULL;
}

// ---------------------------------------------------------

ScanDir::~ScanDir() {
   CHP = 38;
   _DoLists.Clear();
   _Times.Clear();
   if (_LoopStr != NULL) {
      free(_LoopStr);
      _LoopStr = NULL;
   }
   if (_FileInbound != NULL) {
      free(_FileInbound);
      _FileInbound = NULL;
   }
   if (_FlagFile != NULL) {
      free(_FlagFile);
      _FlagFile = NULL;
   }
   if (_Base != NULL) {
      delete _Base;
      _Base = NULL;
   }
   if (_ScriptBefore != NULL) {
      free(_ScriptBefore);
      _ScriptBefore = NULL;
   }
   if (_ScriptAfter != NULL) {
      free(_ScriptAfter);
      _ScriptAfter = NULL;
   }
}

// ---------------------------------------------------------

int ScanDir::Flagged(void) {
   if (_FlagFile == NULL) return TRUE;
   if (_FlagFile[0] == '!') {
      return (access(&_FlagFile[1],F_OK) != 0);
   } else {
      if (_FlagFile[0] == '#') {
         return (access(&_FlagFile[1],F_OK) == 0);
      } else {
         return (access(_FlagFile,F_OK) == 0);
      }
   }
}

// ---------------------------------------------------------

int ScanDir::Execute(MSGBASE &b, cMSG &m) {
IndBiList<DoList>::ElemPtr tmt;
MaskType tmtm;
   CHP = 67;
   PrepareMsgForScript(m);
   for (tmt = _DoLists.GetFirst(); tmt != NULL; tmt++) {
      CHP = 68;
      if ((tmtm = tmt->InMask(m)) != MASK_ERROR) {
         CHP = 69;
         if (!tmt->Do(b,m)) {
            CHP = 70;
            return(FALSE);
         }
         CHP = 71;
         if (tmtm != MASK_SKIP) {
            break;
         }
      }
      CHP = 72;
   }
   return TRUE;
}

// ---------------------------------------------------------

int ScanDir::Do(void) {
cMSG m;
MSGBASE *s;

   CHP = 39;
   if (_Base == NULL) return TRUE;
   if (_Base->Open() != TRUE) {
      return FALSE;
   }
   if (!Flagged()) {
      Log.Level(LOGI) << "Skip base '" << _Base->BaseName() << "' by Flag." << EOL;
      _Base->Close();
      return TRUE;
   }
   if (!InTime(_Times.GetFirst())) {
      Log.Level(LOGI) << "Skip base '" << _Base->BaseName() << "' by Time." << EOL;
      _Base->Close();
      return TRUE;
   }
   Log.Level(LOGD) << EOL;
   Log.Level(LOGI) << "Scanning message base " << _Base->BaseName() << EOL;
   Log.Level(LOGD) << "--------------------------------------" << EOL;
   if (_ScriptBefore != NULL) {
      switch (DoSomeWordRc(_ScriptBefore)) {
         case SS_OK:  // do nothing, all is OK
         case SS_NOTDEF: break; // the same
         case SS_ERROR: _Base->Close();
                        Log.Level(LOGE) << "Stop ScanDir: by scritp '" << _ScriptBefore << "' error." << EOL;
                        return FALSE;
         case SS_FALSE: _Base->Close();
                       Log.Level(LOGI) << "Leave FTrack: by script '" << _ScriptBefore << "' return." << EOL;
                       return TRUE;
         default:     break;
      }
   }
   if (_Unpack) {
      s = TempMail;
      Log.Level(LOGI) << "Unpack outbound to " << _Base->BaseName() << EOL;
      TempMail = _Base;
      if (!DoRepack()) {
         Log.Level(LOGE) << "Unable unpack outbound" << EOL;
         TempMail = s;
         _Base->Close();
//         DoSomeWord("ErrorInScandir");
         return FALSE;
      }
      TempMail = s;
   }

   if (!_Base->Rewind()) {
      Log.Level(LOGD) << "Base is empty." << EOL;
      goto DoneOK;
   }
   CHP = 64;
   if (!_DoLists.IsEmpty()) {
      do {
         Log.Level(LOGD) << EOL;
         Log.Level(LOGD) << "Current message: " << _Base->MessageName() << EOL;
         if (!_Base->ReadMsg(m)) {
            if (errno == EACCES) {
               Log.Level(LOGE) << "Message " << _Base->MessageName() << " Locked by another process." << EOL;
               errno = 0;
            } else {
               CHP = 65;
               if (errno == ENOENT) { 
                  _Base->Close();
               } else {
                  DoBadMsg(*_Base);
                  CHP = 66;
               }
            }
         } else {
            if (LogLevel >= 5) {
               m.Print();
            }
            if (Execute(*_Base,m) != TRUE) {
               _Base->Close();
               return FALSE;
            }
            CHP = 73;
         }
         CHP = 74;
      } while(_Base->Next());
      Log.Level(LOGD) << "No more messages." << EOL;
   } else {
      Log.Level(LOGD) << "No masks in scandir." << EOL;
   }
      
   if (_FlagFile != NULL && _FlagFile[0] == '#') {
      unlink(&_FlagFile[1]);
   }

   if (_Renumber) {
      Log.Level(LOGI) << "Renumber message base " << _Base->BaseName() << EOL;
      if (!_Base->Renumber()) {
         _Base->Close();
         return FALSE;
      }
   }

DoneOK:   
   if (_ScriptAfter != NULL) {
      switch (DoSomeWordRc(_ScriptAfter)) {
         case SS_OK:  // do nothing, all is OK
         case SS_NOTDEF: break; // the same
         case SS_ERROR: _Base->Close();
                        Log.Level(LOGE) << "Stop ScanDir: by script '" << _ScriptAfter << "' error." << EOL;
                        return FALSE;
         case SS_FALSE: _Base->Close();
                       Log.Level(LOGI) << "Leave ScanDir: by script '" << _ScriptAfter << "' return." << EOL;
                       return TRUE;
         default:     break;
      }
   }

   _Base->Close();
   CHP = 75;
   return TRUE;
}

// ---------------------------------------------------------

int ScanDir::DoWithRoute(MSGBASE &b, cMSG &m) {
   CHP = 390001;
   if (_Base != NULL) {
      Log.Level(LOGE) << "Internal error. ScanDir::DoWithRoute _Base != NULL" << EOL;
      return FALSE;
   }
   Log.Level(LOGD) << EOL;
   Log.Level(LOGD) << "----- ScanDir::DoWithRoute -----" << EOL;
   if (!Flagged()) {
      Log.Level(LOGI) << "Skip this base by flag state." << EOL;
      return TRUE;
   }
   CHP = 390010;
   _Base = &b;
   if (!_DoLists.IsEmpty()) {
      Log.Level(LOGD) << EOL;
      if (LogLevel >= 5) {
         m.Print();
      }
      CHP = 390020;
      if (Execute(*_Base,m) != TRUE) {
         _Base = NULL;
         return FALSE;
      }
      CHP = 390030;
   } else {
      Log.Level(LOGD) << "No masks in scandir." << EOL;
   }
   _Base = NULL;
   if (_FlagFile != NULL && _FlagFile[0] == '#') {
      unlink(&_FlagFile[1]);
   }
   CHP = 390040;
   return TRUE;
}

// ---------------------------------------------------------

void ScanDir::Print(void) {
IndBiList<tTimes>::ElemPtr tmt;
IndBiList<DoList>::ElemPtr dtmt;

   if (LogLevel < 5) return;
   Log.Level(LOGD) << "------------------------------------" << EOL;
   Log.Level(LOGD) << "ScanDir.Print()" << EOL;
   Log.Level(LOGD) << "Base: '" << ((_Base != NULL) ? _Base->BaseName() : "--NONE--") << "'" << EOL;
   Log.Level(LOGD) << "Renumber: " << ((_Renumber) ? "YES" : "NO") << EOL;
   Log.Level(LOGD) << "Unpack: " << ((_Unpack) ? "YES" : "NO") << EOL;
   Log.Level(LOGD) << "Fresh: " << ((_Fresh) ? "YES" : "NO") << EOL;
   Log.Level(LOGD) << "MaxAge: " << _MaxAge << EOL;
   Log.Level(LOGD) << "MaxAttachSize: " << _MaxAttachSize << EOL;
   Log.Level(LOGD) << "LoopStr: '" << ((LoopStr() != NULL) ? LoopStr() : "--NONE--") << "'" << EOL;
   Log.Level(LOGD) << "FileInbound: '" << ((_FileInbound != NULL) ? _FileInbound : "--NONE--") << "'" << EOL;
   Log.Level(LOGD) << "FlagFile: '" << ((_FlagFile != NULL) ? _FlagFile : "--NONE--") << "'" << EOL;
   Log.Level(LOGD) << "ScriptBefore: '" << ((_ScriptBefore != NULL) ? _ScriptBefore : "--NONE--") << "'" << EOL;
   Log.Level(LOGD) << "ScriptAfter: '" << ((_ScriptAfter != NULL) ? _ScriptAfter : "--NONE--") << "'" << EOL;
   Log.Level(LOGD) << "Times: ";
   if (_Times.IsEmpty()) {
      Log.Level(LOGD) << "Always";
   } else {
      for (tmt = _Times.GetFirst(); tmt != NULL; tmt++) {
         tmt->Print();
         Log.Level(LOGD) << " ";
      }   
   }
   Log.Level(LOGD) << EOL;
   Log.Level(LOGD) << "--" << EOL;
   if (_DoLists.IsEmpty()) {
      Log.Level(LOGD) << "ScanDir without masks and actions" << EOL;
   } else {
      for (dtmt = _DoLists.GetFirst(); dtmt != NULL; dtmt++) {
         dtmt->Print();
      }   
   }
   Log.Level(LOGD) << "------------------------------------" << EOL;
}

// ---------------------------------------------------------

IndBiList<ScanDir> ScanDirs;

void PrintScanDirs(void) {
IndBiList<ScanDir>::ElemPtr tmt;

   CHP = 41;
   for (tmt = ScanDirs.GetFirst(); tmt != NULL; tmt++) {
      tmt->Print();
   }   
}

void DoScanDirs(void) {
IndBiList<ScanDir>::ElemPtr tmt;

   CHP = 42;
   for (tmt = ScanDirs.GetFirst(); tmt != NULL; tmt++) {
      CHP = 43;
      if (!tmt->Do()) {
         return;
      }
      CHP = 44;
   }   
}

// --------------------------------------------------------------------


syntax highlighted by Code2HTML, v. 0.9.1