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.
28 #include "debug_printf.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
;
40 * Not every platfarm has strndup, so here we have a nice little custom implementation
42 char * strndup(const char *s
, size_t n
)
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
))) {
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;
73 state
->nextsongpos
= -1;
74 state
->nextsongid
= -1;
75 state
->dbUpdateTime
= 0;
76 state
->updatingDb
= 0;
82 state
->elapsedtime
= 0;
84 state
->samplerate
= 0;
89 state
->playlistLength
= 0;
90 state
->error
[0] = '\0';
95 static MpdObj
* mpd_create()
97 MpdObj
* mi
= malloc(sizeof(*mi
));
100 /* should never happen on linux */
105 /* set default values */
106 /* we start not connected */
107 mi
->connected
= FALSE
;
108 /* port 6600 is the default mpd port */
111 mi
->hostname
= strdup("localhost");
114 /* 1 second timeout */
115 mi
->connection_timeout
= 1.0;
116 /* we have no connection pointer */
117 mi
->connection
= NULL
;
123 mi
->error_mpd_code
= 0;
124 mi
->error_msg
= NULL
;
125 mi
->CurrentSong
= NULL
;
127 mpd_init_MpdServerState(&(mi
->CurrentState
));
128 mpd_init_MpdServerState(&(mi
->OldState
));
131 * Set signals to NULL
133 /* connection changed signal */
134 mi
->the_connection_changed_callback
= NULL
;
135 mi
->the_connection_changed_signal_userdata
= NULL
;
138 mi
->the_status_changed_callback
= NULL
;
139 mi
->the_status_changed_signal_userdata
= NULL
;
142 mi
->the_error_callback
= NULL
;
143 mi
->the_error_signal_userdata
= NULL
;
144 /* connection is locked because where not connected */
145 mi
->connection_lock
= TRUE
;
147 /* set queue to NULL */
150 mi
->search_type
= MPD_SEARCH_TYPE_NONE
;
151 /* no need to initialize, but set it to anything anyway*/
152 mi
->search_field
= MPD_TAG_ITEM_ARTIST
;
156 mi
->output_states
= NULL
;
158 mi
->url_handlers
= NULL
;
160 memset((mi
->supported_tags
), 0,sizeof(mi
->supported_tags
));
168 void mpd_free(MpdObj
*mi
)
170 debug_printf(DEBUG_INFO
, "destroying MpdObj object\n");
174 debug_printf(DEBUG_WARNING
, "Connection still running, disconnecting\n");
192 mpd_closeConnection(mi
->connection
);
196 mpd_freeStatus(mi
->status
);
200 mpd_freeStats(mi
->stats
);
204 mpd_freeSong(mi
->CurrentSong
);
208 g_strfreev(mi
->url_handlers
);
209 mi
->url_handlers
= NULL
;
211 mpd_free_queue_ob(mi
);
212 mpd_server_free_commands(mi
);
216 int mpd_check_error(MpdObj
*mi
)
220 debug_printf(DEBUG_ERROR
, "mi == NULL?");
221 return MPD_ARGS_ERROR
;
224 /* this shouldn't happen, ever */
225 if(mi
->connection
== NULL
)
227 debug_printf(DEBUG_ERROR
, "mi->connection == NULL?");
228 return MPD_FATAL_ERROR
;
231 /* TODO: map these errors in the future */
232 mi
->error
= mi
->connection
->error
;
233 mi
->error_mpd_code
= mi
->connection
->errorCode
;
234 /*TODO: do I need to strdup this? */
235 if(!g_utf8_validate(mi
->connection
->errorStr
, -1, NULL
)){
236 mi
->error_msg
= g_locale_to_utf8(mi
->connection
->errorStr
, -1, NULL
, NULL
,NULL
);
238 mi
->error_msg
= g_strdup(mi
->connection
->errorStr
);
241 if(mi
->error_msg
== NULL
) mi
->error_msg
= g_strdup("Failed to convert error message to utf-8");
243 /* Check for permission */
244 /* First check for an error reported by MPD
245 * Then check what type of error mpd reported
247 if(mi
->error
== MPD_ERROR_ACK
)
250 debug_printf(DEBUG_ERROR
,"clearing errors in mpd_Connection: %i-%s", mi
->connection
->errorCode
, mi
->connection
->errorStr
);
251 mpd_clearError(mi
->connection
);
252 if (mi
->the_error_callback
)
254 debug_printf(DEBUG_ERROR
, "Error callback 1 (ACK)");
255 if(mi
->the_error_callback(mi
, mi
->error_mpd_code
, mi
->error_msg
, mi
->the_error_signal_userdata
))
257 debug_printf(DEBUG_ERROR
, "Error callback told me to disconnect");
260 mi
->error_msg
= NULL
;
262 return MPD_SERVER_ERROR
;
266 mi
->error_msg
= NULL
;
272 debug_printf(DEBUG_ERROR
, "Following error occurred: %i: code: %i msg: %s", mi
->error
,mi
->connection
->errorCode
, mi
->error_msg
);
274 if (mi
->the_error_callback
)
276 debug_printf(DEBUG_ERROR
, "Error callback 2");
277 mi
->the_error_callback(mi
, mi
->error
, mi
->error_msg
, mi
->the_error_signal_userdata
);
281 mi
->error_msg
= NULL
;
283 return MPD_SERVER_ERROR
;
286 mi
->error_msg
= NULL
;
292 int mpd_lock_conn(MpdObj
*mi
)
295 if(mi
->connection_lock
)
297 debug_printf(DEBUG_WARNING
, "Failed to lock connection, already locked\n");
298 return MPD_LOCK_FAILED
;
300 mi
->connection_lock
= TRUE
;
304 int mpd_unlock_conn(MpdObj
*mi
)
306 if(!mi
->connection_lock
)
308 debug_printf(DEBUG_ERROR
, "Failed to unlock connection, already unlocked\n");
309 return MPD_LOCK_FAILED
;
312 mi
->connection_lock
= FALSE
;
314 return mpd_check_error(mi
);
317 MpdObj
* mpd_new_default()
319 debug_printf(DEBUG_INFO
, "creating a new mpdInt object\n");
323 MpdObj
*mpd_new(char *hostname
, int port
, char *password
)
325 MpdObj
*mi
= mpd_create();
332 mpd_set_hostname(mi
, hostname
);
336 mpd_set_port(mi
, port
);
340 mpd_set_password(mi
, password
);
346 const char * mpd_get_hostname(MpdObj
*mi
)
355 int mpd_set_hostname(MpdObj
*mi
, char *hostname
)
359 debug_printf(DEBUG_ERROR
, "mi == NULL\n");
360 return MPD_ARGS_ERROR
;
363 if(mi
->hostname
!= NULL
)
367 /* possible location todo some post processing of hostname */
368 mi
->hostname
= strdup(hostname
);
372 int mpd_set_password(MpdObj
*mi
, const char *password
)
376 debug_printf(DEBUG_ERROR
, "mi == NULL\n");
377 return MPD_ARGS_ERROR
;
380 if(mi
->password
!= NULL
)
384 /* possible location todo some post processing of password */
385 mi
->password
= strdup(password
);
390 int mpd_send_password(MpdObj
*mi
)
392 if(!mi
) return MPD_ARGS_ERROR
;
393 if(mi
->password
&& mpd_check_connected(mi
) && strlen(mi
->password
))
395 if(mpd_lock_conn(mi
))
397 debug_printf(DEBUG_WARNING
, "failed to lock connection");
398 return MPD_LOCK_FAILED
;
400 mpd_sendPasswordCommand(mi
->connection
, mi
->password
);
401 mpd_finishCommand(mi
->connection
);
402 if(mpd_unlock_conn(mi
))
404 debug_printf(DEBUG_ERROR
, "Failed to unlock connection\n");
405 return MPD_LOCK_FAILED
;
407 mpd_server_get_allowed_commands(mi
);
408 /*TODO: should I do it here, or in the
409 * mpd_server_get_allowed_command, so it also get's executed on
412 if((mi
->the_status_changed_callback
!= NULL
))
414 /** update the supported tags */
417 char **retv
= mpd_server_get_tag_types(mi
);
419 for(i
=0;i
<MPD_TAG_ITEM_ANY
;i
++)
422 for(j
=0;retv
[j
] && strcasecmp(retv
[j
],mpdTagItemKeys
[i
]); j
++);
423 if(retv
[j
]) mi
->supported_tags
[i
] = TRUE
;
424 else mi
->supported_tags
[i
] = FALSE
;
428 printf("updated tags\n");
429 /* also always true */
430 mi
->supported_tags
[MPD_TAG_ITEM_FILENAME
] = TRUE
;
431 mi
->supported_tags
[MPD_TAG_ITEM_ANY
] = TRUE
;
433 /* If permission updates, we should also call an output update, The data might be available now. */
434 mi
->the_status_changed_callback( mi
,
435 MPD_CST_PERMISSION
|MPD_CST_OUTPUT
, mi
->the_status_changed_signal_userdata
);
441 int mpd_set_port(MpdObj
*mi
, int port
)
445 debug_printf(DEBUG_ERROR
, "mi == NULL\n");
446 return MPD_ARGS_ERROR
;
452 int mpd_set_connection_timeout(MpdObj
*mi
, float timeout
)
456 debug_printf(DEBUG_ERROR
, "mi == NULL\n");
457 return MPD_ARGS_ERROR
;
459 mi
->connection_timeout
= timeout
;
460 if(mpd_check_connected(mi
))
462 /*TODO: set timeout */
463 if(mpd_lock_conn(mi
))
465 debug_printf(DEBUG_ERROR
,"lock failed\n");
466 return MPD_LOCK_FAILED
;
468 mpd_setConnectionTimeout(mi
->connection
, timeout
);
469 mpd_finishCommand(mi
->connection
);
477 static void mpd_server_free_commands(MpdObj
*mi
)
482 while(mi
->commands
[i
].command_name
)
484 free(mi
->commands
[i
].command_name
);
492 char *mpd_server_get_version(MpdObj
*mi
)
495 if(!mi
|| !mpd_check_connected(mi
))
497 retval
= malloc(10*sizeof(char));
498 snprintf(retval
,10,"%i.%i.%i", mi
->connection
->version
[0], mi
->connection
->version
[1], mi
->connection
->version
[2]);
499 /* always make sure the string is terminated */
504 int mpd_server_get_allowed_commands(MpdObj
*mi
)
507 int num_commands
= 0;
509 debug_printf(DEBUG_ERROR
, "mi != NULL failed\n");
510 return MPD_ARGS_ERROR
;
512 if(!mpd_check_connected(mi
)) {
513 debug_printf(DEBUG_WARNING
, "Not Connected");
514 return MPD_NOT_CONNECTED
;
516 if(!mpd_server_check_version(mi
,0,12,0)){
517 debug_printf(DEBUG_INFO
, "Not supported by mpd");
518 return MPD_SERVER_NOT_SUPPORTED
;
521 mpd_server_free_commands(mi
);
523 if(mpd_lock_conn(mi
))
525 debug_printf(DEBUG_ERROR
, "lock failed");
526 return MPD_LOCK_FAILED
;
528 mpd_sendCommandsCommand(mi
->connection
);
529 while((temp
= mpd_getNextCommand(mi
->connection
)))
532 mi
->commands
= realloc(mi
->commands
, (num_commands
+1)*sizeof(MpdCommand
));
533 mi
->commands
[num_commands
-1].command_name
= temp
;
534 mi
->commands
[num_commands
-1].enabled
= TRUE
;
535 mi
->commands
[num_commands
].command_name
= NULL
;
536 mi
->commands
[num_commands
].enabled
= FALSE
;
537 if(strcmp(mi
->commands
[num_commands
-1].command_name
, "idle") == 0) {
541 mpd_finishCommand(mi
->connection
);
542 mpd_sendNotCommandsCommand(mi
->connection
);
543 while((temp
= mpd_getNextCommand(mi
->connection
)))
546 mi
->commands
= realloc(mi
->commands
, (num_commands
+1)*sizeof(MpdCommand
));
547 mi
->commands
[num_commands
-1].command_name
= temp
;
548 mi
->commands
[num_commands
-1].enabled
= FALSE
;
549 mi
->commands
[num_commands
].command_name
= NULL
;
550 mi
->commands
[num_commands
].enabled
= FALSE
;
552 mpd_finishCommand(mi
->connection
);
554 if(mpd_unlock_conn(mi
))
556 return MPD_LOCK_FAILED
;
563 int mpd_disconnect(MpdObj
*mi
)
568 debug_printf(DEBUG_INFO
, "disconnecting\n");
572 mpd_closeConnection(mi
->connection
);
573 mi
->connection
= NULL
;
577 mpd_freeStatus(mi
->status
);
582 mpd_freeStats(mi
->stats
);
587 mpd_freeSong(mi
->CurrentSong
);
588 mi
->CurrentSong
= NULL
;
592 g_strfreev(mi
->url_handlers
);
593 mi
->url_handlers
= NULL
;
595 mi
->CurrentState
.playlistid
= -1;
596 mi
->CurrentState
.storedplaylistid
= -1;
597 mi
->CurrentState
.state
= -1;
598 mi
->CurrentState
.songid
= -1;
599 mi
->CurrentState
.songpos
= -1;
600 mi
->CurrentState
.nextsongid
= -1;
601 mi
->CurrentState
.nextsongpos
= -1;
602 mi
->CurrentState
.dbUpdateTime
= 0;
603 mi
->CurrentState
.updatingDb
= 0;
604 mi
->CurrentState
.repeat
= -1;
605 mi
->CurrentState
.random
= -1;
606 mi
->CurrentState
.volume
= -2;
607 mi
->CurrentState
.xfade
= -1;
608 mi
->CurrentState
.totaltime
= 0;
609 mi
->CurrentState
.elapsedtime
= 0;
610 mi
->CurrentState
.bitrate
= 0;
611 mi
->CurrentState
.samplerate
= 0;
612 mi
->CurrentState
.channels
= 0;
613 mi
->CurrentState
.bits
= 0;
614 mi
->CurrentState
.playlistLength
= 0;
615 mi
->CurrentState
.error
[0] = '\0';
617 mi
->search_type
= MPD_SEARCH_TYPE_NONE
;
618 /* no need to initialize, but set it to anything anyway*/
619 mi
->search_field
= MPD_TAG_ITEM_ARTIST
;
622 if(mi
->output_states
)
623 g_free(mi
->output_states
);
624 mi
->output_states
= NULL
;
627 memset((mi
->supported_tags
), 0,sizeof(mi
->supported_tags
));
631 memcpy(&(mi
->OldState
), &(mi
->CurrentState
) , sizeof(MpdServerState
));
633 mpd_free_queue_ob(mi
);
634 mpd_server_free_commands(mi
);
635 /*don't reset errors */
636 /* Remove this signal, we don't actually disconnect */
639 /* set disconnect flag */
640 mi
->connected
= FALSE
;
642 if(mi
->the_connection_changed_callback
!= NULL
)
644 mi
->the_connection_changed_callback( mi
, FALSE
, mi
->the_connection_changed_signal_userdata
);
647 debug_printf(DEBUG_INFO
, "Disconnect completed\n");
650 int mpd_connect(MpdObj
*mi
)
652 return mpd_connect_real(mi
,NULL
);
654 int mpd_connect_real(MpdObj
*mi
,mpd_Connection
*connection
)
659 /* should return some spiffy error here */
660 debug_printf(DEBUG_ERROR
, "mi != NULL failed");
661 return MPD_ARGS_ERROR
;
665 mi
->error_mpd_code
= 0;
666 if(mi
->error_msg
!= NULL
)
670 mi
->error_msg
= NULL
;
672 debug_printf(DEBUG_INFO
, "connecting\n");
673 mpd_init_MpdServerState(&(mi
->CurrentState
));
675 memcpy(&(mi
->OldState
), &(mi
->CurrentState
), sizeof(MpdServerState
));
683 if(mi
->hostname
== NULL
)
685 mpd_set_hostname(mi
, "localhost");
687 /* make sure this is locked */
688 if(!mi
->connection_lock
)
693 mi
->connection
= connection
;
695 /* make timeout configurable */
696 mi
->connection
= mpd_newConnection(mi
->hostname
,mi
->port
,mi
->connection_timeout
);
698 if(mi
->connection
== NULL
)
700 /* TODO: make seperate error message? */
701 return MPD_NOT_CONNECTED
;
703 if(mpd_check_error(mi
) != MPD_OK
)
705 /* TODO: make seperate error message? */
706 return MPD_NOT_CONNECTED
;
709 /* set connected state */
710 mi
->connected
= TRUE
;
711 if(mpd_unlock_conn(mi
))
713 return MPD_LOCK_FAILED
;
716 /* get the commands we are allowed to use */
717 retv
= mpd_server_get_allowed_commands(mi
);
722 if(mi
->password
&& strlen(mi
->password
) > 0)
724 mpd_send_password(mi
);
729 char **retv
= mpd_server_get_tag_types(mi
);
731 for(i
=0;i
<MPD_TAG_ITEM_ANY
;i
++)
734 for(j
=0;retv
[j
] && strcasecmp(retv
[j
],mpdTagItemKeys
[i
]); j
++);
735 if(retv
[j
]) mi
->supported_tags
[i
] = TRUE
;
736 else mi
->supported_tags
[i
] = FALSE
;
740 /* also always true */
741 mi
->supported_tags
[MPD_TAG_ITEM_FILENAME
] = TRUE
;
742 mi
->supported_tags
[MPD_TAG_ITEM_ANY
] = TRUE
;
746 retv = mpd_server_update_outputs(mi);
751 retv
= mpd_server_update_outputs(mi
);
752 /** update the supported tags */
753 debug_printf(DEBUG_INFO
, "Propagating connection changed");
755 if(mi
->the_connection_changed_callback
!= NULL
)
757 mi
->the_connection_changed_callback( mi
, TRUE
, mi
->the_connection_changed_signal_userdata
);
763 debug_printf(DEBUG_INFO
, "Connected to mpd");
767 int mpd_check_connected(MpdObj
*mi
)
773 return mi
->connected
;
778 void mpd_signal_connect_status_changed (MpdObj
*mi
, StatusChangedCallback status_changed
, void *userdata
)
782 debug_printf(DEBUG_ERROR
, "mi != NULL failed");
785 mi
->the_status_changed_callback
= status_changed
;
786 mi
->the_status_changed_signal_userdata
= userdata
;
790 void mpd_signal_connect_error(MpdObj
*mi
, ErrorCallback error_callback
, void *userdata
)
794 debug_printf(DEBUG_ERROR
, "mi != NULL failed");
797 mi
->the_error_callback
= error_callback
;
798 mi
->the_error_signal_userdata
= userdata
;
801 void mpd_signal_connect_connection_changed(MpdObj
*mi
, ConnectionChangedCallback connection_changed
, void *userdata
)
805 debug_printf(DEBUG_ERROR
, "mi != NULL failed");
808 mi
->the_connection_changed_callback
= connection_changed
;
809 mi
->the_connection_changed_signal_userdata
= userdata
;
815 MpdData
*mpd_new_data_struct(void)
818 data
= g_slice_new(MpdData_real
);
824 data
->directory
= NULL
;
825 data
->playlist
= NULL
;
826 data
->output_dev
= NULL
;
831 data
->userdata
= NULL
;
832 data
->freefunc
= NULL
;
833 return (MpdData
*)data
;
836 MpdData
*mpd_new_data_struct_append(MpdData
* data
)
838 MpdData_real
*data_real
= (MpdData_real
*)data
;
839 if(data_real
== NULL
)
841 data_real
= (MpdData_real
*)mpd_new_data_struct();
842 data_real
->first
= data_real
;
846 data_real
->next
= (MpdData_real
*)mpd_new_data_struct();
847 data_real
->next
->prev
= data_real
;
848 data_real
= data_real
->next
;
849 data_real
->next
= NULL
;
850 data_real
->first
= data_real
->prev
->first
;
852 return (MpdData
*)data_real
;
855 MpdData
* mpd_data_get_first(MpdData
const * const data
)
857 MpdData_real
const * const data_real
= (MpdData_real
const * const)data
;
858 if(data_real
!= NULL
)
860 return (MpdData
*)data_real
->first
;
866 MpdData
* mpd_data_get_next(MpdData
* const data
)
868 return mpd_data_get_next_real(data
, TRUE
);
871 MpdData
* mpd_data_get_next_real(MpdData
* const data
, int kill_list
)
873 MpdData_real
*data_real
= (MpdData_real
*)data
;
874 if (data_real
!= NULL
)
876 if (data_real
->next
!= NULL
)
878 return (MpdData
*)data_real
->next
;
882 if (kill_list
) mpd_data_free((MpdData
*)data_real
);
886 return (MpdData
*)data_real
;
889 int mpd_data_is_last(MpdData
const * const data
)
891 MpdData_real
const * const data_real
= (MpdData_real
const * const)data
;
892 if(data_real
!= NULL
)
894 if (data_real
->next
== NULL
)
902 MpdData_head *mpd_data_get_head(MpdData const * const data) {
903 return ((MpdData_real*)data)->head;
906 MpdData
* mpd_data_concatenate( MpdData
* const first
, MpdData
* const second
)
908 MpdData_real
*first_real
= (MpdData_real
*)first
;
909 MpdData_real
*second_real
= (MpdData_real
*)second
;
910 MpdData_real
*first_head
= NULL
;
912 if ( first
== NULL
) {
913 if ( second
!= NULL
)
914 return (MpdData
*)second_real
;
918 if ( second
== NULL
)
919 return (MpdData
*)first_real
;
922 first_head
= (MpdData_real
*)mpd_data_get_first(first
);
924 /* find last element in first data list */
925 while (!mpd_data_is_last((MpdData
*)first_real
)) first_real
= (MpdData_real
*)mpd_data_get_next_real((MpdData
*)first_real
, FALSE
);
926 second_real
=(MpdData_real
*) mpd_data_get_first((MpdData
*)second_real
);
928 first_real
->next
= second_real
;
929 second_real
->prev
= first_real
;
931 /* I need to set all the -> first correct */
934 second_real
->first
= first_head
;
935 second_real
= (MpdData_real
*)mpd_data_get_next_real((MpdData
*)second_real
, FALSE
);
938 return (MpdData
*)first_head
;
941 * Deletes an item from the list. It returns the next item in the list.
942 * if that is not available, it will return the last item
944 MpdData
* mpd_data_delete_item(MpdData
*data
)
946 MpdData_real
*temp
= NULL
, *data_real
= (MpdData_real
*)data
;
947 if(data_real
== NULL
) return NULL
;
948 /* if there is a next item, fix the prev pointer of the next item */
951 data_real
->next
->prev
= data_real
->prev
;
952 temp
= data_real
->next
;
954 /* if there is a previous item, fix the next pointer of the previous item */
957 /* the next item of the previous is the next item of the current */
958 data_real
->prev
->next
= data_real
->next
;
959 /* temp is the previous item */
960 temp
= data_real
->prev
;
963 /* fix first, if removed item is the first */
964 if(temp
&& temp
->first
== data_real
)
966 MpdData_real
*first
,*node
= temp
;
968 for(;node
->prev
;node
= node
->prev
);
975 /* make the removed row a valid list, so I can use the default free function to free it */
976 data_real
->next
= NULL
;
977 data_real
->prev
= NULL
;
978 data_real
->first
= data_real
;
980 mpd_data_free((MpdData
*)data_real
);
982 return (MpdData
*)temp
;
985 void mpd_data_free(MpdData
*data
)
987 MpdData_real
*data_real
,*temp
;
990 debug_printf(DEBUG_ERROR
, "data != NULL Failed");
993 data_real
= (MpdData_real
*)mpd_data_get_first(data
);
996 if (data_real
->type
== MPD_DATA_TYPE_SONG
) {
997 if(data_real
->song
) mpd_freeSong(data_real
->song
);
998 } else if (data_real
->type
== MPD_DATA_TYPE_OUTPUT_DEV
) {
999 mpd_freeOutputElement(data_real
->output_dev
);
1000 } else if(data_real
->type
== MPD_DATA_TYPE_DIRECTORY
) {
1001 if(data_real
->directory
)free(data_real
->directory
);
1002 } else if(data_real
->type
== MPD_DATA_TYPE_PLAYLIST
) {
1003 if(data_real
->playlist
) mpd_freePlaylistFile(data_real
->playlist
);
1005 free((void*)(data_real
->tag
));
1007 if(data_real
->freefunc
)
1009 if(data_real
->userdata
)
1010 data_real
->freefunc(data_real
->userdata
);
1012 data_real
= data_real
->next
;
1013 g_slice_free1(sizeof(*temp
), temp
);
1017 /* clean this up.. make one while loop */
1018 static void mpd_free_queue_ob(MpdObj
*mi
)
1020 MpdQueue
*temp
= NULL
;
1023 debug_printf(DEBUG_ERROR
, "mi != NULL failed");
1026 if(mi
->queue
== NULL
)
1028 debug_printf(DEBUG_INFO
, "mi->queue != NULL failed, nothing to clean.");
1031 mi
->queue
= mi
->queue
->first
;
1032 while(mi
->queue
!= NULL
)
1034 temp
= mi
->queue
->next
;
1036 if(mi
->queue
->path
!= NULL
)
1038 free(mi
->queue
->path
);
1048 MpdQueue
*mpd_new_queue_struct()
1050 MpdQueue
* queue
= malloc(sizeof(MpdQueue
));
1060 void mpd_queue_get_next(MpdObj
*mi
)
1062 if(mi
->queue
!= NULL
&& mi
->queue
->next
!= NULL
)
1064 mi
->queue
= mi
->queue
->next
;
1066 else if(mi
->queue
->next
== NULL
)
1068 mpd_free_queue_ob(mi
);
1073 long unsigned mpd_server_get_database_update_time(MpdObj
*mi
)
1075 if(!mpd_check_connected(mi
))
1077 debug_printf(DEBUG_WARNING
,"not connected\n");
1078 return MPD_NOT_CONNECTED
;
1080 if(mpd_stats_check(mi
) != MPD_OK
)
1082 debug_printf(DEBUG_WARNING
,"Failed grabbing status\n");
1083 return MPD_STATS_FAILED
;
1085 return mi
->stats
->dbUpdateTime
;
1089 MpdData
* mpd_server_get_output_devices(MpdObj
*mi
)
1091 mpd_OutputEntity
*output
= NULL
;
1092 MpdData
*data
= NULL
;
1093 if(!mpd_check_connected(mi
))
1095 debug_printf(DEBUG_WARNING
,"not connected\n");
1098 /* TODO: Check version */
1099 if(mpd_lock_conn(mi
))
1101 debug_printf(DEBUG_ERROR
,"lock failed\n");
1105 mpd_sendOutputsCommand(mi
->connection
);
1106 while (( output
= mpd_getNextOutput(mi
->connection
)) != NULL
)
1108 data
= mpd_new_data_struct_append(data
);
1109 data
->type
= MPD_DATA_TYPE_OUTPUT_DEV
;
1110 data
->output_dev
= output
;
1112 mpd_finishCommand(mi
->connection
);
1115 if(mpd_unlock_conn(mi
) != MPD_OK
)
1117 if(data
)mpd_data_free(data
);
1124 return mpd_data_get_first(data
);
1127 int mpd_server_set_output_device(MpdObj
*mi
,int device_id
,int state
)
1129 if(!mpd_check_connected(mi
))
1131 debug_printf(DEBUG_WARNING
,"not connected\n");
1132 return MPD_NOT_CONNECTED
;
1134 if(mpd_lock_conn(mi
))
1136 debug_printf(DEBUG_ERROR
,"lock failed\n");
1137 return MPD_LOCK_FAILED
;
1141 mpd_sendEnableOutputCommand(mi
->connection
, device_id
);
1145 mpd_sendDisableOutputCommand(mi
->connection
, device_id
);
1147 mpd_finishCommand(mi
->connection
);
1149 mpd_unlock_conn(mi
);
1150 mpd_status_queue_update(mi
);
1154 int mpd_server_check_version(MpdObj
*mi
, int major
, int minor
, int micro
)
1156 if(!mpd_check_connected(mi
))
1158 debug_printf(DEBUG_WARNING
,"not connected\n");
1161 if(major
> mi
->connection
->version
[0]) return FALSE
;
1162 if(mi
->connection
->version
[0] > major
) return TRUE
;
1163 if(minor
> mi
->connection
->version
[1]) return FALSE
;
1164 if(mi
->connection
->version
[1] > minor
) return TRUE
;
1165 if(micro
> mi
->connection
->version
[2]) return FALSE
;
1166 if(mi
->connection
->version
[2] > micro
) return TRUE
;
1170 int mpd_server_check_command_allowed(MpdObj
*mi
, const char *command
)
1173 if(!mi
|| !command
) return MPD_SERVER_COMMAND_ERROR
;
1174 /* when we are connected to a mpd server that doesn't support commands and not commands
1175 * feature. (like mpd 0.11.5) allow everything
1177 if(!mpd_server_check_version(mi
, 0,12,0)) return MPD_SERVER_COMMAND_ALLOWED
;
1179 * Also when somehow we failted to get commands
1181 if(mi
->commands
== NULL
) return MPD_SERVER_COMMAND_ALLOWED
;
1185 for(i
=0;mi
->commands
[i
].command_name
;i
++)
1187 if(!strcasecmp(mi
->commands
[i
].command_name
, command
))
1188 return mi
->commands
[i
].enabled
;
1190 return MPD_SERVER_COMMAND_NOT_SUPPORTED
;
1193 char ** mpd_server_get_url_handlers(MpdObj
*mi
)
1198 if(!mpd_check_connected(mi
))
1200 debug_printf(DEBUG_WARNING
,"not connected\n");
1203 if(mi
->url_handlers
){
1204 return g_strdupv(mi
->url_handlers
);
1206 if(mpd_lock_conn(mi
))
1208 debug_printf(DEBUG_ERROR
,"lock failed\n");
1212 * Fetch url handlers and store them
1214 mpd_sendUrlHandlersCommand(mi
->connection
);
1215 while((temp
= mpd_getNextHandler(mi
->connection
)) != NULL
)
1217 mi
->url_handlers
= realloc(mi
->url_handlers
,(i
+2)*sizeof(*mi
->url_handlers
));
1218 mi
->url_handlers
[i
] = temp
;
1219 mi
->url_handlers
[i
+1] = NULL
;
1222 mpd_finishCommand(mi
->connection
);
1225 mpd_unlock_conn(mi
);
1227 return g_strdupv(mi
->url_handlers
);
1229 char ** mpd_server_get_tag_types(MpdObj
*mi
)
1234 if(!mpd_check_connected(mi
))
1236 debug_printf(DEBUG_WARNING
,"not connected\n");
1239 if(mpd_lock_conn(mi
))
1241 debug_printf(DEBUG_ERROR
,"lock failed\n");
1244 mpd_sendTagTypesCommand(mi
->connection
);
1245 while((temp
= mpd_getNextTagType(mi
->connection
)) != NULL
)
1247 retv
= realloc(retv
,(i
+2)*sizeof(*retv
));
1252 mpd_finishCommand(mi
->connection
);
1255 mpd_unlock_conn(mi
);
1259 int mpd_misc_get_tag_by_name(char *name
)
1264 return MPD_ARGS_ERROR
;
1266 for(i
=0; i
< MPD_TAG_NUM_OF_ITEM_TYPES
; i
++)
1268 if(!strcasecmp(mpdTagItemKeys
[i
], name
))
1273 return MPD_TAG_NOT_FOUND
;
1276 int mpd_server_update_outputs(MpdObj
*mi
)
1278 mpd_OutputEntity
*output
= NULL
;
1279 if(!mpd_check_connected(mi
))
1281 debug_printf(DEBUG_WARNING
,"not connected\n");
1282 return MPD_NOT_CONNECTED
;
1284 if(mpd_lock_conn(mi
))
1286 debug_printf(DEBUG_ERROR
,"lock failed\n");
1287 return MPD_LOCK_FAILED
;
1289 mpd_sendOutputsCommand(mi
->connection
);
1290 while (( output
= mpd_getNextOutput(mi
->connection
)) != NULL
)
1293 mi
->output_states
= realloc(mi
->output_states
,mi
->num_outputs
*sizeof(int));
1294 mi
->output_states
[mi
->num_outputs
-1] = FALSE
;/*output->enabled;*/
1295 mpd_freeOutputElement(output
);
1297 mpd_finishCommand(mi
->connection
);
1298 return mpd_unlock_conn(mi
);
1301 int mpd_server_has_idle(MpdObj
*mi
)
1303 return mi
->has_idle
;
1306 int mpd_server_tag_supported(MpdObj
*mi
, int tag
)
1308 if(!mi
) return FALSE
;
1309 if(tag
< 0 || tag
>= MPD_TAG_NUM_OF_ITEM_TYPES
) {
1312 return mi
->supported_tags
[tag
];