view sqlickrypt.c @ 120:54979d35611a

Add support for Advanced Rogue 5.
author John "Elwin" Edwards <elwin@sdf.org>
date Sat, 11 Aug 2012 19:48:25 -0700
parents b1480488ce9d
children 0a3ff1267c24
line wrap: on
line source

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sqlite3.h>
#include <unistd.h>
#include <crypt.h>

#define DATABASE "/dgldir/dgamelaunch-X.db"
#define IBUFSIZE 200

/* General idea for return status:
 * 0: success
 * 1: password check failed
 * 2: username not found
 * 3: database error
 * 4: invalid input
 */

/* Opens the database and, less obviously, initializes a statement. */
int opendb(sqlite3 **dbp, sqlite3_stmt **stmtp, char *query) {
  int status;
  status = sqlite3_open(DATABASE, dbp);
  if (status) {
    sqlite3_close(*dbp);
    exit(3);
  }
  sqlite3_prepare_v2(*dbp, query, -1, stmtp, NULL);
  if (*stmtp == NULL) {
    sqlite3_close(*dbp);
    exit(3);
  }
  return 0;
}

void cleanup(sqlite3 *db, sqlite3_stmt *stmt, int status) {
  if (stmt)
    sqlite3_finalize(stmt);
  sqlite3_close(db);
  if (status)
    exit(status);
  return;
}

int check(char *uname, char *pw) {
  char *pwhash, *comphash;
  char *query = "SELECT password FROM dglusers WHERE username=?;";
  int status;
  sqlite3 *db;
  sqlite3_stmt *qstmt;

  opendb(&db, &qstmt, query);
  status = sqlite3_bind_text(qstmt, 1, uname, -1, SQLITE_TRANSIENT);
  if (status)
    cleanup(db, qstmt, 3);
  status = sqlite3_step(qstmt);
  if (status != SQLITE_ROW) {
    sqlite3_finalize(qstmt);
    sqlite3_close(db);
    if (status == SQLITE_DONE)
      return 2; /* User not found */
    return 3;
  }
  pwhash = strdup((char *) sqlite3_column_text(qstmt, 0));
  cleanup(db, qstmt, 0);

  /* Check the password */
  comphash = crypt(pw, pwhash);
  if (!strcmp(pwhash, comphash))
    status = 0;
  else
    status = 1;
  free(pwhash);
  return status;
}

int insertuser(char *uname, char *pw, char *email) {
  char *checkquery = "SELECT * FROM dglusers WHERE username = ?;";
  char *addquery =  "INSERT INTO dglusers (username, password, email) VALUES (?, ?, ?);";
  int status;
  sqlite3 *db;
  sqlite3_stmt *qstmt;

  opendb(&db, &qstmt, checkquery);
  /* Check for existing account in the same transaction with creating it. */
  status = sqlite3_exec(db, "BEGIN;", NULL, NULL, NULL);
  if (status)
    cleanup(db, qstmt, 3);
  sqlite3_bind_text(qstmt, 1, uname, -1, SQLITE_TRANSIENT);
  status = sqlite3_step(qstmt);
  if (status != SQLITE_DONE) {
    sqlite3_finalize(qstmt);
    sqlite3_close(db);
    if (status == SQLITE_ROW)
      return 1; /* User already exists */
    return 3;
  }
  /* The username doesn't exist yet, so create a new account. */
  sqlite3_finalize(qstmt);
  sqlite3_prepare_v2(db, addquery, -1, &qstmt, NULL);
  if (qstmt == NULL)
    cleanup(db, NULL, 3);
  sqlite3_bind_text(qstmt, 1, uname, -1, SQLITE_TRANSIENT);
  sqlite3_bind_text(qstmt, 2, strdup(crypt(pw, pw)), -1, free);
  sqlite3_bind_text(qstmt, 3, email, -1, SQLITE_TRANSIENT);
  status = sqlite3_step(qstmt);
  if (status != SQLITE_DONE)
    cleanup(db, qstmt, 3);
  status = sqlite3_exec(db, "COMMIT;", NULL, NULL, NULL);
  cleanup(db, qstmt, 0);
  return status;
}

