

/*************************************************************************
 *									 *
 *									 *
 *        SIPB XLOGIN HACKS						 *
 *									 *
 *  There are two hooks that this code needs in the xlogin source.	 *
 *  Both are (currently) in xlogin.c.  The very last line of main()	 *
 *  (before XtMainLoop, moron) should be a call to sipb_init().		 *
 *  This will load in configuration files and bitmap			 *
 *  files.  The next is a block of code in the form			 *
 *									 *
 *    if (sipb_verify (their_username)) {				 *
 *      resetCB(NULL, NULL, NULL);					 *
 *      return;								 *
 *    }									 *
 *									 *
 *  which needs to be added when the user enters their username,	 *
 *  currently on line 553 of xlogin.c (in loginACT(...)).		 *
 *									 *
 *  The username is first checked for in /etc/passwd, and always	 *
 *  passes if it's there.  Then it is looked up in the file		 *
 *  /afs/sipb/admin/office/members_and_prospectives.  This file is in	 *
 *  two columns, establishing a username-userclass pair.  The default	 *
 *  userclass is "unknown".  Current actions are:			 *
 *									 *
 *    Userclass    Explanation						 *
 *    ---------    -----------						 *
 *    member       Let them in just like normal				 *
 *    propsective  Currently, same as member.				 *
 *    unknown      Explain the GOS and allow them to continue login	 *
 *    twit         Explain the GOS and DON'T allow them to continue	 *
 *    jik          ...not saying...					 *
 *    web          ...heh heh heh...					 *
 *									 *
 *************************************************************************/

#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/time.h>
#include <X11/Xlib.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#include <X11/Xaw/Command.h>
#include <X11/Xaw/Dialog.h>
#include <X11/Xaw/Label.h>
#include <X11/Xaw/Text.h>
#include "../wcl/WcCreate.h"
#include "fuzzball.xbm"
#include "sipb.xbm"

#define MAPPING_FILE "/afs/sipb.mit.edu/admin/office/members_and_prospectives"
#define GOS_FILE "/afs/sipb.mit.edu/admin/office/GOS"
#define HEADER_FONT "-adobe-new century schoolbook-bold-r-*-240-*"
#define BODY_FONT "-adobe-new century schoolbook-bold-r-*-140-*"
#define FALLBACK_FONT "9x15"

extern Widget appShell;
Pixmap fuzzy, sipb;
XFontStruct *body, *header;
char *GOS;
  

typedef int (*uclass)();

struct ucpair {
  char name[10];
  uclass class;
  struct ucpair *next;
};

struct classdef {
  char *classname;
  uclass class;
};


int class_member();
int class_prospective();
int class_unknown();
int class_twit();
int class_jik();
int class_web();

static struct ucpair *classes = NULL;
uclass default_class = class_member;
static struct classdef classdefs[] = {
  {"member", class_member}, 
  {"prospective", class_prospective}, 
  {"unknown", class_unknown}, 
  {"twit", class_twit}, 
  {"jik", class_jik},
  {"web", class_web},
  {NULL, NULL}};

char *load_file();

void sipb_init()
{
  FILE *f;
  char linebuf[1024];
  char *c, *username, *classname;
  struct ucpair *ucp;
  struct classdef *def;
  Display *dpy = XtDisplay (appShell);
  Screen *screen = XtScreen (appShell);
  Window win = XtWindow (appShell);
  
  GOS = load_file(GOS_FILE);
  fuzzy = XCreatePixmapFromBitmapData (dpy, win,
				       fuzzball_bits, fuzzball_width, fuzzball_height,
				       BlackPixelOfScreen(screen), WhitePixelOfScreen(screen),
				       DefaultDepthOfScreen(screen));
  
  sipb = XCreatePixmapFromBitmapData (dpy, win,
				      sipb_bits, sipb_width, sipb_height,
				      BlackPixelOfScreen(screen), WhitePixelOfScreen(screen),
				      DefaultDepthOfScreen(screen));
  if (!(body = XLoadQueryFont (dpy, BODY_FONT))) 
    body = XLoadQueryFont (dpy, FALLBACK_FONT);
  if (!(header = XLoadQueryFont (dpy, HEADER_FONT))) 
    header = XLoadQueryFont (dpy, FALLBACK_FONT);
  if (!(f = fopen(MAPPING_FILE, "r"))) {
    perror (MAPPING_FILE);		/* It's the right thing for fopen.  so there. */
    exit(1);
  }
  default_class = class_unknown;
  while (fgets(linebuf, 1024, f)) {
    c = linebuf;
    while (*c && isspace(*c)) c++;
    if (*c && *c != '#') {
      username = c;
      while (*c && !isspace(*c)) c++;
      if (*c) {
	*c++ = 0;
	while (*c && isspace(*c)) c++;
	classname = c;
	while (*c && !isspace(*c)) c++;
	*c = 0;
	for (def = classdefs; def->classname; def++)
	  if (!strcmp(def->classname, classname))
	    break;
	ucp = (struct ucpair *) malloc(sizeof(struct ucpair));
	strncpy (ucp->name, username, 9);
	if (def->class)
	  ucp->class = def->class;
	else
	  ucp->class = default_class;
	ucp->next = classes;
	classes = ucp;
      }
    }
  }
}

