/*++

Copyright (c) 1995  Microsoft Corporation

Module Name:

   remdlg.cpp

Abstract:

   Remove licenses dialog implementation.

Author:

   Jeff Parham (jeffparh) 13-Dec-1995

Revision History:

--*/


#include "stdafx.h"
#include "ccfapi.h"
#include "remdlg.h"
#include "utils.h"
#include "licobj.h"
#include "imagelst.h"
#include "nlicdlg.h"
#include <htmlhelp.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

// describes the list view layout
static LV_COLUMN_INFO g_removeColumnInfo =
{
    0, 1, LVID_REMOVE_TOTAL_COLUMNS,

    {{LVID_REMOVE_SERIAL_NUMBER,    IDS_SERIAL_NUMBER,   LVCX_REMOVE_SERIAL_NUMBER  },
     {LVID_REMOVE_PRODUCT_NAME,     IDS_PRODUCT_NAME,    LVCX_REMOVE_PRODUCT_NAME   },
     {LVID_REMOVE_LICENSE_MODE,     IDS_LICENSE_MODE,    LVCX_REMOVE_LICENSE_MODE   },
     {LVID_REMOVE_NUM_LICENSES,     IDS_QUANTITY,        LVCX_REMOVE_NUM_LICENSES   },
     {LVID_REMOVE_SOURCE,           IDS_SOURCE,          LVCX_REMOVE_SOURCE         }},
};


CCertRemoveSelectDlg::CCertRemoveSelectDlg(CWnd* pParent /*=NULL*/)
   : CDialog(CCertRemoveSelectDlg::IDD, pParent)

/*++

Routine Description:

   Constructor for dialog.

Arguments:

   pParent - owner window.

Return Values:

   None.

--*/

{
   //{{AFX_DATA_INIT(CCertRemoveSelectDlg)
   m_nLicenses = 0;
   //}}AFX_DATA_INIT

   m_hLls                  = NULL;
   m_bLicensesRefreshed    = FALSE;
   m_dwRemoveFlags         = 0;
}


CCertRemoveSelectDlg::~CCertRemoveSelectDlg()

/*++

Routine Description:

   Destructor for dialog.

Arguments:

   None.

Return Values:

   None.

--*/

{
   if ( NULL != m_hLls )
   {
      LlsClose( m_hLls );
   }
}


void CCertRemoveSelectDlg::DoDataExchange(CDataExchange* pDX)

/*++

Routine Description:

   Called by framework to exchange dialog data.

Arguments:

   pDX - data exchange object.

Return Values:

   None.

--*/

{
   CDialog::DoDataExchange(pDX);
   //{{AFX_DATA_MAP(CCertRemoveSelectDlg)
   DDX_Control(pDX, IDC_SPIN_LICENSES, m_spinLicenses);
   DDX_Control(pDX, IDC_CERTIFICATE_LIST, m_listCertificates);
   DDX_Text(pDX, IDC_NUM_LICENSES, m_nLicenses);
   //}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CCertRemoveSelectDlg, CDialog)
   //{{AFX_MSG_MAP(CCertRemoveSelectDlg)
   ON_BN_CLICKED(IDC_MY_HELP, OnHelp)
   ON_NOTIFY(LVN_COLUMNCLICK, IDC_CERTIFICATE_LIST, OnColumnClickCertificateList)
   ON_NOTIFY(LVN_GETDISPINFO, IDC_CERTIFICATE_LIST, OnGetDispInfoCertificateList)
   ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_LICENSES, OnDeltaPosSpinLicenses)
   ON_NOTIFY(NM_DBLCLK, IDC_CERTIFICATE_LIST, OnDblClkCertificateList)
   ON_NOTIFY(NM_RETURN, IDC_CERTIFICATE_LIST, OnReturnCertificateList)
   ON_WM_DESTROY()
   ON_NOTIFY(NM_CLICK, IDC_CERTIFICATE_LIST, OnClickCertificateList)
   ON_NOTIFY(LVN_KEYDOWN, IDC_CERTIFICATE_LIST, OnKeyDownCertificateList)
   ON_BN_CLICKED(IDC_REFRESH, OnRefresh)
   ON_MESSAGE( WM_HELP , OnHelpCmd )
   //}}AFX_MSG_MAP
END_MESSAGE_MAP()


BOOL CCertRemoveSelectDlg::OnInitDialog()

/*++

Routine Description:

   Handler for WM_INITDIALOG.

Arguments:

   None.

Return Values:

   Returns false if focus set manually.

--*/

