view src/libaudacious/beepctrl.c @ 2503:10692383c103 trunk

[svn] first try for libid3tag integration. this improved libid3tag supports vfs operations and is capable of adding id3v2 tag to files which doesn't have id3v2 tag ever.
author yaz
date Sun, 11 Feb 2007 05:19:07 -0800
parents f24ae4f40e29
children 5fea66ad690c
line wrap: on
line source

/*  Audacious
 *  Copyright (C) 2005-2007  Audacious team
 *
 *  XMMS - Cross-platform multimedia player
 *  Copyright (C) 1998-2003  Peter Alm, Mikael Alm, Olle Hallnas,
 *                           Thomas Nilsson and 4Front Technologies
 *  Copyright (C) 1999-2003  Haavard Kvaalen
 *
 *  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; under version 2 of the License.
 *
 *  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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif

#include <glib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "beepctrl.h"
#include "audacious/controlsocket.h"
#include "libaudacious/configdb.h"

#include <netdb.h>
#include <netinet/in.h>
#include <unistd.h>
#include <grp.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <arpa/inet.h>

/* overrides audacious_get_session_uri(). */
gchar *audacious_session_uri = NULL;
gint  audacious_session_type = 0;

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

static gpointer
remote_read_packet(gint fd)
{
    gpointer data = NULL;
    ServerPktHeader pkt_hdr = { 0, 0 };

    if (read(fd, &pkt_hdr, sizeof(ServerPktHeader)) ==
        sizeof(ServerPktHeader))
    {
        if (pkt_hdr.version == XMMS_PROTOCOL_VERSION && 
		pkt_hdr.data_length > 0)
        {
            size_t data_length = pkt_hdr.data_length;
            data = g_malloc0(data_length);
            if ((size_t)read(fd, data, data_length) < data_length)
            {
                g_free(data);
                data = NULL;
            }
        }
    }

    return data;
}

static void
remote_read_ack(gint fd)
{
    gpointer data;

    data = remote_read_packet(fd);
    if (data)
        g_free(data);

}

static void
remote_send_packet(gint fd, guint32 command, gpointer data,
                   guint32 data_length)
{
    ClientPktHeader pkt_hdr;

    memset(&pkt_hdr, '\0', sizeof(ClientPktHeader));

    pkt_hdr.version = XMMS_PROTOCOL_VERSION;
    pkt_hdr.command = command;
    pkt_hdr.data_length = data_length;
    if ((size_t)write(fd, &pkt_hdr, sizeof(ClientPktHeader)) < sizeof(pkt_hdr))
    {
        g_warning("remote_send_packet: failed to write packet header");
        return;
    }
    if (data_length && data)
        if( data_length != write(fd, data, data_length))
        {
            g_warning("remote_send_packet: failed to write packet");
            return;
        }
}

static void
remote_send_guint32(gint session, guint32 cmd, guint32 val)
{
    gint fd;

    if ((fd = xmms_connect_to_session(session)) == -1)
        return;
    remote_send_packet(fd, cmd, &val, sizeof(guint32));
    remote_read_ack(fd);
    close(fd);
}

static void
remote_send_boolean(gint session, guint32 cmd, gboolean val)
{
    gint fd;

    if ((fd = xmms_connect_to_session(session)) == -1)
        return;
    remote_send_packet(fd, cmd, &val, sizeof(gboolean));
    remote_read_ack(fd);
    close(fd);
}

static void
remote_send_gfloat(gint session, guint32 cmd, gfloat value)
{
    gint fd;

    if ((fd = xmms_connect_to_session(session)) == -1)
        return;
    remote_send_packet(fd, cmd, &value, sizeof(gfloat));
    remote_read_ack(fd);
    close(fd);
}

static void
remote_send_string(gint session, guint32 cmd, gchar * string)
{
    gint fd;

    if ((fd = xmms_connect_to_session(session)) == -1)
        return;
    remote_send_packet(fd, cmd, string, string ? strlen(string) + 1 : 0);
    remote_read_ack(fd);
    close(fd);
}

static gboolean
remote_cmd(gint session, guint32 cmd)
{
    gint fd;

    if ((fd = xmms_connect_to_session(session)) == -1)
        return FALSE;
    remote_send_packet(fd, cmd, NULL, 0);
    remote_read_ack(fd);
    close(fd);

    return TRUE;
}

static gboolean
remote_get_gboolean(gint session, gint cmd)
{
    gboolean ret = FALSE;
    gpointer data;
    gint fd;

    if ((fd = xmms_connect_to_session(session)) == -1)
        return ret;
    remote_send_packet(fd, cmd, NULL, 0);
    data = remote_read_packet(fd);
    if (data) {
        ret = *((gboolean *) data);
        g_free(data);
    }
    remote_read_ack(fd);
    close(fd);

    return ret;
}

static guint32
remote_get_gint(gint session, gint cmd)
{
    gpointer data;
    gint fd, ret = 0;

    if ((fd = xmms_connect_to_session(session)) == -1)
        return ret;
    remote_send_packet(fd, cmd, NULL, 0);
    data = remote_read_packet(fd);
    if (data) {
        ret = *((gint *) data);
        g_free(data);
    }
    remote_read_ack(fd);
    close(fd);
    return ret;
}

static gfloat
remote_get_gfloat(gint session, gint cmd)
{
    gpointer data;
    gint fd;
    gfloat ret = 0.0;

    if ((fd = xmms_connect_to_session(session)) == -1)
        return ret;
    remote_send_packet(fd, cmd, NULL, 0);
    data = remote_read_packet(fd);
    if (data) {
        ret = *((gfloat *) data);
        g_free(data);
    }
    remote_read_ack(fd);
    close(fd);
    return ret;
}

