Implement consume, single mode.
[libmpd.git] / src / libmpd.c
blob92a2a3108728b15cfa6499f8b40b99dd2bb59e1f
1 /* libmpd (high level libmpdclient library)
2 * Copyright (C) 2004-2009 Qball Cow <qball@sarine.nl>
3 * Project homepage: http://gmpcwiki.sarine.nl/
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include <stdio.h>
21 #include <stdlib.h>
22 #define __USE_GNU
24 #include <string.h>
25 #include <stdarg.h>
26 #include <config.h>
27 #include <glib.h>
28 #include "debug_printf.h"
30 #include "libmpd.h"
31 #include "libmpd-internal.h"
33 static void mpd_free_queue_ob(MpdObj *mi);
34 static void mpd_server_free_commands(MpdObj *mi);
37 char *libmpd_version = LIBMPD_VERSION;
38 #ifndef HAVE_STRNDUP
39 /**
40 * Not every platfarm has strndup, so here we have a nice little custom implementation
42 char * strndup(const char *s, size_t n)
44 size_t nAvail;
45 char *p;
47 if(!s) {
48 return NULL;
51 /* nAvail = min( strlen(s)+1, n+1 ); */
52 nAvail=((strlen(s)+1) > (n+1)) ? n+1 : strlen(s)+1;
53 if(!(p=malloc(nAvail))) {
54 return NULL;
56 memcpy(p, s, nAvail);
57 p[nAvail - 1] = 0;
58 return p;
60 #endif
61 /**
62 * @param state a #MpdServerState to initialize
64 * Initialize #MpdServerState. To stop duplicating code.
66 static void mpd_init_MpdServerState(MpdServerState *state)
68 state->playlistid = -1;
69 state->storedplaylistid = -1;
70 state->state = -1;
71 state->songid = -1;
72 state->songpos = -1;
73 state->nextsongpos = -1;
74 state->nextsongid = -1;
75 state->dbUpdateTime = 0;
76 state->updatingDb = 0;
77 state->repeat = -1;
78 state->random = -1;
79 state->volume = -2;
80 state->xfade = -1;
81 state->totaltime = 0;
82 state->elapsedtime = 0;
83 state->bitrate = 0;
84 state->samplerate = 0;
85 state->channels = 0;
86 state->bits = 0;
87 state->playlistLength = 0;
88 state->error[0] = '\0';
93 static MpdObj * mpd_create()
95 MpdObj * mi = malloc(sizeof(*mi));
96 if( mi == NULL )
98 /* should never happen on linux */
99 return NULL;
103 /* set default values */
104 /* we start not connected */
105 mi->connected = FALSE;
106 /* port 6600 is the default mpd port */
107 mi->port = 6600;
108 /* localhost */
109 mi->hostname = strdup("localhost");
110 /* no password */
111 mi->password = NULL;
112 /* 1 second timeout */
113 mi->connection_timeout = 1.0;
114 /* we have no connection pointer */
115 mi->connection = NULL;
116 /* no status */
117 mi->status = NULL;
118 /* no stats */
119 mi->stats = NULL;
120 mi->error = 0;
121 mi->error_mpd_code = 0;
122 mi->error_msg = NULL;
123 mi->CurrentSong = NULL;
124 /* info */
125 mpd_init_MpdServerState(&(mi->CurrentState));
126 mpd_init_MpdServerState(&(mi->OldState));
129 * Set signals to NULL
131 /* connection changed signal */
132 mi->the_connection_changed_callback = NULL;
133 mi->the_connection_changed_signal_userdata = NULL;
135 /* status changed */
136 mi->the_status_changed_callback = NULL;
137 mi->the_status_changed_signal_userdata = NULL;
139 /* error callback */
140 mi->the_error_callback = NULL;
141 mi->the_error_signal_userdata = NULL;
142 /* connection is locked because where not connected */
143 mi->connection_lock = TRUE;
145 /* set queue to NULL */
146 mi->queue = NULL;
147 /* search stuff */
148 mi->search_type = MPD_SEARCH_TYPE_NONE;
149 /* no need to initialize, but set it to anything anyway*/
150 mi->search_field = MPD_TAG_ITEM_ARTIST;
152 /* outputs */
153 mi->num_outputs = 0;
154 mi->output_states = NULL;
155 /* set 0 */
156 memset((mi->supported_tags), 0,sizeof(mi->supported_tags));
158 mi->has_idle = 0;
159 /* commands */
160 mi->commands = NULL;
161 return mi;
164 void mpd_free(MpdObj *mi)
166 debug_printf(DEBUG_INFO, "destroying MpdObj object\n");
167 if(mi->connected)
169 /* disconnect */
170 debug_printf(DEBUG_WARNING, "Connection still running, disconnecting\n");
171 mpd_disconnect(mi);
173 if(mi->hostname)
175 free(mi->hostname);
177 if(mi->password)
179 free(mi->password);
181 if(mi->error_msg)
183 free(mi->error_msg);
185 if(mi->connection)
187 /* obsolete */
188 mpd_closeConnection(mi->connection);
190 if(mi->status)
192 mpd_freeStatus(mi->status);
194 if(mi->stats)
196 mpd_freeStats(mi->stats);
198 if(mi->CurrentSong)
200 mpd_freeSong(mi->CurrentSong);
202 mpd_free_queue_ob(mi);
203 mpd_server_free_commands(mi);
204 free(mi);
207 int mpd_check_error(MpdObj *mi)
209 if(mi == NULL)
211 debug_printf(DEBUG_ERROR, "mi == NULL?");
212 return MPD_ARGS_ERROR;
215 /* this shouldn't happen, ever */
216 if(mi->connection == NULL)
218 debug_printf(DEBUG_ERROR, "mi->connection == NULL?");
219 return MPD_FATAL_ERROR;
222 /* TODO: map these errors in the future */
223 mi->error = mi->connection->error;
224 mi->error_mpd_code = mi->connection->errorCode;
225 /*TODO: do I need to strdup this? */
226 if(!g_utf8_validate(mi->connection->errorStr, -1, NULL)){
227 mi->error_msg = g_locale_to_utf8(mi->connection->errorStr, -1, NULL, NULL,NULL);
228 }else{
229 mi->error_msg = g_strdup(mi->connection->errorStr);
232 if(mi->error_msg == NULL) mi->error_msg = g_strdup("Failed to convert error message to utf-8");
234 /* Check for permission */
235 /* First check for an error reported by MPD
236 * Then check what type of error mpd reported
238 if(mi->error == MPD_ERROR_ACK)
241 debug_printf(DEBUG_ERROR,"clearing errors in mpd_Connection: %i-%s", mi->connection->errorCode, mi->connection->errorStr);
242 mpd_clearError(mi->connection);
243 if (mi->the_error_callback)
245 debug_printf(DEBUG_ERROR, "Error callback 1 (ACK)");
246 if(mi->the_error_callback(mi, mi->error_mpd_code, mi->error_msg, mi->the_error_signal_userdata ))
248 debug_printf(DEBUG_ERROR, "Error callback told me to disconnect");
249 mpd_disconnect(mi);
250 free(mi->error_msg);
251 mi->error_msg = NULL;
253 return MPD_SERVER_ERROR;
256 free(mi->error_msg);
257 mi->error_msg = NULL;
258 return TRUE;
260 if(mi->error)
263 debug_printf(DEBUG_ERROR, "Following error occurred: %i: code: %i msg: %s", mi->error,mi->connection->errorCode, mi->error_msg);
265 if (mi->the_error_callback)
267 debug_printf(DEBUG_ERROR, "Error callback 2");
268 mi->the_error_callback(mi, mi->error, mi->error_msg, mi->the_error_signal_userdata );
270 mpd_disconnect(mi);
271 free(mi->error_msg);
272 mi->error_msg = NULL;
274 return MPD_SERVER_ERROR;
276 free(mi->error_msg);
277 mi->error_msg = NULL;
278 return MPD_OK;
283 int mpd_lock_conn(MpdObj *mi)
286 if(mi->connection_lock)
288 debug_printf(DEBUG_WARNING, "Failed to lock connection, already locked\n");
289 return MPD_LOCK_FAILED;
291 mi->connection_lock = TRUE;
292 return MPD_OK;
295 int mpd_unlock_conn(MpdObj *mi)
297 if(!mi->connection_lock)
299 debug_printf(DEBUG_ERROR, "Failed to unlock connection, already unlocked\n");
300 return MPD_LOCK_FAILED;
303 mi->connection_lock = FALSE;
305 return mpd_check_error(mi);
308 MpdObj * mpd_new_default()
310 debug_printf(DEBUG_INFO, "creating a new mpdInt object\n");
311 return mpd_create();
314 MpdObj *mpd_new(char *hostname, int port, char *password)
316 MpdObj *mi = mpd_create();
317 if(mi == NULL)
319 return NULL;
321 if(hostname != NULL)
323 mpd_set_hostname(mi, hostname);
325 if(port != 0)
327 mpd_set_port(mi, port);
329 if(password != NULL)
331 mpd_set_password(mi, password);
333 return mi;
337 const char * mpd_get_hostname(MpdObj *mi)
339 if(mi == NULL)
341 return NULL;
343 return mi->hostname;
346 int mpd_set_hostname(MpdObj *mi, char *hostname)
348 if(mi == NULL)
350 debug_printf(DEBUG_ERROR, "mi == NULL\n");
351 return MPD_ARGS_ERROR;
354 if(mi->hostname != NULL)
356 free(mi->hostname);
358 /* possible location todo some post processing of hostname */
359 mi->hostname = strdup(hostname);
360 return MPD_OK;
363 int mpd_set_password(MpdObj *mi, const char *password)
365 if(mi == NULL)
367 debug_printf(DEBUG_ERROR, "mi == NULL\n");
368 return MPD_ARGS_ERROR;
371 if(mi->password != NULL)
373 free(mi->password);
375 /* possible location todo some post processing of password */
376 mi->password = strdup(password);
377 return MPD_OK;
381 int mpd_send_password(MpdObj *mi)
383 if(!mi) return MPD_ARGS_ERROR;
384 if(mi->password && mpd_check_connected(mi) && strlen(mi->password))
386 if(mpd_lock_conn(mi))
388 debug_printf(DEBUG_WARNING, "failed to lock connection");
389 return MPD_LOCK_FAILED;
391 mpd_sendPasswordCommand(mi->connection, mi->password);
392 mpd_finishCommand(mi->connection);
393 if(mpd_unlock_conn(mi))
395 debug_printf(DEBUG_ERROR, "Failed to unlock connection\n");
396 return MPD_LOCK_FAILED;
398 mpd_server_get_allowed_commands(mi);
399 /*TODO: should I do it here, or in the
400 * mpd_server_get_allowed_command, so it also get's executed on
401 * connect
403 if((mi->the_status_changed_callback != NULL))
405 /** update the supported tags */
407 int i;
408 char **retv = mpd_server_get_tag_types(mi);
409 if(retv){
410 for(i=0;i<MPD_TAG_ITEM_ANY;i++)
412 int j=0;
413 for(j=0;retv[j] && strcasecmp(retv[j],mpdTagItemKeys[i]); j++);
414 if(retv[j]) mi->supported_tags[i] = TRUE;
415 else mi->supported_tags[i] = FALSE;
417 g_strfreev(retv);
419 printf("updated tags\n");
420 /* also always true */
421 mi->supported_tags[MPD_TAG_ITEM_FILENAME] = TRUE;
422 mi->supported_tags[MPD_TAG_ITEM_ANY] = TRUE;
424 /* If permission updates, we should also call an output update, The data might be available now. */
425 mi->the_status_changed_callback( mi,
426 MPD_CST_PERMISSION|MPD_CST_OUTPUT, mi->the_status_changed_signal_userdata );
429 return MPD_OK;
432 int mpd_set_port(MpdObj *mi, int port)
434 if(mi == NULL)
436 debug_printf(DEBUG_ERROR, "mi == NULL\n");
437 return MPD_ARGS_ERROR;
439 mi->port = port;
440 return MPD_OK;
443 int mpd_set_connection_timeout(MpdObj *mi, float timeout)
445 if(mi == NULL)
447 debug_printf(DEBUG_ERROR, "mi == NULL\n");
448 return MPD_ARGS_ERROR;
450 mi->connection_timeout = timeout;
451 if(mpd_check_connected(mi))
453 /*TODO: set timeout */
454 if(mpd_lock_conn(mi))
456 debug_printf(DEBUG_ERROR,"lock failed\n");
457 return MPD_LOCK_FAILED;
459 mpd_setConnectionTimeout(mi->connection, timeout);
460 mpd_finishCommand(mi->connection);
462 mpd_unlock_conn(mi);
465 return MPD_OK;
468 static void mpd_server_free_commands(MpdObj *mi)
470 if(mi->commands)
472 int i=0;
473 while(mi->commands[i].command_name)
475 free(mi->commands[i].command_name);
476 i++;
478 free(mi->commands);
479 mi->commands = NULL;
483 char *mpd_server_get_version(MpdObj *mi)
485 char *retval = NULL;
486 if(!mi || !mpd_check_connected(mi))
487 return NULL;
488 retval = malloc(10*sizeof(char));
489 snprintf(retval,10,"%i.%i.%i", mi->connection->version[0], mi->connection->version[1], mi->connection->version[2]);
490 /* always make sure the string is terminated */
491 retval[9] = '\0';
492 return retval;
495 int mpd_server_get_allowed_commands(MpdObj *mi)
497 char *temp = NULL;
498 int num_commands = 0;
499 if(!mi){
500 debug_printf(DEBUG_ERROR, "mi != NULL failed\n");
501 return MPD_ARGS_ERROR;
503 if(!mpd_check_connected(mi)) {
504 debug_printf(DEBUG_WARNING, "Not Connected");
505 return MPD_NOT_CONNECTED;
507 if(!mpd_server_check_version(mi,0,12,0)){
508 debug_printf(DEBUG_INFO, "Not supported by mpd");
509 return MPD_SERVER_NOT_SUPPORTED;
512 mpd_server_free_commands(mi);
514 if(mpd_lock_conn(mi))
516 debug_printf(DEBUG_ERROR, "lock failed");
517 return MPD_LOCK_FAILED;
519 mpd_sendCommandsCommand(mi->connection);
520 while((temp = mpd_getNextCommand(mi->connection)))
522 num_commands++;
523 mi->commands = realloc(mi->commands, (num_commands+1)*sizeof(MpdCommand));
524 mi->commands[num_commands-1].command_name = temp;
525 mi->commands[num_commands-1].enabled = TRUE;
526 mi->commands[num_commands].command_name = NULL;
527 mi->commands[num_commands].enabled = FALSE;
528 if(strcmp(mi->commands[num_commands-1].command_name, "idle") == 0) {
529 mi->has_idle = TRUE;
532 mpd_finishCommand(mi->connection);
533 mpd_sendNotCommandsCommand(mi->connection);
534 while((temp = mpd_getNextCommand(mi->connection)))
536 num_commands++;
537 mi->commands = realloc(mi->commands, (num_commands+1)*sizeof(MpdCommand));
538 mi->commands[num_commands-1].command_name = temp;
539 mi->commands[num_commands-1].enabled = FALSE;
540 mi->commands[num_commands].command_name = NULL;
541 mi->commands[num_commands].enabled = FALSE;
543 mpd_finishCommand(mi->connection);
545 if(mpd_unlock_conn(mi))
547 return MPD_LOCK_FAILED;
549 return MPD_OK;
554 int mpd_disconnect(MpdObj *mi)
557 /* lock */
558 mpd_lock_conn(mi);
559 debug_printf(DEBUG_INFO, "disconnecting\n");
561 if(mi->connection)
563 mpd_closeConnection(mi->connection);
564 mi->connection = NULL;
566 if(mi->status)
568 mpd_freeStatus(mi->status);
569 mi->status = NULL;
571 if(mi->stats)
573 mpd_freeStats(mi->stats);
574 mi->stats = NULL;
576 if(mi->CurrentSong)
578 mpd_freeSong(mi->CurrentSong);
579 mi->CurrentSong = NULL;
581 mi->CurrentState.playlistid = -1;
582 mi->CurrentState.storedplaylistid = -1;
583 mi->CurrentState.state = -1;
584 mi->CurrentState.songid = -1;
585 mi->CurrentState.songpos = -1;
586 mi->CurrentState.nextsongid = -1;
587 mi->CurrentState.nextsongpos = -1;
588 mi->CurrentState.dbUpdateTime = 0;
589 mi->CurrentState.updatingDb = 0;
590 mi->CurrentState.repeat = -1;
591 mi->CurrentState.random = -1;
592 mi->CurrentState.volume = -2;
593 mi->CurrentState.xfade = -1;
594 mi->CurrentState.totaltime = 0;
595 mi->CurrentState.elapsedtime = 0;
596 mi->CurrentState.bitrate = 0;
597 mi->CurrentState.samplerate = 0;
598 mi->CurrentState.channels = 0;
599 mi->CurrentState.bits = 0;
600 mi->CurrentState.playlistLength = 0;
601 mi->CurrentState.error[0] = '\0';
602 /* search stuff */
603 mi->search_type = MPD_SEARCH_TYPE_NONE;
604 /* no need to initialize, but set it to anything anyway*/
605 mi->search_field = MPD_TAG_ITEM_ARTIST;
606 /* outputs */
607 mi->num_outputs = 0;
608 if(mi->output_states)
609 g_free(mi->output_states);
610 mi->output_states = NULL;
612 /* set 0 */
613 memset((mi->supported_tags), 0,sizeof(mi->supported_tags));
615 mi->has_idle = 0;
617 memcpy(&(mi->OldState), &(mi->CurrentState) , sizeof(MpdServerState));
619 mpd_free_queue_ob(mi);
620 mpd_server_free_commands(mi);
621 /*don't reset errors */
622 /* Remove this signal, we don't actually disconnect */
623 if(mi->connected)
625 /* set disconnect flag */
626 mi->connected = FALSE;
628 if(mi->the_connection_changed_callback != NULL)
630 mi->the_connection_changed_callback( mi, FALSE, mi->the_connection_changed_signal_userdata );
633 debug_printf(DEBUG_INFO, "Disconnect completed\n");
634 return MPD_OK;
636 int mpd_connect(MpdObj *mi)
638 return mpd_connect_real(mi,NULL);
640 int mpd_connect_real(MpdObj *mi,mpd_Connection *connection)
642 int retv;
643 if(mi == NULL)
645 /* should return some spiffy error here */
646 debug_printf(DEBUG_ERROR, "mi != NULL failed");
647 return MPD_ARGS_ERROR;
649 /* reset errors */
650 mi->error = 0;
651 mi->error_mpd_code = 0;
652 if(mi->error_msg != NULL)
654 free(mi->error_msg);
656 mi->error_msg = NULL;
658 debug_printf(DEBUG_INFO, "connecting\n");
659 mpd_init_MpdServerState(&(mi->CurrentState));
661 memcpy(&(mi->OldState), &(mi->CurrentState), sizeof(MpdServerState));
663 if(mi->connected)
665 /* disconnect */
666 mpd_disconnect(mi);
669 if(mi->hostname == NULL)
671 mpd_set_hostname(mi, "localhost");
673 /* make sure this is locked */
674 if(!mi->connection_lock)
676 mpd_lock_conn(mi);
678 if(connection) {
679 mi->connection = connection;
680 } else {
681 /* make timeout configurable */
682 mi->connection = mpd_newConnection(mi->hostname,mi->port,mi->connection_timeout);
684 if(mi->connection == NULL)
686 /* TODO: make seperate error message? */
687 return MPD_NOT_CONNECTED;
689 if(mpd_check_error(mi) != MPD_OK)
691 /* TODO: make seperate error message? */
692 return MPD_NOT_CONNECTED;
695 /* set connected state */
696 mi->connected = TRUE;
697 if(mpd_unlock_conn(mi))
699 return MPD_LOCK_FAILED;
702 /* get the commands we are allowed to use */
703 retv = mpd_server_get_allowed_commands(mi);
704 if(retv!= MPD_OK)
706 return retv;
708 if(mi->password && strlen(mi->password) > 0)
710 mpd_send_password(mi);
712 else
714 int i;
715 char **retv = mpd_server_get_tag_types(mi);
716 if(retv){
717 for(i=0;i<MPD_TAG_ITEM_ANY;i++)
719 int j=0;
720 for(j=0;retv[j] && strcasecmp(retv[j],mpdTagItemKeys[i]); j++);
721 if(retv[j]) mi->supported_tags[i] = TRUE;
722 else mi->supported_tags[i] = FALSE;
724 g_strfreev(retv);
726 /* also always true */
727 mi->supported_tags[MPD_TAG_ITEM_FILENAME] = TRUE;
728 mi->supported_tags[MPD_TAG_ITEM_ANY] = TRUE;
732 retv = mpd_server_update_outputs(mi);
733 if(retv != MPD_OK)
734 return retv;
737 retv = mpd_server_update_outputs(mi);
738 /** update the supported tags */
739 debug_printf(DEBUG_INFO, "Propagating connection changed");
741 if(mi->the_connection_changed_callback != NULL)
743 mi->the_connection_changed_callback( mi, TRUE, mi->the_connection_changed_signal_userdata );
746 if(retv != MPD_OK)
747 return retv;
749 debug_printf(DEBUG_INFO, "Connected to mpd");
750 return MPD_OK;
753 int mpd_check_connected(MpdObj *mi)
755 if(mi == NULL)
757 return FALSE;
759 return mi->connected;
763 /* SIGNALS */
764 void mpd_signal_connect_status_changed (MpdObj *mi, StatusChangedCallback status_changed, void *userdata)
766 if(mi == NULL)
768 debug_printf(DEBUG_ERROR, "mi != NULL failed");
769 return;
771 mi->the_status_changed_callback = status_changed;
772 mi->the_status_changed_signal_userdata = userdata;
776 void mpd_signal_connect_error(MpdObj *mi, ErrorCallback error_callback, void *userdata)
778 if(mi == NULL)
780 debug_printf(DEBUG_ERROR, "mi != NULL failed");
781 return;
783 mi->the_error_callback = error_callback;
784 mi->the_error_signal_userdata = userdata;
787 void mpd_signal_connect_connection_changed(MpdObj *mi, ConnectionChangedCallback connection_changed, void *userdata)
789 if(mi == NULL)
791 debug_printf(DEBUG_ERROR, "mi != NULL failed");
792 return;
794 mi->the_connection_changed_callback = connection_changed;
795 mi->the_connection_changed_signal_userdata = userdata;
799 /* more playlist */
800 /* MpdData Part */
801 MpdData *mpd_new_data_struct(void)
803 MpdData_real* data;
804 data = g_slice_new(MpdData_real);
805 data->type = 0;
807 data->tag_type = 0;
808 data->tag = NULL;
809 data->song = NULL;
810 data->directory = NULL;
811 data->playlist = NULL;
812 data->output_dev = NULL;
813 data->next = NULL;
814 data->prev = NULL;
815 data->first = NULL;
817 data->userdata = NULL;
818 data->freefunc = NULL;
819 return (MpdData*)data;
822 MpdData *mpd_new_data_struct_append(MpdData * data)
824 MpdData_real *data_real = (MpdData_real*)data;
825 if(data_real == NULL)
827 data_real = (MpdData_real*)mpd_new_data_struct();
828 data_real->first = data_real;
830 else
832 data_real->next = (MpdData_real*)mpd_new_data_struct();
833 data_real->next->prev = data_real;
834 data_real = data_real->next;
835 data_real->next = NULL;
836 data_real->first = data_real->prev->first;
838 return (MpdData*)data_real;
841 MpdData * mpd_data_get_first(MpdData const * const data)
843 MpdData_real const * const data_real = (MpdData_real const * const)data;
844 if(data_real != NULL)
846 return (MpdData*)data_real->first;
848 return NULL;
852 MpdData * mpd_data_get_next(MpdData * const data)
854 return mpd_data_get_next_real(data, TRUE);
857 MpdData * mpd_data_get_next_real(MpdData * const data, int kill_list)
859 MpdData_real *data_real = (MpdData_real*)data;
860 if (data_real != NULL)
862 if (data_real->next != NULL )
864 return (MpdData*)data_real->next;
866 else
868 if (kill_list) mpd_data_free((MpdData*)data_real);
869 return NULL;
872 return (MpdData*)data_real;
875 int mpd_data_is_last(MpdData const * const data)
877 MpdData_real const * const data_real = (MpdData_real const * const)data;
878 if(data_real != NULL)
880 if (data_real->next == NULL)
882 return TRUE;
885 return FALSE;
888 MpdData_head *mpd_data_get_head(MpdData const * const data) {
889 return ((MpdData_real*)data)->head;
892 MpdData* mpd_data_concatenate( MpdData * const first, MpdData * const second)
894 MpdData_real *first_real = (MpdData_real*)first;
895 MpdData_real *second_real = (MpdData_real*)second;
896 MpdData_real *first_head = NULL;
898 if ( first == NULL ) {
899 if ( second != NULL )
900 return (MpdData*)second_real;
901 else
902 return NULL;
903 } else {
904 if ( second == NULL )
905 return (MpdData*)first_real;
908 first_head = (MpdData_real *)mpd_data_get_first(first);
910 /* find last element in first data list */
911 while (!mpd_data_is_last((MpdData*)first_real)) first_real = (MpdData_real*)mpd_data_get_next_real((MpdData*)first_real, FALSE);
912 second_real =(MpdData_real*) mpd_data_get_first((MpdData*)second_real);
914 first_real->next = second_real;
915 second_real->prev = first_real;
917 /* I need to set all the -> first correct */
918 while (second_real)
920 second_real->first = first_head;
921 second_real = (MpdData_real*)mpd_data_get_next_real((MpdData*)second_real, FALSE);
924 return (MpdData*)first_head;
926 /**
927 * Deletes an item from the list. It returns the next item in the list.
928 * if that is not available, it will return the last item
930 MpdData * mpd_data_delete_item(MpdData *data)
932 MpdData_real *temp = NULL, *data_real = (MpdData_real*)data;
933 if(data_real == NULL) return NULL;
934 /* if there is a next item, fix the prev pointer of the next item */
935 if (data_real->next)
937 data_real->next->prev = data_real->prev;
938 temp = data_real->next;
940 /* if there is a previous item, fix the next pointer of the previous item */
941 if (data_real->prev)
943 /* the next item of the previous is the next item of the current */
944 data_real->prev->next = data_real->next;
945 /* temp is the previous item */
946 temp = data_real->prev;
949 /* fix first, if removed item is the first */
950 if(temp && temp->first == data_real)
952 MpdData_real *first,*node = temp;
953 /* get first */
954 for(;node->prev;node = node->prev);
955 first = node;
956 while(node){
957 node->first = first;
958 node = node->next;
961 /* make the removed row a valid list, so I can use the default free function to free it */
962 data_real->next = NULL;
963 data_real->prev = NULL;
964 data_real->first = data_real;
965 /* free it */
966 mpd_data_free((MpdData *)data_real);
968 return (MpdData *)temp;
971 void mpd_data_free(MpdData *data)
973 MpdData_real *data_real,*temp;
974 if(data == NULL)
976 debug_printf(DEBUG_ERROR, "data != NULL Failed");
977 return;
979 data_real = (MpdData_real *)mpd_data_get_first(data);
980 while(data_real){
981 temp = data_real;
982 if (data_real->type == MPD_DATA_TYPE_SONG) {
983 if(data_real->song) mpd_freeSong(data_real->song);
984 } else if (data_real->type == MPD_DATA_TYPE_OUTPUT_DEV) {
985 mpd_freeOutputElement(data_real->output_dev);
986 } else if(data_real->type == MPD_DATA_TYPE_DIRECTORY) {
987 if(data_real->directory)free(data_real->directory);
988 } else if(data_real->type == MPD_DATA_TYPE_PLAYLIST) {
989 if(data_real->playlist) mpd_freePlaylistFile(data_real->playlist);
990 } else {
991 free((void*)(data_real->tag));
993 if(data_real->freefunc)
995 if(data_real->userdata)
996 data_real->freefunc(data_real->userdata);
998 data_real = data_real->next;
999 g_slice_free1(sizeof(*temp), temp);
1003 /* clean this up.. make one while loop */
1004 static void mpd_free_queue_ob(MpdObj *mi)
1006 MpdQueue *temp = NULL;
1007 if(mi == NULL)
1009 debug_printf(DEBUG_ERROR, "mi != NULL failed");
1010 return;
1012 if(mi->queue == NULL)
1014 debug_printf(DEBUG_INFO, "mi->queue != NULL failed, nothing to clean.");
1015 return;
1017 mi->queue = mi->queue->first;
1018 while(mi->queue != NULL)
1020 temp = mi->queue->next;
1022 if(mi->queue->path != NULL)
1024 free(mi->queue->path);
1027 free(mi->queue);
1028 mi->queue = temp;
1030 mi->queue = NULL;
1034 MpdQueue *mpd_new_queue_struct()
1036 MpdQueue* queue = malloc(sizeof(MpdQueue));
1038 queue->type = 0;
1039 queue->path = NULL;
1040 queue->id = 0;
1042 return queue;
1046 void mpd_queue_get_next(MpdObj *mi)
1048 if(mi->queue != NULL && mi->queue->next != NULL)
1050 mi->queue = mi->queue->next;
1052 else if(mi->queue->next == NULL)
1054 mpd_free_queue_ob(mi);
1055 mi->queue = NULL;
1059 long unsigned mpd_server_get_database_update_time(MpdObj *mi)
1061 if(!mpd_check_connected(mi))
1063 debug_printf(DEBUG_WARNING,"not connected\n");
1064 return MPD_NOT_CONNECTED;
1066 if(mpd_stats_check(mi) != MPD_OK)
1068 debug_printf(DEBUG_WARNING,"Failed grabbing status\n");
1069 return MPD_STATS_FAILED;
1071 return mi->stats->dbUpdateTime;
1075 MpdData * mpd_server_get_output_devices(MpdObj *mi)
1077 mpd_OutputEntity *output = NULL;
1078 MpdData *data = NULL;
1079 if(!mpd_check_connected(mi))
1081 debug_printf(DEBUG_WARNING,"not connected\n");
1082 return NULL;
1084 /* TODO: Check version */
1085 if(mpd_lock_conn(mi))
1087 debug_printf(DEBUG_ERROR,"lock failed\n");
1088 return NULL;
1091 mpd_sendOutputsCommand(mi->connection);
1092 while (( output = mpd_getNextOutput(mi->connection)) != NULL)
1094 data = mpd_new_data_struct_append(data);
1095 data->type = MPD_DATA_TYPE_OUTPUT_DEV;
1096 data->output_dev = output;
1098 mpd_finishCommand(mi->connection);
1100 /* unlock */
1101 if(mpd_unlock_conn(mi) != MPD_OK)
1103 if(data)mpd_data_free(data);
1104 return NULL;
1106 if(data == NULL)
1108 return NULL;
1110 return mpd_data_get_first(data);
1113 int mpd_server_set_output_device(MpdObj *mi,int device_id,int state)
1115 if(!mpd_check_connected(mi))
1117 debug_printf(DEBUG_WARNING,"not connected\n");
1118 return MPD_NOT_CONNECTED;
1120 if(mpd_lock_conn(mi))
1122 debug_printf(DEBUG_ERROR,"lock failed\n");
1123 return MPD_LOCK_FAILED;
1125 if(state)
1127 mpd_sendEnableOutputCommand(mi->connection, device_id);
1129 else
1131 mpd_sendDisableOutputCommand(mi->connection, device_id);
1133 mpd_finishCommand(mi->connection);
1135 mpd_unlock_conn(mi);
1136 mpd_status_queue_update(mi);
1137 return FALSE;
1140 int mpd_server_check_version(MpdObj *mi, int major, int minor, int micro)
1142 if(!mpd_check_connected(mi))
1144 debug_printf(DEBUG_WARNING,"not connected\n");
1145 return FALSE;
1147 if(major > mi->connection->version[0]) return FALSE;
1148 if(mi->connection->version[0] > major) return TRUE;
1149 if(minor > mi->connection->version[1]) return FALSE;
1150 if(mi->connection->version[1] > minor) return TRUE;
1151 if(micro > mi->connection->version[2]) return FALSE;
1152 if(mi->connection->version[2] > micro) return TRUE;
1153 return TRUE;
1156 int mpd_server_check_command_allowed(MpdObj *mi, const char *command)
1158 int i;
1159 if(!mi || !command) return MPD_SERVER_COMMAND_ERROR;
1160 /* when we are connected to a mpd server that doesn't support commands and not commands
1161 * feature. (like mpd 0.11.5) allow everything
1163 if(!mpd_server_check_version(mi, 0,12,0)) return MPD_SERVER_COMMAND_ALLOWED;
1165 * Also when somehow we failted to get commands
1167 if(mi->commands == NULL) return MPD_SERVER_COMMAND_ALLOWED;
1171 for(i=0;mi->commands[i].command_name;i++)
1173 if(!strcasecmp(mi->commands[i].command_name, command))
1174 return mi->commands[i].enabled;
1176 return MPD_SERVER_COMMAND_NOT_SUPPORTED;
1179 char ** mpd_server_get_url_handlers(MpdObj *mi)
1181 char *temp = NULL;
1182 int i=0;
1183 char **retv = NULL;
1184 if(!mpd_check_connected(mi))
1186 debug_printf(DEBUG_WARNING,"not connected\n");
1187 return FALSE;
1189 if(mpd_lock_conn(mi))
1191 debug_printf(DEBUG_ERROR,"lock failed\n");
1192 return NULL;
1194 mpd_sendUrlHandlersCommand(mi->connection);
1195 while((temp = mpd_getNextHandler(mi->connection)) != NULL)
1197 retv = realloc(retv,(i+2)*sizeof(*retv));
1198 retv[i] = temp;
1199 retv[i+1] = NULL;
1200 i++;
1202 mpd_finishCommand(mi->connection);
1205 mpd_unlock_conn(mi);
1206 return retv;
1208 char ** mpd_server_get_tag_types(MpdObj *mi)
1210 char *temp = NULL;
1211 int i=0;
1212 char **retv = NULL;
1213 if(!mpd_check_connected(mi))
1215 debug_printf(DEBUG_WARNING,"not connected\n");
1216 return FALSE;
1218 if(mpd_lock_conn(mi))
1220 debug_printf(DEBUG_ERROR,"lock failed\n");
1221 return NULL;
1223 mpd_sendTagTypesCommand(mi->connection);
1224 while((temp = mpd_getNextTagType(mi->connection)) != NULL)
1226 retv = realloc(retv,(i+2)*sizeof(*retv));
1227 retv[i] = temp;
1228 retv[i+1] = NULL;
1229 i++;
1231 mpd_finishCommand(mi->connection);
1234 mpd_unlock_conn(mi);
1235 return retv;
1238 int mpd_misc_get_tag_by_name(char *name)
1240 int i;
1241 if(name == NULL)
1243 return MPD_ARGS_ERROR;
1245 for(i=0; i < MPD_TAG_NUM_OF_ITEM_TYPES; i++)
1247 if(!strcasecmp(mpdTagItemKeys[i], name))
1249 return i;
1252 return MPD_TAG_NOT_FOUND;
1255 int mpd_server_update_outputs(MpdObj *mi)
1257 mpd_OutputEntity *output = NULL;
1258 if(!mpd_check_connected(mi))
1260 debug_printf(DEBUG_WARNING,"not connected\n");
1261 return MPD_NOT_CONNECTED;
1263 if(mpd_lock_conn(mi))
1265 debug_printf(DEBUG_ERROR,"lock failed\n");
1266 return MPD_LOCK_FAILED;
1268 mpd_sendOutputsCommand(mi->connection);
1269 while (( output = mpd_getNextOutput(mi->connection)) != NULL)
1271 mi->num_outputs++;
1272 mi->output_states = g_realloc(mi->output_states,mi->num_outputs*sizeof(int));
1273 mi->output_states[mi->num_outputs-1] = FALSE;/*output->enabled;*/
1274 mpd_freeOutputElement(output);
1276 mpd_finishCommand(mi->connection);
1277 return mpd_unlock_conn(mi);
1280 int mpd_server_has_idle(MpdObj *mi)
1282 return mi->has_idle;
1285 int mpd_server_tag_supported(MpdObj *mi, int tag)
1287 if(!mi) return FALSE;
1288 if(tag < 0 || tag >= MPD_TAG_NUM_OF_ITEM_TYPES) {
1289 return FALSE;
1291 return mi->supported_tags[tag];