{
   CDialog::OnInitDialog();

   LoadImages();

   m_listCertificates.SetImageList( &m_smallImages, LVSIL_SMALL );

   ::LvInitColumns( &m_listCertificates, &g_removeColumnInfo );

   RefreshLicenses();
   RefreshCertificateList();
   UpdateSpinControlRange();

   return TRUE;  // return TRUE unless you set the focus to a control
                 // EXCEPTION: OCX Property Pages should return FALSE
}


void CCertRemoveSelectDlg::OnOK()

/*++

Routine Description:

   Handler for BN_CLICKED of OK.

Arguments:

   None.

Return Values:

   None.

--*/

{
   RemoveSelectedCertificate();
}

/*++

Routine Description:

   Handler for pressing F1.

Arguments:

   None.

Return Values:

   Nothing significant.

--*/
LRESULT CCertRemoveSelectDlg::OnHelpCmd( WPARAM , LPARAM )
{
    OnHelp();

    return 0;
}

void CCertRemoveSelectDlg::OnHelp()

/*++

Routine Description:

   Handler for help button click.

Arguments:

   None.

Return Values:

   None.

--*/

{
   WinHelp( IDD, HELP_CONTEXT );
}


void CCertRemoveSelectDlg::WinHelp(DWORD dwData, UINT nCmd)

/*++

Routine Description:

   Call WinHelp for this dialog.

Arguments:

   dwData (DWORD)
   nCmd (UINT)

Return Values:

   None.

--*/

{
   ::HtmlHelp(m_hWnd, L"liceconcepts.chm", HH_DISPLAY_TOPIC,0);
/*
   BOOL ok = ::WinHelp( m_hWnd, theApp.GetHelpFileName(), nCmd, dwData );
   ASSERT( ok );
*/
}


void CCertRemoveSelectDlg::OnDestroy()

/*++

Routine Description:

   Handler for WM_DESTROY.

Arguments:

   None.

Return Values:

   None.

--*/

{
   ResetLicenses();
/*
   ::WinHelp( m_hWnd, theApp.GetHelpFileName(), HELP_QUIT, 0 );
*/

   CDialog::OnDestroy();
}


void CCertRemoveSelectDlg::OnColumnClickCertificateList(NMHDR* pNMHDR, LRESULT* pResult)

/*++

Routine Description:

   Handler for LVN_COLUMNCLICK of certificate list view.

Arguments:

   pNMHDR (NMHDR*)
   pResult (LRESULT*)

Return Values:

   None.

--*/

{
   g_removeColumnInfo.bSortOrder  = GetKeyState(VK_CONTROL) < 0;
   g_removeColumnInfo.nSortedItem = ((NM_LISTVIEW*)pNMHDR)->iSubItem;

   m_listCertificates.SortItems( CompareLicenses, 0 );    // use column info

   *pResult = 0;
}


void CCertRemoveSelectDlg::OnGetDispInfoCertificateList(NMHDR* pNMHDR, LRESULT* pResult)

/*++

Routine Description:

   Handler for LVN_GETDISPINFO of certificate list view.

Arguments:

   pNMHDR (NMHDR*)
   pResult (LRESULT*)

Return Values:

   None.

--*/

{
   LV_ITEM* plvItem = &((LV_DISPINFO*)pNMHDR)->item;
   ASSERT(plvItem);

   CLicense* pLicense = (CLicense*)plvItem->lParam;
   VALIDATE_OBJECT(pLicense, CLicense);

   switch (plvItem->iSubItem)
   {
   case LVID_REMOVE_SERIAL_NUMBER:
      plvItem->iImage = BMPI_CERTIFICATE;
      {
         CString  strSerialNumber;

         strSerialNumber.Format( TEXT("%ld"), (LONG) ( pLicense->m_dwCertificateID ) );
         lstrcpyn( plvItem->pszText, strSerialNumber, plvItem->cchTextMax );
      }
      break;

   case LVID_REMOVE_PRODUCT_NAME:
      lstrcpyn( plvItem->pszText, pLicense->m_strProduct, plvItem->cchTextMax );
      break;

   case LVID_REMOVE_LICENSE_MODE:
      lstrcpyn( plvItem->pszText, pLicense->GetAllowedModesString(), plvItem->cchTextMax );
      break;

   case LVID_REMOVE_NUM_LICENSES:
      {
         CString  strLicenses;

         strLicenses.Format( TEXT("%ld"), (LONG) ( pLicense->m_lQuantity ) );
         lstrcpyn( plvItem->pszText, strLicenses, plvItem->cchTextMax );
      }
      break;

   case LVID_REMOVE_SOURCE:
      lstrcpyn( plvItem->pszText, pLicense->GetSourceDisplayName(), plvItem->cchTextMax );
      break;

   default:
      ASSERT( FALSE );
      break;
   }

   *pResult = 0;
}


