#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#pragma hdrstop



#define MAXLEN 256
#define	CHUNK 32



struct Session
{
	char *client; // client machine
	char *user; // user name
	DWORD time; // idle since (seconds)
};



struct SessionList
{
	int nReserved;
	int nUsed; // number of elements
	Session *s; // points to array of Session structs containing the info we want
};



// we do all our declares ourselves. To learn why, check out
// http://www.mvps.org/win32/network/nt95.html



// ----- cut here ----- NT declarations

#define	NET_API_STATUS DWORD
#define	NET_API_FUNCTION __stdcall

typedef NET_API_STATUS (NET_API_FUNCTION *NetSessionEnum_NT)( LPWSTR servername,
	LPWSTR UncClientName, LPWSTR username, DWORD level, LPBYTE *bufptr,
	DWORD prefmaxlen, LPDWORD entriesread, LPDWORD totalentries,
	LPDWORD resume_handle );

typedef NET_API_STATUS (NET_API_FUNCTION *NetApiBufferFree_NT)( LPVOID Buffer );



struct SESSION_INFO_10_NT {
	LPWSTR sesi10_cname;
	LPWSTR sesi10_username;
	DWORD sesi10_time;
	DWORD sesi10_idle_time;
};

// ----- cut here ----- 95 declarations

#define API_FUNCTION DECLSPEC_IMPORT API_RET_TYPE APIENTRY

typedef __declspec(dllimport) DWORD (__stdcall *NetSessionEnum_95)(
	const char FAR *pszServer, short sLevel, char FAR *pbBuffer,
	unsigned short cbBuffer, unsigned short FAR *pcEntriesRead,
	unsigned short FAR *pcTotalAvail );



#pragma pack(push, 1)
struct session_info_0_95 {
	char FAR *sesi0_cname;
};



struct session_info_10_95 {
	char FAR *sesi10_cname;
	char FAR *sesi10_username;
	unsigned long sesi10_time;
	unsigned long sesi10_idle_time;
};



struct session_info_50_95 {
	char FAR *sesi50_cname;
	char FAR *sesi50_username;
	unsigned long sesi50_key;
	unsigned short sesi50_num_conns;
	unsigned short sesi50_num_opens;
	unsigned long sesi50_time;
	unsigned long sesi50_idle_time;
	unsigned char sesi50_protocol;
	unsigned char pad1;
};
#pragma pack(pop)

// ----- cut here ----- End of OS-specific declarations



void FillSessionList95( const char *server, SessionList& sl )
{
	char *buf;
	int bufsize, level, successLevel;
	session_info_10_95 *cur10;
	session_info_50_95 *cur50;
	NetSessionEnum_95 pNSE;
	HINSTANCE hDLL;
	DWORD rc, i;
	WORD read, total;

	hDLL = LoadLibrary( "svrapi.dll" );
	if ( hDLL == NULL )
	{
		printf( "LoadLibrary( \"svrapi.dll\" ): gle = %lu\n", GetLastError() );
		exit( 1 );
	}

	pNSE = (NetSessionEnum_95) GetProcAddress( hDLL, "NetSessionEnum" );
	if ( pNSE == NULL )
	{
		printf( "GetProcAddress( \"svrapi.dll\" ): gle = %lu\n", GetLastError() );
		exit( 1 );
	}

	buf = NULL;
	successLevel = 90;

	// try levels 10 and 50 in this order, until we don't get a level error
	for ( rc = ERROR_INVALID_LEVEL, level = 10;
		rc == ERROR_INVALID_LEVEL && level <= 50; level += 40 )
	{
		bufsize = 1024;
		do
		{
			printf( "Trying: level %d, bufsize = %d bytes\n", level, bufsize );
			free( buf );
			buf = (char *) malloc( bufsize );

			// NOTE: pre-Jul98 Platform SDKs had bugs in the headers
			// and required a cast for the first three args (to char*).

			rc = pNSE( server, level, buf, bufsize, &read, &total );

			// printf( "rc = %ld; server = %s, level = %d, buf = %p, bufsize = %d, read = %d, total = %d\n",
			//	(int) rc, server, level, buf, bufsize, (int) read, (int) total );

			if ( rc == 0 )
				successLevel = level;

			bufsize += 1024;
		} while ( bufsize < 65536 && ( rc == ERROR_MORE_DATA || rc == ERROR_INVALID_PARAMETER ) );
	}

	if ( rc != ERROR_SUCCESS )
	{
		printf( "NSE() returned %lu\n", rc );
		exit( 1 );
	}

	// belts and braces
	if ( successLevel != 10 && successLevel != 50 )
	{
		printf( "why is successLevel == %d?\n", successLevel );
		exit( 1 );
	}

	if ( (DWORD) ( sl.nReserved - sl.nUsed ) < read )
	{
		// make some room in the list
		sl.s = (Session *) realloc( sl.s, ( sl.nReserved + max( CHUNK, read ) ) * sizeof sl.s[0] );
		if ( sl.s == NULL )
		{
			printf( "Unable to get memory for %d additional sessions.\n", max( CHUNK, read ) );
			exit( 1 );
		}
		sl.nReserved += max( CHUNK, read );
	}

	switch ( successLevel )
	{
		case 10:
			for ( i = 0, cur10 = (session_info_10_95 *) buf; i < read; ++ i, ++ cur10 )
			{
				memset( &sl.s[sl.nUsed], '\0', sizeof sl.s[sl.nUsed] );
				if ( cur10->sesi10_cname != NULL )
					sl.s[sl.nUsed].client = strdup( cur10->sesi10_cname );
				if ( cur10->sesi10_username != NULL )
					sl.s[sl.nUsed].user = strdup( cur10->sesi10_username );
				sl.s[sl.nUsed].time = cur10->sesi10_time;
				++ sl.nUsed;
			}
			break;
		case 50:
			for ( i = 0, cur50 = (session_info_50_95 *) buf; i < read; ++ i, ++ cur50 )
			{
				memset( &sl.s[sl.nUsed], '\0', sizeof sl.s[sl.nUsed] );
				if ( cur50->sesi50_cname != NULL )
					sl.s[sl.nUsed].client = strdup( cur50->sesi50_cname );
				if ( cur50->sesi50_username != NULL )
					sl.s[sl.nUsed].user = strdup( cur50->sesi50_username );
				sl.s[sl.nUsed].time = cur50->sesi50_time;
				++ sl.nUsed;
			}
			break;
		default:
			printf( "why is successLevel == %d, and why didn't the "
				"earlier \"if\" catch it?\n", successLevel );
			exit( 1 );
			break;
	}

	FreeLibrary( hDLL );
	free( buf );
}



