#include "bbs.h"

int trans = 0;
int partial = 0;
int justified = 0;
int regformed = 0;

char * const str_space = " \t\n\r";


////////////////////////////////////////////////////////////////////////////
// Regform Utilities
////////////////////////////////////////////////////////////////////////////
#define FN_REGFORM "regform"
#define FN_REQLIST "reg.wait"

// TODO define and use structure instead, even in reg request file.
typedef struct {
    char userid[IDLEN+1];
    char name  [20];
    char career[40];
    char addr  [50];
    char phone [20];
} RegformEntry;

// regform format utilities
int
load_regform_entry(RegformEntry *pre, FILE *fp)
{
    char buf[STRLEN];
    char *v;

    memset(pre, 0, sizeof(RegformEntry));
    while (fgets(buf, sizeof(buf), fp))
    {
        if (buf[0] == '-')
            break;
        buf[sizeof(buf)-1] = 0;
        v = strchr(buf, ':');
        if (v == NULL)
            continue;
        *v++ = 0;
        if (*v == ' ') v++;
        chomp(v);

        if (strcmp(buf, "uid") == 0)
            strlcpy(pre->userid, v, sizeof(pre->userid));
        else if (strcmp(buf, "name") == 0)
            strlcpy(pre->name, v, sizeof(pre->name));
        else if (strcmp(buf, "career") == 0)
            strlcpy(pre->career, v, sizeof(pre->career));
        else if (strcmp(buf, "addr") == 0)
            strlcpy(pre->addr, v, sizeof(pre->addr));
        else if (strcmp(buf, "phone") == 0)
            strlcpy(pre->phone, v, sizeof(pre->phone));
    }
    return pre->userid[0] ? 1 : 0;
}

int
regfrm_load(const char *userid, RegformEntry *pre)
{
    FILE *fp = NULL;
    char fn[PATHLEN];
    int ret = 0;
    sethomefile(fn, userid, FN_REGFORM);
    if (!dashf(fn))
        return 0;

    fp = fopen(fn, "rt");
    if (!fp)
        return 0;
    ret = load_regform_entry(pre, fp);
    fclose(fp);
    return ret;
}

// working queue
FILE *
regq_init_pull()
{
    FILE *fp = tmpfile(), *src =NULL;
    char buf[STRLEN];
    if (!fp) return NULL;
    src = fopen(FN_REQLIST, "rt");
    if (!src) { fclose(fp); return NULL; }
    while (fgets(buf, sizeof(buf), src))
	fputs(buf, fp);
    fclose(src);
    rewind(fp);
    return fp;
}

int 
regq_pull(FILE *fp, char *uid)
{
    char buf[STRLEN];
    size_t idlen = 0;
    uid[0] = 0;
    if (fgets(buf, sizeof(buf), fp) == NULL)
	return 0;
    idlen = strcspn(buf, str_space);
    if (idlen < 1) return 0;
    if (idlen > IDLEN) idlen = IDLEN;
    strlcpy(uid, buf, idlen+1);
    return 1;
}

int
regq_end_pull(FILE *fp)
{
    // no need to unlink because fp is a tmpfile.
    if (!fp) return 0;
    fclose(fp);
    return 1;
}

void transform(userec_t *new, userec_t *old, int i)
{
    char fn[1024];
    char *s, *s2;
    memcpy(new, old, sizeof(userec_t));

    // already converted?
    if (old->version == PASSWD_VERSION || !old->userid[0])
	return;

    if (old->version != 2275 && old->userid[0])
    {
	fprintf(stderr, "invalid record at %d: %s\n", i, old->userid);
	if (i == 1)
	    memset(new, 0, sizeof(userec_t));
	return;
    }

    // update version info
    new->version = PASSWD_VERSION;
    // zero out
    memset(new->career,  0, sizeof(new->career));
    memset(new->phone,   0, sizeof(new->phone));
    memset(new->chkpad0, 0, sizeof(new->chkpad0));
    memset(new->chkpad1, 0, sizeof(new->chkpad1));
    memset(new->chkpad2, 0, sizeof(new->chkpad2));

    // quick skip
    if (!old->justify[0] || !old->userid[0])
	return;

    printf("%s ", old->userid);
    printf("%s -> ", old->justify);

    s = old->justify;

    // process only if justify is truncated.
    while (strlen(old->justify) == sizeof(old->justify) -1)
    {
	RegformEntry re;

	// try regform first.
	if (regfrm_load(old->userid, &re)) {
	    // trust the form.
	    strlcpy(new->career, re.career, sizeof(new->career));
	    strlcpy(new->phone,  re.phone,  sizeof(new->phone));
	    // justify will be updated someday... throw it
	    printf("<regq> [%s][%s][%s]\n", new->career, new->phone, new->justify);
	    regformed ++;
	    trans++;
	    return;
	}

	// try to build from ~/justify, if exists.
	sethomefile(fn, old->userid, "justify");
	if (dashf(fn)) {
	    FILE *fp = fopen(fn, "rt");
	    fn[0] = 0;
	    fgets(fn, sizeof(fn), fp);
	    fclose(fp);
	    chomp(fn);

	    if (strlen(fn) > strlen(s))
	    {
		char *xs;
		int quotes = 3;
		s = fn;
		// some justifies have multiple entries without \n...
		xs = s + strlen(s);
		while (quotes > 0 && xs > s)
		    if (*--xs == ':')
			quotes--;
		if (xs > s)
		    s = xs+1;

		justified ++;
	    }
	}

	break;
    }

    // build career/phone
    s2 = strchr(s, ':');
    if (s2) {
	*s2++ = 0;
	while (*s && !isdigit(*s) && *s != '(') s++;
	strlcpy(new->phone,   s,  sizeof(new->phone));
	strlcpy(new->justify, s2, sizeof(new->justify));
	s = s2;
	s2 = strchr(s, ':');
	if (s2)
	{
	    *s2++ = 0;
	}
	else if (new->userlevel & PERM_LOGINOK)
	{
	    // skip non-loginOK users
	    partial ++;
	    printf(" <partial> ");
	}
	strlcpy(new->career, s, sizeof(new->career));
	if (s2)
	    strlcpy(new->justify, s2, sizeof(new->justify));
	else
	    strlcpy(new->justify, "", sizeof(new->justify));
    }
    printf("[%s][%s][%s]\n", new->career, new->phone, new->justify);

    trans++;
}

int main(void)
{
    int fd, fdw;
    userec_t new;
    userec_t old;
    int i = 0;

    printf("sizeof(userec_t)=%u\n", (unsigned int)sizeof(userec_t));
    printf("You're going to convert your .PASSWDS\n");
    printf("The new file will be named .PASSWDS.trans.tmp\n");

    if (chdir(BBSHOME) < 0) {
	perror("chdir");
	exit(-1);
    }

    if ((fd = open(FN_PASSWD, O_RDONLY)) < 0 ||
	 (fdw = open(FN_PASSWD".trans.tmp", O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0 ) {
	perror("open");
	exit(-1);
    }

    while (read(fd, &old, sizeof(old)) > 0) {
	transform(&new, &old, ++i);
	write(fdw, &new, sizeof(new));
    }

    close(fd);
    close(fdw);

    // TODO use FN_REQLIST to process other forms
    printf("total %d records translated (%d partial, %d justified, %d regformed).\n", 
	    trans, partial, justified, regformed);
    return 0;
}
