/*++

   Copyright    (c)    2001    Microsoft Corporation

   Module  Name :
        MappingPage.cpp

   Abstract:
        App config mapping page implementation

   Author:
        Sergei Antonov (sergeia)

   Project:
        Internet Services Manager

   Revision History:

--*/
#include "stdafx.h"
#include "MappingPage.h"

enum 
{
   COL_EXTENSION = 0,
   COL_PATH,
   COL_EXCLUSIONS
};

#define EXT_WIDTH          58
#define PATH_WIDTH         204
#define EXCLUSIONS_WIDTH   72

LRESULT
CAppMappingPage::OnInitDialog(HWND hDlg, LPARAM lParam)
{
   CString str;
   CError err;

   DoDataExchange();

   DWORD dwStyle = m_list.GetExtendedListViewStyle();
   m_list.SetExtendedListViewStyle(
      dwStyle | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_HEADERDRAGDROP | LVS_EX_LABELTIP);

   str.LoadString(_Module.GetResourceInstance(), IDS_EXTENSION);
   m_list.InsertColumn(COL_EXTENSION, str, LVCFMT_LEFT, EXT_WIDTH, 0);
   str.LoadString(_Module.GetResourceInstance(), IDS_EXECUTABLE_PATH);
   m_list.InsertColumn(COL_PATH, str, LVCFMT_LEFT, PATH_WIDTH, 1);
   str.LoadString(_Module.GetResourceInstance(), IDS_VERBS);
   m_list.InsertColumn(COL_EXCLUSIONS, str, LVCFMT_LEFT, EXCLUSIONS_WIDTH, 2);

   ASSERT(m_pData != NULL);
   CMappings::iterator it;
   int idx = 0;
   CString all_verbs;
   VERIFY(all_verbs.LoadString(_Module.GetResourceInstance(), IDS_ALL));
   for (it = m_pData->m_Mappings.begin(); it != m_pData->m_Mappings.end(); it++, idx++)
   {
      Mapping map = (*it).second;
      VERIFY(-1 != m_list.InsertItem(idx, map.ext));
      VERIFY(m_list.SetItemText(idx, COL_PATH, map.path));
      VERIFY(m_list.SetItemData(idx, map.flags));
      VERIFY(m_list.SetItemText(idx, COL_EXCLUSIONS, 
         map.verbs.IsEmpty() ? all_verbs : map.verbs));
   }

   CString remainder;
   CMetabasePath::GetRootPath(m_pData->m_MetaPath, str, &remainder);
   ::EnableWindow(GetDlgItem(IDC_CACHE_ISAPI), remainder.IsEmpty());

   int count = m_list.GetItemCount();
   if (count > 0)
   {
      m_list.SelectItem(0);
   }
   ::EnableWindow(GetDlgItem(IDC_EDIT), count > 0);
   ::EnableWindow(GetDlgItem(IDC_REMOVE), count > 0);

   return 0;
}

void 
CAppMappingPage::OnAdd(UINT nCode, UINT nID, HWND hWnd)
{
   CEditMap dlg;
   dlg.m_new = TRUE;
   dlg.m_flags = MD_SCRIPTMAPFLAG_SCRIPT | MD_SCRIPTMAPFLAG_CHECK_PATH_INFO;
   dlg.m_pData = m_pData;
   if (dlg.DoModal() == IDOK)
   {
      CString all_verbs;
      VERIFY(all_verbs.LoadString(_Module.GetResourceInstance(), IDS_ALL));
      
      Mapping map;
      map.ext = dlg.m_ext;
      map.path = dlg.m_exec;
      map.verbs = dlg.m_verbs;
      map.flags = dlg.m_flags;
      m_pData->m_Mappings.insert(CMappings::value_type(map.ext, map));

      int count = m_list.GetItemCount();
      VERIFY(-1 != m_list.InsertItem(count, map.ext));
      VERIFY(m_list.SetItemText(count, COL_PATH, dlg.m_exec));
      VERIFY(m_list.SetItemData(count, dlg.m_flags));
      VERIFY(m_list.SetItemText(count, COL_EXCLUSIONS, 
         dlg.m_verbs[0] == 0 ? all_verbs : dlg.m_verbs));
      m_list.SelectItem(count);
      SET_MODIFIED(TRUE);
      ::EnableWindow(GetDlgItem(IDC_REMOVE), TRUE);
      ::EnableWindow(GetDlgItem(IDC_EDIT), TRUE);
   }
}