void CCertRemoveSelectDlg::OnDeltaPosSpinLicenses(NMHDR* pNMHDR, LRESULT* pResult)

/*++

Routine Description:

   Handler for UDN_DELTAPOS of number of licenses.

Arguments:

   pNMHDR (NMHDR*)
   pResult (LRESULT*)

Return Values:

   None.

--*/

{
   if ( UpdateData(TRUE) )   // get data
   {
      m_nLicenses += ((NM_UPDOWN*)pNMHDR)->iDelta;

      int   nLow;
      int   nHigh;

      m_spinLicenses.GetRange( nLow, nHigh );

      if (m_nLicenses < nLow)
      {
         m_nLicenses = nLow;

         ::MessageBeep(MB_OK);
      }
      else if (m_nLicenses > nHigh )
      {
         m_nLicenses = nHigh;

         ::MessageBeep(MB_OK);
      }

      UpdateData(FALSE);  // set data
   }

   *pResult = 1;   // handle ourselves...
}


void CCertRemoveSelectDlg::OnDblClkCertificateList(NMHDR* pNMHDR, LRESULT* pResult)

/*++

Routine Description:

   Handler for NM_DBLCLK of certificate list view.

Arguments:

   pNMHDR (NMHDR*)
   pResult (LRESULT*)

Return Values:

   None.

--*/

{
   RemoveSelectedCertificate();
   *pResult = 0;
}


void CCertRemoveSelectDlg::OnReturnCertificateList(NMHDR* pNMHDR, LRESULT* pResult)

/*++

Routine Description:

   Handler for NM_RETURN of certificate list view.

Arguments:

   None.

Return Values:

   None.

--*/

{
   RemoveSelectedCertificate();
   *pResult = 0;
}


void CCertRemoveSelectDlg::ResetLicenses()

/*++

Routine Description:

   Remove all licenses from internal list.

Arguments:

   None.

Return Values:

   None.

--*/

{
   CLicense* pLicense;
   int       iLicense = (int)m_licenseArray.GetSize();

   while (iLicense--)
   {
      if (pLicense = (CLicense*)m_licenseArray[iLicense])
      {
         ASSERT(pLicense->IsKindOf(RUNTIME_CLASS(CLicense)));
         delete pLicense;
      }
   }

   m_licenseArray.RemoveAll();
   m_listCertificates.DeleteAllItems();

   m_bLicensesRefreshed = FALSE;
}


BOOL CCertRemoveSelectDlg::RefreshLicenses()

/*++

Routine Description:

   Refresh internal license list with data from license server.

Arguments:

   None.

Return Values:

   BOOL.

--*/

