1 /* simple-libmpd.c
2  *
3  * Copyright (c) 2006-2012 Landry Breuil <landry at xfce.org>
4  * This code is licenced under a BSD-style licence.
5  * (OpenBSD variant modeled after the ISC licence)
6  * All rights reserved.
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 /* double inclusion ?*/
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 
25 #include "simple-libmpd.h"
26 
27 /* for DBG(..) macros */
28 #include <libxfce4util/libxfce4util.h>
29 
30 #include <string.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <netinet/in.h>
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <netdb.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 
40 int mpd_wait_for_answer(MpdObj *);
41 int mpd_send_single_cmd(MpdObj*, char*);
42 void send_complex_cmd(MpdObj*, char*, void (*)(), void *);
43 void parse_status_answer(MpdObj*, void*);
44 void parse_one_song(MpdObj*, void*);
45 void parse_playlistinfo_answer(MpdObj*, void*);
46 void parse_outputs_answer(MpdObj*, void*);
47 
mpd_new(char * host,int port,char * pass)48 MpdObj* mpd_new(char* host, int port, char* pass)
49 {
50    MpdObj* mo = g_new0(MpdObj,1);
51 
52    DBG("host=%s, port=%d, pass=%s", host, port, pass);
53 
54    mo->host = g_strdup(host);
55    mo->port = port;
56    mo->pass = g_strdup(pass);
57    mo->socket = 0;
58    mo->status = 0;
59    mo->repeat = 0;
60    mo->random = 0;
61    mo->curvol = 0;
62    mo->song = 0;
63    mo->songid = 0;
64    mo->playlistlength = 0;
65    mo->cursong = NULL;
66    mo->error = 0;
67    mo->buffer[0] = '\0';
68    mo->buflen = 0;
69 
70    return mo;
71 }
72 
mpd_free(MpdObj * mo)73 void mpd_free(MpdObj* mo)
74 {
75    DBG("!");
76 
77    if (mo->socket)
78       close(mo->socket);
79    g_free(mo->host);
80    g_free(mo->pass);
81    g_free(mo);
82 }
83 
mpd_connect(MpdObj * mo)84 void mpd_connect(MpdObj* mo)
85 {
86    struct hostent* remote_he;
87    struct sockaddr* remote_sa;
88    struct sockaddr_in remote_si;
89    int err,nbread;
90    struct timeval tv;
91    fd_set fds;
92 
93    mo->buffer[0] = '\0';
94    mo->buflen = 0;
95 
96    /* ??? */
97    if (mo->socket) close(mo->socket);
98 
99    DBG("!");
100 
101    if (!(remote_he = (struct hostent*) gethostbyname(mo->host)))
102    {
103       mo->error = MPD_ERROR_UNKHOST;
104       DBG("ERROR @gethostbyname(), err=%s",strerror(errno));
105       return;
106    }
107    memset(&remote_si, 0, sizeof(struct sockaddr_in));
108    remote_si.sin_family = AF_INET;
109    remote_si.sin_port = htons(mo->port);
110    memcpy((char *)&remote_si.sin_addr.s_addr,( char *)remote_he->h_addr, remote_he->h_length);
111 
112    remote_sa = (struct sockaddr *)&remote_si;
113 
114    if ((mo->socket = socket(AF_INET,SOCK_STREAM,0)) < 0)
115    {
116       mo->error = MPD_ERROR_NOSOCK;
117       DBG("ERROR @socket(), err=%s",strerror(errno));
118       return;
119    }
120 
121    if (connect(mo->socket,remote_sa, sizeof(struct sockaddr_in)) < 0)
122    {
123       mo->error = MPD_ERROR_CONNPORT;
124       DBG("ERROR @connect(), err=%s",strerror(errno));
125       return;
126    }
127 
128    tv.tv_sec = 1;
129    tv.tv_usec = 0;
130    FD_ZERO(&fds);
131    FD_SET(mo->socket,&fds);
132    if((err = select(mo->socket+1,&fds,NULL,NULL,&tv)) == 1)
133    {
134       if ((nbread = recv(mo->socket, mo->buffer, MAXBUFLEN, 0)) < 0)
135       {
136          mo->error = MPD_ERROR_NORESPONSE;
137          DBG("ERROR @recv(), err=%s",strerror(errno));
138          return;
139       }
140       if (nbread == 0)
141       {
142           mo->error = MPD_ERROR_CONNCLOSED;
143           DBG("ERROR @recv(), connection closed by server");
144           return;
145       }
146       mo->buflen = nbread;
147       mo->buffer[mo->buflen] = '\0';
148    }
149    else if(err < 0)
150    {
151       mo->error = MPD_ERROR_CONNPORT;
152       DBG("ERROR @select(), err=%s",strerror(errno));
153       return;
154    }
155    else
156    {
157       mo->error = MPD_ERROR_NORESPONSE;
158       DBG("ERROR @select(), timeoute'ed -> err=%s",strerror(errno));
159       return;
160    }
161 
162    if (strncmp(mo->buffer,MPD_WELCOME_MESSAGE, strlen(MPD_WELCOME_MESSAGE)))
163    {
164       mo->error = MPD_ERROR_NOTMPD;
165       DBG("ERROR @strncmp() -> answer didn't come from mpd");
166       return;
167    }
168 
169    DBG("Received %d bytes = welcome message :\"%s\"", mo->buflen, mo->buffer);
170 
171    *mo->buffer = '\0';
172    mo->buflen = 0;
173    mo->error = 0;
174 }
175 
mpd_disconnect(MpdObj * mo)176 void mpd_disconnect(MpdObj* mo)
177 {
178    DBG("!");
179    if (mo->socket)
180       close(mo->socket);
181 }
182 
mpd_status_get_volume(MpdObj * mo)183 int mpd_status_get_volume(MpdObj* mo)
184 {
185    DBG("! return %d",mo->curvol);
186    return mo->curvol;
187 }
188 
mpd_wait_for_answer(MpdObj * mo)189 int mpd_wait_for_answer(MpdObj *mo)
190 {
191    struct timeval tv;
192    int err,nbread;
193    fd_set fds;
194    err = nbread = 0;
195 
196    DBG("!");
197 
198    tv.tv_sec = 1;
199    tv.tv_usec = 0;
200    FD_ZERO(&fds);
201    FD_SET(mo->socket,&fds);
202    if((err = select(mo->socket+1,&fds,NULL,NULL,&tv)) == 1)
203    {
204       if ((nbread = recv(mo->socket, mo->buffer, MAXBUFLEN, 0)) < 0)
205       {
206          mo->error = MPD_ERROR_NORESPONSE;
207          DBG("ERROR @recv(), err=%s",strerror(errno));
208          return -1;
209       }
210       if (nbread == 0)
211       {
212          mo->error = MPD_ERROR_CONNCLOSED;
213          DBG("ERROR @recv(), connection closed by server");
214          return -1;
215       }
216 
217       mo->buflen = nbread;
218       mo->buffer[mo->buflen] = '\0';
219       DBG("Read %d bytes, buff=\"%s\"", nbread, mo->buffer);
220    }
221    else if(err < 0)
222    {
223       mo->error = MPD_ERROR_CONNPORT;
224       DBG("ERROR @select(), err=%s",strerror(errno));
225       return -1;
226    }
227    else
228    {
229       mo->error = MPD_ERROR_NORESPONSE;
230       DBG("ERROR @select(), timeoute'ed -> err=%s",strerror(errno));
231       return -1;
232    }
233    if (!strncmp(mo->buffer,"ACK",3))
234       mo->error = MPD_NOTOK;
235    else
236       mo->error = MPD_OK;
237    return nbread;
238 }
239 
mpd_send_single_cmd(MpdObj * mo,char * cmd)240 int mpd_send_single_cmd(MpdObj*mo, char* cmd)
241 {
242    int nbwri = 0;
243 
244    if (mo->socket)
245    {
246       DBG("Sending \"%s\"",cmd);
247       if ((nbwri = send(mo->socket, cmd, strlen(cmd), 0)) <= 0)
248       {
249          mo->error = MPD_ERROR_SENDING;
250          DBG("ERROR @send(), err=%s",strerror(errno));
251       }
252       DBG("Sent %d bytes",nbwri);
253       /* wait for OK */
254       mpd_wait_for_answer(mo);
255 
256       if(!mo->error)
257       {
258          if (strcmp(mo->buffer,"OK\n") != 0)
259          {
260             mo->error = MPD_FAILED;
261             DBG("ERROR : did not received OK");
262          }
263       }
264       *mo->buffer = '\0';
265       mo->buflen = 0;
266    }
267    else
268    {
269       mo->error = MPD_ERROR_NOSOCK;
270       DBG("ERROR : socket == NULL ?");
271    }
272    return ((!mo->error) ? MPD_OK : MPD_FAILED);
273 }
274 
send_complex_cmd(MpdObj * mo,char * cmd,void (* parse_answer_fct)(),void * res)275 void send_complex_cmd(MpdObj* mo, char* cmd, void (*parse_answer_fct)(), void *res)
276 {
277    int nbwri, nbread, tmp_bufsize;
278    char *ptr;
279    char *tmp_buffer, *tmp;
280    /* write 'cmd' to socket */
281    if (mo->socket)
282    {
283       DBG("Sending \"%s\"",cmd);
284       if ((nbwri = send(mo->socket, cmd, strlen(cmd), 0)) < 0)
285       {
286          mo->error = MPD_ERROR_SENDING;
287          DBG("ERROR @send(), err=%s",strerror(errno));
288          return;
289       }
290       DBG("Sent %d bytes",nbwri);
291 
292       nbread = mpd_wait_for_answer(mo);
293       /* special case for long answers with 'playlistinfo' - hack to loop until we have received the final OK\n*/
294       while (!mo->error && ( nbread == MAXBUFLEN || 0 != strcmp(mo->buffer + strlen(mo->buffer) - 3,"OK\n")))
295       {
296          /* save the end of the buffer from last occurence of 'file:', and replace it with 'OK\n' */
297          ptr = g_strrstr(mo->buffer, "file:");
298          if (!ptr)
299          {
300             mo->error = MPD_ERROR_CONNCLOSED;
301             DBG("ERROR parsing reply");
302             *mo->buffer = '\0';
303             mo->buflen = 0;
304             return;
305          }
306 
307          tmp_buffer = (char*) calloc (MAXBUFLEN*2, sizeof(char));
308          strcpy(tmp_buffer, ptr);
309          tmp_bufsize = strlen(tmp_buffer);
310          strcpy(ptr, "OK\n");
311          DBG("tmp_buffer contains \"%s\"", tmp_buffer);
312          DBG("buffer now contains \"%s\"", mo->buffer);
313          /* parse buffer */
314          (*parse_answer_fct)(mo, res);
315 
316          /* re-read stuff */
317          nbread = mpd_wait_for_answer(mo);
318          /* append the saved end to the beginning of buffer */
319          /* ugly memory management. really. */
320          tmp = (char*) calloc (MAXBUFLEN*2, sizeof(char));
321          strcpy(tmp, mo->buffer);
322          strcpy(mo->buffer, tmp_buffer);
323          strcpy(mo->buffer + tmp_bufsize, tmp);
324          mo->buffer[tmp_bufsize + nbread] = '\0';
325          DBG("added tmp_buffer at the beginning of buffer, now contains \"%s\"", mo->buffer);
326          free(tmp_buffer);
327          free(tmp);
328       }
329       /* finally parse the end of the answer => 'currentsong' and 'status' parsing should jump directly here*/
330       if (!mo->error)
331          (*parse_answer_fct)(mo, res);
332 
333       *mo->buffer = '\0';
334       mo->buflen = 0;
335    }
336    else
337    {
338       mo->error = MPD_ERROR_NOSOCK;
339       DBG("ERROR : socket == NULL ?");
340    }
341 }
342 
parse_status_answer(MpdObj * mo,void * unused_param)343 void parse_status_answer(MpdObj *mo, void* unused_param)
344 {
345    gchar **lines, **tokens;
346    int i;
347    mo->songid = -1;
348    lines = g_strsplit(mo->buffer, "\n", 0);
349    for (i = 0 ; lines[i] && strncmp(lines[i], "OK", 2) ; i++)
350    {
351       tokens = g_strsplit(lines[i], ":", 2);
352       /* remove leading whitespace */
353       tokens[1] = g_strchug(tokens[1]);
354       DBG("key=\"%s\",value=\"%s\"", tokens[0], tokens[1]);
355       if      (0 == strcmp("volume",tokens[0])) mo->curvol = atoi(tokens[1]);
356       else if (0 == strcmp("repeat",tokens[0])) mo->repeat = atoi(tokens[1]);
357       else if (0 == strcmp("random",tokens[0])) mo->random = atoi(tokens[1]);
358       else if (0 == strcmp("playlistlength",tokens[0])) mo->playlistlength = atoi(tokens[1]);
359       else if (0 == strcmp("state", tokens[0]))
360       {
361          if      (0 == strcmp("play", tokens[1])) mo->status = MPD_PLAYER_PLAY;
362          else if (0 == strcmp("pause",tokens[1])) mo->status = MPD_PLAYER_PAUSE;
363          else if (0 == strcmp("stop", tokens[1])) mo->status = MPD_PLAYER_STOP;
364       }
365       else if (0 == strcmp("song",tokens[0])) mo->song = atoi(tokens[1]);
366       else if (0 == strcmp("songid",tokens[0])) mo->songid = atoi(tokens[1]);
367       g_strfreev(tokens);
368    }
369    g_strfreev(lines);
370 }
371 
mpd_status_update(MpdObj * mo)372 int mpd_status_update(MpdObj* mo)
373 {
374    mo->status = 0;
375    DBG("!");
376    send_complex_cmd(mo, "status\n", parse_status_answer, NULL);
377    return ((!mo->error) ? MPD_OK : MPD_FAILED);
378 }
379 
380 
parse_one_song(MpdObj * mo,void * param)381 void parse_one_song(MpdObj *mo, void* param)
382 {
383    mpd_Song* ms = (mpd_Song*) param;
384    gchar **lines, **tokens;
385    int i;
386    ms->file = ms->artist = ms->album = ms->title = ms->track = NULL;
387    ms->id = ms->pos = -1;
388 
389    lines = g_strsplit(mo->buffer, "\n", 0);
390    for (i = 0 ; lines[i] && strcmp(lines[i], "OK") ; i++)
391    {
392       tokens = g_strsplit(lines[i], ":", 2);
393       /* remove leading whitespace */
394       tokens[1] = g_strchug(tokens[1]);
395       DBG("key=\"%s\",value=\"%s\"", tokens[0], tokens[1]);
396       if      (!ms->file   && 0 == strcmp("file",  tokens[0])) ms->file  = g_strdup(tokens[1]);
397       else if (!ms->artist && 0 == strcmp("Artist",tokens[0])) ms->artist= g_strdup(tokens[1]);
398       else if (!ms->album  && 0 == strcmp("Album", tokens[0])) ms->album = g_strdup(tokens[1]);
399       else if (!ms->title  && 0 == strcmp("Title", tokens[0])) ms->title = g_strdup(tokens[1]);
400       else if (!ms->track  && 0 == strcmp("Track", tokens[0])) ms->track = g_strdup(tokens[1]);
401       else if (ms->pos < 0 && 0 == strcmp("Pos",   tokens[0])) ms->pos   = atoi(tokens[1]);
402       else if (ms->id < 0  && 0 == strcmp("Id",    tokens[0])) ms->id    = atoi(tokens[1]);
403       g_strfreev(tokens);
404    }
405    if (ms->id < 0)
406       mo->error = MPD_FAILED;
407    g_strfreev(lines);
408 }
409 
parse_playlistinfo_answer(MpdObj * mo,void * param)410 void parse_playlistinfo_answer(MpdObj *mo, void *param)
411 {
412    MpdData* md = (MpdData*) param;
413    mpd_Song* ms;
414    gchar **lines, **tokens;
415    int i = 0;
416 
417    lines = g_strsplit(mo->buffer, "\n", 0);
418    while(lines[i] && strcmp(lines[i],"OK"))
419    {
420       ms = &md->allsongs[md->nb];
421       ms->file = ms->artist = ms->album = ms->title = ms->track = NULL;
422       ms->id = ms->pos = -1;
423       DBG("Going to parse song #%d", md->nb);
424 
425       while(lines[i] && strcmp(lines[i],"OK") && ms->id < 0)
426       {
427          tokens = g_strsplit(lines[i], ":", 2);
428          /* remove leading whitespace */
429          tokens[1] = g_strchug(tokens[1]);
430          DBG("key=\"%s\",value=\"%s\"", tokens[0], tokens[1]);
431          if      (!ms->file   && 0 == strcmp("file",  tokens[0])) ms->file  = g_strdup(tokens[1]);
432          else if (!ms->artist && 0 == strcmp("Artist",tokens[0])) ms->artist= g_strdup(tokens[1]);
433          else if (!ms->album  && 0 == strcmp("Album", tokens[0])) ms->album = g_strdup(tokens[1]);
434          else if (!ms->title  && 0 == strcmp("Title", tokens[0])) ms->title = g_strdup(tokens[1]);
435          else if (!ms->track  && 0 == strcmp("Track", tokens[0])) ms->track = g_strdup(tokens[1]);
436          else if (ms->pos < 0 && 0 == strcmp("Pos",   tokens[0])) ms->pos   = atoi(tokens[1]);
437          else if (ms->id < 0  && 0 == strcmp("Id",    tokens[0])) ms->id    = atoi(tokens[1]);
438          i++;
439          g_strfreev(tokens);
440       }
441       md->nb++;
442    }
443    g_strfreev(lines);
444    DBG("Got 'OK', md->nb = %d", md->nb);
445 }
446 
parse_outputs_answer(MpdObj * mo,void * param)447 void parse_outputs_answer(MpdObj *mo, void *param)
448 {
449    MpdData* md = (MpdData*) param;
450    gchar **lines, **tokens;
451    int i = 0;
452    lines = g_strsplit(mo->buffer, "\n", 0);
453    while(lines[i] && strcmp(lines[i],"OK"))
454    {
455       md->alloutputs[md->nb] = g_new(mpd_Output, 1);
456       md->alloutputs[md->nb]->enabled = -1;
457       DBG("Going to parse output #%d", md->nb);
458       while(lines[i] && strcmp(lines[i],"OK") && md->alloutputs[md->nb]->enabled < 0)
459       {
460          tokens = g_strsplit(lines[i], ":", 2);
461          /* remove leading whitespace */
462          tokens[1] = g_strchug(tokens[1]);
463          DBG("key=\"%s\",value=\"%s\"", tokens[0], tokens[1]);
464          if      (0 == strcmp("outputid",tokens[0])) md->alloutputs[md->nb]->id = atoi(tokens[1]);
465          else if (0 == strcmp("outputname",tokens[0])) md->alloutputs[md->nb]->name = g_strdup(tokens[1]);
466          else if (0 == strcmp("outputenabled",tokens[0])) md->alloutputs[md->nb]->enabled = atoi(tokens[1]);
467          i++;
468          g_strfreev(tokens);
469       }
470       md->nb++;
471       /* ignore extra lines with 'attribute:' */
472       while (lines[i] != NULL && strcmp(lines[i],"OK") && 0 == strncmp(lines[i], "attribute:", 10))
473          i++;
474       if (strcmp(lines[i],"OK")) {
475          /* make room for the next output ptr */
476          md->alloutputs = g_renew(mpd_Output*, md->alloutputs, md->nb + 1);
477       }
478    }
479    g_strfreev(lines);
480 }
481 
mpd_playlist_get_current_song(MpdObj * mo)482 mpd_Song* mpd_playlist_get_current_song(MpdObj* mo)
483 {
484    DBG("!");
485    if (mo->cursong != NULL && mo->cursong->id != mo->songid)
486    {
487       if (mo->cursong->file)   free(mo->cursong->file);
488       if (mo->cursong->artist) free(mo->cursong->artist);
489       if (mo->cursong->album)  free(mo->cursong->album);
490       if (mo->cursong->title)  free(mo->cursong->title);
491       if (mo->cursong->track)  free(mo->cursong->track);
492       free(mo->cursong);
493       mo->cursong = NULL;
494    }
495    if (mo->cursong == NULL)
496    {
497       mo->cursong = g_new0(mpd_Song,1);
498       DBG("updating currentsong");
499       send_complex_cmd(mo, "currentsong\n", parse_one_song, (void*) mo->cursong);
500    }
501    return ((!mo->error) ? mo->cursong : NULL);
502 }
503 
mpd_playlist_get_playlist_length(MpdObj * mo)504 int mpd_playlist_get_playlist_length(MpdObj* mo)
505 {
506    return mo->playlistlength;
507 }
508 
mpd_player_get_current_song_pos(MpdObj * mo)509 int mpd_player_get_current_song_pos(MpdObj* mo)
510 {
511    DBG("!");
512    return ((mpd_status_update(mo) != MPD_OK) ? -1 : mo->song);
513 }
514 
mpd_playlist_get_changes(MpdObj * mo,int old_playlist_id)515 MpdData* mpd_playlist_get_changes(MpdObj* mo, int old_playlist_id)
516 {
517    MpdData* md = g_new0(MpdData,1);
518    md->cur = md->nb = 0;
519    md->type = MPD_DATA_TYPE_SONG;
520    md->allsongs = g_new(mpd_Song, mo->playlistlength);
521    DBG("!");
522    send_complex_cmd(mo, "playlistinfo\n", parse_playlistinfo_answer, (void*) md);
523    md->song = &(md->allsongs[0]);
524    return ((!mo->error) ? md : NULL);
525 }
526 
mpd_data_get_next(MpdData * md)527 MpdData* mpd_data_get_next(MpdData* md)
528 {
529    md->cur++;
530    /* free(md) and return NULL if after last */
531    if (md->cur == md->nb)
532    {
533       for (md->cur--; md->cur; md->cur--)
534       {
535          switch (md->type) {
536             case MPD_DATA_TYPE_OUTPUT_DEV:
537             if (md->alloutputs[md->cur]->name) free(md->alloutputs[md->cur]->name);
538             break;
539             case MPD_DATA_TYPE_SONG:
540             if (md->allsongs[md->cur].file) free(md->allsongs[md->cur].file);
541             if (md->allsongs[md->cur].artist) free(md->allsongs[md->cur].artist);
542             if (md->allsongs[md->cur].album) free(md->allsongs[md->cur].album);
543             if (md->allsongs[md->cur].title) free(md->allsongs[md->cur].title);
544             if (md->allsongs[md->cur].track) free(md->allsongs[md->cur].track);
545             break;
546          }
547       }
548       switch (md->type) {
549          case MPD_DATA_TYPE_OUTPUT_DEV:
550             g_free(md->alloutputs);
551             break;
552          case MPD_DATA_TYPE_SONG:
553             g_free(md->allsongs);
554             break;
555       }
556       g_free(md);
557       DBG("Free()'d md");
558       return NULL;
559    }
560    switch (md->type) {
561       case MPD_DATA_TYPE_OUTPUT_DEV:
562          md->output_dev = md->alloutputs[md->cur];
563          break;
564       case MPD_DATA_TYPE_SONG:
565          md->song = (&md->allsongs[md->cur]);
566          break;
567    }
568    return md;
569 }
570 
mpd_server_get_output_devices(MpdObj * mo)571 MpdData* mpd_server_get_output_devices(MpdObj* mo)
572 {
573    MpdData* md = g_new0(MpdData,1);
574    DBG("!");
575    md->cur = md->nb = 0;
576    md->alloutputs = g_new(mpd_Output*,1);
577    md->type = MPD_DATA_TYPE_OUTPUT_DEV;
578    send_complex_cmd(mo, "outputs\n", parse_outputs_answer, (void*) md);
579    md->output_dev = md->alloutputs[0];
580    return ((!mo->error) ? md : NULL);
581 }
582 
mpd_server_set_output_device(MpdObj * mo,int id,int state)583 int mpd_server_set_output_device (MpdObj* mo, int id, int state)
584 {
585    char outbuf[18];
586    /* write (enable|disable)output 'id' to socket */
587    DBG("!");
588    snprintf(outbuf, sizeof(outbuf), "%soutput %d\n",(state ? "enable" : "disable"), id);
589    return mpd_send_single_cmd(mo,outbuf);
590 }
591 
mpd_status_set_volume(MpdObj * mo,int newvol)592 void mpd_status_set_volume(MpdObj* mo, int newvol)
593 {
594    char outbuf[15];
595    /* write setvol 'newvol' to socket */
596    DBG("!");
597    snprintf(outbuf, sizeof(outbuf), "setvol %d\n",newvol);
598    mpd_send_single_cmd(mo,outbuf);
599 }
600 
mpd_player_get_state(MpdObj * mo)601 int mpd_player_get_state(MpdObj* mo)
602 {
603    DBG("! return %d",mo->status);
604    return mo->status;
605 }
606 
mpd_player_get_random(MpdObj * mo)607 int mpd_player_get_random(MpdObj* mo)
608 {
609    DBG("! return %d",mo->random);
610    return mo->random;
611 }
612 
mpd_player_set_random(MpdObj * mo,int random)613 int mpd_player_set_random(MpdObj* mo, int random)
614 {
615    char outbuf[15];
616    DBG("!");
617    snprintf(outbuf, sizeof(outbuf), "random %d\n",random);
618    return mpd_send_single_cmd(mo,outbuf);
619 
620 }
621 
mpd_player_set_repeat(MpdObj * mo,int repeat)622 int mpd_player_set_repeat(MpdObj* mo, int repeat)
623 {
624    char outbuf[15];
625    DBG("!");
626    snprintf(outbuf, sizeof(outbuf), "repeat %d\n",repeat);
627    return mpd_send_single_cmd(mo,outbuf);
628 }
629 
mpd_player_get_repeat(MpdObj * mo)630 int mpd_player_get_repeat(MpdObj* mo)
631 {
632    DBG("! return %d",mo->repeat);
633    return mo->repeat;
634 }
635 
mpd_player_prev(MpdObj * mo)636 int mpd_player_prev(MpdObj* mo)
637 {
638    DBG("!");
639    return mpd_send_single_cmd(mo,"previous\n");
640 }
641 
mpd_player_next(MpdObj * mo)642 int mpd_player_next(MpdObj* mo)
643 {
644    DBG("!");
645    return mpd_send_single_cmd(mo,"next\n");
646 }
647 
mpd_player_stop(MpdObj * mo)648 int mpd_player_stop(MpdObj* mo)
649 {
650    DBG("!");
651    return mpd_send_single_cmd(mo,"stop\n");
652 }
653 
mpd_player_pause(MpdObj * mo)654 int mpd_player_pause(MpdObj* mo)
655 {
656    DBG("!");
657    if (mo->status != MPD_PLAYER_PLAY)
658       return mpd_send_single_cmd(mo,"pause 0\n");
659    else
660       return mpd_send_single_cmd(mo,"pause 1\n");
661 }
662 
mpd_player_play(MpdObj * mo)663 int mpd_player_play(MpdObj* mo)
664 {
665    DBG("!");
666    return mpd_send_single_cmd(mo,"play\n");
667 }
668 
mpd_player_play_id(MpdObj * mo,int id)669 int mpd_player_play_id(MpdObj* mo, int id)
670 {
671    char outbuf[15];
672    DBG("!");
673    snprintf(outbuf, sizeof(outbuf), "playid %d\n",id);
674    return mpd_send_single_cmd(mo,outbuf);
675 }
676 
mpd_check_error(MpdObj * mo)677 int mpd_check_error(MpdObj* mo)
678 {
679    DBG("! return %d",mo->error);
680    return mo->error;
681 }
682 
mpd_send_password(MpdObj * mo)683 void mpd_send_password(MpdObj* mo)
684 {
685    char outbuf[256];
686    int wrote;
687    DBG("!");
688    /* write password 'password' to socket */
689    wrote = snprintf(outbuf, sizeof(outbuf), "password %s\n",mo->pass);
690    if (wrote > 255) {
691 	/* the password is too long to fit though there doesn't seem to be a
692 	 * nice way to report this error :-/ */
693 	fprintf(stderr, "xfce4-mpc-plugin: password too long!\n");
694 	mo->error = MPD_ERROR_SYSTEM;
695 	return;
696    }
697    mpd_send_single_cmd(mo,outbuf);
698 }
699 
mpd_set_hostname(MpdObj * mo,char * host)700 void mpd_set_hostname(MpdObj* mo, char* host)
701 {
702    DBG("! new hostname=%s",host);
703    g_free(mo->host);
704    mo->host = g_strdup(host);
705 }
706 
mpd_set_password(MpdObj * mo,char * pass)707 void mpd_set_password(MpdObj* mo, char* pass)
708 {
709    DBG("! new password=%s",pass);
710    g_free(mo->pass);
711    mo->pass = g_strdup(pass);
712 }
713 
mpd_set_port(MpdObj * mo,int port)714 void mpd_set_port(MpdObj* mo,int port)
715 {
716    DBG("! new port=%d",port);
717    mo->port = port;
718 }
719 
720