char *load_file (name)
     char *name;
{
  FILE *f;
  char buf[4096], *ret;
  int n;
  
  if (!(f = fopen(name, "r"))) {
    perror(f);
    exit(1);
  }
  n = fread (buf, 1, 4095, f);
  fclose(f);
  buf[n] = 0;
  ret = (char *) malloc(n + 1);
  strcpy (ret, buf);
  return ret;
}


int sipb_verify (who)
     char *who;
{
  struct ucpair *ucp;
  uclass class;
  int deny = 0;
  
  if (!getpwnam(who)) {			/* local accounts are always ok */
    class = default_class;
    for (ucp = classes; ucp; ucp = ucp->next)
      if (!strcmp(who, ucp->name)) {
	class = ucp->class;
	break;
    }
    deny = (*class)(who);
  }
  return deny;
}

sipb_abort_verify()
{
    cleanup(NULL);
    _exit(1);
}


void msleep(msec)			/* Sleep for given number of milliseconds */
     int msec;
{
  struct timeval timeout;
  timeout.tv_sec = msec / 1000;
  timeout.tv_usec = 1000*(msec % 1000);
  select (0,0,0,0,&timeout);
}


void center_text (dpy, win, gc, font, win_width, y, text)
     Display *dpy;
     Window win;
     GC gc;
     XFontStruct *font;
     int win_width;
     int y;
     char *text;
{
  int len = strlen(text);
  int wid = XTextWidth (font, text, len);
  XSetFont (dpy, gc, font->fid);
  XDrawString (dpy, win, gc, (win_width - wid) / 2, y, text, len);
}


void justify_text (dpy, win, gc, font, hsize, left_x, top_y, text)
     Display *dpy;
     Window win;
     GC gc;
     XFontStruct *font;
     int hsize;
     int left_x;
     int top_y;
     char *text;
{
  int this_width;
  int x;
  int y = top_y;
  char *first, *goodbreak;

  XSetFont (dpy, gc, font->fid);
  while (*text) {
    this_width = 0;
    x = left_x;
    first = text;
    goodbreak = NULL;
    while (*text && *text != '\n' && this_width <= hsize) {
      if (*text == ' ') goodbreak = text;
      this_width += XTextWidth (font, text++, 1);
    }
    if (!*text || *text == '\n') goodbreak = text;
    if (!goodbreak) goodbreak = text - 1;
    XDrawString (dpy, win, gc, left_x, y, first, goodbreak - first);
    y += font->ascent + font->descent;
    text = goodbreak;
    while (*text == ' ') text++;
    if (*text == '\n') text++;
  }
}

Window make_a_window (dpy, screen, x, y, width, height)
     Display *dpy;
     Screen *screen;
     int x, y, width, height;
{
  XSetWindowAttributes xswa;
  Window win;
  
  xswa.event_mask = KeyPressMask | ButtonPressMask | ButtonReleaseMask;
  xswa.background_pixel = WhitePixelOfScreen(screen);
  win = XCreateWindow (dpy, RootWindowOfScreen(screen),
		       x, y, width, height,
		       3, DefaultDepthOfScreen(screen),
		       InputOutput, DefaultVisualOfScreen(screen),
		       CWEventMask | CWBackPixel, &xswa);
  XMapWindow (dpy, win);
  XClearWindow (dpy, win);
  return win;
}

GC make_a_gc (dpy, screen, win)
     Display *dpy;
     Screen *screen;
     Window win;
{
  XGCValues xgcv;
	      
  xgcv.foreground = BlackPixelOfScreen(screen);
  xgcv.background = WhitePixelOfScreen(screen);
  xgcv.function = GXcopy;
  return XCreateGC(dpy, win, GCForeground | GCBackground | GCFunction, &xgcv);
}