{
   ResetLicenses();

   if ( ConnectServer() )
   {
      NTSTATUS    NtStatus;
      DWORD       ResumeHandle = 0L;

      int iLicense = 0;

      do
      {
         DWORD  EntriesRead;
         DWORD  TotalEntries;
         LPBYTE ReturnBuffer = NULL;
         DWORD  Level = LlsCapabilityIsSupported( m_hLls, LLS_CAPABILITY_SECURE_CERTIFICATES ) ? 1 : 0;

         BeginWaitCursor();
         NtStatus = ::LlsLicenseEnum( m_hLls,
                                      Level,
                                      &ReturnBuffer,
                                      LLS_PREFERRED_LENGTH,
                                      &EntriesRead,
                                      &TotalEntries,
                                      &ResumeHandle );
         EndWaitCursor();

         if (    ( STATUS_SUCCESS      == NtStatus )
              || ( STATUS_MORE_ENTRIES == NtStatus ) )
         {
            CLicense*            pLicense;
            PLLS_LICENSE_INFO_0  pLicenseInfo0;
            PLLS_LICENSE_INFO_1  pLicenseInfo1;

            pLicenseInfo0 = (PLLS_LICENSE_INFO_0)ReturnBuffer;
            pLicenseInfo1 = (PLLS_LICENSE_INFO_1)ReturnBuffer;

            while (EntriesRead--)
            {
               if (    ( m_strProductName.IsEmpty() || !m_strProductName.CompareNoCase( Level ? pLicenseInfo1->Product : pLicenseInfo0->Product ) )
                    && ( m_strSourceToUse.IsEmpty() || !m_strSourceToUse.CompareNoCase( Level ? pLicenseInfo1->Source  : TEXT("None")           ) ) )
               {
                  // we want to list this license

                  // have we seen this certificate yet?
                  for ( int i=0; i < m_licenseArray.GetSize(); i++ )
                  {
                     pLicense = (CLicense*) m_licenseArray[ i ];

                     VALIDATE_OBJECT( pLicense, CLicense );

                     if (    (    ( 1 == Level )
                               && ( pLicense->m_dwCertificateID == pLicenseInfo1->CertificateID     )
                               && ( pLicense->m_dwAllowedModes  == pLicenseInfo1->AllowedModes      )
                               && ( pLicense->m_dwMaxQuantity   == pLicenseInfo1->MaxQuantity       )
                               && ( !pLicense->m_strSource.CompareNoCase(  pLicenseInfo1->Source  ) )
                               && ( !pLicense->m_strProduct.CompareNoCase( pLicenseInfo1->Product ) )
                               && ( !memcmp( pLicense->m_adwSecrets,
                                             pLicenseInfo1->Secrets,
                                             sizeof( pLicense->m_adwSecrets ) )                     ) )
                          || (    ( 0 == Level )
                               && ( !pLicense->m_strProduct.CompareNoCase( pLicenseInfo0->Product ) ) ) )
                     {
                        // we've seen this certificate before; update the tally
                        pLicense->m_lQuantity += ( Level ? pLicenseInfo1->Quantity : pLicenseInfo0->Quantity );
                        break;
                     }
                  }

                  if ( i >= m_licenseArray.GetSize() )
                  {
                     // we haven't seen this certificate yet; create a new license for it
                     if ( 1 == Level )
                     {
                        pLicense = new CLicense( pLicenseInfo1->Product,
                                                 pLicenseInfo1->Vendor,
                                                 pLicenseInfo1->Admin,
                                                 pLicenseInfo1->Date,
                                                 pLicenseInfo1->Quantity,
                                                 pLicenseInfo1->Comment,
                                                 pLicenseInfo1->AllowedModes,
                                                 pLicenseInfo1->CertificateID,
                                                 pLicenseInfo1->Source,
                                                 pLicenseInfo1->ExpirationDate,
                                                 pLicenseInfo1->MaxQuantity,
                                                 pLicenseInfo1->Secrets );

                        ::LlsFreeMemory( pLicenseInfo1->Product );
                        ::LlsFreeMemory( pLicenseInfo1->Admin   );
                        ::LlsFreeMemory( pLicenseInfo1->Comment );
                        ::LlsFreeMemory( pLicenseInfo1->Source  );
                     }
                     else
                     {
                        ASSERT( 0 == Level );

                        pLicense = new CLicense( pLicenseInfo0->Product,
                                                 TEXT( "Microsoft" ),
                                                 pLicenseInfo0->Admin,
                                                 pLicenseInfo0->Date,
                                                 pLicenseInfo0->Quantity,
                                                 pLicenseInfo0->Comment );

                        ::LlsFreeMemory( pLicenseInfo0->Product );
                        ::LlsFreeMemory( pLicenseInfo0->Admin   );
                        ::LlsFreeMemory( pLicenseInfo0->Comment );
                     }

                     if ( NULL == pLicense )
                     {
                        NtStatus = ERROR_OUTOFMEMORY;
                        break;
                     }

                     m_licenseArray.Add( pLicense );
                  }
               }

               pLicenseInfo1++;
               pLicenseInfo0++;
            }

            ::LlsFreeMemory(ReturnBuffer);
         }

      } while ( STATUS_MORE_ENTRIES == NtStatus );

      theApp.SetLastLlsError( NtStatus );   // called api

      if ( STATUS_SUCCESS == NtStatus )
      {
         // add per server entries
         LPTSTR pszServerName = m_strServerName.GetBuffer(0);

         if ( NULL != pszServerName )
         {
            BeginWaitCursor();

            HKEY  hKeyLocalMachine;

            NtStatus = RegConnectRegistry( pszServerName, HKEY_LOCAL_MACHINE, &hKeyLocalMachine );

            if ( ERROR_SUCCESS != NtStatus )
            {
               theApp.SetLastError( NtStatus );
            }
            else
            {
               HKEY  hKeyLicenseInfo;

               NtStatus = RegOpenKeyEx( hKeyLocalMachine, TEXT( "SYSTEM\\CurrentControlSet\\Services\\LicenseInfo" ), 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_SET_VALUE, &hKeyLicenseInfo );

               if ( ERROR_SUCCESS != NtStatus )
               {
                  theApp.SetLastError( NtStatus );
               }
               else
               {
                  NTSTATUS ntEnum;
                  BOOL     bFoundKey = FALSE;
                  DWORD    iSubKey = 0;

                  // if the service is 3.51-style per server, add it to the list
                  do
                  {
                     TCHAR    szKeyName[ 128 ];
                     DWORD    cchKeyName = sizeof( szKeyName ) / sizeof( *szKeyName );

                     ntEnum = RegEnumKeyEx( hKeyLicenseInfo, iSubKey++, szKeyName, &cchKeyName, NULL, NULL, NULL, NULL );

                     if ( ERROR_SUCCESS == ntEnum )
                     {
                        HKEY  hKeyProduct;

                        NtStatus = RegOpenKeyEx( hKeyLicenseInfo, szKeyName, 0, KEY_QUERY_VALUE | KEY_SET_VALUE, &hKeyProduct );

                        if ( ERROR_SUCCESS == NtStatus )
                        {
                           DWORD    dwType;
                           TCHAR    szDisplayName[ 128 ];
                           DWORD    cbDisplayName = sizeof( szDisplayName );

                           NtStatus = RegQueryValueEx( hKeyProduct, TEXT( "DisplayName" ), NULL, &dwType, (LPBYTE) szDisplayName, &cbDisplayName );

                           if ( ERROR_SUCCESS == NtStatus )
                           {
                              // is this product secure?
                              BOOL bIsSecure = FALSE;

                              if ( LlsCapabilityIsSupported( m_hLls, LLS_CAPABILITY_SECURE_CERTIFICATES ) )
                              {
                                 NtStatus = ::LlsProductSecurityGet( m_hLls, szDisplayName, &bIsSecure );
                                 theApp.SetLastLlsError( NtStatus );

                                 if ( STATUS_SUCCESS != NtStatus )
                                 {
                                    bIsSecure = FALSE;
                                 }
                              }

                              if ( !bIsSecure )
                              {
#ifdef REMOVE_CONCURRENT_ONLY_IF_PER_SERVER_MODE
                              // not secure; is it in per server mode?
                              DWORD    dwMode;
                              DWORD    cbMode = sizeof( dwMode );

                              NtStatus = RegQueryValueEx( hKeyProduct, TEXT( "Mode" ), NULL, &dwType, (LPBYTE) &dwMode, &cbMode );

                              if ( ( ERROR_SUCCESS == NtStatus ) && dwMode )
                              {
                                 // per server mode; add to list
#endif
                                 DWORD    dwConcurrentLimit;
                                 DWORD    cbConcurrentLimit = sizeof( dwConcurrentLimit );

                                 NtStatus = RegQueryValueEx( hKeyProduct, TEXT( "ConcurrentLimit" ), NULL, &dwType, (LPBYTE) &dwConcurrentLimit, &cbConcurrentLimit );

                                 if (    ( ERROR_SUCCESS == NtStatus )
                                      && ( 0 < dwConcurrentLimit )
                                      && ( m_strProductName.IsEmpty() || !m_strProductName.CompareNoCase( szDisplayName ) )
                                      && ( m_strSourceToUse.IsEmpty() || !m_strSourceToUse.CompareNoCase( TEXT("None")  ) ) )
                                 {
                                    CLicense * pLicense = new CLicense( szDisplayName,
                                                                        TEXT(""),
                                                                        TEXT(""),
                                                                        0,
                                                                        dwConcurrentLimit,
                                                                        TEXT(""),
                                                                        LLS_LICENSE_MODE_ALLOW_PER_SERVER );

                                    if ( NULL != pLicense )
                                    {
                                       m_licenseArray.Add( pLicense );
                                    }
                                 }
                              }
#ifdef REMOVE_CONCURRENT_ONLY_IF_PER_SERVER_MODE
                              }
#endif
                           }

                           RegCloseKey( hKeyProduct );
                        }
                     }
                  } while ( ERROR_SUCCESS == ntEnum );

                  RegCloseKey( hKeyLicenseInfo );
               }

               RegCloseKey( hKeyLocalMachine );
            }

            m_strServerName.ReleaseBuffer();
         }

         EndWaitCursor();

         m_bLicensesRefreshed = TRUE;

         // remove any entries from the list that aren't removable
         for ( int i=0; i < m_licenseArray.GetSize(); )
         {
            CLicense* pLicense = (CLicense*) m_licenseArray[ i ];

            VALIDATE_OBJECT( pLicense, CLicense );

            if ( pLicense->m_lQuantity <= 0 )
            {
               delete pLicense;
               m_licenseArray.RemoveAt( i );
            }
            else
            {
               i++;
            }
         }
      }
      else
      {
         theApp.DisplayLastError();
         ResetLicenses();
      }
   }

   return m_bLicensesRefreshed;
}


