changeset 38:48834441dc86

pasterd: generate smaller identifiers, closes #2480 @1h
author David Demelier <markand@malikania.fr>
date Wed, 12 Feb 2020 20:55:00 +0100
parents e05b26209ad3
children 5e39e116c38d
files database.c http.c paste.c paste.h tests/test-database.c themes/minimal/index-paste.html themes/minimal/paste.html themes/siimple/index-paste.html themes/siimple/paste.html
diffstat 9 files changed, 90 insertions(+), 56 deletions(-) [+]
line wrap: on
line diff
--- a/database.c	Wed Feb 12 20:45:00 2020 +0100
+++ b/database.c	Wed Feb 12 20:55:00 2020 +0100
@@ -116,31 +116,10 @@
 	return estrdup(s ? (const char *)(s) : "");
 }
 
-static const char *
-create_id(void)
-{
-	static char uuid[256];
-
-	/*
-	 * Not a very strong generation but does not require to link against
-	 * util-linux.
-	 *
-	 * See https://stackoverflow.com/questions/2174768/generating-random-uuids-in-linux
-	 */
-	sprintf(uuid, "%x%x-%x-%x-%x-%x%x%x",
-	    rand(), rand(),
-	    rand(),
-	    ((rand() & 0x0fff) | 0x4000),
-	    rand() % 0x3fff + 0x8000,
-	    rand(), rand(), rand());
-
-	return uuid;
-}
-
 static void
 convert(sqlite3_stmt *stmt, struct paste *paste)
 {
-	paste->uuid = dup(sqlite3_column_text(stmt, 0));
+	paste->id = dup(sqlite3_column_text(stmt, 0));
 	paste->title = dup(sqlite3_column_text(stmt, 1));
 	paste->author = dup(sqlite3_column_text(stmt, 2));
 	paste->language = dup(sqlite3_column_text(stmt, 3));
@@ -150,6 +129,57 @@
 	paste->duration = sqlite3_column_int64(stmt, 7);
 }
 
+static bool
+exists(const char *id)
+{
+	assert(id);
+
+	sqlite3_stmt *stmt = NULL;
+	bool ret = false;
+
+	if (sqlite3_prepare(db, sql_get, -1, &stmt, NULL) != SQLITE_OK) {
+		log_warn("database: error (exists): %s", sqlite3_errmsg(db));
+		return false;
+	}
+
+	ret = sqlite3_step(stmt) == SQLITE_ROW;
+	sqlite3_finalize(stmt);
+
+	return ret;
+}
+
+static const char *
+create_id(void)
+{
+	static const char table[] = "abcdefghijklmnopqrstuvwxyz1234567890";
+	static char id[12];
+
+	for (int i = 0; i < sizeof (id); ++i)
+		id[i] = table[rand() % (sizeof (table))];
+
+	return id;
+}
+
+static bool
+set_id(struct paste *paste)
+{
+	assert(paste);
+
+	paste->id = NULL;
+
+	/*
+	 * Avoid infinite loop, we only try to create a new id in 30 steps.
+	 */
+	int tries = 0;
+
+	do {
+		free(paste->id);
+		paste->id = estrdup(create_id());
+	} while (++tries < 30 && exists(paste->id));
+
+	return tries < 30;
+}
+
 bool
 database_open(const char *path)
 {
@@ -252,7 +282,8 @@
 {
 	assert(paste);
 
-	sqlite3_stmt* stmt = NULL;
+	sqlite3_stmt *stmt = NULL;
+
 	log_debug("database: creating new paste");
 
 	if (sqlite3_exec(db, "BEGIN EXCLUSIVE TRANSACTION", NULL, NULL, NULL) != SQLITE_OK) {
@@ -260,13 +291,16 @@
 		return false;
 	}
 
+	if (!set_id(paste)) {
+		log_warn("database: unable to randomize unique identifier");
+		sqlite3_exec(db, "END TRANSACTION", NULL, NULL, NULL);
+		return false;
+	}
+
 	if (sqlite3_prepare(db, sql_insert, -1, &stmt, NULL) != SQLITE_OK)
 		goto sqlite_err;
 
-	/* Create a new uuid first. */
-	paste->uuid = estrdup(create_id());
-
-	sqlite3_bind_text(stmt, 1, paste->uuid, -1, SQLITE_STATIC);
+	sqlite3_bind_text(stmt, 1, paste->id, -1, SQLITE_STATIC);
 	sqlite3_bind_text(stmt, 2, paste->title, -1, SQLITE_STATIC);
 	sqlite3_bind_text(stmt, 3, paste->author, -1, SQLITE_STATIC);
 	sqlite3_bind_text(stmt, 4, paste->language, -1, SQLITE_STATIC);
@@ -281,7 +315,7 @@
 	sqlite3_finalize(stmt);
 
 	log_info("database: new paste (%s) from %s expires in one %lld seconds",
-	    paste->uuid, paste->author, paste->duration);
+	    paste->id, paste->author, paste->duration);
 
 	return true;
 
@@ -292,8 +326,8 @@
 	if (stmt)
 		sqlite3_finalize(stmt);
 
-	free(paste->uuid);
-	paste->uuid = NULL;
+	free(paste->id);
+	paste->id = NULL;
 
 	return false;
 }
