SMOLNET PORTAL home about changes
/* qi.c - UIUC CCSO nameserver query interpreter */
/* Bruce Tanner - Cerritos College */

/* Version history: */
/* 1.0  1993/08/15 Initial version */
/* 1.1  1993/08/25 Add field instance attribute, conditionalize options */
/* 1.2  1993/09/08 Soundex is now an indexed explicit field; exact match mode */
/* 1.3  1993/09/15 Added ID to index key to remove duplicate index records */
/* 2.0  1993/09/16 Add login mode (login, answer, clear, logout) */
/* 2.1  1993/09/20 Interactive mode */

#include stdio
#include string
#include ctype
#include ssdef
#include descrip
#include time
#include stdarg
#include rms
#include dvidef
#include dcdef
#include "qi.h"


Fields fields[MAX_FIELD];  /* field attributes, global */
int mode = DEFAULT_MODE;   /* global mode flags */
extern int db_status;      /* status of database */
char login_alias[KEYWORD_SIZE + 1];  /* current login */
char login_challenge[CHALLENGE_SIZE + 1];
int login_mode = MODE_ANONYMOUS;

void db_open();
void db_close();
int read_fields(char *);
int fields_cmd(char *, int);
char *new_string(char *);
char *getlogical(char *);
void qilog(int, char *, ...);
extern int query(char *, int);
extern char *get_value(int, char*, char*, char *);
int quit(char *, int);
Arg *parse_cmd(char *, int);
Arg *make_arg(char *, int, char *, int);
void free_args(Arg *);
void swrite(int, char *, ...);
void writestring(int, char *);
void qiabort(int, char *);
int id_cmd(char *, int);
int stat_cmd(char *, int);
int set_cmd(char *, int);
int site_cmd(char *, int);
int login_cmd(char *, int);
int answer_cmd(char *, int);
int clear_cmd(char *, int);
int logout_cmd(char *, int);
char *challenge(int size);

struct verb_struct {
    char *name;
    int mode; /* requires login, etc */
    int (*proc)(char *, int);
} verbs[] = {{"quit", MODE_ANONYMOUS | MODE_LOGIN, quit},
             {"stop", MODE_ANONYMOUS | MODE_LOGIN, quit},
             {"exit", MODE_ANONYMOUS | MODE_LOGIN, quit},
             {"fields", MODE_ANONYMOUS | MODE_LOGIN, fields_cmd},
             {"query", MODE_ANONYMOUS | MODE_LOGIN, query},
             {"ph", MODE_ANONYMOUS | MODE_LOGIN, query},
             {"status", MODE_ANONYMOUS | MODE_LOGIN, stat_cmd},
             {"id", MODE_ANONYMOUS | MODE_LOGIN, id_cmd},
             {"set", MODE_ANONYMOUS | MODE_LOGIN, set_cmd},
             {"siteinfo", MODE_ANONYMOUS | MODE_LOGIN, site_cmd},
             {"login", MODE_ANONYMOUS, login_cmd},
             {"answer", MODE_PASSWORD, answer_cmd},
             {"clear", MODE_PASSWORD, clear_cmd},
             {"logout", MODE_LOGIN, logout_cmd}
            };

#define MAX_VERBS (sizeof(verbs) / sizeof(struct verb_struct))



/* This program is designed to run as an 'inetd' detached process */

main()
{
    int status, sock, class;
    unsigned short chan;
    $DESCRIPTOR(sysin_dsc, "SYS$INPUT");

    /* open a channel to an INET device */
    status = sys$assign(&sysin_dsc, &chan, 0, 0, 0);
    if ((status & 1) != SS$_NORMAL) {
        qilog(-1, "Open fail status = %d", status);
        exit(status);
    }
    sock = (int) chan;

    status = lib$getdvi(&DVI$_DEVCLASS, &chan, 0, &class, 0, 0);

    /* load fields */
    if (read_fields(getlogical(CONFIG_NAME)) == False)
        exit(4);

    /* initialize randomness */
    srand((int) time((time_t *) NULL));

    db_open();
    while (process(sock, class == DC$_TERM));
    closenet(sock);
    db_close();
}


/* process a command stream */