gchar *
remote_get_string(gint session, gint cmd)
{
    gpointer data;
    gint fd;

    if ((fd = xmms_connect_to_session(session)) == -1)
        return NULL;
    remote_send_packet(fd, cmd, NULL, 0);
    data = remote_read_packet(fd);
    remote_read_ack(fd);
    close(fd);
    return data;
}

gchar *
remote_get_string_pos(gint session, gint cmd, guint32 pos)
{
    gpointer data;
    gint fd;

    if ((fd = xmms_connect_to_session(session)) == -1)
        return NULL;
    remote_send_packet(fd, cmd, &pos, sizeof(guint32));
    data = remote_read_packet(fd);
    remote_read_ack(fd);
    close(fd);
    return data;
}

/**
 * audacious_set_session_uri:
 * @uri: The session URI to set the client API to.
 *
 * Sets the Session URI where Audacious can be reached at.
 **/
void
audacious_set_session_uri(gchar *uri)
{
    audacious_session_uri = uri;
}

/**
 * audacious_get_session_uri:
 * @session: Legacy XMMS session id (usually 0).
 *
 * Attempts to determine what the Session URI may be.
 *
 * Return value: A session URI.
 **/
gchar *
audacious_get_session_uri(gint session)
{
    ConfigDb *db;
    gchar *value = NULL;

    if (audacious_session_uri != NULL)
    {
        return g_strdup(audacious_session_uri);
    }

    if (audacious_session_type != AUDACIOUS_TYPE_UNIX)
    {
        db = bmp_cfg_db_open();

        bmp_cfg_db_get_string(db, NULL, "listen_uri_base", &value);

        bmp_cfg_db_close(db);
    }

    if (value == NULL)
        return g_strdup_printf("unix://localhost/%s/%s_%s.%d", g_get_tmp_dir(),
		CTRLSOCKET_NAME, g_get_user_name(), session);

    audacious_session_uri = value;

    return value;
}

/**
 * audacious_set_session_type:
 * @type: The type to set the session type to.
 *
 * Sets the type of session used by the audacious server.
 **/
void
audacious_set_session_type(gint type)
{
   audacious_session_type = type;
}

/**
 * audacious_determine_session_type:
 * @session: Legacy XMMS session id (usually 0).
 *
 * Attempts to determine what the session type may be.
 **/
gint
audacious_determine_session_type(gint session)
{
    gchar *uri = NULL;

    if (audacious_session_type != 0)
    {
        return audacious_session_type;
    }

    uri = audacious_get_session_uri(session);

    if (!g_strncasecmp(uri, "tcp://", 6))
        audacious_session_type = AUDACIOUS_TYPE_TCP;
    else
        audacious_session_type = AUDACIOUS_TYPE_UNIX;

    if (audacious_session_type == 0)
        audacious_session_type = AUDACIOUS_TYPE_UNIX;

    /* memory leak! */
    g_free(uri);

    return audacious_session_type;
}

/* tcp://192.168.100.1:5900/zyzychynxi389xvmfewqaxznvnw */

/**
 * audacious_decode_tcp_uri:
 * @session: The legacy XMMS session id (usually 0).
 * @in: A TCP:// Session URI to decode.
 * @host: Pointer to a host buffer.
 * @port: Pointer to the TCP port.
 * @key: Pointer to a security key buffer.
 *
 * Decodes a tcp:// session URI.
 **/
void
audacious_decode_tcp_uri(gint session, gchar *in, gchar **host, gint *port, gchar **key)
{
    static gchar *workbuf, *keybuf;
    gint iport;
    gchar *tmp = g_strdup(in);

    /* split out the host/port and key */
    workbuf = tmp;
    workbuf += 6;

    keybuf = strchr(workbuf, '/');
    *keybuf++ = '\0';

    *key = g_strdup(keybuf);

    if (strchr(workbuf, ':') == NULL)
    {
        *host = g_strdup(workbuf);
        *port = 37370 + session;
    }
    else
    {
        gchar *hostbuf = NULL;
        sscanf(workbuf, "%s:%d", hostbuf, &iport);

        *port = iport + session;
    }

    g_free(tmp);
}

/* unix://localhost/tmp/audacious_nenolod.0 */

/**
 * audacious_decode_unix_uri:
 * @session: The legacy XMMS session id (usually 0).
 * @in: A UNIX:// Session URI to decode.
 * @key: Pointer to a UNIX path buffer.
 *
 * Decodes a unix:// session URI.
 **/
void
audacious_decode_unix_uri(gint session, gchar *in, gchar **key)
{
    static gchar *workbuf, *keybuf;
    gchar *tmp = g_strdup(in);

    /* split out the host/port and key */
    workbuf = tmp;
    workbuf += 7;

    keybuf = strchr(workbuf, '/');
    *keybuf++ = '\0';

    *key = g_strdup(keybuf);

    g_free(tmp);
}

/**
 * xmms_connect_to_session:
 * @session: Legacy XMMS-style session identifier.
 *
 * Connects to an audacious server.
 *
 * Return value: an FD on success, otherwise -1.
 **/
