/* by Christian R. Shelton January 9, 2000
 * (C) 2000 Christian R. Shelton <cshelton@ai.mit.edu>
 * a modification of xlassie (whose copyright appears below)
 *
 * No warrentee is implied or offered.  You may distribute and modify the
 * code as you wish
 *
 * compile as:
 * gcc -o xpress xpress.c -L/usr/X11/lib -lX11 -lXext -lnsl
 * (or maybe add -lsocket depending on the machine)
 */

/* Copyright (C) 1998 Trent Piepho  <xyzzy@u.washington.edu>
 *           (C) 1999 Trent Piepho  <xyzzy@speakeasy.org>
 *
 * This program 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; version 2 of the License.
 *
 * This program 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 this program; if not, write to the Free Software Foundation, Inc., 675
 * Mass Ave, Cambridge, MA 02139, USA.  */

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <signal.h>
#include <time.h>
#include <pwd.h>
#include <errno.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <X11/Xatom.h>
#include <X11/extensions/shape.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <netdb.h>

#define PORT 6006
#define HOST "www.dipbounced.com"

#define FONTNAME "-*-utopia-medium-r-normal--40-*-*-*-*-*-iso8859-1"
#define TIME_INTERVAL 60


/* X related globals */
Display *dpy;
Window win;
GC gc;
XFontStruct *font;
int Ascent=33,Descent=0;
int Width=55,Height=33;
XColor FgXColor,HiXColor;
Pixmap ShapeMask;			/* shape stuff */
GC ShapeGC;					/* shape stuff */
int MaskWidth,MaskHeight;	/* shape stuff */

/* app-default related globals */
char *FgColor="Black",*BgColor="White",*HiColor="Red";
char *FontName=FONTNAME;
char *DisplayName=NULL;
char *AppName;
int Number = -10;
int Interval=TIME_INTERVAL;
char SpoolFile[256];
char Command[256];
char Username[32];
char Suffix[100];
char Password[32];
int Passflag = 0;
char Game[32];
char Power[32];
char *Geometry=NULL;
int Options=0;
#define BELL_MODE		0x01
#define SHAPED_WINDOW	0x02
#define HILIGHT_MAIL	0x04

void usage();
void update();
void handler(int);
void parse_options(int argc,char *argv[]);
void init(int argc,char *argv[]);
int count_mail();

void update()
{
	static int old_number=-1;
	char str[32];
	static int oldw=-1,oldh=-1;
	int w,h;

	if (Number<0) sprintf(str,"?");
	else sprintf(str,"%d",Number);

	w = (Width-XTextWidth(font,str,strlen(str)))/2;
	h = (Height+Ascent-Descent)/2;

	if(Options&SHAPED_WINDOW)  {
		if(Number!=old_number || oldw!=w || oldh!=h)  {
			old_number=Number; oldw=w; oldh=h;
			/* these next 3 lines clear the pixmap, is there a cleaner way? */
			XSetFunction(dpy,ShapeGC,GXclear);
			XFillRectangle(dpy,ShapeMask,ShapeGC,0,0,MaskWidth,MaskHeight);
			XSetFunction(dpy,ShapeGC,GXset);
			XDrawString(dpy,ShapeMask,ShapeGC,0,Ascent,str,strlen(str));
			XShapeCombineMask(dpy, win, ShapeBounding, 
				w, h-Ascent, ShapeMask, ShapeSet);
		};
	} else {
		XClearWindow(dpy,win);
	};

	if(Options&HILIGHT_MAIL)  {
		XSetForeground(dpy,gc,Number?HiXColor.pixel:FgXColor.pixel);
	};

	XDrawString(dpy,win,gc,w,h,str,strlen(str));
}

void handler(int nothing)
{
	int old;

	old = Number;
	count_mail();
	if(old==Number) return;
	update();
	if(Options&BELL_MODE && Number>old) XBell(dpy,100);
	XFlush(dpy);
}

