/* <qfix.c> 14feb05
**
** Program to fix bad things in <queue.dat> file.
**
** Copyright (C) 2002-2005 Richard P. Howell IV.  This is free
** software; you can distribute it and/or modify it under the terms of
** the GNU General Public License.  There is no warranty whatsoever.
**
** It will try to do the following repairs:
**  - Check for obsolete status code 3
**  - Free up old entries still showing "downloading" status
**  - Reverse server IP addresses which are endian swapped
**  - Supply port 8080 if the port address is missing 
**  - Check for orphaned results files
*/

#ifndef SYSTYPE			/* Really it should always be compiler arg */
# if defined (__i386__) && (defined(__linux__) || defined(__unix__))
#  define SYSTYPE	0
# elif defined (__i386__) && (defined(__WIN32__) || defined(WIN32))
#  define SYSTYPE	1
# elif defined(__ppc__) && defined(__APPLE__)
#  define SYSTYPE	2
# else
#  define SYSTYPE	-1
# endif
#endif
#ifndef MAXQVER
# define MAXQVER	510	/* Maximum queue version number allowed */
#endif

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>

typedef int bool;
#ifndef FALSE
# define FALSE		0
# define TRUE		1
#endif

#define TIME_OFS 946684800U	/* Linux epoch is 1970, FAH (Cosm) is 2000 */

#define be4(x)	(((x)[0]<<24)|(((x)[1]&0xFF)<<16)|(((x)[2]&0xFF)<<8)|((x)[3]&0xFF))
#define le4(x)	(((x)[3]<<24)|(((x)[2]&0xFF)<<16)|(((x)[1]&0xFF)<<8)|((x)[0]&0xFF))
#define le2(x)	((((x)[1]&0xFF)<<8)|((x)[0]&0xFF))

FILE *fp, *fp2;
char *qfil;			/* Name of queue data file (default "queue.dat") */
char *fdir;			/* Folding directory (default "./") */
int systype;		/* System type (from SYSTYPE) */
unsigned int qver;	/* Queue version number, from queue or best guess */
bool eswap;			/* Queue file with opposite endianness (default FALSE) */
char wbuf[300];
char rbuf[100];

/* Structure of <queue.dat> file */

typedef unsigned int u32;
typedef unsigned short u16;

struct qf
{	u32		version;	/* 0000 Queue (client) version (v2.17 and above) */
	u32		current;	/* 0004 Current index number */
	struct qs
	{	u32		stat;		/* 000 Status */
		char	z004[4];	/* 004 Pad for Windows, others as of v4.01 */
		u32		tdata[8];	/* 008 Time data (epoch 0000 1jan00 UTC) */
		u32		svr1;		/* 040 Server IP address (until v3.0) */
		u32		ustat;		/* 044 Upload status */
		char	url[128];	/* 048 Web address for core downloads */
		u32		m176;		/* 176 Misc1a */
		u32		core;		/* 180 Core_xx number (hex) */
		u32		m184;		/* 184 Misc1b */
		u32		dsiz;		/* 188 wudata_xx.dat file size */
		char	z192[16];
		union
		{	struct
			{	char	proj[2];	/* 208 Project number (LE) */
				char	run[2];		/* 210 Run (LE) */
				char	clone[2];	/* 212 Clone (LE) */
				char	gen[2];		/* 214 Generation (LE) */
				char	issue[2][4];	/* 216 WU issue time (LE) */
			}		f;			/* Folding@home data */
			struct
			{	char	proj[2];	/* 208 Project number (LE) */
				u16		miscg1;		/* 210 Miscg1 */
				char	issue[2][4];	/* 212 WU issue time (LE) */
				u16		miscg2;		/* 220 Miscg2 */
				u16		miscg3;		/* 222 Miscg3 */
			}		g;			/* Genome@home data */
		}		wuid;		/* 208 Work unit ID information */
		char	z224[36];
		char	mid[4];		/* 260 Machine ID (LE) */
		u32		svr2;		/* 264 Server IP address */
		u32		port;		/* 268 Server port number */
		char	type[64];	/* 272 Work unit type */
		char	uname[64];	/* 336 User Name */
		char	teamn[64];	/* 400 Team Number */
		char	uid[8];		/* 464 Stored ID for unit (UserID + MachineID) */
		char	bench[4];	/* 472 Benchmark (as of v3.24) (LE) */
		char	m476[4];	/* 476 Misc3b (unused as of v3.24) (LE) */
		char	z480[16];
		u32		expire;		/* 496 Allowed time to return (seconds) */
		char	z500[8];
		char	aiflag[4];	/* 508 Assignment info present flag (LE or BE) */
		char	aitime[4];	/* 512 Assignment timestamp (LE or BE) */
		char	aidata[4];	/* 516 Assignment info (Ax xx xx xx) (LE or BE) */
		char	csip[4];	/* 520 Collection server IP address (as of v5.00) (LE) */
		char	dstart[4];	/* 524 Download started time (as of v5.00) (BE) */
		char	z528[160];
		u32		due[4];		/* 688 WU expiration time */
		u32		plimit;		/* 704 Packet size limit (as of v5.00) */
		u32		uploads;	/* 708 Number of upload failures (as of v5.00) */
	}		entry[10];	/* 0008 Array of ten queue entries */
	u32		pfract;		/* 7128 Performance fraction (as of v3.24) */
	u32		punits;		/* 7132 Performance fraction unit weight (as of v3.24) */
	u32		drate;		/* 7136 Download rate sliding average (as of v4.00) */
	u32		dunits;		/* 7140 Download rate unit weight (as of v4.00) */
	u32		urate;		/* 7144 Upload rate sliding average (as of v4.00) */
	u32		uunits;		/* 7148 Upload rate unit weight (as of v4.00) */
	char	z7152[16];	/* 7152 (as of v5.00) ...all zeros after queue conversion... */
} qbuf;