gint
xmms_connect_to_session(gint session)
{
    gint fd;
    gint type = audacious_determine_session_type(session);
    gchar *uri = audacious_get_session_uri(session);

    if (type == AUDACIOUS_TYPE_UNIX)
    {
        if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) != -1)
        {
            uid_t stored_uid, euid;
            struct sockaddr_un saddr;
	    gchar *path;

            saddr.sun_family = AF_UNIX;
            stored_uid = getuid();
            euid = geteuid();
            setuid(euid);
	    
	    audacious_decode_unix_uri(session, uri, &path);

	    g_strlcpy(saddr.sun_path, path, 108);
            g_free(path);
            setreuid(stored_uid, euid);

	    g_free(uri);

            if (connect(fd, (struct sockaddr *) &saddr, sizeof(saddr)) != -1)
                return fd;
        }
    }
    else
    {
        if ((fd = socket(AF_INET, SOCK_STREAM, 0)) != -1)
        {
	    struct hostent *hp;
	    struct sockaddr_in saddr;
	    gchar *host, *key;
	    gint port;

	    audacious_decode_tcp_uri(session, uri, &host, &port, &key);

            /* resolve it */
            if ((hp = gethostbyname(host)) == NULL)
            {
                 close(fd);
                 return -1;
            }

	    memset(&saddr, '\0', sizeof(saddr));
            saddr.sin_family = AF_INET;
            saddr.sin_port = htons(port);
            memcpy(&saddr.sin_addr, hp->h_addr, hp->h_length);

            g_free(host);
            g_free(key);

	    g_free(uri);

            if (connect(fd, (struct sockaddr *) &saddr, sizeof(saddr)) != -1)
                return fd;
        }
    }

    close(fd);
    return -1;
}

/**
 * xmms_remote_playlist:
 * @session: Legacy XMMS-style session identifier.
 * @list: A list of URIs to play.
 * @num: Number of URIs to play.
 * @enqueue: Whether or not the new playlist should be added on, or replace the current playlist.
 *
 * Sends a playlist to audacious.
 **/
void
xmms_remote_playlist(gint session, gchar ** list, gint num, gboolean enqueue)
{
    gint fd, i;
    gchar *data, *ptr;
    gint data_length;
    guint32 len;

    g_return_if_fail(list != NULL);
    g_return_if_fail(num > 0);

    if (!enqueue)
        xmms_remote_playlist_clear(session);

    if ((fd = xmms_connect_to_session(session)) == -1)
        return;

    for (i = 0, data_length = 0; i < num; i++)
        data_length += (((strlen(list[i]) + 1) + 3) / 4) * 4 + 4;
    if (data_length) {
        data_length += 4;
        data = g_malloc(data_length);
        for (i = 0, ptr = data; i < num; i++) {
            len = strlen(list[i]) + 1;
            *((guint32 *) ptr) = len;
            ptr += 4;
            memcpy(ptr, list[i], len);
            ptr += ((len + 3) / 4) * 4;
        }
        *((guint32 *) ptr) = 0;
        remote_send_packet(fd, CMD_PLAYLIST_ADD, data, data_length);
        remote_read_ack(fd);
        close(fd);
        g_free(data);
    }

    if (!enqueue)
        xmms_remote_play(session);
}

/**
 * xmms_remote_get_version:
 * @session: Legacy XMMS-style session identifier.
 *
 * Queries audacious for it's protocol version.
 *
 * Return value: The protocol version used by Audacious.
 **/
gint
xmms_remote_get_version(gint session)
{
    return remote_get_gint(session, CMD_GET_VERSION);
}

/**
 * xmms_remote_play_files:
 * @session: Legacy XMMS-style session identifier.
 * @list: A GList of URIs to play.
 *
 * Sends a list of URIs to Audacious to play.
 **/
void
xmms_remote_play_files(gint session, GList * list)
{
    g_return_if_fail(list != NULL);

    xmms_remote_playlist_clear(session);
    xmms_remote_playlist_add(session, list);
    xmms_remote_play(session);
}

/**
 * xmms_remote_playlist_add:
 * @session: Legacy XMMS-style session identifier.
 * @list: A GList of URIs to add to the playlist.
 *
 * Sends a list of URIs to Audacious to add to the playlist.
 **/
void
xmms_remote_playlist_add(gint session, GList * list)
{
    gchar **str_list;
    GList *node;
    gint i, num;

    g_return_if_fail(list != NULL);

    num = g_list_length(list);
    str_list = g_malloc0(num * sizeof(gchar *));
    for (i = 0, node = list; i < num && node; i++, node = g_list_next(node))
        str_list[i] = node->data;

    xmms_remote_playlist(session, str_list, num, TRUE);
    g_free(str_list);
}

/**
 * xmms_remote_playlist_delete:
 * @session: Legacy XMMS-style session identifier.
 * @pos: The playlist position to delete.
 *
 * Deletes a playlist entry.
 **/
void
xmms_remote_playlist_delete(gint session, gint pos)
{
    remote_send_guint32(session, CMD_PLAYLIST_DELETE, pos);
}

/**
 * xmms_remote_play:
 * @session: Legacy XMMS-style session identifier.
 *
 * Tells audacious to begin playback.
 **/
void
xmms_remote_play(gint session)
{
    remote_cmd(session, CMD_PLAY);
}

/**
 * xmms_remote_pause:
 * @session: Legacy XMMS-style session identifier.
 *
 * Tells audacious to pause.
 **/
void
xmms_remote_pause(gint session)
{
    remote_cmd(session, CMD_PAUSE);
}

/**
 * xmms_remote_stop:
 * @session: Legacy XMMS-style session identifier.
 *
 * Tells audacious to stop.
 **/
void
xmms_remote_stop(gint session)
{
    remote_cmd(session, CMD_STOP);
}

/**
 * xmms_remote_play_pause:
 * @session: Legacy XMMS-style session identifier.
 *
 * Tells audacious to either play or pause.
 **/
void
xmms_remote_play_pause(gint session)
{
    remote_cmd(session, CMD_PLAY_PAUSE);
}