void font_height(void)
{
	int foo,bar,baz;
	XCharStruct extents;

	XTextExtents(font,"0123456789",10,&foo,&bar,&baz,&extents);
	Ascent=extents.ascent;
	Descent=extents.descent;
}

void usage(void)
{
	fprintf(stderr,"XPress v0.5b\t by Christian R. Shelton <cshelton@ai.mit.edu>\n");
	fprintf(stderr,"XPress is a modified version of\n");
	fprintf(stderr,"XLassie v1.4\tby Trent Piepho <xyzzy@u.washington.edu>\n\n");
	fprintf(stderr,"Usage: xpress [-h] | [-options]\n");
	fprintf(stderr,"where options include:\n");
	fprintf(stderr,"    -h                      print usage\n");
	fprintf(stderr,"    -name <name>            Set app name\n");
	fprintf(stderr,"    -suffix <suffix>        Set window title suffix\n");
	fprintf(stderr,"    -fg <color>             foreground color [%s]\n",FgColor);
	fprintf(stderr,"    -bg <color>             background color [%s]\n",BgColor);
	fprintf(stderr,"    -update <number>        update monitor every number seconds\n");
	fprintf(stderr,"    -bell                   ring bell when press count becomes positive arrives\n");
	fprintf(stderr,"    -display <displayname>  X server to connect to\n");
	fprintf(stderr,"    -shape                  Use a shaped window\n");
	fprintf(stderr,"    -command <command>      Command to execute when clicked on\n");
    fprintf(stderr,"    -hilight [color]        Use a different foreground color when\n"
	               "                            there is press [%s]\n",HiColor);
	fprintf(stderr,"    -username <name>        Set username (default: prompt for it)\n");
	fprintf(stderr,"    -password <passwd>      Set password (default: prompt for it)\n");
	fprintf(stderr,"    -game <game>            Set game (default: prompt for it)\n");
	fprintf(stderr,"    -power <power>          Set power (default: prompt for it)\n");
	fprintf(stderr,"[note: if the game string is the single character '*', the total of all press for all games will be displayed]\n");
}

void parse_options(int argc,char *argv[])
{
	int i;
	int intervalused=0;

	for(i=1;i<argc;i++)  {
		if(!strcmp(argv[i],"-fg"))  {
			if(++i==argc)  { usage(); exit(2); };
			FgColor=argv[i];
		} else if(!strcmp(argv[i],"-bg"))  {
			if(++i==argc)  { usage(); exit(2); };
			BgColor=argv[i];
		} else if(!strcmp(argv[i],"-update"))  {
			if(++i==argc)  { usage(); exit(2); };
			Interval=atoi(argv[i]);
			intervalused=1;
		} else if(!strcmp(argv[i],"-bell"))  {
			Options|=BELL_MODE;
		} else if(!strcmp(argv[i],"-shape"))  {
			Options|=SHAPED_WINDOW;
		} else if(!strcmp(argv[i],"-fn") || !strcmp(argv[i],"-font"))  {
			if(++i==argc)  { usage(); exit(2); };
			FontName=argv[i];
		} else if(!strcmp(argv[i],"-display"))  {
			if(++i==argc)  { usage(); exit(2); };
			DisplayName=argv[i];
		} else if(!strcmp(argv[i],"-hilight"))  {
			Options|=HILIGHT_MAIL;
			if(i+1!=argc && argv[i+1][0]!='-') HiColor=argv[++i];
		} else if (!strcmp(argv[i],"-suffix")) {
			if (++i==argc) { usage(); exit(2); }
			strcpy(Suffix,argv[i]);
		} else if (!strcmp(argv[i],"-username")) {
			if (++i==argc) { usage(); exit(2); }
			strcpy(Username,argv[i]);
		} else if (!strcmp(argv[i],"-passflag")) {
			Passflag = 1;
		} else if (!strcmp(argv[i],"-password")) {
			if (++i==argc) { usage(); exit(2); }
			strcpy(Password,argv[i]);
		} else if (!strcmp(argv[i],"-game")) {
			if (++i==argc) { usage(); exit(2); }
			strcpy(Game,argv[i]);
		} else if (!strcmp(argv[i],"-power")) {
			if (++i==argc) { usage(); exit(2); }
			strcpy(Power,argv[i]);
		} else if(!strcmp(argv[i],"-command"))  {
			if(++i==argc)  { usage(); exit(2); };
			strcpy(Command,argv[i]);
		} else if(!strcmp(argv[i],"-geometry"))  {
			if(++i==argc)  { usage(); exit(2); };
			Geometry=argv[i];
		} else if(!strcmp(argv[i],"-name")) {
			if(++i==argc)  { usage(); exit(2); };
			AppName=argv[i];
		} else if(!strcmp(argv[i],"-h") || !strcmp(argv[i],"-help"))  {
			usage(); exit(0);
		} else {
			fprintf(stderr,"Unknown option %s\n",argv[i]);
			fprintf(stderr,"Use -h for help\n");
			exit(2);
		};
	};
}