int getmail(char *uname) {
  char *mailquery = "SELECT email FROM dglusers WHERE username = ?;";
  int status;
  sqlite3 *db;
  sqlite3_stmt *qstmt;

  opendb(&db, &qstmt, mailquery);
  status = sqlite3_bind_text(qstmt, 1, uname, -1, SQLITE_TRANSIENT);
  if (status)
    cleanup(db, qstmt, 3);
  status = sqlite3_step(qstmt);
  if (status != SQLITE_ROW) {
    sqlite3_finalize(qstmt);
    sqlite3_close(db);
    if (status == SQLITE_DONE)
      return 2; /* User not found */
    return 3;
  }
  printf("%s\n", sqlite3_column_text(qstmt, 0));
  cleanup(db, qstmt, 0);
  return 0;
}

int setmail(char *uname, char *newmail) {
  char *setquery = "UPDATE dglusers SET email = ? WHERE username = ?;";
  sqlite3 *db;
  sqlite3_stmt *qstmt;
  int status;

  opendb(&db, &qstmt, setquery);
  status = sqlite3_bind_text(qstmt, 2, uname, -1, SQLITE_TRANSIENT);
  if (status)
    cleanup(db, qstmt, 3);
  status = sqlite3_bind_text(qstmt, 1, newmail, -1, SQLITE_TRANSIENT);
  if (status)
    cleanup(db, qstmt, 3);
  status = sqlite3_step(qstmt);
  if (status != SQLITE_DONE)
    cleanup(db, qstmt, 3);
  cleanup(db, qstmt, 0);
  return 0;
}

int setpw(char *uname, char *newpw) {
  char *setquery = "UPDATE dglusers SET password = ? WHERE username = ?;";
  sqlite3 *db;
  sqlite3_stmt *qstmt;
  int status;
  char *hash;

  /* This is not a smart use of crypt, but it needs to be compatible with
   * dgamelaunch. */
  hash = crypt(newpw, newpw);
  opendb(&db, &qstmt, setquery);
  status = sqlite3_bind_text(qstmt, 2, uname, -1, SQLITE_TRANSIENT);
  if (status)
    cleanup(db, qstmt, 3);
  status = sqlite3_bind_text(qstmt, 1, hash, -1, SQLITE_TRANSIENT);
  if (status)
    cleanup(db, qstmt, 3);
  status = sqlite3_step(qstmt);
  if (status != SQLITE_DONE)
    cleanup(db, qstmt, 3);
  cleanup(db, qstmt, 0);
  return 0;
}

int main(int argc, char *argv[]) {
  char ibuf[IBUFSIZE], *uname, *line1, *line2;
  char *cptr; // Utility pointer
  int status;

  /* Read in the username and password */
  fgets(ibuf, IBUFSIZE, stdin);
  uname = ibuf;
  line1 = strchr(uname, '\n');
  if (line1 == NULL)
    exit(4); /* Truncated */
  *line1 = '\0';
  if (argc == 1 || strcmp(argv[1], "getmail")) {
    line1++;
    fgets(line1, IBUFSIZE - (line1 - ibuf), stdin);
    if (line1[strlen(line1) - 1] == '\n')
      line1[strlen(line1) - 1] = '\0';
    else
      exit(4); /* Truncated */
  }
  if (argc > 1 && !strcmp(argv[1], "setmail")) {
    /* E-mail, sanitize */
    for (cptr = line1; *cptr != '\0'; cptr++) {
      if (!isalnum(*cptr) && !strchr("@._-", *cptr)) {
        exit(4);
      }
    }
  }
  if (argc > 1 && !strcmp(argv[1], "register")) {
    line2 = line1 + strlen(line1) + 1;
    fgets(line2, IBUFSIZE - (line2 - ibuf), stdin);
    if (line2[strlen(line2) - 1] == '\n')
      line2[strlen(line2) - 1] = '\0';
    else
      exit(4);
    for (cptr = line2; *cptr != '\0'; cptr++) {
      if (!isalnum(*cptr) && !strchr("@._-", *cptr)) {
        exit(4);
      }
    }
  }
  /* Sanitize the username, because it gets put into a query. */
  for (cptr = uname; *cptr != '\0'; cptr++) {
    if (!isalnum(*cptr)) {
      exit(4);
    }
  }
  if (argc == 1 || !strcmp(argv[1], "check"))
    status = check(uname, line1);
  else if (!strcmp(argv[1], "register")) {
    status = insertuser(uname, line1, line2);
  }
  else if (!strcmp(argv[1], "getmail")) {
    status = getmail(uname);
  }
  else if (!strcmp(argv[1], "setmail")) {
    status = setmail(uname, line1);
  }
  else if (!strcmp(argv[1], "setpw")) {
    status = setpw(uname, line1);
  }
  else
    status = 127;
  return status;
}