/**
 * xmms_remote_is_playing:
 * @session: Legacy XMMS-style session identifier.
 *
 * Queries audacious about whether it is playing or not.
 *
 * Return value: TRUE if playing, FALSE otherwise.
 **/
gboolean
xmms_remote_is_playing(gint session)
{
    return remote_get_gboolean(session, CMD_IS_PLAYING);
}

/**
 * xmms_remote_is_paused:
 * @session: Legacy XMMS-style session identifier.
 *
 * Queries audacious about whether it is paused or not.
 *
 * Return value: TRUE if playing, FALSE otherwise.
 **/
gboolean
xmms_remote_is_paused(gint session)
{
    return remote_get_gboolean(session, CMD_IS_PAUSED);
}

/**
 * xmms_remote_get_playlist_pos:
 * @session: Legacy XMMS-style session identifier.
 *
 * Queries audacious about the current playlist position.
 *
 * Return value: The current playlist position.
 **/
gint
xmms_remote_get_playlist_pos(gint session)
{
    return remote_get_gint(session, CMD_GET_PLAYLIST_POS);
}

/**
 * xmms_remote_set_playlist_pos:
 * @session: Legacy XMMS-style session identifier.
 * @pos: Playlist position to jump to.
 *
 * Tells audacious to jump to a different playlist position.
 **/
void
xmms_remote_set_playlist_pos(gint session, gint pos)
{
    remote_send_guint32(session, CMD_SET_PLAYLIST_POS, pos);
}

/**
 * xmms_remote_get_playlist_length:
 * @session: Legacy XMMS-style session identifier.
 *
 * Queries audacious about the current playlist length.
 *
 * Return value: The amount of entries in the playlist.
 **/
gint
xmms_remote_get_playlist_length(gint session)
{
    return remote_get_gint(session, CMD_GET_PLAYLIST_LENGTH);
}

/**
 * xmms_remote_playlist_clear:
 * @session: Legacy XMMS-style session identifier.
 *
 * Clears the playlist.
 **/
void
xmms_remote_playlist_clear(gint session)
{
    remote_cmd(session, CMD_PLAYLIST_CLEAR);
}

/**
 * xmms_remote_get_output_time:
 * @session: Legacy XMMS-style session identifier.
 *
 * Queries audacious about the current output position.
 *
 * Return value: The current output position.
 **/
gint
xmms_remote_get_output_time(gint session)
{
    return remote_get_gint(session, CMD_GET_OUTPUT_TIME);
}

/**
 * xmms_remote_jump_to_time:
 * @session: Legacy XMMS-style session identifier.
 * @pos: The time (in milliseconds) to jump to.
 *
 * Tells audacious to seek to a new time position.
 **/
void
xmms_remote_jump_to_time(gint session, gint pos)
{
    remote_send_guint32(session, CMD_JUMP_TO_TIME, pos);
}

/**
 * xmms_remote_get_volume:
 * @session: Legacy XMMS-style session identifier.
 * @vl: Pointer to integer containing the left channel's volume.
 * @vr: Pointer to integer containing the right channel's volume.
 *
 * Queries audacious about the current volume.
 **/
void
xmms_remote_get_volume(gint session, gint * vl, gint * vr)
{
    gint fd;
    gpointer data;

    if ((fd = xmms_connect_to_session(session)) == -1)
        return;

    remote_send_packet(fd, CMD_GET_VOLUME, NULL, 0);
    data = remote_read_packet(fd);
    if (data) {
        *vl = ((guint32 *) data)[0];
        *vr = ((guint32 *) data)[1];
        g_free(data);
    }
    remote_read_ack(fd);
    close(fd);
}

/**
 * xmms_remote_get_main_volume:
 * @session: Legacy XMMS-style session identifier.
 *
 * Queries audacious about the current volume.
 *
 * Return value: The current volume.
 **/
gint
xmms_remote_get_main_volume(gint session)
{
    gint vl, vr;

    xmms_remote_get_volume(session, &vl, &vr);

    return (vl > vr) ? vl : vr;
}

/**
 * xmms_remote_get_balance:
 * @session: Legacy XMMS-style session identifier.
 *
 * Queries audacious about the current balance.
 *
 * Return value: The current balance.
 **/
gint
xmms_remote_get_balance(gint session)
{
    return remote_get_gint(session, CMD_GET_BALANCE);
}

/**
 * xmms_remote_set_volume:
 * @session: Legacy XMMS-style session identifier.
 * @vl: The volume for the left channel.
 * @vr: The volume for the right channel.
 *
 * Sets the volume for the left and right channels in Audacious.
 **/
void
xmms_remote_set_volume(gint session, gint vl, gint vr)
{
    gint fd;
    guint32 v[2];

    if (vl < 0)
        vl = 0;
    if (vl > 100)
        vl = 100;
    if (vr < 0)
        vr = 0;
    if (vr > 100)
        vr = 100;

    if ((fd = xmms_connect_to_session(session)) == -1)
        return;
    v[0] = vl;
    v[1] = vr;
    remote_send_packet(fd, CMD_SET_VOLUME, v, 2 * sizeof(guint32));
    remote_read_ack(fd);
    close(fd);
}

/**
 * xmms_remote_set_main_volume:
 * @session: Legacy XMMS-style session identifier.
 * @v: The volume to set.
 *
 * Sets the volume in Audacious.
 **/
