Files
mpd/src/command.c
Eric Wong 90847fc881 Replace strdup and {c,re,m}alloc with x* variants to check for OOM errors
I'm checking for zero-size allocations and assert()-ing them,
so we can more easily get backtraces and debug problems, but we'll
also allow -DNDEBUG people to live on the edge if they wish.

We do not rely on errno when checking for OOM errors because
some implementations of malloc do not set it, and malloc
is commonly overridden by userspace wrappers.

I've spent some time looking through the source and didn't find any
obvious places where we would explicitly allocate 0 bytes, so we
shouldn't trip any of those assertions.

We also avoid allocating zero bytes because C libraries don't
handle this consistently (some return NULL, some not); and it's
dangerous either way.

git-svn-id: https://svn.musicpd.org/mpd/trunk@4690 09075e82-0dd4-0310-85a5-a0d7c8717e4f
2006-08-26 06:25:57 +00:00

1149 lines
31 KiB
C

/* the Music Player Daemon (MPD)
* (c)2003-2006 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 "dbUtils.h"
#include "tag.h"
#include "utils.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_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 = NULL;
static int command_listNum = 0;
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 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[])
{
if (argc == 1) {
if (printDirectoryInfo(fd, NULL) < 0)
return -1;
else
return lsPlaylists(fd, "");
} else {
if (printDirectoryInfo(fd, argv[1]) < 0)
return -1;
else
return lsPlaylists(fd, argv[1]);
}
}
static int handleRm(int fd, int *permission, int argc, char *argv[])
{
return deletePlaylist(fd, argv[1]);
}
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 listHandleUpdate(int fd,
int *permission,
int argc,
char *argv[],
struct strnode *cmdnode, CommandEntry * cmd)
{
static List *pathList = NULL;
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;
}
/* 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;
}
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);
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) {
fdprintf(fd, "ACK [%i@%i] {%s} ",
(int)error, command_listNum, current_command);
current_command = NULL;
} else
fdprintf(STDERR_FILENO, "ACK [%i@%i] ",
(int)error, command_listNum);
vfdprintf(fd, fmt, args);
va_end(args);
fdprintf(fd, "\n");
}