Initial revision 6759
[qball-mpd.git] / src / command.c
blob83a8d3a2d84aad8621c7d2bf6aeec4f356de5612
1 /* the Music Player Daemon (MPD)
2 * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
3 * This project's homepage is: 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.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include "command.h"
20 #include "player.h"
21 #include "playlist.h"
22 #include "ls.h"
23 #include "directory.h"
24 #include "volume.h"
25 #include "stats.h"
26 #include "myfprintf.h"
27 #include "list.h"
28 #include "permission.h"
29 #include "buffer2array.h"
30 #include "log.h"
31 #include "tag.h"
32 #include "utils.h"
33 #include "storedPlaylist.h"
35 #include <assert.h>
36 #include <stdarg.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
41 #define COMMAND_PLAY "play"
42 #define COMMAND_PLAYID "playid"
43 #define COMMAND_STOP "stop"
44 #define COMMAND_PAUSE "pause"
45 #define COMMAND_STATUS "status"
46 #define COMMAND_KILL "kill"
47 #define COMMAND_CLOSE "close"
48 #define COMMAND_ADD "add"
49 #define COMMAND_ADDID "addid"
50 #define COMMAND_DELETE "delete"
51 #define COMMAND_DELETEID "deleteid"
52 #define COMMAND_PLAYLIST "playlist"
53 #define COMMAND_SHUFFLE "shuffle"
54 #define COMMAND_CLEAR "clear"
55 #define COMMAND_SAVE "save"
56 #define COMMAND_LOAD "load"
57 #define COMMAND_LISTPLAYLIST "listplaylist"
58 #define COMMAND_LISTPLAYLISTINFO "listplaylistinfo"
59 #define COMMAND_LSINFO "lsinfo"
60 #define COMMAND_RM "rm"
61 #define COMMAND_PLAYLISTINFO "playlistinfo"
62 #define COMMAND_PLAYLISTID "playlistid"
63 #define COMMAND_FIND "find"
64 #define COMMAND_SEARCH "search"
65 #define COMMAND_UPDATE "update"
66 #define COMMAND_NEXT "next"
67 #define COMMAND_PREVIOUS "previous"
68 #define COMMAND_LISTALL "listall"
69 #define COMMAND_VOLUME "volume"
70 #define COMMAND_REPEAT "repeat"
71 #define COMMAND_RANDOM "random"
72 #define COMMAND_STATS "stats"
73 #define COMMAND_CLEAR_ERROR "clearerror"
74 #define COMMAND_LIST "list"
75 #define COMMAND_MOVE "move"
76 #define COMMAND_MOVEID "moveid"
77 #define COMMAND_SWAP "swap"
78 #define COMMAND_SWAPID "swapid"
79 #define COMMAND_SEEK "seek"
80 #define COMMAND_SEEKID "seekid"
81 #define COMMAND_LISTALLINFO "listallinfo"
82 #define COMMAND_PING "ping"
83 #define COMMAND_SETVOL "setvol"
84 #define COMMAND_PASSWORD "password"
85 #define COMMAND_CROSSFADE "crossfade"
86 #define COMMAND_URL_HANDLERS "urlhandlers"
87 #define COMMAND_PLCHANGES "plchanges"
88 #define COMMAND_PLCHANGESPOSID "plchangesposid"
89 #define COMMAND_CURRENTSONG "currentsong"
90 #define COMMAND_ENABLE_DEV "enableoutput"
91 #define COMMAND_DISABLE_DEV "disableoutput"
92 #define COMMAND_DEVICES "outputs"
93 #define COMMAND_COMMANDS "commands"
94 #define COMMAND_NOTCOMMANDS "notcommands"
95 #define COMMAND_PLAYLISTCLEAR "playlistclear"
96 #define COMMAND_PLAYLISTADD "playlistadd"
97 #define COMMAND_PLAYLISTFIND "playlistfind"
98 #define COMMAND_PLAYLISTSEARCH "playlistsearch"
99 #define COMMAND_PLAYLISTMOVE "playlistmove"
100 #define COMMAND_PLAYLISTDELETE "playlistdelete"
101 #define COMMAND_QUEUEID "queueid"
102 #define COMMAND_DEQUEUE "dequeue"
103 #define COMMAND_QUEUEINFO "queueinfo"
104 #define COMMAND_TAGTYPES "tagtypes"
105 #define COMMAND_COUNT "count"
106 #define COMMAND_RENAME "rename"
108 #define COMMAND_STATUS_VOLUME "volume"
109 #define COMMAND_STATUS_STATE "state"
110 #define COMMAND_STATUS_REPEAT "repeat"
111 #define COMMAND_STATUS_RANDOM "random"
112 #define COMMAND_STATUS_PLAYLIST "playlist"
113 #define COMMAND_STATUS_PLAYLIST_QUEUE "playlistqueue"
114 #define COMMAND_STATUS_PLAYLIST_LENGTH "playlistlength"
115 #define COMMAND_STATUS_SONG "song"
116 #define COMMAND_STATUS_SONGID "songid"
117 #define COMMAND_STATUS_TIME "time"
118 #define COMMAND_STATUS_BITRATE "bitrate"
119 #define COMMAND_STATUS_ERROR "error"
120 #define COMMAND_STATUS_CROSSFADE "xfade"
121 #define COMMAND_STATUS_AUDIO "audio"
122 #define COMMAND_STATUS_UPDATING_DB "updating_db"
125 * The most we ever use is for search/find, and that limits it to the
126 * number of tags we can have. Add one for the command, and one extra
127 * to catch errors clients may send us
129 #define COMMAND_ARGV_MAX (2+(TAG_NUM_OF_ITEM_TYPES*2))
131 typedef struct _CommandEntry CommandEntry;
133 typedef int (*CommandHandlerFunction) (int, int *, int, char **);
134 typedef int (*CommandListHandlerFunction)
135 (int, int *, int, char **, struct strnode *, CommandEntry *);
137 /* if min: -1 don't check args *
138 * if max: -1 no max args */
139 struct _CommandEntry {
140 char *cmd;
141 int min;
142 int max;
143 int reqPermission;
144 CommandHandlerFunction handler;
145 CommandListHandlerFunction listHandler;
148 static char *current_command;
149 static int command_listNum;
151 static CommandEntry *getCommandEntryFromString(char *string, int *permission);
153 static List *commandList;
155 static CommandEntry *newCommandEntry(void)
157 CommandEntry *cmd = xmalloc(sizeof(CommandEntry));
158 cmd->cmd = NULL;
159 cmd->min = 0;
160 cmd->max = 0;
161 cmd->handler = NULL;
162 cmd->listHandler = NULL;
163 cmd->reqPermission = 0;
164 return cmd;
167 static void addCommand(char *name,
168 int reqPermission,
169 int minargs,
170 int maxargs,
171 CommandHandlerFunction handler_func,
172 CommandListHandlerFunction listHandler_func)
174 CommandEntry *cmd = newCommandEntry();
175 cmd->cmd = name;
176 cmd->min = minargs;
177 cmd->max = maxargs;
178 cmd->handler = handler_func;
179 cmd->listHandler = listHandler_func;
180 cmd->reqPermission = reqPermission;
182 insertInList(commandList, cmd->cmd, cmd);
185 static int handleUrlHandlers(int fd, int *permission, int argc, char *argv[])
187 return printRemoteUrlHandlers(fd);
190 static int handleTagTypes(int fd, int *permission, int argc, char *argv[])
192 printTagTypes(fd);
193 return 0;
196 static int handlePlay(int fd, int *permission, int argc, char *argv[])
198 int song = -1;
199 char *test;
201 if (argc == 2) {
202 song = strtol(argv[1], &test, 10);
203 if (*test != '\0') {
204 commandError(fd, ACK_ERROR_ARG,
205 "need a positive integer");
206 return -1;
209 return playPlaylist(fd, song, 0);
212 static int handlePlayId(int fd, int *permission, int argc, char *argv[])
214 int id = -1;
215 char *test;
217 if (argc == 2) {
218 id = strtol(argv[1], &test, 10);
219 if (*test != '\0') {
220 commandError(fd, ACK_ERROR_ARG,
221 "need a positive integer");
222 return -1;
225 return playPlaylistById(fd, id, 0);
228 static int handleStop(int fd, int *permission, int argc, char *argv[])
230 return stopPlaylist(fd);
233 static int handleCurrentSong(int fd, int *permission, int argc, char *argv[])
235 int song = getPlaylistCurrentSong();
237 if (song >= 0) {
238 return playlistInfo(fd, song);
239 } else
240 return 0;
243 static int handlePause(int fd, int *permission, int argc, char *argv[])
245 if (argc == 2) {
246 char *test;
247 int pause = strtol(argv[1], &test, 10);
248 if (*test != '\0' || (pause != 0 && pause != 1)) {
249 commandError(fd, ACK_ERROR_ARG, "\"%s\" is not 0 or 1",
250 argv[1]);
251 return -1;
253 return playerSetPause(fd, pause);
255 return playerPause(fd);
258 static int commandStatus(int fd, int *permission, int argc, char *argv[])
260 char *state = NULL;
261 int updateJobId;
262 int song;
264 /*syncPlayerAndPlaylist(); */
265 playPlaylistIfPlayerStopped();
266 switch (getPlayerState()) {
267 case PLAYER_STATE_STOP:
268 state = COMMAND_STOP;
269 break;
270 case PLAYER_STATE_PAUSE:
271 state = COMMAND_PAUSE;
272 break;
273 case PLAYER_STATE_PLAY:
274 state = COMMAND_PLAY;
275 break;
278 fdprintf(fd, "%s: %i\n", COMMAND_STATUS_VOLUME, getVolumeLevel());
279 fdprintf(fd, "%s: %i\n", COMMAND_STATUS_REPEAT,
280 getPlaylistRepeatStatus());
281 fdprintf(fd, "%s: %i\n", COMMAND_STATUS_RANDOM,
282 getPlaylistRandomStatus());
283 fdprintf(fd, "%s: %li\n", COMMAND_STATUS_PLAYLIST,
284 getPlaylistVersion());
285 fdprintf(fd, "%s: %li\n", COMMAND_STATUS_PLAYLIST_QUEUE,
286 getPlaylistQueueVersion());
287 fdprintf(fd, "%s: %i\n", COMMAND_STATUS_PLAYLIST_LENGTH,
288 getPlaylistLength());
289 fdprintf(fd, "%s: %i\n", COMMAND_STATUS_CROSSFADE,
290 (int)(getPlayerCrossFade() + 0.5));
292 fdprintf(fd, "%s: %s\n", COMMAND_STATUS_STATE, state);
294 song = getPlaylistCurrentSong();
295 if (song >= 0) {
296 fdprintf(fd, "%s: %i\n", COMMAND_STATUS_SONG, song);
297 fdprintf(fd, "%s: %i\n", COMMAND_STATUS_SONGID,
298 getPlaylistSongId(song));
300 if (getPlayerState() != PLAYER_STATE_STOP) {
301 fdprintf(fd, "%s: %i:%i\n", COMMAND_STATUS_TIME,
302 getPlayerElapsedTime(), getPlayerTotalTime());
303 fdprintf(fd, "%s: %li\n", COMMAND_STATUS_BITRATE,
304 getPlayerBitRate());
305 fdprintf(fd, "%s: %u:%i:%i\n", COMMAND_STATUS_AUDIO,
306 getPlayerSampleRate(), getPlayerBits(),
307 getPlayerChannels());
310 if ((updateJobId = isUpdatingDB())) {
311 fdprintf(fd, "%s: %i\n", COMMAND_STATUS_UPDATING_DB,
312 updateJobId);
315 if (getPlayerError() != PLAYER_ERROR_NOERROR) {
316 fdprintf(fd, "%s: %s\n", COMMAND_STATUS_ERROR,
317 getPlayerErrorStr());
320 return 0;
323 static int handleKill(int fd, int *permission, int argc, char *argv[])
325 return COMMAND_RETURN_KILL;
328 static int handleClose(int fd, int *permission, int argc, char *argv[])
330 return COMMAND_RETURN_CLOSE;
333 static int handleAdd(int fd, int *permission, int argc, char *argv[])
335 char *path = argv[1];
337 if (isRemoteUrl(path))
338 return addToPlaylist(fd, path, 0);
340 return addAllIn(fd, path);
343 static int handleAddId(int fd, int *permission, int argc, char *argv[])
345 return addToPlaylist(fd, argv[1], 1);
348 static int handleDelete(int fd, int *permission, int argc, char *argv[])
350 int song;
351 char *test;
353 song = strtol(argv[1], &test, 10);
354 if (*test != '\0') {
355 commandError(fd, ACK_ERROR_ARG, "need a positive integer");
356 return -1;
358 return deleteFromPlaylist(fd, song);
361 static int handleDeleteId(int fd, int *permission, int argc, char *argv[])
363 int id;
364 char *test;
366 id = strtol(argv[1], &test, 10);
367 if (*test != '\0') {
368 commandError(fd, ACK_ERROR_ARG, "need a positive integer");
369 return -1;
371 return deleteFromPlaylistById(fd, id);
374 static int handlePlaylist(int fd, int *permission, int argc, char *argv[])
376 return showPlaylist(fd);
379 static int handleShuffle(int fd, int *permission, int argc, char *argv[])
381 return shufflePlaylist(fd);
384 static int handleClear(int fd, int *permission, int argc, char *argv[])
386 return clearPlaylist(fd);
389 static int handleSave(int fd, int *permission, int argc, char *argv[])
391 return savePlaylist(fd, argv[1]);
394 static int handleLoad(int fd, int *permission, int argc, char *argv[])
396 return loadPlaylist(fd, argv[1]);
399 static int handleListPlaylist(int fd, int *permission, int argc, char *argv[])
401 return PlaylistInfo(fd, argv[1], 0);
404 static int handleListPlaylistInfo(int fd, int *permission,
405 int argc, char *argv[])
407 return PlaylistInfo(fd, argv[1], 1);
410 static int handleLsInfo(int fd, int *permission, int argc, char *argv[])
412 char *path = "";
414 if (argc == 2)
415 path = argv[1];
417 if (printDirectoryInfo(fd, path) < 0)
418 return -1;
420 if (isRootDirectory(path))
421 return lsPlaylists(fd, path);
423 return 0;
426 static int handleRm(int fd, int *permission, int argc, char *argv[])
428 return deletePlaylist(fd, argv[1]);
431 static int handleRename(int fd, int *permission, int argc, char *argv[])
433 return renameStoredPlaylist(fd, argv[1], argv[2]);
436 static int handlePlaylistChanges(int fd, int *permission,
437 int argc, char *argv[])
439 unsigned long version;
440 char *test;
442 version = strtoul(argv[1], &test, 10);
443 if (*test != '\0') {
444 commandError(fd, ACK_ERROR_ARG, "need a positive integer");
445 return -1;
447 return playlistChanges(fd, version);
450 static int handlePlaylistChangesPosId(int fd, int *permission,
451 int argc, char *argv[])
453 unsigned long version;
454 char *test;
456 version = strtoul(argv[1], &test, 10);
457 if (*test != '\0') {
458 commandError(fd, ACK_ERROR_ARG, "need a positive integer");
459 return -1;
461 return playlistChangesPosId(fd, version);
464 static int handlePlaylistInfo(int fd, int *permission, int argc, char *argv[])
466 int song = -1;
467 char *test;
469 if (argc == 2) {
470 song = strtol(argv[1], &test, 10);
471 if (*test != '\0') {
472 commandError(fd, ACK_ERROR_ARG,
473 "need a positive integer");
474 return -1;
477 return playlistInfo(fd, song);
480 static int handlePlaylistId(int fd, int *permission, int argc, char *argv[])
482 int id = -1;
483 char *test;
485 if (argc == 2) {
486 id = strtol(argv[1], &test, 10);
487 if (*test != '\0') {
488 commandError(fd, ACK_ERROR_ARG,
489 "need a positive integer");
490 return -1;
493 return playlistId(fd, id);
496 static int handleFind(int fd, int *permission, int argc, char *argv[])
498 int ret;
500 LocateTagItem *items;
501 int numItems = newLocateTagItemArrayFromArgArray(argv + 1,
502 argc - 1,
503 &items);
505 if (numItems <= 0) {
506 commandError(fd, ACK_ERROR_ARG, "incorrect arguments");
507 return -1;
510 ret = findSongsIn(fd, NULL, numItems, items);
512 freeLocateTagItemArray(numItems, items);
514 return ret;
517 static int handleSearch(int fd, int *permission, int argc, char *argv[])
519 int ret;
521 LocateTagItem *items;
522 int numItems = newLocateTagItemArrayFromArgArray(argv + 1,
523 argc - 1,
524 &items);
526 if (numItems <= 0) {
527 commandError(fd, ACK_ERROR_ARG, "incorrect arguments");
528 return -1;
531 ret = searchForSongsIn(fd, NULL, numItems, items);
533 freeLocateTagItemArray(numItems, items);
535 return ret;
538 static int handleCount(int fd, int *permission, int argc, char *argv[])
540 int ret;
542 LocateTagItem *items;
543 int numItems = newLocateTagItemArrayFromArgArray(argv + 1,
544 argc - 1,
545 &items);
547 if (numItems <= 0) {
548 commandError(fd, ACK_ERROR_ARG, "incorrect arguments");
549 return -1;
552 ret = searchStatsForSongsIn(fd, NULL, numItems, items);
554 freeLocateTagItemArray(numItems, items);
556 return ret;
559 static int handlePlaylistFind(int fd, int *permission, int argc, char *argv[])
561 LocateTagItem *items;
562 int numItems = newLocateTagItemArrayFromArgArray(argv + 1,
563 argc - 1,
564 &items);
566 if (numItems <= 0) {
567 commandError(fd, ACK_ERROR_ARG, "incorrect arguments");
568 return -1;
571 findSongsInPlaylist(fd, numItems, items);
573 freeLocateTagItemArray(numItems, items);
575 return 0;
578 static int handlePlaylistSearch(int fd, int *permission, int argc, char *argv[])
580 LocateTagItem *items;
581 int numItems = newLocateTagItemArrayFromArgArray(argv + 1,
582 argc - 1,
583 &items);
585 if (numItems <= 0) {
586 commandError(fd, ACK_ERROR_ARG, "incorrect arguments");
587 return -1;
590 searchForSongsInPlaylist(fd, numItems, items);
592 freeLocateTagItemArray(numItems, items);
594 return 0;
597 static int handlePlaylistDelete(int fd, int *permission, int argc, char *argv[]) {
598 char *playlist = argv[1];
599 int from;
600 char *test;
602 from = strtol(argv[2], &test, 10);
603 if (*test != '\0') {
604 commandError(fd, ACK_ERROR_ARG,
605 "\"%s\" is not a integer", argv[2]);
606 return -1;
609 return removeOneSongFromStoredPlaylistByPath(fd, playlist, from);
612 static int handlePlaylistMove(int fd, int *permission, int argc, char *argv[])
614 char *playlist = argv[1];
615 int from, to;
616 char *test;
618 from = strtol(argv[2], &test, 10);
619 if (*test != '\0') {
620 commandError(fd, ACK_ERROR_ARG,
621 "\"%s\" is not a integer", argv[2]);
622 return -1;
624 to = strtol(argv[3], &test, 10);
625 if (*test != '\0') {
626 commandError(fd, ACK_ERROR_ARG,
627 "\"%s\" is not a integer", argv[3]);
628 return -1;
631 return moveSongInStoredPlaylistByPath(fd, playlist, from, to);
634 static int handleQueueInfo(int fd, int *permission, int argc, char *argv[])
636 return playlistQueueInfo(fd);
639 static int handleQueueId(int fd, int *permission, int argc, char *argv[])
641 int id, position = -1;
642 char *test;
644 id = strtol(argv[1], &test, 10);
645 if (*test != '\0') {
646 commandError(fd, ACK_ERROR_ARG,
647 "\"%s\" is not a integer", argv[1]);
648 return -1;
650 if (argc == 3) {
651 position = strtol(argv[2], &test, 10);
652 if (*test != '\0') {
653 commandError(fd, ACK_ERROR_ARG,
654 "\"%s\" is not a integer", argv[2]);
655 return -1;
658 return addToPlaylistQueueById(fd, id, position);
661 static int handleDequeue(int fd, int *permission, int argc, char *argv[])
663 int pos;
664 char *test;
666 pos = strtol(argv[1], &test, 10);
667 if (*test != '\0') {
668 commandError(fd, ACK_ERROR_ARG,
669 "\"%s\" is not a integer", argv[1]);
670 return -1;
672 return deleteFromPlaylistQueue(fd, pos);
675 static int listHandleUpdate(int fd,
676 int *permission,
677 int argc,
678 char *argv[],
679 struct strnode *cmdnode, CommandEntry * cmd)
681 static List *pathList;
682 CommandEntry *nextCmd = NULL;
683 struct strnode *next = cmdnode->next;
685 if (!pathList)
686 pathList = makeList(NULL, 1);
688 if (argc == 2)
689 insertInList(pathList, argv[1], NULL);
690 else
691 insertInList(pathList, "", NULL);
693 if (next)
694 nextCmd = getCommandEntryFromString(next->data, permission);
696 if (cmd != nextCmd) {
697 int ret = updateInit(fd, pathList);
698 freeList(pathList);
699 pathList = NULL;
700 return ret;
703 return 0;
706 static int handleUpdate(int fd, int *permission, int argc, char *argv[])
708 if (argc == 2) {
709 int ret;
710 List *pathList = makeList(NULL, 1);
711 insertInList(pathList, argv[1], NULL);
712 ret = updateInit(fd, pathList);
713 freeList(pathList);
714 return ret;
716 return updateInit(fd, NULL);
719 static int handleNext(int fd, int *permission, int argc, char *argv[])
721 return nextSongInPlaylist(fd);
724 static int handlePrevious(int fd, int *permission, int argc, char *argv[])
726 return previousSongInPlaylist(fd);
729 static int handleListAll(int fd, int *permission, int argc, char *argv[])
731 char *directory = NULL;
733 if (argc == 2)
734 directory = argv[1];
735 return printAllIn(fd, directory);
738 static int handleVolume(int fd, int *permission, int argc, char *argv[])
740 int change;
741 char *test;
743 change = strtol(argv[1], &test, 10);
744 if (*test != '\0') {
745 commandError(fd, ACK_ERROR_ARG, "need an integer");
746 return -1;
748 return changeVolumeLevel(fd, change, 1);
751 static int handleSetVol(int fd, int *permission, int argc, char *argv[])
753 int level;
754 char *test;
756 level = strtol(argv[1], &test, 10);
757 if (*test != '\0') {
758 commandError(fd, ACK_ERROR_ARG, "need an integer");
759 return -1;
761 return changeVolumeLevel(fd, level, 0);
764 static int handleRepeat(int fd, int *permission, int argc, char *argv[])
766 int status;
767 char *test;
769 status = strtol(argv[1], &test, 10);
770 if (*test != '\0') {
771 commandError(fd, ACK_ERROR_ARG, "need an integer");
772 return -1;
774 return setPlaylistRepeatStatus(fd, status);
777 static int handleRandom(int fd, int *permission, int argc, char *argv[])
779 int status;
780 char *test;
782 status = strtol(argv[1], &test, 10);
783 if (*test != '\0') {
784 commandError(fd, ACK_ERROR_ARG, "need an integer");
785 return -1;
787 return setPlaylistRandomStatus(fd, status);
790 static int handleStats(int fd, int *permission, int argc, char *argv[])
792 return printStats(fd);
795 static int handleClearError(int fd, int *permission, int argc, char *argv[])
797 clearPlayerError();
798 return 0;
801 static int handleList(int fd, int *permission, int argc, char *argv[])
803 int numConditionals = 0;
804 LocateTagItem *conditionals = NULL;
805 int tagType = getLocateTagItemType(argv[1]);
806 int ret;
808 if (tagType < 0) {
809 commandError(fd, ACK_ERROR_ARG, "\"%s\" is not known", argv[1]);
810 return -1;
813 if (tagType == LOCATE_TAG_ANY_TYPE) {
814 commandError(fd, ACK_ERROR_ARG,
815 "\"any\" is not a valid return tag type");
816 return -1;
819 /* for compatibility with < 0.12.0 */
820 if (argc == 3) {
821 if (tagType != TAG_ITEM_ALBUM) {
822 commandError(fd, ACK_ERROR_ARG,
823 "should be \"%s\" for 3 arguments",
824 mpdTagItemKeys[TAG_ITEM_ALBUM]);
825 return -1;
827 conditionals = newLocateTagItem(mpdTagItemKeys[TAG_ITEM_ARTIST],
828 argv[2]);
829 numConditionals = 1;
830 } else {
831 numConditionals =
832 newLocateTagItemArrayFromArgArray(argv + 2,
833 argc - 2, &conditionals);
835 if (numConditionals < 0) {
836 commandError(fd, ACK_ERROR_ARG,
837 "not able to parse args");
838 return -1;
842 ret = listAllUniqueTags(fd, tagType, numConditionals, conditionals);
844 if (conditionals)
845 freeLocateTagItemArray(numConditionals, conditionals);
847 return ret;
850 static int handleMove(int fd, int *permission, int argc, char *argv[])
852 int from;
853 int to;
854 char *test;
856 from = strtol(argv[1], &test, 10);
857 if (*test != '\0') {
858 commandError(fd, ACK_ERROR_ARG,
859 "\"%s\" is not a integer", argv[1]);
860 return -1;
862 to = strtol(argv[2], &test, 10);
863 if (*test != '\0') {
864 commandError(fd, ACK_ERROR_ARG,
865 "\"%s\" is not a integer", argv[2]);
866 return -1;
868 return moveSongInPlaylist(fd, from, to);
871 static int handleMoveId(int fd, int *permission, int argc, char *argv[])
873 int id;
874 int to;
875 char *test;
877 id = strtol(argv[1], &test, 10);
878 if (*test != '\0') {
879 commandError(fd, ACK_ERROR_ARG,
880 "\"%s\" is not a integer", argv[1]);
881 return -1;
883 to = strtol(argv[2], &test, 10);
884 if (*test != '\0') {
885 commandError(fd, ACK_ERROR_ARG,
886 "\"%s\" is not a integer", argv[2]);
887 return -1;
889 return moveSongInPlaylistById(fd, id, to);
892 static int handleSwap(int fd, int *permission, int argc, char *argv[])
894 int song1;
895 int song2;
896 char *test;
898 song1 = strtol(argv[1], &test, 10);
899 if (*test != '\0') {
900 commandError(fd, ACK_ERROR_ARG,
901 "\"%s\" is not a integer", argv[1]);
902 return -1;
904 song2 = strtol(argv[2], &test, 10);
905 if (*test != '\0') {
906 commandError(fd, ACK_ERROR_ARG, "\"%s\" is not a integer",
907 argv[2]);
908 return -1;
910 return swapSongsInPlaylist(fd, song1, song2);
913 static int handleSwapId(int fd, int *permission, int argc, char *argv[])
915 int id1;
916 int id2;
917 char *test;
919 id1 = strtol(argv[1], &test, 10);
920 if (*test != '\0') {
921 commandError(fd, ACK_ERROR_ARG,
922 "\"%s\" is not a integer", argv[1]);
923 return -1;
925 id2 = strtol(argv[2], &test, 10);
926 if (*test != '\0') {
927 commandError(fd, ACK_ERROR_ARG, "\"%s\" is not a integer",
928 argv[2]);
929 return -1;
931 return swapSongsInPlaylistById(fd, id1, id2);
934 static int handleSeek(int fd, int *permission, int argc, char *argv[])
936 int song;
937 int time;
938 char *test;
940 song = strtol(argv[1], &test, 10);
941 if (*test != '\0') {
942 commandError(fd, ACK_ERROR_ARG,
943 "\"%s\" is not a integer", argv[1]);
944 return -1;
946 time = strtol(argv[2], &test, 10);
947 if (*test != '\0') {
948 commandError(fd, ACK_ERROR_ARG,
949 "\"%s\" is not a integer", argv[2]);
950 return -1;
952 return seekSongInPlaylist(fd, song, time);
955 static int handleSeekId(int fd, int *permission, int argc, char *argv[])
957 int id;
958 int time;
959 char *test;
961 id = strtol(argv[1], &test, 10);
962 if (*test != '\0') {
963 commandError(fd, ACK_ERROR_ARG,
964 "\"%s\" is not a integer", argv[1]);
965 return -1;
967 time = strtol(argv[2], &test, 10);
968 if (*test != '\0') {
969 commandError(fd, ACK_ERROR_ARG,
970 "\"%s\" is not a integer", argv[2]);
971 return -1;
973 return seekSongInPlaylistById(fd, id, time);
976 static int handleListAllInfo(int fd, int *permission, int argc, char *argv[])
978 char *directory = NULL;
980 if (argc == 2)
981 directory = argv[1];
982 return printInfoForAllIn(fd, directory);
985 static int handlePing(int fd, int *permission, int argc, char *argv[])
987 return 0;
990 static int handlePassword(int fd, int *permission, int argc, char *argv[])
992 if (getPermissionFromPassword(argv[1], permission) < 0) {
993 commandError(fd, ACK_ERROR_PASSWORD, "incorrect password");
994 return -1;
997 return 0;
1000 static int handleCrossfade(int fd, int *permission, int argc, char *argv[])
1002 int time;
1003 char *test;
1005 time = strtol(argv[1], &test, 10);
1006 if (*test != '\0' || time < 0) {
1007 commandError(fd, ACK_ERROR_ARG,
1008 "\"%s\" is not a integer >= 0", argv[1]);
1009 return -1;
1012 setPlayerCrossFade(time);
1014 return 0;
1017 static int handleEnableDevice(int fd, int *permission, int argc, char *argv[])
1019 int device;
1020 char *test;
1022 device = strtol(argv[1], &test, 10);
1023 if (*test != '\0' || device < 0) {
1024 commandError(fd, ACK_ERROR_ARG,
1025 "\"%s\" is not a integer >= 0", argv[1]);
1026 return -1;
1029 return enableAudioDevice(fd, device);
1032 static int handleDisableDevice(int fd, int *permission, int argc, char *argv[])
1034 int device;
1035 char *test;
1037 device = strtol(argv[1], &test, 10);
1038 if (*test != '\0' || device < 0) {
1039 commandError(fd, ACK_ERROR_ARG,
1040 "\"%s\" is not a integer >= 0", argv[1]);
1041 return -1;
1044 return disableAudioDevice(fd, device);
1047 static int handleDevices(int fd, int *permission, int argc, char *argv[])
1049 printAudioDevices(fd);
1051 return 0;
1054 /* don't be fooled, this is the command handler for "commands" command */
1055 static int handleCommands(int fd, int *permission, int argc, char *argv[])
1057 ListNode *node = commandList->firstNode;
1058 CommandEntry *cmd;
1060 while (node != NULL) {
1061 cmd = (CommandEntry *) node->data;
1062 if (cmd->reqPermission == (*permission & cmd->reqPermission)) {
1063 fdprintf(fd, "command: %s\n", cmd->cmd);
1066 node = node->nextNode;
1069 return 0;
1072 static int handleNotcommands(int fd, int *permission, int argc, char *argv[])
1074 ListNode *node = commandList->firstNode;
1075 CommandEntry *cmd;
1077 while (node != NULL) {
1078 cmd = (CommandEntry *) node->data;
1080 if (cmd->reqPermission != (*permission & cmd->reqPermission)) {
1081 fdprintf(fd, "command: %s\n", cmd->cmd);
1084 node = node->nextNode;
1087 return 0;
1090 static int handlePlaylistClear(int fd, int *permission, int argc, char *argv[])
1092 return clearStoredPlaylist(fd, argv[1]);
1095 static int handlePlaylistAdd(int fd, int *permission, int argc, char *argv[])
1097 char *playlist = argv[1];
1098 char *path = argv[2];
1100 if (isRemoteUrl(path))
1101 return addToStoredPlaylist(fd, path, playlist);
1103 return addAllInToStoredPlaylist(fd, path, playlist);
1106 void initCommands(void)
1108 commandList = makeList(free, 1);
1110 /* addCommand(name, permission, min, max, handler, list handler); */
1111 addCommand(COMMAND_PLAY, PERMISSION_CONTROL, 0, 1, handlePlay, NULL);
1112 addCommand(COMMAND_PLAYID, PERMISSION_CONTROL, 0, 1, handlePlayId, NULL);
1113 addCommand(COMMAND_STOP, PERMISSION_CONTROL, 0, 0, handleStop, NULL);
1114 addCommand(COMMAND_CURRENTSONG, PERMISSION_READ, 0, 0, handleCurrentSong, NULL);
1115 addCommand(COMMAND_PAUSE, PERMISSION_CONTROL, 0, 1, handlePause, NULL);
1116 addCommand(COMMAND_STATUS, PERMISSION_READ, 0, 0, commandStatus, NULL);
1117 addCommand(COMMAND_KILL, PERMISSION_ADMIN, -1, -1, handleKill, NULL);
1118 addCommand(COMMAND_CLOSE, PERMISSION_NONE, -1, -1, handleClose, NULL);
1119 addCommand(COMMAND_ADD, PERMISSION_ADD, 1, 1, handleAdd, NULL);
1120 addCommand(COMMAND_ADDID, PERMISSION_ADD, 1, 1, handleAddId, NULL);
1121 addCommand(COMMAND_DELETE, PERMISSION_CONTROL, 1, 1, handleDelete, NULL);
1122 addCommand(COMMAND_DELETEID, PERMISSION_CONTROL, 1, 1, handleDeleteId, NULL);
1123 addCommand(COMMAND_PLAYLIST, PERMISSION_READ, 0, 0, handlePlaylist, NULL);
1124 addCommand(COMMAND_PLAYLISTID, PERMISSION_READ, 0, 1, handlePlaylistId, NULL);
1125 addCommand(COMMAND_SHUFFLE, PERMISSION_CONTROL, 0, 0, handleShuffle, NULL);
1126 addCommand(COMMAND_CLEAR, PERMISSION_CONTROL, 0, 0, handleClear, NULL);
1127 addCommand(COMMAND_SAVE, PERMISSION_CONTROL, 1, 1, handleSave, NULL);
1128 addCommand(COMMAND_LOAD, PERMISSION_ADD, 1, 1, handleLoad, NULL);
1129 addCommand(COMMAND_LISTPLAYLIST, PERMISSION_READ, 1, 1, handleListPlaylist, NULL);
1130 addCommand(COMMAND_LISTPLAYLISTINFO, PERMISSION_READ, 1, 1, handleListPlaylistInfo, NULL);
1131 addCommand(COMMAND_LSINFO, PERMISSION_READ, 0, 1, handleLsInfo, NULL);
1132 addCommand(COMMAND_RM, PERMISSION_CONTROL, 1, 1, handleRm, NULL);
1133 addCommand(COMMAND_PLAYLISTINFO, PERMISSION_READ, 0, 1, handlePlaylistInfo, NULL);
1134 addCommand(COMMAND_FIND, PERMISSION_READ, 2, -1, handleFind, NULL);
1135 addCommand(COMMAND_SEARCH, PERMISSION_READ, 2, -1, handleSearch, NULL);
1136 addCommand(COMMAND_UPDATE, PERMISSION_ADMIN, 0, 1, handleUpdate, listHandleUpdate);
1137 addCommand(COMMAND_NEXT, PERMISSION_CONTROL, 0, 0, handleNext, NULL);
1138 addCommand(COMMAND_PREVIOUS, PERMISSION_CONTROL, 0, 0, handlePrevious, NULL);
1139 addCommand(COMMAND_LISTALL, PERMISSION_READ, 0, 1, handleListAll, NULL);
1140 addCommand(COMMAND_VOLUME, PERMISSION_CONTROL, 1, 1, handleVolume, NULL);
1141 addCommand(COMMAND_REPEAT, PERMISSION_CONTROL, 1, 1, handleRepeat, NULL);
1142 addCommand(COMMAND_RANDOM, PERMISSION_CONTROL, 1, 1, handleRandom, NULL);
1143 addCommand(COMMAND_STATS, PERMISSION_READ, 0, 0, handleStats, NULL);
1144 addCommand(COMMAND_CLEAR_ERROR, PERMISSION_CONTROL, 0, 0, handleClearError, NULL);
1145 addCommand(COMMAND_LIST, PERMISSION_READ, 1, -1, handleList, NULL);
1146 addCommand(COMMAND_MOVE, PERMISSION_CONTROL, 2, 2, handleMove, NULL);
1147 addCommand(COMMAND_MOVEID, PERMISSION_CONTROL, 2, 2, handleMoveId, NULL);
1148 addCommand(COMMAND_SWAP, PERMISSION_CONTROL, 2, 2, handleSwap, NULL);
1149 addCommand(COMMAND_SWAPID, PERMISSION_CONTROL, 2, 2, handleSwapId, NULL);
1150 addCommand(COMMAND_SEEK, PERMISSION_CONTROL, 2, 2, handleSeek, NULL);
1151 addCommand(COMMAND_SEEKID, PERMISSION_CONTROL, 2, 2, handleSeekId, NULL);
1152 addCommand(COMMAND_LISTALLINFO, PERMISSION_READ, 0, 1, handleListAllInfo, NULL);
1153 addCommand(COMMAND_PING, PERMISSION_NONE, 0, 0, handlePing, NULL);
1154 addCommand(COMMAND_SETVOL, PERMISSION_CONTROL, 1, 1, handleSetVol, NULL);
1155 addCommand(COMMAND_PASSWORD, PERMISSION_NONE, 1, 1, handlePassword, NULL);
1156 addCommand(COMMAND_CROSSFADE, PERMISSION_CONTROL, 1, 1, handleCrossfade, NULL);
1157 addCommand(COMMAND_URL_HANDLERS, PERMISSION_READ, 0, 0, handleUrlHandlers, NULL);
1158 addCommand(COMMAND_PLCHANGES, PERMISSION_READ, 1, 1, handlePlaylistChanges, NULL);
1159 addCommand(COMMAND_PLCHANGESPOSID, PERMISSION_READ, 1, 1, handlePlaylistChangesPosId, NULL);
1160 addCommand(COMMAND_ENABLE_DEV, PERMISSION_ADMIN, 1, 1, handleEnableDevice, NULL);
1161 addCommand(COMMAND_DISABLE_DEV, PERMISSION_ADMIN, 1, 1, handleDisableDevice, NULL);
1162 addCommand(COMMAND_DEVICES, PERMISSION_READ, 0, 0, handleDevices, NULL);
1163 addCommand(COMMAND_COMMANDS, PERMISSION_NONE, 0, 0, handleCommands, NULL);
1164 addCommand(COMMAND_NOTCOMMANDS, PERMISSION_NONE, 0, 0, handleNotcommands, NULL);
1165 addCommand(COMMAND_PLAYLISTCLEAR, PERMISSION_CONTROL, 1, 1, handlePlaylistClear, NULL);
1166 addCommand(COMMAND_PLAYLISTADD, PERMISSION_CONTROL, 2, 2, handlePlaylistAdd, NULL);
1167 addCommand(COMMAND_PLAYLISTFIND, PERMISSION_READ, 2, -1, handlePlaylistFind, NULL);
1168 addCommand(COMMAND_PLAYLISTSEARCH, PERMISSION_READ, 2, -1, handlePlaylistSearch, NULL);
1169 addCommand(COMMAND_PLAYLISTMOVE, PERMISSION_CONTROL, 3, 3, handlePlaylistMove, NULL);
1170 addCommand(COMMAND_PLAYLISTDELETE, PERMISSION_CONTROL, 2, 2, handlePlaylistDelete, NULL);
1171 addCommand(COMMAND_QUEUEINFO, PERMISSION_CONTROL, 0, 0, handleQueueInfo, NULL);
1172 addCommand(COMMAND_QUEUEID, PERMISSION_CONTROL, 1, 2, handleQueueId, NULL);
1173 addCommand(COMMAND_DEQUEUE, PERMISSION_CONTROL, 1, 1, handleDequeue, NULL);
1174 addCommand(COMMAND_TAGTYPES, PERMISSION_READ, 0, 0, handleTagTypes, NULL);
1175 addCommand(COMMAND_COUNT, PERMISSION_READ, 2, -1, handleCount, NULL);
1176 addCommand(COMMAND_RENAME, PERMISSION_CONTROL, 2, 2, handleRename, NULL);
1178 sortList(commandList);
1181 void finishCommands(void)
1183 freeList(commandList);
1186 static int checkArgcAndPermission(CommandEntry * cmd, int fd,
1187 int permission, int argc, char *argv[])
1189 int min = cmd->min + 1;
1190 int max = cmd->max + 1;
1192 if (cmd->reqPermission != (permission & cmd->reqPermission)) {
1193 if (fd) {
1194 commandError(fd, ACK_ERROR_PERMISSION,
1195 "you don't have permission for \"%s\"",
1196 cmd->cmd);
1198 return -1;
1201 if (min == 0)
1202 return 0;
1204 if (min == max && max != argc) {
1205 if (fd) {
1206 commandError(fd, ACK_ERROR_ARG,
1207 "wrong number of arguments for \"%s\"",
1208 argv[0]);
1210 return -1;
1211 } else if (argc < min) {
1212 if (fd) {
1213 commandError(fd, ACK_ERROR_ARG,
1214 "too few arguments for \"%s\"", argv[0]);
1216 return -1;
1217 } else if (argc > max && max /* != 0 */ ) {
1218 if (fd) {
1219 commandError(fd, ACK_ERROR_ARG,
1220 "too many arguments for \"%s\"", argv[0]);
1222 return -1;
1223 } else
1224 return 0;
1227 static CommandEntry *getCommandEntryAndCheckArgcAndPermission(int fd,
1228 int *permission,
1229 int argc,
1230 char *argv[])
1232 static char unknown[] = "";
1233 CommandEntry *cmd;
1235 current_command = unknown;
1237 if (argc == 0)
1238 return NULL;
1240 if (!findInList(commandList, argv[0], (void *)&cmd)) {
1241 if (fd) {
1242 commandError(fd, ACK_ERROR_UNKNOWN,
1243 "unknown command \"%s\"", argv[0]);
1245 return NULL;
1248 current_command = cmd->cmd;
1250 if (checkArgcAndPermission(cmd, fd, *permission, argc, argv) < 0) {
1251 return NULL;
1254 return cmd;
1257 static CommandEntry *getCommandEntryFromString(char *string, int *permission)
1259 CommandEntry *cmd = NULL;
1260 char *argv[COMMAND_ARGV_MAX] = { NULL };
1261 int argc = buffer2array(string, argv, COMMAND_ARGV_MAX);
1263 if (0 == argc)
1264 return NULL;
1266 cmd = getCommandEntryAndCheckArgcAndPermission(0, permission,
1267 argc, argv);
1269 return cmd;
1272 static int processCommandInternal(int fd, int *permission,
1273 char *commandString, struct strnode *cmdnode)
1275 int argc;
1276 char *argv[COMMAND_ARGV_MAX] = { NULL };
1277 CommandEntry *cmd;
1278 int ret = -1;
1280 argc = buffer2array(commandString, argv, COMMAND_ARGV_MAX);
1282 if (argc == 0)
1283 return 0;
1285 if ((cmd = getCommandEntryAndCheckArgcAndPermission(fd, permission,
1286 argc, argv))) {
1287 if (!cmdnode || !cmd->listHandler) {
1288 ret = cmd->handler(fd, permission, argc, argv);
1289 } else {
1290 ret = cmd->listHandler(fd, permission, argc, argv,
1291 cmdnode, cmd);
1295 current_command = NULL;
1297 return ret;
1300 int processListOfCommands(int fd, int *permission, int *expired,
1301 int listOK, struct strnode *list)
1303 struct strnode *cur = list;
1304 int ret = 0;
1306 command_listNum = 0;
1308 while (cur) {
1309 DEBUG("processListOfCommands: process command \"%s\"\n",
1310 cur->data);
1311 ret = processCommandInternal(fd, permission, cur->data, cur);
1312 DEBUG("processListOfCommands: command returned %i\n", ret);
1313 if (ret != 0 || (*expired) != 0)
1314 goto out;
1315 else if (listOK)
1316 fdprintf(fd, "list_OK\n");
1317 command_listNum++;
1318 cur = cur->next;
1320 out:
1321 command_listNum = 0;
1322 return ret;
1325 int processCommand(int fd, int *permission, char *commandString)
1327 return processCommandInternal(fd, permission, commandString, NULL);
1330 mpd_fprintf_ void commandError(int fd, int error, const char *fmt, ...)
1332 va_list args;
1333 va_start(args, fmt);
1335 if (current_command && fd != STDERR_FILENO) {
1336 fdprintf(fd, "ACK [%i@%i] {%s} ",
1337 (int)error, command_listNum, current_command);
1338 vfdprintf(fd, fmt, args);
1339 fdprintf(fd, "\n");
1340 current_command = NULL;
1341 } else {
1342 fdprintf(STDERR_FILENO, "ACK [%i@%i] ",
1343 (int)error, command_listNum);
1344 vfdprintf(STDERR_FILENO, fmt, args);
1345 fdprintf(STDERR_FILENO, "\n");
1348 va_end(args);