void
xmms_remote_set_main_volume(gint session, gint v)
{
    gint b, vl, vr;

    b = xmms_remote_get_balance(session);

    if (b < 0) {
        vl = v;
        vr = (v * (100 - abs(b))) / 100;
    }
    else if (b > 0) {
        vl = (v * (100 - b)) / 100;
        vr = v;
    }
    else
        vl = vr = v;
    xmms_remote_set_volume(session, vl, vr);
}

/**
 * xmms_remote_set_balance:
 * @session: Legacy XMMS-style session identifier.
 * @b: The balance to set.
 *
 * Sets the balance in Audacious.
 **/
void
xmms_remote_set_balance(gint session, gint b)
{
    gint v, vl, vr;

    if (b < -100)
        b = -100;
    if (b > 100)
        b = 100;

    v = xmms_remote_get_main_volume(session);

    if (b < 0) {
        vl = v;
        vr = (v * (100 - abs(b))) / 100;
    }
    else if (b > 0) {
        vl = (v * (100 - b)) / 100;
        vr = v;
    }
    else
        vl = vr = v;
    xmms_remote_set_volume(session, vl, vr);
}

/**
 * xmms_remote_get_skin:
 * @session: Legacy XMMS-style session identifier.
 *
 * Queries Audacious about it's skin.
 *
 * Return value: A path to the currently selected skin.
 **/
gchar *
xmms_remote_get_skin(gint session)
{
    return remote_get_string(session, CMD_GET_SKIN);
}

/**
 * xmms_remote_set_skin:
 * @session: Legacy XMMS-style session identifier.
 * @skinfile: Path to a skinfile to use with Audacious.
 *
 * Tells audacious to start using the skinfile provided.
 **/
void
xmms_remote_set_skin(gint session, gchar * skinfile)
{
    remote_send_string(session, CMD_SET_SKIN, skinfile);
}

/**
 * xmms_remote_get_playlist_file:
 * @session: Legacy XMMS-style session identifier.
 * @pos: The playlist position to query for.
 *
 * Queries Audacious about a playlist entry's file.
 *
 * Return value: A path to the file in the playlist at %pos position.
 **/
gchar *
xmms_remote_get_playlist_file(gint session, gint pos)
{
    return remote_get_string_pos(session, CMD_GET_PLAYLIST_FILE, pos);
}

/**
 * xmms_remote_get_playlist_title:
 * @session: Legacy XMMS-style session identifier.
 * @pos: The playlist position to query for.
 *
 * Queries Audacious about a playlist entry's title.
 *
 * Return value: The title for the entry in the playlist at %pos position.
 **/
gchar *
xmms_remote_get_playlist_title(gint session, gint pos)
{
    return remote_get_string_pos(session, CMD_GET_PLAYLIST_TITLE, pos);
}

/**
 * xmms_remote_get_playlist_time:
 * @session: Legacy XMMS-style session identifier.
 * @pos: The playlist position to query for.
 *
 * Queries Audacious about a playlist entry's length.
 *
 * Return value: The length of the entry in the playlist at %pos position.
 **/
gint
xmms_remote_get_playlist_time(gint session, gint pos)
{
    gpointer data;
    gint fd, ret = 0;
    guint32 p = pos;

    if ((fd = xmms_connect_to_session(session)) == -1)
        return ret;
    remote_send_packet(fd, CMD_GET_PLAYLIST_TIME, &p, sizeof(guint32));
    data = remote_read_packet(fd);
    if (data) {
        ret = *((gint *) data);
        g_free(data);
    }
    remote_read_ack(fd);
    close(fd);
    return ret;
}

/**
 * xmms_remote_get_info:
 * @session: Legacy XMMS-style session identifier.
 * @rate: Pointer to an integer containing the bitrate.
 * @freq: Pointer to an integer containing the frequency.
 * @nch: Pointer to an integer containing the number of channels.
 *
 * Queries Audacious about the current audio format.
 **/
void
xmms_remote_get_info(gint session, gint * rate, gint * freq, gint * nch)
{
    gint fd;
    gpointer data;

    if ((fd = xmms_connect_to_session(session)) == -1)
        return;
    remote_send_packet(fd, CMD_GET_INFO, NULL, 0);
    data = remote_read_packet(fd);
    if (data) {
        *rate = ((guint32 *) data)[0];
        *freq = ((guint32 *) data)[1];
        *nch = ((guint32 *) data)[2];
        g_free(data);
    }
    remote_read_ack(fd);
    close(fd);
}

/**
 * xmms_remote_get_eq_data:
 * @session: Legacy XMMS-style session identifier.
 *
 * Not implemented, present for compatibility with libxmms API.
 **/
void
xmms_remote_get_eq_data(gint session)
{
    /* Obsolete */
}

/**
 * xmms_remote_set_eq_data:
 * @session: Legacy XMMS-style session identifier.
 *
 * Not implemented, present for compatibility with libxmms API.
 **/
void
xmms_remote_set_eq_data(gint session)
{
    /* Obsolete */
}

/**
 * xmms_remote_pl_win_toggle:
 * @session: Legacy XMMS-style session identifier.
 * @show: Whether or not to show the playlist window.
 *
 * Toggles the playlist window's visibility.
 **/
void
xmms_remote_pl_win_toggle(gint session, gboolean show)
{
    remote_send_boolean(session, CMD_PL_WIN_TOGGLE, show);
}

/**
 * xmms_remote_eq_win_toggle:
 * @session: Legacy XMMS-style session identifier.
 * @show: Whether or not to show the equalizer window.
 *
 * Toggles the equalizer window's visibility.
 **/
void
xmms_remote_eq_win_toggle(gint session, gboolean show)
{
    remote_send_boolean(session, CMD_EQ_WIN_TOGGLE, show);
}