int do_buttons (dpy, win, gc, font, win_width, y,
		num_buttons, butt1, butt2, butt3, butt4) /* No more than four for now.. */
     Display *dpy;
     Window win;
     GC gc;
     XFontStruct *font;
     int win_width;
     int y;
     int num_buttons;
     char *butt1;
     char *butt2;
     char *butt3;
     char *butt4;
{
  struct sbutton {
    char *name;
    int x, y, width, height;
  } bu[4];
  int space, but, x, len;
  int which_down = -1;
  int which_in;
  int chosen = -1;
  int bx, by;
  XEvent ev;

  bu[0].name = butt1;
  bu[1].name = butt2;
  bu[2].name = butt3;
  bu[3].name = butt4;
  space = win_width / (num_buttons + 1);
  XSetFont (dpy, gc, font->fid);
  for (x = space, but = 0; but < num_buttons; x += space, but++) {
    /*
    XSetLineAttributes(dpy, gc, 2, LineSolid, CapButt, JoinBevel);
    */
    bu[but].width = 10 + XTextWidth(font, bu[but].name, len = strlen(bu[but].name));
    bu[but].height = 10 + font->ascent + font->descent;
    bu[but].x = x - bu[but].width/2;
    bu[but].y = y;
    XDrawRectangle (dpy, win, gc, bu[but].x, bu[but].y, bu[but].width - 1, bu[but].height - 1);
    XDrawRectangle (dpy, win, gc, bu[but].x + 1, bu[but].y + 1, bu[but].width - 3, bu[but].height - 3);
    XDrawString (dpy, win, gc, bu[but].x + 5, bu[but].y + 5 + font->ascent,
		 bu[but].name, len);
  }
  XSetFunction (dpy, gc, GXinvert); 
  XSetFillStyle (dpy, gc, FillSolid);
  do {
    XNextEvent (dpy, &ev);
    if (ev.type == KeyPress) exit(1);
    if (ev.type == ButtonRelease || ev.type == ButtonPress) {
      if (which_down >= 0) 
	XFillRectangle (dpy, win, gc,
			bu[which_down].x + 2, bu[which_down].y + 2,
			bu[which_down].width - 4, bu[which_down].height - 4);
      bx = ev.xbutton.x;
      by = ev.xbutton.y;
      which_in = -1;
      for (but = 0; but < num_buttons; but++)
	if (bx >= bu[but].x && by >= bu[but].y &&
	    bx < bu[but].x + bu[but].width && by < bu[but].y + bu[but].height) {
	  which_in = but;
	  break;
	}
      if (ev.type == ButtonRelease && which_in == which_down)
	chosen = which_down;
      if (ev.type == ButtonPress)
	which_down = which_in;
      else
	which_down = -1;
      if (which_down >= 0)
	XFillRectangle (dpy, win, gc,
			bu[which_down].x + 2, bu[which_down].y + 2,
			bu[which_down].width - 4, bu[which_down].height - 4);
    }
  } while (chosen < 0);
  XSetFunction (dpy, gc, GXcopy);
  return chosen;
}

int class_member(who)
     char *who;
{
#if 0
  Widget login = WcFullNameToWidget(appShell, "*login");
  Widget logo = WcFullNameToWidget(appShell, "*login*logo");
  Display *dpy = XtDisplay(login);
  Screen *screen = XtScreen(login);
  Window window = XtWindow(login);
  int lx, ly, x, y;
  
  XtVaGetValues (login, XtNx, &lx, XtNy, &ly, NULL);
  XtVaGetValues (logo, XtNx, &x, XtNy, &y, NULL);
  printf ("The logo is at (%d,%d) and the login window is at (%d,%d)\n",
	  x, y, lx, ly);
  return 1;
#else
  return 0;
#endif
}

int class_prospective(who)
     char *who;
{
  return 0;
}

