postit.c (10275B)
1#include <stdlib.h> 2#include <stdint.h> 3#include <stdio.h> 4#include <string.h> 5#include <unistd.h> 6#include <signal.h> 7#include <time.h> 8 9#include "sqlite3.h" 10 11#include "util.h" 12#include "crypto.h" 13 14static char *current_user = NULL; 15static sqlite3 *db = NULL; 16 17static const char *BANNER = 18"\n" 19"...........................................................\n" 20": ########::: #######::: ######:: ########: ####: ########:\n" 21": ##.... ##: ##.... ##: ##... ##:... ##..::. ##::... ##..::\n" 22": ##:::: ##: ##:::: ##: ##:::..::::: ##::::: ##::::: ##::::\n" 23": ########:: ##:::: ##:. ######::::: ##::::: ##::::: ##::::\n" 24": ##.....::: ##:::: ##::..... ##:::: ##::::: ##::::: ##::::\n" 25": ##:::::::: ##:::: ##: ##::: ##:::: ##::::: ##::::: ##::::\n" 26": ##::::::::. #######::. ######::::: ##:::: ####:::: ##::::\n" 27":..::::::::::.......::::......::::::..:::::....:::::..:::::\n" 28"\n" 29" Commands: help, register, users, info, login, post, posts\n" 30"\n"; 31 32void 33cleanup(void) 34{ 35 sqlite3_close(db); 36} 37 38void 39timeout(int sig) 40{ 41 printf("time's up!\n"); 42 exit(1); 43} 44 45void 46init(int argc, const char **argv) 47{ 48 const char *dbpath, *sql; 49 char *err_msg = NULL; 50 int status; 51 52 setvbuf(stdin, NULL, _IONBF, 0); 53 setvbuf(stdout, NULL, _IONBF, 0); 54 55 dbpath = "db.sqlite3"; 56 if (argc > 1) dbpath = argv[1]; 57 58 status = sqlite3_open(dbpath, &db); 59 ASSERTV(status == SQLITE_OK, "Cannot access database: %s", 60 sqlite3_errmsg(db)); 61 62 status = sqlite3_busy_timeout(db, 10000); 63 ASSERTV(status == SQLITE_OK, "Failed to set busy timeout: %s", 64 sqlite3_errmsg); 65 66 sql = "CREATE TABLE IF NOT EXISTS users(uid INTEGER PRIMARY KEY," 67 " name TEXT, mod TEXT, exp TEXT, creat INTEGER);"; 68 status = sqlite3_exec(db, sql, 0, 0, NULL); 69 ASSERTV(status == SQLITE_OK, "Failed to create users table: %s", 70 sqlite3_errmsg(db)); 71 72 sql = "CREATE TABLE IF NOT EXISTS posts(pid INTEGER PRIMARY KEY," 73 " uid INTEGER SECONDARY KEY, text TEXT," 74 " creat INTEGER);"; 75 status = sqlite3_exec(db, sql, 0, 0, NULL); 76 ASSERTV(status == SQLITE_OK, "Failed to create posts table: %s", 77 sqlite3_errmsg(db)); 78 79 signal(SIGALRM, timeout); 80 81 atexit(cleanup); 82} 83 84int 85user_id(const char *username) 86{ 87 sqlite3_stmt *res; 88 int status, uid; 89 90 status = sqlite3_prepare_v2(db, "SELECT uid FROM users WHERE name = ?", 91 -1, &res, NULL); 92 ASSERTV(status == SQLITE_OK, "Failed to fetch users from database: %s", 93 sqlite3_errmsg(db)); 94 95 status = sqlite3_bind_text(res, 1, username, -1, NULL); 96 ASSERTV(status == SQLITE_OK, "Failed to bind param to sql query: %s", 97 sqlite3_errmsg(db)); 98 99 uid = -1; 100 if (sqlite3_step(res) == SQLITE_ROW) 101 uid = sqlite3_column_int(res, 0); 102 103 sqlite3_finalize(res); 104 105 return uid; 106} 107 108void 109user_info(const char *username, char **exp, char **mod) 110{ 111 sqlite3_stmt *res; 112 int status, uid; 113 114 status = sqlite3_prepare_v2(db, "SELECT exp, mod FROM users WHERE name = ?", 115 -1, &res, NULL); 116 ASSERTV(status == SQLITE_OK, "Failed to fetch users from database: %s", 117 sqlite3_errmsg(db)); 118 119 status = sqlite3_bind_text(res, 1, username, -1, NULL); 120 ASSERTV(status == SQLITE_OK, "Failed to bind param to sql query: %s", 121 sqlite3_errmsg(db)); 122 123 ASSERT(sqlite3_step(res) == SQLITE_ROW); 124 125 *exp = strdup((void*) sqlite3_column_text(res, 0)); 126 ASSERT(*exp != NULL); 127 128 *mod = strdup((void*) sqlite3_column_text(res, 1)); 129 ASSERT(*mod != NULL); 130 131 sqlite3_finalize(res); 132} 133 134void 135api_create_user(char *username) 136{ 137 sqlite3_stmt *res; 138 char *mod, *exp; 139 int status; 140 141 if (!username) { 142 printf("Please supply a username\n"); 143 return; 144 } 145 146 if (user_id(username) >= 0) { 147 printf("A user with that name already exists\n"); 148 return; 149 } 150 151 mod = NULL; 152 exp = strdup(ask("Enter RSA exponent: ")); 153 ASSERT(exp != NULL); 154 if (!is_numstr(exp)) { 155 printf("Invalid RSA exponent\n"); 156 goto cleanup; 157 } 158 159 mod = strdup(ask("Enter RSA modulus: ")); 160 ASSERT(mod != NULL); 161 if (!is_numstr(mod)) { 162 printf("Invalid RSA modulus\n"); 163 goto cleanup; 164 } 165 166 status = sqlite3_prepare_v2(db, 167 "INSERT INTO users (name, exp, mod, creat) VALUES (?, ?, ?, ?);", 168 -1, &res, NULL); 169 ASSERTV(status == SQLITE_OK, "Failed to fetch data from database: %s", 170 sqlite3_errmsg(db)); 171 172 status = sqlite3_bind_text(res, 1, username, -1, NULL); 173 ASSERTV(status == SQLITE_OK, "Failed to bind param to sql query: %s", 174 sqlite3_errmsg(db)); 175 176 status = sqlite3_bind_text(res, 2, exp, -1, NULL); 177 ASSERTV(status == SQLITE_OK, "Failed to bind param to sql query: %s", 178 sqlite3_errmsg(db)); 179 180 status = sqlite3_bind_text(res, 3, mod, -1, NULL); 181 ASSERTV(status == SQLITE_OK, "Failed to bind param to sql query: %s", 182 sqlite3_errmsg(db)); 183 184 status = sqlite3_bind_int(res, 4, time(NULL)); 185 ASSERTV(status == SQLITE_OK, "Failed to bind param to sql query: %s", 186 sqlite3_errmsg(db)); 187 188 ASSERTV(sqlite3_step(res) == SQLITE_DONE, "Failed to create user: %s", 189 sqlite3_errmsg(db)); 190 191 sqlite3_finalize(res); 192 193cleanup: 194 free(exp); 195 free(mod); 196} 197 198void 199api_list_users(char *args) 200{ 201 sqlite3_stmt *res; 202 int status; 203 204 status = sqlite3_prepare_v2(db, "SELECT name FROM users", -1, &res, NULL); 205 ASSERTV(status == SQLITE_OK, "Failed to fetch data from database: %s", 206 sqlite3_errmsg(db)); 207 208 while (sqlite3_step(res) == SQLITE_ROW) 209 printf("- %s\n", sqlite3_column_text(res, 0)); 210 211 sqlite3_finalize(res); 212} 213 214void 215api_user_info(char *username) 216{ 217 sqlite3_stmt *res; 218 int status, uid; 219 220 if (!username) { 221 printf("Please supply a username\n"); 222 return; 223 } 224 225 if ((uid = user_id(username)) < 0) { 226 printf("A user with that name does not exist\n"); 227 return; 228 } 229 230 status = sqlite3_prepare_v2(db, "SELECT name, exp, mod FROM users WHERE uid = ?", 231 -1, &res, NULL); 232 ASSERTV(status == SQLITE_OK, "Failed to fetch data from database: %s", 233 sqlite3_errmsg(db)); 234 235 status = sqlite3_bind_int(res, 1, uid); 236 ASSERTV(status == SQLITE_OK, "Failed to bind param to sql query: %s", 237 sqlite3_errmsg(db)); 238 239 ASSERTV(sqlite3_step(res) == SQLITE_ROW, "Failed to fetch user data: %s", 240 sqlite3_errmsg(db)); 241 242 printf("Username: %s\n", sqlite3_column_text(res, 0)); 243 printf("RSA Exponent: %s\n", sqlite3_column_text(res, 1)); 244 printf("RSA Modulus: %s\n", sqlite3_column_text(res, 2)); 245 246 sqlite3_finalize(res); 247} 248 249void 250api_login(char *username) 251{ 252 const char *sig; 253 char *chall, *exp, *mod; 254 int uid; 255 256 if (!username) { 257 printf("Please supply a username\n"); 258 return; 259 } 260 261 if ((uid = user_id(username)) < 0) { 262 printf("A user with that name does not exist\n"); 263 return; 264 } 265 266 exp = mod = NULL; 267 chall = randstr(16); 268 printf("Please verify your identity.\n"); 269 printf("Sign this message: %s\n", chall); 270 271 sig = ask("Signature: "); 272 if (!is_numstr(sig)) { 273 printf("Invalid signature format (base 10)\n"); 274 goto cleanup; 275 } 276 277 user_info(username, &exp, &mod); 278 279 if (!check_signature(chall, sig, exp, mod)) { 280 printf("Invalid signature\n"); 281 goto cleanup; 282 } 283 284 if (current_user) free(current_user); 285 current_user = strdup(username); 286 ASSERT(current_user != NULL); 287 288cleanup: 289 free(chall); 290 free(mod); 291 free(exp); 292} 293 294void 295api_create_post(char *msg) 296{ 297 sqlite3_stmt *res; 298 int uid, status; 299 300 if (!current_user) { 301 printf("Not logged in!\n"); 302 return; 303 } 304 305 ASSERT((uid = user_id(current_user)) >= 0); 306 307 if (!msg || !*msg) { 308 printf("Message can not be empty\n"); 309 return; 310 } 311 312 status = sqlite3_prepare_v2(db, 313 "INSERT INTO posts (uid, text, creat) VALUES (?, ?, ?);", 314 -1, &res, NULL); 315 ASSERTV(status == SQLITE_OK, "Failed to fetch data from database: %s", 316 sqlite3_errmsg(db)); 317 318 status = sqlite3_bind_int(res, 1, uid); 319 ASSERTV(status == SQLITE_OK, "Failed to bind param to sql query: %s", 320 sqlite3_errmsg(db)); 321 322 status = sqlite3_bind_text(res, 2, msg, -1, NULL); 323 ASSERTV(status == SQLITE_OK, "Failed to bind param to sql query: %s", 324 sqlite3_errmsg(db)); 325 326 status = sqlite3_bind_int(res, 3, time(NULL)); 327 ASSERTV(status == SQLITE_OK, "Failed to bind param to sql query: %s", 328 sqlite3_errmsg(db)); 329 330 ASSERTV(sqlite3_step(res) == SQLITE_DONE, "Failed to create post: %s", 331 sqlite3_errmsg(db)); 332 333 sqlite3_finalize(res); 334} 335 336void 337api_list_posts(char *args) 338{ 339 sqlite3_stmt *res; 340 const char post_text; 341 int uid, status; 342 343 if (!current_user) { 344 printf("Not logged in!\n"); 345 return; 346 } 347 348 ASSERT((uid = user_id(current_user)) >= 0); 349 350 status = sqlite3_prepare_v2(db, "SELECT text FROM posts WHERE uid = ?", 351 -1, &res, NULL); 352 ASSERTV(status == SQLITE_OK, "Failed to fetch posts from database: %s", 353 sqlite3_errmsg(db)); 354 355 status = sqlite3_bind_int(res, 1, uid); 356 ASSERTV(status == SQLITE_OK, "Failed to bind param to sql query: %s", 357 sqlite3_errmsg(db)); 358 359 while (sqlite3_step(res) == SQLITE_ROW) 360 printf("- %s\n", sqlite3_column_text(res, 0)); 361 362 sqlite3_finalize(res); 363} 364 365void 366api_help(char *command) 367{ 368 struct { 369 const char *cmd, *args, *desc; 370 } descs[] = { 371 { "help", "COMMAND", "Returns usage information" }, 372 { "register", "USER", "Create a new user" }, 373 { "login", "USER", "Login as a user" }, 374 { "users", "", "Lists all registered users" }, 375 { "post", "", "Create a new post as the current user" }, 376 { "posts", "", "Lists posts created by the current user" }, 377 }; 378 int i; 379 380 if (!command) { 381 printf("Supply a command to view usage info\n"); 382 return; 383 } 384 385 for (i = 0; i < ARRSIZE(descs); i++) { 386 if (!strcmp(descs[i].cmd, command)) { 387 printf("%s %s%s: %s\n", descs[i].cmd, descs[i].args, 388 *descs[i].args ? " " : "", descs[i].desc); 389 break; 390 } 391 } 392 393 if (i == ARRSIZE(descs)) 394 printf("Unknown command: %s\n", command); 395} 396 397int 398main(int argc, const char **argv) 399{ 400 struct { 401 const char *name; 402 void (*func)(char *args); 403 } cmds[] = { 404 { "register", api_create_user }, 405 { "users", api_list_users }, 406 { "info", api_user_info }, 407 { "login", api_login }, 408 { "post", api_create_post }, 409 { "posts", api_list_posts }, 410 { "help", api_help }, 411 }; 412 char *cmd, *tok, *args; 413 int exit, i; 414 415 init(argc, argv); 416 417 printf("%s", BANNER); 418 419 exit = 0; 420 while (!exit) { 421 alarm(120); 422 cmd = ask("\r$ "); 423 if (!*cmd) continue; 424 425 cmd = strdup(cmd); 426 ASSERT(cmd != NULL); 427 428 tok = strchr(cmd, ' '); 429 if (tok) *tok = '\0'; 430 args = tok ? tok + 1 : NULL; 431 if (args && !*args) args = NULL; 432 433 for (i = 0; i < ARRSIZE(cmds); i++) { 434 if (!strcmp(cmd, cmds[i].name)) { 435 cmds[i].func(args); 436 break; 437 } 438 } 439 440 if (!strcmp(cmd, "exit")) 441 break; 442 443 if (i == ARRSIZE(cmds)) 444 printf("Unknown command: %s\n", cmd); 445 446 free(cmd); 447 } 448 449 printf("bye!\n"); 450 sqlite3_close(db); 451}