2 *Copyright (C) 2004-2007 Qball Cow <Qball@qballcow.nl>
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public
15 * License along with this program; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
29 #include "debug_printf.h"
32 #include "libmpd-internal.h"
34 static void mpd_free_queue_ob(MpdObj
*mi
);
35 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;
70 state
->storedplaylistid
= -1;
74 state
->dbUpdateTime
= 0;
75 state
->updatingDb
= 0;
81 state
->elapsedtime
= 0;
83 state
->samplerate
= 0;
86 state
->playlistLength
= 0;
87 state
->error
[0] = '\0';
92 static MpdObj
* mpd_create()
94 MpdObj
* mi
= malloc(sizeof(*mi
));
97 /* should never happen on linux */
102 /* set default values */
103 /* we start not connected */
104 mi
->connected
= FALSE
;
105 /* port 6600 is the default mpd port */
108 mi
->hostname
= strdup("localhost");
111 /* 1 second timeout */
112 mi
->connection_timeout
= 1.0;
113 /* we have no connection pointer */
114 mi
->connection
= NULL
;
120 mi
->error_mpd_code
= 0;
121 mi
->error_msg
= NULL
;
122 mi
->CurrentSong
= NULL
;
124 mpd_init_MpdServerState(&(mi
->CurrentState
));
125 mpd_init_MpdServerState(&(mi
->OldState
));
128 * Set signals to NULL
130 /* connection changed signal */
131 mi
->the_connection_changed_callback
= NULL
;
132 mi
->the_connection_changed_signal_userdata
= NULL
;
135 mi
->the_status_changed_callback
= NULL
;
136 mi
->the_status_changed_signal_userdata
= NULL
;
139 mi
->the_error_callback
= NULL
;
140 mi
->the_error_signal_userdata
= NULL
;
141 /* connection is locked because where not connected */
142 mi
->connection_lock
= TRUE
;
144 /* set queue to NULL */
147 mi
->search_type
= MPD_SEARCH_TYPE_NONE
;
148 /* no need to initialize, but set it to anything anyway*/
149 mi
->search_field
= MPD_TAG_ITEM_ARTIST
;
156 void mpd_free(MpdObj
*mi
)
158 debug_printf(DEBUG_INFO
, "destroying MpdObj object\n");
162 debug_printf(DEBUG_WARNING
, "Connection still running, disconnecting\n");
180 mpd_closeConnection(mi
->connection
);
184 mpd_freeStatus(mi
->status
);
188 mpd_freeStats(mi
->stats
);
192 mpd_freeSong(mi
->CurrentSong
);
194 mpd_free_queue_ob(mi
);
195 mpd_server_free_commands(mi
);
199 int mpd_check_error(MpdObj
*mi
)
203 debug_printf(DEBUG_ERROR
, "mi == NULL?");
204 return MPD_ARGS_ERROR
;
207 /* this shouldn't happen, ever */
208 if(mi
->connection
== NULL
)
210 debug_printf(DEBUG_ERROR
, "mi->connection == NULL?");
211 return MPD_FATAL_ERROR
;
214 /* TODO: map these errors in the future */
215 mi
->error
= mi
->connection
->error
;
216 mi
->error_mpd_code
= mi
->connection
->errorCode
;
217 /*TODO: do I need to strdup this? */
218 mi
->error_msg
= strdup(mi
->connection
->errorStr
);
220 /* Check for permission */
221 /* First check for an error reported by MPD
222 * Then check what type of error mpd reported
224 if(mi
->error
== MPD_ERROR_ACK
)
227 debug_printf(DEBUG_ERROR
,"clearing errors in mpd_Connection: %i-%s", mi
->connection
->errorCode
, mi
->connection
->errorStr
);
228 mpd_clearError(mi
->connection
);
229 if (mi
->the_error_callback
)
231 debug_printf(DEBUG_ERROR
, "Error callback 1 (ACK)");
232 mi
->the_error_callback(mi
, mi
->error_mpd_code
, mi
->error_msg
, mi
->the_error_signal_userdata
);
235 mi
->error_msg
= NULL
;
241 debug_printf(DEBUG_ERROR
, "Following error occured: %i: code: %i msg: %s", mi
->error
,mi
->connection
->errorCode
, mi
->error_msg
);
243 if (mi
->the_error_callback
)
245 debug_printf(DEBUG_ERROR
, "Error callback 2");
246 mi
->the_error_callback(mi
, mi
->error
, mi
->error_msg
, mi
->the_error_signal_userdata
);
250 mi
->error_msg
= NULL
;
252 return MPD_SERVER_ERROR
;
255 mi
->error_msg
= NULL
;
261 int mpd_lock_conn(MpdObj
*mi
)
264 if(mi
->connection_lock
)
266 debug_printf(DEBUG_WARNING
, "Failed to lock connection, already locked\n");
267 return MPD_LOCK_FAILED
;
269 mi
->connection_lock
= TRUE
;
273 int mpd_unlock_conn(MpdObj
*mi
)
275 if(!mi
->connection_lock
)
277 debug_printf(DEBUG_ERROR
, "Failed to unlock connection, already unlocked\n");
278 return MPD_LOCK_FAILED
;
281 mi
->connection_lock
= FALSE
;
283 return mpd_check_error(mi
);
286 MpdObj
* mpd_new_default()
288 debug_printf(DEBUG_INFO
, "creating a new mpdInt object\n");
292 MpdObj
*mpd_new(char *hostname
, int port
, char *password
)
294 MpdObj
*mi
= mpd_create();
301 mpd_set_hostname(mi
, hostname
);
305 mpd_set_port(mi
, port
);
309 mpd_set_password(mi
, password
);
315 const char * mpd_get_hostname(MpdObj
*mi
)
324 int mpd_set_hostname(MpdObj
*mi
, char *hostname
)
328 debug_printf(DEBUG_ERROR
, "mi == NULL\n");
329 return MPD_ARGS_ERROR
;
332 if(mi
->hostname
!= NULL
)
336 /* possible location todo some post processing of hostname */
337 mi
->hostname
= strdup(hostname
);
341 int mpd_set_password(MpdObj
*mi
, char *password
)
345 debug_printf(DEBUG_ERROR
, "mi == NULL\n");
346 return MPD_ARGS_ERROR
;
349 if(mi
->password
!= NULL
)
353 /* possible location todo some post processing of password */
354 mi
->password
= strdup(password
);
359 int mpd_send_password(MpdObj
*mi
)
361 if(!mi
) return MPD_ARGS_ERROR
;
362 if(mi
->password
&& mpd_check_connected(mi
) && strlen(mi
->password
))
364 if(mpd_lock_conn(mi
))
366 debug_printf(DEBUG_WARNING
, "failed to lock connection");
367 return MPD_LOCK_FAILED
;
369 mpd_sendPasswordCommand(mi
->connection
, mi
->password
);
370 mpd_finishCommand(mi
->connection
);
371 if(mpd_unlock_conn(mi
))
373 debug_printf(DEBUG_ERROR
, "Failed to unlock connection\n");
374 return MPD_LOCK_FAILED
;
376 mpd_server_get_allowed_commands(mi
);
377 /*TODO: should I do it here, or in the
378 * mpd_server_get_allowed_command, so it also get's executed on
381 if((mi
->the_status_changed_callback
!= NULL
))
383 mi
->the_status_changed_callback( mi
,
384 MPD_CST_PERMISSION
, mi
->the_status_changed_signal_userdata
);
390 int mpd_set_port(MpdObj
*mi
, int port
)
394 debug_printf(DEBUG_ERROR
, "mi == NULL\n");
395 return MPD_ARGS_ERROR
;
401 int mpd_set_connection_timeout(MpdObj
*mi
, float timeout
)
405 debug_printf(DEBUG_ERROR
, "mi == NULL\n");
406 return MPD_ARGS_ERROR
;
408 mi
->connection_timeout
= timeout
;
409 if(mpd_check_connected(mi
))
411 /*TODO: set timeout */
412 if(mpd_lock_conn(mi
))
414 debug_printf(DEBUG_ERROR
,"lock failed\n");
415 return MPD_LOCK_FAILED
;
417 mpd_setConnectionTimeout(mi
->connection
, timeout
);
418 mpd_finishCommand(mi
->connection
);
426 static void mpd_server_free_commands(MpdObj
*mi
)
431 while(mi
->commands
[i
].command_name
)
433 free(mi
->commands
[i
].command_name
);
441 char *mpd_server_get_version(MpdObj
*mi
)
444 if(!mi
|| !mpd_check_connected(mi
))
446 retval
= malloc(10*sizeof(char));
447 snprintf(retval
,10,"%i.%i.%i", mi
->connection
->version
[0], mi
->connection
->version
[1], mi
->connection
->version
[2]);
448 /* always make sure the string is terminated */
453 int mpd_server_get_allowed_commands(MpdObj
*mi
)
456 int num_commands
= 0;
458 debug_printf(DEBUG_ERROR
, "mi != NULL failed\n");
459 return MPD_ARGS_ERROR
;
461 if(!mpd_check_connected(mi
)) {
462 debug_printf(DEBUG_WARNING
, "Not Connected");
463 return MPD_NOT_CONNECTED
;
465 if(!mpd_server_check_version(mi
,0,12,0)){
466 debug_printf(DEBUG_INFO
, "Not supported by mpd");
467 return MPD_SERVER_NOT_SUPPORTED
;
470 mpd_server_free_commands(mi
);
472 if(mpd_lock_conn(mi
))
474 debug_printf(DEBUG_ERROR
, "lock failed");
475 return MPD_LOCK_FAILED
;
477 mpd_sendCommandsCommand(mi
->connection
);
478 while((temp
= mpd_getNextCommand(mi
->connection
)))
481 mi
->commands
= realloc(mi
->commands
, (num_commands
+1)*sizeof(MpdCommand
));
482 mi
->commands
[num_commands
-1].command_name
= temp
;
483 mi
->commands
[num_commands
-1].enabled
= TRUE
;
484 mi
->commands
[num_commands
].command_name
= NULL
;
485 mi
->commands
[num_commands
].enabled
= FALSE
;
487 mpd_finishCommand(mi
->connection
);
488 mpd_sendNotCommandsCommand(mi
->connection
);
489 while((temp
= mpd_getNextCommand(mi
->connection
)))
492 mi
->commands
= realloc(mi
->commands
, (num_commands
+1)*sizeof(MpdCommand
));
493 mi
->commands
[num_commands
-1].command_name
= temp
;
494 mi
->commands
[num_commands
-1].enabled
= FALSE
;
495 mi
->commands
[num_commands
].command_name
= NULL
;
496 mi
->commands
[num_commands
].enabled
= FALSE
;
498 mpd_finishCommand(mi
->connection
);
506 int mpd_disconnect(MpdObj
*mi
)
511 debug_printf(DEBUG_INFO
, "disconnecting\n");
515 mpd_closeConnection(mi
->connection
);
516 mi
->connection
= NULL
;
520 mpd_freeStatus(mi
->status
);
525 mpd_freeStats(mi
->stats
);
530 mpd_freeSong(mi
->CurrentSong
);
531 mi
->CurrentSong
= NULL
;
533 mi
->CurrentState
.playlistid
= -1;
534 mi
->CurrentState
.queueid
= -2;
535 mi
->CurrentState
.storedplaylistid
= -1;
536 mi
->CurrentState
.state
= -1;
537 mi
->CurrentState
.songid
= -1;
538 mi
->CurrentState
.songpos
= -1;
539 mi
->CurrentState
.dbUpdateTime
= 0;
540 mi
->CurrentState
.updatingDb
= 0;
541 mi
->CurrentState
.repeat
= -1;
542 mi
->CurrentState
.random
= -1;
543 mi
->CurrentState
.volume
= -2;
544 mi
->CurrentState
.xfade
= -1;
545 mi
->CurrentState
.totaltime
= 0;
546 mi
->CurrentState
.elapsedtime
= 0;
547 mi
->CurrentState
.bitrate
= 0;
548 mi
->CurrentState
.samplerate
= 0;
549 mi
->CurrentState
.channels
= 0;
550 mi
->CurrentState
.bits
= 0;
551 mi
->CurrentState
.playlistLength
= 0;
552 mi
->CurrentState
.error
[0] = '\0';
554 mi
->search_type
= MPD_SEARCH_TYPE_NONE
;
555 /* no need to initialize, but set it to anything anyway*/
556 mi
->search_field
= MPD_TAG_ITEM_ARTIST
;
561 memcpy(&(mi
->OldState
), &(mi
->CurrentState
) , sizeof(MpdServerState
));
563 mpd_free_queue_ob(mi
);
564 mpd_server_free_commands(mi
);
565 /*don't reset errors */
566 /* Remove this signal, we don't actually disconnect */
569 /* set disconnect flag */
570 mi
->connected
= FALSE
;
572 if(mi
->the_connection_changed_callback
!= NULL
)
574 mi
->the_connection_changed_callback( mi
, FALSE
, mi
->the_connection_changed_signal_userdata
);
577 debug_printf(DEBUG_INFO
, "Disconnect completed\n");
580 int mpd_connect(MpdObj
*mi
)
582 return mpd_connect_real(mi
,NULL
);
584 int mpd_connect_real(MpdObj
*mi
,mpd_Connection
*connection
)
588 /* should return some spiffy error here */
589 debug_printf(DEBUG_ERROR
, "mi != NULL failed");
590 return MPD_ARGS_ERROR
;
594 mi
->error_mpd_code
= 0;
595 if(mi
->error_msg
!= NULL
)
599 mi
->error_msg
= NULL
;
601 debug_printf(DEBUG_INFO
, "connecting\n");
602 mpd_init_MpdServerState(&(mi
->CurrentState
));
604 memcpy(&(mi
->OldState
), &(mi
->CurrentState
), sizeof(MpdServerState
));
612 if(mi
->hostname
== NULL
)
614 mpd_set_hostname(mi
, "localhost");
616 /* make sure this is locked */
617 if(!mi
->connection_lock
)
622 mi
->connection
= connection
;
624 /* make timeout configurable */
625 mi
->connection
= mpd_newConnection(mi
->hostname
,mi
->port
,mi
->connection_timeout
);
627 if(mi
->connection
== NULL
)
629 /* TODO: make seperate error message? */
630 return MPD_NOT_CONNECTED
;
632 if(mpd_check_error(mi
) != MPD_OK
)
634 /* TODO: make seperate error message? */
635 return MPD_NOT_CONNECTED
;
638 /* set connected state */
639 mi
->connected
= TRUE
;
640 if(mpd_unlock_conn(mi
))
642 return MPD_LOCK_FAILED
;
645 /* get the commands we are allowed to use */
646 mpd_server_get_allowed_commands(mi
);
649 if(mi
->the_connection_changed_callback
!= NULL
)
651 mi
->the_connection_changed_callback( mi
, TRUE
, mi
->the_connection_changed_signal_userdata
);
654 debug_printf(DEBUG_INFO
, "Connected to mpd");
658 int mpd_check_connected(MpdObj
*mi
)
664 return mi
->connected
;
669 void mpd_signal_connect_status_changed (MpdObj
*mi
, StatusChangedCallback status_changed
, void *userdata
)
673 debug_printf(DEBUG_ERROR
, "mi != NULL failed");
676 mi
->the_status_changed_callback
= status_changed
;
677 mi
->the_status_changed_signal_userdata
= userdata
;
681 void mpd_signal_connect_error(MpdObj
*mi
, ErrorCallback error_callback
, void *userdata
)
685 debug_printf(DEBUG_ERROR
, "mi != NULL failed");
688 mi
->the_error_callback
= error_callback
;
689 mi
->the_error_signal_userdata
= userdata
;
692 void mpd_signal_connect_connection_changed(MpdObj
*mi
, ConnectionChangedCallback connection_changed
, void *userdata
)
696 debug_printf(DEBUG_ERROR
, "mi != NULL failed");
699 mi
->the_connection_changed_callback
= connection_changed
;
700 mi
->the_connection_changed_signal_userdata
= userdata
;
706 MpdData
*mpd_new_data_struct(void)
709 data
= g_slice_new(MpdData_real
);
715 data
->directory
= NULL
;
716 data
->playlist
= NULL
;
717 data
->output_dev
= NULL
;
721 return (MpdData
*)data
;
724 MpdData
*mpd_new_data_struct_append(MpdData
* data
)
726 MpdData_real
*data_real
= (MpdData_real
*)data
;
727 if(data_real
== NULL
)
729 data_real
= (MpdData_real
*)mpd_new_data_struct();
730 data_real
->first
= data_real
;
734 data_real
->next
= (MpdData_real
*)mpd_new_data_struct();
735 data_real
->next
->prev
= data_real
;
736 data_real
= data_real
->next
;
737 data_real
->next
= NULL
;
738 data_real
->first
= data_real
->prev
->first
;
740 return (MpdData
*)data_real
;
743 MpdData
* mpd_data_get_first(MpdData
const * const data
)
745 MpdData_real
const * const data_real
= (MpdData_real
const * const)data
;
746 if(data_real
!= NULL
)
748 return (MpdData
*)data_real
->first
;
754 MpdData
* mpd_data_get_next(MpdData
* const data
)
756 return mpd_data_get_next_real(data
, TRUE
);
759 MpdData
* mpd_data_get_next_real(MpdData
* const data
, int kill_list
)
761 MpdData_real
*data_real
= (MpdData_real
*)data
;
762 if (data_real
!= NULL
)
764 if (data_real
->next
!= NULL
)
766 return (MpdData
*)data_real
->next
;
770 if (kill_list
) mpd_data_free((MpdData
*)data_real
);
774 return (MpdData
*)data_real
;
777 int mpd_data_is_last(MpdData
const * const data
)
779 MpdData_real
const * const data_real
= (MpdData_real
const * const)data
;
780 if(data_real
!= NULL
)
782 if (data_real
->next
== NULL
)
790 MpdData_head *mpd_data_get_head(MpdData const * const data) {
791 return ((MpdData_real*)data)->head;
794 MpdData
* mpd_data_concatenate( MpdData
* const first
, MpdData
* const second
)
796 MpdData_real
*first_real
= (MpdData_real
*)first
;
797 MpdData_real
*second_real
= (MpdData_real
*)second
;
798 MpdData_real
*first_head
= NULL
;
800 if ( first
== NULL
) {
801 if ( second
!= NULL
)
802 return (MpdData
*)second_real
;
806 if ( second
== NULL
)
807 return (MpdData
*)first_real
;
810 first_head
= (MpdData_real
*)mpd_data_get_first(first
);
812 /* find last element in first data list */
813 while (!mpd_data_is_last((MpdData
*)first_real
)) first_real
= (MpdData_real
*)mpd_data_get_next_real((MpdData
*)first_real
, FALSE
);
814 second_real
=(MpdData_real
*) mpd_data_get_first((MpdData
*)second_real
);
816 first_real
->next
= second_real
;
817 second_real
->prev
= first_real
;
819 /* I need to set all the -> first correct */
822 second_real
->first
= first_head
;
823 second_real
= (MpdData_real
*)mpd_data_get_next_real((MpdData
*)second_real
, FALSE
);
826 return (MpdData
*)first_head
;
829 * Deletes an item from the list. It returns the next item in the list.
830 * if that is not available, it will return the last item
832 MpdData
* mpd_data_delete_item(MpdData
*data
)
834 MpdData_real
*temp
= NULL
, *data_real
= (MpdData_real
*)data
;
835 if(data_real
== NULL
) return NULL
;
836 /* if there is a next item, fix the prev pointer of the next item */
839 data_real
->next
->prev
= data_real
->prev
;
840 temp
= data_real
->next
;
842 /* if there is a previous item, fix the next pointer of the previous item */
845 /* the next item of the previous is the next item of the current */
846 data_real
->prev
->next
= data_real
->next
;
847 /* temp is the previous item */
848 temp
= data_real
->prev
;
851 /* fix first, if removed item is the first */
852 if(temp
&& temp
->first
== data_real
)
854 MpdData_real
*first
,*node
= temp
;
856 for(;node
->prev
;node
= node
->prev
);
863 /* make the removed row a valid list, so I can use the default free function to free it */
864 data_real
->next
= NULL
;
865 data_real
->prev
= NULL
;
866 data_real
->first
= data_real
;
868 mpd_data_free((MpdData
*)data_real
);
870 return (MpdData
*)temp
;
873 void mpd_data_free(MpdData
*data
)
875 MpdData_real
*data_real
,*temp
;
878 debug_printf(DEBUG_ERROR
, "data != NULL Failed");
881 data_real
= (MpdData_real
*)mpd_data_get_first(data
);
884 if (data_real
->type
== MPD_DATA_TYPE_SONG
) {
885 if(data_real
->song
) mpd_freeSong(data_real
->song
);
886 } else if (data_real
->type
== MPD_DATA_TYPE_OUTPUT_DEV
) {
887 mpd_freeOutputElement(data_real
->output_dev
);
888 } else if(data_real
->type
== MPD_DATA_TYPE_DIRECTORY
) {
889 if(data_real
->directory
)free(data_real
->directory
);
890 } else if(data_real
->type
== MPD_DATA_TYPE_PLAYLIST
) {
891 if(data_real
->playlist
)free(data_real
->playlist
);
893 free((void*)(data_real
->tag
));
895 data_real
= data_real
->next
;
896 g_slice_free1(sizeof(*temp
), temp
);
900 /* clean this up.. make one while loop */
901 static void mpd_free_queue_ob(MpdObj
*mi
)
903 MpdQueue
*temp
= NULL
;
906 debug_printf(DEBUG_ERROR
, "mi != NULL failed");
909 if(mi
->queue
== NULL
)
911 debug_printf(DEBUG_INFO
, "mi->queue != NULL failed, nothing to clean.");
914 mi
->queue
= mi
->queue
->first
;
915 while(mi
->queue
!= NULL
)
917 temp
= mi
->queue
->next
;
919 if(mi
->queue
->path
!= NULL
)
921 free(mi
->queue
->path
);
931 MpdQueue
*mpd_new_queue_struct()
933 MpdQueue
* queue
= malloc(sizeof(MpdQueue
));
943 void mpd_queue_get_next(MpdObj
*mi
)
945 if(mi
->queue
!= NULL
&& mi
->queue
->next
!= NULL
)
947 mi
->queue
= mi
->queue
->next
;
949 else if(mi
->queue
->next
== NULL
)
951 mpd_free_queue_ob(mi
);
956 long unsigned mpd_server_get_database_update_time(MpdObj
*mi
)
958 if(!mpd_check_connected(mi
))
960 debug_printf(DEBUG_WARNING
,"not connected\n");
961 return MPD_NOT_CONNECTED
;
963 if(mpd_stats_check(mi
) != MPD_OK
)
965 debug_printf(DEBUG_WARNING
,"Failed grabbing status\n");
966 return MPD_STATS_FAILED
;
968 return mi
->stats
->dbUpdateTime
;
972 MpdData
* mpd_server_get_output_devices(MpdObj
*mi
)
974 mpd_OutputEntity
*output
= NULL
;
975 MpdData
*data
= NULL
;
976 if(!mpd_check_connected(mi
))
978 debug_printf(DEBUG_WARNING
,"not connected\n");
981 /* TODO: Check version */
982 if(mpd_lock_conn(mi
))
984 debug_printf(DEBUG_ERROR
,"lock failed\n");
988 mpd_sendOutputsCommand(mi
->connection
);
989 while (( output
= mpd_getNextOutput(mi
->connection
)) != NULL
)
991 data
= mpd_new_data_struct_append(data
);
992 data
->type
= MPD_DATA_TYPE_OUTPUT_DEV
;
993 data
->output_dev
= output
;
995 mpd_finishCommand(mi
->connection
);
1003 return mpd_data_get_first(data
);
1006 int mpd_server_set_output_device(MpdObj
*mi
,int device_id
,int state
)
1008 if(!mpd_check_connected(mi
))
1010 debug_printf(DEBUG_WARNING
,"not connected\n");
1011 return MPD_NOT_CONNECTED
;
1013 if(mpd_lock_conn(mi
))
1015 debug_printf(DEBUG_ERROR
,"lock failed\n");
1016 return MPD_LOCK_FAILED
;
1020 mpd_sendEnableOutputCommand(mi
->connection
, device_id
);
1024 mpd_sendDisableOutputCommand(mi
->connection
, device_id
);
1026 mpd_finishCommand(mi
->connection
);
1028 mpd_unlock_conn(mi
);
1029 mpd_status_queue_update(mi
);
1033 int mpd_server_check_version(MpdObj
*mi
, int major
, int minor
, int micro
)
1035 if(!mpd_check_connected(mi
))
1037 debug_printf(DEBUG_WARNING
,"not connected\n");
1040 if(major
> mi
->connection
->version
[0]) return FALSE
;
1041 if(mi
->connection
->version
[0] > major
) return TRUE
;
1042 if(minor
> mi
->connection
->version
[1]) return FALSE
;
1043 if(mi
->connection
->version
[1] > minor
) return TRUE
;
1044 if(micro
> mi
->connection
->version
[2]) return FALSE
;
1045 if(mi
->connection
->version
[2] > micro
) return TRUE
;
1049 int mpd_server_check_command_allowed(MpdObj
*mi
, const char *command
)
1052 if(!mi
|| !command
) return MPD_SERVER_COMMAND_ERROR
;
1053 /* when we are connected to a mpd server that doesn't support commands and not commands
1054 * feature. (like mpd 0.11.5) allow everything
1056 if(!mpd_server_check_version(mi
, 0,12,0)) return MPD_SERVER_COMMAND_ALLOWED
;
1058 * Also when somehow we failted to get commands
1060 if(mi
->commands
== NULL
) return MPD_SERVER_COMMAND_ALLOWED
;
1064 for(i
=0;mi
->commands
[i
].command_name
;i
++)
1066 if(!strcasecmp(mi
->commands
[i
].command_name
, command
))
1067 return mi
->commands
[i
].enabled
;
1069 return MPD_SERVER_COMMAND_NOT_SUPPORTED
;
1072 char ** mpd_server_get_url_handlers(MpdObj
*mi
)
1077 if(!mpd_check_connected(mi
))
1079 debug_printf(DEBUG_WARNING
,"not connected\n");
1082 if(mpd_lock_conn(mi
))
1084 debug_printf(DEBUG_ERROR
,"lock failed\n");
1087 mpd_sendUrlHandlersCommand(mi
->connection
);
1088 while((temp
= mpd_getNextHandler(mi
->connection
)) != NULL
)
1090 retv
= realloc(retv
,(i
+2)*sizeof(*retv
));
1095 mpd_finishCommand(mi
->connection
);
1098 mpd_unlock_conn(mi
);
1105 regex_t
** mpd_misc_tokenize(char *string
)
1107 regex_t
** result
= NULL
; /* the result with tokens */
1108 int i
= 0; /* position in string */
1109 int br
= 0; /* number for open ()[]'s */
1110 int bpos
= 0; /* begin position of the cur. token */
1113 if(string
== NULL
) return NULL
;
1114 for(i
=0; i
< strlen(string
)+1;i
++)
1116 /* check for opening [( */
1117 if(string
[i
] == '(' || string
[i
] == '[' || string
[i
] == '{') br
++;
1119 else if(string
[i
] == ')' || string
[i
] == ']' || string
[i
] == '}') br
--;
1120 /* if multiple spaces at begin of token skip them */
1121 else if(string
[i
] == ' ' && !(i
-bpos
))bpos
++;
1122 /* if token end or string end add token to list */
1123 else if((string
[i
] == ' ' && !br
) || string
[i
] == '\0')
1126 result
= (regex_t
**)realloc(result
,(tokens
+2)*sizeof(regex_t
*));
1127 result
[tokens
] = malloc(sizeof(regex_t
));
1128 temp
= (char *)strndup((const char *)&string
[bpos
], i
-bpos
);
1129 if(regcomp(result
[tokens
], temp
, REG_EXTENDED
|REG_ICASE
|REG_NOSUB
))
1131 result
[tokens
+1] = NULL
;
1132 mpd_misc_tokens_free(result
);
1136 result
[tokens
+1] = NULL
;
1145 void mpd_misc_tokens_free(regex_t
** tokens
)
1148 if(tokens
== NULL
) return;
1149 for(i
=0;tokens
[i
] != NULL
;i
++)
1157 int mpd_misc_get_tag_by_name(char *name
)
1162 return MPD_ARGS_ERROR
;
1164 for(i
=0; i
< MPD_TAG_NUM_OF_ITEM_TYPES
; i
++)
1166 if(!strcasecmp(mpdTagItemKeys
[i
], name
))
1171 return MPD_TAG_NOT_FOUND
;