/**
 * xmms_remote_main_win_toggle:
 * @session: Legacy XMMS-style session identifier.
 * @show: Whether or not to show the main window.
 *
 * Toggles the main window's visibility.
 **/
void
xmms_remote_main_win_toggle(gint session, gboolean show)
{
    remote_send_boolean(session, CMD_MAIN_WIN_TOGGLE, show);
}

/**
 * xmms_remote_is_main_win:
 * @session: Legacy XMMS-style session identifier.
 *
 * Queries Audacious about the main window's visibility.
 *
 * Return value: TRUE if visible, FALSE otherwise.
 **/
gboolean
xmms_remote_is_main_win(gint session)
{
    return remote_get_gboolean(session, CMD_IS_MAIN_WIN);
}

/**
 * xmms_remote_is_pl_win:
 * @session: Legacy XMMS-style session identifier.
 *
 * Queries Audacious about the playlist window's visibility.
 *
 * Return value: TRUE if visible, FALSE otherwise.
 **/
gboolean
xmms_remote_is_pl_win(gint session)
{
    return remote_get_gboolean(session, CMD_IS_PL_WIN);
}

/**
 * xmms_remote_is_eq_win:
 * @session: Legacy XMMS-style session identifier.
 *
 * Queries Audacious about the equalizer window's visibility.
 *
 * Return value: TRUE if visible, FALSE otherwise.
 **/
gboolean
xmms_remote_is_eq_win(gint session)
{
    return remote_get_gboolean(session, CMD_IS_EQ_WIN);
}

/**
 * xmms_remote_show_prefs_box:
 * @session: Legacy XMMS-style session identifier.
 *
 * Tells audacious to show the preferences pane.
 **/
void
xmms_remote_show_prefs_box(gint session)
{
    remote_cmd(session, CMD_SHOW_PREFS_BOX);
}

/**
 * xmms_remote_show_jtf_box:
 * @session: Legacy XMMS-style session identifier.
 *
 * Tells audacious to show the Jump-to-File pane.
 **/
void
xmms_remote_show_jtf_box(gint session)
{
    remote_cmd(session, CMD_SHOW_JTF_BOX);
}

/**
 * xmms_remote_toggle_aot:
 * @session: Legacy XMMS-style session identifier.
 * @ontop: Whether or not Audacious should be always-on-top.
 *
 * Tells audacious to toggle the always-on-top feature.
 **/
void
xmms_remote_toggle_aot(gint session, gboolean ontop)
{
    remote_send_boolean(session, CMD_TOGGLE_AOT, ontop);
}

/**
 * xmms_remote_show_about_box:
 * @session: Legacy XMMS-style session identifier.
 *
 * Tells audacious to show the about pane.
 **/
void
xmms_remote_show_about_box(gint session)
{
    remote_cmd(session, CMD_SHOW_ABOUT_BOX);
}

/**
 * xmms_remote_eject:
 * @session: Legacy XMMS-style session identifier.
 *
 * Tells audacious to display the open files pane.
 **/
void
xmms_remote_eject(gint session)
{
    remote_cmd(session, CMD_EJECT);
}

/**
 * xmms_remote_playlist_prev:
 * @session: Legacy XMMS-style session identifier.
 *
 * Tells audacious to move backwards in the playlist.
 **/
void
xmms_remote_playlist_prev(gint session)
{
    remote_cmd(session, CMD_PLAYLIST_PREV);
}

/**
 * xmms_remote_playlist_next:
 * @session: Legacy XMMS-style session identifier.
 *
 * Tells audacious to move forward in the playlist.
 **/
void
xmms_remote_playlist_next(gint session)
{
    remote_cmd(session, CMD_PLAYLIST_NEXT);
}

/**
 * xmms_remote_playlist_add_url_string:
 * @session: Legacy XMMS-style session identifier.
 * @string: The URI to add.
 *
 * Tells audacious to add an URI to the playlist.
 **/
void
xmms_remote_playlist_add_url_string(gint session, gchar * string)
{
    g_return_if_fail(string != NULL);
    remote_send_string(session, CMD_PLAYLIST_ADD_URL_STRING, string);
}

/**
 * xmms_remote_playlist_enqueue_to_temp:
 * @session: Legacy XMMS-style session identifier.
 * @string: The URI to enqueue to a temporary playlist.
 *
 * Tells audacious to add an URI to a temporary playlist.
 **/
void
xmms_remote_playlist_enqueue_to_temp(gint session, gchar * string)
{
    g_return_if_fail(string != NULL);
    remote_send_string(session, CMD_PLAYLIST_ENQUEUE_TO_TEMP, string);
}

/**
 * xmms_remote_playlist_ins_url_string:
 * @session: Legacy XMMS-style session identifier.
 * @string: The URI to add.
 * @pos: The position to add the URI at.
 *
 * Tells audacious to add an URI to the playlist at a specific position.
 **/
void
xmms_remote_playlist_ins_url_string(gint session, gchar * string, gint pos)
{
    gint fd, size;
    gchar *packet;

    g_return_if_fail(string != NULL);

    size = strlen(string) + 1 + sizeof(gint);

    if ((fd = xmms_connect_to_session(session)) == -1)
        return;

    packet = g_malloc0(size);
    *((gint *) packet) = pos;
    strcpy(packet + sizeof(gint), string);
    remote_send_packet(fd, CMD_PLAYLIST_INS_URL_STRING, packet, size);
    remote_read_ack(fd);
    close(fd);
    g_free(packet);
}

/**
 * xmms_remote_is_running:
 * @session: Legacy XMMS-style session identifier.
 *
 * Checks to see if an Audacious server is running.
 *
 * Return value: TRUE if yes, otherwise FALSE.
 **/