void cfn(char *, int);		/* Construct file name (in wbuf) */
void printip(char *, unsigned int, unsigned int);	/* Print IP address */
u32 es32(u32);				/* Endian swap, 4 bytes */
u16 es16(u16);				/* Endian swap, 2 bytes */
void eswp(struct qf *);		/* Endian swap, entire queue file */

/* Finally, the program! */

int main(int argc, char *argv[])		/* qfix */
{
int fsiz;
int wf;
u32 *pstat;
int nn;
int i, n;
bool gf;
struct qs *p;
struct qf *bp;
u32 *tp, *dp;
int proj;
u32 pr, ru, cl, ge;
struct stat st;

	qfil = "queue.dat";
	fdir = "";
	systype = SYSTYPE;		/* Default is native queue type */
	eswap = FALSE;

	cfn(qfil, 0);
	if ((fp = fopen(wbuf, "r+b")) == NULL)
	{	printf("Can't open <%s> file\n", wbuf);
		exit(1);
	}
	fsiz = fread(&qbuf, 1, sizeof(struct qf), fp);
	if (fsiz < 6844)
	{	printf("Can't read <%s> file\n", wbuf);
		exit(1);
	}

	gf = (qbuf.version > 0xFFFF)
			&& ((qbuf.current > 0xFFFF) || (qbuf.current == 0));
	qver = 0;
	switch (fsiz)	/* Determine system type, mainly from file length */
	{
	default:
		printf("Can't determine <%s> file type\n", wbuf);
		exit(1);
	case 6884:		/* 2.15W - 2.17W */
		qver = 217;
		goto tw;
	case 6888:		/* 3.00W - 3.14W */
		qver = 314;
		goto tw;
	case 7048:		/* 3.24W earlier */
	case 7056:		/* 3.24W later */
		qver = 324;
tw:		systype = 1;		/* Windows */
		break;
	case 6848:		/* 2.19L (3.12M) - 3.14LM */
		qver = 314;
		goto tl;
	case 7008:		/* 3.24LM earlier */
	case 7016:		/* 3.24LM later */
	case 7032:		/* 4.00L */
		qver = 324;
tl:		if (gf)			/* Wrong endianness */
#if (SYSTYPE != 2)
			systype = 2;	/* Mac */
		else
#endif
			systype = 0;	/* Linux */
		break;
	case 7072:		/* 4.00W, 4.01LWM */
		qver = 401;
		goto tt;
	case 7168:		/* 5.00LWM */
		qver = 500;
tt:		if (gf)			/* Wrong endianness */
#if (SYSTYPE == 2)
			systype = 1;	/* Linux or Windows */
#else
			systype = 2;	/* Mac */
		else if (qbuf.version == 400)
			systype = 1;	/* Windows */
#endif
		break;
	}
#if (SYSTYPE == 2)
	if (systype != 2)
		eswap = TRUE;
#else
	if (systype == 2)
		eswap = TRUE;
#endif

	i = qbuf.version;
	if (eswap) i = es32(i);
	if ((i <= 9) || (i > MAXQVER))
	{	printf("Unknown version number of <%s> file\n", wbuf);
		exit(1);
	}
	bp = &qbuf;

	if (eswap)
		eswp(&qbuf);

	wf = 0;
	for (nn = 10; --nn >= 0; )
	{	n = ((bp->current & 0xFF) - nn + 10) % 10;		/* Process oldest first */
		p = &bp->entry[n];
		if (qver < 324)
			p = (struct qs *) ((char *) p - 16 * n);
		if (qver < 500)
			p = (struct qs *) ((char *) p - 8 * n);
		if ((qver < 401) && (systype != 1))
		{	p = (struct qs *) ((char *) p - 4 * n);
			pstat = &p->stat;
			p = (struct qs *) ((char *) p - 4);
		}
		else
			pstat = &p->stat;
		tp = p->tdata;						/* Beginning and ending times */
		dp = p->due;						/* Due date */
		if (systype == 2)
		{	++tp;							/* OS X is 4 bytes higher */
			++dp;
		}
		gf = ((p->core == 0xC9) || (p->core == 0xCA));
		proj = gf ? le2(p->wuid.g.proj) : le2(p->wuid.f.proj);

	/* Now the repairs... */

		i = p->svr2;
		printf("entry %d, status %d,", n, *pstat);
		printip(" address ", i, p->port);
		printf("\n");

	/* Check for obsolete status code 3 */

		if (*pstat == 3) 					/* Bad status! */
		{	*pstat = 1; 					/* Fix it */
			printf("  Fixing bad status in entry %d\n", n);
			++wf;
		}

	/* Free up old entry still showing "downloading" status */

		if	(	(*pstat == 4) 				/* Downloading */
			&&	(((bp->current - n + 10) % 10) > 4)	/* and a while ago */
			)
		{	*pstat = 0; 					/* Fix it */
			p->ustat = 0; 					/* Mark it deleted */
			printf("  Deleting \"downloading\" status in entry %d\n", n);
			++wf;
		}

	/* Reverse server IP addresses which are endian swapped */

		if ((i & 0x00FFFFFF) == 0x007A40AB)	/* Server address (VSP), backward */
		{	p->svr2 = 0xAB407A00 + ((i >> 24) & 0xFF);	/* Fix it 171.64.122.0 */
			goto we;
		}
		else if ((i & 0x00FFFFFF) == 0x006741AB)	/* Server address (VSPMF), backward */
		{	p->svr2 = 0xAB416700 + ((i >> 24) & 0xFF);	/* Fix it 171.65.103.0 */
			goto we;
		}
		else if ((i & 0x00FFFFFF) == 0x005943AB)	/* Server address (VSPX), backward */
		{	p->svr2 = 0xAB435900 + ((i >> 24) & 0xFF);	/* Fix it 171.67.89.0 */
we:			i = p->svr2;
			printf("  Fixing wrong-endian address in entry %d\n", n);
			++wf;
		}

	/* Supply port 8080 if the port address is missing */

		if	(	(	((i & 0xFFFFFF00) == 0xAB407A00)	/* Server address */
				||	((i & 0xFFFFFF00) == 0xAB416700)	/* Server address */
				||	((i & 0xFFFFFF00) == 0xAB435900)	/* Server address */
				)
			&&	(p->port == 0)				/* Port 0 ?? */
			)
		{	p->port = 8080;					/* Make it 8080 */
			printf("  Fixing zero port address in entry %d\n", n);
			++wf;
		}

	/* Check for an orphaned results file */

		cfn("work/wuresults_**.dat", n);
		fp2 = NULL;
		if	(	(stat(wbuf, &st) == 0)
			&&	(st.st_size > 1000)
			&&	((fp2 = fopen(wbuf,"rb")) != NULL)
			&&	(fread(rbuf, 1, sizeof(rbuf), fp2) == sizeof(rbuf))
			)
		{	pr = le2(&rbuf[80]);
			ru = le2(&rbuf[82]);
			cl = le2(&rbuf[84]);
			ge = le2(&rbuf[86]);
			printf("  Found results <%s>: proj %u, run %u, clone %u, gen %u\n   -- ",
					wbuf, pr, ru, cl, ge);
			printf("queue entry: proj %u, run %u, clone %u, gen %u\n   -- ",
					proj, le2(p->wuid.f.run), le2(p->wuid.f.clone), le2(p->wuid.f.gen));
			if	(	(pr != proj)
				||	(ru != le2(p->wuid.f.run))
				||	(cl != le2(p->wuid.f.clone))
				||	(ge != le2(p->wuid.f.gen))
				) printf("doesn't match queue entry\n");
			else if ((*pstat == 0) && (p->ustat == 1))
				printf("already sent\n");
			else if (*pstat == 2)
			{	if (p->plimit < (i = st.st_size + 1000000))
				{	printf("increasing packet limit from %u to %u\n", p->plimit, i);
					p->plimit = i;
					++wf;
				}
				else
					printf("already queued for upload\n");
			}
			else if (*pstat != 0)
				printf("queue entry isn't empty\n");
			else if ((p->expire != 0) && ((u32) time(NULL) > (dp[0] + TIME_OFS)))
				printf("expired\n");
			else if (st.st_mtime < (tp[0] + TIME_OFS))
				printf("older than start time\n");
			else
			{	*pstat = 2;					/* Mark the unit ready for upload */
				p->ustat = 1;
				tp[4] = st.st_mtime - TIME_OFS;
				printf("requeued for upload\n");
				++wf;
			}
		}
		if (fp2 != NULL)
			fclose(fp2);
	}

	/* Write it back if it changed */

	if (wf != 0)
	{	if (eswap)
			eswp(&qbuf);
		fseek(fp, 0, 0);
		fwrite(&qbuf, 1, fsiz, fp);
		printf("File needed repair.  Errors fixed: %d.\n", wf);
	}
	else printf("File is OK\n");
	fclose(fp);
	exit(0);
}

