mpd-client
changeset 0 77136249e5ee
equal deleted inserted replaced
-1:000000000000 0:77136249e5ee
       
     1 diff -r 171db9560cb5 clients/mpd/immsmpd.cc
       
     2 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
       
     3 +++ b/clients/mpd/immsmpd.cc	Mon May 19 00:21:51 2008 -0400
       
     4 @@ -0,0 +1,282 @@
       
     5 +#include "immsutil.h"
       
     6 +#include "clientstub.h"
       
     7 +
       
     8 +#include <glib.h>
       
     9 +#include <signal.h>
       
    10 +
       
    11 +#include "mpdinterface.h"
       
    12 +#include <list>
       
    13 +
       
    14 +
       
    15 +using std::endl;
       
    16 +using std::list;
       
    17 +using namespace mpd_interface;
       
    18 +
       
    19 +// interface for communication with IMMS server
       
    20 +// comes from clientstub.h
       
    21 +struct MPDOps;
       
    22 +typedef IMMSClient<MPDOps> MPDClient;
       
    23 +
       
    24 +int poll_time = 250; // miliseconds
       
    25 +const double reconnect_interval = 1.0; // seconds
       
    26 +GMainLoop *loop = nullptr;
       
    27 +GSource* ts;
       
    28 +
       
    29 +MPDClient imms;
       
    30 +Player mpd;
       
    31 +list<int> playqueue;
       
    32 +const list<int>::size_type pq_capacity = 1;
       
    33 +
       
    34 +
       
    35 +// that's the main function - it's called regularly and handles all the
       
    36 +// IMMS and MPD communication and decides what to do when anything happens
       
    37 +gboolean do_events(void *unused);
       
    38 +
       
    39 +// determines whether the currently playing song has been selected
       
    40 +// specifically by the user
       
    41 +bool next_jumped(); 
       
    42 +
       
    43 +void notify_song_ended(const Song& song, double elapsed_time, bool jumped);
       
    44 +
       
    45 +// resets the elapsed_timer and sets the jumped flag, too
       
    46 +void notify_song_started(const Song& song, double& elapsed_time,
       
    47 +    bool& jumped);
       
    48 +
       
    49 +void update_playqueue();
       
    50 +bool playqueue_contains(int song);
       
    51 +
       
    52 +void quit(int signum);
       
    53 +
       
    54 +
       
    55 +struct MPDOps
       
    56 +{
       
    57 +    // IMMS server's suggestion of the next song
       
    58 +    static void set_next(int next)
       
    59 +    {
       
    60 +      if(playqueue.size() < pq_capacity) {
       
    61 +	if(mpd.song(current).path() == mpd.song(next).path() ||
       
    62 +	    mpd.song(previous).path() == mpd.song(next).path() ||
       
    63 +	    playqueue_contains(next) || next >= mpd.playlist_length()) {
       
    64 +          update_playqueue();
       
    65 +	}
       
    66 +	else {
       
    67 +	  playqueue.push_back(next);
       
    68 +	}
       
    69 +      }
       
    70 +    }
       
    71 +    // no idea, works just peach as it is
       
    72 +    // asked mag about it, waiting for an answer
       
    73 +    // TODO if he doesn't answer, dig it out
       
    74 +    static void reset_selection() { }
       
    75 +    // path of the song at the given pos for IMMS server
       
    76 +    static string get_item(int index) {return mpd.song(index).path(); }
       
    77 +    // size of the playlist for the IMMS server
       
    78 +    static int get_length() { return mpd.playlist_length(); }
       
    79 +}; 
       
    80 +
       
    81 +int main(int argc, char **argv)
       
    82 +{
       
    83 +  // glib initialization
       
    84 +  loop = g_main_loop_new(nullptr, FALSE);
       
    85 +
       
    86 +  signal(SIGINT,  quit);
       
    87 +  signal(SIGTERM, quit);
       
    88 +  signal(SIGPIPE, SIG_IGN);
       
    89 +
       
    90 +  ts = g_timeout_source_new(poll_time);
       
    91 +  g_source_attach(ts, nullptr);
       
    92 +  g_source_set_callback(ts, (GSourceFunc)do_events, nullptr, nullptr);
       
    93 +  // end of glib init
       
    94 +
       
    95 +  g_main_loop_run(loop);
       
    96 +
       
    97 +  LOG(INFO) << "Exitting." << endl;
       
    98 +  return 0;
       
    99 +}
       
   100 +
       
   101 +gboolean do_events(void *unused)
       
   102 +{
       
   103 +  static const double time_inc = double(poll_time)/1000.0;
       
   104 +    // the time that ACTUALLY elapsed when playing the song (in seconds)
       
   105 +  static double elapsed_time = 0; 
       
   106 +  static bool jumped = false;
       
   107 +  static double reconnect_timeout = 0;
       
   108 +
       
   109 +  try {
       
   110 +    if(!mpd.connected()) {
       
   111 +      if(reconnect_timeout < time_inc) {
       
   112 +	LOG(INFO) << "Not connected to MPD. Attempting to connect..."
       
   113 +	  << endl;
       
   114 +	reconnect_timeout = reconnect_interval;
       
   115 +	mpd.connect();
       
   116 +	LOG(INFO) << "Connected to MPD." << endl;
       
   117 +	reconnect_timeout = 0;
       
   118 +      }
       
   119 +      else {
       
   120 +	reconnect_timeout -= time_inc;
       
   121 +	return TRUE;
       
   122 +      }
       
   123 +    }
       
   124 +    if(imms.check_connection()) imms.setup(0);
       
   125 +    if(!imms.isok()) return TRUE;
       
   126 +
       
   127 +    mpd.refresh();
       
   128 +
       
   129 +    if(mpd.playlist_changed()) {
       
   130 +      imms.playlist_changed(mpd.playlist_length());
       
   131 +      playqueue.clear();
       
   132 +    }
       
   133 +    if(mpd.radnom()) update_playqueue();
       
   134 +
       
   135 +    if(mpd.status_changed()) {
       
   136 +      if(mpd.status(current) == stopped) {
       
   137 +	notify_song_ended(mpd.song(current), elapsed_time, jumped);
       
   138 +      }
       
   139 +      else if(mpd.status(previous) == stopped &&
       
   140 +	  mpd.status(current)==playing) {
       
   141 +	notify_song_started(mpd.song(current), elapsed_time, jumped);
       
   142 +	
       
   143 +	// clear the song_changed() flag
       
   144 +	if(mpd.song_changed()) mpd.refresh(); 
       
   145 +      }
       
   146 +    }
       
   147 +    if(mpd.status(current) == playing) {
       
   148 +      elapsed_time += time_inc;
       
   149 +      if(mpd.song_changed()) {
       
   150 +	notify_song_ended(mpd.song(previous), elapsed_time, jumped);
       
   151 +
       
   152 +	if(!mpd.radnom() || playqueue.empty()) {
       
   153 +	  notify_song_started(mpd.song(current), elapsed_time, jumped);
       
   154 +	}
       
   155 +	else {
       
   156 +	  mpd.play_song(playqueue.front());
       
   157 +	  notify_song_started(mpd.song(playqueue.front()), elapsed_time,
       
   158 +	      jumped);
       
   159 +	  playqueue.erase(playqueue.begin());
       
   160 +	  update_playqueue();
       
   161 +	  // this could otherwise incorrectly set song_changed() flag
       
   162 +	  mpd.refresh(); 
       
   163 +	}
       
   164 +      }
       
   165 +      else {
       
   166 +	// fix the real timer if it's inconsistent with what player
       
   167 +	// says is the elapsed time of the current song
       
   168 +	if(elapsed_time - double(mpd.song(current).elapsed()) >= 3.0) {
       
   169 +	  if(mpd.song(current).elapsed() == 0) {
       
   170 +	    Song tmp_ended = mpd.song(current);
       
   171 +	    tmp_ended.set_length(int(elapsed_time) * 2);
       
   172 +	    notify_song_ended(tmp_ended, elapsed_time, jumped);
       
   173 +	    notify_song_started(mpd.song(current), elapsed_time, jumped);
       
   174 +	    jumped = false;
       
   175 +	  }
       
   176 +	  elapsed_time = mpd.song(current).elapsed();
       
   177 +	}
       
   178 +      }
       
   179 +    }
       
   180 +  }
       
   181 +  catch (connection_err) {
       
   182 +    if(reconnect_timeout >= reconnect_interval) {
       
   183 +	  LOG(INFO) << "Connecting to MPD was unsuccessful. "
       
   184 +	    "Next attempt in " << reconnect_interval << " second"
       
   185 +	    << (reconnect_interval == 1.0 ? "":"s") << "." << endl;
       
   186 +    }
       
   187 +    else {
       
   188 +      LOG(INFO) << "Disconnected from MPD." << endl;
       
   189 +    }
       
   190 +  }
       
   191 +  catch (mpd_err ex) {
       
   192 +    LOG(ERROR) << "Error: " << ex.message() << endl;
       
   193 +    g_main_quit(loop);
       
   194 +    loop = nullptr;
       
   195 +    exit(1);
       
   196 +  }
       
   197 +  return TRUE;
       
   198 +}
       
   199 +
       
   200 +// determines whether the song has been played whole
       
   201 +bool played_whole(double elapsed_time, int length)
       
   202 +{
       
   203 +  static const double abs_limit = 20.0;
       
   204 +  static const double coeficient = 0.08;
       
   205 +
       
   206 +  double rel_limit = double(length) * coeficient;
       
   207 +
       
   208 +  double limit = (abs_limit < rel_limit) ? abs_limit : rel_limit;
       
   209 +  return double(length)-elapsed_time < limit; 
       
   210 +}
       
   211 +// determines whether the song has been skipped early
       
   212 +// (thus being set as not a prefed one)
       
   213 +bool bad(double elapsed_time, int length)
       
   214 +{
       
   215 +  static const double abs_limit = 30.0;
       
   216 +  static const double coeficient = 0.13;
       
   217 +
       
   218 +  double rel_limit = double(length) * coeficient;
       
   219 +
       
   220 +  double limit = (rel_limit < abs_limit) ? rel_limit : abs_limit;
       
   221 +  return elapsed_time < limit;
       
   222 +}
       
   223 +bool next_jumped()
       
   224 +{
       
   225 +  if(mpd.radnom()) return false;
       
   226 +
       
   227 +  if(mpd.song(current).pos() == mpd.song(previous).pos()+1)
       
   228 +    return false;
       
   229 +  if(mpd.song(current).pos() == 0 &&
       
   230 +      mpd.song(previous).pos() == mpd.playlist_length()-1)
       
   231 +    return false;
       
   232 +
       
   233 +  return true;
       
   234 +}
       
   235 +void notify_song_ended(const Song& song, double elapsed_time, bool jumped)
       
   236 +{
       
   237 +  if(song.length() == Song::invalid_time) return;
       
   238 +
       
   239 +  imms.end_song(played_whole(elapsed_time, song.length()), jumped,
       
   240 +      bad(elapsed_time, song.length()) );
       
   241 +}
       
   242 +
       
   243 +void notify_song_started(const Song& song, double& elapsed_timer,
       
   244 +    bool& jumped)
       
   245 +{
       
   246 +  imms.start_song(song.pos(), song.path());
       
   247 +  elapsed_timer = 0;
       
   248 +  jumped = next_jumped();
       
   249 +  mpd.song_changed(); // clear the change notification
       
   250 +}
       
   251 +
       
   252 +void update_playqueue()
       
   253 +{
       
   254 +  static const int timeout_value = 3;
       
   255 +  static int timeout = 0;
       
   256 +
       
   257 +  if(timeout == 0)
       
   258 +  {
       
   259 +    if(playqueue.size() < pq_capacity) {
       
   260 +      list<int>::size_type i;
       
   261 +      for(i = pq_capacity - playqueue.size(); i!=0; i--) {
       
   262 +	imms.select_next();
       
   263 +      }
       
   264 +      timeout = timeout_value;
       
   265 +    }
       
   266 +  }
       
   267 +  else --timeout;
       
   268 +}
       
   269 +
       
   270 +bool playqueue_contains(int song)
       
   271 +{
       
   272 +  list<int>::const_iterator i;
       
   273 +  for(i = playqueue.begin(); i!= playqueue.end(); i++) {
       
   274 +    if (*i == song) return true;
       
   275 +  }
       
   276 +  return false;
       
   277 +}
       
   278 +
       
   279 +void quit(int signum)
       
   280 +{
       
   281 +  if (loop) g_main_quit(loop);
       
   282 +  loop = nullptr;
       
   283 +  signal(signum, SIG_DFL);
       
   284 +}
       
   285 +
       
   286 +
       
   287 diff -r 171db9560cb5 clients/mpd/libmpdclient.c
       
   288 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
       
   289 +++ b/clients/mpd/libmpdclient.c	Mon May 19 00:21:51 2008 -0400
       
   290 @@ -0,0 +1,1955 @@
       
   291 +/* libmpdclient
       
   292 +   (c)2003-2006 by Warren Dukes (warren.dukes@gmail.com)
       
   293 +   This project's homepage is: http://www.musicpd.org
       
   294 +
       
   295 +   Redistribution and use in source and binary forms, with or without
       
   296 +   modification, are permitted provided that the following conditions
       
   297 +   are met:
       
   298 +
       
   299 +   - Redistributions of source code must retain the above copyright
       
   300 +   notice, this list of conditions and the following disclaimer.
       
   301 +
       
   302 +   - Redistributions in binary form must reproduce the above copyright
       
   303 +   notice, this list of conditions and the following disclaimer in the
       
   304 +   documentation and/or other materials provided with the distribution.
       
   305 +
       
   306 +   - Neither the name of the Music Player Daemon nor the names of its
       
   307 +   contributors may be used to endorse or promote products derived from
       
   308 +   this software without specific prior written permission.
       
   309 +
       
   310 +   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
       
   311 +   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
       
   312 +   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
       
   313 +   A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
       
   314 +   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
       
   315 +   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
       
   316 +   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
       
   317 +   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
       
   318 +   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
       
   319 +   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
       
   320 +   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
       
   321 +*/
       
   322 +
       
   323 +#include "libmpdclient.h"
       
   324 +
       
   325 +#include <errno.h>
       
   326 +#include <ctype.h>
       
   327 +#include <sys/types.h>
       
   328 +#include <stdio.h>
       
   329 +#include <sys/param.h>
       
   330 +#include <string.h>
       
   331 +#include <unistd.h>
       
   332 +#include <stdlib.h>
       
   333 +#include <fcntl.h>
       
   334 +#include <limits.h>
       
   335 +
       
   336 +#ifdef WIN32
       
   337 +#  include <ws2tcpip.h>
       
   338 +#  include <winsock.h>
       
   339 +#else
       
   340 +#  include <netinet/in.h>
       
   341 +#  include <arpa/inet.h>
       
   342 +#  include <sys/socket.h>
       
   343 +#  include <netdb.h>
       
   344 +#endif
       
   345 +
       
   346 +/* (bits+1)/3 (plus the sign character) */
       
   347 +#define INTLEN      ((sizeof(int)       * CHAR_BIT + 1) / 3 + 1)
       
   348 +#define LONGLONGLEN ((sizeof(long long) * CHAR_BIT + 1) / 3 + 1)
       
   349 +
       
   350 +#define COMMAND_LIST    1
       
   351 +#define COMMAND_LIST_OK 2
       
   352 +
       
   353 +#ifndef MPD_NO_GAI
       
   354 +#  ifdef AI_ADDRCONFIG
       
   355 +#    define MPD_HAVE_GAI
       
   356 +#  endif
       
   357 +#endif
       
   358 +
       
   359 +#ifndef MSG_DONTWAIT
       
   360 +#  define MSG_DONTWAIT 0
       
   361 +#endif
       
   362 +
       
   363 +#ifdef WIN32
       
   364 +#  define SELECT_ERRNO_IGNORE   (errno == WSAEINTR || errno == WSAEINPROGRESS)
       
   365 +#  define SENDRECV_ERRNO_IGNORE SELECT_ERRNO_IGNORE
       
   366 +#else
       
   367 +#  define SELECT_ERRNO_IGNORE   (errno == EINTR)
       
   368 +#  define SENDRECV_ERRNO_IGNORE (errno == EINTR || errno == EAGAIN)
       
   369 +#  define winsock_dll_error(c)  0
       
   370 +#  define closesocket(s)        close(s)
       
   371 +#  define WSACleanup()          do { /* nothing */ } while (0)
       
   372 +#endif
       
   373 +
       
   374 +#ifdef WIN32
       
   375 +static int winsock_dll_error(mpd_Connection *connection)
       
   376 +{
       
   377 +	WSADATA wsaData;
       
   378 +	if ((WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0 ||
       
   379 +			LOBYTE(wsaData.wVersion) != 2 ||
       
   380 +			HIBYTE(wsaData.wVersion) != 2 ) {
       
   381 +		strcpy(connection->errorStr,
       
   382 +		       "Could not find usable WinSock DLL.");
       
   383 +		connection->error = MPD_ERROR_SYSTEM;
       
   384 +		return 1;
       
   385 +	}
       
   386 +	return 0;
       
   387 +}
       
   388 +
       
   389 +static int do_connect_fail(mpd_Connection *connection,
       
   390 +                           const struct sockaddr *serv_addr, int addrlen)
       
   391 +{
       
   392 +	int iMode = 1; /* 0 = blocking, else non-blocking */
       
   393 +	ioctlsocket(connection->sock, FIONBIO, (u_long FAR*) &iMode);
       
   394 +	return (connect(connection->sock,serv_addr,addrlen) == SOCKET_ERROR
       
   395 +			&& WSAGetLastError() != WSAEWOULDBLOCK);
       
   396 +}
       
   397 +#else /* !WIN32 (sane operating systems) */
       
   398 +static int do_connect_fail(mpd_Connection *connection,
       
   399 +                           const struct sockaddr *serv_addr, int addrlen)
       
   400 +{
       
   401 +	int flags = fcntl(connection->sock, F_GETFL, 0);
       
   402 +	fcntl(connection->sock, F_SETFL, flags | O_NONBLOCK);
       
   403 +	return (connect(connection->sock,serv_addr,addrlen)<0 &&
       
   404 +				errno!=EINPROGRESS);
       
   405 +}
       
   406 +#endif /* !WIN32 */
       
   407 +
       
   408 +#ifdef MPD_HAVE_GAI
       
   409 +static int mpd_connect(mpd_Connection * connection, const char * host, int port,
       
   410 +                       float timeout)
       
   411 +{
       
   412 +	int error;
       
   413 +	char service[INTLEN+1];
       
   414 +	struct addrinfo hints;
       
   415 +	struct addrinfo *res = NULL;
       
   416 +	struct addrinfo *addrinfo = NULL;
       
   417 +
       
   418 +	/**
       
   419 +	 * Setup hints
       
   420 +	 */
       
   421 +	hints.ai_flags     = AI_ADDRCONFIG;
       
   422 +	hints.ai_family    = PF_UNSPEC;
       
   423 +	hints.ai_socktype  = SOCK_STREAM;
       
   424 +	hints.ai_protocol  = IPPROTO_TCP;
       
   425 +	hints.ai_addrlen   = 0;
       
   426 +	hints.ai_addr      = NULL;
       
   427 +	hints.ai_canonname = NULL;
       
   428 +	hints.ai_next      = NULL;
       
   429 +
       
   430 +	snprintf(service, sizeof(service), "%i", port);
       
   431 +
       
   432 +	error = getaddrinfo(host, service, &hints, &addrinfo);
       
   433 +
       
   434 +	if (error) {
       
   435 +		snprintf(connection->errorStr, MPD_ERRORSTR_MAX_LENGTH,
       
   436 +		         "host \"%s\" not found: %s",
       
   437 +		         host, gai_strerror(error));
       
   438 +		connection->error = MPD_ERROR_UNKHOST;
       
   439 +		return -1;
       
   440 +	}
       
   441 +
       
   442 +	for (res = addrinfo; res; res = res->ai_next) {
       
   443 +		/* create socket */
       
   444 +		connection->sock = socket(res->ai_family, SOCK_STREAM,
       
   445 +		                          res->ai_protocol);
       
   446 +		if (connection->sock < 0) {
       
   447 +			snprintf(connection->errorStr, MPD_ERRORSTR_MAX_LENGTH,
       
   448 +			         "problems creating socket: %s",
       
   449 +			         strerror(errno));
       
   450 +			connection->error = MPD_ERROR_SYSTEM;
       
   451 +			freeaddrinfo(addrinfo);
       
   452 +			return -1;
       
   453 +		}
       
   454 +
       
   455 +		mpd_setConnectionTimeout(connection, timeout);
       
   456 +
       
   457 +		/* connect stuff */
       
   458 + 		if (do_connect_fail(connection,
       
   459 +		                    res->ai_addr, res->ai_addrlen)) {
       
   460 + 			/* try the next address family */
       
   461 + 			closesocket(connection->sock);
       
   462 + 			connection->sock = -1;
       
   463 + 			continue;
       
   464 +		}
       
   465 +	}
       
   466 +
       
   467 +	freeaddrinfo(addrinfo);
       
   468 +
       
   469 +	if (connection->sock < 0) {
       
   470 +		snprintf(connection->errorStr, MPD_ERRORSTR_MAX_LENGTH,
       
   471 +		         "problems connecting to \"%s\" on port %i: %s",
       
   472 +		         host, port, strerror(errno));
       
   473 +		connection->error = MPD_ERROR_CONNPORT;
       
   474 +
       
   475 +		return -1;
       
   476 +	}
       
   477 +
       
   478 +	return 0;
       
   479 +}
       
   480 +#else /* !MPD_HAVE_GAI */
       
   481 +static int mpd_connect(mpd_Connection * connection, const char * host, int port,
       
   482 +                       float timeout)
       
   483 +{
       
   484 +	struct hostent * he;
       
   485 +	struct sockaddr * dest;
       
   486 +	int destlen;
       
   487 +	struct sockaddr_in sin;
       
   488 +
       
   489 +	if(!(he=gethostbyname(host))) {
       
   490 +		snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
       
   491 +				"host \"%s\" not found",host);
       
   492 +		connection->error = MPD_ERROR_UNKHOST;
       
   493 +		return -1;
       
   494 +	}
       
   495 +
       
   496 +	memset(&sin,0,sizeof(struct sockaddr_in));
       
   497 +	/*dest.sin_family = he->h_addrtype;*/
       
   498 +	sin.sin_family = AF_INET;
       
   499 +	sin.sin_port = htons(port);
       
   500 +
       
   501 +	switch(he->h_addrtype) {
       
   502 +	case AF_INET:
       
   503 +		memcpy((char *)&sin.sin_addr.s_addr,(char *)he->h_addr,
       
   504 +				he->h_length);
       
   505 +		dest = (struct sockaddr *)&sin;
       
   506 +		destlen = sizeof(struct sockaddr_in);
       
   507 +		break;
       
   508 +	default:
       
   509 +		strcpy(connection->errorStr,"address type is not IPv4");
       
   510 +		connection->error = MPD_ERROR_SYSTEM;
       
   511 +		return -1;
       
   512 +		break;
       
   513 +	}
       
   514 +
       
   515 +	if((connection->sock = socket(dest->sa_family,SOCK_STREAM,0))<0) {
       
   516 +		strcpy(connection->errorStr,"problems creating socket");
       
   517 +		connection->error = MPD_ERROR_SYSTEM;
       
   518 +		return -1;
       
   519 +	}
       
   520 +
       
   521 +	mpd_setConnectionTimeout(connection,timeout);
       
   522 +
       
   523 +	/* connect stuff */
       
   524 +	if (do_connect_fail(connection, dest, destlen)) {
       
   525 +		snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
       
   526 +				"problems connecting to \"%s\" on port"
       
   527 +				" %i",host,port);
       
   528 +		connection->error = MPD_ERROR_CONNPORT;
       
   529 +		return -1;
       
   530 +	}
       
   531 +
       
   532 +	return 0;
       
   533 +}
       
   534 +#endif /* !MPD_HAVE_GAI */
       
   535 +
       
   536 +char * mpdTagItemKeys[MPD_TAG_NUM_OF_ITEM_TYPES] =
       
   537 +{
       
   538 +	"Artist",
       
   539 +	"Album",
       
   540 +	"Title",
       
   541 +	"Track",
       
   542 +	"Name",
       
   543 +	"Genre",
       
   544 +	"Date",
       
   545 +	"Composer",
       
   546 +	"Performer",
       
   547 +	"Comment",
       
   548 +	"Disc",
       
   549 +	"Filename",
       
   550 +	"Any"
       
   551 +};
       
   552 +
       
   553 +static char * mpd_sanitizeArg(const char * arg) {
       
   554 +	size_t i;
       
   555 +	char * ret;
       
   556 +	register const char *c;
       
   557 +	register char *rc;
       
   558 +
       
   559 +	/* instead of counting in that loop above, just
       
   560 +	 * use a bit more memory and half running time
       
   561 +	 */
       
   562 +	ret = (char *)malloc(strlen(arg) * 2 + 1);
       
   563 +
       
   564 +	c = arg;
       
   565 +	rc = ret;
       
   566 +	for(i = strlen(arg)+1; i != 0; --i) {
       
   567 +		if(*c=='"' || *c=='\\')
       
   568 +			*rc++ = '\\';
       
   569 +		*(rc++) = *(c++);
       
   570 +	}
       
   571 +
       
   572 +	return ret;
       
   573 +}
       
   574 +
       
   575 +static mpd_ReturnElement * mpd_newReturnElement(const char * name, const char * value)
       
   576 +{
       
   577 +	mpd_ReturnElement * ret = (mpd_ReturnElement *)malloc(sizeof(mpd_ReturnElement));
       
   578 +
       
   579 +	ret->name = strdup(name);
       
   580 +	ret->value = strdup(value);
       
   581 +
       
   582 +	return ret;
       
   583 +}
       
   584 +
       
   585 +static void mpd_freeReturnElement(mpd_ReturnElement * re) {
       
   586 +	free(re->name);
       
   587 +	free(re->value);
       
   588 +	free(re);
       
   589 +}
       
   590 +
       
   591 +void mpd_setConnectionTimeout(mpd_Connection * connection, float timeout) {
       
   592 +	connection->timeout.tv_sec = (int)timeout;
       
   593 +	connection->timeout.tv_usec = (int)(timeout*1e6 -
       
   594 +	                                    connection->timeout.tv_sec*1000000 +
       
   595 +					    0.5);
       
   596 +}
       
   597 +
       
   598 +static int mpd_parseWelcome(mpd_Connection * connection, const char * host, int port,
       
   599 +                            char * rt, char * output) {
       
   600 +	char * tmp;
       
   601 +	char * test;
       
   602 +	int i;
       
   603 +
       
   604 +	if(strncmp(output,MPD_WELCOME_MESSAGE,strlen(MPD_WELCOME_MESSAGE))) {
       
   605 +		snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
       
   606 +				"mpd not running on port %i on host \"%s\"",
       
   607 +				port,host);
       
   608 +		connection->error = MPD_ERROR_NOTMPD;
       
   609 +		return 1;
       
   610 +	}
       
   611 +
       
   612 +	tmp = &output[strlen(MPD_WELCOME_MESSAGE)];
       
   613 +
       
   614 +	for(i=0;i<3;i++) {
       
   615 +		if(tmp) connection->version[i] = strtol(tmp,&test,10);
       
   616 +
       
   617 +		if (!tmp || (test[0] != '.' && test[0] != '\0')) {
       
   618 +			snprintf(connection->errorStr,
       
   619 +			         MPD_ERRORSTR_MAX_LENGTH,
       
   620 +			         "error parsing version number at "
       
   621 +			         "\"%s\"",
       
   622 +			         &output[strlen(MPD_WELCOME_MESSAGE)]);
       
   623 +			connection->error = MPD_ERROR_NOTMPD;
       
   624 +			return 1;
       
   625 +		}
       
   626 +		tmp = ++test;
       
   627 +	}
       
   628 +
       
   629 +	return 0;
       
   630 +}
       
   631 +
       
   632 +mpd_Connection * mpd_newConnection(const char * host, int port, float timeout) {
       
   633 +	int err;
       
   634 +	char * rt;
       
   635 +	char * output =  NULL;
       
   636 +	mpd_Connection * connection = (mpd_Connection *)malloc(sizeof(mpd_Connection));
       
   637 +	struct timeval tv;
       
   638 +	fd_set fds;
       
   639 +	strcpy(connection->buffer,"");
       
   640 +	connection->buflen = 0;
       
   641 +	connection->bufstart = 0;
       
   642 +	strcpy(connection->errorStr,"");
       
   643 +	connection->error = 0;
       
   644 +	connection->doneProcessing = 0;
       
   645 +	connection->commandList = 0;
       
   646 +	connection->listOks = 0;
       
   647 +	connection->doneListOk = 0;
       
   648 +	connection->returnElement = NULL;
       
   649 +	connection->request = NULL;
       
   650 +
       
   651 +	if (winsock_dll_error(connection))
       
   652 +		return connection;
       
   653 +
       
   654 +	if (mpd_connect(connection, host, port, timeout) < 0)
       
   655 +		return connection;
       
   656 +
       
   657 +	while(!(rt = strstr(connection->buffer,"\n"))) {
       
   658 +		tv.tv_sec = connection->timeout.tv_sec;
       
   659 +		tv.tv_usec = connection->timeout.tv_usec;
       
   660 +		FD_ZERO(&fds);
       
   661 +		FD_SET(connection->sock,&fds);
       
   662 +		if((err = select(connection->sock+1,&fds,NULL,NULL,&tv)) == 1) {
       
   663 +			int readed;
       
   664 +			readed = recv(connection->sock,
       
   665 +					&(connection->buffer[connection->buflen]),
       
   666 +					MPD_BUFFER_MAX_LENGTH-connection->buflen,0);
       
   667 +			if(readed<=0) {
       
   668 +				snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
       
   669 +						"problems getting a response from"
       
   670 +						" \"%s\" on port %i : %s",host,
       
   671 +						port, strerror(errno));
       
   672 +				connection->error = MPD_ERROR_NORESPONSE;
       
   673 +				return connection;
       
   674 +			}
       
   675 +			connection->buflen+=readed;
       
   676 +			connection->buffer[connection->buflen] = '\0';
       
   677 +		}
       
   678 +		else if(err<0) {
       
   679 + 			if (SELECT_ERRNO_IGNORE)
       
   680 +				continue;
       
   681 +			snprintf(connection->errorStr,
       
   682 +					MPD_ERRORSTR_MAX_LENGTH,
       
   683 +					"problems connecting to \"%s\" on port"
       
   684 +					" %i",host,port);
       
   685 +			connection->error = MPD_ERROR_CONNPORT;
       
   686 +			return connection;
       
   687 +		}
       
   688 +		else {
       
   689 +			snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
       
   690 +					"timeout in attempting to get a response from"
       
   691 +					" \"%s\" on port %i",host,port);
       
   692 +			connection->error = MPD_ERROR_NORESPONSE;
       
   693 +			return connection;
       
   694 +		}
       
   695 +	}
       
   696 +
       
   697 +	*rt = '\0';
       
   698 +	output = strdup(connection->buffer);
       
   699 +	strcpy(connection->buffer,rt+1);
       
   700 +	connection->buflen = strlen(connection->buffer);
       
   701 +
       
   702 +	if(mpd_parseWelcome(connection,host,port,rt,output) == 0) connection->doneProcessing = 1;
       
   703 +
       
   704 +	free(output);
       
   705 +
       
   706 +	return connection;
       
   707 +}
       
   708 +
       
   709 +void mpd_clearError(mpd_Connection * connection) {
       
   710 +	connection->error = 0;
       
   711 +	connection->errorStr[0] = '\0';
       
   712 +}
       
   713 +
       
   714 +void mpd_closeConnection(mpd_Connection * connection) {
       
   715 +	closesocket(connection->sock);
       
   716 +	if(connection->returnElement) free(connection->returnElement);
       
   717 +	if(connection->request) free(connection->request);
       
   718 +	free(connection);
       
   719 +	WSACleanup();
       
   720 +}
       
   721 +
       
   722 +static void mpd_executeCommand(mpd_Connection * connection, char * command) {
       
   723 +	int ret;
       
   724 +	struct timeval tv;
       
   725 +	fd_set fds;
       
   726 +	char * commandPtr = command;
       
   727 +	int commandLen = strlen(command);
       
   728 +
       
   729 +	if(!connection->doneProcessing && !connection->commandList) {
       
   730 +		strcpy(connection->errorStr,"not done processing current command");
       
   731 +		connection->error = 1;
       
   732 +		return;
       
   733 +	}
       
   734 +
       
   735 +	mpd_clearError(connection);
       
   736 +
       
   737 +	FD_ZERO(&fds);
       
   738 +	FD_SET(connection->sock,&fds);
       
   739 +	tv.tv_sec = connection->timeout.tv_sec;
       
   740 +	tv.tv_usec = connection->timeout.tv_usec;
       
   741 +
       
   742 +	while((ret = select(connection->sock+1,NULL,&fds,NULL,&tv)==1) ||
       
   743 +			(ret==-1 && SELECT_ERRNO_IGNORE)) {
       
   744 +		ret = send(connection->sock,commandPtr,commandLen,MSG_DONTWAIT);
       
   745 +		if(ret<=0)
       
   746 +		{
       
   747 +			if (SENDRECV_ERRNO_IGNORE) continue;
       
   748 +			snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
       
   749 +			         "problems giving command \"%s\"",command);
       
   750 +			connection->error = MPD_ERROR_SENDING;
       
   751 +			return;
       
   752 +		}
       
   753 +		else {
       
   754 +			commandPtr+=ret;
       
   755 +			commandLen-=ret;
       
   756 +		}
       
   757 +
       
   758 +		if(commandLen<=0) break;
       
   759 +	}
       
   760 +
       
   761 +	if(commandLen>0) {
       
   762 +		perror("");
       
   763 +		snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
       
   764 +		         "timeout sending command \"%s\"",command);
       
   765 +		connection->error = MPD_ERROR_TIMEOUT;
       
   766 +		return;
       
   767 +	}
       
   768 +
       
   769 +	if(!connection->commandList) connection->doneProcessing = 0;
       
   770 +	else if(connection->commandList == COMMAND_LIST_OK) {
       
   771 +		connection->listOks++;
       
   772 +	}
       
   773 +}
       
   774 +
       
   775 +static void mpd_getNextReturnElement(mpd_Connection * connection) {
       
   776 +	char * output = NULL;
       
   777 +	char * rt = NULL;
       
   778 +	char * name = NULL;
       
   779 +	char * value = NULL;
       
   780 +	fd_set fds;
       
   781 +	struct timeval tv;
       
   782 +	char * tok = NULL;
       
   783 +	int readed;
       
   784 +	char * bufferCheck = NULL;
       
   785 +	int err;
       
   786 +	int pos;
       
   787 +
       
   788 +	if(connection->returnElement) mpd_freeReturnElement(connection->returnElement);
       
   789 +	connection->returnElement = NULL;
       
   790 +
       
   791 +	if(connection->doneProcessing || (connection->listOks &&
       
   792 +	   connection->doneListOk))
       
   793 +	{
       
   794 +		strcpy(connection->errorStr,"already done processing current command");
       
   795 +		connection->error = 1;
       
   796 +		return;
       
   797 +	}
       
   798 +
       
   799 +	bufferCheck = connection->buffer+connection->bufstart;
       
   800 +	while(connection->bufstart>=connection->buflen ||
       
   801 +			!(rt = strchr(bufferCheck,'\n'))) {
       
   802 +		if(connection->buflen>=MPD_BUFFER_MAX_LENGTH) {
       
   803 +			memmove(connection->buffer,
       
   804 +					connection->buffer+
       
   805 +					connection->bufstart,
       
   806 +					connection->buflen-
       
   807 +					connection->bufstart+1);
       
   808 +			connection->buflen-=connection->bufstart;
       
   809 +			connection->bufstart = 0;
       
   810 +		}
       
   811 +		if(connection->buflen>=MPD_BUFFER_MAX_LENGTH) {
       
   812 +			strcpy(connection->errorStr,"buffer overrun");
       
   813 +			connection->error = MPD_ERROR_BUFFEROVERRUN;
       
   814 +			connection->doneProcessing = 1;
       
   815 +			connection->doneListOk = 0;
       
   816 +			return;
       
   817 +		}
       
   818 +		bufferCheck = connection->buffer+connection->buflen;
       
   819 +		tv.tv_sec = connection->timeout.tv_sec;
       
   820 +		tv.tv_usec = connection->timeout.tv_usec;
       
   821 +		FD_ZERO(&fds);
       
   822 +		FD_SET(connection->sock,&fds);
       
   823 +		if((err = select(connection->sock+1,&fds,NULL,NULL,&tv) == 1)) {
       
   824 +			readed = recv(connection->sock,
       
   825 +					connection->buffer+connection->buflen,
       
   826 +					MPD_BUFFER_MAX_LENGTH-connection->buflen,
       
   827 +					MSG_DONTWAIT);
       
   828 +			if(readed<0 && SENDRECV_ERRNO_IGNORE) {
       
   829 +				continue;
       
   830 +			}
       
   831 +			if(readed<=0) {
       
   832 +				strcpy(connection->errorStr,"connection"
       
   833 +				       " closed");
       
   834 +				connection->error = MPD_ERROR_CONNCLOSED;
       
   835 +				connection->doneProcessing = 1;
       
   836 +				connection->doneListOk = 0;
       
   837 +				return;
       
   838 +			}
       
   839 +			connection->buflen+=readed;
       
   840 +			connection->buffer[connection->buflen] = '\0';
       
   841 +		}
       
   842 +		else if(err<0 && SELECT_ERRNO_IGNORE) continue;
       
   843 +		else {
       
   844 +			strcpy(connection->errorStr,"connection timeout");
       
   845 +			connection->error = MPD_ERROR_TIMEOUT;
       
   846 +			connection->doneProcessing = 1;
       
   847 +			connection->doneListOk = 0;
       
   848 +			return;
       
   849 +		}
       
   850 +	}
       
   851 +
       
   852 +	*rt = '\0';
       
   853 +	output = connection->buffer+connection->bufstart;
       
   854 +	connection->bufstart = rt - connection->buffer + 1;
       
   855 +
       
   856 +	if(strcmp(output,"OK")==0) {
       
   857 +		if(connection->listOks > 0) {
       
   858 +			strcpy(connection->errorStr, "expected more list_OK's");
       
   859 +			connection->error = 1;
       
   860 +		}
       
   861 +		connection->listOks = 0;
       
   862 +		connection->doneProcessing = 1;
       
   863 +		connection->doneListOk = 0;
       
   864 +		return;
       
   865 +	}
       
   866 +
       
   867 +	if(strcmp(output, "list_OK") == 0) {
       
   868 +		if(!connection->listOks) {
       
   869 +			strcpy(connection->errorStr,
       
   870 +					"got an unexpected list_OK");
       
   871 +			connection->error = 1;
       
   872 +		}
       
   873 +		else {
       
   874 +			connection->doneListOk = 1;
       
   875 +			connection->listOks--;
       
   876 +		}
       
   877 +		return;
       
   878 +	}
       
   879 +
       
   880 +	if(strncmp(output,"ACK",strlen("ACK"))==0) {
       
   881 +		char * test;
       
   882 +		char * needle;
       
   883 +		int val;
       
   884 +
       
   885 +		strcpy(connection->errorStr, output);
       
   886 +		connection->error = MPD_ERROR_ACK;
       
   887 +		connection->errorCode = MPD_ACK_ERROR_UNK;
       
   888 +		connection->errorAt = MPD_ERROR_AT_UNK;
       
   889 +		connection->doneProcessing = 1;
       
   890 +		connection->doneListOk = 0;
       
   891 +
       
   892 +		needle = strchr(output, '[');
       
   893 +		if(!needle) return;
       
   894 +		val = strtol(needle+1, &test, 10);
       
   895 +		if(*test != '@') return;
       
   896 +		connection->errorCode = val;
       
   897 +		val = strtol(test+1, &test, 10);
       
   898 +		if(*test != ']') return;
       
   899 +		connection->errorAt = val;
       
   900 +		return;
       
   901 +	}
       
   902 +
       
   903 +	tok = strchr(output, ':');
       
   904 +	if (!tok) return;
       
   905 +	pos = tok - output;
       
   906 +	value = ++tok;
       
   907 +	name = output;
       
   908 +	name[pos] = '\0';
       
   909 +
       
   910 +	if(value[0]==' ') {
       
   911 +		connection->returnElement = mpd_newReturnElement(name,&(value[1]));
       
   912 +	}
       
   913 +	else {
       
   914 +		snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH,
       
   915 +					"error parsing: %s:%s",name,value);
       
   916 +		connection->error = 1;
       
   917 +	}
       
   918 +}
       
   919 +
       
   920 +void mpd_finishCommand(mpd_Connection * connection) {
       
   921 +	while(!connection->doneProcessing) {
       
   922 +		if(connection->doneListOk) connection->doneListOk = 0;
       
   923 +		mpd_getNextReturnElement(connection);
       
   924 +	}
       
   925 +}
       
   926 +
       
   927 +static void mpd_finishListOkCommand(mpd_Connection * connection) {
       
   928 +	while(!connection->doneProcessing && connection->listOks &&
       
   929 +			!connection->doneListOk)
       
   930 +	{
       
   931 +		mpd_getNextReturnElement(connection);
       
   932 +	}
       
   933 +}
       
   934 +
       
   935 +int mpd_nextListOkCommand(mpd_Connection * connection) {
       
   936 +	mpd_finishListOkCommand(connection);
       
   937 +	if(!connection->doneProcessing) connection->doneListOk = 0;
       
   938 +	if(connection->listOks == 0 || connection->doneProcessing) return -1;
       
   939 +	return 0;
       
   940 +}
       
   941 +
       
   942 +void mpd_sendStatusCommand(mpd_Connection * connection) {
       
   943 +	mpd_executeCommand(connection,"status\n");
       
   944 +}
       
   945 +
       
   946 +mpd_Status * mpd_getStatus(mpd_Connection * connection) {
       
   947 +	mpd_Status * status;
       
   948 +
       
   949 +	/*mpd_executeCommand(connection,"status\n");
       
   950 +
       
   951 +	if(connection->error) return NULL;*/
       
   952 +
       
   953 +	if(connection->doneProcessing || (connection->listOks &&
       
   954 +	   connection->doneListOk))
       
   955 +	{
       
   956 +		return NULL;
       
   957 +	}
       
   958 +
       
   959 +	if(!connection->returnElement) mpd_getNextReturnElement(connection);
       
   960 +
       
   961 +	status = (mpd_Status *)malloc(sizeof(mpd_Status));
       
   962 +	status->volume = -1;
       
   963 +	status->repeat = 0;
       
   964 +	status->random = 0;
       
   965 +	status->playlist = -1;
       
   966 +	status->playlistLength = -1;
       
   967 +	status->state = -1;
       
   968 +	status->song = 0;
       
   969 +	status->songid = 0;
       
   970 +	status->elapsedTime = 0;
       
   971 +	status->totalTime = 0;
       
   972 +	status->bitRate = 0;
       
   973 +	status->sampleRate = 0;
       
   974 +	status->bits = 0;
       
   975 +	status->channels = 0;
       
   976 +	status->crossfade = -1;
       
   977 +	status->error = NULL;
       
   978 +	status->updatingDb = 0;
       
   979 +
       
   980 +	if(connection->error) {
       
   981 +		free(status);
       
   982 +		return NULL;
       
   983 +	}
       
   984 +	while(connection->returnElement) {
       
   985 +		mpd_ReturnElement * re = connection->returnElement;
       
   986 +		if(strcmp(re->name,"volume")==0) {
       
   987 +			status->volume = atoi(re->value);
       
   988 +		}
       
   989 +		else if(strcmp(re->name,"repeat")==0) {
       
   990 +			status->repeat = atoi(re->value);
       
   991 +		}
       
   992 +		else if(strcmp(re->name,"random")==0) {
       
   993 +			status->random = atoi(re->value);
       
   994 +		}
       
   995 +		else if(strcmp(re->name,"playlist")==0) {
       
   996 +			status->playlist = strtol(re->value,NULL,10);
       
   997 +		}
       
   998 +		else if(strcmp(re->name,"playlistlength")==0) {
       
   999 +			status->playlistLength = atoi(re->value);
       
  1000 +		}
       
  1001 +		else if(strcmp(re->name,"bitrate")==0) {
       
  1002 +			status->bitRate = atoi(re->value);
       
  1003 +		}
       
  1004 +		else if(strcmp(re->name,"state")==0) {
       
  1005 +			if(strcmp(re->value,"play")==0) {
       
  1006 +				status->state = MPD_STATUS_STATE_PLAY;
       
  1007 +			}
       
  1008 +			else if(strcmp(re->value,"stop")==0) {
       
  1009 +				status->state = MPD_STATUS_STATE_STOP;
       
  1010 +			}
       
  1011 +			else if(strcmp(re->value,"pause")==0) {
       
  1012 +				status->state = MPD_STATUS_STATE_PAUSE;
       
  1013 +			}
       
  1014 +			else {
       
  1015 +				status->state = MPD_STATUS_STATE_UNKNOWN;
       
  1016 +			}
       
  1017 +		}
       
  1018 +		else if(strcmp(re->name,"song")==0) {
       
  1019 +			status->song = atoi(re->value);
       
  1020 +		}
       
  1021 +		else if(strcmp(re->name,"songid")==0) {
       
  1022 +			status->songid = atoi(re->value);
       
  1023 +		}
       
  1024 +		else if(strcmp(re->name,"time")==0) {
       
  1025 +			char * tok = strchr(re->value,':');
       
  1026 +			/* the second strchr below is a safety check */
       
  1027 +			if (tok && (strchr(tok,0) > (tok+1))) {
       
  1028 +				/* atoi stops at the first non-[0-9] char: */
       
  1029 +				status->elapsedTime = atoi(re->value);
       
  1030 +				status->totalTime = atoi(tok+1);
       
  1031 +			}
       
  1032 +		}
       
  1033 +		else if(strcmp(re->name,"error")==0) {
       
  1034 +			status->error = strdup(re->value);
       
  1035 +		}
       
  1036 +		else if(strcmp(re->name,"xfade")==0) {
       
  1037 +			status->crossfade = atoi(re->value);
       
  1038 +		}
       
  1039 +		else if(strcmp(re->name,"updating_db")==0) {
       
  1040 +			status->updatingDb = atoi(re->value);
       
  1041 +		}
       
  1042 +		else if(strcmp(re->name,"audio")==0) {
       
  1043 +			char * tok = strchr(re->value,':');
       
  1044 +			if (tok && (strchr(tok,0) > (tok+1))) {
       
  1045 +				status->sampleRate = atoi(re->value);
       
  1046 +				status->bits = atoi(++tok);
       
  1047 +				tok = strchr(tok,':');
       
  1048 +				if (tok && (strchr(tok,0) > (tok+1)))
       
  1049 +					status->channels = atoi(tok+1);
       
  1050 +			}
       
  1051 +		}
       
  1052 +
       
  1053 +		mpd_getNextReturnElement(connection);
       
  1054 +		if(connection->error) {
       
  1055 +			free(status);
       
  1056 +			return NULL;
       
  1057 +		}
       
  1058 +	}
       
  1059 +
       
  1060 +	if(connection->error) {
       
  1061 +		free(status);
       
  1062 +		return NULL;
       
  1063 +	}
       
  1064 +	else if(status->state<0) {
       
  1065 +		strcpy(connection->errorStr,"state not found");
       
  1066 +		connection->error = 1;
       
  1067 +		free(status);
       
  1068 +		return NULL;
       
  1069 +	}
       
  1070 +
       
  1071 +	return status;
       
  1072 +}
       
  1073 +
       
  1074 +void mpd_freeStatus(mpd_Status * status) {
       
  1075 +	if(status->error) free(status->error);
       
  1076 +	free(status);
       
  1077 +}
       
  1078 +
       
  1079 +void mpd_sendStatsCommand(mpd_Connection * connection) {
       
  1080 +	mpd_executeCommand(connection,"stats\n");
       
  1081 +}
       
  1082 +
       
  1083 +mpd_Stats * mpd_getStats(mpd_Connection * connection) {
       
  1084 +	mpd_Stats * stats;
       
  1085 +
       
  1086 +	/*mpd_executeCommand(connection,"stats\n");
       
  1087 +
       
  1088 +	if(connection->error) return NULL;*/
       
  1089 +
       
  1090 +	if(connection->doneProcessing || (connection->listOks &&
       
  1091 +	   connection->doneListOk))
       
  1092 +	{
       
  1093 +		return NULL;
       
  1094 +	}
       
  1095 +
       
  1096 +	if(!connection->returnElement) mpd_getNextReturnElement(connection);
       
  1097 +
       
  1098 +	stats = (mpd_Stats *)malloc(sizeof(mpd_Stats));
       
  1099 +	stats->numberOfArtists = 0;
       
  1100 +	stats->numberOfAlbums = 0;
       
  1101 +	stats->numberOfSongs = 0;
       
  1102 +	stats->uptime = 0;
       
  1103 +	stats->dbUpdateTime = 0;
       
  1104 +	stats->playTime = 0;
       
  1105 +	stats->dbPlayTime = 0;
       
  1106 +
       
  1107 +	if(connection->error) {
       
  1108 +		free(stats);
       
  1109 +		return NULL;
       
  1110 +	}
       
  1111 +	while(connection->returnElement) {
       
  1112 +		mpd_ReturnElement * re = connection->returnElement;
       
  1113 +		if(strcmp(re->name,"artists")==0) {
       
  1114 +			stats->numberOfArtists = atoi(re->value);
       
  1115 +		}
       
  1116 +		else if(strcmp(re->name,"albums")==0) {
       
  1117 +			stats->numberOfAlbums = atoi(re->value);
       
  1118 +		}
       
  1119 +		else if(strcmp(re->name,"songs")==0) {
       
  1120 +			stats->numberOfSongs = atoi(re->value);
       
  1121 +		}
       
  1122 +		else if(strcmp(re->name,"uptime")==0) {
       
  1123 +			stats->uptime = strtol(re->value,NULL,10);
       
  1124 +		}
       
  1125 +		else if(strcmp(re->name,"db_update")==0) {
       
  1126 +			stats->dbUpdateTime = strtol(re->value,NULL,10);
       
  1127 +		}
       
  1128 +		else if(strcmp(re->name,"playtime")==0) {
       
  1129 +			stats->playTime = strtol(re->value,NULL,10);
       
  1130 +		}
       
  1131 +		else if(strcmp(re->name,"db_playtime")==0) {
       
  1132 +			stats->dbPlayTime = strtol(re->value,NULL,10);
       
  1133 +		}
       
  1134 +
       
  1135 +		mpd_getNextReturnElement(connection);
       
  1136 +		if(connection->error) {
       
  1137 +			free(stats);
       
  1138 +			return NULL;
       
  1139 +		}
       
  1140 +	}
       
  1141 +
       
  1142 +	if(connection->error) {
       
  1143 +		free(stats);
       
  1144 +		return NULL;
       
  1145 +	}
       
  1146 +
       
  1147 +	return stats;
       
  1148 +}
       
  1149 +
       
  1150 +void mpd_freeStats(mpd_Stats * stats) {
       
  1151 +	free(stats);
       
  1152 +}
       
  1153 +
       
  1154 +mpd_SearchStats * mpd_getSearchStats(mpd_Connection * connection)
       
  1155 +{
       
  1156 +	mpd_SearchStats * stats;
       
  1157 +	mpd_ReturnElement * re;
       
  1158 +
       
  1159 +	if (connection->doneProcessing ||
       
  1160 +	    (connection->listOks && connection->doneListOk)) {
       
  1161 +		return NULL;
       
  1162 +	}
       
  1163 +
       
  1164 +	if (!connection->returnElement) mpd_getNextReturnElement(connection);
       
  1165 +
       
  1166 +	if (connection->error)
       
  1167 +		return NULL;
       
  1168 +
       
  1169 +	stats = (mpd_SearchStats *)malloc(sizeof(mpd_SearchStats));
       
  1170 +	stats->numberOfSongs = 0;
       
  1171 +	stats->playTime = 0;
       
  1172 +
       
  1173 +	while (connection->returnElement) {
       
  1174 +		re = connection->returnElement;
       
  1175 +
       
  1176 +		if (strcmp(re->name, "songs") == 0) {
       
  1177 +			stats->numberOfSongs = atoi(re->value);
       
  1178 +		} else if (strcmp(re->name, "playtime") == 0) {
       
  1179 +			stats->playTime = strtol(re->value, NULL, 10);
       
  1180 +		}
       
  1181 +
       
  1182 +		mpd_getNextReturnElement(connection);
       
  1183 +		if (connection->error) {
       
  1184 +			free(stats);
       
  1185 +			return NULL;
       
  1186 +		}
       
  1187 +	}
       
  1188 +
       
  1189 +	if (connection->error) {
       
  1190 +		free(stats);
       
  1191 +		return NULL;
       
  1192 +	}
       
  1193 +
       
  1194 +	return stats;
       
  1195 +}
       
  1196 +
       
  1197 +void mpd_freeSearchStats(mpd_SearchStats * stats)
       
  1198 +{
       
  1199 +	free(stats);
       
  1200 +}
       
  1201 +
       
  1202 +static void mpd_initSong(mpd_Song * song) {
       
  1203 +	song->file = NULL;
       
  1204 +	song->artist = NULL;
       
  1205 +	song->album = NULL;
       
  1206 +	song->track = NULL;
       
  1207 +	song->title = NULL;
       
  1208 +	song->name = NULL;
       
  1209 +	song->date = NULL;
       
  1210 +	/* added by Qball */
       
  1211 +	song->genre = NULL;
       
  1212 +	song->composer = NULL;
       
  1213 +	song->performer = NULL;
       
  1214 +	song->disc = NULL;
       
  1215 +	song->comment = NULL;
       
  1216 +
       
  1217 +	song->time = MPD_SONG_NO_TIME;
       
  1218 +	song->pos = MPD_SONG_NO_NUM;
       
  1219 +	song->id = MPD_SONG_NO_ID;
       
  1220 +}
       
  1221 +
       
  1222 +static void mpd_finishSong(mpd_Song * song) {
       
  1223 +	if(song->file) free(song->file);
       
  1224 +	if(song->artist) free(song->artist);
       
  1225 +	if(song->album) free(song->album);
       
  1226 +	if(song->title) free(song->title);
       
  1227 +	if(song->track) free(song->track);
       
  1228 +	if(song->name) free(song->name);
       
  1229 +	if(song->date) free(song->date);
       
  1230 +	if(song->genre) free(song->genre);
       
  1231 +	if(song->composer) free(song->composer);
       
  1232 +	if(song->disc) free(song->disc);
       
  1233 +	if(song->comment) free(song->comment);
       
  1234 +}
       
  1235 +
       
  1236 +mpd_Song * mpd_newSong(void) {
       
  1237 +	mpd_Song * ret = (mpd_Song *)malloc(sizeof(mpd_Song));
       
  1238 +
       
  1239 +	mpd_initSong(ret);
       
  1240 +
       
  1241 +	return ret;
       
  1242 +}
       
  1243 +
       
  1244 +void mpd_freeSong(mpd_Song * song) {
       
  1245 +	mpd_finishSong(song);
       
  1246 +	free(song);
       
  1247 +}
       
  1248 +
       
  1249 +mpd_Song * mpd_songDup(mpd_Song * song) {
       
  1250 +	mpd_Song * ret = mpd_newSong();
       
  1251 +
       
  1252 +	if(song->file) ret->file = strdup(song->file);
       
  1253 +	if(song->artist) ret->artist = strdup(song->artist);
       
  1254 +	if(song->album) ret->album = strdup(song->album);
       
  1255 +	if(song->title) ret->title = strdup(song->title);
       
  1256 +	if(song->track) ret->track = strdup(song->track);
       
  1257 +	if(song->name) ret->name = strdup(song->name);
       
  1258 +	if(song->date) ret->date = strdup(song->date);
       
  1259 +	if(song->genre) ret->genre= strdup(song->genre);
       
  1260 +	if(song->composer) ret->composer= strdup(song->composer);
       
  1261 +	if(song->disc) ret->disc = strdup(song->disc);
       
  1262 +	if(song->comment) ret->comment = strdup(song->comment);
       
  1263 +	ret->time = song->time;
       
  1264 +	ret->pos = song->pos;
       
  1265 +	ret->id = song->id;
       
  1266 +
       
  1267 +	return ret;
       
  1268 +}
       
  1269 +
       
  1270 +static void mpd_initDirectory(mpd_Directory * directory) {
       
  1271 +	directory->path = NULL;
       
  1272 +}
       
  1273 +
       
  1274 +static void mpd_finishDirectory(mpd_Directory * directory) {
       
  1275 +	if(directory->path) free(directory->path);
       
  1276 +}
       
  1277 +
       
  1278 +mpd_Directory * mpd_newDirectory(void) {
       
  1279 +	mpd_Directory * directory = (mpd_Directory *)malloc(sizeof(mpd_Directory));;
       
  1280 +
       
  1281 +	mpd_initDirectory(directory);
       
  1282 +
       
  1283 +	return directory;
       
  1284 +}
       
  1285 +
       
  1286 +void mpd_freeDirectory(mpd_Directory * directory) {
       
  1287 +	mpd_finishDirectory(directory);
       
  1288 +
       
  1289 +	free(directory);
       
  1290 +}
       
  1291 +
       
  1292 +mpd_Directory * mpd_directoryDup(mpd_Directory * directory) {
       
  1293 +	mpd_Directory * ret = mpd_newDirectory();
       
  1294 +
       
  1295 +	if(directory->path) ret->path = strdup(directory->path);
       
  1296 +
       
  1297 +	return ret;
       
  1298 +}
       
  1299 +
       
  1300 +static void mpd_initPlaylistFile(mpd_PlaylistFile * playlist) {
       
  1301 +	playlist->path = NULL;
       
  1302 +}
       
  1303 +
       
  1304 +static void mpd_finishPlaylistFile(mpd_PlaylistFile * playlist) {
       
  1305 +	if(playlist->path) free(playlist->path);
       
  1306 +}
       
  1307 +
       
  1308 +mpd_PlaylistFile * mpd_newPlaylistFile(void) {
       
  1309 +	mpd_PlaylistFile * playlist = (mpd_PlaylistFile *)malloc(sizeof(mpd_PlaylistFile));
       
  1310 +
       
  1311 +	mpd_initPlaylistFile(playlist);
       
  1312 +
       
  1313 +	return playlist;
       
  1314 +}
       
  1315 +
       
  1316 +void mpd_freePlaylistFile(mpd_PlaylistFile * playlist) {
       
  1317 +	mpd_finishPlaylistFile(playlist);
       
  1318 +	free(playlist);
       
  1319 +}
       
  1320 +
       
  1321 +mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist) {
       
  1322 +	mpd_PlaylistFile * ret = mpd_newPlaylistFile();
       
  1323 +
       
  1324 +	if(playlist->path) ret->path = strdup(playlist->path);
       
  1325 +
       
  1326 +	return ret;
       
  1327 +}
       
  1328 +
       
  1329 +static void mpd_initInfoEntity(mpd_InfoEntity * entity) {
       
  1330 +	entity->info.directory = NULL;
       
  1331 +}
       
  1332 +
       
  1333 +static void mpd_finishInfoEntity(mpd_InfoEntity * entity) {
       
  1334 +	if(entity->info.directory) {
       
  1335 +		if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
       
  1336 +			mpd_freeDirectory(entity->info.directory);
       
  1337 +		}
       
  1338 +		else if(entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
       
  1339 +			mpd_freeSong(entity->info.song);
       
  1340 +		}
       
  1341 +		else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
       
  1342 +			mpd_freePlaylistFile(entity->info.playlistFile);
       
  1343 +		}
       
  1344 +	}
       
  1345 +}
       
  1346 +
       
  1347 +mpd_InfoEntity * mpd_newInfoEntity(void) {
       
  1348 +	mpd_InfoEntity * entity = (mpd_InfoEntity *)malloc(sizeof(mpd_InfoEntity));
       
  1349 +
       
  1350 +	mpd_initInfoEntity(entity);
       
  1351 +
       
  1352 +	return entity;
       
  1353 +}
       
  1354 +
       
  1355 +void mpd_freeInfoEntity(mpd_InfoEntity * entity) {
       
  1356 +	mpd_finishInfoEntity(entity);
       
  1357 +	free(entity);
       
  1358 +}
       
  1359 +
       
  1360 +static void mpd_sendInfoCommand(mpd_Connection * connection, char * command) {
       
  1361 +	mpd_executeCommand(connection,command);
       
  1362 +}
       
  1363 +
       
  1364 +mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection) {
       
  1365 +	mpd_InfoEntity * entity = NULL;
       
  1366 +
       
  1367 +	if(connection->doneProcessing || (connection->listOks &&
       
  1368 +	   connection->doneListOk))
       
  1369 +	{
       
  1370 +		return NULL;
       
  1371 +	}
       
  1372 +
       
  1373 +	if(!connection->returnElement) mpd_getNextReturnElement(connection);
       
  1374 +
       
  1375 +	if(connection->returnElement) {
       
  1376 +		if(strcmp(connection->returnElement->name,"file")==0) {
       
  1377 +			entity = mpd_newInfoEntity();
       
  1378 +			entity->type = MPD_INFO_ENTITY_TYPE_SONG;
       
  1379 +			entity->info.song = mpd_newSong();
       
  1380 +			entity->info.song->file =
       
  1381 +				strdup(connection->returnElement->value);
       
  1382 +		}
       
  1383 +		else if(strcmp(connection->returnElement->name,
       
  1384 +					"directory")==0) {
       
  1385 +			entity = mpd_newInfoEntity();
       
  1386 +			entity->type = MPD_INFO_ENTITY_TYPE_DIRECTORY;
       
  1387 +			entity->info.directory = mpd_newDirectory();
       
  1388 +			entity->info.directory->path =
       
  1389 +				strdup(connection->returnElement->value);
       
  1390 +		}
       
  1391 +		else if(strcmp(connection->returnElement->name,"playlist")==0) {
       
  1392 +			entity = mpd_newInfoEntity();
       
  1393 +			entity->type = MPD_INFO_ENTITY_TYPE_PLAYLISTFILE;
       
  1394 +			entity->info.playlistFile = mpd_newPlaylistFile();
       
  1395 +			entity->info.playlistFile->path =
       
  1396 +				strdup(connection->returnElement->value);
       
  1397 +		}
       
  1398 +		else if(strcmp(connection->returnElement->name, "cpos") == 0){
       
  1399 +			entity = mpd_newInfoEntity();
       
  1400 +			entity->type = MPD_INFO_ENTITY_TYPE_SONG;
       
  1401 +			entity->info.song = mpd_newSong();
       
  1402 +			entity->info.song->pos = atoi(connection->returnElement->value);
       
  1403 +		}
       
  1404 +		else {
       
  1405 +			connection->error = 1;
       
  1406 +			strcpy(connection->errorStr,"problem parsing song info");
       
  1407 +			return NULL;
       
  1408 +		}
       
  1409 +	}
       
  1410 +	else return NULL;
       
  1411 +
       
  1412 +	mpd_getNextReturnElement(connection);
       
  1413 +	while(connection->returnElement) {
       
  1414 +		mpd_ReturnElement * re = connection->returnElement;
       
  1415 +
       
  1416 +		if(strcmp(re->name,"file")==0) return entity;
       
  1417 +		else if(strcmp(re->name,"directory")==0) return entity;
       
  1418 +		else if(strcmp(re->name,"playlist")==0) return entity;
       
  1419 +		else if(strcmp(re->name,"cpos")==0) return entity;
       
  1420 +
       
  1421 +		if(entity->type == MPD_INFO_ENTITY_TYPE_SONG &&
       
  1422 +				strlen(re->value)) {
       
  1423 +			if(!entity->info.song->artist &&
       
  1424 +					strcmp(re->name,"Artist")==0) {
       
  1425 +				entity->info.song->artist = strdup(re->value);
       
  1426 +			}
       
  1427 +			else if(!entity->info.song->album &&
       
  1428 +					strcmp(re->name,"Album")==0) {
       
  1429 +				entity->info.song->album = strdup(re->value);
       
  1430 +			}
       
  1431 +			else if(!entity->info.song->title &&
       
  1432 +					strcmp(re->name,"Title")==0) {
       
  1433 +				entity->info.song->title = strdup(re->value);
       
  1434 +			}
       
  1435 +			else if(!entity->info.song->track &&
       
  1436 +					strcmp(re->name,"Track")==0) {
       
  1437 +				entity->info.song->track = strdup(re->value);
       
  1438 +			}
       
  1439 +			else if(!entity->info.song->name &&
       
  1440 +					strcmp(re->name,"Name")==0) {
       
  1441 +				entity->info.song->name = strdup(re->value);
       
  1442 +			}
       
  1443 +			else if(entity->info.song->time==MPD_SONG_NO_TIME &&
       
  1444 +					strcmp(re->name,"Time")==0) {
       
  1445 +				entity->info.song->time = atoi(re->value);
       
  1446 +			}
       
  1447 +			else if(entity->info.song->pos==MPD_SONG_NO_NUM &&
       
  1448 +					strcmp(re->name,"Pos")==0) {
       
  1449 +				entity->info.song->pos = atoi(re->value);
       
  1450 +			}
       
  1451 +			else if(entity->info.song->id==MPD_SONG_NO_ID &&
       
  1452 +					strcmp(re->name,"Id")==0) {
       
  1453 +				entity->info.song->id = atoi(re->value);
       
  1454 +			}
       
  1455 +			else if(!entity->info.song->date &&
       
  1456 +					strcmp(re->name, "Date") == 0) {
       
  1457 +				entity->info.song->date = strdup(re->value);
       
  1458 +			}
       
  1459 +			else if(!entity->info.song->genre &&
       
  1460 +					strcmp(re->name, "Genre") == 0) {
       
  1461 +				entity->info.song->genre = strdup(re->value);
       
  1462 +			}
       
  1463 +			else if(!entity->info.song->composer &&
       
  1464 +					strcmp(re->name, "Composer") == 0) {
       
  1465 +				entity->info.song->composer = strdup(re->value);
       
  1466 +			}
       
  1467 +			else if(!entity->info.song->performer &&
       
  1468 +					strcmp(re->name, "Performer") == 0) {
       
  1469 +				entity->info.song->performer = strdup(re->value);
       
  1470 +			}
       
  1471 +			else if(!entity->info.song->disc &&
       
  1472 +					strcmp(re->name, "Disc") == 0) {
       
  1473 +				entity->info.song->disc = strdup(re->value);
       
  1474 +			}
       
  1475 +			else if(!entity->info.song->comment &&
       
  1476 +					strcmp(re->name, "Comment") == 0) {
       
  1477 +				entity->info.song->comment = strdup(re->value);
       
  1478 +			}
       
  1479 +		}
       
  1480 +		else if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
       
  1481 +		}
       
  1482 +		else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) {
       
  1483 +		}
       
  1484 +
       
  1485 +		mpd_getNextReturnElement(connection);
       
  1486 +	}
       
  1487 +
       
  1488 +	return entity;
       
  1489 +}
       
  1490 +
       
  1491 +static char * mpd_getNextReturnElementNamed(mpd_Connection * connection,
       
  1492 +		const char * name)
       
  1493 +{
       
  1494 +	if(connection->doneProcessing || (connection->listOks &&
       
  1495 +				connection->doneListOk))
       
  1496 +	{
       
  1497 +		return NULL;
       
  1498 +	}
       
  1499 +
       
  1500 +	mpd_getNextReturnElement(connection);
       
  1501 +	while(connection->returnElement) {
       
  1502 +		mpd_ReturnElement * re = connection->returnElement;
       
  1503 +
       
  1504 +		if(strcmp(re->name,name)==0) return strdup(re->value);
       
  1505 +		mpd_getNextReturnElement(connection);
       
  1506 +	}
       
  1507 +
       
  1508 +	return NULL;
       
  1509 +}
       
  1510 +
       
  1511 +char *mpd_getNextTag(mpd_Connection *connection, int type)
       
  1512 +{
       
  1513 +	if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES ||
       
  1514 +	    type == MPD_TAG_ITEM_ANY)
       
  1515 +		return NULL;
       
  1516 +	if (type == MPD_TAG_ITEM_FILENAME)
       
  1517 +		return mpd_getNextReturnElementNamed(connection, "file");
       
  1518 +	return mpd_getNextReturnElementNamed(connection, mpdTagItemKeys[type]);
       
  1519 +}
       
  1520 +
       
  1521 +char * mpd_getNextArtist(mpd_Connection * connection) {
       
  1522 +	return mpd_getNextReturnElementNamed(connection,"Artist");
       
  1523 +}
       
  1524 +
       
  1525 +char * mpd_getNextAlbum(mpd_Connection * connection) {
       
  1526 +	return mpd_getNextReturnElementNamed(connection,"Album");
       
  1527 +}
       
  1528 +
       
  1529 +void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songPos) {
       
  1530 +	int len = strlen("playlistinfo")+2+INTLEN+3;
       
  1531 +	char *string = (char *)malloc(len);
       
  1532 +	snprintf(string, len, "playlistinfo \"%i\"\n", songPos);
       
  1533 +	mpd_sendInfoCommand(connection,string);
       
  1534 +	free(string);
       
  1535 +}
       
  1536 +
       
  1537 +void mpd_sendPlaylistIdCommand(mpd_Connection * connection, int id) {
       
  1538 +	int len = strlen("playlistid")+2+INTLEN+3;
       
  1539 +	char *string = (char *)malloc(len);
       
  1540 +	snprintf(string, len, "playlistid \"%i\"\n", id);
       
  1541 +	mpd_sendInfoCommand(connection, string);
       
  1542 +	free(string);
       
  1543 +}
       
  1544 +
       
  1545 +void mpd_sendPlChangesCommand(mpd_Connection * connection, long long playlist) {
       
  1546 +	int len = strlen("plchanges")+2+LONGLONGLEN+3;
       
  1547 +	char *string = (char *)malloc(len);
       
  1548 +	snprintf(string, len, "plchanges \"%lld\"\n", playlist);
       
  1549 +	mpd_sendInfoCommand(connection,string);
       
  1550 +	free(string);
       
  1551 +}
       
  1552 +
       
  1553 +void mpd_sendPlChangesPosIdCommand(mpd_Connection * connection, long long playlist) {
       
  1554 +	int len = strlen("plchangesposid")+2+LONGLONGLEN+3;
       
  1555 +	char *string = (char *)malloc(len);
       
  1556 +	snprintf(string, len, "plchangesposid \"%lld\"\n", playlist);
       
  1557 +	mpd_sendInfoCommand(connection,string);
       
  1558 +	free(string);
       
  1559 +}
       
  1560 +
       
  1561 +void mpd_sendListallCommand(mpd_Connection * connection, const char * dir) {
       
  1562 +	char * sDir = mpd_sanitizeArg(dir);
       
  1563 +	int len = strlen("listall")+2+strlen(sDir)+3;
       
  1564 +	char *string = (char *)malloc(len);
       
  1565 +	snprintf(string, len, "listall \"%s\"\n", sDir);
       
  1566 +	mpd_sendInfoCommand(connection,string);
       
  1567 +	free(string);
       
  1568 +	free(sDir);
       
  1569 +}
       
  1570 +
       
  1571 +void mpd_sendListallInfoCommand(mpd_Connection * connection, const char * dir) {
       
  1572 +	char * sDir = mpd_sanitizeArg(dir);
       
  1573 +	int len = strlen("listallinfo")+2+strlen(sDir)+3;
       
  1574 +	char *string = (char *)malloc(len);
       
  1575 +	snprintf(string, len, "listallinfo \"%s\"\n", sDir);
       
  1576 +	mpd_sendInfoCommand(connection,string);
       
  1577 +	free(string);
       
  1578 +	free(sDir);
       
  1579 +}
       
  1580 +
       
  1581 +void mpd_sendLsInfoCommand(mpd_Connection * connection, const char * dir) {
       
  1582 +	char * sDir = mpd_sanitizeArg(dir);
       
  1583 +	int len = strlen("lsinfo")+2+strlen(sDir)+3;
       
  1584 +	char *string = (char *)malloc(len);
       
  1585 +	snprintf(string, len, "lsinfo \"%s\"\n", sDir);
       
  1586 +	mpd_sendInfoCommand(connection,string);
       
  1587 +	free(string);
       
  1588 +	free(sDir);
       
  1589 +}
       
  1590 +
       
  1591 +void mpd_sendCurrentSongCommand(mpd_Connection * connection) {
       
  1592 +	mpd_executeCommand(connection,"currentsong\n");
       
  1593 +}
       
  1594 +
       
  1595 +void mpd_sendSearchCommand(mpd_Connection * connection, int table,
       
  1596 +		const char * str)
       
  1597 +{
       
  1598 +	mpd_startSearch(connection, 0);
       
  1599 +	mpd_addConstraintSearch(connection, table, str);
       
  1600 +	mpd_commitSearch(connection);
       
  1601 +}
       
  1602 +
       
  1603 +void mpd_sendFindCommand(mpd_Connection * connection, int table,
       
  1604 +		const char * str)
       
  1605 +{
       
  1606 +	mpd_startSearch(connection, 1);
       
  1607 +	mpd_addConstraintSearch(connection, table, str);
       
  1608 +	mpd_commitSearch(connection);
       
  1609 +}
       
  1610 +
       
  1611 +void mpd_sendListCommand(mpd_Connection * connection, int table,
       
  1612 +		const char * arg1)
       
  1613 +{
       
  1614 +	char st[10];
       
  1615 +	int len;
       
  1616 +	char *string;
       
  1617 +	if(table == MPD_TABLE_ARTIST) strcpy(st,"artist");
       
  1618 +	else if(table == MPD_TABLE_ALBUM) strcpy(st,"album");
       
  1619 +	else {
       
  1620 +		connection->error = 1;
       
  1621 +		strcpy(connection->errorStr,"unknown table for list");
       
  1622 +		return;
       
  1623 +	}
       
  1624 +	if(arg1) {
       
  1625 +		char * sanitArg1 = mpd_sanitizeArg(arg1);
       
  1626 +		len = strlen("list")+1+strlen(sanitArg1)+2+strlen(st)+3;
       
  1627 +		string = (char *)malloc(len);
       
  1628 +		snprintf(string, len, "list %s \"%s\"\n", st, sanitArg1);
       
  1629 +		free(sanitArg1);
       
  1630 +	}
       
  1631 +	else {
       
  1632 +		len = strlen("list")+1+strlen(st)+2;
       
  1633 +		string = (char *)malloc(len);
       
  1634 +		snprintf(string, len, "list %s\n", st);
       
  1635 +	}
       
  1636 +	mpd_sendInfoCommand(connection,string);
       
  1637 +	free(string);
       
  1638 +}
       
  1639 +
       
  1640 +void mpd_sendAddCommand(mpd_Connection * connection, const char * file) {
       
  1641 +	char * sFile = mpd_sanitizeArg(file);
       
  1642 +	int len = strlen("add")+2+strlen(sFile)+3;
       
  1643 +	char *string = (char *)malloc(len);
       
  1644 +	snprintf(string, len, "add \"%s\"\n", sFile);
       
  1645 +	mpd_executeCommand(connection,string);
       
  1646 +	free(string);
       
  1647 +	free(sFile);
       
  1648 +}
       
  1649 +
       
  1650 +int mpd_sendAddIdCommand(mpd_Connection *connection, const char *file)
       
  1651 +{
       
  1652 +	int retval = -1;
       
  1653 +	char *sFile = mpd_sanitizeArg(file);
       
  1654 +	int len = strlen("addid")+2+strlen(sFile)+3;
       
  1655 +	char *string = (char *)malloc(len);
       
  1656 +
       
  1657 +	snprintf(string, len, "addid \"%s\"\n", sFile);
       
  1658 +	mpd_sendInfoCommand(connection, string);
       
  1659 +	free(string);
       
  1660 +	free(sFile);
       
  1661 +
       
  1662 +	string = mpd_getNextReturnElementNamed(connection, "Id");
       
  1663 +	if (string) {
       
  1664 +		retval = atoi(string);
       
  1665 +		free(string);
       
  1666 +	}
       
  1667 +	
       
  1668 +	return retval;
       
  1669 +}
       
  1670 +
       
  1671 +void mpd_sendDeleteCommand(mpd_Connection * connection, int songPos) {
       
  1672 +	int len = strlen("delete")+2+INTLEN+3;
       
  1673 +	char *string = (char *)malloc(len);
       
  1674 +	snprintf(string, len, "delete \"%i\"\n", songPos);
       
  1675 +	mpd_sendInfoCommand(connection,string);
       
  1676 +	free(string);
       
  1677 +}
       
  1678 +
       
  1679 +void mpd_sendDeleteIdCommand(mpd_Connection * connection, int id) {
       
  1680 +	int len = strlen("deleteid")+2+INTLEN+3;
       
  1681 +	char *string = (char *)malloc(len);
       
  1682 +	snprintf(string, len, "deleteid \"%i\"\n", id);
       
  1683 +	mpd_sendInfoCommand(connection,string);
       
  1684 +	free(string);
       
  1685 +}
       
  1686 +
       
  1687 +void mpd_sendSaveCommand(mpd_Connection * connection, const char * name) {
       
  1688 +	char * sName = mpd_sanitizeArg(name);
       
  1689 +	int len = strlen("save")+2+strlen(sName)+3;
       
  1690 +	char *string = (char *)malloc(len);
       
  1691 +	snprintf(string, len, "save \"%s\"\n", sName);
       
  1692 +	mpd_executeCommand(connection,string);
       
  1693 +	free(string);
       
  1694 +	free(sName);
       
  1695 +}
       
  1696 +
       
  1697 +void mpd_sendLoadCommand(mpd_Connection * connection, const char * name) {
       
  1698 +	char * sName = mpd_sanitizeArg(name);
       
  1699 +	int len = strlen("load")+2+strlen(sName)+3;
       
  1700 +	char *string = (char *)malloc(len);
       
  1701 +	snprintf(string, len, "load \"%s\"\n", sName);
       
  1702 +	mpd_executeCommand(connection,string);
       
  1703 +	free(string);
       
  1704 +	free(sName);
       
  1705 +}
       
  1706 +
       
  1707 +void mpd_sendRmCommand(mpd_Connection * connection, const char * name) {
       
  1708 +	char * sName = mpd_sanitizeArg(name);
       
  1709 +	int len = strlen("rm")+2+strlen(sName)+3;
       
  1710 +	char *string = (char *)malloc(len);
       
  1711 +	snprintf(string, len, "rm \"%s\"\n", sName);
       
  1712 +	mpd_executeCommand(connection,string);
       
  1713 +	free(string);
       
  1714 +	free(sName);
       
  1715 +}
       
  1716 +
       
  1717 +void mpd_sendRenameCommand(mpd_Connection *connection, const char *from,
       
  1718 +                           const char *to)
       
  1719 +{
       
  1720 +	char *sFrom = mpd_sanitizeArg(from);
       
  1721 +	char *sTo = mpd_sanitizeArg(to);
       
  1722 +	int len = strlen("rename")+2+strlen(sFrom)+3+strlen(sTo)+3;
       
  1723 +	char *string = (char *)malloc(len);
       
  1724 +	snprintf(string, len, "rename \"%s\" \"%s\"\n", sFrom, sTo);
       
  1725 +	mpd_executeCommand(connection, string);
       
  1726 +	free(string);
       
  1727 +	free(sFrom);
       
  1728 +	free(sTo);
       
  1729 +}
       
  1730 +
       
  1731 +void mpd_sendShuffleCommand(mpd_Connection * connection) {
       
  1732 +	mpd_executeCommand(connection,"shuffle\n");
       
  1733 +}
       
  1734 +
       
  1735 +void mpd_sendClearCommand(mpd_Connection * connection) {
       
  1736 +	mpd_executeCommand(connection,"clear\n");
       
  1737 +}
       
  1738 +
       
  1739 +void mpd_sendPlayCommand(mpd_Connection * connection, int songPos) {
       
  1740 +	int len = strlen("play")+2+INTLEN+3;
       
  1741 +	char *string = (char *)malloc(len);
       
  1742 +	snprintf(string, len, "play \"%i\"\n", songPos);
       
  1743 +	mpd_sendInfoCommand(connection,string);
       
  1744 +	free(string);
       
  1745 +}
       
  1746 +
       
  1747 +void mpd_sendPlayIdCommand(mpd_Connection * connection, int id) {
       
  1748 +	int len = strlen("playid")+2+INTLEN+3;
       
  1749 +	char *string = (char *)malloc(len);
       
  1750 +	snprintf(string, len, "playid \"%i\"\n", id);
       
  1751 +	mpd_sendInfoCommand(connection,string);
       
  1752 +	free(string);
       
  1753 +}
       
  1754 +
       
  1755 +void mpd_sendStopCommand(mpd_Connection * connection) {
       
  1756 +	mpd_executeCommand(connection,"stop\n");
       
  1757 +}
       
  1758 +
       
  1759 +void mpd_sendPauseCommand(mpd_Connection * connection, int pauseMode) {
       
  1760 +	int len = strlen("pause")+2+INTLEN+3;
       
  1761 +	char *string = (char *)malloc(len);
       
  1762 +	snprintf(string, len, "pause \"%i\"\n", pauseMode);
       
  1763 +	mpd_executeCommand(connection,string);
       
  1764 +	free(string);
       
  1765 +}
       
  1766 +
       
  1767 +void mpd_sendNextCommand(mpd_Connection * connection) {
       
  1768 +	mpd_executeCommand(connection,"next\n");
       
  1769 +}
       
  1770 +
       
  1771 +void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to) {
       
  1772 +	int len = strlen("move")+2+INTLEN+3+INTLEN+3;
       
  1773 +	char *string = (char *)malloc(len);
       
  1774 +	snprintf(string, len, "move \"%i\" \"%i\"\n", from, to);
       
  1775 +	mpd_sendInfoCommand(connection,string);
       
  1776 +	free(string);
       
  1777 +}
       
  1778 +
       
  1779 +void mpd_sendMoveIdCommand(mpd_Connection * connection, int id, int to) {
       
  1780 +	int len = strlen("moveid")+2+INTLEN+3+INTLEN+3;
       
  1781 +	char *string = (char *)malloc(len);
       
  1782 +	snprintf(string, len, "moveid \"%i\" \"%i\"\n", id, to);
       
  1783 +	mpd_sendInfoCommand(connection,string);
       
  1784 +	free(string);
       
  1785 +}
       
  1786 +
       
  1787 +void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2) {
       
  1788 +	int len = strlen("swap")+2+INTLEN+3+INTLEN+3;
       
  1789 +	char *string = (char *)malloc(len);
       
  1790 +	snprintf(string, len, "swap \"%i\" \"%i\"\n", song1, song2);
       
  1791 +	mpd_sendInfoCommand(connection,string);
       
  1792 +	free(string);
       
  1793 +}
       
  1794 +
       
  1795 +void mpd_sendSwapIdCommand(mpd_Connection * connection, int id1, int id2) {
       
  1796 +	int len = strlen("swapid")+2+INTLEN+3+INTLEN+3;
       
  1797 +	char *string = (char *)malloc(len);
       
  1798 +	snprintf(string, len, "swapid \"%i\" \"%i\"\n", id1, id2);
       
  1799 +	mpd_sendInfoCommand(connection,string);
       
  1800 +	free(string);
       
  1801 +}
       
  1802 +
       
  1803 +void mpd_sendSeekCommand(mpd_Connection * connection, int song, int time) {
       
  1804 +	int len = strlen("seek")+2+INTLEN+3+INTLEN+3;
       
  1805 +	char *string = (char *)malloc(len);
       
  1806 +	snprintf(string, len, "seek \"%i\" \"%i\"\n", song, time);
       
  1807 +	mpd_sendInfoCommand(connection,string);
       
  1808 +	free(string);
       
  1809 +}
       
  1810 +
       
  1811 +void mpd_sendSeekIdCommand(mpd_Connection * connection, int id, int time) {
       
  1812 +	int len = strlen("seekid")+2+INTLEN+3+INTLEN+3;
       
  1813 +	char *string = (char *)malloc(len);
       
  1814 +	snprintf(string, len, "seekid \"%i\" \"%i\"\n", id, time);
       
  1815 +	mpd_sendInfoCommand(connection,string);
       
  1816 +	free(string);
       
  1817 +}
       
  1818 +
       
  1819 +void mpd_sendUpdateCommand(mpd_Connection * connection, char * path) {
       
  1820 +	char * sPath = mpd_sanitizeArg(path);
       
  1821 +	int len = strlen("update")+2+strlen(sPath)+3;
       
  1822 +	char *string = (char *)malloc(len);
       
  1823 +	snprintf(string, len, "update \"%s\"\n", sPath);
       
  1824 +	mpd_sendInfoCommand(connection,string);
       
  1825 +	free(string);
       
  1826 +	free(sPath);
       
  1827 +}
       
  1828 +
       
  1829 +int mpd_getUpdateId(mpd_Connection * connection) {
       
  1830 +	char * jobid;
       
  1831 +	int ret = 0;
       
  1832 +
       
  1833 +	jobid = mpd_getNextReturnElementNamed(connection,"updating_db");
       
  1834 +	if(jobid) {
       
  1835 +		ret = atoi(jobid);
       
  1836 +		free(jobid);
       
  1837 +	}
       
  1838 +
       
  1839 +	return ret;
       
  1840 +}
       
  1841 +
       
  1842 +void mpd_sendPrevCommand(mpd_Connection * connection) {
       
  1843 +	mpd_executeCommand(connection,"previous\n");
       
  1844 +}
       
  1845 +
       
  1846 +void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode) {
       
  1847 +	int len = strlen("repeat")+2+INTLEN+3;
       
  1848 +	char *string = (char *)malloc(len);
       
  1849 +	snprintf(string, len, "repeat \"%i\"\n", repeatMode);
       
  1850 +	mpd_executeCommand(connection,string);
       
  1851 +	free(string);
       
  1852 +}
       
  1853 +
       
  1854 +void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode) {
       
  1855 +	int len = strlen("random")+2+INTLEN+3;
       
  1856 +	char *string = (char *)malloc(len);
       
  1857 +	snprintf(string, len, "random \"%i\"\n", randomMode);
       
  1858 +	mpd_executeCommand(connection,string);
       
  1859 +	free(string);
       
  1860 +}
       
  1861 +
       
  1862 +void mpd_sendSetvolCommand(mpd_Connection * connection, int volumeChange) {
       
  1863 +	int len = strlen("setvol")+2+INTLEN+3;
       
  1864 +	char *string = (char *)malloc(len);
       
  1865 +	snprintf(string, len, "setvol \"%i\"\n", volumeChange);
       
  1866 +	mpd_executeCommand(connection,string);
       
  1867 +	free(string);
       
  1868 +}
       
  1869 +
       
  1870 +void mpd_sendVolumeCommand(mpd_Connection * connection, int volumeChange) {
       
  1871 +	int len = strlen("volume")+2+INTLEN+3;
       
  1872 +	char *string = (char *)malloc(len);
       
  1873 +	snprintf(string, len, "volume \"%i\"\n", volumeChange);
       
  1874 +	mpd_executeCommand(connection,string);
       
  1875 +	free(string);
       
  1876 +}
       
  1877 +
       
  1878 +void mpd_sendCrossfadeCommand(mpd_Connection * connection, int seconds) {
       
  1879 +	int len = strlen("crossfade")+2+INTLEN+3;
       
  1880 +	char *string = (char *)malloc(len);
       
  1881 +	snprintf(string, len, "crossfade \"%i\"\n", seconds);
       
  1882 +	mpd_executeCommand(connection,string);
       
  1883 +	free(string);
       
  1884 +}
       
  1885 +
       
  1886 +void mpd_sendPasswordCommand(mpd_Connection * connection, const char * pass) {
       
  1887 +	char * sPass = mpd_sanitizeArg(pass);
       
  1888 +	int len = strlen("password")+2+strlen(sPass)+3;
       
  1889 +	char *string = (char *)malloc(len);
       
  1890 +	snprintf(string, len, "password \"%s\"\n", sPass);
       
  1891 +	mpd_executeCommand(connection,string);
       
  1892 +	free(string);
       
  1893 +	free(sPass);
       
  1894 +}
       
  1895 +
       
  1896 +void mpd_sendCommandListBegin(mpd_Connection * connection) {
       
  1897 +	if(connection->commandList) {
       
  1898 +		strcpy(connection->errorStr,"already in command list mode");
       
  1899 +		connection->error = 1;
       
  1900 +		return;
       
  1901 +	}
       
  1902 +	connection->commandList = COMMAND_LIST;
       
  1903 +	mpd_executeCommand(connection,"command_list_begin\n");
       
  1904 +}
       
  1905 +
       
  1906 +void mpd_sendCommandListOkBegin(mpd_Connection * connection) {
       
  1907 +	if(connection->commandList) {
       
  1908 +		strcpy(connection->errorStr,"already in command list mode");
       
  1909 +		connection->error = 1;
       
  1910 +		return;
       
  1911 +	}
       
  1912 +	connection->commandList = COMMAND_LIST_OK;
       
  1913 +	mpd_executeCommand(connection,"command_list_ok_begin\n");
       
  1914 +	connection->listOks = 0;
       
  1915 +}
       
  1916 +
       
  1917 +void mpd_sendCommandListEnd(mpd_Connection * connection) {
       
  1918 +	if(!connection->commandList) {
       
  1919 +		strcpy(connection->errorStr,"not in command list mode");
       
  1920 +		connection->error = 1;
       
  1921 +		return;
       
  1922 +	}
       
  1923 +	connection->commandList = 0;
       
  1924 +	mpd_executeCommand(connection,"command_list_end\n");
       
  1925 +}
       
  1926 +
       
  1927 +void mpd_sendOutputsCommand(mpd_Connection * connection) {
       
  1928 +	mpd_executeCommand(connection,"outputs\n");
       
  1929 +}
       
  1930 +
       
  1931 +mpd_OutputEntity * mpd_getNextOutput(mpd_Connection * connection) {
       
  1932 +	mpd_OutputEntity * output = NULL;
       
  1933 +
       
  1934 +	if(connection->doneProcessing || (connection->listOks &&
       
  1935 +				connection->doneListOk))
       
  1936 +	{
       
  1937 +		return NULL;
       
  1938 +	}
       
  1939 +
       
  1940 +	if(connection->error) return NULL;
       
  1941 +
       
  1942 +	output = (mpd_OutputEntity *)malloc(sizeof(mpd_OutputEntity));
       
  1943 +	output->id = -10;
       
  1944 +	output->name = NULL;
       
  1945 +	output->enabled = 0;
       
  1946 +
       
  1947 +	if(!connection->returnElement) mpd_getNextReturnElement(connection);
       
  1948 +
       
  1949 +	while(connection->returnElement) {
       
  1950 +		mpd_ReturnElement * re = connection->returnElement;
       
  1951 +		if(strcmp(re->name,"outputid")==0) {
       
  1952 +			if(output!=NULL && output->id>=0) return output;
       
  1953 +			output->id = atoi(re->value);
       
  1954 +		}
       
  1955 +		else if(strcmp(re->name,"outputname")==0) {
       
  1956 +			output->name = strdup(re->value);
       
  1957 +		}
       
  1958 +		else if(strcmp(re->name,"outputenabled")==0) {
       
  1959 +			output->enabled = atoi(re->value);
       
  1960 +		}
       
  1961 +
       
  1962 +		mpd_getNextReturnElement(connection);
       
  1963 +		if(connection->error) {
       
  1964 +			free(output);
       
  1965 +			return NULL;
       
  1966 +		}
       
  1967 +
       
  1968 +	}
       
  1969 +
       
  1970 +	return output;
       
  1971 +}
       
  1972 +
       
  1973 +void mpd_sendEnableOutputCommand(mpd_Connection * connection, int outputId) {
       
  1974 +	int len = strlen("enableoutput")+2+INTLEN+3;
       
  1975 +	char *string = (char *)malloc(len);
       
  1976 +	snprintf(string, len, "enableoutput \"%i\"\n", outputId);
       
  1977 +	mpd_executeCommand(connection,string);
       
  1978 +	free(string);
       
  1979 +}
       
  1980 +
       
  1981 +void mpd_sendDisableOutputCommand(mpd_Connection * connection, int outputId) {
       
  1982 +	int len = strlen("disableoutput")+2+INTLEN+3;
       
  1983 +	char *string = (char *)malloc(len);
       
  1984 +	snprintf(string, len, "disableoutput \"%i\"\n", outputId);
       
  1985 +	mpd_executeCommand(connection,string);
       
  1986 +	free(string);
       
  1987 +}
       
  1988 +
       
  1989 +void mpd_freeOutputElement(mpd_OutputEntity * output) {
       
  1990 +	free(output->name);
       
  1991 +	free(output);
       
  1992 +}
       
  1993 +
       
  1994 +/**
       
  1995 + * mpd_sendNotCommandsCommand
       
  1996 + * odd naming, but it gets the not allowed commands
       
  1997 + */
       
  1998 +
       
  1999 +void mpd_sendNotCommandsCommand(mpd_Connection * connection)
       
  2000 +{
       
  2001 +	mpd_executeCommand(connection, "notcommands\n");
       
  2002 +}
       
  2003 +
       
  2004 +/**
       
  2005 + * mpd_sendCommandsCommand
       
  2006 + * odd naming, but it gets the allowed commands
       
  2007 + */
       
  2008 +void mpd_sendCommandsCommand(mpd_Connection * connection)
       
  2009 +{
       
  2010 +	mpd_executeCommand(connection, "commands\n");
       
  2011 +}
       
  2012 +
       
  2013 +/**
       
  2014 + * Get the next returned command
       
  2015 + */
       
  2016 +char * mpd_getNextCommand(mpd_Connection * connection)
       
  2017 +{
       
  2018 +	return mpd_getNextReturnElementNamed(connection, "command");
       
  2019 +}
       
  2020 +
       
  2021 +void mpd_sendUrlHandlersCommand(mpd_Connection * connection)
       
  2022 +{
       
  2023 +	mpd_executeCommand(connection, "urlhandlers\n");
       
  2024 +}
       
  2025 +
       
  2026 +char * mpd_getNextHandler(mpd_Connection * connection)
       
  2027 +{
       
  2028 +	return mpd_getNextReturnElementNamed(connection, "handler");
       
  2029 +}
       
  2030 +
       
  2031 +void mpd_sendTagTypesCommand(mpd_Connection * connection)
       
  2032 +{
       
  2033 +	mpd_executeCommand(connection, "tagtypes\n");
       
  2034 +}
       
  2035 +
       
  2036 +char * mpd_getNextTagType(mpd_Connection * connection)
       
  2037 +{
       
  2038 +	return mpd_getNextReturnElementNamed(connection, "tagtype");
       
  2039 +}
       
  2040 +
       
  2041 +void mpd_startSearch(mpd_Connection *connection, int exact)
       
  2042 +{
       
  2043 +	if (connection->request) {
       
  2044 +		strcpy(connection->errorStr, "search already in progress");
       
  2045 +		connection->error = 1;
       
  2046 +		return;
       
  2047 +	}
       
  2048 +
       
  2049 +	if (exact) connection->request = strdup("find");
       
  2050 +	else connection->request = strdup("search");
       
  2051 +}
       
  2052 +
       
  2053 +void mpd_startStatsSearch(mpd_Connection *connection)
       
  2054 +{
       
  2055 +	if (connection->request) {
       
  2056 +		strcpy(connection->errorStr, "search already in progress");
       
  2057 +		connection->error = 1;
       
  2058 +		return;
       
  2059 +	}
       
  2060 +
       
  2061 +	connection->request = strdup("count");
       
  2062 +}
       
  2063 +
       
  2064 +void mpd_startPlaylistSearch(mpd_Connection *connection, int exact)
       
  2065 +{
       
  2066 +	if (connection->request) {
       
  2067 +		strcpy(connection->errorStr, "search already in progress");
       
  2068 +		connection->error = 1;
       
  2069 +		return;
       
  2070 +	}
       
  2071 +
       
  2072 +	if (exact) connection->request = strdup("playlistfind");
       
  2073 +	else connection->request = strdup("playlistsearch");
       
  2074 +}
       
  2075 +
       
  2076 +void mpd_startFieldSearch(mpd_Connection *connection, int type)
       
  2077 +{
       
  2078 +	char *strtype;
       
  2079 +	int len;
       
  2080 +
       
  2081 +	if (connection->request) {
       
  2082 +		strcpy(connection->errorStr, "search already in progress");
       
  2083 +		connection->error = 1;
       
  2084 +		return;
       
  2085 +	}
       
  2086 +
       
  2087 +	if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES) {
       
  2088 +		strcpy(connection->errorStr, "invalid type specified");
       
  2089 +		connection->error = 1;
       
  2090 +		return;
       
  2091 +	}
       
  2092 +
       
  2093 +	strtype = mpdTagItemKeys[type];
       
  2094 +
       
  2095 +	len = 5+strlen(strtype)+1;
       
  2096 +	connection->request = (char *)malloc(len);
       
  2097 +
       
  2098 +	snprintf(connection->request, len, "list %c%s",
       
  2099 +	         tolower(strtype[0]), strtype+1);
       
  2100 +}
       
  2101 +
       
  2102 +void mpd_addConstraintSearch(mpd_Connection *connection, int type, const char *name)
       
  2103 +{
       
  2104 +	char *strtype;
       
  2105 +	char *arg;
       
  2106 +	int len;
       
  2107 +	char *string;
       
  2108 +
       
  2109 +	if (!connection->request) {
       
  2110 +		strcpy(connection->errorStr, "no search in progress");
       
  2111 +		connection->error = 1;
       
  2112 +		return;
       
  2113 +	}
       
  2114 +
       
  2115 +	if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES) {
       
  2116 +		strcpy(connection->errorStr, "invalid type specified");
       
  2117 +		connection->error = 1;
       
  2118 +		return;
       
  2119 +	}
       
  2120 +
       
  2121 +	if (name == NULL) {
       
  2122 +		strcpy(connection->errorStr, "no name specified");
       
  2123 +		connection->error = 1;
       
  2124 +		return;
       
  2125 +	}
       
  2126 +
       
  2127 +	string = strdup(connection->request);
       
  2128 +	strtype = mpdTagItemKeys[type];
       
  2129 +	arg = mpd_sanitizeArg(name);
       
  2130 +
       
  2131 +	len = strlen(string)+1+strlen(strtype)+2+strlen(arg)+2;
       
  2132 +	connection->request = (char *)realloc(connection->request, len);
       
  2133 +	snprintf(connection->request, len, "%s %c%s \"%s\"",
       
  2134 +	         string, tolower(strtype[0]), strtype+1, arg);
       
  2135 +
       
  2136 +	free(string);
       
  2137 +	free(arg);
       
  2138 +}
       
  2139 +
       
  2140 +void mpd_commitSearch(mpd_Connection *connection)
       
  2141 +{
       
  2142 +	int len;
       
  2143 +
       
  2144 +	if (!connection->request) {
       
  2145 +		strcpy(connection->errorStr, "no search in progress");
       
  2146 +		connection->error = 1;
       
  2147 +		return;
       
  2148 +	}
       
  2149 +
       
  2150 +	len = strlen(connection->request)+2;
       
  2151 +	connection->request = (char *)realloc(connection->request, len);
       
  2152 +	connection->request[len-2] = '\n';
       
  2153 +	connection->request[len-1] = '\0';
       
  2154 +	mpd_sendInfoCommand(connection, connection->request);
       
  2155 +
       
  2156 +	free(connection->request);
       
  2157 +	connection->request = NULL;
       
  2158 +}
       
  2159 +
       
  2160 +/**
       
  2161 + * @param connection a MpdConnection
       
  2162 + * @param path	the path to the playlist.
       
  2163 + *
       
  2164 + * List the content, with full metadata, of a stored playlist.
       
  2165 + *
       
  2166 + */
       
  2167 +void mpd_sendListPlaylistInfoCommand(mpd_Connection *connection, char *path)
       
  2168 +{
       
  2169 +	char *arg = mpd_sanitizeArg(path);
       
  2170 +	int len = strlen("listplaylistinfo")+2+strlen(arg)+3;
       
  2171 +	char *query = (char *)malloc(len);
       
  2172 +	snprintf(query, len, "listplaylistinfo \"%s\"\n", arg);
       
  2173 +	mpd_sendInfoCommand(connection, query);
       
  2174 +	free(arg);
       
  2175 +	free(query);
       
  2176 +}
       
  2177 +
       
  2178 +/**
       
  2179 + * @param connection a MpdConnection
       
  2180 + * @param path	the path to the playlist.
       
  2181 + *
       
  2182 + * List the content of a stored playlist.
       
  2183 + *
       
  2184 + */
       
  2185 +void mpd_sendListPlaylistCommand(mpd_Connection *connection, char *path)
       
  2186 +{
       
  2187 +	char *arg = mpd_sanitizeArg(path);
       
  2188 +	int len = strlen("listplaylist")+2+strlen(arg)+3;
       
  2189 +	char *query = (char *)malloc(len);
       
  2190 +	snprintf(query, len, "listplaylist \"%s\"\n", arg);
       
  2191 +	mpd_sendInfoCommand(connection, query);
       
  2192 +	free(arg);
       
  2193 +	free(query);
       
  2194 +}
       
  2195 +
       
  2196 +void mpd_sendPlaylistClearCommand(mpd_Connection *connection, char *path)
       
  2197 +{
       
  2198 +	char *sPath = mpd_sanitizeArg(path);
       
  2199 +	int len = strlen("playlistclear")+2+strlen(sPath)+3;
       
  2200 +	char *string = (char *)malloc(len);
       
  2201 +	snprintf(string, len, "playlistclear \"%s\"\n", sPath);
       
  2202 +	mpd_executeCommand(connection, string);
       
  2203 +	free(sPath);
       
  2204 +	free(string);
       
  2205 +}
       
  2206 +
       
  2207 +void mpd_sendPlaylistAddCommand(mpd_Connection *connection,
       
  2208 +                                char *playlist, char *path)
       
  2209 +{
       
  2210 +	char *sPlaylist = mpd_sanitizeArg(playlist);
       
  2211 +	char *sPath = mpd_sanitizeArg(path);
       
  2212 +	int len = strlen("playlistadd")+2+strlen(sPlaylist)+3+strlen(sPath)+3;
       
  2213 +	char *string = (char *)malloc(len);
       
  2214 +	snprintf(string, len, "playlistadd \"%s\" \"%s\"\n", sPlaylist, sPath);
       
  2215 +	mpd_executeCommand(connection, string);
       
  2216 +	free(sPlaylist);
       
  2217 +	free(sPath);
       
  2218 +	free(string);
       
  2219 +}
       
  2220 +
       
  2221 +void mpd_sendPlaylistMoveCommand(mpd_Connection *connection,
       
  2222 +                                 char *playlist, int from, int to)
       
  2223 +{
       
  2224 +	char *sPlaylist = mpd_sanitizeArg(playlist);
       
  2225 +	int len = strlen("playlistmove")+
       
  2226 +	          2+strlen(sPlaylist)+3+INTLEN+3+INTLEN+3;
       
  2227 +	char *string = (char *)malloc(len);
       
  2228 +	snprintf(string, len, "playlistmove \"%s\" \"%i\" \"%i\"\n",
       
  2229 +	         sPlaylist, from, to);
       
  2230 +	mpd_executeCommand(connection, string);
       
  2231 +	free(sPlaylist);
       
  2232 +	free(string);
       
  2233 +}
       
  2234 +
       
  2235 +void mpd_sendPlaylistDeleteCommand(mpd_Connection *connection,
       
  2236 +                                   char *playlist, int pos)
       
  2237 +{
       
  2238 +	char *sPlaylist = mpd_sanitizeArg(playlist);
       
  2239 +	int len = strlen("playlistdelete")+2+strlen(sPlaylist)+3+INTLEN+3;
       
  2240 +	char *string = (char *)malloc(len);
       
  2241 +	snprintf(string, len, "playlistdelete \"%s\" \"%i\"\n", sPlaylist, pos);
       
  2242 +	mpd_executeCommand(connection, string);
       
  2243 +	free(sPlaylist);
       
  2244 +	free(string);
       
  2245 +}
       
  2246 diff -r 171db9560cb5 clients/mpd/libmpdclient.h
       
  2247 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
       
  2248 +++ b/clients/mpd/libmpdclient.h	Mon May 19 00:21:51 2008 -0400
       
  2249 @@ -0,0 +1,670 @@
       
  2250 +/* libmpdclient
       
  2251 +   (c)2003-2006 by Warren Dukes (warren.dukes@gmail.com)
       
  2252 +   This project's homepage is: http://www.musicpd.org
       
  2253 +
       
  2254 +   Redistribution and use in source and binary forms, with or without
       
  2255 +   modification, are permitted provided that the following conditions
       
  2256 +   are met:
       
  2257 +
       
  2258 +   - Redistributions of source code must retain the above copyright
       
  2259 +   notice, this list of conditions and the following disclaimer.
       
  2260 +
       
  2261 +   - Redistributions in binary form must reproduce the above copyright
       
  2262 +   notice, this list of conditions and the following disclaimer in the
       
  2263 +   documentation and/or other materials provided with the distribution.
       
  2264 +
       
  2265 +   - Neither the name of the Music Player Daemon nor the names of its
       
  2266 +   contributors may be used to endorse or promote products derived from
       
  2267 +   this software without specific prior written permission.
       
  2268 +
       
  2269 +   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
       
  2270 +   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
       
  2271 +   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
       
  2272 +   A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
       
  2273 +   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
       
  2274 +   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
       
  2275 +   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
       
  2276 +   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
       
  2277 +   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
       
  2278 +   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
       
  2279 +   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
       
  2280 +*/
       
  2281 +
       
  2282 +#ifndef LIBMPDCLIENT_H
       
  2283 +#define LIBMPDCLIENT_H
       
  2284 +
       
  2285 +#ifdef WIN32
       
  2286 +#  define __W32API_USE_DLLIMPORT__ 1
       
  2287 +#endif
       
  2288 +
       
  2289 +#include <sys/time.h>
       
  2290 +#include <stdarg.h>
       
  2291 +#define MPD_BUFFER_MAX_LENGTH	50000
       
  2292 +#define MPD_ERRORSTR_MAX_LENGTH	1000
       
  2293 +#define MPD_WELCOME_MESSAGE	"OK MPD "
       
  2294 +
       
  2295 +#define MPD_ERROR_TIMEOUT	10 /* timeout trying to talk to mpd */
       
  2296 +#define MPD_ERROR_SYSTEM	11 /* system error */
       
  2297 +#define MPD_ERROR_UNKHOST	12 /* unknown host */
       
  2298 +#define MPD_ERROR_CONNPORT	13 /* problems connecting to port on host */
       
  2299 +#define MPD_ERROR_NOTMPD	14 /* mpd not running on port at host */
       
  2300 +#define MPD_ERROR_NORESPONSE	15 /* no response on attempting to connect */
       
  2301 +#define MPD_ERROR_SENDING	16 /* error sending command */
       
  2302 +#define MPD_ERROR_CONNCLOSED	17 /* connection closed by mpd */
       
  2303 +#define MPD_ERROR_ACK		18 /* ACK returned! */
       
  2304 +#define MPD_ERROR_BUFFEROVERRUN	19 /* Buffer was overrun! */
       
  2305 +
       
  2306 +#define MPD_ACK_ERROR_UNK	-1
       
  2307 +#define MPD_ERROR_AT_UNK	-1
       
  2308 +
       
  2309 +#define MPD_ACK_ERROR_NOT_LIST			1
       
  2310 +#define MPD_ACK_ERROR_ARG			2
       
  2311 +#define MPD_ACK_ERROR_PASSWORD			3
       
  2312 +#define MPD_ACK_ERROR_PERMISSION		4
       
  2313 +#define MPD_ACK_ERROR_UNKNOWN_CMD		5
       
  2314 +
       
  2315 +#define MPD_ACK_ERROR_NO_EXIST			50
       
  2316 +#define MPD_ACK_ERROR_PLAYLIST_MAX		51
       
  2317 +#define MPD_ACK_ERROR_SYSTEM			52
       
  2318 +#define MPD_ACK_ERROR_PLAYLIST_LOAD		53
       
  2319 +#define MPD_ACK_ERROR_UPDATE_ALREADY		54
       
  2320 +#define MPD_ACK_ERROR_PLAYER_SYNC		55
       
  2321 +#define MPD_ACK_ERROR_EXIST			56
       
  2322 +
       
  2323 +#ifdef __cplusplus
       
  2324 +extern "C" {
       
  2325 +#endif
       
  2326 +
       
  2327 +typedef enum mpd_TagItems
       
  2328 +{
       
  2329 +	MPD_TAG_ITEM_ARTIST,
       
  2330 +	MPD_TAG_ITEM_ALBUM,
       
  2331 +	MPD_TAG_ITEM_TITLE,
       
  2332 +	MPD_TAG_ITEM_TRACK,
       
  2333 +	MPD_TAG_ITEM_NAME,
       
  2334 +	MPD_TAG_ITEM_GENRE,
       
  2335 +	MPD_TAG_ITEM_DATE,
       
  2336 +	MPD_TAG_ITEM_COMPOSER,
       
  2337 +	MPD_TAG_ITEM_PERFORMER,
       
  2338 +	MPD_TAG_ITEM_COMMENT,
       
  2339 +	MPD_TAG_ITEM_DISC,
       
  2340 +	MPD_TAG_ITEM_FILENAME,
       
  2341 +	MPD_TAG_ITEM_ANY,
       
  2342 +	MPD_TAG_NUM_OF_ITEM_TYPES
       
  2343 +} mpd_TagItems;
       
  2344 +
       
  2345 +extern char * mpdTagItemKeys[MPD_TAG_NUM_OF_ITEM_TYPES];
       
  2346 +
       
  2347 +/* internal stuff don't touch this struct */
       
  2348 +typedef struct _mpd_ReturnElement {
       
  2349 +	char * name;
       
  2350 +	char * value;
       
  2351 +} mpd_ReturnElement;
       
  2352 +
       
  2353 +/* mpd_Connection
       
  2354 + * holds info about connection to mpd
       
  2355 + * use error, and errorStr to detect errors
       
  2356 + */
       
  2357 +typedef struct _mpd_Connection {
       
  2358 +	/* use this to check the version of mpd */
       
  2359 +	int version[3];
       
  2360 +	/* IMPORTANT, you want to get the error messages from here */
       
  2361 +	char errorStr[MPD_ERRORSTR_MAX_LENGTH+1];
       
  2362 +	int errorCode;
       
  2363 +	int errorAt;
       
  2364 +	/* this will be set to MPD_ERROR_* if there is an error, 0 if not */
       
  2365 +	int error;
       
  2366 +	/* DON'T TOUCH any of the rest of this stuff */
       
  2367 +	int sock;
       
  2368 +	char buffer[MPD_BUFFER_MAX_LENGTH+1];
       
  2369 +	int buflen;
       
  2370 +	int bufstart;
       
  2371 +	int doneProcessing;
       
  2372 +	int listOks;
       
  2373 +	int doneListOk;
       
  2374 +	int commandList;
       
  2375 +	mpd_ReturnElement * returnElement;
       
  2376 +	struct timeval timeout;
       
  2377 +	char *request;
       
  2378 +} mpd_Connection;
       
  2379 +
       
  2380 +/* mpd_newConnection
       
  2381 + * use this to open a new connection
       
  2382 + * you should use mpd_closeConnection, when your done with the connection,
       
  2383 + * even if an error has occurred
       
  2384 + * _timeout_ is the connection timeout period in seconds
       
  2385 + */
       
  2386 +mpd_Connection * mpd_newConnection(const char * host, int port, float timeout);
       
  2387 +
       
  2388 +void mpd_setConnectionTimeout(mpd_Connection * connection, float timeout);
       
  2389 +
       
  2390 +/* mpd_closeConnection
       
  2391 + * use this to close a connection and free'ing subsequent memory
       
  2392 + */
       
  2393 +void mpd_closeConnection(mpd_Connection * connection);
       
  2394 +
       
  2395 +/* mpd_clearError
       
  2396 + * clears error
       
  2397 + */
       
  2398 +void mpd_clearError(mpd_Connection * connection);
       
  2399 +
       
  2400 +/* STATUS STUFF */
       
  2401 +
       
  2402 +/* use these with status.state to determine what state the player is in */
       
  2403 +#define MPD_STATUS_STATE_UNKNOWN	0
       
  2404 +#define MPD_STATUS_STATE_STOP		1
       
  2405 +#define MPD_STATUS_STATE_PLAY		2
       
  2406 +#define MPD_STATUS_STATE_PAUSE		3
       
  2407 +
       
  2408 +/* us this with status.volume to determine if mpd has volume support */
       
  2409 +#define MPD_STATUS_NO_VOLUME		-1
       
  2410 +
       
  2411 +/* mpd_Status
       
  2412 + * holds info return from status command
       
  2413 + */
       
  2414 +typedef struct mpd_Status {
       
  2415 +	/* 0-100, or MPD_STATUS_NO_VOLUME when there is no volume support */
       
  2416 +	int volume;
       
  2417 +	/* 1 if repeat is on, 0 otherwise */
       
  2418 +	int repeat;
       
  2419 +	/* 1 if random is on, 0 otherwise */
       
  2420 +	int random;
       
  2421 +	/* playlist length */
       
  2422 +	int playlistLength;
       
  2423 +	/* playlist, use this to determine when the playlist has changed */
       
  2424 +	long long playlist;
       
  2425 +	/* use with MPD_STATUS_STATE_* to determine state of player */
       
  2426 +	int state;
       
  2427 +	/* crossfade setting in seconds */
       
  2428 +	int crossfade;
       
  2429 +	/* if a song is currently selected (always the case when state is
       
  2430 +	 * PLAY or PAUSE), this is the position of the currently
       
  2431 +	 * playing song in the playlist, beginning with 0
       
  2432 +	 */
       
  2433 +	int song;
       
  2434 +	/* Song ID of the currently selected song */
       
  2435 +	int songid;
       
  2436 +	/* time in seconds that have elapsed in the currently playing/paused
       
  2437 +	 * song
       
  2438 +	 */
       
  2439 +	int elapsedTime;
       
  2440 +	/* length in seconds of the currently playing/paused song */
       
  2441 +	int totalTime;
       
  2442 +	/* current bit rate in kbs */
       
  2443 +	int bitRate;
       
  2444 +	/* audio sample rate */
       
  2445 +	unsigned int sampleRate;
       
  2446 +	/* audio bits */
       
  2447 +	int bits;
       
  2448 +	/* audio channels */
       
  2449 +	int channels;
       
  2450 +	/* 1 if mpd is updating, 0 otherwise */
       
  2451 +	int updatingDb;
       
  2452 +	/* error */
       
  2453 +	char * error;
       
  2454 +} mpd_Status;
       
  2455 +
       
  2456 +void mpd_sendStatusCommand(mpd_Connection * connection);
       
  2457 +
       
  2458 +/* mpd_getStatus
       
  2459 + * returns status info, be sure to free it with mpd_freeStatus()
       
  2460 + * call this after mpd_sendStatusCommand()
       
  2461 + */
       
  2462 +mpd_Status * mpd_getStatus(mpd_Connection * connection);
       
  2463 +
       
  2464 +/* mpd_freeStatus
       
  2465 + * free's status info malloc'd and returned by mpd_getStatus
       
  2466 + */
       
  2467 +void mpd_freeStatus(mpd_Status * status);
       
  2468 +
       
  2469 +typedef struct _mpd_Stats {
       
  2470 +	int numberOfArtists;
       
  2471 +	int numberOfAlbums;
       
  2472 +	int numberOfSongs;
       
  2473 +	unsigned long uptime;
       
  2474 +	unsigned long dbUpdateTime;
       
  2475 +	unsigned long playTime;
       
  2476 +	unsigned long dbPlayTime;
       
  2477 +} mpd_Stats;
       
  2478 +
       
  2479 +typedef struct _mpd_SearchStats {
       
  2480 +	int numberOfSongs;
       
  2481 +	unsigned long playTime;
       
  2482 +} mpd_SearchStats;
       
  2483 +
       
  2484 +void mpd_sendStatsCommand(mpd_Connection * connection);
       
  2485 +
       
  2486 +mpd_Stats * mpd_getStats(mpd_Connection * connection);
       
  2487 +
       
  2488 +void mpd_freeStats(mpd_Stats * stats);
       
  2489 +
       
  2490 +mpd_SearchStats * mpd_getSearchStats(mpd_Connection * connection);
       
  2491 +
       
  2492 +void mpd_freeSearchStats(mpd_SearchStats * stats);
       
  2493 +
       
  2494 +/* SONG STUFF */
       
  2495 +
       
  2496 +#define MPD_SONG_NO_TIME	-1
       
  2497 +#define MPD_SONG_NO_NUM		-1
       
  2498 +#define MPD_SONG_NO_ID		-1
       
  2499 +
       
  2500 +/* mpd_Song
       
  2501 + * for storing song info returned by mpd
       
  2502 + */
       
  2503 +typedef struct _mpd_Song {
       
  2504 +	/* filename of song */
       
  2505 +	char * file;
       
  2506 +	/* artist, maybe NULL if there is no tag */
       
  2507 +	char * artist;
       
  2508 +	/* title, maybe NULL if there is no tag */
       
  2509 +	char * title;
       
  2510 +	/* album, maybe NULL if there is no tag */
       
  2511 +	char * album;
       
  2512 +	/* track, maybe NULL if there is no tag */
       
  2513 +	char * track;
       
  2514 +	/* name, maybe NULL if there is no tag; it's the name of the current
       
  2515 +	 * song, f.e. the icyName of the stream */
       
  2516 +	char * name;
       
  2517 +	/* date */
       
  2518 +	char *date;
       
  2519 +
       
  2520 +	/* added by qball */
       
  2521 +	/* Genre */
       
  2522 +	char *genre;
       
  2523 +	/* Composer */
       
  2524 +	char *composer;
       
  2525 +	/* Performer */
       
  2526 +	char *performer;
       
  2527 +	/* Disc */
       
  2528 +	char *disc;
       
  2529 +	/* Comment */
       
  2530 +	char *comment;
       
  2531 +
       
  2532 +	/* length of song in seconds, check that it is not MPD_SONG_NO_TIME  */
       
  2533 +	int time;
       
  2534 +	/* if plchanges/playlistinfo/playlistid used, is the position of the
       
  2535 +	 * song in the playlist */
       
  2536 +	int pos;
       
  2537 +	/* song id for a song in the playlist */
       
  2538 +	int id;
       
  2539 +} mpd_Song;
       
  2540 +
       
  2541 +/* mpd_newSong
       
  2542 + * use to allocate memory for a new mpd_Song
       
  2543 + * file, artist, etc all initialized to NULL
       
  2544 + * if your going to assign values to file, artist, etc
       
  2545 + * be sure to malloc or strdup the memory
       
  2546 + * use mpd_freeSong to free the memory for the mpd_Song, it will also
       
  2547 + * free memory for file, artist, etc, so don't do it yourself
       
  2548 + */
       
  2549 +mpd_Song * mpd_newSong(void);
       
  2550 +
       
  2551 +/* mpd_freeSong
       
  2552 + * use to free memory allocated by mpd_newSong
       
  2553 + * also it will free memory pointed to by file, artist, etc, so be careful
       
  2554 + */
       
  2555 +void mpd_freeSong(mpd_Song * song);
       
  2556 +
       
  2557 +/* mpd_songDup
       
  2558 + * works like strDup, but for a mpd_Song
       
  2559 + */
       
  2560 +mpd_Song * mpd_songDup(mpd_Song * song);
       
  2561 +
       
  2562 +/* DIRECTORY STUFF */
       
  2563 +
       
  2564 +/* mpd_Directory
       
  2565 + * used to store info fro directory (right now that just the path)
       
  2566 + */
       
  2567 +typedef struct _mpd_Directory {
       
  2568 +	char * path;
       
  2569 +} mpd_Directory;
       
  2570 +
       
  2571 +/* mpd_newDirectory
       
  2572 + * allocates memory for a new directory
       
  2573 + * use mpd_freeDirectory to free this memory
       
  2574 + */
       
  2575 +mpd_Directory * mpd_newDirectory(void);
       
  2576 +
       
  2577 +/* mpd_freeDirectory
       
  2578 + * used to free memory allocated with mpd_newDirectory, and it frees
       
  2579 + * path of mpd_Directory, so be careful
       
  2580 + */
       
  2581 +void mpd_freeDirectory(mpd_Directory * directory);
       
  2582 +
       
  2583 +/* mpd_directoryDup
       
  2584 + * works like strdup, but for mpd_Directory
       
  2585 + */
       
  2586 +mpd_Directory * mpd_directoryDup(mpd_Directory * directory);
       
  2587 +
       
  2588 +/* PLAYLISTFILE STUFF */
       
  2589 +
       
  2590 +/* mpd_PlaylistFile
       
  2591 + * stores info about playlist file returned by lsinfo
       
  2592 + */
       
  2593 +typedef struct _mpd_PlaylistFile {
       
  2594 +	char * path;
       
  2595 +} mpd_PlaylistFile;
       
  2596 +
       
  2597 +/* mpd_newPlaylistFile
       
  2598 + * allocates memory for new mpd_PlaylistFile, path is set to NULL
       
  2599 + * free this memory with mpd_freePlaylistFile
       
  2600 + */
       
  2601 +mpd_PlaylistFile * mpd_newPlaylistFile(void);
       
  2602 +
       
  2603 +/* mpd_freePlaylist
       
  2604 + * free memory allocated for freePlaylistFile, will also free
       
  2605 + * path, so be careful
       
  2606 + */
       
  2607 +void mpd_freePlaylistFile(mpd_PlaylistFile * playlist);
       
  2608 +
       
  2609 +/* mpd_playlistFileDup
       
  2610 + * works like strdup, but for mpd_PlaylistFile
       
  2611 + */
       
  2612 +mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist);
       
  2613 +
       
  2614 +/* INFO ENTITY STUFF */
       
  2615 +
       
  2616 +/* the type of entity returned from one of the commands that generates info
       
  2617 + * use in conjunction with mpd_InfoEntity.type
       
  2618 + */
       
  2619 +#define MPD_INFO_ENTITY_TYPE_DIRECTORY		0
       
  2620 +#define MPD_INFO_ENTITY_TYPE_SONG		1
       
  2621 +#define MPD_INFO_ENTITY_TYPE_PLAYLISTFILE	2
       
  2622 +
       
  2623 +/* mpd_InfoEntity
       
  2624 + * stores info on stuff returned info commands
       
  2625 + */
       
  2626 +typedef struct mpd_InfoEntity {
       
  2627 +	/* the type of entity, use with MPD_INFO_ENTITY_TYPE_* to determine
       
  2628 +	 * what this entity is (song, directory, etc...)
       
  2629 +	 */
       
  2630 +	int type;
       
  2631 +	/* the actual data you want, mpd_Song, mpd_Directory, etc */
       
  2632 +	union {
       
  2633 +		mpd_Directory * directory;
       
  2634 +		mpd_Song * song;
       
  2635 +		mpd_PlaylistFile * playlistFile;
       
  2636 +	} info;
       
  2637 +} mpd_InfoEntity;
       
  2638 +
       
  2639 +mpd_InfoEntity * mpd_newInfoEntity(void);
       
  2640 +
       
  2641 +void mpd_freeInfoEntity(mpd_InfoEntity * entity);
       
  2642 +
       
  2643 +/* INFO COMMANDS AND STUFF */
       
  2644 +
       
  2645 +/* use this function to loop over after calling Info/Listall functions */
       
  2646 +mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection);
       
  2647 +
       
  2648 +/* fetches the currently seeletect song (the song referenced by status->song
       
  2649 + * and status->songid*/
       
  2650 +void mpd_sendCurrentSongCommand(mpd_Connection * connection);
       
  2651 +
       
  2652 +/* songNum of -1, means to display the whole list */
       
  2653 +void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songNum);
       
  2654 +
       
  2655 +/* songId of -1, means to display the whole list */
       
  2656 +void mpd_sendPlaylistIdCommand(mpd_Connection * connection, int songId);
       
  2657 +
       
  2658 +/* use this to get the changes in the playlist since version _playlist_ */
       
  2659 +void mpd_sendPlChangesCommand(mpd_Connection * connection, long long playlist);
       
  2660 +
       
  2661 +/**
       
  2662 + * @param connection: A valid and connected mpd_Connection.
       
  2663 + * @param playlist: The playlist version you want the diff with.
       
  2664 + * A more bandwidth efficient version of the mpd_sendPlChangesCommand.
       
  2665 + * It only returns the pos+id of the changes song.
       
  2666 + */
       
  2667 +void mpd_sendPlChangesPosIdCommand(mpd_Connection * connection, long long playlist);
       
  2668 +
       
  2669 +/* recursivel fetches all songs/dir/playlists in "dir* (no metadata is
       
  2670 + * returned) */
       
  2671 +void mpd_sendListallCommand(mpd_Connection * connection, const char * dir);
       
  2672 +
       
  2673 +/* same as sendListallCommand, but also metadata is returned */
       
  2674 +void mpd_sendListallInfoCommand(mpd_Connection * connection, const char * dir);
       
  2675 +
       
  2676 +/* non-recursive version of ListallInfo */
       
  2677 +void mpd_sendLsInfoCommand(mpd_Connection * connection, const char * dir);
       
  2678 +
       
  2679 +#define MPD_TABLE_ARTIST	MPD_TAG_ITEM_ARTIST
       
  2680 +#define MPD_TABLE_ALBUM		MPD_TAG_ITEM_ALBUM
       
  2681 +#define MPD_TABLE_TITLE		MPD_TAG_ITEM_TITLE
       
  2682 +#define MPD_TABLE_FILENAME	MPD_TAG_ITEM_FILENAME
       
  2683 +
       
  2684 +void mpd_sendSearchCommand(mpd_Connection * connection, int table,
       
  2685 +		const char * str);
       
  2686 +
       
  2687 +void mpd_sendFindCommand(mpd_Connection * connection, int table,
       
  2688 +		const char * str);
       
  2689 +
       
  2690 +/* LIST TAG COMMANDS */
       
  2691 +
       
  2692 +/* use this function fetch next artist entry, be sure to free the returned
       
  2693 + * string.  NULL means there are no more.  Best used with sendListArtists
       
  2694 + */
       
  2695 +char * mpd_getNextArtist(mpd_Connection * connection);
       
  2696 +
       
  2697 +char * mpd_getNextAlbum(mpd_Connection * connection);
       
  2698 +
       
  2699 +char * mpd_getNextTag(mpd_Connection *connection, int type);
       
  2700 +
       
  2701 +/* list artist or albums by artist, arg1 should be set to the artist if
       
  2702 + * listing albums by a artist, otherwise NULL for listing all artists or albums
       
  2703 + */
       
  2704 +void mpd_sendListCommand(mpd_Connection * connection, int table,
       
  2705 +		const char * arg1);
       
  2706 +
       
  2707 +/* SIMPLE COMMANDS */
       
  2708 +
       
  2709 +void mpd_sendAddCommand(mpd_Connection * connection, const char * file);
       
  2710 +
       
  2711 +int mpd_sendAddIdCommand(mpd_Connection *connection, const char *file);
       
  2712 +
       
  2713 +void mpd_sendDeleteCommand(mpd_Connection * connection, int songNum);
       
  2714 +
       
  2715 +void mpd_sendDeleteIdCommand(mpd_Connection * connection, int songNum);
       
  2716 +
       
  2717 +void mpd_sendSaveCommand(mpd_Connection * connection, const char * name);
       
  2718 +
       
  2719 +void mpd_sendLoadCommand(mpd_Connection * connection, const char * name);
       
  2720 +
       
  2721 +void mpd_sendRmCommand(mpd_Connection * connection, const char * name);
       
  2722 +
       
  2723 +void mpd_sendRenameCommand(mpd_Connection *connection, const char *from,
       
  2724 +                           const char *to);
       
  2725 +
       
  2726 +void mpd_sendShuffleCommand(mpd_Connection * connection);
       
  2727 +
       
  2728 +void mpd_sendClearCommand(mpd_Connection * connection);
       
  2729 +
       
  2730 +/* use this to start playing at the beginning, useful when in random mode */
       
  2731 +#define MPD_PLAY_AT_BEGINNING	-1
       
  2732 +
       
  2733 +void mpd_sendPlayCommand(mpd_Connection * connection, int songNum);
       
  2734 +
       
  2735 +void mpd_sendPlayIdCommand(mpd_Connection * connection, int songNum);
       
  2736 +
       
  2737 +void mpd_sendStopCommand(mpd_Connection * connection);
       
  2738 +
       
  2739 +void mpd_sendPauseCommand(mpd_Connection * connection, int pauseMode);
       
  2740 +
       
  2741 +void mpd_sendNextCommand(mpd_Connection * connection);
       
  2742 +
       
  2743 +void mpd_sendPrevCommand(mpd_Connection * connection);
       
  2744 +
       
  2745 +void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to);
       
  2746 +
       
  2747 +void mpd_sendMoveIdCommand(mpd_Connection * connection, int from, int to);
       
  2748 +
       
  2749 +void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2);
       
  2750 +
       
  2751 +void mpd_sendSwapIdCommand(mpd_Connection * connection, int song1, int song2);
       
  2752 +
       
  2753 +void mpd_sendSeekCommand(mpd_Connection * connection, int song, int time);
       
  2754 +
       
  2755 +void mpd_sendSeekIdCommand(mpd_Connection * connection, int song, int time);
       
  2756 +
       
  2757 +void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode);
       
  2758 +
       
  2759 +void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode);
       
  2760 +
       
  2761 +void mpd_sendSetvolCommand(mpd_Connection * connection, int volumeChange);
       
  2762 +
       
  2763 +/* WARNING: don't use volume command, its depreacted */
       
  2764 +void mpd_sendVolumeCommand(mpd_Connection * connection, int volumeChange);
       
  2765 +
       
  2766 +void mpd_sendCrossfadeCommand(mpd_Connection * connection, int seconds);
       
  2767 +
       
  2768 +void mpd_sendUpdateCommand(mpd_Connection * connection, char * path);
       
  2769 +
       
  2770 +/* returns the update job id, call this after a update command*/
       
  2771 +int mpd_getUpdateId(mpd_Connection * connection);
       
  2772 +
       
  2773 +void mpd_sendPasswordCommand(mpd_Connection * connection, const char * pass);
       
  2774 +
       
  2775 +/* after executing a command, when your done with it to get its status
       
  2776 + * (you want to check connection->error for an error)
       
  2777 + */
       
  2778 +void mpd_finishCommand(mpd_Connection * connection);
       
  2779 +
       
  2780 +/* command list stuff, use this to do things like add files very quickly */
       
  2781 +void mpd_sendCommandListBegin(mpd_Connection * connection);
       
  2782 +
       
  2783 +void mpd_sendCommandListOkBegin(mpd_Connection * connection);
       
  2784 +
       
  2785 +void mpd_sendCommandListEnd(mpd_Connection * connection);
       
  2786 +
       
  2787 +/* advance to the next listOk
       
  2788 + * returns 0 if advanced to the next list_OK,
       
  2789 + * returns -1 if it advanced to an OK or ACK */
       
  2790 +int mpd_nextListOkCommand(mpd_Connection * connection);
       
  2791 +
       
  2792 +typedef struct _mpd_OutputEntity {
       
  2793 +	int id;
       
  2794 +	char * name;
       
  2795 +	int enabled;
       
  2796 +} mpd_OutputEntity;
       
  2797 +
       
  2798 +void mpd_sendOutputsCommand(mpd_Connection * connection);
       
  2799 +
       
  2800 +mpd_OutputEntity * mpd_getNextOutput(mpd_Connection * connection);
       
  2801 +
       
  2802 +void mpd_sendEnableOutputCommand(mpd_Connection * connection, int outputId);
       
  2803 +
       
  2804 +void mpd_sendDisableOutputCommand(mpd_Connection * connection, int outputId);
       
  2805 +
       
  2806 +void mpd_freeOutputElement(mpd_OutputEntity * output);
       
  2807 +
       
  2808 +/**
       
  2809 + * @param connection a #mpd_Connection
       
  2810 + *
       
  2811 + * Queries mpd for the allowed commands
       
  2812 + */
       
  2813 +void mpd_sendCommandsCommand(mpd_Connection * connection);
       
  2814 +
       
  2815 +/**
       
  2816 + * @param connection a #mpd_Connection
       
  2817 + *
       
  2818 + * Queries mpd for the not allowed commands
       
  2819 + */
       
  2820 +void mpd_sendNotCommandsCommand(mpd_Connection * connection);
       
  2821 +
       
  2822 +/**
       
  2823 + * @param connection a #mpd_Connection
       
  2824 + *
       
  2825 + * returns the next supported command.
       
  2826 + *
       
  2827 + * @returns a string, needs to be free'ed
       
  2828 + */
       
  2829 +char *mpd_getNextCommand(mpd_Connection *connection);
       
  2830 +
       
  2831 +void mpd_sendUrlHandlersCommand(mpd_Connection * connection);
       
  2832 +
       
  2833 +char *mpd_getNextHandler(mpd_Connection * connection);
       
  2834 +
       
  2835 +void mpd_sendTagTypesCommand(mpd_Connection * connection);
       
  2836 +
       
  2837 +char *mpd_getNextTagType(mpd_Connection * connection);
       
  2838 +
       
  2839 +/**
       
  2840 + * @param connection a MpdConnection
       
  2841 + * @param path	the path to the playlist.
       
  2842 + *
       
  2843 + * List the content, with full metadata, of a stored playlist.
       
  2844 + *
       
  2845 + */
       
  2846 +void mpd_sendListPlaylistInfoCommand(mpd_Connection *connection, char *path);
       
  2847 +
       
  2848 +/**
       
  2849 + * @param connection a MpdConnection
       
  2850 + * @param path	the path to the playlist.
       
  2851 + *
       
  2852 + * List the content of a stored playlist.
       
  2853 + *
       
  2854 + */
       
  2855 +void mpd_sendListPlaylistCommand(mpd_Connection *connection, char *path);
       
  2856 +
       
  2857 +/**
       
  2858 + * @param connection a #mpd_Connection
       
  2859 + * @param exact if to match exact
       
  2860 + *
       
  2861 + * starts a search, use mpd_addConstraintSearch to add
       
  2862 + * a constraint to the search, and mpd_commitSearch to do the actual search
       
  2863 + */
       
  2864 +void mpd_startSearch(mpd_Connection *connection, int exact);
       
  2865 +
       
  2866 +/**
       
  2867 + * @param connection a #mpd_Connection
       
  2868 + * @param type
       
  2869 + * @param name
       
  2870 + */
       
  2871 +void mpd_addConstraintSearch(mpd_Connection *connection, int type, const char *name);
       
  2872 +
       
  2873 +/**
       
  2874 + * @param connection a #mpd_Connection
       
  2875 + */
       
  2876 +void mpd_commitSearch(mpd_Connection *connection);
       
  2877 +
       
  2878 +/**
       
  2879 + * @param connection a #mpd_Connection
       
  2880 + * @param type The type to search for
       
  2881 + *
       
  2882 + * starts a search for fields... f.e. get a list of artists would be:
       
  2883 + * @code
       
  2884 + * mpd_startFieldSearch(connection, MPD_TAG_ITEM_ARTIST);
       
  2885 + * mpd_commitSearch(connection);
       
  2886 + * @endcode
       
  2887 + *
       
  2888 + * or get a list of artist in genre "jazz" would be:
       
  2889 + * @code
       
  2890 + * mpd_startFieldSearch(connection, MPD_TAG_ITEM_ARTIST);
       
  2891 + * mpd_addConstraintSearch(connection, MPD_TAG_ITEM_GENRE, "jazz")
       
  2892 + * mpd_commitSearch(connection);
       
  2893 + * @endcode
       
  2894 + *
       
  2895 + * mpd_startSearch will return  a list of songs (and you need mpd_getNextInfoEntity)
       
  2896 + * this one will return a list of only one field (the one specified with type) and you need
       
  2897 + * mpd_getNextTag to get the results
       
  2898 + */
       
  2899 +void mpd_startFieldSearch(mpd_Connection *connection, int type);
       
  2900 +
       
  2901 +void mpd_startPlaylistSearch(mpd_Connection *connection, int exact);
       
  2902 +
       
  2903 +void mpd_startStatsSearch(mpd_Connection *connection);
       
  2904 +
       
  2905 +void mpd_sendPlaylistClearCommand(mpd_Connection *connection, char *path);
       
  2906 +
       
  2907 +void mpd_sendPlaylistAddCommand(mpd_Connection *connection,
       
  2908 +                                char *playlist, char *path);
       
  2909 +
       
  2910 +void mpd_sendPlaylistMoveCommand(mpd_Connection *connection,
       
  2911 +                                 char *playlist, int from, int to);
       
  2912 +
       
  2913 +void mpd_sendPlaylistDeleteCommand(mpd_Connection *connection,
       
  2914 +                                   char *playlist, int pos);
       
  2915 +#ifdef __cplusplus
       
  2916 +}
       
  2917 +#endif
       
  2918 +
       
  2919 +#endif
       
  2920 diff -r 171db9560cb5 clients/mpd/mpdinterface.cc
       
  2921 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
       
  2922 +++ b/clients/mpd/mpdinterface.cc	Mon May 19 00:21:51 2008 -0400
       
  2923 @@ -0,0 +1,404 @@
       
  2924 +#include "mpdinterface.h"
       
  2925 +#include "immsutil.h"
       
  2926 +#include <cctype>
       
  2927 +#include <fstream>
       
  2928 +#include <sstream>
       
  2929 +
       
  2930 +namespace mpd_interface
       
  2931 +{
       
  2932 +  position_err::position_err(int position, int playlist_length)
       
  2933 +  {
       
  2934 +    std::ostringstream s;
       
  2935 +    s << "Attempted to access song of invalid position in playlist. "
       
  2936 +      "Used position: " << position << ", playlist size: "
       
  2937 +      << playlist_length;
       
  2938 +    msg = s.str();
       
  2939 +  }
       
  2940 +
       
  2941 +  std::string Song::music_directory = "";
       
  2942 +
       
  2943 +  void Song::set_default_dir(std::string path)
       
  2944 +  {
       
  2945 +    music_directory = path;
       
  2946 +    if(!path.empty() && path[path.length()-1]!='/') {
       
  2947 +      music_directory.append("/");
       
  2948 +    }
       
  2949 +  }
       
  2950 +
       
  2951 +  bool operator==(const Song& first, const Song& second)
       
  2952 +  {
       
  2953 +    return (first.song_path == second.song_path) &&
       
  2954 +      (first.pl_pos == second.pl_pos) &&
       
  2955 +      (first.song_length == second.song_length);
       
  2956 +  }
       
  2957 +  bool operator!=(const Song& first, const Song& second)
       
  2958 +  {
       
  2959 +    return !(first == second);
       
  2960 +  }
       
  2961 +
       
  2962 +  // determines whether the line contains the given parameter in the
       
  2963 +  // mpd.conf fashion
       
  2964 +  bool contains_parameter(const std::string& line, const std::string& parameter)
       
  2965 +  {
       
  2966 +    if(line.empty() || line[0]=='#') return false;
       
  2967 +    
       
  2968 +    std::string::size_type pos = line.find(parameter);
       
  2969 +    if(pos == std::string::npos) return false;
       
  2970 +
       
  2971 +    while(pos>0) {
       
  2972 +      if(!isspace(line[pos-1])) return false;
       
  2973 +      --pos;
       
  2974 +    }
       
  2975 +    return true;
       
  2976 +  }
       
  2977 +
       
  2978 +  // returns the value of the given parameter from an mpd.config line
       
  2979 +  std::string get_value(const std::string& line,
       
  2980 +      const std::string& parameter)
       
  2981 +  {
       
  2982 +    if(!contains_parameter(line, parameter)) return "";
       
  2983 +
       
  2984 +    std::string::size_type start = line.find_first_of("\"");
       
  2985 +    ++start; // position of the first non-quote char
       
  2986 +    if(start == std::string::npos || start>=line.size()) return "";
       
  2987 +
       
  2988 +    std::string::size_type end = line.find_last_of("\"");
       
  2989 +    if(end == std::string::npos) return "";
       
  2990 +
       
  2991 +    std::string result = line.substr(start, end-start);
       
  2992 +    return result;
       
  2993 +  }
       
  2994 +
       
  2995 +  config read_configuration(std::string conf_path) throw(config_err)
       
  2996 +  {
       
  2997 +    static const std::string address_param = "bind_to_address";
       
  2998 +    static const std::string port_param = "port";
       
  2999 +    static const std::string dir_param = "music_directory";
       
  3000 +
       
  3001 +    if(conf_path == "")
       
  3002 +    {
       
  3003 +      std::string user_path = getenv("HOME");
       
  3004 +      if(user_path[user_path.size()-1] != '/') user_path.append("/");
       
  3005 +      user_path.append(".mpdconf");
       
  3006 +
       
  3007 +      try {
       
  3008 +	// read the "~/.mpdconf" file
       
  3009 +	return read_configuration(user_path);
       
  3010 +      }
       
  3011 +      catch (config_err) { // "~/.mpdconf" not found
       
  3012 +	  // read the "/etc/mpd.conf" instead
       
  3013 +	  return read_configuration("/etc/mpd.conf");
       
  3014 +      }
       
  3015 +    }
       
  3016 +    else
       
  3017 +    {
       
  3018 +      std::ifstream con_f(conf_path.c_str());
       
  3019 +      if(!con_f) {
       
  3020 +	throw config_err("cannot open the MPD config file \"" +
       
  3021 +	    conf_path + "\"");
       
  3022 +      }
       
  3023 +
       
  3024 +      config result;
       
  3025 +      std::string line;
       
  3026 +      while(getline(con_f, line)) {
       
  3027 +	if(contains_parameter(line, address_param)) {
       
  3028 +	  result.hostname = get_value(line, address_param);
       
  3029 +	  if(result.hostname.empty()) result.hostname = "localhost";
       
  3030 +	}
       
  3031 +	else if(contains_parameter(line, port_param)) {
       
  3032 +	  std::stringstream s;
       
  3033 +	  s << get_value(line, port_param);
       
  3034 +	  if(!(s >> result.port)) result.port = 6600;
       
  3035 +	}
       
  3036 +	else if(contains_parameter(line, dir_param)) {
       
  3037 +	  result.music_dir = get_value(line, dir_param);
       
  3038 +	}
       
  3039 +      }
       
  3040 +      con_f.close();
       
  3041 +      if(result.music_dir.empty()) {
       
  3042 +	throw config_err("infalid format of the MPD config file \"" + 
       
  3043 +	    conf_path + "\"");
       
  3044 +      }
       
  3045 +
       
  3046 +      return result;
       
  3047 +    }
       
  3048 +  }
       
  3049 +
       
  3050 +  
       
  3051 +  bool Server::connect(const std::string& hostname, int port)
       
  3052 +    throw(connection_err)
       
  3053 +  {
       
  3054 +    if(connected()) disconnect();
       
  3055 +
       
  3056 +    try {
       
  3057 +      connection = mpd_newConnection(hostname.c_str(), port, 10);
       
  3058 +      if(mpd_error()) throw connection_err(connection->errorStr);
       
  3059 +    }
       
  3060 +    catch(connection_err) {
       
  3061 +      disconnect();
       
  3062 +      throw;
       
  3063 +    }
       
  3064 +    
       
  3065 +    if(connected()) {
       
  3066 +    }
       
  3067 +    return connected();
       
  3068 +  }
       
  3069 +
       
  3070 +  void Server::disconnect()
       
  3071 +  {
       
  3072 +    if(connection!=nullptr) {
       
  3073 +      mpd_closeConnection(connection);
       
  3074 +    }
       
  3075 +    connection = nullptr;
       
  3076 +  }
       
  3077 +
       
  3078 +  bool Server::ack_error() const throw(connection_err)
       
  3079 +  {
       
  3080 +    if(!connected()) throw connection_err();
       
  3081 +    return connection->error == MPD_ERROR_ACK;
       
  3082 +  }
       
  3083 +  // determines whether the MPD is in error state which is not the ACK error
       
  3084 +  bool Server::mpd_error() const throw(connection_err)
       
  3085 +  {
       
  3086 +    if(!connected()) throw connection_err();
       
  3087 +    return connection->error!=0 && !ack_error();
       
  3088 +  }
       
  3089 +
       
  3090 +  // changes the mpd internal status to the playback_status type
       
  3091 +  playback_status resolve_state(int mpd_state)
       
  3092 +  {
       
  3093 +    switch(mpd_state)
       
  3094 +    {
       
  3095 +      case MPD_STATUS_STATE_PLAY:
       
  3096 +	return playing;
       
  3097 +      case MPD_STATUS_STATE_STOP:
       
  3098 +	return stopped;
       
  3099 +      case MPD_STATUS_STATE_PAUSE:
       
  3100 +	return paused;
       
  3101 +      default:
       
  3102 +	return playing;
       
  3103 +    }
       
  3104 +  }
       
  3105 +
       
  3106 +  void Server::refresh() throw(connection_err)
       
  3107 +  {
       
  3108 +    if(!connected()) throw connection_err();
       
  3109 +    try {
       
  3110 +      
       
  3111 +// the mess below is here because otherwise the results in nasty problems
       
  3112 +// when MPD connection times out
       
  3113 +//TODO prolly doesn't have to be everywhere, though. Weed it out
       
  3114 +if(mpd_error()) throw connection_err(connection->errorStr);
       
  3115 +      mpd_sendCommandListOkBegin(connection);
       
  3116 +if(mpd_error()) throw connection_err(connection->errorStr);
       
  3117 +      mpd_sendStatusCommand(connection);
       
  3118 +if(mpd_error()) throw connection_err(connection->errorStr);
       
  3119 +      mpd_sendCurrentSongCommand(connection);
       
  3120 +if(mpd_error()) throw connection_err(connection->errorStr);
       
  3121 +      mpd_sendCommandListEnd(connection);
       
  3122 +if(mpd_error()) throw connection_err(connection->errorStr);
       
  3123 +
       
  3124 +      mpd_Status * status = mpd_getStatus(connection);
       
  3125 +      if(status!=nullptr) {
       
  3126 +
       
  3127 +      pl_length = status->playlistLength;
       
  3128 +      pl_version = status->playlist;
       
  3129 +      pb_status = resolve_state(status->state);
       
  3130 +      current_song.set_elapsed(status->elapsedTime);
       
  3131 +      random = status->random;
       
  3132 +
       
  3133 +      mpd_freeStatus(status);
       
  3134 +      }
       
  3135 +
       
  3136 +      if(mpd_error()) throw connection_err(connection->errorStr);
       
  3137 +
       
  3138 +      mpd_nextListOkCommand(connection);
       
  3139 +      mpd_InfoEntity *entity;
       
  3140 +      while((entity = mpd_getNextInfoEntity(connection))) {
       
  3141 +	mpd_Song *song = entity->info.song;
       
  3142 +
       
  3143 +	if(entity->type!=MPD_INFO_ENTITY_TYPE_SONG) {
       
  3144 +	  mpd_freeInfoEntity(entity);
       
  3145 +	  continue;
       
  3146 +	}
       
  3147 +	
       
  3148 +	current_song.set_path(song->file);
       
  3149 +	current_song.set_pos(song->pos);
       
  3150 +	current_song.set_length(song->time);
       
  3151 +	mpd_freeInfoEntity(entity);
       
  3152 +      }
       
  3153 +      if(mpd_error()) throw connection_err(connection->errorStr);
       
  3154 +
       
  3155 +      mpd_finishCommand(connection);
       
  3156 +      if(mpd_error()) throw connection_err(connection->errorStr);
       
  3157 +    }
       
  3158 +    catch(connection_err) {
       
  3159 +      disconnect();
       
  3160 +      throw;
       
  3161 +    }
       
  3162 +  }
       
  3163 +
       
  3164 +  Song Server::get_song_info(int pl_pos)
       
  3165 +    throw(connection_err, position_err)   
       
  3166 +  {
       
  3167 +    if(!connected()) throw connection_err();
       
  3168 +    if(pl_pos < 0 || pl_pos >= get_playlist_length()) {
       
  3169 +      throw position_err(pl_pos, get_playlist_length());
       
  3170 +    }
       
  3171 +
       
  3172 +    Song result;
       
  3173 +    try {
       
  3174 +      mpd_sendPlaylistInfoCommand(connection, pl_pos);
       
  3175 +      if(mpd_error()) throw connection_err(connection->errorStr);
       
  3176 +
       
  3177 +      mpd_InfoEntity *entity;
       
  3178 +      while((entity = mpd_getNextInfoEntity(connection)))
       
  3179 +      {
       
  3180 +	mpd_Song *song = entity->info.song;
       
  3181 +
       
  3182 +	if(entity->type!=MPD_INFO_ENTITY_TYPE_SONG)
       
  3183 +	{
       
  3184 +	  mpd_freeInfoEntity(entity);
       
  3185 +	  continue;
       
  3186 +	}
       
  3187 +	
       
  3188 +	result = Song(song->file,song->pos,song->time);
       
  3189 +
       
  3190 +	mpd_freeInfoEntity(entity);
       
  3191 +      }
       
  3192 +      if(mpd_error()) throw connection_err(connection->errorStr);
       
  3193 +
       
  3194 +      mpd_finishCommand(connection);
       
  3195 +      if(mpd_error()) throw connection_err(connection->errorStr);
       
  3196 +      // shouldn't happen, but just in case
       
  3197 +      if(ack_error())  {
       
  3198 +	refresh();
       
  3199 +	throw position_err(pl_pos, get_playlist_length());
       
  3200 +      }
       
  3201 +    }
       
  3202 +    catch(connection_err)
       
  3203 +    {
       
  3204 +      disconnect();
       
  3205 +      throw;
       
  3206 +    }
       
  3207 +
       
  3208 +    return result;
       
  3209 +  }
       
  3210 +
       
  3211 +  void Server::play_song(int pl_pos) throw(connection_err, position_err)
       
  3212 +  {
       
  3213 +    if(!connected()) throw connection_err();
       
  3214 +    if(pl_pos < 0 || pl_pos >= pl_length) {
       
  3215 +      throw position_err(pl_pos, get_playlist_length());
       
  3216 +    }
       
  3217 +
       
  3218 +    try {
       
  3219 +      mpd_sendPlayCommand(connection, pl_pos);
       
  3220 +      mpd_finishCommand(connection);
       
  3221 +      if(mpd_error()) throw connection_err(connection->errorStr);
       
  3222 +      if(ack_error()) throw position_err(pl_pos, get_playlist_length());
       
  3223 +    }
       
  3224 +    catch(connection_err) {
       
  3225 +      disconnect();
       
  3226 +      throw;
       
  3227 +    }
       
  3228 +  }
       
  3229 +  // end of SERVER section
       
  3230 +
       
  3231 +
       
  3232 +  // Player class
       
  3233 +  Player::Player(): previous_status(stopped), current_status(stopped),
       
  3234 +  previous_song_pos(Song::invalid_pos), current_song_pos(Song::invalid_pos),
       
  3235 +  null_song()
       
  3236 +  {
       
  3237 +  }
       
  3238 +
       
  3239 +  bool Player::connect() throw(config_err, connection_err)
       
  3240 +  {
       
  3241 +    config conf = read_configuration();
       
  3242 +    Song::set_default_dir(conf.music_dir);
       
  3243 +    return mpd.connect(conf.hostname, conf.port);
       
  3244 +  }
       
  3245 +
       
  3246 +  void Player::disconnect()
       
  3247 +  {
       
  3248 +    mpd.disconnect();
       
  3249 +  }
       
  3250 +
       
  3251 +  void Player::refresh()
       
  3252 +  {
       
  3253 +    mpd.refresh();
       
  3254 +
       
  3255 +    playlist__changed = pl_ver != mpd.get_playlist_version();
       
  3256 +    if(playlist__changed) {
       
  3257 +      pl_ver = mpd.get_playlist_version();
       
  3258 +
       
  3259 +      // update the internal playlist
       
  3260 +      playlist.clear();
       
  3261 +      playlist.reserve(mpd.get_playlist_length());
       
  3262 +      for(int i = 0; i<mpd.get_playlist_length(); i++) {
       
  3263 +	playlist.push_back(mpd.get_song_info(i));
       
  3264 +      }
       
  3265 +    }
       
  3266 +
       
  3267 +    song__changed = mpd.get_current_song() != song(current);
       
  3268 +    if(song__changed) {
       
  3269 +      previous_song_pos = current_song_pos;
       
  3270 +      current_song_pos = mpd.get_current_song().pos();
       
  3271 +    }
       
  3272 +    else if(!playlist.empty() && current_song_pos != Song::invalid_pos) {
       
  3273 +      playlist[current_song_pos].set_elapsed(mpd.get_current_song().
       
  3274 +	  elapsed());
       
  3275 +    }
       
  3276 +
       
  3277 +    status__changed = current_status != mpd.get_playback_status();
       
  3278 +    if(status__changed) {
       
  3279 +      previous_status = current_status;
       
  3280 +      current_status = mpd.get_playback_status();
       
  3281 +    }
       
  3282 +  }
       
  3283 +
       
  3284 +  const Song& Player::song(recent value) const
       
  3285 +  {
       
  3286 +    if(playlist.empty()) return null_song;
       
  3287 +
       
  3288 +    switch(value) {
       
  3289 +      case previous:
       
  3290 +	return previous_song_pos == Song::invalid_pos ?
       
  3291 +	  null_song : song(previous_song_pos);
       
  3292 +
       
  3293 +      case current:
       
  3294 +      default:
       
  3295 +	return current_song_pos == Song::invalid_pos ?
       
  3296 +	  null_song : song(current_song_pos);
       
  3297 +    }
       
  3298 +  }
       
  3299 +  const Song& Player::song(int position) const throw(position_err)
       
  3300 +  {
       
  3301 +    try {
       
  3302 +      return playlist.at(position);
       
  3303 +    }
       
  3304 +    catch (...) {
       
  3305 +      throw position_err(position, playlist.size());
       
  3306 +    }
       
  3307 +  }
       
  3308 +
       
  3309 +  playback_status Player::status(recent value) const
       
  3310 +  {
       
  3311 +    switch (value) {
       
  3312 +      case previous: return previous_status;
       
  3313 +      case current:
       
  3314 +      default: return current_status;
       
  3315 +    }
       
  3316 +  }
       
  3317 +
       
  3318 +  void Player::play_song(int pl_pos)
       
  3319 +  {
       
  3320 +    mpd.play_song(pl_pos);
       
  3321 +  }
       
  3322 +  // end of Player class
       
  3323 +
       
  3324 +
       
  3325 +} // namespace end
       
  3326 +
       
  3327 +
       
  3328 diff -r 171db9560cb5 clients/mpd/mpdinterface.h
       
  3329 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
       
  3330 +++ b/clients/mpd/mpdinterface.h	Mon May 19 00:21:51 2008 -0400
       
  3331 @@ -0,0 +1,209 @@
       
  3332 +#ifndef MPDINTERFACE_H
       
  3333 +#define MPDINTERFACE_H
       
  3334 +
       
  3335 +#include "libmpdclient.h"
       
  3336 +#include <string>
       
  3337 +#include <vector>
       
  3338 +
       
  3339 +namespace mpd_interface
       
  3340 +{
       
  3341 +const int nullptr = 0; 
       
  3342 +
       
  3343 +/* list of thrown exceptions */
       
  3344 +
       
  3345 +  // generic exception that can be thrown by MPD
       
  3346 +  class mpd_err
       
  3347 +  {
       
  3348 +  protected:
       
  3349 +    std::string msg;
       
  3350 +  public:
       
  3351 +    mpd_err(const std::string& message = "unknown error"): msg(message) { }
       
  3352 +    std::string message() const { return msg; }
       
  3353 +  };
       
  3354 +
       
  3355 +  // thrown when for whatever reason the connection cannot be maintained
       
  3356 +  // the Player is always set to the disconnected state when this is thrown
       
  3357 +  class connection_err: public mpd_err
       
  3358 +  {
       
  3359 +  public:
       
  3360 +    connection_err(const std::string& message = "not connected"):
       
  3361 +      mpd_err(message) { }
       
  3362 +  };
       
  3363 +
       
  3364 +  class config_err: public mpd_err
       
  3365 +  {
       
  3366 +  public:
       
  3367 +    config_err(const std::string& message = "") : mpd_err(message) { }
       
  3368 +  };
       
  3369 +
       
  3370 +  // thrown when trying to access element out of the playlist range
       
  3371 +  // contains information about the length of the playlist
       
  3372 +  // and the invalid position
       
  3373 +  class position_err: public mpd_err
       
  3374 +  {
       
  3375 +    int invalid_position;
       
  3376 +    int pl_length;
       
  3377 +  public:
       
  3378 +    position_err(int position, int playlist_length);
       
  3379 +    int pos() const { return invalid_position; }
       
  3380 +    int playlist_length() const {return pl_length; }
       
  3381 +  };
       
  3382 +/* end of the exception list */
       
  3383 +
       
  3384 +
       
  3385 +  // information about the configuration of MPD
       
  3386 +  // it's the relevant info read from the mpd.config file
       
  3387 +  struct config
       
  3388 +  {
       
  3389 +    std::string hostname;
       
  3390 +    int port;
       
  3391 +    std::string music_dir;
       
  3392 +  };
       
  3393 +
       
  3394 +  // attempts to read the configuration file
       
  3395 +  // if no path is specified following actions are taken:
       
  3396 +  // first, the ~/.mpdconf file is looked for
       
  3397 +  // then, the /etc/mpd.conf file is tried.
       
  3398 +  config read_configuration(std::string conf_path="") throw(config_err);
       
  3399 +
       
  3400 +
       
  3401 +  // Represents one song in playlist.
       
  3402 +  // Note that when one song is twice in a playlist (their positions differ)
       
  3403 +  // they are two different songs from this class' point of view.
       
  3404 +  class Song
       
  3405 +  {
       
  3406 +    static std::string music_directory; // directory in which the songs are
       
  3407 +
       
  3408 +    std::string song_path; 
       
  3409 +    int pl_pos; 
       
  3410 +    int song_length; // time - in seconds
       
  3411 +    int song_elapsed; // time - in seconds
       
  3412 +
       
  3413 +  public:
       
  3414 +    static const int invalid_pos = MPD_SONG_NO_NUM;
       
  3415 +    static const int invalid_time = MPD_SONG_NO_TIME;
       
  3416 +
       
  3417 +    static void set_default_dir(std::string path); // set the music directory
       
  3418 +
       
  3419 +    Song(std::string path = "", int position = invalid_pos,
       
  3420 +	int length = invalid_time, int elapsed = invalid_time):
       
  3421 +      song_path(music_directory+path), pl_pos(position),
       
  3422 +      song_length(length), song_elapsed(elapsed) { }
       
  3423 +
       
  3424 +    friend bool operator==(const Song& first, const Song& second);
       
  3425 +    friend bool operator!=(const Song& first, const Song& second);
       
  3426 +
       
  3427 +    std::string path() const { return song_path; }
       
  3428 +    void set_path(std::string path) { song_path = music_directory+path; }
       
  3429 +    int pos() const { return pl_pos; }
       
  3430 +    void set_pos(int position) { pl_pos = position; }
       
  3431 +    int length() const { return song_length; }
       
  3432 +    void set_length(int length) { song_length = length; }
       
  3433 +    int elapsed() const {return song_elapsed; }
       
  3434 +    void set_elapsed(int elapsed) { song_elapsed = elapsed; }
       
  3435 +  };
       
  3436 +
       
  3437 +
       
  3438 +
       
  3439 +  // the status of the playback - whether it's stopped, paused
       
  3440 +  // or is playing a song right now
       
  3441 +  enum playback_status {playing, stopped, paused} ;
       
  3442 +
       
  3443 +  // represents the communication with the MPD server
       
  3444 +  // It's a confortable wrapper around the libmpdclient library
       
  3445 +  // When its refresh() method is called it gets all information from server
       
  3446 +  // and stores them so as not to flood the MPD with commands
       
  3447 +  class Server
       
  3448 +  {
       
  3449 +  public:
       
  3450 +    Server(): connection(nullptr) {};
       
  3451 +    ~Server() { disconnect(); }
       
  3452 +
       
  3453 +    bool connect(const std::string& hostname="localhost", int port = 6600)
       
  3454 +      throw(connection_err);
       
  3455 +    void disconnect();
       
  3456 +    bool connected() const {return connection!=nullptr; }
       
  3457 +
       
  3458 +
       
  3459 +    void refresh() throw(connection_err); // the Server asks MPD for new values
       
  3460 +    int get_playlist_length() const { return pl_length; }
       
  3461 +    long long get_playlist_version() { return pl_version; }
       
  3462 +    playback_status get_playback_status() { return pb_status; }
       
  3463 +    bool get_random() const {return random; }
       
  3464 +
       
  3465 +    Song get_song_info(int pl_pos) throw(connection_err,position_err);
       
  3466 +    Song get_current_song() const { return current_song; }
       
  3467 +
       
  3468 +    // plays the song at the specified position
       
  3469 +    void play_song(int pl_pos) throw(connection_err, position_err);
       
  3470 +
       
  3471 +  private:
       
  3472 +    Server& operator=(const Server&);
       
  3473 +    Server(const Server&);
       
  3474 +
       
  3475 +    mpd_Connection *connection;
       
  3476 +    int pl_length;
       
  3477 +    long long pl_version;
       
  3478 +    playback_status pb_status;
       
  3479 +    Song current_song;
       
  3480 +    bool random;
       
  3481 +
       
  3482 +
       
  3483 +    // determines whether the actual MPD error state is the ACK error
       
  3484 +    bool ack_error() const throw(connection_err);
       
  3485 +
       
  3486 +    // determines whether the MPD is in error state which is not the ACK error
       
  3487 +    bool mpd_error() const throw(connection_err);
       
  3488 +  };
       
  3489 +
       
  3490 +
       
  3491 +
       
  3492 +  enum recent {previous, current};
       
  3493 +
       
  3494 +  class Player
       
  3495 +  {
       
  3496 +  public:
       
  3497 +
       
  3498 +    Player();
       
  3499 +
       
  3500 +    bool connect() throw(config_err, connection_err);
       
  3501 +    void disconnect();
       
  3502 +    bool connected() const { return mpd.connected(); }
       
  3503 +
       
  3504 +    void refresh();
       
  3505 +    
       
  3506 +    bool song_changed() {return song__changed; }
       
  3507 +    bool playlist_changed() { return playlist__changed; }
       
  3508 +    bool status_changed() { return status__changed; }
       
  3509 +
       
  3510 +    // gets the current or the previously played song
       
  3511 +    // song(current) returns currently played song
       
  3512 +    // song(previous) returns the previously played song
       
  3513 +    const Song& song(recent) const;
       
  3514 +    const Song& song(int position) const throw(position_err);
       
  3515 +    int playlist_length() const { return mpd.get_playlist_length(); }
       
  3516 +    bool radnom() const { return mpd.get_random(); }
       
  3517 +    void play_song(int pl_pos);
       
  3518 +
       
  3519 +    // gets the current or the previous playback status
       
  3520 +    playback_status status(recent) const;
       
  3521 +
       
  3522 +
       
  3523 +  private:
       
  3524 +    Player& operator=(const Player&);
       
  3525 +    Player(const Player&);
       
  3526 +
       
  3527 +    Server mpd;
       
  3528 +    long long pl_ver;
       
  3529 +    playback_status previous_status, current_status;
       
  3530 +    int previous_song_pos, current_song_pos;
       
  3531 +    const Song null_song;
       
  3532 +    std::vector<Song> playlist;
       
  3533 +
       
  3534 +    bool song__changed, playlist__changed, status__changed;
       
  3535 +  };
       
  3536 +
       
  3537 +}
       
  3538 +
       
  3539 +#endif
       
  3540 +
       
  3541 diff -r 171db9560cb5 clients/mpd/rules.mk
       
  3542 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
       
  3543 +++ b/clients/mpd/rules.mk	Mon May 19 00:21:51 2008 -0400
       
  3544 @@ -0,0 +1,16 @@
       
  3545 +MDPCPPFLAGS= $(GLIB2CPPFLAGS)
       
  3546 +MPDLDFLAGS= $(GLIB2LDFLAGS)
       
  3547 +MPDCOMMON= mpdinterface.o libmpdclient.o clientstubbase.o libimmscore.a libmodel.a
       
  3548 +
       
  3549 +
       
  3550 +immsmpd: immsmpd.o $(MPDCOMMON)
       
  3551 +immsmpd: $(call objects,../clients/mpd)
       
  3552 +immsmpd-CPPFLAGS=$(MDPCPPFLAGS)
       
  3553 +immsmpd-LIBS=$(MPDLDFLAGS)
       
  3554 +
       
  3555 +
       
  3556 +MPDDESTDIR=/usr/bin
       
  3557 +
       
  3558 +immsmpd_install: immsmpd
       
  3559 +	${INSTALL} -D $^ $(MPDDESTDIR)
       
  3560 +
       
  3561 diff -r 171db9560cb5 configure.ac
       
  3562 --- a/configure.ac	Mon May 19 00:16:56 2008 -0400
       
  3563 +++ b/configure.ac	Mon May 19 00:21:51 2008 -0400
       
  3564 @@ -260,6 +260,13 @@
       
  3565  saved_libs="$LIBS"
       
  3566  
       
  3567  PLUGINS=""
       
  3568 +
       
  3569 +AC_CHECK_PROG(with_mdp, mpd --help, "yes", "no")
       
  3570 +if test "$with_mpd" != "no"; then
       
  3571 +    AC_APPEND(PLUGINS, immsmpd)
       
  3572 +fi
       
  3573 +
       
  3574 +
       
  3575  AC_CHECK_PROG(with_xmms, xmms-config, "yes", "no")
       
  3576  if test "$with_xmms" != "no"; then
       
  3577      CPPFLAGS=`xmms-config --cflags`
       
  3578 diff -r 171db9560cb5 vars.mk.in
       
  3579 --- a/vars.mk.in	Mon May 19 00:16:56 2008 -0400
       
  3580 +++ b/vars.mk.in	Mon May 19 00:21:51 2008 -0400
       
  3581 @@ -10,7 +10,7 @@
       
  3582  bindir = @bindir@
       
  3583  datadir = @datadir@
       
  3584  
       
  3585 -VPATH = ../immscore:../analyzer:../model:../autotag:../immsremote:../utils:../clients:../immsd:../data:../clients/xmms:../clients/bmp:../clients/audacious
       
  3586 +VPATH = ../immscore:../analyzer:../model:../autotag:../immsremote:../utils:../clients:../immsd:../data:../clients/xmms:../clients/bmp:../clients/audacious:../clients/mpd
       
  3587  ARFLAGS = rs
       
  3588  
       
  3589  SHELL = bash