BOOL CCertRemoveSelectDlg::RefreshCertificateList()

/*++

Routine Description:

   Refresh certificate list view from internal license list.

Arguments:

   None.

Return Values:

   BOOL.

--*/

{
   BeginWaitCursor();

   BOOL ok = ::LvRefreshObArray( &m_listCertificates, &g_removeColumnInfo, &m_licenseArray );

   EndWaitCursor();

   return ok;
}


int CALLBACK CompareLicenses(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)

/*++

Routine Description:

   Notification handler for LVM_SORTITEMS.

Arguments:

   lParam1 - object to sort.
   lParam2 - object to sort.
   lParamSort - sort criteria.

Return Values:

   Same as lstrcmp.

--*/

{
   CLicense *    pLic1 = (CLicense *) lParam1;
   CLicense *    pLic2 = (CLicense *) lParam2;

   VALIDATE_OBJECT( pLic1, CLicense );
   VALIDATE_OBJECT( pLic2, CLicense );

   int iResult;

   switch (g_removeColumnInfo.nSortedItem)
   {
   case LVID_REMOVE_SERIAL_NUMBER:
      iResult = pLic1->m_dwCertificateID - pLic2->m_dwCertificateID;
      break;

   case LVID_REMOVE_PRODUCT_NAME:
      iResult = pLic1->m_strProduct.CompareNoCase( pLic2->m_strProduct );
      break;

   case LVID_REMOVE_NUM_LICENSES:
      iResult = pLic1->m_lQuantity - pLic2->m_lQuantity;
      break;

   case LVID_REMOVE_SOURCE:
      iResult = pLic1->GetSourceDisplayName().CompareNoCase( pLic2->GetSourceDisplayName() );
      break;

   default:
      iResult = 0;
      break;
   }

   return g_removeColumnInfo.bSortOrder ? -iResult : iResult;
}