void grabnames(void) {
	char buf[100];
	if (!Username[0]) {
		printf("BOUNCED username: ");
		scanf("%s",Username);
	}
	if (!Password[0]) {
		if (Passflag) {
			printf("%s password: ",Username);
			scanf("%s",Password);
		} else { 
			sprintf(buf,"%s password: ",Username);
			strcpy(Password,getpass(buf));
		}
	}
	if (!Game[0]) {
		printf("game name: ");
		scanf("%s",Game);
	}
	if (!Power[0]) {
		printf("power: ");
		scanf("%s",Power);
	}
	//close(0); close(1); close(2);
}

void init(int argc,char *argv[])
{
	int screen;
	XWMHints *wmh;
	XSizeHints *xsh;
	XClassHint *classh;
	XTextProperty winName;
	XColor color,tmp;
	int x,y,g;
	char *buf;

	parse_options(argc,argv);
	grabnames();

	dpy=XOpenDisplay(DisplayName);
	if(dpy==NULL)  {
		fprintf(stderr,"Error: Can't open display: %s\n",DisplayName);
		exit(1);
	};
	screen=DefaultScreen(dpy);

	xsh=XAllocSizeHints();
	wmh=XAllocWMHints();
	classh=XAllocClassHint();

	font=XLoadQueryFont(dpy,FontName);
	if(font==NULL)  {
		fprintf(stderr,"%s:  unable to open font \"%s\"\n",argv[0],FontName);
		exit(1);
	};
	font_height();
	Height=Ascent+Descent;
	Width=XTextWidth(font,"88",2);
	xsh->flags = PSize; xsh->width = Width; xsh->height = Height;

	g=XWMGeometry(dpy,screen,Geometry,NULL,0,xsh,&x,&y,&Width,&Height,&g);
	if(g&XValue)  { xsh->x=x; xsh->flags|=USPosition; };
	if(g&YValue)  { xsh->y=y; xsh->flags|=USPosition; };
	if(g&WidthValue)  {xsh->width=Width; xsh->flags|=USSize; 
	} else            {Width = xsh->width; };
	if(g&HeightValue)  {xsh->height=Height; xsh->flags|=USSize;
	} else            {Height = xsh->height; };

	buf = (char *)malloc(strlen(Game)+strlen(Power)+2);
	sprintf(buf,"%s:%s ",Game,Power,Suffix);
	XStringListToTextProperty(&buf,1,&winName);
	free(buf);
	wmh->initial_state=NormalState;
	wmh->input=False;
	wmh->flags = StateHint | InputHint;
	classh->res_name = (AppName==NULL)?"xpress":AppName;
	classh->res_class = "XBiff";

	win=XCreateSimpleWindow(dpy,RootWindow(dpy,screen),x,y,Width,Height,0,
		BlackPixel(dpy,screen),WhitePixel(dpy,screen));
	XSetWMProperties(dpy,win,&winName,NULL,argv,argc,xsh,wmh,classh);

	gc=XCreateGC(dpy,win,0,NULL);

	XAllocNamedColor(dpy,DefaultColormap(dpy,screen),FgColor,&color,&tmp);
	XSetForeground(dpy,gc,color.pixel); FgXColor=color;
	XAllocNamedColor(dpy,DefaultColormap(dpy,screen),BgColor,&color,&tmp);
	XSetBackground(dpy,gc,color.pixel);
	XSetWindowBackground(dpy,win,color.pixel);
	if(Options&HILIGHT_MAIL)  XAllocNamedColor(dpy,DefaultColormap(dpy,screen),
												HiColor,&HiXColor,&tmp);


	XSetFont(dpy,gc,font->fid);

	if(Options&SHAPED_WINDOW)  {
		MaskWidth = Width; MaskHeight = Height;
		ShapeMask = XCreatePixmap(dpy,win,MaskWidth,MaskHeight,1);
		ShapeGC = XCreateGC(dpy,ShapeMask,0,NULL);
		XSetFont(dpy,ShapeGC,font->fid);
	};
}