/* Construct file name (in wbuf) */

void cfn(char *fn, int n)
{
char *p;
int i;
char w[4];

	if	(	((i = strlen(fdir)) == 0)
		||	(strncmp(fn, "/", 1) == 0)
		||	(strncmp(fn, "./", 2) == 0)
		||	(strncmp(fn, "../", 3) == 0)
#if SYSTYPE == 1
		||	(strncmp(fn, "\\", 1) == 0)
		||	(strncmp(fn, ".\\", 2) == 0)
		||	(strncmp(fn, "..\\", 3) == 0)
		||	(fn[1] == ':')
#endif
		) sprintf(wbuf, "%.200s", fn);
	else if (fdir[i - 1] == '/')
		sprintf(wbuf, "%.200s%.90s", fdir, fn);
	else
		sprintf(wbuf, "%.200s/%.90s", fdir, fn);
	if ((p = strstr(wbuf, "**")) != NULL)
	{	sprintf(w, "%02d", n);
		*p++ = w[0];
		*p = w[1];
	}
#if SYSTYPE == 1
	for (p = wbuf; *p != '\0'; ++p)
		if (*p == '/') *p = '\\';
#endif
}

/* Print IP address */

void printip(char *p, unsigned int ad, unsigned int pt)
{
	printf("%s%d.%d.%d.%d", p,
			ad >> 24, (ad >> 16) & 0xFF, (ad >> 8) & 0xFF, ad & 0xFF);
	if (pt != 0) printf(":%d", pt);
}