void CCertRemoveSelectDlg::UpdateSpinControlRange()

/*++

Routine Description:

   Update range of spin control for number of licenses.

Arguments:

   None.

Return Values:

   None.

--*/

{
   CLicense *  pLicense;

   UpdateData( TRUE );

   if ( pLicense = (CLicense*)::LvGetSelObj( &m_listCertificates ) )
   {
      m_spinLicenses.SetRange( 1, pLicense->m_lQuantity );
      m_nLicenses = pLicense->m_lQuantity;
      GetDlgItem( IDOK )->EnableWindow( TRUE );
   }
   else
   {
      m_spinLicenses.SetRange( 0, 0 );
      m_nLicenses = 0;
      GetDlgItem( IDOK )->EnableWindow( FALSE );
   }

   UpdateData( FALSE );
}


DWORD CCertRemoveSelectDlg::RemoveSelectedCertificate()

/*++

Routine Description:

   Remove the given number of licenses from the selected certificate.

Arguments:

   None.

Return Values:

   ERROR_SUCCESS
   NT status code
   Win error

--*/

{
   NTSTATUS    nt = STATUS_SUCCESS;

   if ( UpdateData( TRUE ) )
   {
      BOOL        bDisplayError = TRUE;
      CLicense *  pLicense;

      if ( !( pLicense = (CLicense*)::LvGetSelObj( &m_listCertificates ) ) )
      {
         // no certificate selected
         bDisplayError = FALSE;
      }
      else if ( ( m_nLicenses < 1 ) || ( m_nLicenses > pLicense->m_lQuantity ) )
      {
         // invalid number of licenses to remove
         AfxMessageBox( IDS_REMOVE_INVALID_NUM_LICENSES, MB_ICONEXCLAMATION | MB_OK, 0 );
         nt = ERROR_CANCELLED;
         bDisplayError = FALSE;
      }
      else
      {
         CString  strLicenses;
         CString  strConfirm;

         strLicenses.Format( TEXT("%d"), m_nLicenses );
         AfxFormatString2( strConfirm, IDS_REMOVE_CERTIFICATE_CONFIRM, strLicenses, pLicense->m_strProduct );

         int nResponse = AfxMessageBox( strConfirm, MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2 );

         if ( IDYES != nResponse )
         {
            nt = ERROR_CANCELLED;
            bDisplayError = FALSE;
         }
         else
         {
            // delete certificate
            LPSTR  pszAscServerName  = (LPSTR) LocalAlloc( LMEM_FIXED, 1 + m_strServerName.GetLength()        );
            LPSTR  pszAscProductName = (LPSTR) LocalAlloc( LMEM_FIXED, 1 + pLicense->m_strProduct.GetLength() );
            LPSTR  pszAscVendor      = (LPSTR) LocalAlloc( LMEM_FIXED, 1 + m_strVendor.GetLength()            );

            CString cstrClose;
            
            cstrClose.LoadString( IDS_CLOSETEXT );

            CWnd *pWnd = GetDlgItem( IDCANCEL );

            if( pWnd != NULL )
            {
                pWnd->SetWindowText( cstrClose );
            }

            if ( ( NULL == pszAscServerName ) || ( NULL == pszAscProductName ) || ( NULL == pszAscVendor ) )
            {
               nt = ERROR_NOT_ENOUGH_MEMORY;
            }
            else
            {
               wsprintfA( pszAscServerName,  "%ls", (LPCWSTR) m_strServerName        );
               wsprintfA( pszAscProductName, "%ls", (LPCWSTR) pLicense->m_strProduct );
               wsprintfA( pszAscVendor,      "%ls", (LPCWSTR) m_strVendor            );

               LLS_LICENSE_INFO_1   lic;

               nt = pLicense->CreateLicenseInfo( &lic );

               if ( STATUS_SUCCESS == nt )
               {
                  // only remove as many licenses as requested
                  lic.Quantity = m_nLicenses;

                  if ( !pLicense->m_strSource.CompareNoCase( TEXT( "None" ) ) )
                  {
                     nt = NoCertificateRemove( m_hWnd, pszAscServerName, m_dwRemoveFlags, 1, &lic );
                     bDisplayError = FALSE;
                  }
                  else
                  {
                     // get certificate source DLL path
                     CString  strKeyName =   TEXT( "Software\\LSAPI\\Microsoft\\CertificateSources\\" )
                                           + pLicense->m_strSource;
                     HKEY     hKeySource;

                     nt = RegOpenKeyEx( HKEY_LOCAL_MACHINE, strKeyName, 0, KEY_READ, &hKeySource );

                     if ( ( ERROR_PATH_NOT_FOUND == nt ) || ( ERROR_FILE_NOT_FOUND == nt ) )
                     {
                        AfxMessageBox( IDS_CERT_SOURCE_NOT_AVAILABLE, MB_ICONSTOP | MB_OK, 0 );
                        nt = ERROR_CANCELLED;
                        bDisplayError = FALSE;
                     }
                     else if ( ERROR_SUCCESS == nt )
                     {
                        TCHAR    szImagePath[ 1 + _MAX_PATH ];
                        DWORD    cbImagePath = sizeof( szImagePath );
                        DWORD    dwType;

                        nt = RegQueryValueEx( hKeySource, TEXT( "ImagePath" ), NULL, &dwType, (LPBYTE) szImagePath, &cbImagePath );

                        if ( ERROR_SUCCESS == nt )
                        {
                           TCHAR    szExpandedImagePath[ 1 + _MAX_PATH ];

                           BOOL ok = ExpandEnvironmentStrings( szImagePath, szExpandedImagePath, sizeof( szExpandedImagePath ) / sizeof( *szExpandedImagePath ) );

                           if ( !ok )
                           {
                              nt = GetLastError();
                           }
                           else
                           {
                              // load certificate source DLL
                              HINSTANCE hDll = ::LoadLibrary( szExpandedImagePath );

                              if ( NULL == hDll )
                              {
                                 nt = GetLastError();
                              }
                              else
                              {
                                 // get certificate remove function
                                 CHAR              szExportName[ 256 ];
                                 PCCF_REMOVE_API   pRemoveFn;

                                 wsprintfA( szExportName, "%lsCertificateRemove", (LPCWSTR) pLicense->m_strSource );
                                 pRemoveFn = (PCCF_REMOVE_API) GetProcAddress( hDll, szExportName );

                                 if ( NULL == pRemoveFn )
                                 {
                                    nt = GetLastError();
                                 }
                                 else
                                 {
                                    // remove certificate
                                    nt = (*pRemoveFn)( m_hWnd, pszAscServerName, m_dwRemoveFlags, 1, &lic );
                                    bDisplayError = FALSE;
                                 }

                                 ::FreeLibrary( hDll );
                              }
                           }
                        }

                        RegCloseKey( hKeySource );
                     }
                  }

                  pLicense->DestroyLicenseInfo( &lic );
               }
            }

            if ( NULL != pszAscServerName  )    LocalFree( pszAscServerName  );
            if ( NULL != pszAscProductName )    LocalFree( pszAscProductName );
            if ( NULL != pszAscVendor      )    LocalFree( pszAscVendor      );

            RefreshLicenses();
            RefreshCertificateList();
            UpdateSpinControlRange();
         }
      }

      if ( bDisplayError && ( ERROR_SUCCESS != nt ) )
      {
         theApp.SetLastError( nt );
         theApp.DisplayLastError();
      }
   }

   return nt;
}