void 
CAppMappingPage::OnEdit(UINT nCode, UINT nID, HWND hWnd)
{
   int idx = m_list.GetSelectedIndex();
   if (idx != -1)
   {
       CEditMap dlg;
       dlg.m_new = FALSE;
       dlg.m_pData = m_pData;
   
       TCHAR buf[MAX_PATH];
       VERIFY(0 != m_list.GetItemText(idx, 0, buf, MAX_PATH));
       CMappings::iterator it = m_pData->m_Mappings.find(buf);
       ASSERT(it != m_pData->m_Mappings.end());
       StrCpyN(dlg.m_ext, buf, MAX_PATH);
       StrCpyN(dlg.m_exec, (*it).second.path, MAX_PATH);
       StrCpyN(dlg.m_verbs, (*it).second.verbs, MAX_PATH);
       dlg.m_flags = (*it).second.flags;

       if (dlg.DoModal() == IDOK)
       {
          CString all_verbs;
          VERIFY(all_verbs.LoadString(_Module.GetResourceInstance(), IDS_ALL));
          (*it).second.path = dlg.m_exec;
          (*it).second.verbs = dlg.m_verbs;
          (*it).second.flags = dlg.m_flags;
          VERIFY(m_list.SetItemText(idx, COL_PATH, dlg.m_exec));
          VERIFY(m_list.SetItemData(idx, dlg.m_flags));
          VERIFY(m_list.SetItemText(idx, COL_EXCLUSIONS, 
             dlg.m_verbs[0] == 0 ? all_verbs : dlg.m_verbs));
          SET_MODIFIED(TRUE);
       }
   }
}

void 
CAppMappingPage::OnRemove(UINT nCode, UINT nID, HWND hWnd)
{
   int idx = m_list.GetSelectedIndex();
   if (idx != LB_ERR)
   {
	   CString msg, caption;
	   msg.LoadString(_Module.GetResourceInstance(), IDS_CONFIRM_REMOVE_MAP);
	   caption.LoadString(_Module.GetResourceInstance(), IDS_SHEET_TITLE);
	   if (MessageBox(msg, caption, MB_ICONQUESTION|MB_YESNO) == IDYES)
	   {
	      int i = m_list.GetSelectedIndex();
	      int count;
	      TCHAR buf[MAX_PATH];
	      VERIFY(0 != m_list.GetItemText(i, 0, buf, MAX_PATH));
	      m_pData->m_Mappings.erase(buf);
	      m_list.DeleteItem(i);
	      SET_MODIFIED(TRUE);
	      if ((count = m_list.GetItemCount()) > 0)
	      {
	         if (i >= count)
	            i = count - 1;
	         m_list.SelectItem(i);
	      }
	      else
	      {
	         ::EnableWindow(GetDlgItem(IDC_REMOVE), FALSE);
	         ::EnableWindow(GetDlgItem(IDC_EDIT), FALSE);
	         ::SetFocus(GetDlgItem(IDC_ADD));
	      }
	   }
	}
}

void 
CAppMappingPage::OnCacheISAPI(UINT nCode, UINT nID, HWND hWnd)
{
   SET_MODIFIED(TRUE);
}

BOOL
CAppMappingPage::OnKillActive()
{
   DoDataExchange(TRUE);
   return SUCCEEDED(m_pData->Save());
}

LRESULT
CAppMappingPage::OnDblClickList(LPNMHDR pHdr)
{
    OnEdit(BN_CLICKED, pHdr->idFrom, pHdr->hwndFrom);
    return TRUE;
}

void
CAppMappingPage::OnHelp()
{
    WinHelp(m_pData->m_HelpPath, HELP_CONTEXT, CAppMappingPage::IDD + WINHELP_NUMBER_BASE);
}

//---------------- CEditMap dialog --------------------------

#define CHECK_VERBS()\
   m_bVerbsValid = \
      (m_verbs_index > 0 && lstrlen(m_verbs) != 0) || (m_verbs_index == 0)
_inline BOOL CheckExt(LPCTSTR ext)
{
    return (ext[0] != 0) && (ext[0] == _T('.'));
}
#define CHECK_EXT()\
    m_bExtValid = CheckExt(m_ext)
_inline BOOL ExecValid(LPCTSTR exec)
{
    return lstrlen(exec) != 0 && !PathIsUNC(exec);
}
#define CHECK_EXEC(buf)\
   m_bExecValid = ExecValid(buf)
