#define _MBCS
#include <windows.h>
#include <iostream>
#include <fksec.h>
#pragma hdrstop


#pragma comment( linker, "-nodefaultlib:libc.lib" )
#pragma comment( linker, "-nodefaultlib:libcp.lib" )
#pragma comment( linker, "-defaultlib:libcmtd.lib" )

#pragma comment( lib, "fksecd.lib" )
#pragma comment( lib, "advapi32.lib" )
#pragma comment( lib, "netapi32.lib" )
#pragma comment( lib, "winspool.lib" )

#define gle ( GetLastError() )
#define err(e) clog << __FILE__ << "(" << __LINE__ << ") : " << (e) << endl;

using namespace std;


// I am too lazy to parse the command line
#define PRINTERNAME "HP6"
#define USERNAME "luser"


int main()
{
	HANDLE hp;
	DWORD n;
	PRINTER_DEFAULTS pd = { 0 };
	byte buf[10240]; // room for PRINTER_INFO_3 and the SECURITY_DESCRIPTOR

	pd.DesiredAccess = PRINTER_ACCESS_ADMINISTER | WRITE_DAC | READ_CONTROL;
	if ( ! OpenPrinter( PRINTERNAME, &hp, &pd ) )
	{
		clog << "OpenPrinter() failed, gle = " << gle << endl;
		return 1;
	}

	if ( ! GetPrinter( hp, 3, buf, sizeof buf, &n ) )
	{
		clog << "GetPrinter() failed, gle = " << gle << endl;
		return 1;
	}

	fksec::sd sd;
	try {
		sd = (SECURITY_DESCRIPTOR *) (((PRINTER_INFO_3 *) &buf[0])->pSecurityDescriptor);
	}
	catch ( fksec::ex *e )
	{
		err( *e );
		return 1;
	}

	cout << "SD before:" << endl << sd << endl;

	try {
		// set two new ACEs
		sd.GetDacl().AddAce( -1, 0, 0, 0, 0x20008, USERNAME );
		sd.GetDacl().AddAce( -1, 0, 0, 0xa, 0x2000000, USERNAME );
		// merge ACEs with the same user/type/flags, in case we already had ACEs for USERNAME
		sd.GetDacl().normalize();
		// sort ACEs into canonical order (unnecessary in this special case, but ...)
		sd.GetDacl().canonicalize();
		// next three lines clear the SD components for which we did not ask
		// permission to change -- else, Setprinter() errors out with ERROR_ACCESS_DENIED
		// note that the fksec functions I call will also cause the SD control word
		// to reflect the absence of these components, by setting the SE_OWNER_DEFAULTED
		// and SE_GROUP_DEFAULTED BITS and clearing the SE_SACL_PRESENT bit
		sd.ClearOwnerSid();
		sd.ClearGroupSid();
		sd.ClearSacl();
	}
	catch ( fksec::ex *e )
	{
		err( *e );
		return 1;
	}

	cout << "SD after:" << endl << sd << endl;

	((PRINTER_INFO_3 *) &buf[0])->pSecurityDescriptor = (SECURITY_DESCRIPTOR *) sd;

	clog << "Apparently, all is well." << endl;

	if ( ! SetPrinter( hp, 3, buf, 0 ) )
	{
		clog << "SetPrinter() failed, gle = " << gle << endl;
		return 1;
	}

	return 0;
}