int process(int sock, int interactive)
{
    int status, ind, length;
    static int bad_cmd = 0;
    char *cp, inputline[MAX_INPUT], *vp, verb[MAX_INPUT];

    strncpy(inputline, "", MAX_INPUT);
    if (interactive) {
        printf("qi> ");
        fgets(inputline, MAX_INPUT, stdin);
        length = strlen(inputline);
    }
    else
        length = readline(sock, inputline, MAX_INPUT); /** Get the line **/
    ZapCRLF(inputline);
    qilog(sock, "Cmd: %s", inputline);

    if (length <= 0) {
        qilog(sock, "Remote end shutdown");
        return False;
    }

    if (strlen(inputline) == 0)  /* ignore blank lines */
        return (++bad_cmd < MAX_BAD);  /* return False if too many null cmds */

    /* put first word of input in verb as lowercase */
    strncpy(verb, "", sizeof(verb));
    vp = verb;
    for (cp = inputline; *cp; cp++)
        if (*cp == ' ')
            break;                  /* break on space */
        else
            *vp++ = _tolower(*cp);  /* copy lower case char to verb */

    for (ind = 0; ind < MAX_VERBS; ind++)
        if (strcmp(verb, verbs[ind].name) == 0)
            break;

    if (ind == MAX_VERBS) {
        qilog(sock, "Unknown command: /%s/%s/", verb, inputline);
        writestring(sock, "514:Unknown command.\r\n");
        return (++bad_cmd < MAX_BAD);  /* return False if too many bad cmds */
    }

    if (((verbs[ind].mode & login_mode) == 0) && (login_mode == MODE_ANONYMOUS)) {
        qilog(sock, "Not logged in: %s", inputline);
        writestring(sock, "506:Request refused; must be logged in to execute.\r\n");
        return (++bad_cmd < MAX_BAD);  /* return False if too many bad cmds */
    }

    if (((verbs[ind].mode & login_mode) == 0) && (login_mode == MODE_PASSWORD)) {
        qilog(sock, "Not answer or clear: %s", inputline);
        writestring(sock, "523:Expecting 'answer' or 'clear'\r\n");
        return (++bad_cmd < MAX_BAD);  /* return False if too many bad cmds */
    }

    if ((ind < MAX_VERBS) && (verbs[ind].mode & login_mode))
            status = (*verbs[ind].proc)(inputline, sock);

    bad_cmd = 0;                  /* reset bad command count */
    return status;
}


int quit(char *cmd, int sock)
{
    writestring(sock, "200:Bye!\r\n");
    return False;
}


char * get_field(char *ptr, char *field, int lower)
{
    int ind;

    for (ind= 0; *ptr != '\0' && *ptr != ':'; ptr++, ind++)
        field[ind] = lower ? _tolower(*ptr) : *ptr;
    field[ind] = '\0';
    if (*ptr == ':') ptr++;  /* skip over terminating ":" */
    return ptr;
}


int read_fields(char *file)
{
    FILE *cnf;
    char *ptr, line[256], field[128];
    int ind, field_idx, count = 0;

    for (ind = 0; ind < MAX_FIELD; ind++) {
        fields[ind].number = NULL;
        fields[ind].name = NULL;
        fields[ind].desc = NULL;
        fields[ind].attrib = 0;
    }

    cnf = fopen(file, "r", "shr=get");
    if (cnf == NULL)
        return (False);

    while (fgets(line, sizeof(line), cnf)) {
        ZapCRLF(line);
        ptr = line;
        if ((*ptr == '#') || (*ptr == '\0'))    /* comment or blank? */
            continue;                           /* yes, skip line */
        count++;
        ptr = get_field(ptr, field, False);     /* field number */
        field_idx = atoi(field);
        fields[field_idx].number = new_string(field);

        ptr = get_field(ptr, field, True);      /* field name */
        fields[field_idx].name = new_string(field);

        ptr = get_field(ptr, field, False);     /* field size (ignore) */

        ptr = get_field(ptr, field, False);     /* field description */
        fields[field_idx].desc = new_string(field);

        ptr = get_field(ptr, field, False);     /* field option (ignore) */

        for (;;) {
            ptr = get_field(ptr, field, True);  /* get attribute */
            if (strlen(field) == 0)
                break;                          /* no more attributes */
            fields[field_idx].attrib |= field_attrib(field);
        }
        if (fields[field_idx].number < 1)
            qilog(-1, "Field \"%s\" has illegal field number",
                  fields[field_idx].name);
    }

    fclose(cnf);
    return True;
}