gboolean
xmms_remote_is_running(gint session)
{
    return remote_cmd(session, CMD_PING);
}

/**
 * xmms_remote_toggle_repeat:
 * @session: Legacy XMMS-style session identifier.
 *
 * Tells audacious to toggle the repeat feature.
 **/
void
xmms_remote_toggle_repeat(gint session)
{
    remote_cmd(session, CMD_TOGGLE_REPEAT);
}

/**
 * xmms_remote_toggle_shuffle:
 * @session: Legacy XMMS-style session identifier.
 *
 * Tells audacious to toggle the shuffle feature.
 **/
void
xmms_remote_toggle_shuffle(gint session)
{
    remote_cmd(session, CMD_TOGGLE_SHUFFLE);
}

/**
 * xmms_remote_toggle_advance:
 * @session: Legacy XMMS-style session identifier.
 *
 * Tells audacious to toggle the no-playlist-advance feature.
 **/
void
xmms_remote_toggle_advance(int session)
{
    remote_cmd(session, CMD_TOGGLE_ADVANCE);
}

/**
 * xmms_remote_is_repeat:
 * @session: Legacy XMMS-style session identifier.
 *
 * Queries audacious about whether or not the repeat feature is active.
 *
 * Return value: TRUE if yes, otherwise FALSE.
 **/
gboolean
xmms_remote_is_repeat(gint session)
{
    return remote_get_gboolean(session, CMD_IS_REPEAT);
}

/**
 * xmms_remote_is_shuffle:
 * @session: Legacy XMMS-style session identifier.
 *
 * Queries audacious about whether or not the shuffle feature is active.
 *
 * Return value: TRUE if yes, otherwise FALSE.
 **/
gboolean
xmms_remote_is_shuffle(gint session)
{
    return remote_get_gboolean(session, CMD_IS_SHUFFLE);
}

/**
 * xmms_remote_is_advance:
 * @session: Legacy XMMS-style session identifier.
 *
 * Queries audacious about whether or not the no-playlist-advance feature is active.
 *
 * Return value: TRUE if yes, otherwise FALSE.
 **/
gboolean
xmms_remote_is_advance(gint session)
{
    return remote_get_gboolean(session, CMD_IS_ADVANCE);
}

/**
 * xmms_remote_playqueue_add:
 * @session: Legacy XMMS-style session identifier.
 * @pos: The playlist position to add to the queue.
 *
 * Tells audacious to add a playlist entry to the playqueue.
 **/
void
xmms_remote_playqueue_add(gint session, gint pos)
{
    remote_send_guint32(session, CMD_PLAYQUEUE_ADD, pos);
}

/**
 * xmms_remote_playqueue_remove:
 * @session: Legacy XMMS-style session identifier.
 * @pos: The playlist position to remove from the queue.
 *
 * Tells audacious to remove a playlist entry from the playqueue.
 **/
void
xmms_remote_playqueue_remove(gint session, gint pos)
{
    remote_send_guint32(session, CMD_PLAYQUEUE_REMOVE, pos);
}

/**
 * xmms_remote_playqueue_clear:
 * @session: Legacy XMMS-style session identifier.
 *
 * Tells audacious to clear the playqueue.
 **/
void
xmms_remote_playqueue_clear(gint session)
{
    remote_cmd(session, CMD_PLAYQUEUE_CLEAR);
}

/**
 * xmms_remote_get_playqueue_length:
 * @session: Legacy XMMS-style session identifier.
 *
 * Queries audacious about the playqueue's length.
 *
 * Return value: The number of entries in the playqueue.
 **/
gint
xmms_remote_get_playqueue_length(gint session)
{
    return remote_get_gint(session, CMD_GET_PLAYQUEUE_LENGTH);
}

/**
 * xmms_remote_playqueue_is_queued:
 * @session: Legacy XMMS-style session identifier.
 * @pos: Position to check queue for.
 *
 * Queries audacious about whether or not a playlist entry is in the playqueue.
 *
 * Return value: TRUE if yes, FALSE otherwise.
 **/
gboolean
xmms_remote_playqueue_is_queued(gint session, gint pos)
{
    gpointer data;
    gint fd, ret = 0;
    guint32 p = pos;

    if ((fd = xmms_connect_to_session(session)) == -1)
        return ret;
    remote_send_packet(fd, CMD_PLAYQUEUE_IS_QUEUED, &p, sizeof(guint32));
    data = remote_read_packet(fd);
    if (data) {
        ret = *((gint *) data);
        g_free(data);
    }
    remote_read_ack(fd);
    close(fd);
    return ret;
}

/**
 * xmms_remote_get_playqueue_position:
 * @session: Legacy XMMS-style session identifier.
 * @pos: Position to check queue for.
 *
 * Queries audacious about what the playqueue position is for a playlist entry.
 *
 * Return value: TRUE if yes, FALSE otherwise.
 **/
gint
xmms_remote_get_playqueue_position(gint session, gint pos)
{
    gpointer data;
    gint fd, ret = 0;
    guint32 p = pos;

    if ((fd = xmms_connect_to_session(session)) == -1)
        return ret;
    remote_send_packet(fd, CMD_PLAYQUEUE_GET_POS, &p, sizeof(guint32));
    data = remote_read_packet(fd);
    if (data) {
        ret = *((gint *) data);
        g_free(data);
    }
    remote_read_ack(fd);
    close(fd);
    return ret;
}

