//=-- AFStandard.cpp - Standard Access Filter Plugins ------------------------=
//
//  AFUnEscape Class:
//
//    This class provides the Unescape plugin that is used to remove the 
//  escape codes from the URL that the browser references.
//
//  AFRemoveIndexFilename Class:
//
//    This class defines the RemoveIndexFilename plugin.  Many servers respond 
//  to queries for a directory by responding with an "index" file.  For 
//  example, if the "/" document is requested, typically /index.html is 
//  returned.  Unfortunately, without this plugin, the requests for those two 
//  URLs are seperated.  If "index.html" is specified to this plugin, then it 
//  removes it so the statistics are combined.
//
//  AFMask Class:
//
//    This class masks off accesses by their URL so that people may get better 
//  usage from the free version of MagicStats.
//
//  AFTruncate Class:
//
//    This class chops off the URL and Referrer fields of an access to a
//  specified length, if they are longer.  This prevents huge URLs.
//
//=---------------------------------------------------------------------------=
//  This file is copyright (c) 1997-2000 Chris Lattner
//=---------------------------------------------------------------------------=

#include "LinkedList.h"
#include "AccessFilterPlugin.h"
#include "AccessFormatPlugin.h"
#include "FilterManager.h"
#include "VarTable.h"

//  AFUnEscape Class:
//
//    This class provides the Unescape plugin that is used to remove the 
//  escape codes from the URL that the browser references. Also fixes up
//  the referrer information.
//
class AFUnEscape : public PluginTemplate<AccessFilterPlugin,AFUnEscape> {
public:
  inline AFUnEscape(int &CE) { CE = 0; }

  inline static unsigned int GetCurrentVersion() {
    return 100;                       // Version 1.00
  }

  inline static const char *GetPluginName() {
    return "UnEscape";
  }

  // The Filter method does the actual filtering for each Access.  If it 
  //   returns a true value, the access is discarded.
  virtual int FilterAccess(AccessFormatPlugin &A) {
    String &Referrer = A.GetReferrer();
    DoFilter(A.GetURL());
    DoFilter(Referrer);

    // Some browsers emit "[unknown origin]" instead of -
    if (Referrer.Length() == 16) {
      if (Referrer == String("[unknown origin]"))
        Referrer = "";
    }

    return 0;
  }

  void DoFilter(String &URL) {
    int i, j = 0, Len = URL.Length();
 
    for (i = 0; i < Len; i++) {
      if (URL[i] == '%') {
        URL[i] = 16*String::Hex2Int(URL[i+1]) + String::Hex2Int(URL[i+2]);
        i += 2;
      } else if (URL[i] == '+') {
        URL[j] = ' ';
      } else {
        if (URL[i] == '/' && j > 0 && URL[j-1] == '/') {
          if (j <= 1 || URL[j-2] != ':')  // Don't kill http:// or ftp://
            i++;  // Skip extra /'s
        }
        URL[j] = URL[i];
      }
      
      j++;
    }
    URL.Left(j);    // Trim URL to shortened length...
  }
};

class AFTruncate : public PluginTemplate<AccessFilterPlugin,AFTruncate> {
private:
  int Length;
public:
  inline AFTruncate(int &CE) { CE = 0; Length = 100; }

  inline static unsigned GetCurrentVersion() {
    return 100;                       // Version 1.00
  }

  inline static const char *GetPluginName() {
    return "Truncate";
  }

  virtual int Initialize(const VTListExp *Params) {
    // Get param 1: 0 is the plugin name
    const VTExp *Exp = Params->GetListElement(1);

	if (Exp && Exp->GetExpType() == VTExp::StringTy)
      Length = Exp->GetStringValue().atoi();
    return 0;
  }


  // The Filter method does the actual filtering for each Access.  If it 
  //   returns a true value, the access is discarded.
  virtual int FilterAccess(AccessFormatPlugin &A) {
    A.GetURL().Left(Length);
    A.GetReferrer().Left(Length);
    return 0;
  }
};


