// Local
#include "config.h"

// STDC++
#include <iostream>

// C++ Libraries
#include <ui-utilcpp/Sys.hpp>
#include <ui-utilcpp/Cap.hpp>

void showCaps(std::string const & s, UI::Util::Cap const & c=UI::Util::Cap())
{
	std::cout << s << ": " << c.get() << std::endl;
}

void changeCaps(std::string const & s)
{
	showCaps("pre : [" + s + "]");
	UI::Util::Cap(s).apply();
	showCaps("post: [" + s + "]");
}

std::string USAGE("\nOnly a little dirty capabilities example program; see source.\n"
"  Roughly this should run w/o exception:\n"
"   $ touch ./local-capabilities.test\n"
"   $ sudo ui-utilcpp-cap\n");

int main()
{
	try
	{
		std::cout << "UID/EUID:" << UI::Util::Sys::getuid() << "/" << UI::Util::Sys::geteuid() << std::endl;

		showCaps("Initial");

		// Run this as root with this local file existing
		std::string const testfile("./local-capabilities.test");

		// Change file permissions to root
		UI::Util::Sys::chown(testfile.c_str(), 0, 0);

		// Keep capabilities when running setuid
		UI::Util::Sys::prctl(PR_SET_KEEPCAPS, 1);

		// Lower capabilities
		UI::Util::Cap("cap_chown+p", UI::Util::Cap::Clear_).apply();
		showCaps("Lowered");

		// Set user and group id
		// Note: we just use 1000 (Debian's default for the first user/group) hardcoded here; change if needed.
		assert(::setuid(1000) == 0);
		assert(::setgid(1000) == 0);
		std::cout << "UID/EUID:" << UI::Util::Sys::getuid() << "/" << UI::Util::Sys::geteuid() << std::endl;

#ifdef HAVE_CAP_COMPARE
		UI::Util::Cap c;
		assert(UI::Util::Cap() == c);
#endif

		showCaps("After setuid");
		{
			UI::Util::CapScope cs("cap_chown");
			showCaps("In CapScope");
			UI::Util::Sys::chown(testfile.c_str(), 1000, 1000);
		}
		showCaps("After CapScope");
	}
	catch (std::exception const & e)
	{
		std::cerr << std::endl << "*EXCEPTION*: " << e.what() << std::endl;
		std::cerr << USAGE << std::endl;
	}
}