int write_afield(int field_num, int sock)
{
    char line[128];
    int aidx;

    if (fields[field_num].name == NULL)
        return False;
    sprintf(line, "-200:%d:%s:max %d",
            field_num, fields[field_num].name, DATA_SIZE);
    for (aidx = 0; aidx < MAX_ATTRIBUTES; aidx++)
        if (fields[field_num].attrib & attributes[aidx].value) {
            strcat(line, " ");
            strcat(line, attributes[aidx].name);
        }
    strcat(line, "\r\n");
    writestring(sock, line);
    swrite(sock, "-200:%d:%s:%s\r\n",
            field_num, fields[field_num].name, fields[field_num].desc);
    return True;
}


int fields_cmd(char *cmd, int sock)
{
    int fidx, aidx, count = 0;
    char line[256];
    Arg *list, *listp;

    list = listp = parse_cmd(cmd, sock);

    if (list == NULL)   /* null arg list means all fields */
        for (fidx = 0; fidx < MAX_FIELD; fidx++)
            count += write_afield(fidx, sock);
    else
        for (; listp; listp = listp->next)
            if (listp->field > -1)
                count += write_afield(listp->field, sock);
            else
                writestring(sock, "507:Field does not exist.\r\n");

    free_args(list);

    writestring(sock, "200:Ok.\r\n");
    if (DEBUG) qilog(sock, "Sent %d field definitions", count);
    return True;
}


id_cmd(char *cmd, int sock)
{
    writestring(sock, "200:Thanks, but we don't use ids here.\r\n");
    return True;
}


stat_cmd(char *cmd, int sock)
{
    if ((db_status & 1) == SS$_NORMAL)
        writestring(sock, "200:Database ready.\r\n");
    else
        writestring(sock, "475:Database unavailable; try later.\r\n");
    return True;
}


/* set global mode flags on/off */
int set_cmd(char *cmd, int sock)
{
    Arg *list, *listp;
    int index;

    list = listp = parse_cmd(cmd, sock);
    for (; listp; listp = listp->next) {
        for (index = 0; index < MAX_MODES; index++)
            if (listp->name && strcmp(listp->name, modes[index].name) == 0) {
                switch (listp->type & TYPE_MASK) {
                case TYPE_ON:
                    mode |= modes[index].value;
                    writestring(sock, "200:Done.\r\n");
                    break;
                case TYPE_OFF:
                    mode &= ~modes[index].value;
                    writestring(sock, "200:Done.\r\n");
                    break;
                default:
                    writestring(sock, "513:Option must be ON or OFF.\r\n");
                    break;
                }
                break;
            }
        if (index == MAX_MODES)
            swrite(sock, "513:Unknown mode %s\r\n",
                   listp && listp->name ? listp->name : "");
    }
    free_args(list);
    return True;
}


/* return some arbitrary site info */
int site_cmd(char *cmd, int sock)
{
    FILE *fd;
    char line[128];

    swrite(sock, "-200:0:version:%s\r\n", VERSION);
    if ((fd = fopen(getlogical(SITEINFO_NAME), "r", "shr=get")) == NULL)
        swrite(sock, "525:No siteinfo available.\r\n");
    else {
        while (fgets(line, sizeof(line), fd)) {
            ZapCRLF(line);
            swrite(sock, "-200:%s\r\n", line);
        }
        swrite(sock, "200:Ok.\r\n");
        fclose(fd);
    }
    return True;
}


/* set current alias */
int login_cmd(char *cmd, int sock)
{
    Arg *list;
    char *ap;

    list = parse_cmd(cmd, sock);
    if ((list == NULL) || (list->next) || (list->type != TYPE_VALUE)) {
        free_args(list);
        writestring(sock, "599:Syntax error\r\n");
        return True;
    }
    if ((ap = get_value(sock, list->value, ALIAS_FIELD, ALIAS_FIELD)) == NULL) {
        free_args(list);
        swrite(sock, "500:Alias does not exist\r\n");
        return True;
    }
    free_args(list);
    strcpy(login_alias, ap);
    if (get_value(sock, login_alias, ALIAS_FIELD, PASSWORD_FIELD) == NULL) {
        swrite(sock, "500:Password does not exist\r\n");
        return True;
    }
    strcpy(login_challenge, challenge(CHALLENGE_SIZE));
    swrite(sock, "301:%s\r\n", login_challenge);
    login_mode = MODE_PASSWORD;
    return True;
}