int main(int argc,char *argv[])
{
	XEvent xev;
	struct itimerval itv;
	struct sigaction sig;
	struct passwd* pwd;
	char *mail;


	pwd = getpwuid(getuid());
	mail=getenv("MAIL");
	if(mail==NULL) {
		sprintf(SpoolFile,"/var/spool/mail/%s",pwd->pw_name);
	} else {
		strcpy(SpoolFile,mail);
	};
	Command[0] = 0;
	Username[0] = 0;
	Password[0] = 0;
	Game[0] = 0;
	Power[0] = 0;
	Suffix[0] = 0;
	
	init(argc,argv);

	count_mail();

	XSelectInput(dpy,win,ExposureMask|ButtonPressMask|StructureNotifyMask);
	XMapWindow(dpy,win);

	if(Options&BELL_MODE && Number) XBell(dpy,100);

	sig.sa_handler=handler;
	sigemptyset(&sig.sa_mask);
	sig.sa_flags=SA_RESTART;
	sigaction(SIGALRM,&sig,NULL);
	itv.it_interval.tv_sec=Interval; itv.it_interval.tv_usec=0;
	itv.it_value = itv.it_interval;
	setitimer(ITIMER_REAL,&itv,NULL);

	for(;;)  {
		XNextEvent(dpy,&xev);
		switch(xev.type)  {
		case Expose:
			while(XCheckTypedEvent(dpy,Expose,&xev));
			update();
			break;
		case ButtonPress:
			if (Command[0] != 0) system(Command);
			break;
		case ConfigureNotify:
			Width = xev.xconfigure.width;
			Height = xev.xconfigure.height;
			update();
			break;
		case DestroyNotify:
			XCloseDisplay(dpy);
			exit(0);
		default:
		};
	};
}

int writen(int fd, char *ptr, int nbytes) {
	int nleft, nwritten;
	fd_set rfds;
	struct timeval tv;
	nleft = nbytes;
	while(nleft>0) {
		nwritten = write(fd,ptr,nleft);
		if (nwritten<=0) {
			if (errno==EAGAIN || errno==EINTR || errno==ENOSPC) {
				FD_ZERO(&rfds);
				FD_SET(fd,&rfds);
				tv.tv_sec = Interval/2;
				tv.tv_usec = 0;
				if (select(fd+1,NULL,&rfds,NULL,&tv)>0) {
					return nwritten;
				}
			}
		}
		nleft -= nwritten;
		ptr += nwritten;
	}
	return nbytes-nleft;
}

