1 /*
2  *  Copyright (C) 2009-2015  Christian Heckendorf <heckendorfc@gmail.com>
3  *
4  *  This program is free software: you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation, either version 3 of the License, or
7  *  (at your option) any later version.
8  *
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
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "../plugin.h"
19 #include <sys/socket.h>
20 #include <sys/types.h>
21 #include <netdb.h>
22 #include <sys/select.h>
23 
24 #define S_DEF_PORT_STR "80"
25 
26 static struct streamHandles{
27 	FILE *rfd;
28 	FILE *wfd;
29 	int sfd;
30 	FILE *ffd;
31 	struct pluginitem *dec;
32 	int metaint;
33 	int bytecount;
34 	int print_meta;
35 	volatile char go;
36 }h;
37 
38 #define S_BUFSIZE (1024)
39 #define S_DEF_TITLE "UnknownStream"
40 
41 #define SI_NAME_SIZE (100)
42 #define SI_GENRE_SIZE (100)
43 #define SI_DESCRIPTION_SIZE (100)
44 #define SI_TYPE_SIZE (30)
45 
46 struct streamInfo{
47 	char *name;
48 	char *genre;
49 	char *description;
50 	char *type;
51 	int bitrate;
52 };
53 
open_pipe()54 static int open_pipe(){
55 	int pipefd[2];
56 	if(pipe(pipefd)){
57 		fprintf(stderr,"pipe error\n");
58 		return 1;
59 	}
60 	h.rfd=fdopen(pipefd[0],"rb");
61 	h.wfd=fdopen(pipefd[1],"wb");
62 	return 0;
63 }
64 
close_pipe()65 static void close_pipe(){
66 	if(h.wfd){
67 		fclose(h.wfd);
68 		h.wfd=NULL;
69 	}
70 	if(h.rfd){
71 		fclose(h.rfd);
72 		h.rfd=NULL;
73 	}
74 }
75 
plugin_close(FILE * ffd)76 static void plugin_close(FILE *ffd){
77 	close_pipe();
78 	close(h.sfd);
79 }
80 
parse_url(const char * path,char ** orig_url,char ** orig_port,char ** orig_filename)81 static int parse_url(const char *path, char **orig_url, char **orig_port, char **orig_filename){
82 	int x;
83 	char *url=*orig_url;
84 	char *port=*orig_port;
85 
86 	if(strncmp("http://",path,7)!=0){
87 		//url=strdup(path);
88 		debug(1,"Unknown protocol.");
89 		return 1;
90 	}
91 	else
92 		url=strdup(path+7);
93 	if(!(port=malloc(10))){
94 		fprintf(stderr,"Malloc failed.");
95 		return 1;
96 	}
97 	*orig_port=port;
98 	*orig_url=url;
99 
100 	for(x=0;url[x] && url[x]!=':';x++);
101 	if(url[x]==':'){
102 		url[x++]=0;
103 		while(url[x] && url[x]>='0' && url[x]<='9')*(port++)=url[x++];
104 		*port=0;
105 		if(*(url+x))
106 			*orig_filename=strdup(url+x);
107 		else{
108 			goto def_filename;
109 		}
110 
111 	}
112 	else{
113 		strcpy(port,S_DEF_PORT_STR);
114 def_filename:
115 		if(!(*orig_filename=malloc(1))){
116 			fprintf(stderr,"Malloc failed.");
117 			return 1;
118 		}
119 		**orig_filename=0;
120 	}
121 
122 	return 0;
123 }
124 
stream_hello(char * filename)125 static int stream_hello(char *filename){
126 	char hello[300];
127 	int hello_len;
128 
129 	if(*filename!=0)
130 		sprintf(hello,"GET %s HTTP/1.0\r\nUser-Agent: HARP\r\nIcy-MetaData:%d\r\n\r\n",filename,h.print_meta);
131 	else
132 		sprintf(hello,"GET / HTTP/1.0\r\nUser-Agent: HARP\r\nIcy-MetaData:%d\r\n\r\n",h.print_meta);
133 
134 	hello_len=strlen(hello);
135 	if(write(h.sfd,hello,hello_len)<hello_len){
136 		fprintf(stderr,"Short write.\n");
137 		return 1;
138 	}
139 	return 0;
140 }
141 
plugin_open(const char * path,const char * mode)142 static FILE *plugin_open(const char *path, const char *mode){
143 	int sfd;
144 	int ret;
145 	struct addrinfo hints,*rp,*result;
146 	char *url,*port,*filename;
147 
148 	if(open_pipe())
149 		return NULL;
150 
151 	if(parse_url(path,&url,&port,&filename)){
152 		return NULL;
153 	}
154 
155 	memset(&hints,0,sizeof(struct addrinfo));
156 	hints.ai_family=AF_INET;
157 	hints.ai_socktype=SOCK_STREAM;
158 	hints.ai_protocol=0;
159 	if((ret=getaddrinfo(url,port,&hints,&result))){
160 		fprintf(stderr,"error (%s) - getaddrinfo: %s\n",path,gai_strerror(ret));
161 		close_pipe();
162 		free(port);
163 		return NULL;
164 	}
165 	free(url);
166 	free(port);
167 
168 	for(rp=result;rp;rp=rp->ai_next){
169 		if((sfd=socket(rp->ai_family,rp->ai_socktype,rp->ai_protocol))==-1)
170 			continue;
171 		if(connect(sfd,rp->ai_addr,rp->ai_addrlen)!=-1){
172 			h.sfd=sfd;
173 			h.ffd=fdopen(sfd,mode);
174 			break;
175 		}
176 		close(sfd);
177 	}
178 	if(!rp){
179 		fprintf(stderr,"Cannot connect to: %s\n",path);
180 		close_pipe();
181 		return NULL;
182 	}
183 	freeaddrinfo(result);
184 
185 	h.print_meta=1;
186 	if(stream_hello(filename)){
187 		plugin_close(NULL);
188 		return NULL;
189 	}
190 	free(filename);
191 
192 	return h.rfd;
193 }
194 
plugin_seek(struct playerHandles * ph,int modtime)195 static void plugin_seek(struct playerHandles *ph, int modtime){
196 	return;
197 }
198 
filetype_by_data(FILE * ffd)199 static int filetype_by_data(FILE *ffd){
200 	if(ffd==h.rfd)
201 		return 1;
202 	return 0;
203 }
204 
print_stream_meta(struct streamInfo * si)205 static void print_stream_meta(struct streamInfo *si){
206 	if(si->name && *si->name){
207 		printf("Name:        %s\n",si->name);
208 		free(si->name);
209 	}
210 	if(si->description && *si->description){
211 		printf("Description: %s\n",si->description);
212 		free(si->description);
213 	}
214 	if(si->genre && *si->genre){
215 		printf("Genre:       %s\n",si->genre);
216 		free(si->genre);
217 	}
218 	if(si->bitrate)
219 		printf("Bitrate:     %d\n",si->bitrate);
220 	if(si->type && *si->type){
221 		printf("Type:        %s\n",si->type);
222 		free(si->type);
223 	}
224 	printf("---------------------\n");
225 }
226 
get_line(char * buf,int len)227 static char *get_line(char *buf, int len){
228 	int x;
229 	int nl=0;
230 	char *ptr=buf;
231 
232 	if(len<1)return NULL;
233 
234 	for(x=0;x<len;x++){
235 		if(!nl && *(ptr-1)==0){
236 			buf=ptr;
237 			if(len-x>=2 && strncmp(ptr,"\r\n",2)==0)
238 				break;
239 		}
240 		if(*ptr=='\r'){
241 			nl=1;
242 			*ptr=0;
243 		}
244 		else if(*ptr=='\n'){
245 			nl=1;
246 			*ptr=0;
247 			break;
248 		}
249 		ptr++;
250 	}
251 
252 	return buf;
253 }
254 
split_icy(char * buf)255 static char *split_icy(char *buf){
256 	if(!*buf)return NULL;
257 
258 	while(*(++buf)){
259 		if(*buf==':'){
260 			*buf=0;
261 			return buf+1;
262 		}
263 	}
264 
265 	return NULL;
266 }
267 
parse_meta_si(char * buf,int * orig_len,void * data)268 static int parse_meta_si(char *buf, int *orig_len, void *data){
269 	static int ret=1; /* use 1 additional block */
270 	static void *last_data=NULL;
271 	int len=*orig_len;
272 	char *ptr=buf;
273 	char *val;
274 	struct streamInfo *si=(struct streamInfo*)data;
275 
276 	if(*buf<32 || *buf>126)return 0;
277 
278 	if(last_data!=data)ret=1; /* Reset block counter if writing to new data */
279 	last_data=data;
280 
281 	while((ptr=get_line(ptr,len-(ptr-buf)))){
282 		if(len-(ptr-buf) && !*ptr){
283 			*orig_len=len-((ptr)-buf);
284 			break;
285 		}
286 		if(strncmp(ptr,"\r\n",2)==0){
287 			*orig_len=len-((ptr+2)-buf);
288 			return 0;
289 		}
290 		if(!(val=split_icy(ptr))){
291 			continue;
292 		}
293 		if(strcmp(ptr,"icy-name")==0){
294 			strncpy(si->name,val,SI_NAME_SIZE);
295 		}
296 		if(strcmp(ptr,"icy-description")==0){
297 			strncpy(si->description,val,SI_DESCRIPTION_SIZE);
298 		}
299 		if(strcmp(ptr,"icy-genre")==0){
300 			strncpy(si->genre,val,SI_GENRE_SIZE);
301 		}
302 		if(strcmp(ptr,"icy-br")==0){
303 			si->bitrate=(int)strtol(val,NULL,10);
304 		}
305 		if(strcmp(ptr,"icy-metaint")==0){
306 			h.metaint=(int)strtol(val,NULL,10);
307 		}
308 		if(strcasecmp(ptr,"Content-Type")==0){
309 			while(*val){
310 				if(*val==' ')val++;
311 				else break;
312 			}
313 			while(*val && *(val++)!='/');
314 			strncpy(si->type,val,SI_TYPE_SIZE);
315 		}
316 	}
317 	len-=(ptr-buf);
318 	return ret--;
319 }
320 
parse_meta_mi(char * buf,int * orig_len,void * data)321 static int parse_meta_mi(char *buf, int *orig_len, void *data){
322 	static int ret=1; /* use 1 additional block */
323 	static void *last_data=NULL;
324 	int len=*orig_len;
325 	char *ptr=buf;
326 	char *val;
327 	struct musicInfo *mi=(struct musicInfo*)data;
328 
329 	if(*buf<32 || *buf>126)return 0;
330 
331 	if(last_data!=data)ret=1; /* Reset block counter if writing to new data */
332 	last_data=data;
333 
334 	while((ptr=get_line(ptr,len-(ptr-buf)))){
335 		if(len-(ptr-buf) && !*ptr){
336 			break;
337 		}
338 		if(strncmp(ptr,"\r\n",2)==0){
339 			len=len-((ptr+2)-buf);
340 			return 0;
341 		}
342 		if(!(val=split_icy(ptr))){
343 			continue;
344 		}
345 		if(strcmp(ptr,"icy-name")==0){
346 			strncpy(mi->title,val,MI_TITLE_SIZE);
347 		}
348 	}
349 	len-=(ptr-buf);
350 	return ret--;
351 }
352 
print_streamtitle(char * buf,int buf_size,int * meta_len)353 static char *print_streamtitle(char *buf, int buf_size, int *meta_len){
354 	if(*meta_len>buf_size)
355 		buf_size-=*meta_len;
356 	else
357 		buf_size=0;
358 	for(;*meta_len>buf_size;(*meta_len)--,buf++)
359 		if(*buf)
360 			printf("%c",*buf<32?'?':*buf);
361 	if(!*meta_len)
362 		putchar('\n');
363 		else putchar('$');
364 	return buf;
365 }
366 
handle_streamtitle(char * buf,int * len,int * metasize)367 static char *handle_streamtitle(char *buf, int *len, int *metasize){
368 	int temp=*metasize;
369 	buf=print_streamtitle(buf,*len,metasize);
370 	*len-=temp-*metasize;
371 	return buf;
372 }
373 
write_pipe_parse_meta(char * buf,int * len,void * data)374 static int write_pipe_parse_meta(char *buf, int *len, void *data){
375 	int ret,temp;
376 	static int metasize=0;
377 	static int save;
378 	char *ptr=buf;
379 
380 	if(!h.wfd){
381 		fprintf(stderr,"No wfd\n");
382 		return 0;
383 	}
384 	if(h.bytecount+*len>h.metaint){
385 		volatile int *up=&(((struct playerHandles *)data)->pflag->update);
386 
387 		temp=h.metaint-h.bytecount;
388 		if((ret=fwrite(ptr,1,temp,h.wfd))<temp)
389 			fprintf(stderr,"Short write to pipe\n");
390 		ptr+=ret+1;
391 		*len-=ret+1;
392 		h.bytecount+=ret; // Should = metaint now
393 
394 		if(!metasize){
395 			save=*up;
396 			*up=0;
397 			metasize=(int)((char)*(ptr-1))*16;
398 			if(!h.print_meta || metasize<0) // Cancel all further printing
399 				metasize=h.print_meta=0;
400 		}
401 
402 		if(metasize)
403 			ptr=handle_streamtitle(ptr,len,&metasize);
404 
405 		if(metasize<=0){
406 			*up=save;
407 			metasize=h.bytecount=0;
408 		}
409 		if(*len<1)return 1;
410 	}
411 
412 	if((ret=fwrite(ptr,1,*len,h.wfd))<*len){
413 		fprintf(stderr,"Short write to pipe\n");
414 	}
415 	h.bytecount+=ret;
416 	return 1;
417 }
418 
write_pipe(char * buf,int * len,void * data)419 static int write_pipe(char *buf, int *len, void *data){
420 	int ret;
421 
422 	if(!h.wfd){
423 		fprintf(stderr,"No wfd\n");
424 		return 0;
425 	}
426 
427 	if((ret=fwrite(buf,1,*len,h.wfd))<*len){
428 		fprintf(stderr,"Short write to pipe\n");
429 	}
430 	return 1;
431 }
432 
streamIO(int parse (char *,int *,void *),void * data)433 static void streamIO(int parse(char*,int*,void*),void *data){
434 	int len,ret;
435 	char buf[S_BUFSIZE+1];
436 	buf[S_BUFSIZE]=0;
437 
438 	while(1){
439 		if(!h.go)
440 			break;
441 
442 		len=ret=read(h.sfd,buf,S_BUFSIZE);
443 		if(!parse(buf,&len,data))
444 			break;
445 	}
446 	if(h.metaint){
447 		h.bytecount=len;
448 	}
449 }
450 
stream_plugin_meta(FILE * ffd,struct musicInfo * mi)451 static void stream_plugin_meta(FILE *ffd, struct musicInfo *mi){
452 	h.go=1;
453 	streamIO(parse_meta_mi,mi);
454 
455 	if(!(*mi->title))
456 		strncpy(mi->title,S_DEF_TITLE,MI_TITLE_SIZE);
457 	mi->length=-1;
458 }
459 
nextType(char * type)460 static char *nextType(char *type){
461 	int x;
462 	for(x=0;type[x];x++){
463 		if(type[x]==';'){
464 			return type+x+1;
465 		}
466 	}
467 	return NULL;
468 }
469 
selectPlugin(struct pluginitem ** list,char * type)470 static struct pluginitem *selectPlugin(struct pluginitem **list, char *type){
471 	char *ptr;
472 	int len;
473 	int i;
474 	if(type==NULL || *type==0)return NULL;
475 	len=strlen(type);
476 
477 	for(i=0;i<PLUGIN_NULL;i++){
478 		ptr=list[i]->contenttype;
479 		while(ptr){
480 			if(strncmp(ptr,type,len)==0)
481 				return list[i];
482 			ptr=nextType(ptr);
483 		}
484 	}
485 	return NULL;
486 }
487 
sio_thread(void * data)488 static void* sio_thread(void *data){
489 	if(h.print_meta && h.metaint)
490 		streamIO(write_pipe_parse_meta,data);
491 	else
492 		streamIO(write_pipe,data);
493 
494 	return (void*)0;
495 }
496 
plugin_run(struct playerHandles * ph,char * key,int * totaltime)497 static int plugin_run(struct playerHandles *ph, char *key, int *totaltime){
498 	int ret,temp=-1;
499 	struct pluginitem *plugin;
500 	struct streamInfo si;
501 	pthread_t threads;
502 	char buf[S_BUFSIZE+1];
503 	fd_set fdset;
504 	struct timeval timeout;
505 	int rfd;
506 	int i;
507 
508 	if(!(si.name=malloc((SI_NAME_SIZE+1)*sizeof(char))) ||
509 		!(si.description=malloc((SI_DESCRIPTION_SIZE+1)*sizeof(char))) ||
510 		!(si.genre=malloc((SI_GENRE_SIZE+1)*sizeof(char))) ||
511 		!(si.type=malloc((SI_TYPE_SIZE+1)*sizeof(char)))){
512 		fprintf(stderr,"Can't malloc for si\n");
513 		return DEC_RET_ERROR;
514 	}
515 	memset(si.name,0,SI_NAME_SIZE);
516 	memset(si.genre,0,SI_GENRE_SIZE);
517 	memset(si.description,0,SI_DESCRIPTION_SIZE);
518 	memset(si.type,0,SI_TYPE_SIZE);
519 	si.bitrate=0;
520 
521 	h.metaint=h.bytecount=0;
522 	h.go=1;
523 	streamIO(parse_meta_si,&si);
524 	print_stream_meta(&si);
525 	if(!(plugin=selectPlugin(ph->plugin_head,si.type))){
526 		fprintf(stderr,"No plugin matches content-type. Trying first plugin.\n");
527 		for(i=0;i<PLUGIN_NULL && ph->plugin_head[i]==NULL;i++)
528 		plugin=ph->plugin_head[i];
529 		if(plugin==NULL)
530 			return -1;
531 	}
532 
533 	ph->ffd=h.rfd;
534 	pthread_create(&threads,NULL,(void *)&sio_thread,(void *)ph);
535 	ret=plugin->modplay(ph,key,&temp);
536 
537 	h.go=0;
538 
539 //#if defined(__APPLE__) || defined(Linux)
540 	rfd=fileno(h.rfd);
541 	do{
542 		timeout.tv_sec=0;
543 		timeout.tv_usec=100000;
544 		FD_ZERO(&fdset);
545 		FD_SET(rfd,&fdset);
546 		if(select(1,&fdset,NULL,NULL,&timeout)<1)
547 			break;
548 	}while((temp=fread(buf,1,S_BUFSIZE,h.rfd))==S_BUFSIZE);
549 
550 	// OSX seems to block when closing the write end unless the read end is first closed
551 	close(rfd);
552 	h.rfd=NULL;
553 //#else
554 #if 0
555 	if(pthread_cancel(threads)!=0)fprintf(stderr,"Failed cancel.\n");
556 	if(pthread_join(threads,NULL)!=0)fprintf(stderr,"Failed join\n");
557 #endif
558 
559 	//usleep(100000);
560 
561 	return ret;
562 }
563 
564 struct pluginitem streamitem={
565 	.modopen=plugin_open,
566 	.modclose=plugin_close,
567 	.moddata=filetype_by_data,
568 	.modplay=plugin_run,
569 	.modseek=plugin_seek,
570 	.modmeta=stream_plugin_meta,
571 	.contenttype="null",
572 	.extension={"///",NULL},
573 	.name="STREAM",
574 };
575