void FillSessionListNT( const char *server, const char *client, const char *user, SessionList& sl )
{
	SESSION_INFO_10_NT *buf;
	SESSION_INFO_10_NT *cur;
	DWORD read, total, resumeh, rc, i;
	char tt[MAXLEN];
	wchar_t *wserver = NULL, *wclient = NULL, *wuser = NULL;
	NetSessionEnum_NT pNSE = NULL;
	NetApiBufferFree_NT pNABF = NULL;
	HINSTANCE hDLL;

	hDLL = LoadLibrary( "netapi32.dll" );
	if ( hDLL == NULL )
	{
		printf( "LoadLibrary( \"netapi32.dll\" ): gle = %lu\n", GetLastError() );
		exit( 1 );
	}

	pNSE = (NetSessionEnum_NT) GetProcAddress( hDLL, "NetSessionEnum" );
	pNABF = (NetApiBufferFree_NT) GetProcAddress( hDLL, "NetApiBufferFree" );
	if ( pNSE == NULL || pNABF == NULL )
	{
		printf( "GetProcAddress( \"netapi32.dll\" ) returned NULL\n" );
		exit( 1 );
	}

	if ( server != NULL )
	{
		wserver = (wchar_t *) malloc( ( strlen( server ) + 10 ) * 2 );
		mbstowcs( wserver, server, -1 );
	}

	if ( client != NULL )
	{
		wclient = (wchar_t *) malloc( ( strlen( client ) + 10 ) * 2 );
		mbstowcs( wclient, client, -1 );
	}

	if ( user != NULL )
	{
		wuser = (wchar_t *) malloc( ( strlen( user ) + 10 ) * 2 );
		mbstowcs( wuser, user, -1 );
	}

	resumeh = 0;
	do
	{
		buf = NULL;

		// NOTE: pre-Jul98 Platform SDKs had bugs in the headers
		// and required a cast for the first three args (to char*).
		rc = pNSE( wserver, wclient, wuser, 10,
			(BYTE **) &buf, 2048, &read, &total, &resumeh );
		if ( rc != ERROR_MORE_DATA && rc != ERROR_SUCCESS )
			break;

		if ( (DWORD) ( sl.nReserved - sl.nUsed ) < read )
		{
			// make some room in the list
			sl.s = (Session *) realloc( sl.s, ( sl.nReserved + max( CHUNK, read ) ) * sizeof sl.s[0] );
			if ( sl.s == NULL )
			{
				printf( "Unable to get memory for %d additional sessions.\n", max( CHUNK, read ) );
				exit( 1 );
			}
			sl.nReserved += max( CHUNK, read );
		}

		printf( "\ngot %lu entries out of %lu\n", read, total );
		for ( i = 0, cur = buf; i < read; ++ i, ++ cur )
		{
			memset( &sl.s[sl.nUsed], '\0', sizeof sl.s[sl.nUsed] );
			if ( cur->sesi10_cname != NULL )
			{
				wcstombs( tt, cur->sesi10_cname, MAXLEN );
				sl.s[sl.nUsed].client = strdup( tt );
			}
			if ( cur->sesi10_username != NULL )
			{
				wcstombs( tt, cur->sesi10_username, MAXLEN );
				sl.s[sl.nUsed].user = strdup( tt );
			}
			sl.s[sl.nUsed].time = cur->sesi10_time;
			++ sl.nUsed;
		}

		if ( buf != NULL )
			pNABF( buf );

	} while ( rc == ERROR_MORE_DATA );

	FreeLibrary( hDLL );

	if ( rc != ERROR_SUCCESS )
		printf( "NSE() returned %lu\n", rc );

	free( wserver );
	free( wclient );
	free( wuser);
}