int unknown_or_twit(who, is_twit)
     char *who;
     int is_twit;
{
  Display *dpy = XtDisplay (appShell);
  Screen *screen = XtScreen (appShell);
  Window win;
  GC gc;
  int win_x = 100;
  int win_y = 100;
  int win_width = 700;
  int win_height = 600;
  int n;

  win = make_a_window (dpy, screen, win_x, win_y, win_width, win_height);
  gc = make_a_gc (dpy, screen, win);
  XSetForeground (dpy, gc, BlackPixelOfScreen(screen));
  XSetBackground (dpy, gc, WhitePixelOfScreen(screen));
  XCopyArea(dpy, fuzzy, win, gc, 0, 0, fuzzball_width, fuzzball_height,
	    10, 10);
  XCopyArea(dpy, fuzzy, win, gc, 0, 0, fuzzball_width, fuzzball_height,
	    win_width - 10 - fuzzball_width, 10);
  XCopyArea(dpy, sipb, win, gc, 0, 0, sipb_width, sipb_height,
	    (win_width - sipb_width) / 2, 10);
  center_text (dpy, win, gc, header, win_width, 100, "Welcome to SIPB");
  justify_text (dpy, win, gc, body, win_width - 40, 20, 150, GOS);
  if (is_twit)
    n = do_buttons (dpy, win, gc, header, win_width, win_height - 50, 1, "I'll be leaving");
  else
    n = do_buttons (dpy, win, gc, header, win_width, win_height - 50, 2, "Log in anyway", "I'll be leaving");
  XFreeGC (dpy, gc);
  XDestroyWindow (dpy, win);
  return (is_twit || n == 1);
}

int class_unknown(who)
     char *who;
{
  return unknown_or_twit(who, 0);
}

int class_twit(who)
     char *who;
{
  return unknown_or_twit(who, 1);
}


int class_jik(who)
     char *who;
{
  Display *dpy = XtDisplay(appShell);
  Screen *screen = XtScreen(appShell);
  Window win;
  GC gc;
  int width = 700;
  int height = 600;
  int x = -fuzzball_width;
  int y = -fuzzball_height;
  int dx = 4;
  int maxy = height - fuzzball_height - 10;
  int maxx = width - fuzzball_width - 20;
  float dy = 0.0;
  char *string = "jik, the office is a pit!";
  int length = strlen(string);
  int string_width = XTextWidth (header, string, length);
  
  win = make_a_window (dpy, screen, 100, 100, width, height);
  gc = make_a_gc(dpy, screen, win);
  while (1) {
    XCopyArea(dpy, fuzzy, win, gc, 0, 0, fuzzball_width, fuzzball_height, x, y);
    XFlush(dpy);
    msleep(10);				/* 1/100th second */
    if (x >= maxx) break;
    XClearArea(dpy, win, x, y, fuzzball_width, fuzzball_height, False);
    x += dx;
    y += (int) dy;
    dy += 0.4;
    if (y > maxy && dy > 0.0) dy = -0.6*dy;
  }
  XSetFont (dpy, gc, header->fid);
  XDrawString (dpy, win, gc, width - string_width - 20, y - 30, string, length);
  XFlush(dpy);
  sleep(1);
  XFreeGC (dpy, gc);
  XDestroyWindow (dpy, win);
  return 0;
}

int class_web (who)
{
  Display *dpy = XtDisplay (appShell);
  Screen *screen = XtScreen (appShell);
  Window win;
  GC gc;
  XGCValues xgcv;
  int win_x = 100;
  int win_y = 100;
  int win_width = 700;
  int win_height = 600;
  char web_text[1000];
  char *txt[3];
  time_t now = time(0);
  int prob = now % 3;
  int right = (now >> 3) % 3;
  static char *probs[3] = {"sur", "bi", "in"};
  int n = right + prob;
  
  txt[n++ % 3] = "at least";		/* sur */
  txt[n++ % 3] = "at most";		/* in */
  txt[n++ % 3] = "exactly";		/* bi */
  sprintf (web_text, "A %sjection from X to Y is:\n\n\
A) a mapping that hits every element in Y %s once\n\n\
B) a mapping that hits every element in Y %s once\n\n\
C) a mapping that hits every element in Y %s once",
	   probs[prob], txt[0], txt[1], txt[2]);
  win = make_a_window (dpy, screen, win_x, win_y, win_width, win_height);
  gc = XCreateGC(dpy, win, 0, &xgcv);
  XCopyArea(dpy, fuzzy, win, gc, 0, 0, fuzzball_width, fuzzball_height,
	    10, 10);
  XCopyArea(dpy, fuzzy, win, gc, 0, 0, fuzzball_width, fuzzball_height,
	    win_width - 10 - fuzzball_width, 10);
  XCopyArea(dpy, sipb, win, gc, 0, 0, sipb_width, sipb_height,
	    (win_width - sipb_width) / 2, 10);
  center_text (dpy, win, gc, header, win_width, 100, "Yo BUDDY, got a question fer ya!");
  justify_text (dpy, win, gc, body, win_width - 40, 20, 150, web_text);
  n = do_buttons (dpy, win, gc, header, win_width, win_height - 50, 3, "A", "B", "C");
  XFreeGC (dpy, gc);
  XDestroyWindow (dpy, win);
  return (n != right);
}
