forked from cory/tildefriends
		
	I guess we support sub-millisecond timestamps. Who knew?
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@3835 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
		
							
								
								
									
										106
									
								
								src/ssb.db.c
									
									
									
									
									
								
							
							
						
						
									
										106
									
								
								src/ssb.db.c
									
									
									
									
									
								
							| @@ -9,49 +9,56 @@ | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
|  | ||||
| static void _tf_ssb_db_exec(sqlite3* db, const char* statement) | ||||
| { | ||||
| 	char* error = NULL; | ||||
| 	int result = sqlite3_exec(db, statement, NULL, NULL, &error); | ||||
| 	if (result != SQLITE_OK) | ||||
| 	{ | ||||
| 		printf("Error running '%s': %s.\n", statement, error); | ||||
| 		abort(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void tf_ssb_db_init(tf_ssb_t* ssb) | ||||
| { | ||||
| 	sqlite3* db = tf_ssb_get_db(ssb); | ||||
| 	sqlite3_exec(db, "PRAGMA journal_mode = WAL", NULL, NULL, NULL); | ||||
| 	sqlite3_exec(db, "PRAGMA synchronous = NORMAL", NULL, NULL, NULL); | ||||
| 	sqlite3_exec(db, | ||||
| 	_tf_ssb_db_exec(db, "PRAGMA journal_mode = WAL"); | ||||
| 	_tf_ssb_db_exec(db, "PRAGMA synchronous = NORMAL"); | ||||
| 	_tf_ssb_db_exec(db, | ||||
| 		"CREATE TABLE IF NOT EXISTS messages (" | ||||
| 		"  author TEXT," | ||||
| 		"  id TEXT PRIMARY KEY," | ||||
| 		"  sequence INTEGER," | ||||
| 		"  timestamp INTEGER," | ||||
| 		"  timestamp REAL," | ||||
| 		"  previous TEXT," | ||||
| 		"  hash TEXT," | ||||
| 		"  content TEXT," | ||||
| 		"  signature TEXT," | ||||
| 		"  sequence_before_author INTEGER," | ||||
| 		"  UNIQUE(author, sequence)" | ||||
| 		")", | ||||
| 		NULL, NULL, NULL); | ||||
| 	sqlite3_exec(db, "ALTER TABLE messages ADD COLUMN sequence_before_author INTEGER", NULL, NULL, NULL); | ||||
| 	sqlite3_exec(db, "CREATE INDEX IF NOT EXISTS messages_author_id_index ON messages (author, id)", NULL, NULL, NULL); | ||||
| 	sqlite3_exec(db, "CREATE INDEX IF NOT EXISTS messages_author_sequence_index ON messages (author, sequence)", NULL, NULL, NULL); | ||||
| 	sqlite3_exec(db, "CREATE INDEX IF NOT EXISTS messages_author_timestamp_index ON messages (author, timestamp)", NULL, NULL, NULL); | ||||
| 	sqlite3_exec(db, | ||||
| 		")"); | ||||
| 	_tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS messages_author_id_index ON messages (author, id)"); | ||||
| 	_tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS messages_author_sequence_index ON messages (author, sequence)"); | ||||
| 	_tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS messages_author_timestamp_index ON messages (author, timestamp)"); | ||||
| 	_tf_ssb_db_exec(db, | ||||
| 		"CREATE TABLE IF NOT EXISTS blobs (" | ||||
| 		"  id TEXT PRIMARY KEY," | ||||
| 		"  content BLOB," | ||||
| 		"  created INTEGER" | ||||
| 		")", | ||||
| 		NULL, NULL, NULL); | ||||
| 	sqlite3_exec(db, | ||||
| 		")"); | ||||
| 	_tf_ssb_db_exec(db, | ||||
| 		"CREATE TABLE IF NOT EXISTS blob_wants (" | ||||
| 		"  id TEXT PRIMARY KEY" | ||||
| 		")", | ||||
| 		NULL, NULL, NULL); | ||||
| 	sqlite3_exec(db, | ||||
| 		")"); | ||||
| 	_tf_ssb_db_exec(db, | ||||
| 		"CREATE TABLE IF NOT EXISTS properties (" | ||||
| 		"  id TEXT," | ||||
| 		"  key TEXT," | ||||
| 		"  value TEXT," | ||||
| 		"  UNIQUE(id, key)" | ||||
| 		")", | ||||
| 		NULL, NULL, NULL); | ||||
| 	sqlite3_exec(db, | ||||
| 		")"); | ||||
| 	_tf_ssb_db_exec(db, | ||||
| 		"CREATE TABLE IF NOT EXISTS connections (" | ||||
| 		"  host TEXT," | ||||
| 		"  port INTEGER," | ||||
| @@ -59,8 +66,41 @@ void tf_ssb_db_init(tf_ssb_t* ssb) | ||||
| 		"  last_attempt INTEGER," | ||||
| 		"  last_success INTEGER," | ||||
| 		"  UNIQUE(host, port, key)" | ||||
| 		")", | ||||
| 		NULL, NULL, NULL); | ||||
| 		")"); | ||||
|  | ||||
| 	bool need_add_sequence_before_author = true; | ||||
| 	bool need_convert_timestamp_to_real = false; | ||||
|  | ||||
| 	sqlite3_stmt* statement = NULL; | ||||
| 	if (sqlite3_prepare(db, "PRAGMA table_info(messages)", -1, &statement, NULL) == SQLITE_OK) | ||||
| 	{ | ||||
| 		while (sqlite3_step(statement) == SQLITE_ROW) | ||||
| 		{ | ||||
| 			const char* name = (const char*)sqlite3_column_text(statement, 1); | ||||
| 			const char* type = (const char*)sqlite3_column_text(statement, 1); | ||||
| 			if (name && type && strcmp(name, "timestamp") == 0 && strcmp(type, "INTEGER") == 0) | ||||
| 			{ | ||||
| 				need_convert_timestamp_to_real = true; | ||||
| 			} | ||||
| 			if (name && strcmp(name, "sequence_before_author") == 0) | ||||
| 			{ | ||||
| 				need_add_sequence_before_author = false; | ||||
| 			} | ||||
| 		} | ||||
| 		sqlite3_finalize(statement); | ||||
| 	} | ||||
|  | ||||
| 	if (need_convert_timestamp_to_real) | ||||
| 	{ | ||||
| 		_tf_ssb_db_exec(db, "ALTER TABLE messages ADD COLUMN timestamp_real REAL"); | ||||
| 		_tf_ssb_db_exec(db, "UPDATE messages SET timestamp_real = timestamp"); | ||||
| 		_tf_ssb_db_exec(db, "ALTER TABLE messages DROP COLUMN timestamp REAL"); | ||||
| 		_tf_ssb_db_exec(db, "ALTER TABLE messages RENAME COLUMN timestamp_real TO timestamp"); | ||||
| 	} | ||||
| 	if (need_add_sequence_before_author) | ||||
| 	{ | ||||
| 		_tf_ssb_db_exec(db, "ALTER TABLE messages ADD COLUMN sequence_before_author INTEGER"); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static bool _tf_ssb_db_previous_message_exists(sqlite3* db, const char* author, int64_t sequence, const char* previous) | ||||
| @@ -97,8 +137,8 @@ bool tf_ssb_db_store_message(tf_ssb_t* ssb, JSContext* context, const char* id, | ||||
| 	const char* author = JS_ToCString(context, authorval); | ||||
| 	int64_t sequence = -1; | ||||
| 	JS_ToInt64(context, &sequence, JS_GetPropertyStr(context, val, "sequence")); | ||||
| 	int64_t timestamp = -1; | ||||
| 	JS_ToInt64(context, ×tamp, JS_GetPropertyStr(context, val, "timestamp")); | ||||
| 	double timestamp = -1.0; | ||||
| 	JS_ToFloat64(context, ×tamp, JS_GetPropertyStr(context, val, "timestamp")); | ||||
|  | ||||
| 	JSValue contentval = JS_GetPropertyStr(context, val, "content"); | ||||
| 	JSValue content = JS_JSONStringify(context, contentval, JS_NULL, JS_NULL); | ||||
| @@ -119,7 +159,7 @@ bool tf_ssb_db_store_message(tf_ssb_t* ssb, JSContext* context, const char* id, | ||||
| 				(previous ? sqlite3_bind_text(statement, 2, previous, -1, NULL) : sqlite3_bind_null(statement, 2)) == SQLITE_OK && | ||||
| 				sqlite3_bind_text(statement, 3, author, -1, NULL) == SQLITE_OK && | ||||
| 				sqlite3_bind_int64(statement, 4, sequence) == SQLITE_OK && | ||||
| 				sqlite3_bind_int64(statement, 5, timestamp) == SQLITE_OK && | ||||
| 				sqlite3_bind_double(statement, 5, timestamp) == SQLITE_OK && | ||||
| 				sqlite3_bind_text(statement, 6, contentstr, content_len, NULL) == SQLITE_OK && | ||||
| 				sqlite3_bind_text(statement, 7, "sha256", 6, NULL) == SQLITE_OK && | ||||
| 				sqlite3_bind_text(statement, 8, signature, -1, NULL) == SQLITE_OK && | ||||
| @@ -304,7 +344,7 @@ bool tf_ssb_db_blob_store(tf_ssb_t* ssb, const uint8_t* blob, size_t size, char* | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* author, int64_t sequence, char* out_message_id, size_t out_message_id_size, int64_t* out_timestamp, char** out_content) | ||||
| bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* author, int64_t sequence, char* out_message_id, size_t out_message_id_size, double* out_timestamp, char** out_content) | ||||
| { | ||||
| 	bool found = false; | ||||
| 	sqlite3_stmt* statement; | ||||
| @@ -321,7 +361,7 @@ bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* aut | ||||
| 			} | ||||
| 			if (out_timestamp) | ||||
| 			{ | ||||
| 				*out_timestamp = sqlite3_column_int64(statement, 1); | ||||
| 				*out_timestamp = sqlite3_column_double(statement, 1); | ||||
| 			} | ||||
| 			if (out_content) | ||||
| 			{ | ||||
| @@ -508,7 +548,7 @@ void tf_ssb_db_visit_query(tf_ssb_t* ssb, const char* query, const JSValue binds | ||||
| 	sqlite3_set_authorizer(db, NULL, NULL); | ||||
| } | ||||
|  | ||||
| static JSValue _tf_ssb_format_message(JSContext* context, const char* previous, const char* author, int64_t sequence, int64_t timestamp, const char* hash, const char* content, const char* signature, bool sequence_before_author) | ||||
| static JSValue _tf_ssb_format_message(JSContext* context, const char* previous, const char* author, int64_t sequence, double timestamp, const char* hash, const char* content, const char* signature, bool sequence_before_author) | ||||
| { | ||||
| 	JSValue value = JS_NewObject(context); | ||||
| 	JS_SetPropertyStr(context, value, "previous", previous ? JS_NewString(context, previous) : JS_NULL); | ||||
| @@ -522,7 +562,7 @@ static JSValue _tf_ssb_format_message(JSContext* context, const char* previous, | ||||
| 		JS_SetPropertyStr(context, value, "author", JS_NewString(context, author)); | ||||
| 		JS_SetPropertyStr(context, value, "sequence", JS_NewInt64(context, sequence)); | ||||
| 	} | ||||
| 	JS_SetPropertyStr(context, value, "timestamp", JS_NewInt64(context, timestamp)); | ||||
| 	JS_SetPropertyStr(context, value, "timestamp", JS_NewFloat64(context, timestamp)); | ||||
| 	JS_SetPropertyStr(context, value, "hash", JS_NewString(context, hash)); | ||||
| 	JS_SetPropertyStr(context, value, "content", JS_ParseJSON(context, content, strlen(content), NULL)); | ||||
| 	JS_SetPropertyStr(context, value, "signature", JS_NewString(context, signature)); | ||||
| @@ -567,7 +607,7 @@ bool tf_ssb_db_check(sqlite3* db, const char* check_author) | ||||
| 			const char* previous = (const char*)sqlite3_column_text(statement, 1); | ||||
| 			const char* author = (const char*)sqlite3_column_text(statement, 2); | ||||
| 			int64_t sequence = sqlite3_column_int64(statement, 3); | ||||
| 			int64_t timestamp = sqlite3_column_int64(statement, 4); | ||||
| 			double timestamp = sqlite3_column_double(statement, 4); | ||||
| 			const char* hash = (const char*)sqlite3_column_text(statement, 5); | ||||
| 			const char* content = (const char*)sqlite3_column_text(statement, 6); | ||||
| 			const char* signature = (const char*)sqlite3_column_text(statement, 7); | ||||
| @@ -580,10 +620,10 @@ bool tf_ssb_db_check(sqlite3* db, const char* check_author) | ||||
| 			const char* jv = JS_ToCString(context, j); | ||||
| 			if (tf_ssb_verify_and_strip_signature(context, message, actual_id, sizeof(actual_id), out_signature, sizeof(out_signature), &actual_sequence_before_author)) | ||||
| 			{ | ||||
| 				/*if (previous && strcmp(previous, previous_id)) | ||||
| 				if (previous && strcmp(previous, previous_id)) | ||||
| 				{ | ||||
| 					printf("%s:%d previous was %s should be %s\n", id, (int)sequence, previous_id, previous); | ||||
| 				}*/ | ||||
| 				} | ||||
| 				if (strcmp(id, actual_id)) | ||||
| 				{ | ||||
| 					if (_tf_ssb_update_message_id(db, id, actual_id)) | ||||
| @@ -598,7 +638,7 @@ bool tf_ssb_db_check(sqlite3* db, const char* check_author) | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				printf("unable to verify signature for %s\n", id); | ||||
| 				printf("%s sequence=%" PRId64 " unable to verify signature for %s sequence_before_author=%d message=[%.*s]\n", author, sequence, id, sequence_before_author, (int)strlen(jv), jv); | ||||
| 			} | ||||
| 			JS_FreeCString(context, jv); | ||||
| 			JS_FreeValue(context, j); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user