windows-nt/Source/XPSP1/NT/inetsrv/iis/svcs/nntp/adminsso/feedinfo.cpp
2020-09-26 16:20:57 +08:00

1484 lines
32 KiB
C++

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
feedinfo.cpp
Abstract:
Author:
Magnus Hedlund (MagnusH) --
Revision History:
Kangrong Yan ( KangYan ) Feb-28-1998
Feed config change no longer calls NNTP RPC. It directly writes to MB
and synchronize with NNTPSVC's response in the metabase.
--*/
#include "stdafx.h"
#include "oleutil.h"
#include "nntpcmn.h"
#include "nntptype.h"
#include "nntpapi.h"
#include "feedpach.h"
#include "feeds.h"
#include "feedinfo.h"
#include <lmapibuf.h>
BOOL IsInboundFeed ( CFeed * pFeed )
{
DWORD dwType = pFeed->m_FeedType;
return FEED_IS_PASSIVE ( dwType ) || FEED_IS_PULL ( dwType );
}
BOOL IsOutboundFeed ( CFeed * pFeed )
{
DWORD dwType = pFeed->m_FeedType;
return FEED_IS_PUSH ( dwType );
}
NNTP_FEED_SERVER_TYPE FeedTypeToEnum ( FEED_TYPE ft )
{
NNTP_FEED_SERVER_TYPE type = NNTP_FEED_TYPE_PEER;
if ( FEED_IS_PEER ( ft ) ) {
type = NNTP_FEED_TYPE_PEER;
}
else if ( FEED_IS_MASTER ( ft ) ) {
type = NNTP_FEED_TYPE_MASTER;
}
else if ( FEED_IS_SLAVE ( ft ) ) {
type = NNTP_FEED_TYPE_SLAVE;
}
else {
_ASSERT ( FALSE );
}
return type;
}
void EnumToFeedType ( NNTP_FEED_SERVER_TYPE type, FEED_TYPE & ftMask )
{
DWORD dwType = 0;
// Clear out the feed type from the mask
ftMask &= ~FEED_REMOTE_MASK;
switch ( type ) {
case NNTP_FEED_TYPE_PEER:
dwType = FEED_TYPE_PEER;
break;
case NNTP_FEED_TYPE_MASTER:
dwType = FEED_TYPE_MASTER;
break;
case NNTP_FEED_TYPE_SLAVE:
dwType = FEED_TYPE_SLAVE;
break;
}
// Move the feed type into the mask:
ftMask |= dwType;
}
// Must define THIS_FILE_* macros to use NntpCreateException()
#define THIS_FILE_HELP_CONTEXT 0
#define THIS_FILE_PROG_ID _T("Nntpadm.Feeds.1")
#define THIS_FILE_IID IID_INntpAdminFeeds
CFeed::CFeed ( )
// CComBSTR's are automatically initialized to NULL
{
//
// Initialize all properties to 0
//
m_dwFeedId = 0;
m_dwPairFeedId = 0;
m_fAllowControlMessages = FALSE;
m_dwAuthenticationType = AUTH_PROTOCOL_NONE;
m_dwConcurrentSessions = 0;
m_fCreateAutomatically = FALSE;
m_dwFeedInterval = 0;
m_datePullNews = 0;
m_dwMaxConnectionAttempts = 0;
m_dwSecurityType = 0;
m_dwOutgoingPort = 0;
m_FeedType = FEED_TYPE_PULL | FEED_TYPE_PEER;
m_fEnabled = TRUE;
}
CFeed::~CFeed ( )
{
AssertValid ();
// CComBSTR's are automatically freed.
}
void CFeed::Destroy ()
{
AssertValid ();
// Need to empty all strings:
m_mszDistributions.Empty();
m_mszNewsgroups.Empty();
m_strRemoteServer.Empty();
m_strUucpName.Empty();
m_strAccountName.Empty();
m_strPassword.Empty();
m_strTempDirectory.Empty();
}
HRESULT CFeed::CreateFeed ( CFeed ** ppNewFeed )
{
CFeed * pFeedNew = new CFeed;
if ( pFeedNew ) {
*ppNewFeed = pFeedNew;
return NOERROR;
}
else {
*ppNewFeed = NULL;
return E_OUTOFMEMORY;
}
}
HRESULT CFeed::CreateFeedFromFeedInfo ( LPNNTP_FEED_INFO pFeedInfo, CFeed ** ppNewFeed )
{
HRESULT hr = NOERROR;
CFeed * pFeedNew;
*ppNewFeed = NULL;
hr = CreateFeed ( &pFeedNew );
BAIL_ON_FAILURE ( hr );
hr = pFeedNew->FromFeedInfo ( pFeedInfo );
BAIL_ON_FAILURE ( hr );
*ppNewFeed = pFeedNew;
Exit:
return hr;
}
HRESULT CFeed::CreateFeedFromINntpOneWayFeed ( INntpOneWayFeed * pFeed, CFeed ** ppNewFeed )
{
HRESULT hr = NOERROR;
CFeed * pFeedNew = NULL;
*ppNewFeed = NULL;
hr = CreateFeed ( &pFeedNew );
BAIL_ON_FAILURE ( hr );
hr = pFeedNew->FromINntpOneWayFeed ( pFeed );
BAIL_ON_FAILURE ( hr );
*ppNewFeed = pFeedNew;
return hr;
Exit:
if (pFeedNew)
delete pFeedNew;
return hr;
}
const CFeed & CFeed::operator= ( const CFeed & feed )
{
AssertValid ();
feed.AssertValid ();
// Check for assignment to self:
if ( &feed == this ) {
return *this;
}
// Empty the old feed values:
this->Destroy ();
// Copy all member variables:
m_dwFeedId = feed.m_dwFeedId;
m_dwPairFeedId = feed.m_dwPairFeedId;
m_FeedType = feed.m_FeedType;
m_fAllowControlMessages = feed.m_fAllowControlMessages;
m_dwAuthenticationType = feed.m_dwAuthenticationType;
m_dwConcurrentSessions = feed.m_dwConcurrentSessions;
m_fCreateAutomatically = feed.m_fCreateAutomatically;
m_fEnabled = feed.m_fEnabled;
m_mszDistributions = feed.m_mszDistributions;
m_dwFeedInterval = feed.m_dwFeedInterval;
m_datePullNews = feed.m_datePullNews;
m_dwMaxConnectionAttempts = feed.m_dwMaxConnectionAttempts;
m_mszNewsgroups = feed.m_mszNewsgroups;
m_dwSecurityType = feed.m_dwSecurityType;
m_dwOutgoingPort = feed.m_dwOutgoingPort;
m_strRemoteServer = feed.m_strRemoteServer;
m_strUucpName = feed.m_strUucpName;
m_strAccountName = feed.m_strAccountName;
m_strPassword = feed.m_strPassword;
m_strTempDirectory = feed.m_strTempDirectory;
m_strRemoteServer = feed.m_strRemoteServer;
m_EnumType = feed.m_EnumType;
return *this;
}
BOOL CFeed::CheckValid ( ) const
{
AssertValid ();
// Check Strings:
if (
!m_mszDistributions ||
!m_mszNewsgroups ||
!m_strUucpName ||
!m_strAccountName ||
!m_strPassword ||
!m_strRemoteServer
) {
return FALSE;
}
return TRUE;
}
HRESULT CFeed::get_FeedAction ( NNTP_FEED_ACTION * feedaction )
{
AssertValid ();
NNTP_FEED_ACTION result;
switch ( m_FeedType & FEED_ACTION_MASK ) {
case FEED_TYPE_PULL:
result = NNTP_FEED_ACTION_PULL;
break;
case FEED_TYPE_PUSH:
result = NNTP_FEED_ACTION_PUSH;
break;
case FEED_TYPE_PASSIVE:
result = NNTP_FEED_ACTION_ACCEPT;
break;
default:
_ASSERT ( FALSE );
result = NNTP_FEED_ACTION_PULL;
}
*feedaction = result;
return NOERROR;
}
HRESULT CFeed::put_FeedAction ( NNTP_FEED_ACTION feedaction )
{
AssertValid ();
FEED_TYPE ftNew;
ftNew = m_FeedType & (~FEED_ACTION_MASK);
switch ( feedaction ) {
case NNTP_FEED_ACTION_PULL:
ftNew |= FEED_TYPE_PULL;
break;
case NNTP_FEED_ACTION_PUSH:
ftNew |= FEED_TYPE_PUSH;
break;
case NNTP_FEED_ACTION_ACCEPT:
ftNew |= FEED_TYPE_PASSIVE;
break;
default:
_ASSERT ( FALSE );
return TranslateFeedError ( ERROR_INVALID_PARAMETER, FEED_PARM_FEEDTYPE );
}
m_FeedType = ftNew;
return NOERROR;
}
HRESULT CFeed::FromFeedInfo ( const NNTP_FEED_INFO * pFeedInfo )
{
AssertValid ();
FILETIME ftPullNewsLocal;
SYSTEMTIME stPullNews;
this->Destroy ();
m_dwFeedId = pFeedInfo->FeedId;
m_dwPairFeedId = pFeedInfo->FeedPairId;
m_FeedType = pFeedInfo->FeedType;
m_fAllowControlMessages = pFeedInfo->fAllowControlMessages;
m_dwAuthenticationType = pFeedInfo->AuthenticationSecurityType;
m_dwConcurrentSessions = pFeedInfo->ConcurrentSessions;
m_fCreateAutomatically = pFeedInfo->AutoCreate;
m_fEnabled = pFeedInfo->Enabled;
m_mszDistributions = pFeedInfo->Distribution ? pFeedInfo->Distribution : _T("\0");
if ( !FEED_IS_PULL (m_FeedType) ||
(
pFeedInfo->PullRequestTime.dwLowDateTime == 0 &&
pFeedInfo->PullRequestTime.dwHighDateTime == 0
)
) {
//
// Not a pull feed - so default to something reasonable here:
//
GetLocalTime ( &stPullNews );
}
else {
FileTimeToLocalFileTime ( &pFeedInfo->PullRequestTime, &ftPullNewsLocal );
FileTimeToSystemTime ( &ftPullNewsLocal, &stPullNews );
}
SystemTimeToVariantTime ( &stPullNews, &m_datePullNews );
m_dwFeedInterval = pFeedInfo->FeedInterval;
m_dwMaxConnectionAttempts = pFeedInfo->MaxConnectAttempts;
m_mszNewsgroups = pFeedInfo->Newsgroups ? pFeedInfo->Newsgroups : _T("\0");
m_dwSecurityType = pFeedInfo->SessionSecurityType;
m_dwOutgoingPort = pFeedInfo->OutgoingPort;
m_strUucpName = pFeedInfo->UucpName ? pFeedInfo->UucpName : _T("");
m_strAccountName = pFeedInfo->NntpAccountName ? pFeedInfo->NntpAccountName : _T("");
m_strPassword = pFeedInfo->NntpPassword ? pFeedInfo->NntpPassword : _T("");
m_strTempDirectory = pFeedInfo->FeedTempDirectory;
if ( pFeedInfo->Distribution ) {
_ASSERT ( m_mszDistributions.SizeInBytes () == pFeedInfo->cbDistribution );
}
_ASSERT ( m_mszNewsgroups.SizeInBytes () == pFeedInfo->cbNewsgroups );
m_strRemoteServer = pFeedInfo->ServerName ? pFeedInfo->ServerName : _T("");
m_EnumType = FeedTypeToEnum ( m_FeedType );
// Check Strings:
if ( !CheckValid () ) {
return E_OUTOFMEMORY;
}
return NOERROR;
}
HRESULT CFeed::ToFeedInfo ( LPNNTP_FEED_INFO pFeedInfo )
{
TraceFunctEnter ( "CFeed::ToFeedInfo" );
AssertValid ();
_ASSERT ( IS_VALID_OUT_PARAM ( pFeedInfo ) );
HRESULT hr = NOERROR;
SYSTEMTIME stPullNews;
FILETIME ftPullNewsLocal;
EnumToFeedType ( m_EnumType, m_FeedType );
ZeroMemory ( pFeedInfo, sizeof (*pFeedInfo) );
pFeedInfo->ServerName = m_strRemoteServer;
pFeedInfo->FeedId = m_dwFeedId;
pFeedInfo->FeedPairId = m_dwPairFeedId;
pFeedInfo->FeedType = m_FeedType;
pFeedInfo->fAllowControlMessages = m_fAllowControlMessages;
pFeedInfo->AuthenticationSecurityType = m_dwAuthenticationType;
pFeedInfo->ConcurrentSessions = m_dwConcurrentSessions;
pFeedInfo->AutoCreate = m_fCreateAutomatically;
pFeedInfo->Enabled = m_fEnabled;
//
// Convert time formats:
//
VariantTimeToSystemTime ( m_datePullNews, &stPullNews );
SystemTimeToFileTime ( &stPullNews, &ftPullNewsLocal );
LocalFileTimeToFileTime ( &ftPullNewsLocal, &pFeedInfo->PullRequestTime);
ZeroMemory ( &pFeedInfo->StartTime, sizeof ( FILETIME ) );
pFeedInfo->FeedInterval = m_dwFeedInterval;
pFeedInfo->MaxConnectAttempts = m_dwMaxConnectionAttempts;
pFeedInfo->SessionSecurityType = m_dwSecurityType;
pFeedInfo->OutgoingPort = m_dwOutgoingPort;
pFeedInfo->ServerName = m_strRemoteServer;
pFeedInfo->UucpName = m_strUucpName;
pFeedInfo->cbUucpName = STRING_BYTE_LENGTH ( m_strUucpName );
pFeedInfo->NntpAccountName = m_strAccountName;
pFeedInfo->cbAccountName = STRING_BYTE_LENGTH ( m_strAccountName );
pFeedInfo->NntpPassword = m_strPassword;
pFeedInfo->cbPassword = STRING_BYTE_LENGTH ( m_strPassword );
pFeedInfo->FeedTempDirectory = m_strTempDirectory;
pFeedInfo->cbFeedTempDirectory = STRING_BYTE_LENGTH ( m_strTempDirectory );
pFeedInfo->Distribution = (LPWSTR) (LPCWSTR) m_mszDistributions;
pFeedInfo->cbDistribution = m_mszDistributions.SizeInBytes();
pFeedInfo->Newsgroups = (LPWSTR) (LPCWSTR) m_mszNewsgroups;
pFeedInfo->cbNewsgroups = m_mszNewsgroups.SizeInBytes();
TraceFunctLeave ();
return hr;
}
HRESULT CFeed::FromINntpOneWayFeed ( INntpOneWayFeed * pFeed )
{
AssertValid ();
CNntpOneWayFeed * pPrivateFeed;
pPrivateFeed = (CNntpOneWayFeed *) pFeed;
*this = pPrivateFeed->m_feed;
if ( !CheckValid () ) {
return E_OUTOFMEMORY;
}
return NOERROR;
}
HRESULT CFeed::ToINntpOneWayFeed ( INntpOneWayFeed ** ppFeed )
{
AssertValid ();
HRESULT hr = NOERROR;
CComObject<CNntpOneWayFeed> * pFeed = NULL;
hr = CComObject<CNntpOneWayFeed>::CreateInstance ( &pFeed );
if ( FAILED(hr) ) {
goto Exit;
}
pFeed->m_feed = *this;
if ( !pFeed->m_feed.CheckValid() ) {
hr = E_OUTOFMEMORY;
goto Exit;
}
hr = pFeed->QueryInterface ( IID_INntpOneWayFeed, (void **) ppFeed );
if ( FAILED(hr) ) {
goto Exit;
}
Exit:
if ( FAILED(hr) ) {
delete pFeed;
}
return hr;
}
HRESULT CFeed::CheckConfirm( IN DWORD dwFeedId,
IN DWORD dwInstanceId,
IN CMetabaseKey* pMK,
OUT PDWORD pdwErr,
OUT PDWORD pdwErrMask )
{
TraceFunctEnter( "CFeed::CheckConfirm" );
_ASSERT( pMK );
_ASSERT( dwFeedId > 0 );
HRESULT hr;
const DWORD dwMaxAttempts = 10;
const DWORD dwWaitMilliSeconds = 500;
DWORD dwHandShake;
for ( int i = 0; i < dwMaxAttempts; i++ ) {
hr = OpenKey( dwFeedId, pMK, METADATA_PERMISSION_READ, dwInstanceId );
if ( FAILED( hr ) ) {
if ( HRESULTTOWIN32( hr ) == ERROR_PATH_BUSY ) {
Sleep( dwWaitMilliSeconds );
continue;
}
else {
ErrorTrace(0, "Open key fail with 0x%x", hr );
return hr;
}
}
//
// getting the handshake
//
hr = pMK->GetDword( MD_FEED_HANDSHAKE, &dwHandShake );
if ( FAILED( hr ) ) { // shouldn't happen , it's an error
pMK->Close();
ErrorTrace(0, "Get handshake fail with 0x%x", hr );
return hr;
}
if ( dwHandShake != FEED_UPDATE_CONFIRM ) {
pMK->Close();
Sleep( dwWaitMilliSeconds );
continue;
}
//
// Now get error / masks
//
hr = pMK->GetDword( MD_FEED_ADMIN_ERROR, pdwErr );
if ( FAILED( hr ) ) { // the server is possibly not
// writing the confirm /error
// in good sequence, lets give
// it more chances
pMK->Close();
Sleep( dwWaitMilliSeconds );
continue;
}
hr = pMK->GetDword( MD_FEED_ERR_PARM_MASK, pdwErrMask );
if ( FAILED( hr ) ) { // same comments as above
pMK->Close();
Sleep( dwWaitMilliSeconds );
continue;
}
//
// Now we are done
//
pMK->Close();
break;
}
if ( i == dwMaxAttempts ) return E_FAIL;
else return S_OK;
}
HRESULT CFeed::Add ( LPCWSTR strServer, DWORD dwInstance, CMetabaseKey* pMK )
{
TraceFunctEnter ( "CFeed::Add" );
AssertValid ();
HRESULT hr = NOERROR;
DWORD dwError = NOERROR;
DWORD dwParmErr = 0;
NNTP_FEED_INFO feedinfo;
DWORD dwFeedId = 0;
FillMemory ( &feedinfo, sizeof (feedinfo), 0 );
hr = ToFeedInfo ( &feedinfo );
if ( FAILED(hr) ) {
goto Exit;
}
feedinfo.FeedId = 0;
//
// KangYan: RPC goes away. We use metabase writes
//
hr = AddFeedToMB( &feedinfo, pMK, &dwParmErr, dwInstance, &dwFeedId );
if ( FAILED( hr ) ) {
ErrorTrace(0, "Add to MB fail with 0x%x", hr );
goto Exit;
}
hr = CheckConfirm( dwFeedId, dwInstance, pMK, &dwError, &dwParmErr );
if ( FAILED( hr ) ) {
ErrorTrace( 0, "Check confirm fail with 0x%x", hr );
goto Exit;
}
if ( dwError != NOERROR ) {
ErrorTraceX ( (LPARAM) this, "Failed to add feed: %x (%x)", dwError, dwParmErr );
hr = TranslateFeedError ( dwError, dwParmErr );
goto Exit;
}
this->m_dwFeedId = dwFeedId;
Exit:
TraceFunctLeave ();
return hr;
}
HRESULT CFeed::Set ( LPCWSTR strServer, DWORD dwInstance, CMetabaseKey* pMK )
{
TraceFunctEnter ( "CFeed::Set" );
AssertValid ();
HRESULT hr = NOERROR;
DWORD dwError = NOERROR;
DWORD dwParmErr = 0;
NNTP_FEED_INFO feedinfo;
FillMemory ( &feedinfo, sizeof (feedinfo), 0 );
hr = ToFeedInfo ( &feedinfo );
if ( FAILED(hr) ) {
goto Exit;
}
//
// KangYan: RPC goes away. We use metabase writes
//
hr = SetFeedToMB( &feedinfo,
pMK,
&dwParmErr,
dwInstance );
if ( FAILED( hr ) ) {
ErrorTrace(0, "Set MB fail with 0x%x", hr );
goto Exit;
}
hr = CheckConfirm( feedinfo.FeedId, dwInstance, pMK, &dwError, &dwParmErr );
if ( FAILED( hr ) ) {
ErrorTrace(0, "Check confirm fail with 0x%x", hr );
goto Exit;
}
if ( dwError != NOERROR ) {
ErrorTraceX ( (LPARAM) this, "Failed to set feed[%d]: %x (%x)", m_dwFeedId, dwError, dwParmErr );
hr = TranslateFeedError ( dwError, dwParmErr );
goto Exit;
}
Exit:
TraceFunctLeave ();
return hr;
}
HRESULT CFeed::Remove ( LPCWSTR strServer, DWORD dwInstance, CMetabaseKey* pMK )
{
TraceFunctEnter ( "CFeed::Remove" );
AssertValid ();
HRESULT hr = NOERROR;
DWORD dwError = NOERROR;
hr = DeleteFeed( m_dwFeedId, pMK, dwInstance );
if ( FAILED( hr ) ) {
dwError = HRESULTTOWIN32( hr );
ErrorTraceX ( (LPARAM) this, "Failed to remove feed[%d]: %x", m_dwFeedId, hr );
hr = TranslateFeedError ( dwError );
goto Exit;
}
Exit:
TraceFunctLeave ();
return hr;
}
HRESULT CFeed::SetPairId ( LPCWSTR strServer, DWORD dwInstance, DWORD dwPairId, CMetabaseKey* pMK )
{
TraceFunctEnter ( "CFeed::SetPairID" );
AssertValid ();
HRESULT hr = NOERROR;
DWORD dwError = NOERROR;
DWORD dwParmErr = 0;
NNTP_FEED_INFO feedinfo;
//
// Assume that this feed was just Added/Set to the server.
//
_ASSERT ( dwPairId != m_dwFeedId );
m_dwPairFeedId = dwPairId;
FillMemory ( &feedinfo, sizeof (feedinfo), 0 );
hr = ToFeedInfo ( &feedinfo );
if ( FAILED(hr) ) {
goto Exit;
}
//
// KangYan: RPC goes away. We use metabase writes
//
hr = SetFeedToMB( &feedinfo,
pMK,
&dwParmErr,
dwInstance );
if ( FAILED( hr ) ) {
ErrorTrace(0, "Set MB fail with 0x%x", hr );
goto Exit;
}
hr = CheckConfirm( feedinfo.FeedId, dwInstance, pMK, &dwError, &dwParmErr );
if ( FAILED( hr ) ) {
ErrorTrace(0, "Check confirm fail with 0x%x", hr );
goto Exit;
}
if ( dwError != NOERROR ) {
ErrorTraceX ( (LPARAM) this, "Failed to set feed[%d]: %x (%x)", m_dwFeedId, dwError, dwParmErr );
hr = TranslateFeedError ( dwError, dwParmErr );
goto Exit;
}
Exit:
TraceFunctLeave ();
return hr;
}
DWORD GetBitPosition ( DWORD dwValue )
{
_ASSERT ( dwValue != 0 );
// _ASSERT ( Only one bit should be on );
DWORD dwResult;
DWORD dwTemp;
for ( dwTemp = dwValue, dwResult = (DWORD) -1;
dwTemp != 0;
dwTemp = dwTemp >> 1, dwResult++ ) {
// Empty for
// Make sure there is at most one bit on:
_ASSERT ( !(dwTemp & 1) || dwTemp == 1 );
}
return dwResult;
}
HRESULT CFeed::TranslateFeedError ( DWORD dwErrorCode, DWORD dwParmErr )
{
HRESULT hr;
_ASSERT ( dwErrorCode != NOERROR );
if ( dwErrorCode != ERROR_INVALID_PARAMETER ||
dwParmErr == 0 ||
dwParmErr == (DWORD) -1
) {
hr = RETURNCODETOHRESULT ( dwErrorCode );
}
else {
DWORD dwBitPosition;
DWORD nID;
dwBitPosition = GetBitPosition ( dwParmErr );
nID = IDS_FEED_PARM_ERR_BASE + dwBitPosition;
hr = NntpCreateException ( nID );
}
return hr;
}
#ifdef DEBUG
void CFeed::AssertValid ( ) const
{
_ASSERT ( !IsBadWritePtr ( (void *) this, sizeof (*this) ) );
//
// !!!magnush - Add more debug code here
//
}
#endif
/////////////////////////////////////////////////////////////////////
//
// CFeedPair Implementation
//
CFeedPair::CFeedPair () :
m_type ( NNTP_FEED_TYPE_PEER ),
m_pInbound ( NULL ),
m_pOutbound ( NULL ),
m_pNext ( NULL )
{
}
CFeedPair::~CFeedPair ()
{
AssertValid ();
Destroy ();
}
void CFeedPair::Destroy ()
{
AssertValid ();
// !!!magnush - Fix reference counting problem here
delete m_pInbound;
delete m_pOutbound;
m_pInbound = NULL;
m_pOutbound = NULL;
}
HRESULT CFeedPair::CreateFeedPair (
CFeedPair ** ppNewFeedPair,
BSTR strRemoteServer,
NNTP_FEED_SERVER_TYPE type
)
{
_ASSERT ( IS_VALID_STRING (strRemoteServer) );
HRESULT hr = NOERROR;
CFeedPair * pNewFeedPair;
*ppNewFeedPair = NULL;
// Allocate a new feed pair:
pNewFeedPair = new CFeedPair;
if ( pNewFeedPair == NULL ) {
BAIL_WITH_FAILURE ( hr, E_OUTOFMEMORY );
}
// Copy properties:
pNewFeedPair->m_strRemoteServer = strRemoteServer;
pNewFeedPair->m_type = type;
// Check for failed copy:
if ( pNewFeedPair->m_strRemoteServer == NULL ) {
BAIL_WITH_FAILURE ( hr, E_OUTOFMEMORY );
}
// Set the out parameter to the new feed:
*ppNewFeedPair = pNewFeedPair;
Exit:
if ( FAILED(hr) ) {
delete pNewFeedPair;
}
return hr;
}
HRESULT CFeedPair::AddFeed ( CFeed * pFeed )
{
AssertValid ();
HRESULT hr = NOERROR;
_ASSERT ( IS_VALID_STRING ( m_strRemoteServer ) );
_ASSERT ( IS_VALID_STRING ( pFeed->m_strRemoteServer ) );
//
// Check error conditions:
// 1. Different remote server name
// 2. Different feed type
// 3. Two inbound feeds
// 4. Two outbound feeds
// 5. Bad feed type
//
if ( lstrcmpi ( m_strRemoteServer, pFeed->m_strRemoteServer ) != 0 ) {
BAIL_WITH_FAILURE ( hr, E_FAIL );
}
if ( m_type != pFeed->m_EnumType ) {
BAIL_WITH_FAILURE ( hr, E_FAIL );
}
if ( IsInboundFeed ( pFeed ) && m_pInbound != NULL ) {
BAIL_WITH_FAILURE ( hr, E_FAIL );
}
if ( IsOutboundFeed ( pFeed ) && m_pOutbound != NULL ) {
BAIL_WITH_FAILURE ( hr, E_FAIL );
}
if ( !IsInboundFeed ( pFeed ) && !IsOutboundFeed ( pFeed ) ) {
_ASSERT (FALSE);
BAIL_WITH_FAILURE ( hr, E_FAIL );
}
//
// Everything is okay so take the feed into this pair:
//
if ( IsInboundFeed ( pFeed ) ) {
m_pInbound = pFeed;
}
else {
// It's an outbound feed:
m_pOutbound = pFeed;
}
Exit:
return hr;
}
HRESULT CFeedPair::FromINntpFeed ( INntpFeed * pFeed )
{
AssertValid ();
HRESULT hr = NOERROR;
CComPtr<INntpOneWayFeed> pInbound;
CComPtr<INntpOneWayFeed> pOutbound;
CFeed * pFeedInbound = NULL;
CFeed * pFeedOutbound = NULL;
m_strRemoteServer.Empty();
pFeed->get_RemoteServer ( &m_strRemoteServer );
pFeed->get_FeedType ( &m_type );
pFeed->get_InboundFeed ( &pInbound );
pFeed->get_OutboundFeed ( &pOutbound );
if ( pInbound ) {
hr = CFeed::CreateFeedFromINntpOneWayFeed ( pInbound, &pFeedInbound );
BAIL_ON_FAILURE(hr);
pFeedInbound->m_strRemoteServer = m_strRemoteServer;
}
if ( pOutbound ) {
hr = CFeed::CreateFeedFromINntpOneWayFeed ( pOutbound, &pFeedOutbound );
BAIL_ON_FAILURE(hr);
pFeedOutbound->m_strRemoteServer = m_strRemoteServer;
}
delete m_pInbound;
delete m_pOutbound;
m_pInbound = pFeedInbound;
m_pOutbound = pFeedOutbound;
Exit:
if ( FAILED(hr) ) {
delete pFeedInbound;
delete pFeedOutbound;
}
return hr;
}
HRESULT CFeedPair::ToINntpFeed ( INntpFeed ** ppFeed )
{
AssertValid ();
CComObject<CNntpFeed> * pFeed = NULL;
HRESULT hr = NOERROR;
hr = CComObject<CNntpFeed>::CreateInstance ( &pFeed );
if ( FAILED(hr) ) {
goto Exit;
}
hr = pFeed->FromFeedPair ( this );
if ( FAILED(hr) ) {
goto Exit;
}
hr = pFeed->QueryInterface ( IID_INntpFeed, (void **) ppFeed );
if ( FAILED(hr) ) {
goto Exit;
}
Exit:
return hr;
}
HRESULT CFeedPair::AddIndividualFeed ( LPCWSTR strServer,
DWORD dwInstance,
CFeed * pFeed ,
CMetabaseKey* pMK )
{
AssertValid ();
pFeed->m_dwPairFeedId = 0;
pFeed->m_EnumType = m_type;
pFeed->m_strRemoteServer = m_strRemoteServer;
return pFeed->Add ( strServer, dwInstance, pMK );
}
HRESULT CFeedPair::SetIndividualFeed ( LPCWSTR strServer, DWORD dwInstance, CFeed * pFeed, CMetabaseKey* pMK )
{
AssertValid ();
pFeed->m_dwPairFeedId = 0;
pFeed->m_EnumType = m_type;
pFeed->m_strRemoteServer = m_strRemoteServer;
return pFeed->Set ( strServer, dwInstance, pMK );
}
HRESULT CFeedPair::SetPairIds ( LPCWSTR strServer, DWORD dwInstance,
CFeed * pFeed1, CFeed * pFeed2, CMetabaseKey* pMK )
{
AssertValid ();
HRESULT hr = NOERROR;
DWORD dwPairId1;
DWORD dwPairId2;
dwPairId1 = pFeed2 ? pFeed2->m_dwFeedId : 0;
dwPairId2 = pFeed1 ? pFeed1->m_dwFeedId : 0;
if ( pFeed1 ) {
hr = pFeed1->SetPairId ( strServer, dwInstance, dwPairId1, pMK );
BAIL_ON_FAILURE(hr);
}
if ( pFeed2 ) {
hr = pFeed2->SetPairId ( strServer, dwInstance, dwPairId2, pMK );
BAIL_ON_FAILURE(hr);
}
Exit:
return hr;
}
HRESULT CFeedPair::AddToServer ( LPCWSTR strServer, DWORD dwInstance, CMetabaseKey* pMK )
{
AssertValid ();
HRESULT hr = E_UNEXPECTED;
if ( m_pInbound ) {
hr = AddIndividualFeed ( strServer, dwInstance, m_pInbound, pMK );
BAIL_ON_FAILURE(hr);
}
if ( m_pOutbound ) {
hr = AddIndividualFeed ( strServer, dwInstance, m_pOutbound, pMK );
BAIL_ON_FAILURE(hr);
}
if ( m_pInbound && m_pOutbound ) {
HRESULT hr2;
hr2 = SetPairIds ( strServer, dwInstance, m_pInbound, m_pOutbound, pMK );
_ASSERT ( SUCCEEDED(hr2) );
}
Exit:
if ( FAILED(hr) ) {
//
// Undo the add:
//
IErrorInfo *pErrorInfo = NULL;
if (FAILED(GetErrorInfo(NULL, &pErrorInfo))) {
pErrorInfo = NULL;
}
if ( m_pInbound ) {
m_pInbound->Remove ( strServer, dwInstance, pMK );
}
if ( m_pOutbound ) {
m_pOutbound->Remove ( strServer, dwInstance, pMK );
}
if (pErrorInfo != NULL) {
SetErrorInfo(NULL, pErrorInfo);
}
}
return hr;
}
HRESULT CFeedPair::UndoFeedAction (
LPCWSTR strServer,
DWORD dwInstance,
CFeed * pNewFeed,
CFeed * pOldFeed,
CMetabaseKey* pMK
)
{
AssertValid ();
HRESULT hr = NOERROR;
if ( pNewFeed != NULL ) {
if ( pOldFeed ) {
hr = pOldFeed->Set ( strServer, dwInstance, pMK );
}
else {
hr = pNewFeed->Remove ( strServer, dwInstance, pMK );
}
}
return hr;
}
HRESULT CFeedPair::SetToServer ( LPCWSTR strServer, DWORD dwInstance, INntpFeed * pFeed, CMetabaseKey* pMK )
{
AssertValid ();
HRESULT hr = NOERROR;
CComPtr<INntpOneWayFeed> pInbound;
CComPtr<INntpOneWayFeed> pOutbound;
CFeed * pNewInbound = NULL;
CFeed * pNewOutbound = NULL;
CComBSTR strOldRemoteServer;
strOldRemoteServer = m_strRemoteServer;
pFeed->get_RemoteServer ( &m_strRemoteServer );
pFeed->get_InboundFeed ( &pInbound );
pFeed->get_OutboundFeed ( &pOutbound );
//
// Add the new feeds:
//
if ( !m_pInbound && pInbound ) {
hr = CFeed::CreateFeedFromINntpOneWayFeed ( pInbound, &pNewInbound );
BAIL_ON_FAILURE(hr);
hr = AddIndividualFeed ( strServer, dwInstance, pNewInbound, pMK );
BAIL_ON_FAILURE(hr);
}
if ( !m_pOutbound && pOutbound ) {
hr = CFeed::CreateFeedFromINntpOneWayFeed ( pOutbound, &pNewOutbound );
BAIL_ON_FAILURE(hr);
hr = AddIndividualFeed ( strServer, dwInstance, pNewOutbound, pMK );
BAIL_ON_FAILURE(hr);
}
//
// Set the existing feeds:
//
if ( m_pInbound && pInbound ) {
// The Inbound feed exists already, so call set on it:
hr = CFeed::CreateFeed ( &pNewInbound );
BAIL_ON_FAILURE(hr);
*pNewInbound = *m_pInbound;
hr = pNewInbound->FromINntpOneWayFeed ( pInbound );
BAIL_ON_FAILURE(hr);
hr = SetIndividualFeed ( strServer, dwInstance, pNewInbound, pMK );
BAIL_ON_FAILURE(hr);
}
if ( m_pOutbound && pOutbound ) {
// The Outbound feed exists already, so call set on it:
hr = CFeed::CreateFeed ( &pNewOutbound );
BAIL_ON_FAILURE(hr);
*pNewOutbound = *m_pOutbound;
hr = pNewOutbound->FromINntpOneWayFeed ( pOutbound );
BAIL_ON_FAILURE(hr);
hr = SetIndividualFeed ( strServer, dwInstance, pNewOutbound, pMK );
BAIL_ON_FAILURE(hr);
}
//
// Remove the deleted feeds:
//
if ( m_pInbound && !pInbound ) {
HRESULT hr2;
hr2 = m_pInbound->Remove ( strServer, dwInstance, pMK );
_ASSERT ( SUCCEEDED(hr2) );
}
if ( m_pOutbound && !pOutbound ) {
HRESULT hr2;
hr2 = m_pOutbound->Remove ( strServer, dwInstance, pMK );
_ASSERT ( SUCCEEDED(hr2) );
}
SetPairIds ( strServer, dwInstance, pNewInbound, pNewOutbound, pMK );
_ASSERT ( SUCCEEDED(hr) );
delete m_pInbound;
delete m_pOutbound;
m_pInbound = pNewInbound;
m_pOutbound = pNewOutbound;
Exit:
if ( FAILED(hr) ) {
//
// Attempt to back out the changes:
//
UndoFeedAction ( strServer, dwInstance, pNewInbound, m_pInbound, pMK );
UndoFeedAction ( strServer, dwInstance, pNewOutbound, m_pOutbound, pMK );
if ( pNewOutbound ) {
if ( m_pOutbound ) {
m_pOutbound->Set ( strServer, dwInstance, pMK );
}
else {
pNewOutbound->Remove ( strServer, dwInstance, pMK );
}
}
delete pNewInbound;
delete pNewOutbound;
m_strRemoteServer = strOldRemoteServer;
}
return hr;
}
HRESULT CFeedPair::RemoveFromServer ( LPCWSTR strServer, DWORD dwInstance, CMetabaseKey* pMK )
{
AssertValid ();
HRESULT hr = NOERROR;
if ( m_pInbound ) {
hr = m_pInbound->Remove ( strServer, dwInstance, pMK );
}
if ( m_pOutbound ) {
hr = m_pOutbound->Remove ( strServer, dwInstance, pMK );
}
return hr;
}
BOOL CFeedPair::ContainsFeedId ( DWORD dwFeedId )
{
AssertValid ();
if ( m_pInbound && m_pInbound->m_dwFeedId == dwFeedId ) {
return TRUE;
}
if ( m_pOutbound && m_pOutbound->m_dwFeedId == dwFeedId ) {
return TRUE;
}
return FALSE;
}
#ifdef DEBUG
void CFeedPair::AssertValid ( ) const
{
_ASSERT ( !IsBadWritePtr ( (void *) this, sizeof (*this) ) );
if ( m_pInbound ) {
m_pInbound->AssertValid ();
}
if ( m_pOutbound ) {
m_pOutbound->AssertValid ();
}
}
#endif
/////////////////////////////////////////////////////////////////////
//
// CFeedPairList Implementation
//
CFeedPairList::CFeedPairList () :
m_cCount ( 0 ),
m_pHead ( NULL ),
m_pTail ( NULL )
{
}
CFeedPairList::~CFeedPairList ()
{
AssertValid ();
Empty();
}
DWORD CFeedPairList::GetCount ( ) const
{
AssertValid ();
return m_cCount;
}
void CFeedPairList::Empty ( )
{
AssertValid ();
CFeedPair * pPair;
CFeedPair * pKill;
pPair = m_pHead;
while ( pPair ) {
pKill = pPair;
pPair = pPair->m_pNext;
delete pKill;
}
m_cCount = 0;
m_pHead = NULL;
m_pTail = NULL;
}
CFeedPair * CFeedPairList::Item ( DWORD index )
{
AssertValid ();
CFeedPair * pResult = NULL;
DWORD i;
if ( index >= m_cCount ) {
return NULL;
}
pResult = m_pHead;
_ASSERT ( pResult );
for ( i = 0; i < index; i++ ) {
pResult = pResult->m_pNext;
_ASSERT ( pResult );
}
return pResult;
}
void CFeedPairList::Add ( CFeedPair * pFeedPair )
{
AssertValid ();
_ASSERT ( GetPairIndex ( pFeedPair ) == (DWORD) -1 );
pFeedPair->m_pNext = NULL;
if ( m_pTail == NULL ) {
//
// Handle special case - Adding to empty list:
//
_ASSERT ( m_pHead == NULL );
m_pHead = pFeedPair;
m_pTail = pFeedPair;
}
else {
m_pTail->m_pNext = pFeedPair;
m_pTail = pFeedPair;
}
m_cCount++;
}
void CFeedPairList::Remove ( CFeedPair * pFeedPair )
{
AssertValid ();
_ASSERT ( GetPairIndex ( pFeedPair ) != (DWORD) -1 );
CFeedPair * pLead;
CFeedPair * pFollow;
for ( pLead = m_pHead, pFollow = NULL;
pLead != NULL && pLead != pFeedPair;
pFollow = pLead, pLead = pLead->m_pNext
) {
// Empty For
}
_ASSERT ( pLead );
if ( !pLead ) {
return;
}
if ( pFollow != NULL ) {
pFollow->m_pNext = pLead->m_pNext;
}
if ( m_pHead == pFeedPair ) {
m_pHead = m_pHead->m_pNext;
}
if ( m_pTail == pFeedPair ) {
m_pTail = pFollow;
}
delete pFeedPair;
m_cCount--;
}
CFeedPair * CFeedPairList::Find ( DWORD dwFeedId )
{
AssertValid ();
CFeedPair * pResult;
if ( dwFeedId == 0 ) {
return NULL;
}
for ( pResult = m_pHead; pResult != NULL; pResult = pResult->m_pNext ) {
if ( pResult->ContainsFeedId ( dwFeedId ) ) {
return pResult;
}
}
return NULL;
}
DWORD CFeedPairList::GetPairIndex ( CFeedPair * pPairToFind ) const
{
AssertValid ();
_ASSERT ( pPairToFind );
DWORD index;
CFeedPair * pFeedPair;
for ( pFeedPair = m_pHead, index = 0;
pFeedPair != NULL;
pFeedPair = pFeedPair->m_pNext, index++ ) {
if ( pFeedPair == pPairToFind ) {
return index;
}
}
return (DWORD) -1;
}
#ifdef DEBUG
void CFeedPairList::AssertValid ( ) const
{
_ASSERT ( !IsBadWritePtr ( (void *) this, sizeof (*this) ) );
// Walk the list and assert each feed pair is valid:
CFeedPair * pPair;
DWORD cCount;
for ( cCount = 0, pPair = m_pHead;
pPair != NULL;
pPair = pPair->m_pNext, cCount++
) {
_ASSERT ( !IsBadWritePtr ( pPair, sizeof (*pPair) ) );
_ASSERT ( IS_VALID_STRING ( pPair->m_strRemoteServer ) );
_ASSERT ( pPair->m_pInbound || pPair->m_pOutbound );
if ( pPair->m_pInbound ) {
_ASSERT ( IS_VALID_STRING ( pPair->m_pInbound->m_strRemoteServer ) );
_ASSERT ( !lstrcmpi ( pPair->m_pInbound->m_strRemoteServer, pPair->m_strRemoteServer ) );
}
if ( pPair->m_pOutbound ) {
_ASSERT ( IS_VALID_STRING ( pPair->m_pOutbound->m_strRemoteServer ) );
_ASSERT ( !lstrcmpi ( pPair->m_pOutbound->m_strRemoteServer, pPair->m_strRemoteServer ) );
}
}
_ASSERT ( m_cCount == cCount );
}
#endif