int clear_cmd(char *cmd, int sock)
{
    char *cp, *pw;

    cp = strchr(cmd, ' ');             /* skip verb */
    for (;cp && *cp && (*cp == ' '); cp++);  /* skip spaces after verb */
    if (cp == NULL) {
        writestring(sock, "599:Syntax error\r\n");
        login_mode = MODE_ANONYMOUS;
        return True;
    }
    if ((pw = get_value(sock, login_alias, ALIAS_FIELD, PASSWORD_FIELD)) == NULL) {
        swrite(sock, "500:Password does not exist\r\n");
        login_mode = MODE_ANONYMOUS;
        return True;
    }
    if (strcmp(cp, pw) == 0) {
        swrite(sock, "200:%s:Password accepted\r\n", login_alias);
        login_mode = MODE_LOGIN;
    }
    else {
        swrite(sock, "500:Login failed\r\n", login_alias);
        login_mode = MODE_ANONYMOUS;
    }
    return True;
}


int answer_cmd(char *cmd, int sock)
{
    char *cp, decrypted[128];

    cp = strchr(cmd, ' ');             /* skip verb */
    for (;cp && *cp && (*cp == ' '); cp++);  /* skip spaces after verb */
    if (cp == NULL) {
        writestring(sock, "599:Syntax error\r\n");
        login_mode = MODE_ANONYMOUS;
        return True;
    }
    crypt_start(get_value(sock, login_alias, ALIAS_FIELD, PASSWORD_FIELD));
    decrypt(decrypted, cp);
    if ((cp = strchr(decrypted, '\r')) || (cp = strchr(decrypted, '\n')))
        *cp = '\0';  /* truncate at cr or lf */
    if (DEBUG)
        swrite(sock, "%s decrypted into\r\n%s compared with\r\n%s\r\n",
               cp, decrypted, login_challenge);
    if (strcmp(decrypted, login_challenge) == 0) {
        swrite(sock, "200:%s:Password accepted\r\n", login_alias);
        login_mode = MODE_LOGIN;
    }
    else {
        swrite(sock, "500:Login failed\r\n");
        login_mode = MODE_ANONYMOUS;
    }
    return True;
}


int logout_cmd(char *cmd, int sock)
{
    strncpy(login_alias, "", sizeof(login_alias));
    login_mode = MODE_ANONYMOUS;
    swrite(sock, "200:Done.\r\n");
    return True;
}


char *challenge(int size)
{
    char *ptr, *base;

    base = calloc(size + 1, sizeof(char));
    for (ptr = base; size; ptr++, size--)
        *ptr = (rand() & 0x3f) + 0x21;
    return (base);
}


/* return the attribute value for the given field name */
int field_attrib(char *str)
{
    int ind;

    for (ind = 0; ind < MAX_ATTRIBUTES; ind++)
        if (*str == _tolower(*attributes[ind].name)) /* check only first char */
            return (attributes[ind].value);
    return (0);  /* no match = no bits */
}


/* return the field_number for the given field name */
int field_number(char *str)
{
    int ind;

    for (ind = 0; ind < MAX_FIELD; ind++)
        if (fields[ind].name && (strcmp(str, fields[ind].name) == 0))
            return (atoi(fields[ind].number));
    return (-1);  /* no field number */
}


/* get a token as part of the 'field=value' clause */
/* return pointer to terminator */
char *get_token(char *cp, char *dp)
{
    int in_quote = False;

    if (*cp) {
        while (isspace(*cp)) cp++; /* skip space */
        while (*cp && (in_quote || ((*cp != ' ') && (*cp != '='))))
            if (*cp == '"') {
                in_quote = !in_quote;
                cp++;
            }
            else
                *dp++ = in_quote ? *cp++ : tolower(*cp++);
    }
    *dp = '\0';
    return cp;
}