#define ENABLE_OK()\
   ::EnableWindow(GetDlgItem(IDOK), m_bExtValid && m_bExecValid && m_bVerbsValid)
#define TYPE_BIT_SET(t,b)\
	((t)&(b)) != 0

LRESULT
CEditMap::OnInitDialog(HWND hDlg, LPARAM lParam)
{
   if (!m_new && m_ext[0] != 0)
   {
      if (m_ext[0] == _T('*') && m_ext[1] == 0)
      {
          m_prev_ext[0] = _T('.');
          m_prev_ext[1] = _T('*');
          m_prev_ext[2] = 0;
          StrCpy(m_ext, m_prev_ext);
      }
      else
      {
         StrCpyN(m_prev_ext, m_ext, sizeof(m_ext) - 1);
      }
   }
   DWORD style = FC_COMMANDLINE;
   if (m_pData->IsLocal())
   {
       style |= FC_PATH_CHECK|FC_DEFAULT_READ|FC_AUTOCOMPLETION;
   }
   ::EnableWindow(GetDlgItem(IDC_BROWSE), m_pData->IsLocal());

   m_FileChooser.Init(this, style, IDC_EXECUTABLE, IDC_BROWSE);
   m_FileChooser.AddExtension(_Module.GetResourceInstance(),
      IDS_EXECUTABLE_FILES, IDS_EXECUTABLE_EXT);
   m_FileChooser.AddExtension(_Module.GetResourceInstance(),
      IDS_DLL_FILES, IDS_DLL_EXT);
   m_FileChooser.AddExtension(_Module.GetResourceInstance(),
      IDS_ALL_FILES, IDS_ALL_EXT);
   m_FileChooser.SetPath(m_exec);

   m_verbs_index = lstrlen(m_verbs) == 0 ? 0 : 1;
   ::EnableWindow(GetDlgItem(IDC_VERBS), m_verbs_index > 0);

   m_script_engine = ((m_flags & MD_SCRIPTMAPFLAG_SCRIPT) != 0);
   m_file_exists = ((m_flags & MD_SCRIPTMAPFLAG_CHECK_PATH_INFO) != 0);
   ::EnableWindow(GetDlgItem(IDC_EXTENSION), m_new);

   CHECK_EXT();
   CHECK_EXEC(m_exec);
   CHECK_VERBS();
   ENABLE_OK();

   DoDataExchange();

   return FALSE;
}

void
CEditMap::OnVerbs(UINT nCode, UINT nID, HWND hWnd)
{
   DoDataExchange(TRUE);
   ::EnableWindow(GetDlgItem(IDC_VERBS), m_verbs_index > 0);
   CHECK_VERBS();
   ENABLE_OK();
}

void
CEditMap::OnVerbsChanged(UINT nCode, UINT nID, HWND hWnd)
{
   DoDataExchange(TRUE, IDC_VERBS);
   CHECK_VERBS();
   ENABLE_OK();
}

void
CEditMap::OnHelp(UINT nCode, UINT nID, HWND hWnd)
{
	WinHelp(m_pData->m_HelpPath, HELP_CONTEXT, CEditMap::IDD + WINHELP_NUMBER_BASE);
}

#define BAIL_WITH_MESSAGE(msg, focus)\
   idmsg = msg;\
   idfocus = focus;\
   break