--- a/http.c	Wed Feb 12 20:45:00 2020 +0100
+++ b/http.c	Wed Feb 12 20:55:00 2020 +0100
@@ -94,7 +94,7 @@
 };
 
 static const char *tmpl_index_pastes_keywords[] = {
-	"uuid",
+	"id",
 	"name",
 	"author",
 	"language",
@@ -103,7 +103,7 @@
 };
 
 static const char *tmpl_paste_keywords[] = {
-	"uuid",
+	"id",
 	"title",
 	"author",
 	"language",
@@ -415,7 +415,7 @@
 
 	switch (index) {
 	case 0:
-		khtml_puts(&htmlreq, paste->uuid);
+		khtml_puts(&htmlreq, paste->id);
 		break;
 	case 1:
 		khtml_puts(&htmlreq, paste->title);
@@ -458,7 +458,7 @@
 
 	switch (index) {
 	case 0:
-		khtml_puts(&htmlreq, paste->uuid);
+		khtml_puts(&htmlreq, paste->id);
 		break;
 	case 1:
 		khtml_puts(&htmlreq, paste->title);
@@ -663,7 +663,7 @@
 	else {
 		/* Redirect to paste details. */
 		khttp_head(req, kresps[KRESP_STATUS], "%s", khttps[KHTTP_302]);
-		khttp_head(req, kresps[KRESP_LOCATION], "/paste/%s", paste.uuid);
+		khttp_head(req, kresps[KRESP_LOCATION], "/paste/%s", paste.id);
 		khttp_body(req);
 		khttp_free(req);
 	}
@@ -771,7 +771,7 @@
 #endif
 		khttp_head(req, kresps[KRESP_CONNECTION], "keep-alive");
 		khttp_head(req, kresps[KRESP_CONTENT_DISPOSITION],
-		    "attachment; filename=\"%s.%s\"", paste.uuid, paste.language);
+		    "attachment; filename=\"%s.%s\"", paste.id, paste.language);
 		khttp_body(req);
 		khttp_puts(req, paste.code);
 		khttp_free(req);
--- a/paste.c	Wed Feb 12 20:45:00 2020 +0100
+++ b/paste.c	Wed Feb 12 20:55:00 2020 +0100
@@ -27,10 +27,10 @@
 {
 	assert(paste);
 
+	free(paste->id);
 	free(paste->title);
 	free(paste->author);
 	free(paste->language);
 	free(paste->code);
-	free(paste->uuid);
 	memset(paste, 0, sizeof (struct paste));
 }
--- a/paste.h	Wed Feb 12 20:45:00 2020 +0100
+++ b/paste.h	Wed Feb 12 20:55:00 2020 +0100
@@ -33,7 +33,7 @@
  * Every string in the paste is assumed to be allocated on the heap.
  */
 struct paste {
-	char *uuid;
+	char *id;
 	char *title;
 	char *author;
 	char *language;
--- a/tests/test-database.c	Wed Feb 12 20:45:00 2020 +0100
+++ b/tests/test-database.c	Wed Feb 12 20:55:00 2020 +0100
@@ -80,7 +80,7 @@
 		GREATEST_FAIL();
 
 	GREATEST_ASSERT_EQ(max, 1);
-	GREATEST_ASSERT(pastes[0].uuid);
+	GREATEST_ASSERT(pastes[0].id);
 	GREATEST_ASSERT_STR_EQ(pastes[0].title, "test 1");
 	GREATEST_ASSERT_STR_EQ(pastes[0].author, "unit test");
 	GREATEST_ASSERT_STR_EQ(pastes[0].language, "cpp");
@@ -145,7 +145,7 @@
 
 	for (int i = 0; i < 3; ++i) {
 		/* Selected in most recents first. */
-		GREATEST_ASSERT(pastes[i].uuid);
+		GREATEST_ASSERT(pastes[i].id);
 		GREATEST_ASSERT_STR_EQ(pastes[i].title,
 		    bprintf("test %d", expected[i]));
 		GREATEST_ASSERT_STR_EQ(pastes[i].author,
@@ -191,7 +191,7 @@
 
 	for (int i = 0; i < 3; ++i) {
 		/* Selected in most recents first. */
-		GREATEST_ASSERT(pastes[i].uuid);
+		GREATEST_ASSERT(pastes[i].id);
 		GREATEST_ASSERT_STR_EQ(pastes[i].title,
 		    bprintf("test %d", expected[i]));
 		GREATEST_ASSERT_STR_EQ(pastes[i].author,
@@ -232,10 +232,10 @@
 
 	if (!database_insert(&original))
 		GREATEST_FAIL();
-	if (!database_get(&new, original.uuid))
+	if (!database_get(&new, original.id))
 		GREATEST_FAIL();
 
-	GREATEST_ASSERT_STR_EQ(new.uuid, original.uuid);
+	GREATEST_ASSERT_STR_EQ(new.id, original.id);
 	GREATEST_ASSERT_STR_EQ(new.title, original.title);
 	GREATEST_ASSERT_STR_EQ(new.author, original.author);
 	GREATEST_ASSERT_STR_EQ(new.language, original.language);
@@ -252,7 +252,7 @@
 	if (!database_get(&new, "unknown"))
 		GREATEST_FAIL();
 
-	GREATEST_ASSERT(!new.uuid);
+	GREATEST_ASSERT(!new.id);
 	GREATEST_ASSERT(!new.title);
 	GREATEST_ASSERT(!new.author);
 	GREATEST_ASSERT(!new.language);
@@ -315,7 +315,7 @@
 		GREATEST_FAIL();
 
 	GREATEST_ASSERT_EQ(max, 1);
-	GREATEST_ASSERT(searched[0].uuid);
+	GREATEST_ASSERT(searched[0].id);
 	GREATEST_ASSERT_STR_EQ(searched[0].title, "This is in C");
 	GREATEST_ASSERT_STR_EQ(searched[0].author, "markand");
 	GREATEST_ASSERT_STR_EQ(searched[0].language, "cpp");
@@ -353,7 +353,7 @@
 		GREATEST_FAIL();
 
 	GREATEST_ASSERT_EQ(max, 0);
-	GREATEST_ASSERT(!searched.uuid);
+	GREATEST_ASSERT(!searched.id);
 	GREATEST_ASSERT(!searched.title);
 	GREATEST_ASSERT(!searched.author);
 	GREATEST_ASSERT(!searched.language);
@@ -389,7 +389,7 @@
 		GREATEST_FAIL();
 
 	GREATEST_ASSERT_EQ(max, 0);
-	GREATEST_ASSERT(!searched.uuid);
+	GREATEST_ASSERT(!searched.id);
 	GREATEST_ASSERT(!searched.title);
 	GREATEST_ASSERT(!searched.author);
 	GREATEST_ASSERT(!searched.language);
@@ -460,7 +460,7 @@
 		GREATEST_FAIL();
 
 	GREATEST_ASSERT_EQ(max, 1);
-	GREATEST_ASSERT(searched.uuid);
+	GREATEST_ASSERT(searched.id);
 	GREATEST_ASSERT_STR_EQ(searched.title, "This is in python");
 	GREATEST_ASSERT_STR_EQ(searched.author, "NiReaS");
 	GREATEST_ASSERT_STR_EQ(searched.language, "python");
--- a/themes/minimal/index-paste.html	Wed Feb 12 20:45:00 2020 +0100
+++ b/themes/minimal/index-paste.html	Wed Feb 12 20:55:00 2020 +0100
@@ -1,5 +1,5 @@
 <tr>
-	<td><a href="/paste/@@uuid@@">@@name@@</a></td>
+	<td><a href="/paste/@@id@@">@@name@@</a></td>
 	<td>@@author@@</td>
 	<td>@@language@@</td>
 	<td>@@date@@</td>
--- a/themes/minimal/paste.html	Wed Feb 12 20:45:00 2020 +0100
+++ b/themes/minimal/paste.html	Wed Feb 12 20:55:00 2020 +0100
@@ -1,14 +1,14 @@
 	<h2>Actions</h2>
 	<ul>
-		<li><a href="/fork/@@uuid@@">Fork</a></li>
-		<li><a href="/download/@@uuid@@">Download</a></li>
+		<li><a href="/fork/@@id@@">Fork</a></li>
+		<li><a href="/download/@@id@@">Download</a></li>
 	</ul>
 
 	<table>
 		<tbody>
 			<tr>
 				<td>Identifier</td>
-				<td>@@uuid@@</td>
+				<td>@@id@@</td>
 			</tr>
 			<tr>
 				<td>Title</td>
--- a/themes/siimple/index-paste.html	Wed Feb 12 20:45:00 2020 +0100
+++ b/themes/siimple/index-paste.html	Wed Feb 12 20:55:00 2020 +0100
@@ -1,5 +1,5 @@
 <div class="siimple-table-row">
-	<div class="siimple-table-cell"><a class="siimple-link" href="/paste/@@uuid@@">@@name@@</a></div>
+	<div class="siimple-table-cell"><a class="siimple-link" href="/paste/@@id@@">@@name@@</a></div>
 	<div class="siimple-table-cell">@@author@@</div>
 	<div class="siimple-table-cell">@@language@@</div>
 	<div class="siimple-table-cell">@@date@@</div>
--- a/themes/siimple/paste.html	Wed Feb 12 20:45:00 2020 +0100
+++ b/themes/siimple/paste.html	Wed Feb 12 20:55:00 2020 +0100
@@ -1,12 +1,12 @@
 	<h1>@@title@@</h1>
 
-	<a class="siimple-btn siimple-btn--navy" href="/fork/@@uuid@@">Fork</a>
-	<a class="siimple-btn siimple-btn--navy" href="/download/@@uuid@@">Download</a>
+	<a class="siimple-btn siimple-btn--navy" href="/fork/@@id@@">Fork</a>
+	<a class="siimple-btn siimple-btn--navy" href="/download/@@id@@">Download</a>
 
 	<div class="siimple-grid">
 		<div class="siimple-grid-row">
 			<div class="siimple-grid-col siimple-grid-col--2"><strong>Identifier</strong></div>
-			<div class="siimple-grid-col siimple-grid-col--10">@@uuid@@</div>
+			<div class="siimple-grid-col siimple-grid-col--10">@@id@@</div>
 		</div>
 		<div class="siimple-grid-row">
 			<div class="siimple-grid-col siimple-grid-col--2"><strong>Author</strong></div>