void ShowSessionList( const char *server, const char *client, const char *user )
{
	OSVERSIONINFO ovi = { sizeof ovi };
	SessionList sl = { 0, 0, NULL };
	int i;

	GetVersionEx( &ovi );
	switch ( ovi.dwPlatformId )
	{
		case VER_PLATFORM_WIN32_WINDOWS:
			FillSessionList95( server, sl );
			break;
		case VER_PLATFORM_WIN32_NT:
			FillSessionListNT( server, client, user, sl );
			break;
		default:
			puts( "Sorry, but this sample requires Windows 9x or NT." );
			exit( 1 );
	}

	printf( "%-30.30s %-30.30s %-8.8s\n", "Client", "User", "Idle sec" );
	printf( "%-30.30s %-30.30s %-8.8s\n", "------------------------------",
		"------------------------------", "--------" );

	for ( i = 0; i < sl.nUsed; ++ i )
	{
		printf( "%-30.30s %-30.30s %02d:%02d:%02d\n",
			sl.s[i].client == NULL? "": sl.s[i].client,
			sl.s[i].user == NULL? "": sl.s[i].user,
			sl.s[i].time / 3600, ( sl.s[i].time % 3600 ) / 60, sl.s[i].time % 60 );
		free( sl.s[i].client );
		free( sl.s[i].user );
	}

	free( sl.s );
}



int main( int argc, char *argv[] )
{
	const char *server = NULL, *user = NULL, *client = NULL;
	int errors = 0, i;
	bool haveServer = false, haveUser = false, haveClient = false;

	if ( argc > 7 )
	{
usage:
		puts( "\nnsesse2 [-s \\\\server] [-c \\\\computername|-u username]\n" );
		puts( "\\\\server -- machine to check, with backslashes, please." );
		puts( "  Default: local machine.\n" );
		puts( "\\\\computername -- only list sessions from this computer." );
		puts( "  Default: all computers.\n" );
		puts( "username -- only list sessions logged in as username." );
		puts( "  Default: all users.\n" );
		puts( "You can use -c, or -u, or neither -- but not both together." );
		puts( "On Win9X, both -c and -u will be ignored." );
		return 1;
	}

	for ( i = 1; i < argc; ++ i )
	{
		if ( *argv[i] != '-' && *argv[1] != '/' )
		{
			printf( "\"%s\": switches must start with a dash or a slash.\n",
				argv[i] );
			++ errors;
			continue;
		}

		switch ( argv[i][1] )
		{
			case '?':
				goto usage;
			case 's':
				if ( i == argc - 1 ) // no further args?
				{
					printf( "\"%s\" requires the server name as an argument.\n", argv[i] );
					++ errors;
					break;
				}
				if ( haveServer )
				{
					printf( "\"%s\" can only be used once.\n", argv[i] );
					++ errors;
					++ i; // skip what probably is another server name
					break;
				}
				++ i; // advance arg index to account for server name
				server = argv[i];
				haveServer = true;
				break;
			case 'c':
				if ( i == argc - 1 ) // no further args?
				{
					printf( "\"%s\" requires the client name as an argument.\n", argv[i] );
					++ errors;
					break;
				}
				if ( haveClient )
				{
					printf( "\"%s\" can only be used once.\n", argv[i] );
					++ i;
					++ errors;
					break;
				}
				++ i;
				client = argv[i];
				haveClient = true;
				if ( client[0] != '\\' || client[1] != '\\' )
				{
					printf( "\"%s\" should start with a double backslash \"\\\\\".\n", client );
					++ errors;
				}
				break;
			case 'u':
				if ( i == argc - 1 ) // no further args?
				{
					printf( "\"%s\" requires the user name as an argument.\n", argv[i] );
					++ errors;
					break;
				}
				if ( haveUser )
				{
					printf( "\"%s\" can only be used once.\n", argv[i] );
					++ i;
					++ errors;
					break;
				}
				++ i;
				user = argv[i];
				haveUser = true;
				break;
			default:
				printf( "\"%s\" is not a valid switch, sorry.\n", argv[i] );
				++ errors;
				break;
		}
	}

	if ( errors )
	{
		printf( "\n%d command line errors. Pass GO, do not collect $200.\n\n",
			errors );
		goto usage;
	}

	ShowSessionList( server, client, user );

	return i;
}
