diff --git a/inc/common/common.h b/inc/common/common.h index 501996fc78aba176e750d2d641b9033bde8bcfdb..074fd87b94972d8a69b397bbd103ee3202cf7da9 100644 --- a/inc/common/common.h +++ b/inc/common/common.h @@ -36,3 +36,5 @@ char *trim(char *str, size_t len, char c); int get_stdin_line(const char *prompt, char *buf, size_t len); int read_self_exe(char *path, size_t len); + +int safe_snprintf(char *dest, const size_t max_len, const char *format, ...); diff --git a/src/commands.c b/src/commands.c index 1b18a65a7821a3fc16968bbb7a8eca8ef89786ba..d8d890ab6b50dca888803a9a1bc19f670635a8ff 100644 --- a/src/commands.c +++ b/src/commands.c @@ -84,7 +84,7 @@ command_currentsong(struct lkt_state *srv, size_t c) LOG_ERROR_SCT("COMMAND", "%s", "Failed to get information about the current kara"); out = lkt_message_new(); - idx = snprintf(out->data, LKT_MESSAGE_MAX, + idx = safe_snprintf(out->data, LKT_MESSAGE_MAX, "Title: %s\n" "Author: %s\n" "Source: %s\n" @@ -119,7 +119,7 @@ command_status(struct lkt_state *srv, size_t c) play_state = queue_state.current <= 0 ? "stop" : (queue_state.paused ? "pause" : "play"); - out->data_len = snprintf(out->data, LKT_MESSAGE_MAX, + out->data_len = safe_snprintf(out->data, LKT_MESSAGE_MAX, "volume: %d\n" "repeat: %d\n" "random: %d\n" @@ -312,10 +312,8 @@ bool command_help(struct lkt_state *srv, size_t c) { struct lkt_message *out; - int idx; out = lkt_message_new(); - idx = snprintf(out->data, LKT_MESSAGE_MAX, "HELP\n"); - out->data_len = idx; + out->data_len = safe_snprintf(out->data, LKT_MESSAGE_MAX, "HELP\n"); lkt_state_send(srv, c, out); return true; } @@ -380,7 +378,7 @@ lkt_callback_send_row_v1(void *_args, int pos_len, int pos, int id, int id_len, struct lkt_callback *args = (struct lkt_callback *) _args; struct lkt_message *out; out = lkt_message_new(); - out->data_len = snprintf(out->data, LKT_MESSAGE_MAX, "%*d: %*d %s\n", pos_len, pos, id_len, id, sql_row); + out->data_len = safe_snprintf(out->data, LKT_MESSAGE_MAX, "%*d: %*d %s\n", pos_len, pos, id_len, id, sql_row); lkt_state_send(args->srv, args->c, out); return true; } @@ -389,7 +387,7 @@ bool lkt_callback_send_row_v2(struct lkt_state *srv, size_t c, int id, int id_len, const char *sql_row) { struct lkt_message *out = lkt_message_new(); - out->data_len = snprintf(out->data, LKT_MESSAGE_MAX, "%*d %s\n", id_len, id, sql_row); + out->data_len = safe_snprintf(out->data, LKT_MESSAGE_MAX, "%*d %s\n", id_len, id, sql_row); lkt_state_send(srv, c, out); return true; } @@ -723,7 +721,7 @@ static bool sticker_send(struct lkt_state *srv, size_t c, char *name, int id, int value) { struct lkt_message *msg = lkt_message_new(); - msg->data_len = snprintf(msg->data, LKT_MESSAGE_ARGS_MAX, "%d: %s . %d", id, name, value); + msg->data_len = safe_snprintf(msg->data, LKT_MESSAGE_ARGS_MAX, "%d: %s . %d", id, name, value); lkt_state_send(srv, c, msg); return true; } diff --git a/src/common.c b/src/common.c index f301ac07491d5fc6ca53711aae491f1987c91276..636bf3d5459a7f8a8810b99c27418ef7994f60c7 100644 --- a/src/common.c +++ b/src/common.c @@ -5,6 +5,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <stdarg.h> #include <sys/stat.h> void @@ -157,3 +158,14 @@ read_self_exe(char *path, size_t len) (readlink(SELF_EXECUTABLE_FREEBSD, path, len - 1) > 0) || (readlink(SELF_EXECUTABLE_SOLARIS, path, len - 1) > 0); } + +int +safe_snprintf(char *dest, const size_t max_len, const char *format, ...) +{ + va_list ap; + va_start(ap, format); + int ret = vsnprintf(dest, max_len, format, ap); + dest[max_len - 1] = '\0'; + va_end(ap); + return ret; +} diff --git a/src/database/config.c b/src/database/config.c index 00c82c62750cafcf78cfb4fa33f35e0c4738abe9..eb09db8541c459fc983e2f50da364853b18380fd 100644 --- a/src/database/config.c +++ b/src/database/config.c @@ -119,8 +119,7 @@ database_config_queue(volatile sqlite3 *db, const char *option, int value) if (value > 100) value = 100; - snprintf(SQL_STMT, LKT_MAX_SQLITE_STATEMENT - 1, SQL_STMT_TMP, option); - SQL_STMT[LKT_MAX_SQLITE_STATEMENT - 1] = 0; + safe_snprintf(SQL_STMT, LKT_MAX_SQLITE_STATEMENT, SQL_STMT_TMP, option); SQLITE_PREPARE(db, stmt, SQL_STMT, error); SQLITE_BIND_INT(db, stmt, 1, value, error); @@ -162,8 +161,7 @@ database_get_config(volatile sqlite3 *db, const char *option, int *value) sqlite3_stmt *stmt = 0; bool ret = false; - snprintf(SQL_STMT, LKT_MAX_SQLITE_STATEMENT - 1, SQL_STMT_TMP, option); - SQL_STMT[LKT_MAX_SQLITE_STATEMENT - 1] = 0; + safe_snprintf(SQL_STMT, LKT_MAX_SQLITE_STATEMENT, SQL_STMT_TMP, option); SQLITE_PREPARE(db, stmt, SQL_STMT, error); SQLITE_STEP_ROW(db, stmt, error); *value = sqlite3_column_int(stmt, 0); diff --git a/src/database/find.c b/src/database/find.c index c31a1ab690e07d45ff1e46760336fc03f01b53ae..8cab8488045c254566baf24ab05c845ff6fff6b3 100644 --- a/src/database/find.c +++ b/src/database/find.c @@ -22,9 +22,7 @@ database_search_database_init(volatile sqlite3 *db, struct lkt_search *ret) ret->type = lkt_search_database; /* Search part */ - snprintf(SQL_STMT, LKT_MAX_SQLITE_STATEMENT - 1, SQL_STMT_TEMPLATE, ret->name, - ret->msg_count, ret->continuation); - SQL_STMT[LKT_MAX_SQLITE_STATEMENT - 1] = 0; + safe_snprintf(SQL_STMT, LKT_MAX_SQLITE_STATEMENT, SQL_STMT_TEMPLATE, ret->name, ret->msg_count, ret->continuation); SQLITE_PREPARE(db, ret->stmt, SQL_STMT, error); SQLITE_BIND_TEXT(db, ret->stmt, 1, ret->ka_rgx, error); ret->db = db; @@ -89,9 +87,7 @@ database_search_queue_init(volatile sqlite3 *db, struct lkt_search *ret) ret->type = lkt_search_queue; /* Search part */ - snprintf(SQL_STMT, LKT_MAX_SQLITE_STATEMENT - 1, SQL_STMT_TEMPLATE, ret->name, - ret->msg_count, ret->continuation); - SQL_STMT[LKT_MAX_SQLITE_STATEMENT - 1] = 0; + safe_snprintf(SQL_STMT, LKT_MAX_SQLITE_STATEMENT, SQL_STMT_TEMPLATE, ret->name, ret->msg_count, ret->continuation); SQLITE_PREPARE(db, ret->stmt, SQL_STMT, error); SQLITE_BIND_TEXT(db, ret->stmt, 1, ret->ka_rgx, error); ret->db = db; diff --git a/src/database/open.c b/src/database/open.c index 24b9fec3fcb855b49a9700cf81c4a1c71533bebc..9c28886822cbb722d6601585f31646453b383418 100644 --- a/src/database/open.c +++ b/src/database/open.c @@ -78,8 +78,7 @@ __attach(volatile sqlite3 *db, const char *name, const char *path) char SQL_ATTACH[LKT_MAX_SQLITE_STATEMENT]; bool ret = false; - snprintf(SQL_ATTACH, LKT_MAX_SQLITE_STATEMENT - 1, SQL_ATTACH_TEMPLATE, path, name); - SQL_ATTACH[LKT_MAX_SQLITE_STATEMENT - 1] = 0; + safe_snprintf(SQL_ATTACH, LKT_MAX_SQLITE_STATEMENT, SQL_ATTACH_TEMPLATE, path, name); SQLITE_EXEC(db, SQL_ATTACH, err_no_attach); LOG_INFO_SCT("DB", "Attached database '%s' with path '%s'", name, path); ret = true; @@ -94,8 +93,7 @@ __detach(volatile sqlite3 *db, const char *name) char SQL_DETACH[LKT_MAX_SQLITE_STATEMENT]; bool ret = false; - snprintf(SQL_DETACH, LKT_MAX_SQLITE_STATEMENT - 1, SQL_DETACH_TEMPLATE, name); - SQL_DETACH[LKT_MAX_SQLITE_STATEMENT - 1] = 0; + safe_snprintf(SQL_DETACH, LKT_MAX_SQLITE_STATEMENT, SQL_DETACH_TEMPLATE, name); SQLITE_EXEC(db, SQL_DETACH, err_no_detach); LOG_INFO_SCT("DB", "Detached database '%s'", name); ret = true; diff --git a/src/database/playlist.c b/src/database/playlist.c index 96f704f075d18d16f217122741422ef532095640..a805e47b214f86c4d35dbb9eadbd3430f6c3ac36 100644 --- a/src/database/playlist.c +++ b/src/database/playlist.c @@ -108,8 +108,7 @@ database_plt_export(volatile sqlite3 *db, const char *name) GOTO_UNLESS(strcasecmp(name, PROTECTED_DATABASE), "Name '"PROTECTED_DATABASE "' is protected, you can't use it for a playlist name", error); - snprintf(SQL_STMT, LKT_MAX_SQLITE_STATEMENT - 1, SQL_SCHEM, name); - SQL_STMT[LKT_MAX_SQLITE_STATEMENT - 1] = 0; + safe_snprintf(SQL_STMT, LKT_MAX_SQLITE_STATEMENT, SQL_SCHEM, name); SQLITE_PREPARE(db, stmt, SQL_STMT, error); code = sqlite3_step(stmt); @@ -141,8 +140,7 @@ database_plt_import(volatile sqlite3 *db, const char *name) GOTO_UNLESS(strcasecmp(name, PROTECTED_DATABASE), "Name '"PROTECTED_DATABASE "' is protected, you can't use it for a playlist name", error); - snprintf(SQL_STMT, LKT_MAX_SQLITE_STATEMENT - 1, SQL_SCHEM, name, name, name); - SQL_STMT[LKT_MAX_SQLITE_STATEMENT - 1] = 0; + safe_snprintf(SQL_STMT, LKT_MAX_SQLITE_STATEMENT, SQL_SCHEM, name, name, name); SQLITE_EXEC(db, SQL_STMT, error); ret = true; error: @@ -184,8 +182,7 @@ database_plt_add_uri(volatile sqlite3 *db, const char *name, struct lkt_uri *uri return false; } - snprintf(SQL_STMT, LKT_MAX_SQLITE_STATEMENT - 1, SQL, column); - SQL_STMT[LKT_MAX_SQLITE_STATEMENT - 1] = 0; + safe_snprintf(SQL_STMT, LKT_MAX_SQLITE_STATEMENT, SQL, column); SQLITE_PREPARE(db, stmt, SQL_STMT, error); SQLITE_BIND_TEXT(db, stmt, 1, name, error); SQLITE_BIND_TEXT(db, stmt, 2, (char *) uri->value, error); diff --git a/src/database/queue.c b/src/database/queue.c index c0cf57fd46b4ddcfba1d19c654cca7922db1ce94..9de497226807e10f87db199bcc6fb9a18b4bcd6b 100644 --- a/src/database/queue.c +++ b/src/database/queue.c @@ -126,8 +126,7 @@ queue_add_with_col_like_str(volatile sqlite3 *db, const char *col, const char *v SQLITE_EXEC(db, "BEGIN TRANSACTION;", error); /* Insert at the end of the queue */ - snprintf(SQL, LKT_MAX_SQLITE_STATEMENT - 1, SQL_STMT, col); - SQL[LKT_MAX_SQLITE_STATEMENT - 1] = 0; + safe_snprintf(SQL, LKT_MAX_SQLITE_STATEMENT, SQL_STMT, col); SQLITE_PREPARE(db, stmt, SQL, error); SQLITE_BIND_INT(db, stmt, 1, priority, error); SQLITE_BIND_TEXT(db, stmt, 2, val, error); @@ -262,8 +261,7 @@ database_queue_del_id(volatile sqlite3 *db, int id) "COMMIT TRANSACTION;"; #undef POS_OF_ID char SQL[LKT_MAX_SQLITE_STATEMENT]; - snprintf(SQL, LKT_MAX_SQLITE_STATEMENT - 1, SQL_TEMPLATE, id, id, id, id); - SQL[LKT_MAX_SQLITE_STATEMENT - 1] = '\0'; + safe_snprintf(SQL, LKT_MAX_SQLITE_STATEMENT, SQL_TEMPLATE, id, id, id, id); SQLITE_EXEC(db, SQL, error); return true; error: @@ -295,8 +293,7 @@ database_queue_next(volatile sqlite3 *db, char filepath[PATH_MAX]) if (code == SQLITE_ROW) { id = MAX(1, sqlite3_column_int(stmt, 1)); - snprintf(SQL_UPDATE, LKT_MAX_SQLITE_STATEMENT - 1, "UPDATE queue_state SET current = %d;", id); - SQL_UPDATE[LKT_MAX_SQLITE_STATEMENT - 1] = 0; + safe_snprintf(SQL_UPDATE, LKT_MAX_SQLITE_STATEMENT, "UPDATE queue_state SET current = %d;", id); if (filepath != NULL) strncpy(filepath, (const char *) sqlite3_column_text(stmt, 0), PATH_MAX); @@ -355,8 +352,7 @@ database_queue_prev(volatile sqlite3 *db, char filepath[PATH_MAX]) if (code == SQLITE_ROW) { id = MAX(1, sqlite3_column_int(stmt, 1)); - snprintf(SQL_UPDATE, LKT_MAX_SQLITE_STATEMENT - 1, "UPDATE queue_state SET current = %d;", id); - SQL_UPDATE[LKT_MAX_SQLITE_STATEMENT - 1] = 0; + safe_snprintf(SQL_UPDATE, LKT_MAX_SQLITE_STATEMENT, "UPDATE queue_state SET current = %d;", id); if (filepath != NULL) strncpy(filepath, (const char *) sqlite3_column_text(stmt, 0), PATH_MAX); @@ -490,9 +486,8 @@ database_queue_set_current_index(volatile sqlite3 *db, int idx) return false; } - RETURN_IF(snprintf(SQL_GET, LKT_MAX_SQLITE_STATEMENT - 1, SQL_GET_TEMPLATE, idx) < 0, "Snprintf failed", - false); - SQL_GET[LKT_MAX_SQLITE_STATEMENT - 1] = 0; + RETURN_IF(safe_snprintf(SQL_GET, LKT_MAX_SQLITE_STATEMENT, SQL_GET_TEMPLATE, idx) < 0, + "Snprintf failed", false); SQLITE_EXEC(db, SQL_GET, error); return true; error: diff --git a/src/database/stickers.c b/src/database/stickers.c index bc49c60339d52926e49cf96e6dd7197fdf2b8f20..a0a7c7f001c9961549b5f016f6e859501e04bbe0 100644 --- a/src/database/stickers.c +++ b/src/database/stickers.c @@ -99,9 +99,8 @@ database_sticker_delete_specify(volatile sqlite3 *db, const char *type, int uri, return false; } - snprintf(SQL, LKT_MAX_SQLITE_STATEMENT - 1, "DELETE FROM 'stickers.%s' " + safe_snprintf(SQL, LKT_MAX_SQLITE_STATEMENT, "DELETE FROM 'stickers.%s' " "WHERE 'stickers.%s' = ? ", type, type); - SQL[LKT_MAX_SQLITE_STATEMENT - 1] = 0; /* If there is a name specified. */ if (!name) { diff --git a/src/database/update.c b/src/database/update.c index 9f5e840e4c33b143261efb0a9850b0d61f82887d..145a60199a5eccdded3248c6c834ae3e27ab8145 100644 --- a/src/database/update.c +++ b/src/database/update.c @@ -27,7 +27,7 @@ database_add_kara(volatile sqlite3 *db, const char *filename) time_t the_time = time(NULL); struct tm *the_local_time = localtime(&the_time); char year[10]; - code = snprintf(year, 10, "%d", the_local_time->tm_year + 1900); + code = safe_snprintf(year, 10, "%d", the_local_time->tm_year + 1900); RETURN_IF(code < 0 || code >= 10, "Failed to get the year of the current date", false); if (kara_metadata_read(&data, filename) != 0) { @@ -89,7 +89,7 @@ database_update_add(volatile sqlite3 *db, const char *kara_path, struct kara_met time_t the_time = time(NULL); struct tm *the_local_time = localtime(&the_time); char year[10]; - code = snprintf(year, 10, "%d", the_local_time->tm_year + 1900); + code = safe_snprintf(year, 10, "%d", the_local_time->tm_year + 1900); GOTO_IF(code < 0 || code >= 10, "Failed to get the year of the current date", error_no_sqlite); /* From here we initialize the sqlite stmt. */ diff --git a/src/main/lkt.c b/src/main/lkt.c index 0d27efe2ffa2c65e391b3e78eeac39d3f6e5012b..87282296a414035f9f0bcbd0674441680e2e2348 100644 --- a/src/main/lkt.c +++ b/src/main/lkt.c @@ -59,19 +59,14 @@ static const char *LKT_QUEUE_DEFAULT[] = { "10" }; static int lkt_valid_type(const char *type) { - return (STR_MATCH(type, "all") || - STR_MATCH(type, "any") || - STR_MATCH(type, "query") || + return (STR_MATCH(type, "all") || STR_MATCH(type, "any") || STR_MATCH(type, "query") || + STR_MATCH(type, "cat") || STR_MATCH(type, "category") || + STR_MATCH(type, "author") || STR_MATCH(type, "auth") || + STR_MATCH(type, "lang") || STR_MATCH(type, "language") || STR_MATCH(type, "id") || STR_MATCH(type, "title") || STR_MATCH(type, "type") || - STR_MATCH(type, "cat") || - STR_MATCH(type, "category") || - STR_MATCH(type, "author") || - STR_MATCH(type, "auth") || - STR_MATCH(type, "source") || - STR_MATCH(type, "lang") || - STR_MATCH(type, "language")); + STR_MATCH(type, "source")); } static int @@ -665,8 +660,7 @@ queue_list__(struct lkt_cmd_args *args) continuation = labs(continuation); song_index = MAX(song_index + 1, 1); - snprintf(buff, LKT_MESSAGE_MAX - 1, "%ld:%ld", song_index, song_index + continuation - 1); - buff[LKT_MESSAGE_MAX - 1] = '\0'; + safe_snprintf(buff, LKT_MESSAGE_MAX, "%ld:%ld", song_index, song_index + continuation - 1); args->argc = 1; args->argv[0] = buff; queue_pos__(args); diff --git a/src/module/mpv.c b/src/module/mpv.c index 30c8d5a739070e29f92dc277c00245c078964b78..3f101b9c51be6406f9bad78df40378080753b13c 100644 --- a/src/module/mpv.c +++ b/src/module/mpv.c @@ -89,7 +89,7 @@ lmpv_set_volume(mpv_handle *ctx, int vol) int status; char str[5]; memset(str, 0, 5); - snprintf(str, 4, "%d", vol); + safe_snprintf(str, 4, "%d", vol); const char *cmd[] = {"set", "ao-volume", str, NULL}; if ((status = mpv_command_async(ctx, 0, cmd)) < 0) { LOG_ERROR_SCT("WINDOW", "Failed to execute command: %s", mpv_error_string(status)); diff --git a/src/net/downloader.c b/src/net/downloader.c index 9df9b1bae07918d97f759d4c2c93d74336d46c0e..9645a50b446424eccad001a8fae29b33cb9841e7 100644 --- a/src/net/downloader.c +++ b/src/net/downloader.c @@ -184,8 +184,7 @@ repo_get_id(struct lkt_repo *const repo, const uint64_t id, struct kara_metadata }; memset(url, 0, URL_MAX_LEN * sizeof(char)); - snprintf(url, URL_MAX_LEN - 1, repo->get_id_json, id); - url[URL_MAX_LEN - 1] = 0; + safe_snprintf(url, URL_MAX_LEN, repo->get_id_json, id); curl_handle = curl_easy_init(); curl_easy_setopt(curl_handle, CURLOPT_URL, url); curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_mem__); @@ -315,8 +314,7 @@ repo_download_id_sync(struct lkt_repo *const repo, const uint64_t id, const char } memset(url, 0, URL_MAX_LEN * sizeof(char)); - snprintf(url, URL_MAX_LEN - 1, repo->get_id_file, id); - url[URL_MAX_LEN - 1] = 0; + safe_snprintf(url, URL_MAX_LEN, repo->get_id_file, id); if (__download_kara(url, kara_path, false)) { LOG_ERROR_SCT("REPO", "Failed to download kara '%s' with url '%s'", kara_path, url); @@ -363,8 +361,7 @@ __handle_got_json(volatile sqlite3 *db, struct lkt_repo *repo, struct json_objec strncat(kara->filename, "/", PATH_MAX - 1); kara->filename[++kara_dir_len] = 0; } - snprintf(kara->filename + kara_dir_len, PATH_MAX - kara_dir_len, "%d.mkv", integer); - kara->filename[PATH_MAX - 1] = 0; + safe_snprintf(kara->filename + kara_dir_len, PATH_MAX - kara_dir_len, "%d.mkv", integer); LOG_INFO_SCT("REPO", "Crafted filename is '%s'", kara->filename); /* Timestamp verification */ @@ -397,8 +394,7 @@ do_it: continue; } - snprintf(url, URL_MAX_LEN - 1, repo->get_id_file, kara->id); - url[URL_MAX_LEN - 1] = '\0'; + safe_snprintf(url, URL_MAX_LEN, repo->get_id_file, kara->id); if (__download_kara(url, kara->filename, true)) { LOG_WARN_SCT("REPO", "Could not download kara %ld at path '%s'", kara->id, kara->filename); diff --git a/src/net/listen.c b/src/net/listen.c index 2e6da6428e825ca160b65c647559f58962957a90..84ec3db308ef37eaa5a69c25adb0c7eca123369e 100644 --- a/src/net/listen.c +++ b/src/net/listen.c @@ -86,8 +86,7 @@ static inline void send_continue(struct lkt_state *srv, size_t c, int i) { struct lkt_message *cont = lkt_message_new(); - cont->data_len = snprintf(cont->data, LKT_MESSAGE_MAX, "continue: %d\n", i); - cont->data[LKT_MESSAGE_MAX - 1] = '\0'; + cont->data_len = safe_snprintf(cont->data, LKT_MESSAGE_MAX, "continue: %d\n", i); lkt_state_send(srv, c, cont); } diff --git a/src/uri.c b/src/uri.c index ec076ed25e67e30fc1eeb8418efffa51107092f7..4f6fe5690d2946c4e7fbe9e2a9e9f21c7ea1ea0f 100644 --- a/src/uri.c +++ b/src/uri.c @@ -98,19 +98,19 @@ __lkt_to_str(struct lkt_uri *uri, char *ret, size_t len) { switch (uri->type) { case uri_id: - snprintf(ret, len - 1, "id=%s", (char *) uri->value); + safe_snprintf(ret, len, "id=%s", (char *) uri->value); break; case uri_type: - snprintf(ret, len - 1, "type=%s", (char *) uri->value); + safe_snprintf(ret, len, "type=%s", (char *) uri->value); break; case uri_author: - snprintf(ret, len - 1, "author=%s", (char *) uri->value); + safe_snprintf(ret, len, "author=%s", (char *) uri->value); break; case uri_category: - snprintf(ret, len - 1, "cat=%s", (char *) uri->value); + safe_snprintf(ret, len, "cat=%s", (char *) uri->value); break; case uri_query: - snprintf(ret, len - 1, "search=%s", (char *) uri->value); + safe_snprintf(ret, len, "search=%s", (char *) uri->value); break; default: LOG_ERROR("URI type %d may not be supported by kurisu, generate an error", uri->type);