BOOL CCertRemoveSelectDlg::ConnectServer()

/*++

Routine Description:

   Establish a connection to the license service on the target server.

Arguments:

   None.

Return Values:

   BOOL.

--*/

{
   if ( NULL == m_hLls )
   {
      LPTSTR   pszServerName;

      if ( m_strServerName.IsEmpty() )
      {
         pszServerName = NULL;
      }
      else
      {
         pszServerName = m_strServerName.GetBuffer( 0 );
      }

      ConnectTo( pszServerName, &m_hLls );

      if ( NULL != pszServerName )
      {
         m_strServerName.ReleaseBuffer();
      }
   }

   if ( NULL == m_hLls )
   {
      theApp.DisplayLastError();

      if ( ( NULL != m_hWnd ) && IsWindow( m_hWnd ) )
      {
         EndDialog( IDABORT );
      }
   }

   return ( NULL != m_hLls );
}


NTSTATUS CCertRemoveSelectDlg::ConnectTo( LPTSTR pszServerName, PLLS_HANDLE phLls )

/*++

Routine Description:

   Establish a connection to the license service on the given server.

Arguments:

   pszServerName (CString)
      The target server.  An empty value indicates the local server.
   phLls (PLLS_HANDLE)
      On return, holds the handle to the standard LLS RPC.

Return Values:

   STATUS_SUCCESS or NT status code.

--*/