/**
 * xmms_remote_get_playqueue_queue_position:
 * @session: Legacy XMMS-style session identifier.
 * @pos: Position to check queue for.
 *
 * Queries audacious about what the playlist position is for a playqueue entry.
 *
 * Return value: TRUE if yes, FALSE otherwise.
 **/
gint
xmms_remote_get_playqueue_queue_position(gint session, gint pos)
{
    gpointer data;
    gint fd, ret = 0;
    guint32 p = pos;

    if ((fd = xmms_connect_to_session(session)) == -1)
        return ret;
    remote_send_packet(fd, CMD_PLAYQUEUE_GET_QPOS, &p, sizeof(guint32));
    data = remote_read_packet(fd);
    if (data) {
        ret = *((gint *) data);
        g_free(data);
    }
    remote_read_ack(fd);
    close(fd);
    return ret;
}

/**
 * xmms_remote_get_eq:
 * @session: Legacy XMMS-style session identifier.
 * @preamp: Pointer to value for preamp setting.
 * @bands: Pointer to array of band settings.
 *
 * Queries audacious about the equalizer settings.
 **/
void
xmms_remote_get_eq(gint session, gfloat * preamp, gfloat ** bands)
{
    gint fd;
    gpointer data;

    if (preamp)
        *preamp = 0.0;

    if (bands)
        *bands = NULL;

    if ((fd = xmms_connect_to_session(session)) == -1)
        return;
    remote_send_packet(fd, CMD_GET_EQ, NULL, 0);
    data = remote_read_packet(fd);
    if (data) {
            if (preamp)
                *preamp = *((gfloat *) data);
            if (bands)
                *bands =
                    (gfloat *) g_memdup((gfloat *) data + 1,
                                        10 * sizeof(gfloat));
        g_free(data);
    }
    remote_read_ack(fd);
    close(fd);
}

/**
 * xmms_remote_get_eq_preamp:
 * @session: Legacy XMMS-style session identifier.
 *
 * Queries audacious about the equalizer preamp's setting.
 *
 * Return value: The equalizer preamp's setting.
 **/
gfloat
xmms_remote_get_eq_preamp(gint session)
{
    return remote_get_gfloat(session, CMD_GET_EQ_PREAMP);
}

/**
 * xmms_remote_get_eq_band:
 * @session: Legacy XMMS-style session identifier.
 * @band: Which band to lookup the value for.
 *
 * Queries audacious about an equalizer band's value.
 *
 * Return value: The equalizer band's value.
 **/
gfloat
xmms_remote_get_eq_band(gint session, gint band)
{
    gint fd;
    gpointer data;
    gfloat val = 0.0;

    if ((fd = xmms_connect_to_session(session)) == -1)
        return val;
    remote_send_packet(fd, CMD_GET_EQ_BAND, &band, sizeof(band));
    data = remote_read_packet(fd);
    if (data) {
        val = *((gfloat *) data);
        g_free(data);
    }
    remote_read_ack(fd);
    close(fd);
    return val;
}

/**
 * xmms_remote_set_eq:
 * @session: Legacy XMMS-style session identifier.
 * @preamp: Value for preamp setting.
 * @bands: Array of band settings.
 *
 * Tells audacious to set the equalizer up using the provided values.
 **/
void
xmms_remote_set_eq(gint session, gfloat preamp, gfloat * bands)
{
    gint fd, i;
    gfloat data[11];

    g_return_if_fail(bands != NULL);

    if ((fd = xmms_connect_to_session(session)) == -1)
        return;
    data[0] = preamp;
    for (i = 0; i < 10; i++)
        data[i + 1] = bands[i];
    remote_send_packet(fd, CMD_SET_EQ, data, sizeof(data));
    remote_read_ack(fd);
    close(fd);
}

/**
 * xmms_remote_set_eq_preamp:
 * @session: Legacy XMMS-style session identifier.
 * @preamp: Value for preamp setting.
 *
 * Tells audacious to set the equalizer's preamp setting.
 **/
void
xmms_remote_set_eq_preamp(gint session, gfloat preamp)
{
    remote_send_gfloat(session, CMD_SET_EQ_PREAMP, preamp);
}

/**
 * xmms_remote_set_eq_band:
 * @session: Legacy XMMS-style session identifier.
 * @band: The band to set the value for.
 * @value: The value to set that band to.
 *
 * Tells audacious to set an equalizer band's setting.
 **/
void
xmms_remote_set_eq_band(gint session, gint band, gfloat value)
{
    gint fd;
    gchar data[sizeof(gint) + sizeof(gfloat)];

    if ((fd = xmms_connect_to_session(session)) == -1)
        return;
    *((gint *) data) = band;
    *((gfloat *) (data + sizeof(gint))) = value;
    remote_send_packet(fd, CMD_SET_EQ_BAND, data, sizeof(data));
    remote_read_ack(fd);
    close(fd);
}

/**
 * xmms_remote_quit:
 * @session: Legacy XMMS-style session identifier.
 *
 * Tells audacious to quit.
 **/
void
xmms_remote_quit(gint session)
{
    gint fd;

    if ((fd = xmms_connect_to_session(session)) == -1)
        return;
    remote_send_packet(fd, CMD_QUIT, NULL, 0);
    remote_read_ack(fd);
    close(fd);
}

/**
 * xmms_remote_activate:
 * @session: Legacy XMMS-style session identifier.
 *
 * Tells audacious to display the main window and become the selected window.
 **/
void
xmms_remote_activate(gint session)
{
    gint fd;

    if ((fd = xmms_connect_to_session(session)) == -1)
        return;
    remote_send_packet(fd, CMD_ACTIVATE, NULL, 0);
    remote_read_ack(fd);
    close(fd);
}