#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <time.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <signal.h>
#include <syslog.h>

#include "procutils.h"
#include "version.h"
#include "config.h"
#include "node.h"
#include "io.h"

static void pipe_handler(int sig)
{
	syslog(LOG_ERR, "received SIGPIPE");
	end_io(STDIN_FILENO);
	exit(1);
}

int main(int argc, char **argv)
{
	int n, len = 1024;
	char *cp = UNSPEC_EOL;
	int inet = 0;
	int logging = 0;

	while ((n = getopt(argc, argv, "ail")) != -1) {
		switch (n) {
		case 'a':
			cp = AX25_EOL;
			len = 128;
			break;
		case 'i':
			cp = INET_EOL;
			inet = 1;
			break;
		case 'l':
			logging = 1;
			break;

		default:
			fprintf(stderr, "usage: nodeusers [-a] [-i] [-l]\r\n");
			return 1;
                }
        }
	if (logging)
		openlog("nodeusers", LOG_PID, LOG_DAEMON);
	signal(SIGPIPE, pipe_handler);
	init_io(STDIN_FILENO, len, cp);
	if (inet && readline(STDIN_FILENO) == NULL) {
		if (logging)
			syslog(LOG_ERR, "readline: %m");
		end_io(STDIN_FILENO);
		return 1;
	}
	n = user_count();
	tprintf("\n%s (%s), %d user%s.\n\n",
		VERSION, COMPILING, n, n == 1 ? "" : "s");
	user_list(0, NULL);
	if (n > 0)
		tputs("\n");
	if (usflush(STDIN_FILENO) == -1 && logging)
		syslog(LOG_ERR, "usflush: %m");
	end_io(STDIN_FILENO);
	closelog();
	return 0;
}

char *print_node(const char *alias, const char *call)
{
	static char node[17];

	sprintf(node, "%s%s%s",
		!strcmp(alias, "*") ? "" : alias,
		!strcmp(alias, "*") ? "" : ":",
		call);
	return node;
}

int user_list(int argc, char **argv)
{
	FILE *f;
	struct user u;
	struct tm *tp;
	struct proc_nr_nodes *np;
	char buf[80];
	long l;

	if ((f = fopen(DATA_NODE_LOGIN_FILE, "r")) == NULL) {
		perror(DATA_NODE_LOGIN_FILE);
		return 0;
	}
	while (fread(&u, sizeof(u), 1, f) == 1) {
		if (u.pid == -1 || (kill(u.pid, 0) == -1 && errno == ESRCH))
			continue;
		switch (u.ul_type) {
		case AF_FLEXNET:
			sprintf(buf, "FlexNet (%.9s)",
				u.call);
			break;
		case AF_AX25:
			sprintf(buf, "Uplink  (%.9s on port %.10s)",
				u.call, u.ul_name);
			break;
		case AF_NETROM:
			if ((np = find_node(u.ul_name, NULL)) != NULL) {
				sprintf(buf, "Circuit (%.9s %.18s)",
					u.call,
					print_node(np->alias, np->call));
			} else {
				sprintf(buf, "Circuit (%.9s %.18s)",
					u.call, u.ul_name);
			}
			break;
#ifdef HAVE_ROSE
		case AF_ROSE:
			sprintf(buf, "ROSE    (%.9s %.18s)",
				u.call, u.ul_name);
			break;
#endif			
		case AF_INET:
			sprintf(buf, "Telnet  (%.9s @ %.16s)",
				u.call, u.ul_name);
			break;
		case AF_UNSPEC:
			sprintf(buf, "Host    (%.9s on local)",
				u.call);
			break;
		default:
			/* something strange... */
			sprintf(buf, "??????  (%.9s %.18s)",
				u.call, u.ul_name);
			break;
		}
		tprintf("%-37.37s ", buf);
		switch (u.state) {
		case STATE_LOGIN:
			tputs("  -> Logging in");
			break;
		case STATE_IDLE:
			time(&l);
			l -= u.cmdtime;
			tp = gmtime(&l);
			tprintf("  -> Idle     (%d:%02d:%02d:%02d)",
				tp->tm_yday, tp->tm_hour,
				tp->tm_min, tp->tm_sec);
			break;
		case STATE_TRYING:
			switch (u.dl_type) {
			case AF_FLEXNET:
				tprintf("  -> Trying   (%s)",
					u.dl_name);
				break;
			case AF_AX25:
				tprintf("  -> Trying   (%s on port %s)",
					u.dl_name, u.dl_port);
				break;
			case AF_NETROM:
				tprintf("  -> Trying   (%s)",
					u.dl_name);
				break;
			case AF_INET:
				tprintf("  -> Trying   (%s:%s)",
					u.dl_name, u.dl_port);
				break;
			default:
				tputs("  -> ???");
				break;
			}
			break;
		case STATE_CONNECTED:
			switch (u.dl_type) {
			case AF_FLEXNET:
				tprintf("<--> FlexNet  (%s)",
					u.dl_name);
				break;
			case AF_AX25:
				tprintf("<--> Downlink (%s on port %s)",
					u.dl_name, u.dl_port);
				break;
			case AF_NETROM:
				tprintf("<--> Circuit  (%s)",
					u.dl_name);
				break;
			case AF_INET:
				tprintf("<--> Telnet   (%s:%s)",
					u.dl_name, u.dl_port);
				break;
			default:
				tprintf("<--> ???");
				break;
			}
			break;
		case STATE_PINGING:
			tprintf("<--> Pinging  (%s)", u.dl_name);
			break;
		case STATE_EXTCMD:
			tprintf("<--> Extcmd   (%s)", u.dl_name);
			break;
		default:
			/* something strange... */
			tputs("  -> ??????");
			break;
		}
		tputs("\n");
	}
	fclose(f);
	return 0;
}

int user_count(void)
{
	FILE *f;
	struct user u;
	int cnt = 0;

	if ((f = fopen(DATA_NODE_LOGIN_FILE, "r")) == NULL) {
		perror(DATA_NODE_LOGIN_FILE);
		return 0;
	}
	while (fread(&u, sizeof(u), 1, f) == 1)
		if (u.pid != -1 && (kill(u.pid, 0) != -1 || errno != ESRCH))
			cnt++;
	fclose(f);
	return cnt;
}