{
   NTSTATUS    nt;

   nt = ::LlsConnect( pszServerName, phLls );
   theApp.SetLastLlsError( nt );

   if ( STATUS_SUCCESS != nt )
   {
      *phLls = NULL;
   }

   return nt;
}


void CCertRemoveSelectDlg::OnClickCertificateList(NMHDR* pNMHDR, LRESULT* pResult)

/*++

Routine Description:

   Handler for NM_CLICK of certificate list view.

Arguments:

   pNMHDR (NMHDR*)
   pResult (LRESULT*)

Return Values:

   None.

--*/

{
   UpdateSpinControlRange();
   *pResult = 1; // not handled...
}

void CCertRemoveSelectDlg::OnKeyDownCertificateList(NMHDR* pNMHDR, LRESULT* pResult)

/*++

Routine Description:

   Handler for LVN_KEYDOWN of certificate list view.

Arguments:

   pNMHDR (NMHDR*)
   pResult (LRESULT*)

Return Values:

   None.

--*/

{
   UpdateSpinControlRange();
   *pResult = 1; // not handled...
}


BOOL CCertRemoveSelectDlg::LoadImages()

/*++

Routine Description:

   Load icons for the list view.

Arguments:

   None.

Return Values:

   BOOL.

--*/

{
   BOOL bImagesLoaded = m_smallImages.Create( IDB_SMALL_ICONS, BMPI_SMALL_SIZE, 0, BMPI_RGB_BKGND );
   ASSERT( bImagesLoaded );

   return bImagesLoaded;
}


void CCertRemoveSelectDlg::OnRefresh()

/*++

Routine Description:

   Handler for BN_CLICK of refresh button.

Arguments:

   None.

Return Values:

   None.

--*/

{
   RefreshLicenses();
   RefreshCertificateList();
   UpdateSpinControlRange();
}


DWORD CCertRemoveSelectDlg::CertificateRemove( LPCSTR pszServerName, LPCSTR pszProductName, LPCSTR pszVendor, DWORD dwFlags, LPCSTR pszSourceToUse )

/*++

Routine Description:

   Display a dialog allowing the user to remove one or more license
   certificates from the system.

Arguments:

   pszServerName (LPCSTR)
      Name of the server on which licenses are to be removed.  A NULL value
      indicates the local server.
   pszProductName (LPCSTR)
      Product for which licenses are to be removed.  A NULL value indicates
      that the user should be allowed to remove licenses from any product.
   pszVendor (LPCSTR)
      Name of the vendor of the product.  This value should be NULL if
      pszProductName is NULL, and should be non-NULL if pszProductName is
      non-NULL.
   dwFlags (DWORD)
      Certificate removal options.  As of this writing, no flags are
      supported.
   pszSourceToUse (LPCSTR)
      Name of the secure certificate source by which licenses are to be
      removed, e.g., "Paper".  A NULL value indicates that the user should
      be allowed to remove licenses that were installed with any source.

Return Value:

   ERROR_SUCCESS
   Win error

--*/

{
   m_strServerName   = pszServerName  ? pszServerName  : "";
   m_strProductName  = pszProductName ? pszProductName : "";
   m_strVendor       = pszVendor      ? pszVendor      : "";
   m_dwRemoveFlags   = dwFlags;
   m_strSourceToUse  = pszSourceToUse ? pszSourceToUse : "";

   DoModal();

   return ERROR_SUCCESS;
}