doc: Remove superfluous comment already described in footnotes.
[mpd-mk.git] / src / command.c
blob58e9316505433aea4d56ada0b02e65ee44bb43e4
1 /*
2 * Copyright (C) 2003-2009 The Music Player Daemon Project
3 * http://www.musicpd.org
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "command.h"
21 #include "player_control.h"
22 #include "playlist.h"
23 #include "playlist_print.h"
24 #include "playlist_save.h"
25 #include "queue_print.h"
26 #include "ls.h"
27 #include "uri.h"
28 #include "directory.h"
29 #include "directory_print.h"
30 #include "database.h"
31 #include "update.h"
32 #include "volume.h"
33 #include "stats.h"
34 #include "permission.h"
35 #include "tokenizer.h"
36 #include "stored_playlist.h"
37 #include "ack.h"
38 #include "output_command.h"
39 #include "output_print.h"
40 #include "locate.h"
41 #include "dbUtils.h"
42 #include "tag.h"
43 #include "client.h"
44 #include "tag_print.h"
45 #include "path.h"
46 #include "idle.h"
47 #include "config.h"
49 #ifdef ENABLE_SQLITE
50 #include "sticker.h"
51 #include "sticker_print.h"
52 #include "song_sticker.h"
53 #include "song_print.h"
54 #endif
56 #include <assert.h>
57 #include <time.h>
58 #include <stdlib.h>
59 #include <errno.h>
61 #define COMMAND_STATUS_STATE "state"
62 #define COMMAND_STATUS_REPEAT "repeat"
63 #define COMMAND_STATUS_SINGLE "single"
64 #define COMMAND_STATUS_CONSUME "consume"
65 #define COMMAND_STATUS_RANDOM "random"
66 #define COMMAND_STATUS_PLAYLIST "playlist"
67 #define COMMAND_STATUS_PLAYLIST_LENGTH "playlistlength"
68 #define COMMAND_STATUS_SONG "song"
69 #define COMMAND_STATUS_SONGID "songid"
70 #define COMMAND_STATUS_NEXTSONG "nextsong"
71 #define COMMAND_STATUS_NEXTSONGID "nextsongid"
72 #define COMMAND_STATUS_TIME "time"
73 #define COMMAND_STATUS_BITRATE "bitrate"
74 #define COMMAND_STATUS_ERROR "error"
75 #define COMMAND_STATUS_CROSSFADE "xfade"
76 #define COMMAND_STATUS_AUDIO "audio"
77 #define COMMAND_STATUS_UPDATING_DB "updating_db"
80 * The most we ever use is for search/find, and that limits it to the
81 * number of tags we can have. Add one for the command, and one extra
82 * to catch errors clients may send us
84 #define COMMAND_ARGV_MAX (2+(TAG_NUM_OF_ITEM_TYPES*2))
86 /* if min: -1 don't check args *
87 * if max: -1 no max args */
88 struct command {
89 const char *cmd;
90 unsigned permission;
91 int min;
92 int max;
93 enum command_return (*handler)(struct client *client, int argc, char **argv);
96 /* this should really be "need a non-negative integer": */
97 static const char need_positive[] = "need a positive integer"; /* no-op */
98 static const char need_range[] = "need a range";
100 /* FIXME: redundant error messages */
101 static const char check_integer[] = "\"%s\" is not a integer";
102 static const char need_integer[] = "need an integer";
104 static const char *current_command;
105 static int command_list_num;
107 void command_success(struct client *client)
109 client_puts(client, "OK\n");
112 static void command_error_v(struct client *client, enum ack error,
113 const char *fmt, va_list args)
115 assert(client != NULL);
116 assert(current_command != NULL);
118 client_printf(client, "ACK [%i@%i] {%s} ",
119 (int)error, command_list_num, current_command);
120 client_vprintf(client, fmt, args);
121 client_puts(client, "\n");
123 current_command = NULL;
126 G_GNUC_PRINTF(3, 4) static void command_error(struct client *client, enum ack error,
127 const char *fmt, ...)
129 va_list args;
130 va_start(args, fmt);
131 command_error_v(client, error, fmt, args);
132 va_end(args);
135 static bool G_GNUC_PRINTF(4, 5)
136 check_uint32(struct client *client, uint32_t *dst,
137 const char *s, const char *fmt, ...)
139 char *test;
141 *dst = strtoul(s, &test, 10);
142 if (*test != '\0') {
143 va_list args;
144 va_start(args, fmt);
145 command_error_v(client, ACK_ERROR_ARG, fmt, args);
146 va_end(args);
147 return false;
149 return true;
152 static bool G_GNUC_PRINTF(4, 5)
153 check_int(struct client *client, int *value_r,
154 const char *s, const char *fmt, ...)
156 char *test;
157 long value;
159 value = strtol(s, &test, 10);
160 if (*test != '\0') {
161 va_list args;
162 va_start(args, fmt);
163 command_error_v(client, ACK_ERROR_ARG, fmt, args);
164 va_end(args);
165 return false;
168 #if LONG_MAX > INT_MAX
169 if (value < INT_MIN || value > INT_MAX) {
170 command_error(client, ACK_ERROR_ARG,
171 "Number too large: %s", s);
172 return false;
174 #endif
176 *value_r = (int)value;
177 return true;
180 static bool G_GNUC_PRINTF(5, 6)
181 check_range(struct client *client, unsigned *value_r1, unsigned *value_r2,
182 const char *s, const char *fmt, ...)
184 char *test, *test2;
185 long value;
187 value = strtol(s, &test, 10);
188 if (*test != '\0' && *test != ':') {
189 va_list args;
190 va_start(args, fmt);
191 command_error_v(client, ACK_ERROR_ARG, fmt, args);
192 va_end(args);
193 return false;
196 if (value == -1 && *test == 0) {
197 /* compatibility with older MPD versions: specifying
198 "-1" makes MPD display the whole list */
199 *value_r1 = 0;
200 *value_r2 = UINT_MAX;
201 return true;
204 if (value < 0) {
205 command_error(client, ACK_ERROR_ARG,
206 "Number is negative: %s", s);
207 return false;
210 #if LONG_MAX > UINT_MAX
211 if (value > UINT_MAX) {
212 command_error(client, ACK_ERROR_ARG,
213 "Number too large: %s", s);
214 return false;
216 #endif
218 *value_r1 = (unsigned)value;
220 if (*test == ':') {
221 value = strtol(++test, &test2, 10);
222 if (*test2 != '\0' || test == test2) {
223 va_list args;
224 va_start(args, fmt);
225 command_error_v(client, ACK_ERROR_ARG, fmt, args);
226 va_end(args);
227 return false;
230 if (value < 0) {
231 command_error(client, ACK_ERROR_ARG,
232 "Number is negative: %s", s);
233 return false;
236 #if LONG_MAX > UINT_MAX
237 if (value > UINT_MAX) {
238 command_error(client, ACK_ERROR_ARG,
239 "Number too large: %s", s);
240 return false;
242 #endif
243 *value_r2 = (unsigned)value;
244 } else {
245 *value_r2 = (unsigned)value + 1;
248 return true;
251 static bool
252 check_unsigned(struct client *client, unsigned *value_r, const char *s)
254 unsigned long value;
255 char *endptr;
257 value = strtoul(s, &endptr, 10);
258 if (*endptr != 0) {
259 command_error(client, ACK_ERROR_ARG,
260 "Integer expected: %s", s);
261 return false;
264 if (value > UINT_MAX) {
265 command_error(client, ACK_ERROR_ARG,
266 "Number too large: %s", s);
267 return false;
270 *value_r = (unsigned)value;
271 return true;
274 static bool
275 check_bool(struct client *client, bool *value_r, const char *s)
277 long value;
278 char *endptr;
280 value = strtol(s, &endptr, 10);
281 if (*endptr != 0 || (value != 0 && value != 1)) {
282 command_error(client, ACK_ERROR_ARG,
283 "Boolean (0/1) expected: %s", s);
284 return false;
287 *value_r = !!value;
288 return true;
291 static enum command_return
292 print_playlist_result(struct client *client,
293 enum playlist_result result)
295 switch (result) {
296 case PLAYLIST_RESULT_SUCCESS:
297 return COMMAND_RETURN_OK;
299 case PLAYLIST_RESULT_ERRNO:
300 command_error(client, ACK_ERROR_SYSTEM, "%s", strerror(errno));
301 return COMMAND_RETURN_ERROR;
303 case PLAYLIST_RESULT_DENIED:
304 command_error(client, ACK_ERROR_NO_EXIST, "Access denied");
305 return COMMAND_RETURN_ERROR;
307 case PLAYLIST_RESULT_NO_SUCH_SONG:
308 command_error(client, ACK_ERROR_NO_EXIST, "No such song");
309 return COMMAND_RETURN_ERROR;
311 case PLAYLIST_RESULT_NO_SUCH_LIST:
312 command_error(client, ACK_ERROR_NO_EXIST, "No such playlist");
313 return COMMAND_RETURN_ERROR;
315 case PLAYLIST_RESULT_LIST_EXISTS:
316 command_error(client, ACK_ERROR_EXIST,
317 "Playlist already exists");
318 return COMMAND_RETURN_ERROR;
320 case PLAYLIST_RESULT_BAD_NAME:
321 command_error(client, ACK_ERROR_ARG,
322 "playlist name is invalid: "
323 "playlist names may not contain slashes,"
324 " newlines or carriage returns");
325 return COMMAND_RETURN_ERROR;
327 case PLAYLIST_RESULT_BAD_RANGE:
328 command_error(client, ACK_ERROR_ARG, "Bad song index");
329 return COMMAND_RETURN_ERROR;
331 case PLAYLIST_RESULT_NOT_PLAYING:
332 command_error(client, ACK_ERROR_PLAYER_SYNC, "Not playing");
333 return COMMAND_RETURN_ERROR;
335 case PLAYLIST_RESULT_TOO_LARGE:
336 command_error(client, ACK_ERROR_PLAYLIST_MAX,
337 "playlist is at the max size");
338 return COMMAND_RETURN_ERROR;
340 case PLAYLIST_RESULT_DISABLED:
341 command_error(client, ACK_ERROR_UNKNOWN,
342 "stored playlist support is disabled");
343 return COMMAND_RETURN_ERROR;
346 assert(0);
347 return COMMAND_RETURN_ERROR;
350 static void
351 print_spl_list(struct client *client, GPtrArray *list)
353 for (unsigned i = 0; i < list->len; ++i) {
354 struct stored_playlist_info *playlist =
355 g_ptr_array_index(list, i);
356 time_t t;
357 #ifndef WIN32
358 struct tm tm;
359 #endif
360 char timestamp[32];
362 client_printf(client, "playlist: %s\n", playlist->name);
364 t = playlist->mtime;
365 strftime(timestamp, sizeof(timestamp), "%FT%TZ",
366 #ifdef WIN32
367 gmtime(&t)
368 #else
369 gmtime_r(&t, &tm)
370 #endif
372 client_printf(client, "Last-Modified: %s\n", timestamp);
376 static enum command_return
377 handle_urlhandlers(struct client *client,
378 G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
380 if (client_get_uid(client) > 0)
381 client_puts(client, "handler: file://\n");
382 print_supported_uri_schemes(client);
383 return COMMAND_RETURN_OK;
386 static enum command_return
387 handle_tagtypes(struct client *client,
388 G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
390 tag_print_types(client);
391 return COMMAND_RETURN_OK;
394 static enum command_return
395 handle_play(struct client *client, int argc, char *argv[])
397 int song = -1;
398 enum playlist_result result;
400 if (argc == 2 && !check_int(client, &song, argv[1], need_positive))
401 return COMMAND_RETURN_ERROR;
402 result = playlist_play(&g_playlist, song);
403 return print_playlist_result(client, result);
406 static enum command_return
407 handle_playid(struct client *client, int argc, char *argv[])
409 int id = -1;
410 enum playlist_result result;
412 if (argc == 2 && !check_int(client, &id, argv[1], need_positive))
413 return COMMAND_RETURN_ERROR;
415 result = playlist_play_id(&g_playlist, id);
416 return print_playlist_result(client, result);
419 static enum command_return
420 handle_stop(G_GNUC_UNUSED struct client *client,
421 G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
423 playlist_stop(&g_playlist);
424 return COMMAND_RETURN_OK;
427 static enum command_return
428 handle_currentsong(struct client *client,
429 G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
431 playlist_print_current(client, &g_playlist);
432 return PLAYLIST_RESULT_SUCCESS;
435 static enum command_return
436 handle_pause(struct client *client,
437 int argc, char *argv[])
439 if (argc == 2) {
440 bool pause_flag;
441 if (!check_bool(client, &pause_flag, argv[1]))
442 return COMMAND_RETURN_ERROR;
443 playerSetPause(pause_flag);
444 return COMMAND_RETURN_OK;
447 playerPause();
448 return COMMAND_RETURN_OK;
451 static enum command_return
452 handle_status(struct client *client,
453 G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
455 const char *state = NULL;
456 int updateJobId;
457 int song;
459 switch (getPlayerState()) {
460 case PLAYER_STATE_STOP:
461 state = "stop";
462 break;
463 case PLAYER_STATE_PAUSE:
464 state = "pause";
465 break;
466 case PLAYER_STATE_PLAY:
467 state = "play";
468 break;
471 client_printf(client,
472 "volume: %i\n"
473 COMMAND_STATUS_REPEAT ": %i\n"
474 COMMAND_STATUS_RANDOM ": %i\n"
475 COMMAND_STATUS_SINGLE ": %i\n"
476 COMMAND_STATUS_CONSUME ": %i\n"
477 COMMAND_STATUS_PLAYLIST ": %li\n"
478 COMMAND_STATUS_PLAYLIST_LENGTH ": %i\n"
479 COMMAND_STATUS_CROSSFADE ": %i\n"
480 COMMAND_STATUS_STATE ": %s\n",
481 volume_level_get(),
482 playlist_get_repeat(&g_playlist),
483 playlist_get_random(&g_playlist),
484 playlist_get_single(&g_playlist),
485 playlist_get_consume(&g_playlist),
486 playlist_get_version(&g_playlist),
487 playlist_get_length(&g_playlist),
488 (int)(getPlayerCrossFade() + 0.5),
489 state);
491 song = playlist_get_current_song(&g_playlist);
492 if (song >= 0) {
493 client_printf(client,
494 COMMAND_STATUS_SONG ": %i\n"
495 COMMAND_STATUS_SONGID ": %u\n",
496 song, playlist_get_song_id(&g_playlist, song));
499 if (getPlayerState() != PLAYER_STATE_STOP) {
500 const struct audio_format *af = player_get_audio_format();
501 client_printf(client,
502 COMMAND_STATUS_TIME ": %i:%i\n"
503 "elapsed: %1.3f\n"
504 COMMAND_STATUS_BITRATE ": %li\n"
505 COMMAND_STATUS_AUDIO ": %u:%u:%u\n",
506 getPlayerElapsedTime(), getPlayerTotalTime(),
507 pc.elapsed_time,
508 getPlayerBitRate(),
509 af->sample_rate, af->bits, af->channels);
512 if ((updateJobId = isUpdatingDB())) {
513 client_printf(client,
514 COMMAND_STATUS_UPDATING_DB ": %i\n",
515 updateJobId);
518 if (getPlayerError() != PLAYER_ERROR_NOERROR) {
519 client_printf(client,
520 COMMAND_STATUS_ERROR ": %s\n",
521 getPlayerErrorStr());
524 song = playlist_get_next_song(&g_playlist);
525 if (song >= 0) {
526 client_printf(client,
527 COMMAND_STATUS_NEXTSONG ": %i\n"
528 COMMAND_STATUS_NEXTSONGID ": %u\n",
529 song, playlist_get_song_id(&g_playlist, song));
532 return COMMAND_RETURN_OK;
535 static enum command_return
536 handle_kill(G_GNUC_UNUSED struct client *client,
537 G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
539 return COMMAND_RETURN_KILL;
542 static enum command_return
543 handle_close(G_GNUC_UNUSED struct client *client,
544 G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
546 return COMMAND_RETURN_CLOSE;
549 static enum command_return
550 handle_add(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
552 char *uri = argv[1];
553 enum playlist_result result;
555 if (strncmp(uri, "file:///", 8) == 0) {
556 #ifdef WIN32
557 result = PLAYLIST_RESULT_DENIED;
558 #else
559 result = playlist_append_file(&g_playlist,
560 uri + 7, client_get_uid(client),
561 NULL);
562 #endif
563 return print_playlist_result(client, result);
566 if (uri_has_scheme(uri)) {
567 if (!uri_supported_scheme(uri)) {
568 command_error(client, ACK_ERROR_NO_EXIST,
569 "unsupported URI scheme");
570 return COMMAND_RETURN_ERROR;
573 result = playlist_append_uri(&g_playlist, uri, NULL);
574 return print_playlist_result(client, result);
577 result = addAllIn(uri);
578 if (result == (enum playlist_result)-1) {
579 command_error(client, ACK_ERROR_NO_EXIST,
580 "directory or file not found");
581 return COMMAND_RETURN_ERROR;
584 return print_playlist_result(client, result);
587 static enum command_return
588 handle_addid(struct client *client, int argc, char *argv[])
590 char *uri = argv[1];
591 unsigned added_id;
592 enum playlist_result result;
594 if (strncmp(uri, "file:///", 8) == 0) {
595 #ifdef WIN32
596 result = PLAYLIST_RESULT_DENIED;
597 #else
598 result = playlist_append_file(&g_playlist, uri + 7,
599 client_get_uid(client),
600 &added_id);
601 #endif
602 } else {
603 if (uri_has_scheme(uri) && !uri_supported_scheme(uri)) {
604 command_error(client, ACK_ERROR_NO_EXIST,
605 "unsupported URI scheme");
606 return COMMAND_RETURN_ERROR;
609 result = playlist_append_uri(&g_playlist, uri, &added_id);
612 if (result != PLAYLIST_RESULT_SUCCESS)
613 return print_playlist_result(client, result);
615 if (argc == 3) {
616 int to;
617 if (!check_int(client, &to, argv[2], check_integer, argv[2]))
618 return COMMAND_RETURN_ERROR;
619 result = playlist_move_id(&g_playlist, added_id, to);
620 if (result != PLAYLIST_RESULT_SUCCESS) {
621 enum command_return ret =
622 print_playlist_result(client, result);
623 playlist_delete_id(&g_playlist, added_id);
624 return ret;
628 client_printf(client, "Id: %u\n", added_id);
629 return COMMAND_RETURN_OK;
632 static enum command_return
633 handle_delete(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
635 int song;
636 enum playlist_result result;
638 if (!check_int(client, &song, argv[1], need_positive))
639 return COMMAND_RETURN_ERROR;
641 result = playlist_delete(&g_playlist, song);
642 return print_playlist_result(client, result);
645 static enum command_return
646 handle_deleteid(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
648 int id;
649 enum playlist_result result;
651 if (!check_int(client, &id, argv[1], need_positive))
652 return COMMAND_RETURN_ERROR;
654 result = playlist_delete_id(&g_playlist, id);
655 return print_playlist_result(client, result);
658 static enum command_return
659 handle_playlist(struct client *client,
660 G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
662 playlist_print_uris(client, &g_playlist);
663 return COMMAND_RETURN_OK;
666 static enum command_return
667 handle_shuffle(G_GNUC_UNUSED struct client *client,
668 G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
670 unsigned start = 0, end = queue_length(&g_playlist.queue);
671 if (argc == 2 && !check_range(client, &start, &end,
672 argv[1], need_range))
673 return COMMAND_RETURN_ERROR;
675 playlist_shuffle(&g_playlist, start, end);
676 return COMMAND_RETURN_OK;
679 static enum command_return
680 handle_clear(G_GNUC_UNUSED struct client *client,
681 G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
683 playlist_clear(&g_playlist);
684 return COMMAND_RETURN_OK;
687 static enum command_return
688 handle_save(struct client *client,
689 G_GNUC_UNUSED int argc, char *argv[])
691 enum playlist_result result;
693 result = spl_save_playlist(argv[1], &g_playlist);
694 return print_playlist_result(client, result);
697 static enum command_return
698 handle_load(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
700 enum playlist_result result;
702 result = playlist_load_spl(&g_playlist, argv[1]);
703 return print_playlist_result(client, result);
706 static enum command_return
707 handle_listplaylist(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
709 bool ret;
711 ret = spl_print(client, argv[1], false);
712 if (!ret) {
713 command_error(client, ACK_ERROR_NO_EXIST, "No such playlist");
714 return COMMAND_RETURN_ERROR;
717 return COMMAND_RETURN_OK;
720 static enum command_return
721 handle_listplaylistinfo(struct client *client,
722 G_GNUC_UNUSED int argc, char *argv[])
724 bool ret;
726 ret = spl_print(client, argv[1], true);
727 if (!ret) {
728 command_error(client, ACK_ERROR_NO_EXIST, "No such playlist");
729 return COMMAND_RETURN_ERROR;
732 return COMMAND_RETURN_OK;
735 static enum command_return
736 handle_lsinfo(struct client *client, int argc, char *argv[])
738 const char *uri;
739 const struct directory *directory;
741 if (argc == 2)
742 uri = argv[1];
743 else
744 /* default is root directory */
745 uri = "";
747 directory = db_get_directory(uri);
748 if (directory == NULL) {
749 command_error(client, ACK_ERROR_NO_EXIST,
750 "directory not found");
751 return COMMAND_RETURN_ERROR;
754 directory_print(client, directory);
756 if (isRootDirectory(uri)) {
757 GPtrArray *list = spl_list();
758 if (list != NULL) {
759 print_spl_list(client, list);
760 spl_list_free(list);
764 return COMMAND_RETURN_OK;
767 static enum command_return
768 handle_rm(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
770 enum playlist_result result;
772 result = spl_delete(argv[1]);
773 return print_playlist_result(client, result);
776 static enum command_return
777 handle_rename(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
779 enum playlist_result result;
781 result = spl_rename(argv[1], argv[2]);
782 return print_playlist_result(client, result);
785 static enum command_return
786 handle_plchanges(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
788 uint32_t version;
790 if (!check_uint32(client, &version, argv[1], need_positive))
791 return COMMAND_RETURN_ERROR;
793 playlist_print_changes_info(client, &g_playlist, version);
794 return COMMAND_RETURN_OK;
797 static enum command_return
798 handle_plchangesposid(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
800 uint32_t version;
802 if (!check_uint32(client, &version, argv[1], need_positive))
803 return COMMAND_RETURN_ERROR;
805 playlist_print_changes_position(client, &g_playlist, version);
806 return COMMAND_RETURN_OK;
809 static enum command_return
810 handle_playlistinfo(struct client *client, int argc, char *argv[])
812 unsigned start = 0, end = UINT_MAX;
813 bool ret;
815 if (argc == 2 && !check_range(client, &start, &end,
816 argv[1], need_range))
817 return COMMAND_RETURN_ERROR;
819 ret = playlist_print_info(client, &g_playlist, start, end);
820 if (!ret)
821 return print_playlist_result(client,
822 PLAYLIST_RESULT_BAD_RANGE);
824 return COMMAND_RETURN_OK;
827 static enum command_return
828 handle_playlistid(struct client *client, int argc, char *argv[])
830 int id = -1;
832 if (argc == 2 && !check_int(client, &id, argv[1], need_positive))
833 return COMMAND_RETURN_ERROR;
835 if (id >= 0) {
836 bool ret = playlist_print_id(client, &g_playlist, id);
837 if (!ret)
838 return print_playlist_result(client,
839 PLAYLIST_RESULT_NO_SUCH_SONG);
840 } else {
841 playlist_print_info(client, &g_playlist, 0, UINT_MAX);
844 return COMMAND_RETURN_OK;
847 static enum command_return
848 handle_find(struct client *client, int argc, char *argv[])
850 int ret;
851 struct locate_item_list *list =
852 locate_item_list_parse(argv + 1, argc - 1);
854 if (list == NULL || list->length == 0) {
855 if (list != NULL)
856 locate_item_list_free(list);
858 command_error(client, ACK_ERROR_ARG, "incorrect arguments");
859 return COMMAND_RETURN_ERROR;
862 ret = findSongsIn(client, NULL, list);
863 if (ret == -1)
864 command_error(client, ACK_ERROR_NO_EXIST,
865 "directory or file not found");
867 locate_item_list_free(list);
869 return ret;
872 static enum command_return
873 handle_findadd(struct client *client, int argc, char *argv[])
875 int ret;
876 struct locate_item_list *list =
877 locate_item_list_parse(argv + 1, argc - 1);
878 if (list == NULL || list->length == 0) {
879 if (list != NULL)
880 locate_item_list_free(list);
882 command_error(client, ACK_ERROR_ARG, "incorrect arguments");
883 return COMMAND_RETURN_ERROR;
886 ret = findAddIn(client, NULL, list);
887 if (ret == -1)
888 command_error(client, ACK_ERROR_NO_EXIST,
889 "directory or file not found");
891 locate_item_list_free(list);
893 return ret;
896 static enum command_return
897 handle_search(struct client *client, int argc, char *argv[])
899 int ret;
900 struct locate_item_list *list =
901 locate_item_list_parse(argv + 1, argc - 1);
903 if (list == NULL || list->length == 0) {
904 if (list != NULL)
905 locate_item_list_free(list);
907 command_error(client, ACK_ERROR_ARG, "incorrect arguments");
908 return COMMAND_RETURN_ERROR;
911 ret = searchForSongsIn(client, NULL, list);
912 if (ret == -1)
913 command_error(client, ACK_ERROR_NO_EXIST,
914 "directory or file not found");
916 locate_item_list_free(list);
918 return ret;
921 static enum command_return
922 handle_count(struct client *client, int argc, char *argv[])
924 int ret;
925 struct locate_item_list *list =
926 locate_item_list_parse(argv + 1, argc - 1);
928 if (list == NULL || list->length == 0) {
929 if (list != NULL)
930 locate_item_list_free(list);
932 command_error(client, ACK_ERROR_ARG, "incorrect arguments");
933 return COMMAND_RETURN_ERROR;
936 ret = searchStatsForSongsIn(client, NULL, list);
937 if (ret == -1)
938 command_error(client, ACK_ERROR_NO_EXIST,
939 "directory or file not found");
941 locate_item_list_free(list);
943 return ret;
946 static enum command_return
947 handle_playlistfind(struct client *client, int argc, char *argv[])
949 struct locate_item_list *list =
950 locate_item_list_parse(argv + 1, argc - 1);
952 if (list == NULL || list->length == 0) {
953 if (list != NULL)
954 locate_item_list_free(list);
956 command_error(client, ACK_ERROR_ARG, "incorrect arguments");
957 return COMMAND_RETURN_ERROR;
960 playlist_print_find(client, &g_playlist, list);
962 locate_item_list_free(list);
964 return COMMAND_RETURN_OK;
967 static enum command_return
968 handle_playlistsearch(struct client *client, int argc, char *argv[])
970 struct locate_item_list *list =
971 locate_item_list_parse(argv + 1, argc - 1);
973 if (list == NULL || list->length == 0) {
974 if (list != NULL)
975 locate_item_list_free(list);
977 command_error(client, ACK_ERROR_ARG, "incorrect arguments");
978 return COMMAND_RETURN_ERROR;
981 playlist_print_search(client, &g_playlist, list);
983 locate_item_list_free(list);
985 return COMMAND_RETURN_OK;
988 static enum command_return
989 handle_playlistdelete(struct client *client,
990 G_GNUC_UNUSED int argc, char *argv[]) {
991 char *playlist = argv[1];
992 int from;
993 enum playlist_result result;
995 if (!check_int(client, &from, argv[2], check_integer, argv[2]))
996 return COMMAND_RETURN_ERROR;
998 result = spl_remove_index(playlist, from);
999 return print_playlist_result(client, result);
1002 static enum command_return
1003 handle_playlistmove(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
1005 char *playlist = argv[1];
1006 int from, to;
1007 enum playlist_result result;
1009 if (!check_int(client, &from, argv[2], check_integer, argv[2]))
1010 return COMMAND_RETURN_ERROR;
1011 if (!check_int(client, &to, argv[3], check_integer, argv[3]))
1012 return COMMAND_RETURN_ERROR;
1014 result = spl_move_index(playlist, from, to);
1015 return print_playlist_result(client, result);
1018 static enum command_return
1019 handle_update(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
1021 char *path = NULL;
1022 unsigned ret;
1024 assert(argc <= 2);
1025 if (argc == 2)
1026 path = g_strdup(argv[1]);
1028 ret = directory_update_init(path);
1029 if (ret > 0) {
1030 client_printf(client, "updating_db: %i\n", ret);
1031 return COMMAND_RETURN_OK;
1032 } else {
1033 command_error(client, ACK_ERROR_UPDATE_ALREADY,
1034 "already updating");
1035 return COMMAND_RETURN_ERROR;
1039 static enum command_return
1040 handle_next(G_GNUC_UNUSED struct client *client,
1041 G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
1043 /* single mode is not considered when this is user who
1044 * wants to change song. */
1045 int single = g_playlist.queue.single;
1046 g_playlist.queue.single = false;
1048 playlist_next(&g_playlist);
1050 g_playlist.queue.single = single;
1051 return COMMAND_RETURN_OK;
1054 static enum command_return
1055 handle_previous(G_GNUC_UNUSED struct client *client,
1056 G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
1058 playlist_previous(&g_playlist);
1059 return COMMAND_RETURN_OK;
1062 static enum command_return
1063 handle_listall(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
1065 char *directory = NULL;
1066 int ret;
1068 if (argc == 2)
1069 directory = argv[1];
1071 ret = printAllIn(client, directory);
1072 if (ret == -1)
1073 command_error(client, ACK_ERROR_NO_EXIST,
1074 "directory or file not found");
1076 return ret;
1079 static enum command_return
1080 handle_setvol(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
1082 int level;
1083 bool success;
1085 if (!check_int(client, &level, argv[1], need_integer))
1086 return COMMAND_RETURN_ERROR;
1088 if (level < 0 || level > 100) {
1089 command_error(client, ACK_ERROR_ARG, "Invalid volume value");
1090 return COMMAND_RETURN_ERROR;
1093 success = volume_level_change(level);
1094 if (!success) {
1095 command_error(client, ACK_ERROR_SYSTEM,
1096 "problems setting volume");
1097 return COMMAND_RETURN_ERROR;
1100 return COMMAND_RETURN_OK;
1103 static enum command_return
1104 handle_repeat(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
1106 int status;
1108 if (!check_int(client, &status, argv[1], need_integer))
1109 return COMMAND_RETURN_ERROR;
1111 if (status != 0 && status != 1) {
1112 command_error(client, ACK_ERROR_ARG,
1113 "\"%i\" is not 0 or 1", status);
1114 return COMMAND_RETURN_ERROR;
1117 playlist_set_repeat(&g_playlist, status);
1118 return COMMAND_RETURN_OK;
1121 static enum command_return
1122 handle_single(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
1124 int status;
1126 if (!check_int(client, &status, argv[1], need_integer))
1127 return COMMAND_RETURN_ERROR;
1129 if (status != 0 && status != 1) {
1130 command_error(client, ACK_ERROR_ARG,
1131 "\"%i\" is not 0 or 1", status);
1132 return COMMAND_RETURN_ERROR;
1135 playlist_set_single(&g_playlist, status);
1136 return COMMAND_RETURN_OK;
1139 static enum command_return
1140 handle_consume(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
1142 int status;
1144 if (!check_int(client, &status, argv[1], need_integer))
1145 return COMMAND_RETURN_ERROR;
1147 if (status != 0 && status != 1) {
1148 command_error(client, ACK_ERROR_ARG,
1149 "\"%i\" is not 0 or 1", status);
1150 return COMMAND_RETURN_ERROR;
1153 playlist_set_consume(&g_playlist, status);
1154 return COMMAND_RETURN_OK;
1157 static enum command_return
1158 handle_random(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
1160 int status;
1162 if (!check_int(client, &status, argv[1], need_integer))
1163 return COMMAND_RETURN_ERROR;
1165 if (status != 0 && status != 1) {
1166 command_error(client, ACK_ERROR_ARG,
1167 "\"%i\" is not 0 or 1", status);
1168 return COMMAND_RETURN_ERROR;
1171 playlist_set_random(&g_playlist, status);
1172 return COMMAND_RETURN_OK;
1175 static enum command_return
1176 handle_stats(struct client *client,
1177 G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
1179 return stats_print(client);
1182 static enum command_return
1183 handle_clearerror(G_GNUC_UNUSED struct client *client,
1184 G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
1186 clearPlayerError();
1187 return COMMAND_RETURN_OK;
1190 static enum command_return
1191 handle_list(struct client *client, int argc, char *argv[])
1193 struct locate_item_list *conditionals;
1194 int tagType = locate_parse_type(argv[1]);
1195 int ret;
1197 if (tagType < 0) {
1198 command_error(client, ACK_ERROR_ARG, "\"%s\" is not known", argv[1]);
1199 return COMMAND_RETURN_ERROR;
1202 if (tagType == LOCATE_TAG_ANY_TYPE) {
1203 command_error(client, ACK_ERROR_ARG,
1204 "\"any\" is not a valid return tag type");
1205 return COMMAND_RETURN_ERROR;
1208 /* for compatibility with < 0.12.0 */
1209 if (argc == 3) {
1210 if (tagType != TAG_ITEM_ALBUM) {
1211 command_error(client, ACK_ERROR_ARG,
1212 "should be \"%s\" for 3 arguments",
1213 tag_item_names[TAG_ITEM_ALBUM]);
1214 return COMMAND_RETURN_ERROR;
1217 locate_item_list_parse(argv + 1, argc - 1);
1219 conditionals = locate_item_list_new(1);
1220 conditionals->items[0].tag = TAG_ITEM_ARTIST;
1221 conditionals->items[0].needle = g_strdup(argv[2]);
1222 } else {
1223 conditionals =
1224 locate_item_list_parse(argv + 2, argc - 2);
1225 if (conditionals == NULL) {
1226 command_error(client, ACK_ERROR_ARG,
1227 "not able to parse args");
1228 return COMMAND_RETURN_ERROR;
1232 ret = listAllUniqueTags(client, tagType, conditionals);
1234 locate_item_list_free(conditionals);
1236 if (ret == -1)
1237 command_error(client, ACK_ERROR_NO_EXIST,
1238 "directory or file not found");
1240 return ret;
1243 static enum command_return
1244 handle_move(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
1246 unsigned start, end;
1247 int to;
1248 enum playlist_result result;
1250 if (!check_range(client, &start, &end,
1251 argv[1], need_range))
1252 return COMMAND_RETURN_ERROR;
1253 if (!check_int(client, &to, argv[2], check_integer, argv[2]))
1254 return COMMAND_RETURN_ERROR;
1255 result = playlist_move_range(&g_playlist, start, end, to);
1256 return print_playlist_result(client, result);
1259 static enum command_return
1260 handle_moveid(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
1262 int id, to;
1263 enum playlist_result result;
1265 if (!check_int(client, &id, argv[1], check_integer, argv[1]))
1266 return COMMAND_RETURN_ERROR;
1267 if (!check_int(client, &to, argv[2], check_integer, argv[2]))
1268 return COMMAND_RETURN_ERROR;
1269 result = playlist_move_id(&g_playlist, id, to);
1270 return print_playlist_result(client, result);
1273 static enum command_return
1274 handle_swap(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
1276 int song1, song2;
1277 enum playlist_result result;
1279 if (!check_int(client, &song1, argv[1], check_integer, argv[1]))
1280 return COMMAND_RETURN_ERROR;
1281 if (!check_int(client, &song2, argv[2], check_integer, argv[2]))
1282 return COMMAND_RETURN_ERROR;
1283 result = playlist_swap_songs(&g_playlist, song1, song2);
1284 return print_playlist_result(client, result);
1287 static enum command_return
1288 handle_swapid(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
1290 int id1, id2;
1291 enum playlist_result result;
1293 if (!check_int(client, &id1, argv[1], check_integer, argv[1]))
1294 return COMMAND_RETURN_ERROR;
1295 if (!check_int(client, &id2, argv[2], check_integer, argv[2]))
1296 return COMMAND_RETURN_ERROR;
1297 result = playlist_swap_songs_id(&g_playlist, id1, id2);
1298 return print_playlist_result(client, result);
1301 static enum command_return
1302 handle_seek(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
1304 int song, seek_time;
1305 enum playlist_result result;
1307 if (!check_int(client, &song, argv[1], check_integer, argv[1]))
1308 return COMMAND_RETURN_ERROR;
1309 if (!check_int(client, &seek_time, argv[2], check_integer, argv[2]))
1310 return COMMAND_RETURN_ERROR;
1312 result = playlist_seek_song(&g_playlist, song, seek_time);
1313 return print_playlist_result(client, result);
1316 static enum command_return
1317 handle_seekid(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
1319 int id, seek_time;
1320 enum playlist_result result;
1322 if (!check_int(client, &id, argv[1], check_integer, argv[1]))
1323 return COMMAND_RETURN_ERROR;
1324 if (!check_int(client, &seek_time, argv[2], check_integer, argv[2]))
1325 return COMMAND_RETURN_ERROR;
1327 result = playlist_seek_song_id(&g_playlist, id, seek_time);
1328 return print_playlist_result(client, result);
1331 static enum command_return
1332 handle_listallinfo(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
1334 char *directory = NULL;
1335 int ret;
1337 if (argc == 2)
1338 directory = argv[1];
1340 ret = printInfoForAllIn(client, directory);
1341 if (ret == -1)
1342 command_error(client, ACK_ERROR_NO_EXIST,
1343 "directory or file not found");
1345 return ret;
1348 static enum command_return
1349 handle_ping(G_GNUC_UNUSED struct client *client,
1350 G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
1352 return COMMAND_RETURN_OK;
1355 static enum command_return
1356 handle_password(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
1358 unsigned permission = 0;
1360 if (getPermissionFromPassword(argv[1], &permission) < 0) {
1361 command_error(client, ACK_ERROR_PASSWORD, "incorrect password");
1362 return COMMAND_RETURN_ERROR;
1365 client_set_permission(client, permission);
1367 return COMMAND_RETURN_OK;
1370 static enum command_return
1371 handle_crossfade(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
1373 unsigned xfade_time;
1375 if (!check_unsigned(client, &xfade_time, argv[1]))
1376 return COMMAND_RETURN_ERROR;
1377 setPlayerCrossFade(xfade_time);
1379 return COMMAND_RETURN_OK;
1382 static enum command_return
1383 handle_enableoutput(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
1385 unsigned device;
1386 bool ret;
1388 if (!check_unsigned(client, &device, argv[1]))
1389 return COMMAND_RETURN_ERROR;
1391 ret = audio_output_enable_index(device);
1392 if (!ret) {
1393 command_error(client, ACK_ERROR_NO_EXIST,
1394 "No such audio output");
1395 return COMMAND_RETURN_ERROR;
1398 return COMMAND_RETURN_OK;
1401 static enum command_return
1402 handle_disableoutput(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
1404 unsigned device;
1405 bool ret;
1407 if (!check_unsigned(client, &device, argv[1]))
1408 return COMMAND_RETURN_ERROR;
1410 ret = audio_output_disable_index(device);
1411 if (!ret) {
1412 command_error(client, ACK_ERROR_NO_EXIST,
1413 "No such audio output");
1414 return COMMAND_RETURN_ERROR;
1417 return COMMAND_RETURN_OK;
1420 static enum command_return
1421 handle_devices(struct client *client,
1422 G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
1424 printAudioDevices(client);
1426 return COMMAND_RETURN_OK;
1429 /* don't be fooled, this is the command handler for "commands" command */
1430 static enum command_return
1431 handle_commands(struct client *client,
1432 G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[]);
1434 static enum command_return
1435 handle_not_commands(struct client *client,
1436 G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[]);
1438 static enum command_return
1439 handle_playlistclear(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
1441 enum playlist_result result;
1443 result = spl_clear(argv[1]);
1444 return print_playlist_result(client, result);
1447 static enum command_return
1448 handle_playlistadd(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
1450 char *playlist = argv[1];
1451 char *uri = argv[2];
1452 enum playlist_result result;
1454 if (uri_has_scheme(uri)) {
1455 if (!uri_supported_scheme(uri)) {
1456 command_error(client, ACK_ERROR_NO_EXIST,
1457 "unsupported URI scheme");
1458 return COMMAND_RETURN_ERROR;
1461 result = spl_append_uri(uri, playlist);
1462 } else
1463 result = addAllInToStoredPlaylist(uri, playlist);
1465 if (result == (enum playlist_result)-1) {
1466 command_error(client, ACK_ERROR_NO_EXIST,
1467 "directory or file not found");
1468 return COMMAND_RETURN_ERROR;
1471 return print_playlist_result(client, result);
1474 static enum command_return
1475 handle_listplaylists(struct client *client,
1476 G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
1478 GPtrArray *list = spl_list();
1479 if (list == NULL) {
1480 command_error(client, ACK_ERROR_SYSTEM,
1481 "failed to get list of stored playlists");
1482 return COMMAND_RETURN_ERROR;
1485 print_spl_list(client, list);
1486 spl_list_free(list);
1487 return COMMAND_RETURN_OK;
1490 static enum command_return
1491 handle_idle(struct client *client,
1492 G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
1494 unsigned flags = 0, j;
1495 int i;
1496 const char *const* idle_names;
1498 idle_names = idle_get_names();
1499 for (i = 1; i < argc; ++i) {
1500 if (!argv[i])
1501 continue;
1503 for (j = 0; idle_names[j]; ++j) {
1504 if (!g_ascii_strcasecmp(argv[i], idle_names[j])) {
1505 flags |= (1 << j);
1510 /* No argument means that the client wants to receive everything */
1511 if (flags == 0)
1512 flags = ~0;
1514 /* enable "idle" mode on this client */
1515 client_idle_wait(client, flags);
1517 /* return value is "1" so the caller won't print "OK" */
1518 return 1;
1521 #ifdef ENABLE_SQLITE
1522 struct sticker_song_find_data {
1523 struct client *client;
1524 const char *name;
1527 static void
1528 sticker_song_find_print_cb(struct song *song, const char *value,
1529 gpointer user_data)
1531 struct sticker_song_find_data *data = user_data;
1533 song_print_url(data->client, song);
1534 sticker_print_value(data->client, data->name, value);
1537 static enum command_return
1538 handle_sticker_song(struct client *client, int argc, char *argv[])
1540 if (argc == 5 && strcmp(argv[1], "get") == 0) {
1541 struct song *song;
1542 char *value;
1544 song = db_get_song(argv[3]);
1545 if (song == NULL) {
1546 command_error(client, ACK_ERROR_NO_EXIST,
1547 "no such song");
1548 return COMMAND_RETURN_ERROR;
1551 value = sticker_song_get_value(song, argv[4]);
1552 if (value == NULL) {
1553 command_error(client, ACK_ERROR_NO_EXIST,
1554 "no such sticker");
1555 return COMMAND_RETURN_ERROR;
1558 sticker_print_value(client, argv[4], value);
1559 g_free(value);
1561 return COMMAND_RETURN_OK;
1562 } else if (argc == 4 && strcmp(argv[1], "list") == 0) {
1563 struct song *song;
1564 struct sticker *sticker;
1566 song = db_get_song(argv[3]);
1567 if (song == NULL) {
1568 command_error(client, ACK_ERROR_NO_EXIST,
1569 "no such song");
1570 return COMMAND_RETURN_ERROR;
1573 sticker = sticker_song_get(song);
1574 if (NULL == sticker) {
1575 command_error(client, ACK_ERROR_NO_EXIST,
1576 "no stickers found");
1577 return COMMAND_RETURN_ERROR;
1580 sticker_print(client, sticker);
1581 sticker_free(sticker);
1583 return COMMAND_RETURN_OK;
1584 } else if (argc == 6 && strcmp(argv[1], "set") == 0) {
1585 struct song *song;
1586 bool ret;
1588 song = db_get_song(argv[3]);
1589 if (song == NULL) {
1590 command_error(client, ACK_ERROR_NO_EXIST,
1591 "no such song");
1592 return COMMAND_RETURN_ERROR;
1595 ret = sticker_song_set_value(song, argv[4], argv[5]);
1596 if (!ret) {
1597 command_error(client, ACK_ERROR_SYSTEM,
1598 "failed to set sticker value");
1599 return COMMAND_RETURN_ERROR;
1602 return COMMAND_RETURN_OK;
1603 } else if ((argc == 4 || argc == 5) &&
1604 strcmp(argv[1], "delete") == 0) {
1605 struct song *song;
1606 bool ret;
1608 song = db_get_song(argv[3]);
1609 if (song == NULL) {
1610 command_error(client, ACK_ERROR_NO_EXIST,
1611 "no such song");
1612 return COMMAND_RETURN_ERROR;
1615 ret = argc == 4
1616 ? sticker_song_delete(song)
1617 : sticker_song_delete_value(song, argv[4]);
1618 if (!ret) {
1619 command_error(client, ACK_ERROR_SYSTEM,
1620 "no such sticker");
1621 return COMMAND_RETURN_ERROR;
1624 return COMMAND_RETURN_OK;
1625 } else if (argc == 5 && strcmp(argv[1], "find") == 0) {
1626 /* "sticker find song a/directory name" */
1627 struct directory *directory;
1628 bool success;
1629 struct sticker_song_find_data data = {
1630 .client = client,
1631 .name = argv[4],
1634 directory = db_get_directory(argv[3]);
1635 if (directory == NULL) {
1636 command_error(client, ACK_ERROR_NO_EXIST,
1637 "no such directory");
1638 return COMMAND_RETURN_ERROR;
1641 success = sticker_song_find(directory, data.name,
1642 sticker_song_find_print_cb, &data);
1643 if (!success) {
1644 command_error(client, ACK_ERROR_SYSTEM,
1645 "failed to set search sticker database");
1646 return COMMAND_RETURN_ERROR;
1649 return COMMAND_RETURN_OK;
1650 } else {
1651 command_error(client, ACK_ERROR_ARG, "bad request");
1652 return COMMAND_RETURN_ERROR;
1656 static enum command_return
1657 handle_sticker(struct client *client, int argc, char *argv[])
1659 assert(argc >= 4);
1661 if (!sticker_enabled()) {
1662 command_error(client, ACK_ERROR_UNKNOWN,
1663 "sticker database is disabled");
1664 return COMMAND_RETURN_ERROR;
1667 if (strcmp(argv[2], "song") == 0)
1668 return handle_sticker_song(client, argc, argv);
1669 else {
1670 command_error(client, ACK_ERROR_ARG,
1671 "unknown sticker domain");
1672 return COMMAND_RETURN_ERROR;
1675 #endif
1678 * The command registry.
1680 * This array must be sorted!
1682 static const struct command commands[] = {
1683 { "add", PERMISSION_ADD, 1, 1, handle_add },
1684 { "addid", PERMISSION_ADD, 1, 2, handle_addid },
1685 { "clear", PERMISSION_CONTROL, 0, 0, handle_clear },
1686 { "clearerror", PERMISSION_CONTROL, 0, 0, handle_clearerror },
1687 { "close", PERMISSION_NONE, -1, -1, handle_close },
1688 { "commands", PERMISSION_NONE, 0, 0, handle_commands },
1689 { "consume", PERMISSION_CONTROL, 1, 1, handle_consume },
1690 { "count", PERMISSION_READ, 2, -1, handle_count },
1691 { "crossfade", PERMISSION_CONTROL, 1, 1, handle_crossfade },
1692 { "currentsong", PERMISSION_READ, 0, 0, handle_currentsong },
1693 { "delete", PERMISSION_CONTROL, 1, 1, handle_delete },
1694 { "deleteid", PERMISSION_CONTROL, 1, 1, handle_deleteid },
1695 { "disableoutput", PERMISSION_ADMIN, 1, 1, handle_disableoutput },
1696 { "enableoutput", PERMISSION_ADMIN, 1, 1, handle_enableoutput },
1697 { "find", PERMISSION_READ, 2, -1, handle_find },
1698 { "findadd", PERMISSION_READ, 2, -1, handle_findadd},
1699 { "idle", PERMISSION_READ, 0, -1, handle_idle },
1700 { "kill", PERMISSION_ADMIN, -1, -1, handle_kill },
1701 { "list", PERMISSION_READ, 1, -1, handle_list },
1702 { "listall", PERMISSION_READ, 0, 1, handle_listall },
1703 { "listallinfo", PERMISSION_READ, 0, 1, handle_listallinfo },
1704 { "listplaylist", PERMISSION_READ, 1, 1, handle_listplaylist },
1705 { "listplaylistinfo", PERMISSION_READ, 1, 1, handle_listplaylistinfo },
1706 { "listplaylists", PERMISSION_READ, 0, 0, handle_listplaylists },
1707 { "load", PERMISSION_ADD, 1, 1, handle_load },
1708 { "lsinfo", PERMISSION_READ, 0, 1, handle_lsinfo },
1709 { "move", PERMISSION_CONTROL, 2, 2, handle_move },
1710 { "moveid", PERMISSION_CONTROL, 2, 2, handle_moveid },
1711 { "next", PERMISSION_CONTROL, 0, 0, handle_next },
1712 { "notcommands", PERMISSION_NONE, 0, 0, handle_not_commands },
1713 { "outputs", PERMISSION_READ, 0, 0, handle_devices },
1714 { "password", PERMISSION_NONE, 1, 1, handle_password },
1715 { "pause", PERMISSION_CONTROL, 0, 1, handle_pause },
1716 { "ping", PERMISSION_NONE, 0, 0, handle_ping },
1717 { "play", PERMISSION_CONTROL, 0, 1, handle_play },
1718 { "playid", PERMISSION_CONTROL, 0, 1, handle_playid },
1719 { "playlist", PERMISSION_READ, 0, 0, handle_playlist },
1720 { "playlistadd", PERMISSION_CONTROL, 2, 2, handle_playlistadd },
1721 { "playlistclear", PERMISSION_CONTROL, 1, 1, handle_playlistclear },
1722 { "playlistdelete", PERMISSION_CONTROL, 2, 2, handle_playlistdelete },
1723 { "playlistfind", PERMISSION_READ, 2, -1, handle_playlistfind },
1724 { "playlistid", PERMISSION_READ, 0, 1, handle_playlistid },
1725 { "playlistinfo", PERMISSION_READ, 0, 1, handle_playlistinfo },
1726 { "playlistmove", PERMISSION_CONTROL, 3, 3, handle_playlistmove },
1727 { "playlistsearch", PERMISSION_READ, 2, -1, handle_playlistsearch },
1728 { "plchanges", PERMISSION_READ, 1, 1, handle_plchanges },
1729 { "plchangesposid", PERMISSION_READ, 1, 1, handle_plchangesposid },
1730 { "previous", PERMISSION_CONTROL, 0, 0, handle_previous },
1731 { "random", PERMISSION_CONTROL, 1, 1, handle_random },
1732 { "rename", PERMISSION_CONTROL, 2, 2, handle_rename },
1733 { "repeat", PERMISSION_CONTROL, 1, 1, handle_repeat },
1734 { "rm", PERMISSION_CONTROL, 1, 1, handle_rm },
1735 { "save", PERMISSION_CONTROL, 1, 1, handle_save },
1736 { "search", PERMISSION_READ, 2, -1, handle_search },
1737 { "seek", PERMISSION_CONTROL, 2, 2, handle_seek },
1738 { "seekid", PERMISSION_CONTROL, 2, 2, handle_seekid },
1739 { "setvol", PERMISSION_CONTROL, 1, 1, handle_setvol },
1740 { "shuffle", PERMISSION_CONTROL, 0, 1, handle_shuffle },
1741 { "single", PERMISSION_CONTROL, 1, 1, handle_single },
1742 { "stats", PERMISSION_READ, 0, 0, handle_stats },
1743 { "status", PERMISSION_READ, 0, 0, handle_status },
1744 #ifdef ENABLE_SQLITE
1745 { "sticker", PERMISSION_ADMIN, 3, -1, handle_sticker },
1746 #endif
1747 { "stop", PERMISSION_CONTROL, 0, 0, handle_stop },
1748 { "swap", PERMISSION_CONTROL, 2, 2, handle_swap },
1749 { "swapid", PERMISSION_CONTROL, 2, 2, handle_swapid },
1750 { "tagtypes", PERMISSION_READ, 0, 0, handle_tagtypes },
1751 { "update", PERMISSION_ADMIN, 0, 1, handle_update },
1752 { "urlhandlers", PERMISSION_READ, 0, 0, handle_urlhandlers },
1755 static const unsigned num_commands = sizeof(commands) / sizeof(commands[0]);
1757 static bool
1758 command_available(G_GNUC_UNUSED const struct command *cmd)
1760 #ifdef ENABLE_SQLITE
1761 if (strcmp(cmd->cmd, "sticker") == 0)
1762 return sticker_enabled();
1763 #endif
1765 return true;
1768 /* don't be fooled, this is the command handler for "commands" command */
1769 static enum command_return
1770 handle_commands(struct client *client,
1771 G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
1773 const unsigned permission = client_get_permission(client);
1774 const struct command *cmd;
1776 for (unsigned i = 0; i < num_commands; ++i) {
1777 cmd = &commands[i];
1779 if (cmd->permission == (permission & cmd->permission) &&
1780 command_available(cmd))
1781 client_printf(client, "command: %s\n", cmd->cmd);
1784 return COMMAND_RETURN_OK;
1787 static enum command_return
1788 handle_not_commands(struct client *client,
1789 G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
1791 const unsigned permission = client_get_permission(client);
1792 const struct command *cmd;
1794 for (unsigned i = 0; i < num_commands; ++i) {
1795 cmd = &commands[i];
1797 if (cmd->permission != (permission & cmd->permission))
1798 client_printf(client, "command: %s\n", cmd->cmd);
1801 return COMMAND_RETURN_OK;
1804 void command_init(void)
1806 #ifndef NDEBUG
1807 /* ensure that the command list is sorted */
1808 for (unsigned i = 0; i < num_commands - 1; ++i)
1809 assert(strcmp(commands[i].cmd, commands[i + 1].cmd) < 0);
1810 #endif
1813 void command_finish(void)
1817 static const struct command *
1818 command_lookup(const char *name)
1820 unsigned a = 0, b = num_commands, i;
1821 int cmp;
1823 /* binary search */
1824 do {
1825 i = (a + b) / 2;
1827 cmp = strcmp(name, commands[i].cmd);
1828 if (cmp == 0)
1829 return &commands[i];
1830 else if (cmp < 0)
1831 b = i;
1832 else if (cmp > 0)
1833 a = i + 1;
1834 } while (a < b);
1836 return NULL;
1839 static bool
1840 command_check_request(const struct command *cmd, struct client *client,
1841 unsigned permission, int argc, char *argv[])
1843 int min = cmd->min + 1;
1844 int max = cmd->max + 1;
1846 if (cmd->permission != (permission & cmd->permission)) {
1847 if (client != NULL)
1848 command_error(client, ACK_ERROR_PERMISSION,
1849 "you don't have permission for \"%s\"",
1850 cmd->cmd);
1851 return false;
1854 if (min == 0)
1855 return true;
1857 if (min == max && max != argc) {
1858 if (client != NULL)
1859 command_error(client, ACK_ERROR_ARG,
1860 "wrong number of arguments for \"%s\"",
1861 argv[0]);
1862 return false;
1863 } else if (argc < min) {
1864 if (client != NULL)
1865 command_error(client, ACK_ERROR_ARG,
1866 "too few arguments for \"%s\"", argv[0]);
1867 return false;
1868 } else if (argc > max && max /* != 0 */ ) {
1869 if (client != NULL)
1870 command_error(client, ACK_ERROR_ARG,
1871 "too many arguments for \"%s\"", argv[0]);
1872 return false;
1873 } else
1874 return true;
1877 static const struct command *
1878 command_checked_lookup(struct client *client, unsigned permission,
1879 int argc, char *argv[])
1881 static char unknown[] = "";
1882 const struct command *cmd;
1884 current_command = unknown;
1886 if (argc == 0)
1887 return NULL;
1889 cmd = command_lookup(argv[0]);
1890 if (cmd == NULL) {
1891 if (client != NULL)
1892 command_error(client, ACK_ERROR_UNKNOWN,
1893 "unknown command \"%s\"", argv[0]);
1894 return NULL;
1897 current_command = cmd->cmd;
1899 if (!command_check_request(cmd, client, permission, argc, argv))
1900 return NULL;
1902 return cmd;
1905 enum command_return
1906 command_process(struct client *client, unsigned num, char *line)
1908 GError *error = NULL;
1909 int argc;
1910 char *argv[COMMAND_ARGV_MAX] = { NULL };
1911 const struct command *cmd;
1912 enum command_return ret = COMMAND_RETURN_ERROR;
1914 command_list_num = num;
1916 /* get the command name (first word on the line) */
1918 argv[0] = tokenizer_next_word(&line, &error);
1919 if (argv[0] == NULL) {
1920 current_command = "";
1921 if (*line == 0)
1922 command_error(client, ACK_ERROR_UNKNOWN,
1923 "No command given");
1924 else {
1925 command_error(client, ACK_ERROR_UNKNOWN,
1926 "%s", error->message);
1927 g_error_free(error);
1929 current_command = NULL;
1931 return COMMAND_RETURN_ERROR;
1934 argc = 1;
1936 /* now parse the arguments (quoted or unquoted) */
1938 while (argc < (int)G_N_ELEMENTS(argv) &&
1939 (argv[argc] =
1940 tokenizer_next_word_or_string(&line, &error)) != NULL)
1941 ++argc;
1943 /* some error checks; we have to set current_command because
1944 command_error() expects it to be set */
1946 current_command = argv[0];
1948 if (argc >= (int)G_N_ELEMENTS(argv)) {
1949 command_error(client, ACK_ERROR_ARG, "Too many arguments");
1950 current_command = NULL;
1951 return COMMAND_RETURN_ERROR;
1954 if (*line != 0) {
1955 command_error(client, ACK_ERROR_ARG,
1956 "%s", error->message);
1957 current_command = NULL;
1958 g_error_free(error);
1959 return COMMAND_RETURN_ERROR;
1962 /* look up and invoke the command handler */
1964 cmd = command_checked_lookup(client, client_get_permission(client),
1965 argc, argv);
1966 if (cmd)
1967 ret = cmd->handler(client, argc, argv);
1969 current_command = NULL;
1970 command_list_num = 0;
1972 return ret;