#define NNTP_CLIENT_ONLY #include #include #include extern "C" { #include #include } #include "mapfile.h" // // Function prototypes // BOOL CreateGroupsFromFile( LPSTR lpstrGroupFile ); BOOL CreateGroupFromBuffer( char *pchBegin, DWORD cb, DWORD &cbOut ); BOOL SetModeratorsFromFile( LPSTR lpstrModeratorsFile ); BOOL SetDescriptionsFromFile( LPSTR lpstrDescriptionsFile ); BOOL SetDescriptionsFromBuffer( char *pchBegin, DWORD cb, DWORD &cbOut ); BOOL ExpandModeratorBuffer( char *pchBegin, DWORD cb, DWORD &cbOut ); DWORD AddGroup( LPWSTR Server, LPNNTP_NEWSGROUP_INFO newsgroup ); DWORD GetInformation( LPWSTR Server, LPNNTP_NEWSGROUP_INFO *newsgroup ); DWORD SetInformation( LPWSTR Server, LPNNTP_NEWSGROUP_INFO newsgroup ); VOID PrintInfo( LPNNTP_NEWSGROUP_INFO newsgroup ); DWORD SkipWS( char* pchBegin, DWORD cb ) { // skip whitespace for( DWORD i=0; i < cb; i++ ) { if( pchBegin[i] != ' ' && pchBegin[i] != '\t' ) { return i; } } return 0; } DWORD ScanWS( char* pchBegin, DWORD cb ) { // // This is a utility used when reading a newsgroup // info. from disk. // for( DWORD i=0; i < cb; i++ ) { if( pchBegin[i] == ' ' || pchBegin[i] == '\t' ) { return i+1 ; } } return 0 ; } DWORD Scan( char* pchBegin, char ch, DWORD cb ) { // // This is a utility used when reading a newsgroup // info. from disk. // for( DWORD i=0; i < cb; i++ ) { if( pchBegin[i] == ch ) { return i+1 ; } } return 0 ; } DWORD ScanEOL( char* pchBegin, DWORD cb ) { // // This is a utility used when reading a newsgroup // info. from disk. // for( DWORD i=0; i < cb; i++ ) { if( pchBegin[i] == '\n' || pchBegin[i] == '\r' ) { i++ ; return i ; } } return 0 ; } DWORD Scan( char* pchBegin, DWORD cb ) { // // This is a utility used when reading a newsgroup // info. from disk. // for( DWORD i=0; i < cb; i++ ) { if( pchBegin[i] == ' ' || pchBegin[i] == '\n' ) { return i+1 ; } } return 0 ; } DWORD ScanDigits( char* pchBegin, DWORD cb ) { // // This is a utility used when reading a newsgroup // info. from disk. // for( DWORD i=0; i < cb; i++ ) { if( pchBegin[i] == ' ' || pchBegin[i] == '\n' || pchBegin[i] == '\r' ) { return i+1 ; } if( !isdigit( pchBegin[i] ) && pchBegin[i] != '-' ) { return 0 ; } } return 0 ; } VOID AsciiToUnicode( LPSTR lpSrc, WCHAR* pwchDst ) { if( !lpSrc || !pwchDst ) return; for( int i=0; lpSrc[i] != '\0' ; i++ ) { pwchDst[i] = (WCHAR)lpSrc[i] ; } pwchDst[i] = L'\0' ; } VOID UnicodeToAscii( WCHAR* pwchSrc, LPSTR lpDst ) { if( !pwchSrc || !lpDst ) return; for( int i=0; pwchSrc[i] != '\0' ; i++ ) { lpDst[i] = (CHAR)pwchSrc[i] ; } lpDst[i] = '\0' ; } VOID HiphenateGroupName( LPSTR szGroup, LPSTR lpstrModerator ) { if( !szGroup || !lpstrModerator ) return; if ( lpstrModerator [0] == '%' && lpstrModerator [1] == 's' ) { // convert all dots in the group name to dashes for( int i=0; szGroup[i] != '\0'; i++) { if( szGroup [i] == '.' ) szGroup [i] = '-'; } lstrcat( szGroup, lpstrModerator+2 ); } else { lstrcpy( szGroup, lpstrModerator ); } } void usage( ) { printf("tigstart takes a list of groups and moderators/descriptions for those\n"); printf("groups and creates them by making RPCs to the server.\n"); printf("tigstart\n"); printf("\t-v verbose\n"); printf("\t-s server name\n"); printf("\t-g group file\n"); printf("\t-m INN style moderators file\n"); printf("\t-d descriptions file\n"); return; } // // Globals // BOOL fVerbose = FALSE; DWORD nGroupCount = 0; char szGroupFile [MAX_PATH+1]; char szModeratorsFile [MAX_PATH+1]; char szDescripFile [MAX_PATH+1]; LPWSTR RemServerW = (PWCH)NULL; WCHAR ServerName[256]; void _CRTAPI1 main( int argc, char * argv[] ) { NET_API_STATUS err; INT cur = 1; PCHAR x; DWORD i; PCHAR server; BOOL GroupFilePresent = FALSE, ModeratorsFilePresent = FALSE, DescripFilePresent = FALSE; if ( argc == 1 ) { usage( ); return; } while ( cur < argc ) { x=argv[cur++]; if ( *(x++) == '-' ) { switch (*x) { case 'g': if ( cur >= argc ) { usage( ); return; } for( i=0; argv[cur][i] != '\0' ; i++ ) { szGroupFile[i] = argv[cur][i] ; } szGroupFile[i] = '\0' ; GroupFilePresent = TRUE ; break; case 'd': if ( cur >= argc ) { usage( ); return; } for( i=0; argv[cur][i] != '\0' ; i++ ) { szDescripFile[i] = argv[cur][i] ; } szDescripFile[i] = '\0' ; DescripFilePresent = TRUE ; break; case 'm': if ( cur >= argc ) { usage( ); return; } for( i=0; argv[cur][i] != '\0' ; i++ ) { szModeratorsFile[i] = argv[cur][i] ; } szModeratorsFile[i] = '\0' ; ModeratorsFilePresent = TRUE ; break; case 'v': if ( cur >= argc ) { usage( ); return; } fVerbose = TRUE; break; case 's': if ( cur >= argc ) { usage(); return; } server = argv[cur++]; for (i=0; server[i] != '\0' ;i++ ) { ServerName[i] = (WCHAR)server[i]; } ServerName[i]=L'\0'; RemServerW = ServerName; break; default: if( cur >= 1 ) printf( "unrecognized argument : %s\n", argv[cur-1] ) ; usage( ); return; } } } // validate arguments if( !GroupFilePresent || !DescripFilePresent || !ModeratorsFilePresent ) { printf( "One or more required files not specified \n") ; usage() ; return ; } // // do pass1 - create groups listed in GroupFile // if( !CreateGroupsFromFile( szGroupFile ) ) { printf(" Failed to process %s\n", szGroupFile); return; } printf(" Successfully created groups listed in file %s\n", szGroupFile); // // do pass2 - set moderator properties described in ModeratorsFile // if( !SetModeratorsFromFile( szModeratorsFile ) ) { printf(" Failed to process %s\n", szModeratorsFile); return; } printf(" Successfully set moderator properties from %s\n", szModeratorsFile); // // do pass3 - set description properties described in DescripFile // if( !SetDescriptionsFromFile( szDescripFile ) ) { printf(" Failed to process %s\n", szDescripFile); return; } printf(" Successfully set description properties from %s\n", szDescripFile); } // main() BOOL CreateGroupsFromFile( LPSTR lpstrGroupFile ) /*++ Routine description : Read a file containing a list of newsgroups - one per line - and make rgroup RPCs to the server to create them. TODO: parse active file symbols like y/n/m to set read-only properties Arguments : lpstrGroupFile - Name of file containing list of newsgroups Return Value : TRUE if successful, FALSE otherwise. --*/ { // // Memory-map the file to read the data // CMapFile map( lpstrGroupFile, FALSE, 0 ) ; if( !map.fGood() ) { printf(" Failed to map file %s \n", lpstrGroupFile); return FALSE; } DWORD cb ; char* pchBegin = (char*)map.pvAddress( &cb ) ; while( cb != 0 ) { DWORD cbUsed = 0 ; BOOL fInit = CreateGroupFromBuffer( pchBegin, cb, cbUsed ) ; if( cbUsed == 0 ) { // Fatal Error - blow out of here printf(" Error parsing %s\n", lpstrGroupFile); return FALSE ; } if( fInit ) { if( fVerbose ) printf(" Successfully created group from buffer \n"); nGroupCount++; } else { printf(" error creating group from buffer \n"); } // advance to next group pchBegin += cbUsed ; cb -= cbUsed ; } return TRUE ; } BOOL CreateGroupFromBuffer( char *pchBegin, DWORD cb, DWORD &cbOut ) { /*++ Routine description : Read a line that starts with a newsgroup name. Create the group by doing an admin RPC to the server. TODO: parse additional properties like y/n/m flags after the group name Arguments : pchBegin - buffer containing article data cb - Number of bytes to the end of the buffer &cbOut - Out parameter, number of bytes read to make up one group Return Value : TRUE if successful, FALSE otherwise. --*/ // // We are intentionally unforgiving, extra // spaces, missing args etc.... will cause us to return // cbOut as 0. This should be used by the caller to // entirely bail processing of the newsgroup data file. // // // cbOut should be the number of bytes we consumed - // we will only return non 0 if we successfully read every field from the file ! // cbOut = 0 ; BOOL fReturn = TRUE ; LPSTR lpstrGroup; NNTP_NEWSGROUP_INFO newsgroup ; WCHAR tempNews[2048]; PCHAR newsgroupName = NULL; DWORD cbScan = 0 ; DWORD cbRead = 0 ; DWORD cbGroupName = 0 ; if( (cbScan = Scan( pchBegin+cbRead, cb-cbRead )) == 0 ) { printf(" expected newsgroup \n"); return FALSE ; } lpstrGroup = new char[cbScan] ; cbGroupName = cbScan ; if( !lpstrGroup ) { printf(" memory allocation failed ! \n"); return FALSE; } CopyMemory( lpstrGroup, pchBegin, cbScan ) ; lpstrGroup[cbScan-1] = '\0' ; ZeroMemory(&newsgroup,sizeof(newsgroup)); AsciiToUnicode( lpstrGroup, tempNews ); newsgroupName = (PCHAR)&tempNews[0] ; newsgroup.Newsgroup = (PUCHAR)newsgroupName ; newsgroup.cbNewsgroup = (wcslen( (LPWSTR)newsgroupName ) + 1 ) * sizeof(WCHAR) ; newsgroup.ReadOnly = FALSE; newsgroup.Descriptor = 0 ; newsgroup.cbDescriptor = 0 ; // RPC to the server ! DWORD err = AddGroup( RemServerW, &newsgroup ) ; if( err != NO_ERROR ) { printf(" err %d in AddGroup \n", err); fReturn = FALSE; } delete [] lpstrGroup; lpstrGroup = NULL; // advance by amount scanned cbRead += cbScan ; // skip till EOL DWORD cbEol = ScanEOL( pchBegin+cbRead, cb-cbRead ); cbRead += cbEol+1; // // Return to the caller the number of bytes consumed // We may still fail - but with this info the caller can continue reading the file ! // cbOut = cbRead ; return fReturn ; } BOOL SetModeratorsFromFile( LPSTR lpstrModeratorsFile ) /*++ Routine description : Read an INN style file containing moderator names for wildmat group patterns. Expand the wildmat to set moderator properties for each group. Arguments : lpstrModeratorsFile - INN style file containing moderator names Return Value : TRUE if successful, FALSE otherwise. --*/ { CMapFile map( lpstrModeratorsFile, FALSE, 0 ) ; if( !map.fGood() ) { printf(" Failed to map file %s \n", lpstrModeratorsFile); return FALSE; } DWORD cb ; char* pchBegin = (char*)map.pvAddress( &cb ) ; while( cb != 0 ) { DWORD cbUsed = 0 ; BOOL fInit = ExpandModeratorBuffer( pchBegin, cb, cbUsed ) ; if( cbUsed == 0 ) { // Fatal Error - blow out of here printf(" error parsing line in file %s \n", lpstrModeratorsFile); return FALSE ; } else { if( fInit ) { if( fVerbose ) printf(" Successfully expanded moderator buffer \n"); } else { // // How should we handle an error // } } pchBegin += cbUsed ; cb -= cbUsed ; } return TRUE ; } BOOL ExpandModeratorBuffer( char *pchBegin, DWORD cb, DWORD &cbOut ) { /*++ Routine description : Process a line in an INN style moderator file. The line should have the following strict format: : Arguments : pchBegin - buffer containing moderator data cb - Number of bytes to the end of the buffer &cbOut - Out parameter, number of bytes read to make up one line Return Value : TRUE if successful, FALSE otherwise. --*/ cbOut = 0 ; BOOL fReturn = TRUE ; LPSTR lpstrGroup, lpstrModerator; NNTP_NEWSGROUP_INFO newsgroup ; WCHAR tempNews[2048]; CHAR szGroup [1024]; LPWSTR NewsgroupW = (PWCH)NULL; PCHAR newsgroupName = NULL; LPNNTP_FIND_LIST pList = NULL; WCHAR tempModerator[2048] ; PCHAR Moderator = NULL ; DWORD cbScan = 0 ; DWORD cbRead = 0 ; DWORD cbGroupName = 0 ; DWORD cbModerator = 0; DWORD cbEol = 0; // scan for newsgroup wildmat if( (cbScan = Scan( pchBegin+cbRead, ':', cb-cbRead )) == 0 ) { printf(" expected : following wildmat \n"); return FALSE ; } lpstrGroup = new char[cbScan] ; cbGroupName = cbScan ; if( !lpstrGroup ) { printf(" memory allocation failed !\n"); return FALSE; } CopyMemory( lpstrGroup, pchBegin, cbScan ) ; lpstrGroup[cbScan-1] = '\0' ; // scan the moderator string cbRead += cbScan ; if( (cbEol = ScanEOL( pchBegin+cbRead, cb-cbRead )) == 0 ) { printf(" no moderator string \n"); delete [] lpstrGroup; return FALSE; } lpstrModerator = new char[cbEol]; if( !lpstrModerator ) { printf(" memory allocation failed !\n"); delete [] lpstrGroup; return FALSE; } CopyMemory( lpstrModerator, pchBegin+cbScan, cbEol ); lpstrModerator [cbEol-1] = '\0'; cbRead += cbEol+1; // newsgroup wildmat AsciiToUnicode( lpstrGroup, tempNews ); NewsgroupW = tempNews; DWORD numResults = nGroupCount; DWORD ResultsFound; DWORD err = NntpFindNewsgroup( RemServerW, NewsgroupW, numResults, &ResultsFound, &pList ) ; if ( err != NO_ERROR ) { printf("err %d in Find\n",err); fReturn = FALSE; } else { if( pList ) { // Iterate over groups in wildmat expansion for(DWORD iGroup=0; iGroupaFindEntry[i].lpszName); ZeroMemory(&newsgroup,sizeof(newsgroup)); newsgroupName = (PCHAR)(pList->aFindEntry[iGroup].lpszName) ; newsgroup.Newsgroup = (PUCHAR)newsgroupName ; newsgroup.cbNewsgroup = (wcslen( (LPWSTR)newsgroupName ) + 1 ) * sizeof(WCHAR) ; UnicodeToAscii( (WCHAR*)newsgroupName, szGroup ); HiphenateGroupName( szGroup, lpstrModerator ); AsciiToUnicode( szGroup, tempModerator ); Moderator = (PCHAR)&tempModerator[0] ; if( Moderator ) { newsgroup.Moderator = (PUCHAR)Moderator ; newsgroup.cbModerator = (wcslen( (LPWSTR)Moderator ) + 1 ) * sizeof( WCHAR ) ; } newsgroup.ReadOnly = FALSE; newsgroup.Descriptor = 0 ; newsgroup.cbDescriptor = 0 ; err = SetInformation( RemServerW, &newsgroup ) ; if( err != NO_ERROR ) { fReturn = FALSE; } } } else { printf( "%s newsgroup does not exist \n", lpstrGroup ) ; fReturn = FALSE; } } // cleanup ! delete [] lpstrGroup; lpstrGroup = NULL; delete [] lpstrModerator; lpstrModerator = NULL; if( pList ) MIDL_user_free(pList); // // Return to the caller the number of bytes consumed // We may still fail - but with this info the caller can continue reading the file ! // cbOut = cbRead ; return fReturn ; } BOOL SetDescriptionsFromFile( LPSTR lpstrDescriptionsFile ) /*++ Routine description : Read a file containing a list of one per line. Make RPCs to the server to set these descriptions. Arguments : lpstrDescriptionsFile - File containing descriptions Return Value : TRUE if successful, FALSE otherwise. --*/ { CMapFile map( lpstrDescriptionsFile, FALSE, 0 ) ; if( !map.fGood() ) { printf(" Failed to map file %s \n", lpstrDescriptionsFile); return FALSE; } DWORD cb ; char* pchBegin = (char*)map.pvAddress( &cb ) ; while( cb != 0 ) { DWORD cbUsed = 0 ; BOOL fInit = SetDescriptionsFromBuffer( pchBegin, cb, cbUsed ) ; if( cbUsed == 0 ) { // Fatal Error - blow out of here printf(" error parsing line in file %s \n", lpstrDescriptionsFile); return FALSE ; } else { if( fInit ) { if( fVerbose ) printf(" Successfully set descriptions from buffer \n"); } else { // // How should we handle an error // } } pchBegin += cbUsed ; cb -= cbUsed ; } return TRUE ; } BOOL SetDescriptionsFromBuffer( char *pchBegin, DWORD cb, DWORD &cbOut ) { /*++ Routine description : Read a line that contains a newsgroup name and description. Make an admin RPC to the server to set the description. Arguments : pchBegin - buffer containing article data cb - Number of bytes to the end of the buffer &cbOut - Out parameter, number of bytes read to make up one group Return Value : TRUE if successful, FALSE otherwise. --*/ // // cbOut should be the number of bytes we consumed - // we will only return non 0 if we successfully read every field from the file ! // cbOut = 0 ; BOOL fReturn = TRUE ; LPSTR lpstrGroup, lpstrDescription; LPNNTP_NEWSGROUP_INFO lpnewsgroup ; NNTP_NEWSGROUP_INFO newsgroup ; WCHAR tempNews[2048]; PCHAR newsgroupName = NULL; WCHAR tempDescription[2048] ; PCHAR Description = NULL ; PCHAR Moderator = NULL ; DWORD cbScan = 0 ; DWORD cbRead = 0 ; DWORD cbGroupName = 0 ; DWORD cbWS = 0; DWORD cbEol = 0; // scan the newsgroup name if( (cbScan = ScanWS( pchBegin+cbRead, cb-cbRead )) == 0 ) { printf(" expected newsgroup \n"); return FALSE ; } lpstrGroup = new char[cbScan] ; cbGroupName = cbScan ; if( !lpstrGroup ) { printf(" memory allocation failed ! \n"); return FALSE; } CopyMemory( lpstrGroup, pchBegin, cbScan ) ; lpstrGroup[cbScan-1] = '\0' ; // scan the description string cbRead += cbScan ; if( (cbWS = SkipWS( pchBegin+cbRead, cb-cbRead )) == 0 ) { printf(" no description string \n"); delete [] lpstrGroup; return FALSE; } cbRead += cbWS; if( (cbEol = ScanEOL( pchBegin+cbRead, cb-cbRead )) == 0 ) { printf(" no description string \n"); delete [] lpstrGroup; return FALSE ; } lpstrDescription = new char[cbEol]; if( !lpstrDescription ) { printf(" memory allocation failed !\n"); delete [] lpstrGroup; return FALSE; } CopyMemory( lpstrDescription, pchBegin+cbRead, cbEol ); lpstrDescription [cbEol-1] = '\0'; cbRead += cbEol+1; // description AsciiToUnicode( lpstrDescription, tempDescription ); Description = (PCHAR)&tempDescription[0]; // // Retrieve the moderator info so we can preserve it ! // ZeroMemory(&newsgroup,sizeof(newsgroup)); AsciiToUnicode( lpstrGroup, tempNews ); newsgroupName = (PCHAR)&tempNews[0] ; newsgroup.Newsgroup = (PUCHAR)newsgroupName ; newsgroup.cbNewsgroup = (wcslen( (LPWSTR)newsgroupName ) + 1 ) * sizeof(WCHAR) ; newsgroup.ReadOnly = FALSE; newsgroup.Descriptor = 0 ; newsgroup.cbDescriptor = 0 ; // RPC to the server ! lpnewsgroup = &newsgroup ; DWORD err = GetInformation( RemServerW, &lpnewsgroup ) ; if( err != NO_ERROR ) { printf(" err %d in GetInformation \n", err); fReturn = FALSE; } // get the old moderator Moderator = (PCHAR)(*lpnewsgroup).Moderator; // // Now set the moderator and descrip info ! // ZeroMemory(&newsgroup,sizeof(newsgroup)); AsciiToUnicode( lpstrGroup, tempNews ); newsgroupName = (PCHAR)&tempNews[0] ; newsgroup.Newsgroup = (PUCHAR)newsgroupName ; newsgroup.cbNewsgroup = (wcslen( (LPWSTR)newsgroupName ) + 1 ) * sizeof(WCHAR) ; if( Moderator ) { newsgroup.Moderator = (PUCHAR)Moderator ; newsgroup.cbModerator = (wcslen( (LPWSTR)Moderator ) + 1 ) * sizeof( WCHAR ) ; } if( Description ) { newsgroup.Description = (PUCHAR)Description ; newsgroup.cbDescription = (wcslen( (LPWSTR)Description ) + 1 ) * sizeof( WCHAR ) ; } newsgroup.ReadOnly = FALSE; newsgroup.Descriptor = 0 ; newsgroup.cbDescriptor = 0 ; // RPC to the server ! err = SetInformation( RemServerW, &newsgroup ) ; if( err != NO_ERROR ) { printf(" err %d in SetInformation \n", err); fReturn = FALSE; } delete [] lpstrGroup; lpstrGroup = NULL; delete [] lpstrDescription; lpstrDescription = NULL; // // Return to the caller the number of bytes consumed // We may still fail - but with this info the caller can continue reading the file ! // cbOut = cbRead ; return fReturn ; } DWORD SetInformation( LPWSTR Server, LPNNTP_NEWSGROUP_INFO newsgroup ) { DWORD err; err = NntpSetNewsgroup( Server, newsgroup ); printf("err %d in SetInfo\n",err); return err; } DWORD AddGroup( LPWSTR Server, LPNNTP_NEWSGROUP_INFO newsgroup ) { DWORD err; DWORD parm = 0; printf( "Attempting to add group : \n" ) ; PrintInfo( newsgroup ) ; err = NntpCreateNewsgroup( Server, newsgroup ); printf("err %d in AddInfo\n",err); if ( err == ERROR_INVALID_PARAMETER ) { printf("parm error %d\n",parm); } return err; } DWORD GetInformation( LPWSTR Server, LPNNTP_NEWSGROUP_INFO* newsgroup ) { DWORD err; err = NntpGetNewsgroup( Server, newsgroup ); if ( err == NO_ERROR ) { if( *newsgroup ) { PrintInfo(*newsgroup); } else { printf( "newsgroup not found \n" ) ; } } else { printf("err %d in GetInfo\n",err); } return err; } VOID PrintInfo( LPNNTP_NEWSGROUP_INFO newsgroup ) { PWCH p; printf("Newsgroup %S\n", newsgroup->Newsgroup); printf("Newsgroup is %s\n", newsgroup->ReadOnly ? "Read Only" : "Read Write" ) ; printf("Description %S\n", newsgroup->Description ? (LPWSTR)newsgroup->Description : (LPWSTR)L"" ) ; printf("Moderator %S\n", newsgroup->Moderator ? (LPWSTR)newsgroup->Moderator : (LPWSTR)L"" ) ; printf("\n"); }