/* cmd = 'verb [field=]value ...' */
Arg *parse_cmd(char *cmd, int sock)
{
    int index;
    char *cp, token[128];
    Arg *start = NULL, *end = NULL;

    cp = strchr(cmd, ' ');  /* skip verb */
    while (cp) {
        cp = get_token(++cp, token);
        if (strlen(token) == 0)
            return start;
        if (start == NULL)
            start = end = make_arg(NULL, -1, NULL, 0);
        else {
            end->next = make_arg(NULL, -1, NULL, 0);
            end = end->next;
        }
        if (*cp == '=') {
            end->name = new_string(token);
            end->field = field_number(token);
            end->type |= TYPE_NAME | TYPE_EQUAL;
            cp = get_token(++cp, token);
        }
        if (strlen(token)) {
            end->value = new_string(token);
            end->type |= TYPE_VALUE;
        }
        if (strcmp(token, "return") == 0)  /* check for special names */
            end->type |= TYPE_RETURN;
        else if (strcmp(token, "on") == 0)
            end->type |= TYPE_ON;
        else if (strcmp(token, "off") == 0)
            end->type |= TYPE_OFF;
        if (end->field == -1)       /* if there were no field name given */
            end->field = field_number(token);  /* try the field value as a field name */
        if (DEBUG)
            swrite(sock, "-100: Parse >> %s (field %d) = %s\r\n",
                    end->name ? end->name : "", end->field,
                    end->value ? end->value : "");
    }    
    return start;  /* should only get here on null list */
}


Arg *make_arg(char * name, int field, char *value, int type)
{
    Arg *ptr;

    ptr = malloc(sizeof (Arg));
    ptr->type = type;
    ptr->name = name;
    ptr->field = field;
    ptr->value = value;
    ptr->next = (Arg *) 0;
    return ptr;
}
    


void free_args(Arg *ptr)
{
    Arg *next;

    while (ptr) {
        next = ptr->next;
        free(ptr);
        ptr = next;
    }
}


/* copy string into malloc'ed space */

char *new_string(char *str)
{
    char *ptr;

    ptr = (char *) malloc(strlen(str) + 1);
    strcpy(ptr, str);
    return (ptr);
}


void swrite(int sock, char *fmt, ...)
{
    char    buf[512];
    va_list arg_ptr;

    va_start(arg_ptr, fmt);
    vsprintf(buf, fmt, arg_ptr);
    va_end(arg_ptr);

    writestring(sock, buf);
}


void qilog(int sock, char *fmt, ...)
{
    FILE    *logfd;
    char    host_name[256];
    time_t  Now;
    char    NowBuf[26];
    char    *cp;
    char    buf[512];
    va_list arg_ptr;

    va_start(arg_ptr, fmt);
    vsprintf(buf, fmt, arg_ptr);
    va_end(arg_ptr);
    host_name[0] = '\0';
    if (sock > -1)
        inet_netnames(sock, host_name);
    time(&Now);
    cp = (char *) ctime(&Now);
    ZapCRLF(cp);
    cp = strcpy(NowBuf, cp);
    if ((logfd = fopen(getlogical(LOG_NAME), "a",
                       "dna=qi.log", "shr=put")) != NULL) {
        if (strlen(fmt)) {
            fprintf(logfd, "%s %s : %s\n", cp, host_name, buf);
            fflush(logfd);
        }
        fclose(logfd);
    }
}


void qiabort(int sock, char *str)
{
    writestring(sock, str);
    ZapCRLF(str);
    qilog(sock, "Abort: %s", str);
    exit(2);
}


void *my_realloc(void *mem, int size)
{
    if ((mem == (void *) 0))
        return ((void *) malloc(size));
    else
        return ((void *) realloc(mem, size));
}


/* translate an exec mode logical name */
char *getlogical(char *name)
{
#include psldef
#include lnmdef

    typedef struct {
        short length;
        short item_code;
        char  *bufadr;
        short *ret_len_addr;
    } item_desc;

    struct {
        item_desc string;
        int terminator;
    } trnlst;

    static char result[128];  /* the object returned */
    short ret_len = 0;
    int acmode = PSL$C_EXEC;
    $DESCRIPTOR(table_dsc, "LNM$SYSTEM_TABLE");
    struct dsc$descriptor_s log_dsc = { strlen(name), DSC$K_DTYPE_T,
                                        DSC$K_CLASS_S, name };

    trnlst.string.bufadr = result;
    trnlst.string.length = sizeof(result);
    trnlst.string.item_code = LNM$_STRING;
    trnlst.string.ret_len_addr = &ret_len;
    trnlst.terminator = 0;

    sys$trnlnm(0, &table_dsc, &log_dsc, &acmode, &trnlst);
    result[ret_len] = '\0';
    return result;
}
.
Response: text/plain
Original URLgopher://bitreich.org/0/gopher2007/2007-gopher-mirror/gop...
Content-Typetext/plain; charset=utf-8