diff --git a/inc/lektor/commands.h b/inc/lektor/commands.h
index 424e6524eaeba06889160cb8ed3f0c221f963b0c..3329c8a48a5e26e2799d045e74eae4251f1797ed 100644
--- a/inc/lektor/commands.h
+++ b/inc/lektor/commands.h
@@ -98,3 +98,9 @@ bool command_user_add(sqlite3 *db, char *argv[LKT_MESSAGE_ARGS_MAX]);
 bool command_restart(struct lkt_state *srv, size_t c);
 bool command_kill(struct lkt_state *srv, size_t c);
 bool command_rescan(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX]);
+
+/* Sticker management */
+bool command_sticker_get(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX]);
+bool command_sticker_set(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX]);
+bool command_sticker_delete(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX]);
+bool command_sticker_list(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX]);
diff --git a/inc/lektor/database.h b/inc/lektor/database.h
index d5632e3c18bbc0632e7e7c2edaf33a6d706e5af0..e26b78f14089117b8bc4c83e49ecf6e248bbf5a9 100644
--- a/inc/lektor/database.h
+++ b/inc/lektor/database.h
@@ -131,6 +131,8 @@ typedef bool (*database_sticker_callback_t)(void *args, const char *sticker, con
 
 bool database_sticker_create(sqlite3 *db, const char *name);
 bool database_sticker_delete(sqlite3 *db, const char *name);
+bool database_sticker_delete_specify(sqlite3 *sb, const char *type, int uri,
+                                     const char *name /* Can be null */);
 bool database_sticker_list(sqlite3 *db, const char *type, void *args, database_sticker_callback_t call);
 bool database_sticker_set(sqlite3 *db, const char *type, const char *name, int uri, int value);
 bool database_sticker_get(sqlite3 *db, const char *type, const char *name, int uri, void *args,
diff --git a/src/commands.c b/src/commands.c
index 23792ffc375facddf208015c3552e3c8222d71f0..2282c9bb041d216aabab8ceaf51ea43681a0bb48 100644
--- a/src/commands.c
+++ b/src/commands.c
@@ -21,6 +21,15 @@
 #define SELF_EXECUTABLE_FREEBSD "/proc/curproc/file"
 #define SELF_EXECUTABLE_SOLARIS "/proc/self/path/a.out"
 
+struct _client_trace_t {
+    struct lkt_state *srv;
+    size_t c;
+    int uri;
+    const char *name;
+    int value;
+    bool is_ok;
+};
+
 inline bool
 command_restart(struct lkt_state *srv, size_t c)
 {
@@ -549,11 +558,6 @@ command_noidle(struct lkt_state *srv, size_t c)
 }
 
 /* Functions for the searchadd and the search mpd commands */
-struct _client_trace_t {
-    struct lkt_state *srv;
-    size_t c;
-};
-
 static bool
 lkt_callback_print_row_v1(void *args, int id, int id_len, const char *sql_row)
 {
@@ -1074,3 +1078,210 @@ command_user_add(sqlite3 *db, char *argv[LKT_MESSAGE_ARGS_MAX])
     return false;
     fprintf(stderr, " . command_user_add: Failed to add user %s\n", argv[0]);
 }
+
+static bool
+sticker_send_one_value(void *_args, const char *sticker, const char *type, int uri, int value)
+{
+    struct _client_trace_t *args = (struct _client_trace_t *) _args;
+    struct lkt_message *out;
+    (void) sticker;
+    (void) type;
+    (void) uri;
+
+    out = lkt_message_new();
+    out->data_len = snprintf(out->data, LKT_MESSAGE_MAX, "value: %d\n", value);
+    lkt_state_send(args->srv, args->c, out);
+    return false;
+}
+
+static bool
+sticker_send_all(void *_args, const char *sticker, const char *type, int uri, int value)
+{
+    struct _client_trace_t *args = (struct _client_trace_t *) _args;
+    struct lkt_message *out;
+
+    out = lkt_message_new();
+    out->data_len = snprintf(out->data, LKT_MESSAGE_MAX, "%s: %d\nsticker: %s\nvalue: %d\n",
+                             type, uri, sticker, value);
+    lkt_state_send(args->srv, args->c, out);
+    return true;
+}
+
+static bool
+sticker_send_check_uri(void *_args, const char *sticker, const char *type, int uri, int value)
+{
+    struct _client_trace_t *args = (struct _client_trace_t *) _args;
+    struct lkt_message *out;
+    (void) type;
+
+    if (uri == args->uri) {
+        out = lkt_message_new();
+        out->data_len = snprintf(out->data, LKT_MESSAGE_MAX, "%s: %d\n", sticker, value);
+        lkt_state_send(args->srv, args->c, out);
+    }
+
+    return true;
+}
+
+static bool
+sticker_send_value_check_uri_name(void *_args, const char *sticker, const char *type, int uri, int value)
+{
+    struct _client_trace_t *args = (struct _client_trace_t *) _args;
+    struct lkt_message *out;
+    (void) type;
+
+    if (uri == args->uri || !strcasecmp(sticker, args->name)) {
+        out = lkt_message_new();
+        out->data_len = snprintf(out->data, LKT_MESSAGE_MAX, "value: %d\n", value);
+        lkt_state_send(args->srv, args->c, out);
+    }
+
+    return true;
+}
+
+static bool
+sticker_check_is_present_eq(void *_args, const char *sticker, const char *type, int uri, int value)
+{
+    (void) type;
+    struct _client_trace_t *args = (struct _client_trace_t *) _args;
+    args->is_ok |= (uri == args->uri) && !strcasecmp(sticker, args->name) && (value == args->value);
+    return true;
+}
+
+static bool
+sticker_check_is_present_lt(void *_args, const char *sticker, const char *type, int uri, int value)
+{
+    (void) type;
+    struct _client_trace_t *args = (struct _client_trace_t *) _args;
+    args->is_ok |= (uri == args->uri) && !strcasecmp(sticker, args->name) && (value < args->value);
+    return true;
+}
+
+static bool
+sticker_check_is_present_gt(void *_args, const char *sticker, const char *type, int uri, int value)
+{
+    (void) type;
+    struct _client_trace_t *args = (struct _client_trace_t *) _args;
+    args->is_ok |= (uri == args->uri) && !strcasecmp(sticker, args->name) && (value > args->value);
+    return true;
+}
+
+bool
+command_sticker_get(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX])
+{
+    if (argv[0] == NULL || argv[1] == NULL || argv[2] == NULL || argv[3] != NULL) {
+        fprintf(stderr, " ! command_sticker_get: Invalid argument, need only 3 arguments\n");
+        return false;
+    }
+
+    int uri = atoi(argv[1]); /* FIXME: Use strtol. */
+    struct _client_trace_t args = {
+        .srv = srv,
+        .c = c,
+    };
+
+    if (!database_sticker_get(srv->db, argv[0], argv[2], uri, &args, sticker_send_one_value)) {
+        fprintf(stderr, " . command_sticker_get: Failed to get sticker '%s' for object %s(%d)\n",
+                argv[2], argv[0], uri);
+        return false;
+    }
+
+    return true;
+}
+
+bool
+command_sticker_set(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX])
+{
+    if (argv[0] == NULL || argv[1] == NULL || argv[2] == NULL || argv[3] == NULL || argv[4] != NULL) {
+        fprintf(stderr, " ! command_sticker_get: Invalid argument, need only 3 arguments\n");
+        return false;
+    }
+
+    (void) c;
+    int uri = atoi(argv[1]);    /* FIXME: Use strtol. */
+    int value = atoi(argv[4]);  /* FIXME: Use strtol. */
+
+    if (!database_sticker_set(srv->db, argv[0], argv[2], uri, value)) {
+        fprintf(stderr, " . command_sticker_get: Failed to get sticker '%s' to value %d for object "
+                "%s(%d)\n", argv[2], value, argv[0], uri);
+        return false;
+    }
+
+    return true;
+}
+
+bool
+command_sticker_list(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX])
+{
+    struct _client_trace_t args = {
+        .srv = srv,
+        .c = c,
+    };
+
+    /* Simple list {type} {uri} command */
+    if (argv[0] != NULL && argv[1] != NULL && argv[2] == NULL)
+        goto simple_list_command;
+
+    /* list {type} {uri} {name} command */
+    else if (argv[0] != NULL && argv[1] != NULL &&
+             argv[2] != NULL && argv[3] == NULL)
+        goto list_stickers_in_uri;
+
+    /* list {type} {uri} {name} `op` {value} command */
+    else if (argv[0] != NULL && argv[1] != NULL &&
+             argv[2] != NULL && argv[3] != NULL &&
+             argv[4] != NULL && argv[5] == NULL)
+        goto list_stickers_check_value;
+
+    /* Just list all stickers */
+    else if ( (argv[0] != NULL && argv[1] == NULL) || argv[0] == NULL )
+        goto just_list_all;
+
+    else
+        goto unknown;
+
+just_list_all:
+    return database_sticker_list(srv->db, argv[0], &args, sticker_send_all);
+
+simple_list_command:
+    args.uri = atoi(argv[1]);   /* FIXME: Use strtol. */
+    return database_sticker_list(srv->db, argv[0], &args, sticker_send_check_uri);
+
+list_stickers_in_uri:
+    args.uri  = atoi(argv[1]);  /* FIXME: Use strtol. */
+    args.name = argv[2];
+    return database_sticker_list(srv->db, argv[0], &args, sticker_send_value_check_uri_name);
+    return false;
+
+list_stickers_check_value:
+    args.uri   = atoi(argv[1]); /* FIXME: Use strtol. */
+    args.value = atoi(argv[4]); /* FIXME: Use strtol. */
+    args.name  = argv[2];
+    switch (argv[3][0]) {
+    case '=':
+        return database_sticker_list(srv->db, argv[0], &args, sticker_check_is_present_eq);
+    case '<':
+        return database_sticker_list(srv->db, argv[0], &args, sticker_check_is_present_lt);
+    case '>':
+        return database_sticker_list(srv->db, argv[0], &args, sticker_check_is_present_gt);
+    default:
+        return 0;
+    }
+
+unknown:
+    fprintf(stderr, " . command_sticker_list: Specified command is invalid or unknown\n");
+    return false;
+}
+
+bool
+command_sticker_delete(struct lkt_state *srv, size_t c, char *argv[LKT_MESSAGE_ARGS_MAX])
+{
+    if (argv[0] == NULL || argv[1] == NULL) {
+        fprintf(stderr, " . command_sticker_delete: Invalid argument\n");
+        return false;
+    }
+
+    (void) c;
+    int uri = atoi(argv[1]);
+    return database_sticker_delete_specify(srv->db, argv[0], uri, argv[2]);
+}
diff --git a/src/database/stickers.c b/src/database/stickers.c
index 0100955c498f1f495d6be2b6db23b5354bb2ece0..4b8661b800f249bdf5fc9fd3cb7c80552c1a7b23 100644
--- a/src/database/stickers.c
+++ b/src/database/stickers.c
@@ -113,7 +113,7 @@ database_sticker_list(sqlite3 *db, const char *type, void *args,
             uri     = sqlite3_column_int(stmt, 1);
             value   = sqlite3_column_int(stmt, 2);
             if (!call(args, sticker, type, uri, value))
-                goto error;
+                goto end_loop;
             continue;
 
         case SQLITE_DONE:
@@ -150,7 +150,7 @@ database_sticker_set(sqlite3 *db, const char *type, const char *name, int uri, i
             "SELECT ?, 'stickers'.id, ? "
             "FROM 'stickers'"
             "WHERE 'stickers'.id = ?;\n";
-    else if (!strcasecmp(type, "song"))
+    else if (!strcasecmp(type, "plt"))
         SQL =
             "INSERT OR REPLACE INTO 'stickers.plt' (id, sticker, value) "
             "SELECT ?, 'stickers'.id, ? "
@@ -193,12 +193,12 @@ database_sticker_get(sqlite3 *db, const char *type, const char *name, int uri, v
             "JOIN 'stickers.song'"
             " ON 'stickers'.id = 'stickers.song'.sticker"
             " AND 'stickers'.name = ?;\n";
-    else if (!strcasecmp(type, "song"))
+    else if (!strcasecmp(type, "plt"))
         SQL =
             "SELECT value "
             "FROM 'stickers' "
-            "JOIN 'stickers.song'"
-            " ON 'stickers'.id = 'stickers.song'.sticker"
+            "JOIN 'stickers.plt'"
+            " ON 'stickers'.id = 'stickers.plt'.sticker"
             " AND 'stickers'.name = ?;\n";
     else {
         fprintf(stderr, " . database_sticker_get: Type '%s' is invalid\n", type);
@@ -213,7 +213,7 @@ database_sticker_get(sqlite3 *db, const char *type, const char *name, int uri, v
         case SQLITE_ROW:
             value = sqlite3_column_int(stmt, 1);
             if (!call(args, name, type, uri, value))
-                goto error;
+                goto end_loop;
             continue;
 
         case SQLITE_DONE:
@@ -230,3 +230,46 @@ error:
     sqlite3_finalize(stmt);
     return ret;
 }
+
+bool
+database_sticker_delete_specify(sqlite3 *db, const char *type, int uri, const char *name)
+{
+    char SQL[LKT_MAX_SQLITE_STATEMENT];
+    sqlite3_stmt *stmt;
+    int ret = false;
+
+    /* Base query. */
+    if (strcasecmp("plt", type) && strcasecmp("song", type)) {
+        fprintf(stderr, " . database_sticker_get: Type '%s' is invalid\n", type);
+        return false;
+    }
+
+    snprintf(SQL, LKT_MAX_SQLITE_STATEMENT - 1, "DELETE FROM 'stickers.%s' "
+            "WHERE 'stickers.%s' = ? ", type, type);
+    SQL[LKT_MAX_SQLITE_STATEMENT - 1] = 0;
+
+    /* If there is a name specified. */
+    if (!name) {
+        strncat(SQL, ";", LKT_MAX_SQLITE_STATEMENT - 1);
+        SQL[LKT_MAX_SQLITE_STATEMENT - 1] = 0;
+    } else {
+        strncat(SQL, "AND sticker = (SELECT id FROM 'stickers' WHERE name = ? LIMIT 1);",
+                LKT_MAX_SQLITE_STATEMENT - 1);
+        SQL[LKT_MAX_SQLITE_STATEMENT - 1] = 0;
+    }
+
+    SQLITE_PREPARE(db, stmt, SQL, error);
+    SQLITE_BIND_INT(db, stmt, 1, uri, error);
+
+    if (name)
+        SQLITE_BIND_TEXT(db, stmt, 2, name, error);
+
+    if (! (ret = (sqlite3_step(stmt) == SQLITE_OK))) {
+        fprintf(stderr, " . database_sticker_delete_specify: Failed to delete sticker\n");
+        goto error;
+    }
+    ret = true;
+error:
+    sqlite3_finalize(stmt);
+    return ret;
+}
diff --git a/src/net/listen.c b/src/net/listen.c
index 049cf75e93610c3cf20cdd397c723de2afc452bf..d42e68ffd5973791b9665e9c2bcf73282570daa4 100644
--- a/src/net/listen.c
+++ b/src/net/listen.c
@@ -162,6 +162,8 @@ handle_simple_command(struct lkt_state *srv, size_t c, struct lkt_command cmd)
 
         else if (!strcmp(cmd.name, "close"))
             err = !lkt_close_client(srv, c);
+        else if (!strcmp(cmd.name, "ping"))
+            err = 0;
 
         else if (!strcmp(cmd.name, "next"))
             err = !command_next(srv->db, &srv->win, &srv->mpd_idle_events);
@@ -186,6 +188,17 @@ handle_simple_command(struct lkt_state *srv, size_t c, struct lkt_command cmd)
         else if (!strcmp(cmd.name, "playlist") || !strcmp(cmd.name, "playlistinfo"))
             err = !command_queue_list(srv, c, cmd.args);
 
+        else if (!strcmp(cmd.name, "sticker") && cmd.args[0]) {
+            if (!strcmp(cmd.args[0], "get"))
+                err = ! command_sticker_get(srv, c, &cmd.args[1]);
+            else if (!strcmp(cmd.args[0], "set"))
+                err = ! command_sticker_set(srv, c, &cmd.args[1]);
+            else if (!strcmp(cmd.args[0], "delete"))
+                err = ! command_sticker_delete(srv, c, &cmd.args[1]);
+            else if (!strcmp(cmd.args[0], "list") || !strcmp(cmd.args[0], "find"))
+                err = ! command_sticker_delete(srv, c, &cmd.args[1]);
+        }
+
         else if (!strcmp(cmd.name, "help"))
             err = !command_help(srv, c);
         else if (!strcmp(cmd.name, "__dbupdate"))
@@ -647,8 +660,7 @@ handle_idle_events(struct lkt_state *srv)
 }
 
 enum mpd_idle_flag *
-lkt_client_get_mask(struct lkt_state *srv, size_t c)
-{
+lkt_client_get_mask(struct lkt_state *srv, size_t c) {
     return &(srv->clients[c - 1].mpd_idle_watch);
 }