2 Copyright (C) 2006-2010 by Jonas Kramer
3 Published under the terms of the GNU General Public License (GPL).
13 #include <sys/types.h>
14 #include <sys/socket.h>
20 #include <netinet/in.h>
21 #include <arpa/inet.h>
24 #include <sys/select.h>
30 #include "interface.h"
44 static int stcpsck
= -1, sunixsck
= -1;
48 void parse_volume(const char *);
51 int tcpsock(const char * ip
, unsigned short port
) {
52 static const int one
= 1;
53 struct sockaddr_in host
;
54 struct hostent
* hostent
;
59 if(-1 == (stcpsck
= socket(AF_INET
, SOCK_STREAM
, PF_UNSPEC
))) {
60 fputs("Failed to create socket.\n", stderr
);
64 if(!(hostent
= gethostbyname(ip
))) {
65 fprintf(stderr
, "Failed to lookup host. %s.\n", hstrerror(h_errno
));
69 host
.sin_family
= PF_INET
;
70 host
.sin_port
= htons(port
);
71 host
.sin_addr
.s_addr
= * (unsigned *) hostent
->h_addr
;
73 if(-1 == setsockopt(stcpsck
, SOL_SOCKET
, SO_REUSEADDR
, & one
, sizeof one
))
74 fprintf(stderr
, "Couldn't make socket re-usable. %s\n", strerror(errno
));
76 if(bind(stcpsck
, (struct sockaddr
*) & host
, sizeof(struct sockaddr_in
))) {
77 fprintf(stderr
, "Failed to bind socket. %s.\n", strerror(errno
));
83 register_listen_socket(stcpsck
);
89 int unixsock(const char * path
) {
90 struct sockaddr_un host
;
96 if(!access(path
, F_OK
)) {
97 fprintf(stderr
, "%s already existing. UNIX socket not created.\n", path
);
102 if(-1 == (sunixsck
= socket(AF_UNIX
, SOCK_STREAM
, PF_UNSPEC
))) {
103 fputs("Failed to create socket.\n", stderr
);
108 memset(& host
, 0, sizeof(struct sockaddr_un
));
109 strncpy(host
.sun_path
, path
, sizeof(host
.sun_path
) - 1);
110 host
.sun_family
= AF_UNIX
;
113 if(bind(sunixsck
, (struct sockaddr
*) & host
, sizeof(struct sockaddr_un
))) {
114 fprintf(stderr
, "Failed to bind socket. %s.\n", strerror(errno
));
120 register_listen_socket(sunixsck
);
139 void accept_client(int listen_socket
) {
140 struct sockaddr client
;
141 socklen_t client_size
= sizeof(struct sockaddr
);
143 int client_socket
= accept(listen_socket
, & client
, & client_size
);
145 if(-1 != client_socket
)
146 register_client_socket(client_socket
);
150 void handle_client(int client_socket
) {
151 static FILE * fd
= NULL
;
155 fd
= fdopen(client_socket
, "rw");
157 signal(SIGPIPE
, SIG_IGN
);
159 if(fd
!= NULL
&& !feof(fd
)) {
160 debug("client socket is ok and readable: %i\n", client_socket
);
163 if(fgets(line
, sizeof(line
), fd
) == NULL
) {
169 char ** lines
= split(line
, "\n", & chunks
);
171 for(i
= 0; i
< chunks
&& !disconnect
; ++i
) {
172 char reply
[BUFSIZE
] = { 0, };
173 debug("client message: <%s>\n", lines
[i
]);
175 disconnect
= execcmd(lines
[i
], reply
);
178 strncat(reply
, "\n", BUFSIZE
- strlen(reply
));
179 write(client_socket
, reply
, strlen(reply
));
185 debug("fd error: %i\n", ferror(fd
));
193 debug("removing client\n");
194 shutdown(SHUT_RDWR
, client_socket
);
195 close(client_socket
);
198 remove_handle(client_socket
);
203 int execcmd(const char * cmd
, char * reply
) {
204 char arg
[1024], * ptr
;
206 const char * known
[] = {
230 memset(arg
, 0, sizeof(arg
));
231 memset(reply
, 0, BUFSIZE
);
233 for(ncmd
= 0; ncmd
< (sizeof(known
) / sizeof(char *)); ++ncmd
) {
234 if(!strncmp(known
[ncmd
], cmd
, strlen(known
[ncmd
])))
239 case (sizeof(known
) / sizeof(char *)):
240 strncpy(reply
, "ERROR", BUFSIZE
);
243 /* "play lastfm://station" */
245 if(sscanf(cmd
, "play %128[a-zA-Z0-9:/_ %,*.-]", arg
) == 1) {
253 /* Love currently played track. */
258 /* Ban currently played track. */
272 /* "info FORMAT" - returns the format string with the meta data filled in. */
275 strncpy(reply
, meta(cmd
+ 5, 0, & track
), BUFSIZE
);
276 else if(haskey(& rc
, "np-file-format"))
279 meta(value(& rc
, "np-file-format"), 0, & track
),
285 /* Pause playback. */
289 kill(playfork
, SIGCONT
);
293 kill(playfork
, SIGSTOP
);
298 /* Toggle discovery mode. Returns "DISCOVERY <ON|OFF>" */
305 enabled(DISCOVERY
) ? "ON" : "OFF"
309 /* "tag-artist tag1,tag2,..." - tag the artist of the current track. */
311 if(sscanf(cmd
, "tag-artist %128s", arg
) == 1)
312 sendtag('a', arg
, track
);
315 /* "tag-album tag1,tag2,..." - tag the album of the current track. */
317 if(sscanf(cmd
, "tag-album %128s", arg
) == 1)
318 sendtag('l', arg
, track
);
321 /* "tag-track tag1,tag2,..." - tag the current track. */
323 if(sscanf(cmd
, "tag-track %128s", arg
) == 1)
324 sendtag('t', arg
, track
);
327 /* Return comma-separated list of the current artists tags. */
329 if((ptr
= oldtags('a', track
)) != NULL
) {
330 strncpy(reply
, ptr
, BUFSIZE
);
336 /* Return comma-separated list of the current albums tags. */
338 if((ptr
= oldtags('l', track
)) != NULL
) {
339 strncpy(reply
, ptr
, BUFSIZE
);
345 /* Return comma-separated list of the current tracks tags. */
347 if((ptr
= oldtags('t', track
)) != NULL
) {
348 strncpy(reply
, ptr
, BUFSIZE
);
359 kill(playfork
, SIGUSR1
);
363 /* Increase absolute volume (0-64) by 1. */
368 /* Decrease absolute volume (0-64) by 1. */
375 "volume 32" - set absolute volume (0-64) to 32 (50%).
376 "volume %50" - same, but using percentual volume.
377 "volume +1" - same as "volume_up".
378 "volume -1" - same as "volume_down".
380 Returns absolute volume ("VOLUME 32").
384 snprintf(reply
, BUFSIZE
, "VOLUME %d", volume
);
387 /* Toggle RTP (report to profile, "scrobbling"). Returns "RTP <ON|OFF>". */
391 snprintf(reply
, BUFSIZE
, "RTP %s", enabled(RTP
) ? "ON" : "OFF");
394 /* Get current status. Returns on of "PAUSE", "PLAYING" and "STOPPED". */
396 strncpy(reply
, playfork
? (pausetime
? "PAUSED" : "PLAYING") : "STOPPED", BUFSIZE
);
399 /* Detach from network interface. */
408 void parse_volume(const char * cmd
) {
412 if(sscanf(cmd
, "volume %1[+-]%d", & sign
, & new_volume
) == 2) {
414 set_volume(volume
- new_volume
);
416 set_volume(volume
+ new_volume
);
419 /* Allow percentual volume (1-100%). */
420 else if(sscanf(cmd
, "volume %%%d", & new_volume
)) {
421 set_volume(new_volume
* 0.64);
424 /* Allow absolute volume (0-64). */
425 else if(sscanf(cmd
, "volume %d", & new_volume
) == 1) {
426 set_volume(new_volume
);