diff options
Diffstat (limited to '')
-rw-r--r-- | trunk/src/command.c | 1299 |
1 files changed, 1299 insertions, 0 deletions
diff --git a/trunk/src/command.c b/trunk/src/command.c new file mode 100644 index 000000000..84a30db2b --- /dev/null +++ b/trunk/src/command.c @@ -0,0 +1,1299 @@ +/* the Music Player Daemon (MPD) + * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com) + * This project's homepage is: http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "command.h" +#include "player.h" +#include "playlist.h" +#include "ls.h" +#include "directory.h" +#include "volume.h" +#include "stats.h" +#include "myfprintf.h" +#include "list.h" +#include "permission.h" +#include "buffer2array.h" +#include "log.h" +#include "tag.h" +#include "utils.h" +#include "storedPlaylist.h" + +#include <assert.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define COMMAND_PLAY "play" +#define COMMAND_PLAYID "playid" +#define COMMAND_STOP "stop" +#define COMMAND_PAUSE "pause" +#define COMMAND_STATUS "status" +#define COMMAND_KILL "kill" +#define COMMAND_CLOSE "close" +#define COMMAND_ADD "add" +#define COMMAND_ADDID "addid" +#define COMMAND_DELETE "delete" +#define COMMAND_DELETEID "deleteid" +#define COMMAND_PLAYLIST "playlist" +#define COMMAND_SHUFFLE "shuffle" +#define COMMAND_CLEAR "clear" +#define COMMAND_SAVE "save" +#define COMMAND_LOAD "load" +#define COMMAND_LISTPLAYLIST "listplaylist" +#define COMMAND_LISTPLAYLISTINFO "listplaylistinfo" +#define COMMAND_LSINFO "lsinfo" +#define COMMAND_RM "rm" +#define COMMAND_PLAYLISTINFO "playlistinfo" +#define COMMAND_PLAYLISTID "playlistid" +#define COMMAND_FIND "find" +#define COMMAND_SEARCH "search" +#define COMMAND_UPDATE "update" +#define COMMAND_NEXT "next" +#define COMMAND_PREVIOUS "previous" +#define COMMAND_LISTALL "listall" +#define COMMAND_VOLUME "volume" +#define COMMAND_REPEAT "repeat" +#define COMMAND_RANDOM "random" +#define COMMAND_STATS "stats" +#define COMMAND_CLEAR_ERROR "clearerror" +#define COMMAND_LIST "list" +#define COMMAND_MOVE "move" +#define COMMAND_MOVEID "moveid" +#define COMMAND_SWAP "swap" +#define COMMAND_SWAPID "swapid" +#define COMMAND_SEEK "seek" +#define COMMAND_SEEKID "seekid" +#define COMMAND_LISTALLINFO "listallinfo" +#define COMMAND_PING "ping" +#define COMMAND_SETVOL "setvol" +#define COMMAND_PASSWORD "password" +#define COMMAND_CROSSFADE "crossfade" +#define COMMAND_URL_HANDLERS "urlhandlers" +#define COMMAND_PLCHANGES "plchanges" +#define COMMAND_PLCHANGESPOSID "plchangesposid" +#define COMMAND_CURRENTSONG "currentsong" +#define COMMAND_ENABLE_DEV "enableoutput" +#define COMMAND_DISABLE_DEV "disableoutput" +#define COMMAND_DEVICES "outputs" +#define COMMAND_COMMANDS "commands" +#define COMMAND_NOTCOMMANDS "notcommands" +#define COMMAND_PLAYLISTCLEAR "playlistclear" +#define COMMAND_PLAYLISTADD "playlistadd" +#define COMMAND_PLAYLISTFIND "playlistfind" +#define COMMAND_PLAYLISTSEARCH "playlistsearch" +#define COMMAND_PLAYLISTMOVE "playlistmove" +#define COMMAND_PLAYLISTDELETE "playlistdelete" +#define COMMAND_TAGTYPES "tagtypes" +#define COMMAND_COUNT "count" +#define COMMAND_RENAME "rename" + +#define COMMAND_STATUS_VOLUME "volume" +#define COMMAND_STATUS_STATE "state" +#define COMMAND_STATUS_REPEAT "repeat" +#define COMMAND_STATUS_RANDOM "random" +#define COMMAND_STATUS_PLAYLIST "playlist" +#define COMMAND_STATUS_PLAYLIST_LENGTH "playlistlength" +#define COMMAND_STATUS_SONG "song" +#define COMMAND_STATUS_SONGID "songid" +#define COMMAND_STATUS_TIME "time" +#define COMMAND_STATUS_BITRATE "bitrate" +#define COMMAND_STATUS_ERROR "error" +#define COMMAND_STATUS_CROSSFADE "xfade" +#define COMMAND_STATUS_AUDIO "audio" +#define COMMAND_STATUS_UPDATING_DB "updating_db" + +/* + * The most we ever use is for search/find, and that limits it to the + * number of tags we can have. Add one for the command, and one extra + * to catch errors clients may send us + */ +#define COMMAND_ARGV_MAX (2+(TAG_NUM_OF_ITEM_TYPES*2)) + +typedef struct _CommandEntry CommandEntry; + +typedef int (*CommandHandlerFunction) (int, int *, int, char **); +typedef int (*CommandListHandlerFunction) + (int, int *, int, char **, struct strnode *, CommandEntry *); + +/* if min: -1 don't check args * + * if max: -1 no max args */ +struct _CommandEntry { + char *cmd; + int min; + int max; + int reqPermission; + CommandHandlerFunction handler; + CommandListHandlerFunction listHandler; +}; + +static char *current_command; +static int command_listNum; + +static CommandEntry *getCommandEntryFromString(char *string, int *permission); + +static List *commandList; + +static CommandEntry *newCommandEntry(void) +{ + CommandEntry *cmd = xmalloc(sizeof(CommandEntry)); + cmd->cmd = NULL; + cmd->min = 0; + cmd->max = 0; + cmd->handler = NULL; + cmd->listHandler = NULL; + cmd->reqPermission = 0; + return cmd; +} + +static void addCommand(char *name, + int reqPermission, + int minargs, + int maxargs, + CommandHandlerFunction handler_func, + CommandListHandlerFunction listHandler_func) +{ + CommandEntry *cmd = newCommandEntry(); + cmd->cmd = name; + cmd->min = minargs; + cmd->max = maxargs; + cmd->handler = handler_func; + cmd->listHandler = listHandler_func; + cmd->reqPermission = reqPermission; + + insertInList(commandList, cmd->cmd, cmd); +} + +static int handleUrlHandlers(int fd, int *permission, int argc, char *argv[]) +{ + return printRemoteUrlHandlers(fd); +} + +static int handleTagTypes(int fd, int *permission, int argc, char *argv[]) +{ + printTagTypes(fd); + return 0; +} + +static int handlePlay(int fd, int *permission, int argc, char *argv[]) +{ + int song = -1; + char *test; + + if (argc == 2) { + song = strtol(argv[1], &test, 10); + if (*test != '\0') { + commandError(fd, ACK_ERROR_ARG, + "need a positive integer"); + return -1; + } + } + return playPlaylist(fd, song, 0); +} + +static int handlePlayId(int fd, int *permission, int argc, char *argv[]) +{ + int id = -1; + char *test; + + if (argc == 2) { + id = strtol(argv[1], &test, 10); + if (*test != '\0') { + commandError(fd, ACK_ERROR_ARG, + "need a positive integer"); + return -1; + } + } + return playPlaylistById(fd, id, 0); +} + +static int handleStop(int fd, int *permission, int argc, char *argv[]) +{ + return stopPlaylist(fd); +} + +static int handleCurrentSong(int fd, int *permission, int argc, char *argv[]) +{ + int song = getPlaylistCurrentSong(); + + if (song >= 0) { + return playlistInfo(fd, song); + } else + return 0; +} + +static int handlePause(int fd, int *permission, int argc, char *argv[]) +{ + if (argc == 2) { + char *test; + int pause = strtol(argv[1], &test, 10); + if (*test != '\0' || (pause != 0 && pause != 1)) { + commandError(fd, ACK_ERROR_ARG, "\"%s\" is not 0 or 1", + argv[1]); + return -1; + } + return playerSetPause(fd, pause); + } + return playerPause(fd); +} + +static int commandStatus(int fd, int *permission, int argc, char *argv[]) +{ + char *state = NULL; + int updateJobId; + int song; + + /*syncPlayerAndPlaylist(); */ + playPlaylistIfPlayerStopped(); + switch (getPlayerState()) { + case PLAYER_STATE_STOP: + state = COMMAND_STOP; + break; + case PLAYER_STATE_PAUSE: + state = COMMAND_PAUSE; + break; + case PLAYER_STATE_PLAY: + state = COMMAND_PLAY; + break; + } + + fdprintf(fd, "%s: %i\n", COMMAND_STATUS_VOLUME, getVolumeLevel()); + fdprintf(fd, "%s: %i\n", COMMAND_STATUS_REPEAT, + getPlaylistRepeatStatus()); + fdprintf(fd, "%s: %i\n", COMMAND_STATUS_RANDOM, + getPlaylistRandomStatus()); + fdprintf(fd, "%s: %li\n", COMMAND_STATUS_PLAYLIST, + getPlaylistVersion()); + fdprintf(fd, "%s: %i\n", COMMAND_STATUS_PLAYLIST_LENGTH, + getPlaylistLength()); + fdprintf(fd, "%s: %i\n", COMMAND_STATUS_CROSSFADE, + (int)(getPlayerCrossFade() + 0.5)); + + fdprintf(fd, "%s: %s\n", COMMAND_STATUS_STATE, state); + + song = getPlaylistCurrentSong(); + if (song >= 0) { + fdprintf(fd, "%s: %i\n", COMMAND_STATUS_SONG, song); + fdprintf(fd, "%s: %i\n", COMMAND_STATUS_SONGID, + getPlaylistSongId(song)); + } + if (getPlayerState() != PLAYER_STATE_STOP) { + fdprintf(fd, "%s: %i:%i\n", COMMAND_STATUS_TIME, + getPlayerElapsedTime(), getPlayerTotalTime()); + fdprintf(fd, "%s: %li\n", COMMAND_STATUS_BITRATE, + getPlayerBitRate()); + fdprintf(fd, "%s: %u:%i:%i\n", COMMAND_STATUS_AUDIO, + getPlayerSampleRate(), getPlayerBits(), + getPlayerChannels()); + } + + if ((updateJobId = isUpdatingDB())) { + fdprintf(fd, "%s: %i\n", COMMAND_STATUS_UPDATING_DB, + updateJobId); + } + + if (getPlayerError() != PLAYER_ERROR_NOERROR) { + fdprintf(fd, "%s: %s\n", COMMAND_STATUS_ERROR, + getPlayerErrorStr()); + } + + return 0; +} + +static int handleKill(int fd, int *permission, int argc, char *argv[]) +{ + return COMMAND_RETURN_KILL; +} + +static int handleClose(int fd, int *permission, int argc, char *argv[]) +{ + return COMMAND_RETURN_CLOSE; +} + +static int handleAdd(int fd, int *permission, int argc, char *argv[]) +{ + char *path = argv[1]; + + if (isRemoteUrl(path)) + return addToPlaylist(fd, path, 0); + + return addAllIn(fd, path); +} + +static int handleAddId(int fd, int *permission, int argc, char *argv[]) +{ + return addToPlaylist(fd, argv[1], 1); +} + +static int handleDelete(int fd, int *permission, int argc, char *argv[]) +{ + int song; + char *test; + + song = strtol(argv[1], &test, 10); + if (*test != '\0') { + commandError(fd, ACK_ERROR_ARG, "need a positive integer"); + return -1; + } + return deleteFromPlaylist(fd, song); +} + +static int handleDeleteId(int fd, int *permission, int argc, char *argv[]) +{ + int id; + char *test; + + id = strtol(argv[1], &test, 10); + if (*test != '\0') { + commandError(fd, ACK_ERROR_ARG, "need a positive integer"); + return -1; + } + return deleteFromPlaylistById(fd, id); +} + +static int handlePlaylist(int fd, int *permission, int argc, char *argv[]) +{ + return showPlaylist(fd); +} + +static int handleShuffle(int fd, int *permission, int argc, char *argv[]) +{ + return shufflePlaylist(fd); +} + +static int handleClear(int fd, int *permission, int argc, char *argv[]) +{ + return clearPlaylist(fd); +} + +static int handleSave(int fd, int *permission, int argc, char *argv[]) +{ + return savePlaylist(fd, argv[1]); +} + +static int handleLoad(int fd, int *permission, int argc, char *argv[]) +{ + return loadPlaylist(fd, argv[1]); +} + +static int handleListPlaylist(int fd, int *permission, int argc, char *argv[]) +{ + return PlaylistInfo(fd, argv[1], 0); +} + +static int handleListPlaylistInfo(int fd, int *permission, + int argc, char *argv[]) +{ + return PlaylistInfo(fd, argv[1], 1); +} + +static int handleLsInfo(int fd, int *permission, int argc, char *argv[]) +{ + char *path = ""; + + if (argc == 2) + path = argv[1]; + + if (printDirectoryInfo(fd, path) < 0) + return -1; + + if (isRootDirectory(path)) + return lsPlaylists(fd, path); + + return 0; +} + +static int handleRm(int fd, int *permission, int argc, char *argv[]) +{ + return deletePlaylist(fd, argv[1]); +} + +static int handleRename(int fd, int *permission, int argc, char *argv[]) +{ + return renameStoredPlaylist(fd, argv[1], argv[2]); +} + +static int handlePlaylistChanges(int fd, int *permission, + int argc, char *argv[]) +{ + unsigned long version; + char *test; + + version = strtoul(argv[1], &test, 10); + if (*test != '\0') { + commandError(fd, ACK_ERROR_ARG, "need a positive integer"); + return -1; + } + return playlistChanges(fd, version); +} + +static int handlePlaylistChangesPosId(int fd, int *permission, + int argc, char *argv[]) +{ + unsigned long version; + char *test; + + version = strtoul(argv[1], &test, 10); + if (*test != '\0') { + commandError(fd, ACK_ERROR_ARG, "need a positive integer"); + return -1; + } + return playlistChangesPosId(fd, version); +} + +static int handlePlaylistInfo(int fd, int *permission, int argc, char *argv[]) +{ + int song = -1; + char *test; + + if (argc == 2) { + song = strtol(argv[1], &test, 10); + if (*test != '\0') { + commandError(fd, ACK_ERROR_ARG, + "need a positive integer"); + return -1; + } + } + return playlistInfo(fd, song); +} + +static int handlePlaylistId(int fd, int *permission, int argc, char *argv[]) +{ + int id = -1; + char *test; + + if (argc == 2) { + id = strtol(argv[1], &test, 10); + if (*test != '\0') { + commandError(fd, ACK_ERROR_ARG, + "need a positive integer"); + return -1; + } + } + return playlistId(fd, id); +} + +static int handleFind(int fd, int *permission, int argc, char *argv[]) +{ + int ret; + + LocateTagItem *items; + int numItems = newLocateTagItemArrayFromArgArray(argv + 1, + argc - 1, + &items); + + if (numItems <= 0) { + commandError(fd, ACK_ERROR_ARG, "incorrect arguments"); + return -1; + } + + ret = findSongsIn(fd, NULL, numItems, items); + + freeLocateTagItemArray(numItems, items); + + return ret; +} + +static int handleSearch(int fd, int *permission, int argc, char *argv[]) +{ + int ret; + + LocateTagItem *items; + int numItems = newLocateTagItemArrayFromArgArray(argv + 1, + argc - 1, + &items); + + if (numItems <= 0) { + commandError(fd, ACK_ERROR_ARG, "incorrect arguments"); + return -1; + } + + ret = searchForSongsIn(fd, NULL, numItems, items); + + freeLocateTagItemArray(numItems, items); + + return ret; +} + +static int handleCount(int fd, int *permission, int argc, char *argv[]) +{ + int ret; + + LocateTagItem *items; + int numItems = newLocateTagItemArrayFromArgArray(argv + 1, + argc - 1, + &items); + + if (numItems <= 0) { + commandError(fd, ACK_ERROR_ARG, "incorrect arguments"); + return -1; + } + + ret = searchStatsForSongsIn(fd, NULL, numItems, items); + + freeLocateTagItemArray(numItems, items); + + return ret; +} + +static int handlePlaylistFind(int fd, int *permission, int argc, char *argv[]) +{ + LocateTagItem *items; + int numItems = newLocateTagItemArrayFromArgArray(argv + 1, + argc - 1, + &items); + + if (numItems <= 0) { + commandError(fd, ACK_ERROR_ARG, "incorrect arguments"); + return -1; + } + + findSongsInPlaylist(fd, numItems, items); + + freeLocateTagItemArray(numItems, items); + + return 0; +} + +static int handlePlaylistSearch(int fd, int *permission, int argc, char *argv[]) +{ + LocateTagItem *items; + int numItems = newLocateTagItemArrayFromArgArray(argv + 1, + argc - 1, + &items); + + if (numItems <= 0) { + commandError(fd, ACK_ERROR_ARG, "incorrect arguments"); + return -1; + } + + searchForSongsInPlaylist(fd, numItems, items); + + freeLocateTagItemArray(numItems, items); + + return 0; +} + +static int handlePlaylistDelete(int fd, int *permission, int argc, char *argv[]) { + char *playlist = argv[1]; + int from; + char *test; + + from = strtol(argv[2], &test, 10); + if (*test != '\0') { + commandError(fd, ACK_ERROR_ARG, + "\"%s\" is not a integer", argv[2]); + return -1; + } + + return removeOneSongFromStoredPlaylistByPath(fd, playlist, from); +} + +static int handlePlaylistMove(int fd, int *permission, int argc, char *argv[]) +{ + char *playlist = argv[1]; + int from, to; + char *test; + + from = strtol(argv[2], &test, 10); + if (*test != '\0') { + commandError(fd, ACK_ERROR_ARG, + "\"%s\" is not a integer", argv[2]); + return -1; + } + to = strtol(argv[3], &test, 10); + if (*test != '\0') { + commandError(fd, ACK_ERROR_ARG, + "\"%s\" is not a integer", argv[3]); + return -1; + } + + return moveSongInStoredPlaylistByPath(fd, playlist, from, to); +} + +static int listHandleUpdate(int fd, + int *permission, + int argc, + char *argv[], + struct strnode *cmdnode, CommandEntry * cmd) +{ + static List *pathList; + CommandEntry *nextCmd = NULL; + struct strnode *next = cmdnode->next; + + if (!pathList) + pathList = makeList(NULL, 1); + + if (argc == 2) + insertInList(pathList, argv[1], NULL); + else + insertInList(pathList, "", NULL); + + if (next) + nextCmd = getCommandEntryFromString(next->data, permission); + + if (cmd != nextCmd) { + int ret = updateInit(fd, pathList); + freeList(pathList); + pathList = NULL; + return ret; + } + + return 0; +} + +static int handleUpdate(int fd, int *permission, int argc, char *argv[]) +{ + if (argc == 2) { + int ret; + List *pathList = makeList(NULL, 1); + insertInList(pathList, argv[1], NULL); + ret = updateInit(fd, pathList); + freeList(pathList); + return ret; + } + return updateInit(fd, NULL); +} + +static int handleNext(int fd, int *permission, int argc, char *argv[]) +{ + return nextSongInPlaylist(fd); +} + +static int handlePrevious(int fd, int *permission, int argc, char *argv[]) +{ + return previousSongInPlaylist(fd); +} + +static int handleListAll(int fd, int *permission, int argc, char *argv[]) +{ + char *directory = NULL; + + if (argc == 2) + directory = argv[1]; + return printAllIn(fd, directory); +} + +static int handleVolume(int fd, int *permission, int argc, char *argv[]) +{ + int change; + char *test; + + change = strtol(argv[1], &test, 10); + if (*test != '\0') { + commandError(fd, ACK_ERROR_ARG, "need an integer"); + return -1; + } + return changeVolumeLevel(fd, change, 1); +} + +static int handleSetVol(int fd, int *permission, int argc, char *argv[]) +{ + int level; + char *test; + + level = strtol(argv[1], &test, 10); + if (*test != '\0') { + commandError(fd, ACK_ERROR_ARG, "need an integer"); + return -1; + } + return changeVolumeLevel(fd, level, 0); +} + +static int handleRepeat(int fd, int *permission, int argc, char *argv[]) +{ + int status; + char *test; + + status = strtol(argv[1], &test, 10); + if (*test != '\0') { + commandError(fd, ACK_ERROR_ARG, "need an integer"); + return -1; + } + return setPlaylistRepeatStatus(fd, status); +} + +static int handleRandom(int fd, int *permission, int argc, char *argv[]) +{ + int status; + char *test; + + status = strtol(argv[1], &test, 10); + if (*test != '\0') { + commandError(fd, ACK_ERROR_ARG, "need an integer"); + return -1; + } + return setPlaylistRandomStatus(fd, status); +} + +static int handleStats(int fd, int *permission, int argc, char *argv[]) +{ + return printStats(fd); +} + +static int handleClearError(int fd, int *permission, int argc, char *argv[]) +{ + clearPlayerError(); + return 0; +} + +static int handleList(int fd, int *permission, int argc, char *argv[]) +{ + int numConditionals = 0; + LocateTagItem *conditionals = NULL; + int tagType = getLocateTagItemType(argv[1]); + int ret; + + if (tagType < 0) { + commandError(fd, ACK_ERROR_ARG, "\"%s\" is not known", argv[1]); + return -1; + } + + if (tagType == LOCATE_TAG_ANY_TYPE) { + commandError(fd, ACK_ERROR_ARG, + "\"any\" is not a valid return tag type"); + return -1; + } + + /* for compatibility with < 0.12.0 */ + if (argc == 3) { + if (tagType != TAG_ITEM_ALBUM) { + commandError(fd, ACK_ERROR_ARG, + "should be \"%s\" for 3 arguments", + mpdTagItemKeys[TAG_ITEM_ALBUM]); + return -1; + } + conditionals = newLocateTagItem(mpdTagItemKeys[TAG_ITEM_ARTIST], + argv[2]); + numConditionals = 1; + } else { + numConditionals = + newLocateTagItemArrayFromArgArray(argv + 2, + argc - 2, &conditionals); + + if (numConditionals < 0) { + commandError(fd, ACK_ERROR_ARG, + "not able to parse args"); + return -1; + } + } + + ret = listAllUniqueTags(fd, tagType, numConditionals, conditionals); + + if (conditionals) + freeLocateTagItemArray(numConditionals, conditionals); + + return ret; +} + +static int handleMove(int fd, int *permission, int argc, char *argv[]) +{ + int from; + int to; + char *test; + + from = strtol(argv[1], &test, 10); + if (*test != '\0') { + commandError(fd, ACK_ERROR_ARG, + "\"%s\" is not a integer", argv[1]); + return -1; + } + to = strtol(argv[2], &test, 10); + if (*test != '\0') { + commandError(fd, ACK_ERROR_ARG, + "\"%s\" is not a integer", argv[2]); + return -1; + } + return moveSongInPlaylist(fd, from, to); +} + +static int handleMoveId(int fd, int *permission, int argc, char *argv[]) +{ + int id; + int to; + char *test; + + id = strtol(argv[1], &test, 10); + if (*test != '\0') { + commandError(fd, ACK_ERROR_ARG, + "\"%s\" is not a integer", argv[1]); + return -1; + } + to = strtol(argv[2], &test, 10); + if (*test != '\0') { + commandError(fd, ACK_ERROR_ARG, + "\"%s\" is not a integer", argv[2]); + return -1; + } + return moveSongInPlaylistById(fd, id, to); +} + +static int handleSwap(int fd, int *permission, int argc, char *argv[]) +{ + int song1; + int song2; + char *test; + + song1 = strtol(argv[1], &test, 10); + if (*test != '\0') { + commandError(fd, ACK_ERROR_ARG, + "\"%s\" is not a integer", argv[1]); + return -1; + } + song2 = strtol(argv[2], &test, 10); + if (*test != '\0') { + commandError(fd, ACK_ERROR_ARG, "\"%s\" is not a integer", + argv[2]); + return -1; + } + return swapSongsInPlaylist(fd, song1, song2); +} + +static int handleSwapId(int fd, int *permission, int argc, char *argv[]) +{ + int id1; + int id2; + char *test; + + id1 = strtol(argv[1], &test, 10); + if (*test != '\0') { + commandError(fd, ACK_ERROR_ARG, + "\"%s\" is not a integer", argv[1]); + return -1; + } + id2 = strtol(argv[2], &test, 10); + if (*test != '\0') { + commandError(fd, ACK_ERROR_ARG, "\"%s\" is not a integer", + argv[2]); + return -1; + } + return swapSongsInPlaylistById(fd, id1, id2); +} + +static int handleSeek(int fd, int *permission, int argc, char *argv[]) +{ + int song; + int time; + char *test; + + song = strtol(argv[1], &test, 10); + if (*test != '\0') { + commandError(fd, ACK_ERROR_ARG, + "\"%s\" is not a integer", argv[1]); + return -1; + } + time = strtol(argv[2], &test, 10); + if (*test != '\0') { + commandError(fd, ACK_ERROR_ARG, + "\"%s\" is not a integer", argv[2]); + return -1; + } + return seekSongInPlaylist(fd, song, time); +} + +static int handleSeekId(int fd, int *permission, int argc, char *argv[]) +{ + int id; + int time; + char *test; + + id = strtol(argv[1], &test, 10); + if (*test != '\0') { + commandError(fd, ACK_ERROR_ARG, + "\"%s\" is not a integer", argv[1]); + return -1; + } + time = strtol(argv[2], &test, 10); + if (*test != '\0') { + commandError(fd, ACK_ERROR_ARG, + "\"%s\" is not a integer", argv[2]); + return -1; + } + return seekSongInPlaylistById(fd, id, time); +} + +static int handleListAllInfo(int fd, int *permission, int argc, char *argv[]) +{ + char *directory = NULL; + + if (argc == 2) + directory = argv[1]; + return printInfoForAllIn(fd, directory); +} + +static int handlePing(int fd, int *permission, int argc, char *argv[]) +{ + return 0; +} + +static int handlePassword(int fd, int *permission, int argc, char *argv[]) +{ + if (getPermissionFromPassword(argv[1], permission) < 0) { + commandError(fd, ACK_ERROR_PASSWORD, "incorrect password"); + return -1; + } + + return 0; +} + +static int handleCrossfade(int fd, int *permission, int argc, char *argv[]) +{ + int time; + char *test; + + time = strtol(argv[1], &test, 10); + if (*test != '\0' || time < 0) { + commandError(fd, ACK_ERROR_ARG, + "\"%s\" is not a integer >= 0", argv[1]); + return -1; + } + + setPlayerCrossFade(time); + + return 0; +} + +static int handleEnableDevice(int fd, int *permission, int argc, char *argv[]) +{ + int device; + char *test; + + device = strtol(argv[1], &test, 10); + if (*test != '\0' || device < 0) { + commandError(fd, ACK_ERROR_ARG, + "\"%s\" is not a integer >= 0", argv[1]); + return -1; + } + + return enableAudioDevice(fd, device); +} + +static int handleDisableDevice(int fd, int *permission, int argc, char *argv[]) +{ + int device; + char *test; + + device = strtol(argv[1], &test, 10); + if (*test != '\0' || device < 0) { + commandError(fd, ACK_ERROR_ARG, + "\"%s\" is not a integer >= 0", argv[1]); + return -1; + } + + return disableAudioDevice(fd, device); +} + +static int handleDevices(int fd, int *permission, int argc, char *argv[]) +{ + printAudioDevices(fd); + + return 0; +} + +/* don't be fooled, this is the command handler for "commands" command */ +static int handleCommands(int fd, int *permission, int argc, char *argv[]) +{ + ListNode *node = commandList->firstNode; + CommandEntry *cmd; + + while (node != NULL) { + cmd = (CommandEntry *) node->data; + if (cmd->reqPermission == (*permission & cmd->reqPermission)) { + fdprintf(fd, "command: %s\n", cmd->cmd); + } + + node = node->nextNode; + } + + return 0; +} + +static int handleNotcommands(int fd, int *permission, int argc, char *argv[]) +{ + ListNode *node = commandList->firstNode; + CommandEntry *cmd; + + while (node != NULL) { + cmd = (CommandEntry *) node->data; + + if (cmd->reqPermission != (*permission & cmd->reqPermission)) { + fdprintf(fd, "command: %s\n", cmd->cmd); + } + + node = node->nextNode; + } + + return 0; +} + +static int handlePlaylistClear(int fd, int *permission, int argc, char *argv[]) +{ + return clearStoredPlaylist(fd, argv[1]); +} + +static int handlePlaylistAdd(int fd, int *permission, int argc, char *argv[]) +{ + char *playlist = argv[1]; + char *path = argv[2]; + + if (isRemoteUrl(path)) + return addToStoredPlaylist(fd, path, playlist); + + return addAllInToStoredPlaylist(fd, path, playlist); +} + +void initCommands(void) +{ + commandList = makeList(free, 1); + + /* addCommand(name, permission, min, max, handler, list handler); */ + addCommand(COMMAND_PLAY, PERMISSION_CONTROL, 0, 1, handlePlay, NULL); + addCommand(COMMAND_PLAYID, PERMISSION_CONTROL, 0, 1, handlePlayId, NULL); + addCommand(COMMAND_STOP, PERMISSION_CONTROL, 0, 0, handleStop, NULL); + addCommand(COMMAND_CURRENTSONG, PERMISSION_READ, 0, 0, handleCurrentSong, NULL); + addCommand(COMMAND_PAUSE, PERMISSION_CONTROL, 0, 1, handlePause, NULL); + addCommand(COMMAND_STATUS, PERMISSION_READ, 0, 0, commandStatus, NULL); + addCommand(COMMAND_KILL, PERMISSION_ADMIN, -1, -1, handleKill, NULL); + addCommand(COMMAND_CLOSE, PERMISSION_NONE, -1, -1, handleClose, NULL); + addCommand(COMMAND_ADD, PERMISSION_ADD, 1, 1, handleAdd, NULL); + addCommand(COMMAND_ADDID, PERMISSION_ADD, 1, 1, handleAddId, NULL); + addCommand(COMMAND_DELETE, PERMISSION_CONTROL, 1, 1, handleDelete, NULL); + addCommand(COMMAND_DELETEID, PERMISSION_CONTROL, 1, 1, handleDeleteId, NULL); + addCommand(COMMAND_PLAYLIST, PERMISSION_READ, 0, 0, handlePlaylist, NULL); + addCommand(COMMAND_PLAYLISTID, PERMISSION_READ, 0, 1, handlePlaylistId, NULL); + addCommand(COMMAND_SHUFFLE, PERMISSION_CONTROL, 0, 0, handleShuffle, NULL); + addCommand(COMMAND_CLEAR, PERMISSION_CONTROL, 0, 0, handleClear, NULL); + addCommand(COMMAND_SAVE, PERMISSION_CONTROL, 1, 1, handleSave, NULL); + addCommand(COMMAND_LOAD, PERMISSION_ADD, 1, 1, handleLoad, NULL); + addCommand(COMMAND_LISTPLAYLIST, PERMISSION_READ, 1, 1, handleListPlaylist, NULL); + addCommand(COMMAND_LISTPLAYLISTINFO, PERMISSION_READ, 1, 1, handleListPlaylistInfo, NULL); + addCommand(COMMAND_LSINFO, PERMISSION_READ, 0, 1, handleLsInfo, NULL); + addCommand(COMMAND_RM, PERMISSION_CONTROL, 1, 1, handleRm, NULL); + addCommand(COMMAND_PLAYLISTINFO, PERMISSION_READ, 0, 1, handlePlaylistInfo, NULL); + addCommand(COMMAND_FIND, PERMISSION_READ, 2, -1, handleFind, NULL); + addCommand(COMMAND_SEARCH, PERMISSION_READ, 2, -1, handleSearch, NULL); + addCommand(COMMAND_UPDATE, PERMISSION_ADMIN, 0, 1, handleUpdate, listHandleUpdate); + addCommand(COMMAND_NEXT, PERMISSION_CONTROL, 0, 0, handleNext, NULL); + addCommand(COMMAND_PREVIOUS, PERMISSION_CONTROL, 0, 0, handlePrevious, NULL); + addCommand(COMMAND_LISTALL, PERMISSION_READ, 0, 1, handleListAll, NULL); + addCommand(COMMAND_VOLUME, PERMISSION_CONTROL, 1, 1, handleVolume, NULL); + addCommand(COMMAND_REPEAT, PERMISSION_CONTROL, 1, 1, handleRepeat, NULL); + addCommand(COMMAND_RANDOM, PERMISSION_CONTROL, 1, 1, handleRandom, NULL); + addCommand(COMMAND_STATS, PERMISSION_READ, 0, 0, handleStats, NULL); + addCommand(COMMAND_CLEAR_ERROR, PERMISSION_CONTROL, 0, 0, handleClearError, NULL); + addCommand(COMMAND_LIST, PERMISSION_READ, 1, -1, handleList, NULL); + addCommand(COMMAND_MOVE, PERMISSION_CONTROL, 2, 2, handleMove, NULL); + addCommand(COMMAND_MOVEID, PERMISSION_CONTROL, 2, 2, handleMoveId, NULL); + addCommand(COMMAND_SWAP, PERMISSION_CONTROL, 2, 2, handleSwap, NULL); + addCommand(COMMAND_SWAPID, PERMISSION_CONTROL, 2, 2, handleSwapId, NULL); + addCommand(COMMAND_SEEK, PERMISSION_CONTROL, 2, 2, handleSeek, NULL); + addCommand(COMMAND_SEEKID, PERMISSION_CONTROL, 2, 2, handleSeekId, NULL); + addCommand(COMMAND_LISTALLINFO, PERMISSION_READ, 0, 1, handleListAllInfo, NULL); + addCommand(COMMAND_PING, PERMISSION_NONE, 0, 0, handlePing, NULL); + addCommand(COMMAND_SETVOL, PERMISSION_CONTROL, 1, 1, handleSetVol, NULL); + addCommand(COMMAND_PASSWORD, PERMISSION_NONE, 1, 1, handlePassword, NULL); + addCommand(COMMAND_CROSSFADE, PERMISSION_CONTROL, 1, 1, handleCrossfade, NULL); + addCommand(COMMAND_URL_HANDLERS, PERMISSION_READ, 0, 0, handleUrlHandlers, NULL); + addCommand(COMMAND_PLCHANGES, PERMISSION_READ, 1, 1, handlePlaylistChanges, NULL); + addCommand(COMMAND_PLCHANGESPOSID, PERMISSION_READ, 1, 1, handlePlaylistChangesPosId, NULL); + addCommand(COMMAND_ENABLE_DEV, PERMISSION_ADMIN, 1, 1, handleEnableDevice, NULL); + addCommand(COMMAND_DISABLE_DEV, PERMISSION_ADMIN, 1, 1, handleDisableDevice, NULL); + addCommand(COMMAND_DEVICES, PERMISSION_READ, 0, 0, handleDevices, NULL); + addCommand(COMMAND_COMMANDS, PERMISSION_NONE, 0, 0, handleCommands, NULL); + addCommand(COMMAND_NOTCOMMANDS, PERMISSION_NONE, 0, 0, handleNotcommands, NULL); + addCommand(COMMAND_PLAYLISTCLEAR, PERMISSION_CONTROL, 1, 1, handlePlaylistClear, NULL); + addCommand(COMMAND_PLAYLISTADD, PERMISSION_CONTROL, 2, 2, handlePlaylistAdd, NULL); + addCommand(COMMAND_PLAYLISTFIND, PERMISSION_READ, 2, -1, handlePlaylistFind, NULL); + addCommand(COMMAND_PLAYLISTSEARCH, PERMISSION_READ, 2, -1, handlePlaylistSearch, NULL); + addCommand(COMMAND_PLAYLISTMOVE, PERMISSION_CONTROL, 3, 3, handlePlaylistMove, NULL); + addCommand(COMMAND_PLAYLISTDELETE, PERMISSION_CONTROL, 2, 2, handlePlaylistDelete, NULL); + addCommand(COMMAND_TAGTYPES, PERMISSION_READ, 0, 0, handleTagTypes, NULL); + addCommand(COMMAND_COUNT, PERMISSION_READ, 2, -1, handleCount, NULL); + addCommand(COMMAND_RENAME, PERMISSION_CONTROL, 2, 2, handleRename, NULL); + + sortList(commandList); +} + +void finishCommands(void) +{ + freeList(commandList); +} + +static int checkArgcAndPermission(CommandEntry * cmd, int fd, + int permission, int argc, char *argv[]) +{ + int min = cmd->min + 1; + int max = cmd->max + 1; + + if (cmd->reqPermission != (permission & cmd->reqPermission)) { + if (fd) { + commandError(fd, ACK_ERROR_PERMISSION, + "you don't have permission for \"%s\"", + cmd->cmd); + } + return -1; + } + + if (min == 0) + return 0; + + if (min == max && max != argc) { + if (fd) { + commandError(fd, ACK_ERROR_ARG, + "wrong number of arguments for \"%s\"", + argv[0]); + } + return -1; + } else if (argc < min) { + if (fd) { + commandError(fd, ACK_ERROR_ARG, + "too few arguments for \"%s\"", argv[0]); + } + return -1; + } else if (argc > max && max /* != 0 */ ) { + if (fd) { + commandError(fd, ACK_ERROR_ARG, + "too many arguments for \"%s\"", argv[0]); + } + return -1; + } else + return 0; +} + +static CommandEntry *getCommandEntryAndCheckArgcAndPermission(int fd, + int *permission, + int argc, + char *argv[]) +{ + static char unknown[] = ""; + CommandEntry *cmd; + + current_command = unknown; + + if (argc == 0) + return NULL; + + if (!findInList(commandList, argv[0], (void *)&cmd)) { + if (fd) { + commandError(fd, ACK_ERROR_UNKNOWN, + "unknown command \"%s\"", argv[0]); + } + return NULL; + } + + current_command = cmd->cmd; + + if (checkArgcAndPermission(cmd, fd, *permission, argc, argv) < 0) { + return NULL; + } + + return cmd; +} + +static CommandEntry *getCommandEntryFromString(char *string, int *permission) +{ + CommandEntry *cmd = NULL; + char *argv[COMMAND_ARGV_MAX] = { NULL }; + int argc = buffer2array(string, argv, COMMAND_ARGV_MAX); + + if (0 == argc) + return NULL; + + cmd = getCommandEntryAndCheckArgcAndPermission(0, permission, + argc, argv); + + return cmd; +} + +static int processCommandInternal(int fd, int *permission, + char *commandString, struct strnode *cmdnode) +{ + int argc; + char *argv[COMMAND_ARGV_MAX] = { NULL }; + CommandEntry *cmd; + int ret = -1; + + argc = buffer2array(commandString, argv, COMMAND_ARGV_MAX); + + if (argc == 0) + return 0; + + if ((cmd = getCommandEntryAndCheckArgcAndPermission(fd, permission, + argc, argv))) { + if (!cmdnode || !cmd->listHandler) { + ret = cmd->handler(fd, permission, argc, argv); + } else { + ret = cmd->listHandler(fd, permission, argc, argv, + cmdnode, cmd); + } + } + + current_command = NULL; + + return ret; +} + +int processListOfCommands(int fd, int *permission, int *expired, + int listOK, struct strnode *list) +{ + struct strnode *cur = list; + int ret = 0; + + command_listNum = 0; + + while (cur) { + DEBUG("processListOfCommands: process command \"%s\"\n", + cur->data); + ret = processCommandInternal(fd, permission, cur->data, cur); + DEBUG("processListOfCommands: command returned %i\n", ret); + if (ret != 0 || (*expired) != 0) + goto out; + else if (listOK) + fdprintf(fd, "list_OK\n"); + command_listNum++; + cur = cur->next; + } +out: + command_listNum = 0; + return ret; +} + +int processCommand(int fd, int *permission, char *commandString) +{ + return processCommandInternal(fd, permission, commandString, NULL); +} + +mpd_fprintf_ void commandError(int fd, int error, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + + if (current_command && fd != STDERR_FILENO) { + fdprintf(fd, "ACK [%i@%i] {%s} ", + (int)error, command_listNum, current_command); + vfdprintf(fd, fmt, args); + fdprintf(fd, "\n"); + current_command = NULL; + } else { + fdprintf(STDERR_FILENO, "ACK [%i@%i] ", + (int)error, command_listNum); + vfdprintf(STDERR_FILENO, fmt, args); + fdprintf(STDERR_FILENO, "\n"); + } + + va_end(args); +} |