//  AFRemoveIndexFilename Class:
//
//    This class defines the RemoveIndexFilename plugin.  Many servers respond 
//  to queries for a directory by responding with an "index" file.  For 
//  example, if the "/" document is requested, typically /index.html is 
//  returned.  Unfortunately, without this plugin, the requests for those two 
//  URLs are seperated.  If "index.html" is specified to this plugin, then it 
//  removes it so the statistics are combined.
//
class AFRemoveIndexFilename : public 
                     PluginTemplate<AccessFilterPlugin,AFRemoveIndexFilename> {
public:
  inline AFRemoveIndexFilename(int &CE) { CE = 0; }

  inline static unsigned int GetCurrentVersion() {
    return 100;                       // Version 1.00
  }

  inline static const char *GetPluginName() {
    return "RemoveIndexFilename";
  }

  virtual int Initialize(const VTListExp *Params) {
    const VTExp *Exp;
    int Index = 1;    // 0 = PluginName

    while ((Exp = Params->GetListElement(Index++))) {
      if (Exp->GetExpType() != VTExp::StringTy) {
        cout << "RemoveIndexFilename AccessFilter does not recognize "
                "argument '" << Exp << "'";
        if (Exp->GetLineNo() != -1) 
          cout << " from line #" << Exp->GetLineNo();
        cout << endl;
      } else {
        IndexList.AddToTail(Exp->GetStringValue());
      }
    }
    return 0;
  }

  virtual int FilterAccess(AccessFormatPlugin &A) {
    DoFilter(A.GetURL());
    if (A.GetReferrer().Length() != 0)
      DoFilter(A.GetReferrer());  // Careful not to turn "" into "/"
    return 0;
  }

  void DoFilter(String &URL) {
    LLLink<String> *Node = IndexList.GetHead();
    while (Node) {
      if (Node->Data == URL) {
        URL = "/";
        return;
      }

      String Temp(URL);
      Temp.Right(Node->Data.Length());
      if (Temp == Node->Data) {
        URL.Left(URL.Length()-Node->Data.Length());
      }

      Node = Node->GetNext();
    }

    if (URL.Length() == 0) 
      URL = "/";
  }

private:
  LinkedList<String> IndexList;
};


//  AFMask Class:
//
//    This class masks off accesses by their URL so that people may get better 
//  usage from the free version of MagicStats.
//
class AFMask : public PluginTemplate<AccessFilterPlugin,AFMask> {
public:
  inline AFMask(int &CE) { CE = 0; }

  inline static unsigned int GetCurrentVersion() {
    return 100;                       // Version 1.00
  }

  inline static const char *GetPluginName() {
    return "Mask";
  }

  virtual int Initialize(const VTListExp *Params) {
    const VTExp *I, *E;

    if (Params->GetNumElements() != 3) {
      cout << "'Mask' AccessFilter expects two parameters!\n";
      return 1;
    }

    // Get the include and exclude portions of the parameter...
    I = Params->GetListElement(1);  // Param 1 (0 = PluginName)
    E = Params->GetListElement(2);  // Param 2

    if (!I || !E || 
        I->GetExpType() != VTExp::StringTy ||
        E->GetExpType() != VTExp::StringTy) {
      cout << "Mask AccessFilter expects two string parameters";

      if (I->GetLineNo() != -1) 
        cout << " from line #" << I->GetLineNo();
      cout << "!\n";
      return 1;
    }

    Mask = FilterManager::GetFilter(I->GetStringValue(), E->GetStringValue());
    return 0;
  }

  virtual int FilterAccess(AccessFormatPlugin &A) {
    if (Mask.Match(A))
      return 0;
    return 1;
  }

private:
  FilterHandle Mask;
};

// INIT_PLUGIN includes the plugins into the internal list of available ones
INIT_PLUGIN(AFUnEscape);
INIT_PLUGIN(AFTruncate);
INIT_PLUGIN(AFRemoveIndexFilename);
INIT_PLUGIN(AFMask);
