/*
 * @(#)CDWserver.cpp
 *
 * This file is part of webCDwriter - Network CD Writing.
 *
 * Copyright (C) 1999-2004 Jörg P. M. Haeger
 *
 * webCDwriter is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * webCDwriter is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with webCDwriter; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Jörg Haeger, 17.03.1999
 * eMail: mail@JoergHaeger.de
 * http://JoergHaeger.de
 */

#include <sys/types.h>
#include <grp.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "BufferedReader.h"
#include "Config.h"
#include "HTTPServer.h"
#include "Log.h"
#include "ServerPro.h"
#include "ServerSocket.h"
#include "Socket.h"
#include "Thread.h"
#include "Version.h"

Config config;
Log log;

class CDWserver: public Thread {
	Socket *clientSocket;
public:
	CDWserver(Socket *clientSocket) {
		this->clientSocket = clientSocket;
	}

	~CDWserver() {
		delete clientSocket;
	}

	void run() {
		try {
			BufferedReader &reader = clientSocket->getInputStream();
			if (reader.ready(10000)) {
				HTTPServer server(*clientSocket);
				server.run();
			}
			else {
				ServerPro server(*clientSocket);
				server.run();
			}
		}
		catch (Exception *e) {
			log.put(1, S.e + "Exception: " + e->getMessage());
			delete e;
		}
		catch (const char *str) {
			log.put(1, S.e + "Catched: " + str);
		}

		log.debug("threads", "terminated");
	}
};

void
sigAbrtHandler(int n) {
	log.put(1, "sigAbrtHandler: preparing for termination");
	_exit(0);
}

void
sigBusHandler(int n) {
	log.put(1, "sigBusHandler: preparing for termination");
	_exit(0);
}

void
sigHupHandler(int n) {
	log.put(1, "sigHupHandler: reopening the log file");
	log.restart();
}

void
sigPipeHandler(int n) {
	log.put(5, "sigPipeHandler: broken pipe");
}

void
sigSegvHandler(int n) {
	log.put(1, "sigSegvHandler: preparing for termination");
	_exit(0);
}

const Thread *mainThread = NULL;

void
sigTermHandler(int sig) {
	if (Thread::currentThread() == mainThread) {
		log.put(1, "sigTermHandler: preparing for termination");
		Server::terminate();
//		signal(sig, SIG_DFL);
//		raise(sig);
		exit(0);
	}
	else {
		log.put(1, "sigTermHandler: not main thread");
		_exit(0);
	}
}

int
main(int argc, char **args) {
	try {
		String appName = args[0];
		if (appName.endsWith("CDWpasswd")) {
			if (argc < 2) {
				printf("Usage: %s <userid>\n", args[0]);
				return 1;
			}
			String id = args[1];
			return ServerPro::passwd(id);
		}
		else
		if (appName.endsWith("CDWuseradd")) {
			if (argc < 2) {
				printf("Usage: %s <userid>\n", args[0]);
				return 1;
			}
			String id = args[1];
			return ServerPro::useradd(id);
		}
	} catch (Exception *e) {
			printf("%s\n", e->toString().getBytes());
			return 1;
	}

	String configFile = "";
	int logToStdOut = 0, foreground = 0;
	for (int i = 1; i < argc; i++) {
		String arg = args[i];
		if (arg.startsWith("--config="))
			configFile = arg.substring(9);
		else
		if (arg.equals("--fg"))
			foreground = 1;
		else
		if (arg.equals("--stdout"))
			logToStdOut = 1;
		else {
			printf("%s%s %s\n",
				Version.product, Version.productPro, Version.version);
			printf("%s %s\n", Version.copyright, Version.author);
			printf("Usage: %s [options]\n", args[0]);
			printf("\t--config=FILE\tthe configuration file to use\n");
			printf("\t--fg\t\tdo not go to the background\n");
			printf("\t--stdout\twrite the log to stdout\n");
			printf("\n");
			return 0;
		}
	}

	config.load(configFile);
	StringBuffer::setLog(&log);

	if (!foreground) {
		// go to the background
		if (fork() > 0)
			exit(0);
		Thread::updateFirstThread(getpid());
	}

	// try to create a pid file
	const char *pidFileName = getenv("pidFile");
	if (pidFileName == NULL)
		pidFileName = "/var/run/CDWserver.pid";
	FILE *pidFile = fopen(pidFileName, "w");
	if (pidFile != NULL) {
		int pid = getpid();
		fprintf(pidFile, "%d\n", pid);
		fclose(pidFile);
	}

	config.setPermissions();

	ServerSocket serverSocket(config.getPortNo());

	// try to change the group of this process
	setgid(config.getGid());

	// try to change the user of this process
	setuid(config.getUid());

	// the files created by CDWserver should be unreadable
	// to normal users
	umask(S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH);

	// fix problem with mkisofs
	unsetenv("HOME");

	// start logging
	log.start(logToStdOut);
	log.put(1, S.e + "*** "
		+ Version.product + Version.productPro + " "
		+ Version.version + ", "
		+ Server::timeToShortString());
	log.put(1, S.e + "uid = " + (int)getuid()
		+ ", euid = " + (int)geteuid()
		+ ", gid = " + (int)getgid()
		+ ", egid = " + (int)getegid());

	Server::initStatic();
	mainThread = Thread::currentThread();
	signal(SIGABRT, sigAbrtHandler);
	signal(SIGBUS, sigBusHandler);
	signal(SIGHUP, sigHupHandler);
	signal(SIGPIPE, sigPipeHandler);
	signal(SIGSEGV, sigSegvHandler);
	signal(SIGTERM, sigTermHandler);

	for (int i = 1; ; i++) {
		log.put(5, "waiting for request...");
		Socket *clientSocket = serverSocket.accept();
		log.put(4, S.e + "Connection " + i + " accepted");

		(new CDWserver(clientSocket))->start();
	}

	return 0;
}