int readline(int fd, char *ptr, int maxlen) {
	int n,rc;
	char c;
	fd_set rfds;
	struct timeval tv;
	for(n=1;n<maxlen;n++) {
		if ((rc=read(fd,&c,1)) == 1) {
			printf ("read %d\n",rc);
			*ptr++ = c;
			if (c=='\n') break;
		} else if (rc==0) {
			printf ("got a zero\n");
			if (n==1) {
				*ptr = 0;
				return 0;
			}
			else break;
		} else {
			if (errno == EBADF || errno==EINVAL || errno==EFAULT) {
				*ptr = 0;
				return -1;
			} else {
				FD_ZERO(&rfds);
				FD_SET(fd,&rfds);
				tv.tv_sec = Interval/2;
				tv.tv_usec = 0;
				printf("trying timeout of %d\n",tv.tv_sec);
				if (select(fd+1,&rfds,NULL,NULL,&tv)>0) {
					/* timeout? */
					perror("nope");
					*ptr = 0;
					return 0;
				}
				printf("yup... more data\n");
			}
		}
	}
	*ptr = 0;
	return n;
}

int count_mail() {
	int fd;
	struct sockaddr_in serv_addr;
	struct hostent *hostp;
	char buffer[100];
	char *num;
	int i;
	fd_set rfs;
	struct timeval tv;
	int tempnum;
	
	bzero((char *)&serv_addr, sizeof(serv_addr));
	if ((serv_addr.sin_addr.s_addr = inet_addr(HOST)) == -1) {
		if((hostp = gethostbyname(HOST)) == NULL) {
			if (Number == -10) {
				fprintf(stderr,"Could not find host %s\n",HOST);
				exit(1);
			}
			Number = -5;
			return;
		}
		if (hostp->h_addrtype != AF_INET) {
			if (Number == -10) {
				fprintf(stderr,"Could not find host %s\n",HOST);
				exit(1);
			}
			Number = -5;
			return;
		}
		serv_addr.sin_family = hostp->h_addrtype;
		bcopy((char*)hostp->h_addr,(char*)&serv_addr.sin_addr,
			sizeof(serv_addr.sin_addr));
	} else {
		serv_addr.sin_family=AF_INET;
	}
	serv_addr.sin_port = htons(PORT);
	if ((fd = socket(serv_addr.sin_family,SOCK_STREAM,0)) < 0) {
		if (Number == -10) {
			fprintf (stderr,"Could not open a socket\n");
			exit(1);
		} 
		Number = -5;
		return;
	}
	if (connect(fd,(struct sockaddr *)&serv_addr,
			sizeof(serv_addr)) < 0) {
		if (Number == -10) {
			fprintf(stderr,"Could not connect\n");
			exit(1);
		}
		Number = -5;
		close(fd);
		return;
	}
	writen(fd,Username,strlen(Username));
	writen(fd,"\n",1);
	writen(fd,Password,strlen(Password));
	writen(fd,"\n",1);
	writen(fd,Game,strlen(Game));
	writen(fd,"\n",1);
	if (!strcmp(Game,"*")) {
		Number = 0;
		while(readline(fd,buffer,99)) {
			printf("%s\n",buffer);
			num = strrchr(buffer,':');
			if (num && *num) {
				num++;
				tempnum = atoi(num);
				if (tempnum>0) Number+=tempnum;
			}
		}
	} else {
		writen(fd,Power,strlen(Power));
		writen(fd,"\n",1);
		readline(fd,buffer,100);
		printf("%s\n",buffer);
		Number = atoi(buffer);
	}
	printf("--------------\n");
	if (Number==-1) {
		fprintf(stderr,"Wrong Password\n");
		exit(1);
	} else if (Number==-2) {
		fprintf(stderr,"%s is not playing %s in game %s\n",Username,Power,Game);
		exit(1);
	} else if (Number==-3) { // No pressflag file
		Number = 0;
	} else if (Number < 0) {
		fprintf(stderr,"Unknown Error\n");
		exit(1);
	}
	close(fd);
}
