text: /*++
  
     Copyright    (c)    1994-1998    Microsoft Corporation
  
     Module  Name :
  
          errors.cpp
  
     Abstract:
  
          HTTP errors property page
  
     Author:
  
          Ronald Meijer (ronaldm)
  
     Project:
  
          Internet Services Manager
  
     Revision History:
  
  --*/
  
  //
  // Include Files
  //
  #include "stdafx.h"
  #include "common.h"
  #include "inetprop.h"
  #include "InetMgrapp.h"
  #include "shts.h"
  #include "w3sht.h"
  #include "resource.h"
  #include "fltdlg.h"
  #include "errors.h"
  #include "errordlg.h"
  
  #ifdef _DEBUG
  #define new DEBUG_NEW
  #undef THIS_FILE
  static char THIS_FILE[] = __FILE__;
  #endif
  
  //
  // CCustomErrorsListBox : a listbox of CCustomError objects
  //
  // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  
  //
  // Column width relative weights
  //
  #define WT_HTTP_ERROR     3
  #define WT_OUTPUT_TYPE    2
  #define WT_CONTENTS       8
  
  //
  // Registry key name for this dialog
  //
  const TCHAR g_szRegKey[] = _T("Errors");
  
  //
  // Key under w3svc where the error descriptions live
  //
  const TCHAR g_cszErrorLocation[] = _T("Info");
  
  
  
  IMPLEMENT_DYNAMIC(CCustomErrorsListBox, CHeaderListBox);
  
  
  
  const int CCustomErrorsListBox::nBitmaps = 3;
  
  
  
  CCustomErrorsListBox::CCustomErrorsListBox(
      IN UINT nIDDefault,
      IN UINT nIDFile,
      IN UINT nIDURL
      )
  /*++
  
  Routine Description:
  
      Error listbox constructor
  
  Arguments:
  
      UINT nIDDefault : String ID for 'default'
      UINT nIDFile    : String ID for 'file'
      UINT nIDURL     : String ID for 'URL'
  
  --*/
       : CHeaderListBox(HLS_STRETCH, g_szRegKey)
  {
      VERIFY(m_str[CCustomError::ERT_DEFAULT].LoadString(nIDDefault));
      VERIFY(m_str[CCustomError::ERT_FILE].LoadString(nIDFile));
      VERIFY(m_str[CCustomError::ERT_URL].LoadString(nIDURL));
  }
  
  
  void
  CCustomErrorsListBox::DrawItemEx(
      IN CRMCListBoxDrawStruct & ds
      )
  /*++
  
  Routine Description:
  
     Draw item in the listbox
  
  Arguments:
  
      CRMCListBoxDrawStruct & ds   : Input data structure
  
  Return Value:
  
      N/A
  
  --*/
  {
      CCustomError * p = (CCustomError *)ds.m_ItemData;
      ASSERT(p != NULL);
  
      DrawBitmap(ds, 0, p->m_nType);
  
      CString strError, strText;
  
      if (p->m_nSubError > 0)
      {
          strError.Format(_T("%d;%d"), p->m_nError, p->m_nSubError);
      }
      else
      {
          strError.Format(_T("%d"), p->m_nError);
      }
  
      ColumnText(ds, 0, TRUE, strError);
      ColumnText(ds, 1, FALSE, m_str[p->m_nType] );
  
      if (p->IsDefault())
      {
          strText.Format(_T("\"%s\""), p->m_strDefault);
      }
      else
      {
          strText = p->m_str;
      }
  
      ColumnText(ds, 2, FALSE, strText);
  }
  
  
  
  /* virtual */
  BOOL
  CCustomErrorsListBox::Initialize()
  /*++
  
  Routine Description:
  
      initialize the listbox.  Insert the columns
      as requested, and lay them out appropriately
  
  Arguments:
  
      None
  
  Return Value:
  
      TRUE if initialized successfully, FALSE otherwise
  
  --*/
  {
      if (!CHeaderListBox::Initialize())
      {
          return FALSE;
      }
  
      HINSTANCE hInst = AfxGetResourceHandle();
      InsertColumn(0, WT_HTTP_ERROR, IDS_HTTP_ERROR, hInst);
      InsertColumn(1, WT_OUTPUT_TYPE, IDS_OUTPUT_TYPE, hInst);
      InsertColumn(2, WT_CONTENTS, IDS_CONTENTS, hInst);
  
      //
      // Try to set the widths from the stored registry value,
      // otherwise distribute according to column weights specified
      //
  //    if (!SetWidthsFromReg())
  //    {
          DistributeColumns();
  //    }
  
      SetRedraw(TRUE);
  
      return TRUE;
  }
  
  
  
  CHTTPErrorDescriptions::CHTTPErrorDescriptions(
      IN LPCTSTR lpszServer
      )
  /*++
  
  Routine Description:
  
      Constructor for default errors object.  This fetches the default
      error definitions
  
  Arguments:
  
      LPCTSTR lpServerName : Server name
  
  Return Value:
  
      None
  
  --*/
      : CMetaProperties(
          QueryAuthInfo(),
          CMetabasePath(g_cszSvc, MASTER_INSTANCE, g_cszErrorLocation)
          )
  {       
      m_dwMDUserType = IIS_MD_UT_SERVER;
      m_dwMDDataType = MULTISZ_METADATA;
  }
  
  
  
  /* virtual */
  void
  CHTTPErrorDescriptions::ParseFields()
  /*++
  
  Routine Description:
  
      Parse the fetched data into fields
  
  Arguments:
  
      None
  
  Return Value:
  
      None
  
  --*/
  {
      BEGIN_PARSE_META_RECORDS(m_dwNumEntries, m_pbMDData)
        HANDLE_META_RECORD(MD_CUSTOM_ERROR_DESC, m_strlErrorDescriptions)
      END_PARSE_META_RECORDS
  }
  
  
  
  //
  // Errors property page
  //
  // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  
  
  
  IMPLEMENT_DYNCREATE(CW3ErrorsPage, CInetPropertyPage)
  
  
  
  CW3ErrorsPage::CW3ErrorsPage(
      IN CInetPropertySheet * pSheet
      )
  /*++
  
  Routine Description:
  
      Constructor for WWW error property page
  
  Arguments:
  
      CInetPropertySheet * pSheet : Sheet object
  
  Return Value:
  
      N/A
  
  
  --*/
      : CInetPropertyPage(CW3ErrorsPage::IDD, pSheet),
        m_list_Errors(IDS_DEFAULT_ERROR, IDS_FILE, IDS_URL),
        m_ListBoxRes(IDB_ERRORS, m_list_Errors.nBitmaps),
        m_strlCustomErrors(),
        m_strlErrorDescriptions(),
        m_oblErrors()
  {
      //{{AFX_DATA_INIT(CW3ErrorsPage)
      //}}AFX_DATA_INIT
  
      m_list_Errors.AttachResources(&m_ListBoxRes);
  }
  
  
  
  CW3ErrorsPage::~CW3ErrorsPage()
  /*++
  
  Routine Description:
  
      Destructor
  
  Arguments:
  
      N/A
  
  Return Value:
  
      N/A
  
  --*/
  {
  }
  
  void
  CW3ErrorsPage::DoDataExchange(
      IN CDataExchange * pDX
      )
  /*++
  
  Routine Description:
  
      Initialise/Store control data
  
  Arguments:
  
      CDataExchange * pDX - DDX/DDV control structure
  
  Return Value:
  
      None
  
  --*/
  {
      CInetPropertyPage::DoDataExchange(pDX);
  
      //{{AFX_DATA_MAP(CW3ErrorsPage)
      DDX_Control(pDX, IDC_BUTTON_SET_TO_DEFAULT, m_button_SetDefault);
      DDX_Control(pDX, IDC_BUTTON_EDIT, m_button_Edit);
      //}}AFX_DATA_MAP
  
      DDX_Control(pDX, IDC_LIST_ERRORS, m_list_Errors);
  
      if (pDX->m_bSaveAndValidate)
      {
          CError err(StoreErrors());
          if (err.MessageBoxOnFailure(m_hWnd))
          {
              pDX->Fail();
          }
      }
  }
  
  //
  // Message Map
  //
  BEGIN_MESSAGE_MAP(CW3ErrorsPage, CInetPropertyPage)
      //{{AFX_MSG_MAP(CW3ErrorsPage)
      ON_BN_CLICKED(IDC_BUTTON_EDIT, OnButtonEdit)
      ON_BN_CLICKED(IDC_BUTTON_SET_TO_DEFAULT, OnButtonSetToDefault)
      ON_LBN_DBLCLK(IDC_LIST_ERRORS, OnDblclkListErrors)
      ON_LBN_SELCHANGE(IDC_LIST_ERRORS, OnSelchangeListErrors)
      //}}AFX_MSG_MAP
  END_MESSAGE_MAP()
  
  
  
  void
  CW3ErrorsPage::FillListBox()
  /*++
  
  Routine Description:
  
      Populate the listbox with the directory entries
  
  Arguments:
  
      None
  
  Return Value:
  
      None
  
  --*/
  {
      CObListIter obli(m_oblErrors);
      CCustomError * pError;
  
      //
      // Remember the selection.
      //
      int nCurSel = m_list_Errors.GetCurSel();
  
      m_list_Errors.SetRedraw(FALSE);
      m_list_Errors.ResetContent();
  
      int cItems = 0;
  
      for (/**/; pError = (CCustomError *)obli.Next(); ++cItems)
      {
          m_list_Errors.AddItem(pError);
      }
  
      m_list_Errors.SetRedraw(TRUE);
      m_list_Errors.SetCurSel(nCurSel);
  }
  
  
  DWORD
  CW3ErrorsPage::SortCustomErrorsList()
  {
      BeginWaitCursor();
      DWORD dw =  m_oblErrors.Sort(
  		(CObjectPlus::PCOBJPLUS_ORDER_FUNC)&CCustomError::OrderByErrorNum);
      EndWaitCursor();
      return dw;
  }
  
  HRESULT
  CW3ErrorsPage::StoreErrors()
  /*++
  
  Routine Description:
  
      Build errors stringlist from the error oblist built up.
  
  Arguments:
  
      None
  
  Return Value:
  
      Error return code
  
  --*/
  {
      CError err;
  
      try
      {
          m_strlCustomErrors.RemoveAll();
          POSITION pos = m_oblErrors.GetHeadPosition();
  
          while(pos)
          {
              CCustomError * pErr = (CCustomError *)m_oblErrors.GetNext(pos);
              if (!pErr->IsDefault())
              {
                  CString str;
                  pErr->BuildErrorString(str);
                  m_strlCustomErrors.AddTail(str);
              }
          }
      }
      catch(CMemoryException * e)
      {
          err = ERROR_NOT_ENOUGH_MEMORY;
          e->Delete();
      }
  
      return err;
  }
  
  
  
  CCustomError *
  CW3ErrorsPage::FindError(
      IN UINT nError,
      IN UINT nSubError
      )
  /*++
  
  Routine Description:
  
      Find error in the list with the given error code and suberror code
  
  Arguments:
  
      UINT nError     : Error code
      UINT nSubError  : Sub error code
  
  Return Value:
  
      Pointer to the error or NULL if not found.
  
  --*/
  {
      CCustomError * pErr = NULL;
  
      POSITION pos = m_oblErrors.GetHeadPosition();
      while(pos)
      {
          pErr = (CCustomError *)m_oblErrors.GetNext(pos);
          ASSERT(pErr != NULL);
          if (pErr->m_nError == nError && pErr->m_nSubError == nSubError)
          {
              //
              // Found it!
              //
              return pErr;
          }
      }
  
      //
      // Not found!
      //
      return NULL;
  }
  
  
  
  HRESULT
  CW3ErrorsPage::FetchErrors()
  /*++
  
  Routine Description:
  
      Build up the errors list
  
  Arguments:
  
      None
  
  Return Value:
  
      Error return code
  
  --*/
  {
      CError err;
  
      CWaitCursor wait;
  
      do
      {
          try
          {
              //
              // First get the default descriptions
              //
              CHTTPErrorDescriptions ed(QueryServerName());
              err = ed.LoadData();
              if (err.Failed())
              {
                  break;
              }
  
              if (!ed.GetErrorDescriptions().IsEmpty())
              {
                  POSITION pos = ed.GetErrorDescriptions().GetHeadPosition();
  
                  while(pos)
                  {
                      CString & str = ed.GetErrorDescriptions().GetNext(pos);
                      m_oblErrors.AddTail(new CCustomError(str));
                  }
              }
              else
              {
                  DoHelpMessageBox(m_hWnd,IDS_NO_DEF_ERRORS, MB_APPLMODAL | MB_OK | MB_ICONINFORMATION, 0);
                  break;
              }
  
              //
              // Now match up the overrides if any
              //
              POSITION pos = m_strlCustomErrors.GetHeadPosition();
              while(pos)
              {
                  CString & strError = m_strlCustomErrors.GetNext(pos);
  
                  TRACEEOLID(strError);
  
                  UINT nError;
                  UINT nSubError;
                  CCustomError::ERT nType;
                  CString str;
                  CCustomError * pErr = NULL;
  
                  if (CCustomError::CrackErrorString(
                      strError, 
                      nError, 
                      nSubError, 
                      nType, 
                      str
                      ))
                  {
                      pErr = FindError(nError, nSubError);
                  }
  
                  if (pErr != NULL)
                  {
                      pErr->SetValue(nType, str);
                  }
                  else
                  {
                      CString strFmt;
                      strFmt.LoadString(IDS_BAD_ERROR);
  
                      str.Format(strFmt, nError, nSubError);
                      ::AfxMessageBox(str);
                      break;
                  }
              }
  
  			SortCustomErrorsList();
              FillListBox();
          }
          catch(CMemoryException * e)
          {
              err = ERROR_NOT_ENOUGH_MEMORY;
              e->Delete();
          }
      }
      while(FALSE);
  
      return err;
  }
  
  
  
  void
  CW3ErrorsPage::SetControlStates()
  /*++
  
  Routine Description:
  
      Enable/disable controls depending on the state of
      the dialog
  
  Arguments:
  
      None
  
  Return Value:
  
      None
  
  --*/
  {
      CCustomError * pErr = GetSelectedListItem();
  
      m_button_Edit.EnableWindow(pErr != NULL);
      m_button_SetDefault.EnableWindow(m_list_Errors.GetSelCount() > 0);
  }
  
  
  INT_PTR
  CW3ErrorsPage::ShowPropertyDialog()
  /*++
  
  Routine Description:
  
      Display the add/edit dialog.  The return
      value is the value returned by the dialog
  
  Arguments:
  
      None
  
  Return Value:
  
      None
  
  --*/
  {
      int nCurSel;
      CCustomError * pErr = GetSelectedListItem(&nCurSel);
  
      if (pErr == NULL)
      {
          //
          // Must be from a double click on extended selection
          //
          return IDCANCEL;
      }
      
      CCustomErrorDlg dlgError(pErr, IsLocal(), this);
      INT_PTR nReturn = dlgError.DoModal();
  
      if (nReturn == IDOK)
      {
          //
          // Re-display the text
          //
          m_list_Errors.InvalidateSelection(nCurSel);
          SetModified(TRUE);
      }
  
      return nReturn;
  }
  
  
  
  //
  // Message Handlers
  //
  // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  
  
  
  BOOL
  CW3ErrorsPage::OnInitDialog()
  /*++
  
  Routine Description:
  
      WM_INITDIALOG handler.  Initialize the dialog.
  
  Arguments:
  
      None.
  
  Return Value:
  
      TRUE if focus is to be set automatically, FALSE if the focus
      is already set.
  
  --*/
  {
      CInetPropertyPage::OnInitDialog();
  
      m_list_Errors.Initialize();
  
      //
      // Build filters oblist
      //
      CError err(FetchErrors());
      err.MessageBoxOnFailure(m_hWnd);
      SetControlStates();
  
      return TRUE;
  }
  
  
  
  void
  CW3ErrorsPage::OnButtonEdit()
  /*++
  
  Routine Description:
  
      'edit' button handler
  
  Arguments:
  
      None
  
  Return Value:
  
      None
  
  --*/
  {
      if (ShowPropertyDialog() == IDOK)
      {
          SetControlStates();
          SetModified(TRUE);
      }
  }
  
  
  
  void
  CW3ErrorsPage::OnButtonSetToDefault()
  /*++
  
  Routine Description:
  
      'set to default' button handler
  
  Arguments:
  
      None
  
  Return Value:
  
      None
  
  --*/
  {
      //
      // Apply to each selected item
      //
      m_list_Errors.SetRedraw(FALSE);
  
      int nSel = 0;
      int cChanges = 0;
      CCustomError * pErr;
      while ((pErr = GetNextSelectedItem(&nSel)) != NULL)
      {
          if (!pErr->IsDefault())
          {
              //
              // Force a redraw of the current item
              //
              pErr->MakeDefault();
              m_list_Errors.InvalidateSelection(nSel);
              ++cChanges;
          }
  
          ++nSel;
      }
  
      if (cChanges)
      {    
          SetModified(TRUE);
      }
  
      m_list_Errors.SetRedraw(TRUE);
      SetControlStates();
  }
  
  
  
  void
  CW3ErrorsPage::OnDblclkListErrors()
  /*++
  
  Routine Description:
  
      error list 'double click' handler
  
  Arguments:
  
      None
  
  Return Value:
  
      None
  
  --*/
  {
      OnButtonEdit();
  }
  
  
  
  void
  CW3ErrorsPage::OnSelchangeListErrors()
  /*++
  
  Routine Description:
  
      error list selection change handler
  
  Arguments:
  
      None
  
  Return Value:
  
      None
  
  --*/
  {
      SetControlStates();
  }
  
  
  
  /* virtual */
  HRESULT
  CW3ErrorsPage::FetchLoadedValues()
  /*++
  
  Routine Description:
      
      Move configuration data from sheet to dialog controls
  
  Arguments:
  
      None
  
  Return Value:
  
      HRESULT
  
  --*/
  {
      CError err;
  
      BEGIN_META_DIR_READ(CW3Sheet)
          FETCH_DIR_DATA_FROM_SHEET(m_strlCustomErrors);
      END_META_DIR_READ(err)
  
      return err;
  }
  
  
  
  /* virtual */
  HRESULT
  CW3ErrorsPage::SaveInfo()
  /*++
  
  Routine Description:
  
      Save the information on this property page
  
  Arguments:
  
      None
  
  Return Value:
  
      Error return code
  
  --*/
  {
      ASSERT(IsDirty());
  
      TRACEEOLID("Saving W3 errors page now...");
  
      CError err;
  
      BeginWaitCursor();
  
      BEGIN_META_DIR_WRITE(CW3Sheet)
          STORE_DIR_DATA_ON_SHEET(m_strlCustomErrors)
      END_META_DIR_WRITE(err)
  
  	if (err.Succeeded())
  	{
  		err = ((CW3Sheet *)GetSheet())->SetKeyType();
  	}
  
      EndWaitCursor();
  
      return err;
  }
  