u32 es32(u32 i)
{
	return ((i << 24) | ((i & 0xFF00) << 8)
			| ((i & 0xFF0000) >> 8) | ((i & 0xFF000000) >> 24));
}

u16 es16(u16 i)
{
	return (((i << 8) | ((i & 0xFF00) >> 8)) & 0xFFFF);
}

void eswp(struct qf *bp)	/* Swap endianness of entire queue file */
{
int i, n;
struct qs *p;

	bp->version = es32(bp->version);
	bp->current = es32(bp->current);
	for (n = 10; --n >= 0; )
	{	p = &bp->entry[n];
		if (qver < 324)
			p = (struct qs *) ((char *) p - 16 * n);
		if (qver < 500)
			p = (struct qs *) ((char *) p - 8 * n);
		if ((qver < 401) && (systype != 1))
			p = (struct qs *) ((char *) p - 4 * n);
		p->stat = es32(p->stat);
		if ((qver < 401) && (systype != 1))
			p = (struct qs *) ((char *) p - 4);
		p->tdata[0] = es32(p->tdata[0]);
		p->tdata[1] = es32(p->tdata[1]);
		p->tdata[2] = es32(p->tdata[2]);
		p->tdata[3] = es32(p->tdata[3]);
		p->tdata[4] = es32(p->tdata[4]);
		p->tdata[5] = es32(p->tdata[5]);
		p->tdata[6] = es32(p->tdata[6]);
		p->tdata[7] = es32(p->tdata[7]);
		p->svr1 = es32(p->svr1);
		p->ustat = es32(p->ustat);
		p->m176 = es32(p->m176);
		p->core = es32(p->core);
		p->m184 = es32(p->m184);
		p->dsiz = es32(p->dsiz);
		if (((i = p->core) & 0xFF) == 0)
			i = es32(i);
		p->svr2 = es32(p->svr2);
		p->port = es32(p->port);
		p->expire = es32(p->expire);
		if (qver >= 324)
		{	p->due[0] = es32(p->due[0]);
			p->due[1] = es32(p->due[1]);
			p->due[2] = es32(p->due[2]);
			p->due[3] = es32(p->due[3]);
		}
		if (qver >= 500)
		{	p->uploads = es32(p->uploads);
			p->plimit = es32(p->plimit);
		}
	}
	if (qver < 500)
		bp = (struct qf *) ((char *) bp - 80);
	if (qver >= 324)
	{	bp->pfract = es32(bp->pfract);
		bp->punits = es32(bp->punits);
	}
	if (qver >= 400)
	{	bp->drate = es32(bp->drate);
		bp->dunits = es32(bp->dunits);
		bp->urate = es32(bp->urate);
		bp->uunits = es32(bp->uunits);
	}
}
