/*
 * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include <fnmatch.h>
#include <Evas.h>

/* local subsystem functions */
static int auth_action_ok(char *a, uid_t uid, gid_t gid);
static int auth_etc_enlightenment_sysactions(char *a, char *u, char *g);
static char *get_word(char *s, char *d);

/* local subsystem globals */
Evas_Hash *actions = NULL;

/* externally accessible functions */
int
main(int argc, char **argv)
{
   int i;
   int test = 0;
   char *action, *cmd;
   uid_t uid;
   gid_t gid;

   for (i = 1; i < argc; i++)
     {
	if ((!strcmp(argv[i], "-h")) ||
	    (!strcmp(argv[i], "-help")) ||
	    (!strcmp(argv[i], "--help")))
	  {
	     printf(
		    "This is an internal tool for Enlightenment.\n"
		    "do not use it.\n"
		    );
	     exit(0);
	  }
     }
   if (argc == 3)
     {
	if (!strcmp(argv[1], "-t")) test = 1;
	action = argv[2];
     }
   else if (argc == 2)
     {
	action = argv[1];
     }
   else
     {
	exit(-1);
     }

   uid = getuid();
   gid = getgid();
   
   if (setuid(0) != 0)
     {
	printf("ERROR: UNABLE TO ASSUME ROOT PRIVILEDGES\n");
	exit(5);
     }
   if (setgid(0) != 0)
     {
	printf("ERROR: UNABLE TO ASSUME ROOT GROUP PRIVILEDGES\n");
	exit(7);
     }
   
   evas_init();

   if (!auth_action_ok(action, uid, gid))
     {
	printf("ERROR: ACTION NOT ALLOWED: %s\n", action);
	exit(10);
     }
   /* we can add more levels of auth here */
   
   cmd = evas_hash_find(actions, action);
   if (!cmd)
     {
	printf("ERROR: UNDEFINED ACTION: %s\n", action);
	exit(20);
     }
   if (!test) return system(cmd);
   
   evas_shutdown();
   
   return 0;
}

/* local subsystem functions */
static int
auth_action_ok(char *a, uid_t uid, gid_t gid)
{
   struct passwd *pw;
   struct group *gp;
   char *usr = NULL, *grp;
   int ret;

   pw = getpwuid(uid);
   if (!pw) return 0;
   usr = pw->pw_name;
   if (!usr) return 0;
   gp = getgrgid(gid);
   if (gp) grp = gp->gr_name;
   /* first stage - check:
    * PREFIX/etc/enlightenment/sysactions.conf
    */
   ret = auth_etc_enlightenment_sysactions(a, usr, grp);
   if (ret == 1) return 1;
   else if (ret == -1) return 0;
   /* the DEFAULT - allow */
   return 1;
}

static int
auth_etc_enlightenment_sysactions(char *a, char *u, char *g)
{
   FILE *f;
   char file[4096], buf[4096], id[4096], ugname[4096], perm[4096], act[4096];
   char *p, *pp, *s;
   int len, line = 0, ok = 0;
   int allow = 0;
   int deny = 0;
   
   snprintf(file, sizeof(file), "/etc/enlightenment/sysactions.conf");
   f = fopen(file, "r");
   if (!f)
     {
	snprintf(file, sizeof(file), PACKAGE_SYSCONF_DIR"/enlightenment/sysactions.conf");
	f = fopen(file, "r");
	if (!f) return 0;
     }
   while (fgets(buf, sizeof(buf), f))
     {
	line++;
	len = strlen(buf);
	if (len < 1) continue;
	if (buf[len - 1] == '\n') buf[len - 1] = 0;
	/* format:
	 * 
	 * # comment
	 * user:  username  [allow:|deny:] halt reboot ...
	 * group: groupname [allow:|deny:] suspend ...
	 */
	if (buf[0] == '#') continue;
	p = buf;
	p = get_word(p, id);
	p = get_word(p, ugname);
	pp = p;
	p = get_word(p, perm);
	allow = 0;
	deny = 0;
	if (!strcmp(id, "user:"))
	  {
	     if (!fnmatch(ugname, u, 0))
	       {
		  if (!strcmp(perm, "allow:")) allow = 1;
		  else if (!strcmp(perm, "deny:")) deny = 1;
		  else
		    goto malformed;
	       }
	     else
	       continue;
	  }
	else if (!strcmp(id, "group:"))
	  {
	     if (!fnmatch(ugname, g, 0))
	       {
		  if (!strcmp(perm, "allow:")) allow = 1;
		  else if (!strcmp(perm, "deny:")) deny = 1;
		  else
		    goto malformed;
	       }
	     else
	       continue;
	  }
	else if (!strcmp(id, "action:"))
	  {
	     while ((*pp) && (isspace(*pp))) pp++;
	     s = evas_hash_find(actions, ugname);
	     if (s)
	       {
		  actions = evas_hash_del(actions, ugname, s);
		  free(s);
	       }
	     actions = evas_hash_add(actions, ugname, strdup(pp));
	     continue;
	  }
	else if (id[0] == 0)
	  continue;
	else
	  goto malformed;
	
	for (;;)
	  {
	     p = get_word(p, act);
	     if (act[0] == 0) break;
	     if (!fnmatch(act, a, 0))
	       {
		  if (allow) ok = 1;
		  else if (deny) ok = -1;
		  goto done;
	       }
	  }
	
	continue;
	malformed:
	printf("WARNING: %s:%i\n"
	       "LINE: '%s'\n"
	       "MALFORMED LINE. SKIPPED.\n",
	       file, line, buf);
     }
   done:
   fclose(f);
   return ok;
}

static char *
get_word(char *s, char *d)
{
   char *p1, *p2;
   
   p1 = s;
   p2 = d;
   while (*p1)
     {
	if (p2 == d)
	  {
	     if (isspace(*p1))
	       {
		  p1++;
		  continue;
	       }
	  }
	if (isspace(*p1)) break;
	*p2 = *p1;
	p1++;
	p2++;
     }
   *p2 = 0;
   return p1;
}