void
CEditMap::OnOK(UINT nCode, UINT nID, HWND hWnd)
{
   UINT idmsg = 0, idfocus = 0;
   DoDataExchange(TRUE);

   // When All is selected, verbs string is empty
   do
   {
      if (m_verbs_index == 0)
      {
         m_verbs[0] = 0;
      }
      else if (m_verbs[0] == 0)
      {
         BAIL_WITH_MESSAGE(IDS_ERR_NOVERBS, IDC_VERBS);
      }

      CString ext = m_ext;
      if (ext.ReverseFind(_T('.')) > 0)
      {
         BAIL_WITH_MESSAGE(IDS_ERR_BADEXT, IDC_EXTENSION);
      }
      // Ext should be unique, if new or changed
      if (  (m_new || StrCmpI(m_prev_ext, ext) != 0)
         && m_pData->m_Mappings.find(ext) != m_pData->m_Mappings.end()
         )
      {
         BAIL_WITH_MESSAGE(IDS_ERR_USEDEXT, IDC_EXTENSION);
      }
      if (ext.GetAt(0) == _T('*'))
         ext.erase(1);
      else if (ext.Compare(_T(".*")) == 0)
         ext = _T("*");
      else if (ext.GetAt(0) != _T('.'))
         ext = _T('.') + ext;

      StrCpyN(m_ext, ext, sizeof(m_ext)-1);

      CString buf;
      if (FC_SUCCESS != m_FileChooser.GetFileName(buf))
      {
         BAIL_WITH_MESSAGE(IDS_ERR_BADEXECFORMAT, IDC_EXECUTABLE);
      }

      int pos;
      CString path;
      if (buf[0] == _T('\"'))
      {
         if ((pos = buf.find_last_of(_T('\"'))) != CString::npos)
         {
            path = buf.substr(1, pos);
         }
         else
         {
            BAIL_WITH_MESSAGE(IDS_ERR_BADEXECFORMAT, IDC_EXECUTABLE);
         }
      }
      else if (CString::npos != (pos = buf.find(_T(' '))))
      {
         // in case there are parameters after the file name, just take it to the first space
         path = buf.substr(0, --pos);
      }
      if (PathIsUNC(path))
      {
         BAIL_WITH_MESSAGE(IDS_ERR_NOUNC, IDC_EXECUTABLE);
      }
      // perform extra local-machine tests
      if (m_pData->IsLocal())
      {
         // if local, the drive can't be redirected
         // test the drive and only accept valid, non-remote drives
         if (PathIsNetworkPath(path))
         {
            BAIL_WITH_MESSAGE(IDS_ERR_NOREMOTE, IDC_EXECUTABLE);
         }
         // check that the file exists
         if (PathIsDirectory(path))
         {
            BAIL_WITH_MESSAGE(IDS_ERR_FILENOTEXISTS, IDC_EXECUTABLE);
         }
      }
      m_flags = 0;
      if (m_script_engine)
         m_flags |= MD_SCRIPTMAPFLAG_SCRIPT;
      if (m_file_exists)
         m_flags |= MD_SCRIPTMAPFLAG_CHECK_PATH_INFO;
      StrCpy(m_exec, buf);
   }
   while (FALSE);

   if (idmsg != 0)
   {
      CString msg;
      CString cap;
      msg.LoadString(_Module.GetResourceInstance(), idmsg);
      cap.LoadString(_Module.GetResourceInstance(), IDS_SHEET_TITLE);
      MessageBox(msg, cap);
      ::SetFocus(GetDlgItem(idfocus));
      SendDlgItemMessage(idfocus, EM_SETSEL, 0, -1);
      return;
   }
   EndDialog(nID);
}

void
CEditMap::OnCancel(UINT nCode, UINT nID, HWND hWnd)
{
   EndDialog(nID);
}

void
CEditMap::OnExtChanged(UINT nCode, UINT nID, HWND hWnd)
{
   DoDataExchange(TRUE, IDC_EXTENSION);
   CHECK_EXT();
   if (m_bExtValid)
   {
	   int l = lstrlen(m_ext);
	   int i = 0;
	   while (i < l)
	   {
		   TCHAR c = m_ext[i];
		   UINT type = PathGetCharType(c);
		   if (		TYPE_BIT_SET(type, GCT_INVALID)
//			   ||	TYPE_BIT_SET(type, GCT_WILD)
			   ||	TYPE_BIT_SET(type, GCT_SEPARATOR)
			   ||	(TYPE_BIT_SET(type, GCT_LFNCHAR) && !TYPE_BIT_SET(type, GCT_SHORTCHAR))
			   ||	c == _T('/') || c == _T('<') || c == _T('|') || c == _T('>') || c == _T('?')
			   )
		   {
			   m_bExtValid = FALSE;
			   break;
		   }
		   i++;
	   }
	   if (!m_bExtValid)
	   {
			SendDlgItemMessage(IDC_EXTENSION, EM_SETSEL, i, l);
	   }
   }
   ENABLE_OK();
}

void
CEditMap::OnExecChanged(UINT nCode, UINT nID, HWND hWnd)
{
	m_FileChooser.OnEditChange();
	CString str;
	if (FC_SUCCESS == m_FileChooser.GetFileName(str))
	{
		TCHAR buff[MAX_PATH];
		StrCpyN(buff, str, MAX_PATH - 1);
		PathRemoveArgs(buff);
		CHECK_EXEC(buff);
		ENABLE_OK();
	}
}