#include #include #include #include #include #include #include #include #include "cforum.h" #include "crypt/ow-crypt.h" static char *strdupn(const unsigned char *); /* * The `add' functions insert an att/post/user struct into the database. */ int addatt(struct att *att) { sqlite3_stmt *stmt; if(sqlite3_prepare(db, "INSERT INTO atts" " (post, name, desc, mime, data)" " VALUES (?, ?, ?, ?, ?)", -1, &stmt, 0) != SQLITE_OK) goto err; if(sqlite3_bind_int(stmt, 1, att->post) != SQLITE_OK) goto err; if(sqlite3_bind_text(stmt, 2, att->name, -1, SQLITE_STATIC) != SQLITE_OK) goto err; if(sqlite3_bind_text(stmt, 3, att->desc, -1, SQLITE_STATIC) != SQLITE_OK) goto err; if(sqlite3_bind_text(stmt, 4, att->mime, -1, SQLITE_STATIC) != SQLITE_OK) goto err; if(sqlite3_bind_blob(stmt, 5, att->data, att->bytes, SQLITE_STATIC) != SQLITE_OK) goto err; if(sqlite3_step(stmt) != SQLITE_DONE) goto err; sqlite3_finalize(stmt); return 1; err: sqlite3_finalize(stmt); return 0; } int addpost(struct post *post) { sqlite3_stmt *stmt; if(sqlite3_prepare(db, "INSERT INTO posts" " (parent, user, created, subject, text)" " VALUES (?, ?, ?, ?, ?)", -1, &stmt, 0) != SQLITE_OK) goto err; if(sqlite3_bind_int(stmt, 1, post->parent) != SQLITE_OK) goto err; if(sqlite3_bind_int(stmt, 2, post->user) != SQLITE_OK) goto err; if(sqlite3_bind_int(stmt, 3, post->created) != SQLITE_OK) goto err; if(sqlite3_bind_text(stmt, 4, post->subject, -1, SQLITE_STATIC) != SQLITE_OK) goto err; if(sqlite3_bind_text(stmt, 5, post->text, -1, SQLITE_STATIC) != SQLITE_OK) goto err; if(sqlite3_step(stmt) != SQLITE_DONE) goto err; sqlite3_finalize(stmt); return 1; err: sqlite3_finalize(stmt); return 0; } int addsession(struct session *session) { sqlite3_stmt *stmt; if(sqlite3_prepare(db, "INSERT INTO sessions" " (user, string, created)" " VALUES (?, ?, ?)", -1, &stmt, 0) != SQLITE_OK) goto err; if(sqlite3_bind_int(stmt, 1, session->user) != SQLITE_OK) goto err; if(sqlite3_bind_text(stmt, 2, session->string, -1, SQLITE_STATIC) != SQLITE_OK) goto err; if(sqlite3_bind_int(stmt, 3, time(NULL)) != SQLITE_OK) goto err; if(sqlite3_step(stmt) != SQLITE_DONE) goto err; sqlite3_finalize(stmt); return 1; err: sqlite3_finalize(stmt); return 0; } int adduser(struct user *user) { sqlite3_stmt *stmt; if(sqlite3_prepare(db, "INSERT INTO users" " (name, full, hash, salt, created)" " VALUES (?, ?, ?, ?, ?)", -1, &stmt, 0) != SQLITE_OK) goto err; if(sqlite3_bind_text(stmt, 1, user->name, -1, SQLITE_STATIC) != SQLITE_OK) goto err; if(sqlite3_bind_text(stmt, 2, user->full, -1, SQLITE_STATIC) != SQLITE_OK) goto err; if(sqlite3_bind_text(stmt, 3, user->hash, -1, SQLITE_STATIC) != SQLITE_OK) goto err; if(sqlite3_bind_text(stmt, 4, user->salt, -1, SQLITE_STATIC) != SQLITE_OK) goto err; if(sqlite3_bind_int(stmt, 5, time(NULL)) != SQLITE_OK) goto err; if(sqlite3_step(stmt) != SQLITE_DONE) goto err; sqlite3_finalize(stmt); return 1; err: sqlite3_finalize(stmt); return 0; } int deletesession(struct session *session) { sqlite3_stmt *stmt; if(sqlite3_prepare(db, "DELETE FROM sessions WHERE oid = ?", -1, &stmt, 0) != SQLITE_OK) return 0; if(sqlite3_bind_int(stmt, 1, session->id) != SQLITE_OK) return 0; return sqlite3_step(stmt) != SQLITE_ROW; } /* * The `get' functions retrieve an att/post/user struct once, * after which the statement is automatically finalized. */ struct att * getatt(sqlite3_stmt *stmt) { struct att *att; att = nextatt(stmt); sqlite3_finalize(stmt); return att; } struct post * getpost(sqlite3_stmt *stmt) { struct post *post; post = nextpost(stmt); sqlite3_finalize(stmt); return post; } struct session * getsession(sqlite3_stmt *stmt) { struct session *session; session = nextsession(stmt); sqlite3_finalize(stmt); return session; } struct user * getuser(sqlite3_stmt *stmt) { struct user *user; user = nextuser(stmt); sqlite3_finalize(stmt); return user; } /* Return true if user has given password. */ int haspass(struct user *user, char *pass) { char *newhash; newhash = crypt(pass, user->salt); return strcmp(user->hash, newhash) == 0; } /* Generate string of size-1 random lowercase letters. */ void makerandom(char *buf, int size) { int fd, i; if((fd = open("/dev/urandom", O_RDONLY)) == -1) err(1, "open"); if(read(fd, buf, size-1) == -1) err(1, "read"); close(fd); /* Convert to lowercase letters. */ for(i = 0; i < size; i++) buf[i] = (buf[i]+128)%25+97; buf[size] = 0; } /* Generate new salt and hash for password. */ void makehash(char *pass, char **hash, char **salt) { char data[50]; int fd, n; if((fd = open("/dev/urandom", O_RDONLY)) == -1) err(1, "open"); if((n = read(fd, data, sizeof(data))) == -1) err(1, "read"); if(!(*salt = crypt_gensalt("$2b$", 10, data, sizeof(data)))) err(1, "crypt_gensalt"); *hash = crypt(pass, *salt); close(fd); } /* * The `next' functions create an att/post/user struct by querying * the database with the given stmt. They may be called multiple times * with the same stmt to retrieve multiple rows. */ struct att * nextatt(sqlite3_stmt *stmt) { struct att *att; if(!stmt) return NULL; if(sqlite3_step(stmt) != SQLITE_ROW) return NULL; if(!(att = malloc(sizeof(struct att)))) err(1, "malloc"); att->id = sqlite3_column_int(stmt, 0); att->post = sqlite3_column_int(stmt, 1); att->name = strdupn(sqlite3_column_text(stmt, 2)); att->desc = strdupn(sqlite3_column_text(stmt, 3)); att->mime = strdupn(sqlite3_column_text(stmt, 4)); att->bytes = sqlite3_column_bytes(stmt, 5); if(!(att->data = malloc(att->bytes))) err(1, "malloc"); memcpy(att->data, sqlite3_column_blob(stmt, 5), att->bytes); return att; } struct post * nextpost(sqlite3_stmt *stmt) { struct post *post; if(!stmt) return NULL; if(sqlite3_step(stmt) != SQLITE_ROW) return NULL; if(!(post = malloc(sizeof(struct post)))) err(1, "malloc"); post->id = sqlite3_column_int(stmt, 0); post->parent = sqlite3_column_int(stmt, 1); post->user = sqlite3_column_int(stmt, 2); post->created = sqlite3_column_int(stmt, 3); post->edited = sqlite3_column_int(stmt, 4); post->subject = strdupn(sqlite3_column_text(stmt, 5)); post->text = strdupn(sqlite3_column_text(stmt, 6)); return post; } struct session * nextsession(sqlite3_stmt *stmt) { struct session *session; if(!stmt) return NULL; if(sqlite3_step(stmt) != SQLITE_ROW) return NULL; if(!(session = malloc(sizeof(struct session)))) err(1, "malloc"); session->id = sqlite3_column_int(stmt, 0); session->user = sqlite3_column_int(stmt, 1); session->string = strdupn(sqlite3_column_text(stmt, 2)); session->created = sqlite3_column_int(stmt, 3); return session; } struct user * nextuser(sqlite3_stmt *stmt) { struct user *user; if(!stmt) return NULL; if(sqlite3_step(stmt) != SQLITE_ROW) return NULL; if(!(user = malloc(sizeof(struct user)))) err(1, "malloc"); user->id = sqlite3_column_int(stmt, 0); user->name = strdupn(sqlite3_column_text(stmt, 1)); user->full = strdupn(sqlite3_column_text(stmt, 2)); user->hash = strdupn(sqlite3_column_text(stmt, 3)); user->salt = strdupn(sqlite3_column_text(stmt, 4)); user->created = sqlite3_column_int(stmt, 5); return user; } /* * Create a statement that selects from a given table on a given integer. * The returned statement must eventually be finalized by calling * sqlite3_finalize(sqlite3_stmt *). */ sqlite3_stmt * selectbyint(char *table, char *field, int i) { char sql[100]; sqlite3_stmt *stmt; snprintf(sql, 100, "SELECT oid, * FROM %s WHERE %s = ?", table, field); if(sqlite3_prepare(db, sql, -1, &stmt, 0) != SQLITE_OK) return NULL; if(sqlite3_bind_int(stmt, 1, i) != SQLITE_OK) return NULL; return stmt; } /* Return an allocated copy of string unless NULL. */ char * strdupn(const unsigned char *s) { return s? strdup((char *)s): NULL; }