/*
 * @(#CDWrootGate.cpp
 *
 * This file is part of webCDwriter - Network CD Writing.
 *
 * Copyright (C) 2003-2004 Jörg P. M. Haeger
 *
 * webCDwriter is free software. See CDWserver.cpp for details.
 *
 * Author: Jörg P. M. Haeger, 11.10.2003
 */

#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include "Error.h"
#include "Version.h"

#ifdef PAM
#include "PAM.h"
#endif

/**
 * Services for webCDwriter
 *
 * @version 20041007
 * @author Jörg P. M. Haeger
 */
class CDWrootGate {
	private: enum { none, listIDE, pam, prepareDev } mode;
	private: int uid;

	private: char dev[1024], driver[1024], media[1024];

	public: CDWrootGate() {
		mode = none;
		uid = 0;
	}

	protected: void listIDEDevices() {
		for (char ch = 'a'; ch <= 'z'; ch++) {
			readMedia(ch);

			sprintf(driver, "/proc/ide/hd%c/driver", ch);
			FILE *in = fopen(driver, "r");
			if (in == NULL)
				continue;
			if (fgets(driver, sizeof driver, in) != NULL)
				removeLF(driver);
			fclose(in);

			sprintf(dev, "/dev/hd%c", ch);

			printf("%s %s %s\n", dev, media, driver);
		}
	}

	public: static int main(int argc, char *args[]) {
		printf("CDWrootGate %s (part of %s)\n", version, project);
		fflush(stdout);

		CDWrootGate *app = new CDWrootGate();
		try {
			app->scanArgs(argc, args);
		}
		catch (Error *error) {
			printf("error: %s\n", error->toString());
			delete error;
			return 1;
		}
		return 0;
	}

#ifdef PAM
	protected: void runPAM() {
		printf("User: ");
		fflush(stdout);
		char user[1024];
		fgets(user, sizeof user, stdin);
		for (int i = 0; i < sizeof user; i++)
			if (user[i] == '\n')
				user[i] = 0;

		printf("Password: ");
		fflush(stdout);
		char passwd[1024];
		fgets(passwd, sizeof passwd, stdin);
		for (int i = 0; i < sizeof passwd; i++)
			if (passwd[i] == '\n')
				passwd[i] = 0;

		PAM4CDWserver server;
		try {
			server.check(user, passwd);
			printf("%s authenticated\n", user);
		}
		catch (Exception *exception) {
			throw new Error(exception->toString());
		}
	}
#endif

	protected: void readMedia(char ch) {
		sprintf(media, "/proc/ide/hd%c/media", ch);
		FILE *in = fopen(media, "r");
		if (in == NULL)
			return;
		if (fgets(media, sizeof media, in) != NULL)
			removeLF(media);
		fclose(in);
	}

	public: static void removeLF(char *str) {
		for (int i = 0; str[i] != 0; i++)
			if (str[i] == '\n')
				str[i] = 0;
	}

	protected: void scanArgs(int argc, char *args[]) {
		if (argc < 2) {
			showUsage(args[0]);
			return;
		}

		for (int i = 1; i < argc; i++)
			if (strcmp(args[i], "-listIDEDevices") == 0)
				mode = listIDE;
#ifdef PAM
			else
			if (strcmp(args[i], "-pam") == 0)
				mode = pam;
#endif
			else
			if (strcmp(args[i], "-prepareDevice") == 0)
				mode = prepareDev;
			else
			if (strstr(args[i], "dev=") == args[i]) {
				strncpy(dev, &args[i][4], sizeof dev - 1);
				dev[sizeof dev - 1] = 0;
			}
			else
			if (sscanf(args[i], "uid=%d", &uid) == 1)
				;
			else {
				showUsage(args[0]);
				return;
			}

		for (int i = 1; i < argc; i++)
			if (strcmp(args[i], "-listIDEDevices") == 0)
				listIDEDevices();
#ifdef PAM
			else
			if (strcmp(args[i], "-pam") == 0)
				runPAM();
#endif
			else
			if (strcmp(args[i], "-prepareDevice") == 0)
				prepareDevice();
	}

	public: static void showUsage(const char *arg0) {
#ifdef PAM
		const char *pamArg = "\t-pam\n";
#else
		const char *pamArg = "";
#endif
		printf("%s offers some services that need root privileges\n\n"
			"Usage:\n"
			"\t%s [ mode ] [options]\n\n"
			"Modes:\n"
			"\t-listIDEDevices\n"
			"%s"
			"\t-prepareDevice\n"
			"\n"
			"Options:\n"
			"\tdev=/dev/hdx\n"
			"\tuid=#"
				"\t\tcdwriter uid\n\n",
			arg0, arg0, pamArg);
	}

	protected: void prepareDevice() {
		char ch, ch2;
		if (sscanf(dev, "/dev/cd%c", &ch) == 1)
			strcpy(media, "cdrom");
		else
		if (sscanf(dev, "/dev/hd%c%c", &ch, &ch2) == 1)
			readMedia(ch);
		else
		if (sscanf(dev, "/dev/scd%c", &ch) == 1
				|| sscanf(dev, "/dev/sr%c", &ch) == 1)
			strcpy(media, "cdrom");
		else
			throw new Error("Illegal device %s", dev);

		if (strcmp(media, "cdrom") == 0) {
			struct stat statBuf;
			if (stat(dev, &statBuf) == 0) {
				if (chown(dev, uid, statBuf.st_gid) == 0) {
					statBuf.st_mode |= S_IRUSR | S_IWUSR;
					chmod(dev, statBuf.st_mode);

					if (strstr(dev, "/dev/cd") == dev) {
						char dev2[1024];
						sprintf(dev2, "/dev/pass%c", ch);
						if (stat(dev2, &statBuf) == 0)
							if (chown(dev2, uid, statBuf.st_gid) == 0) {
								statBuf.st_mode |= S_IRUSR | S_IWUSR;
								chmod(dev2, statBuf.st_mode);
							}
					}
				}
				else
					throw new Error("Cannot change owner of %s", dev);
			}
			else
				throw new Error("Cannot stat device %s", dev);
		}
		else
			throw new Error("Device %s is not a cdrom", dev);
	}
};

int main(int argc, char *args[]) {
	return CDWrootGate::main(argc, args);
}
