1 /* -*- c-basic-offset: 8; -*- */
2 /* shout.c: Implementation of public libshout interface shout.h
3  *
4  *  Copyright (C) 2002-2004 the Icecast team <team@icecast.org>,
5  *  Copyright (C) 2012-2019 Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Library General Public
9  *  License as published by the Free Software Foundation; either
10  *  version 2 of the License, or (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Library General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Library General Public
18  *  License along with this library; if not, write to the Free
19  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  * $Id$
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #   include <config.h>
26 #endif
27 
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #ifdef HAVE_STRINGS_H
33 #   include <strings.h>
34 #endif
35 #include <errno.h>
36 
37 #include <shout/shout.h>
38 
39 #include <common/net/sock.h>
40 #include <common/timing/timing.h>
41 #include <common/httpp/httpp.h>
42 
43 #include "shout_private.h"
44 #include "util.h"
45 
46 #ifdef _MSC_VER
47 #   ifndef va_copy
48 #       define va_copy(ap1, ap2) memcpy(&ap1, &ap2, sizeof(va_list))
49 #   endif
50 #   define vsnprintf      _vsnprintf
51 #   define inline         _inline
52 #endif
53 
54 /* -- local prototypes -- */
55 static int shout_cb_connection_callback(shout_connection_t *con, shout_event_t event, void *userdata, va_list ap);
56 static int try_connect(shout_t *self);
57 
58 /* -- static data -- */
59 static int _initialized = 0;
60 
61 /* -- public functions -- */
62 
shout_init(void)63 void shout_init(void)
64 {
65     if (_initialized)
66         return;
67 
68     sock_initialize();
69     _initialized = 1;
70 }
71 
shout_shutdown(void)72 void shout_shutdown(void)
73 {
74     if (!_initialized)
75         return;
76 
77     sock_shutdown();
78     _initialized = 0;
79 }
80 
shout_new(void)81 shout_t *shout_new(void)
82 {
83     shout_t *self;
84 
85     /* in case users haven't done this explicitly. Should we error
86      * if not initialized instead? */
87     shout_init();
88 
89     if (!(self = (shout_t*)calloc(1, sizeof(shout_t)))) {
90         return NULL;
91     }
92 
93     if (shout_set_host(self, LIBSHOUT_DEFAULT_HOST) != SHOUTERR_SUCCESS) {
94         shout_free(self);
95         return NULL;
96     }
97 
98     if (shout_set_user(self, LIBSHOUT_DEFAULT_USER) != SHOUTERR_SUCCESS) {
99         shout_free(self);
100         return NULL;
101     }
102 
103     if (shout_set_agent(self, LIBSHOUT_DEFAULT_USERAGENT) != SHOUTERR_SUCCESS) {
104         shout_free(self);
105         return NULL;
106     }
107 
108     if (!(self->audio_info = _shout_util_dict_new())) {
109         shout_free(self);
110         return NULL;
111     }
112 
113     if (!(self->meta = _shout_util_dict_new())) {
114         shout_free(self);
115         return NULL;
116     }
117 
118     if (shout_set_meta(self, "name", "no name") != SHOUTERR_SUCCESS) {
119         shout_free(self);
120         return NULL;
121     }
122 
123 #ifdef HAVE_OPENSSL
124     if (shout_set_allowed_ciphers(self, LIBSHOUT_DEFAULT_ALLOWED_CIPHERS) != SHOUTERR_SUCCESS) {
125         shout_free(self);
126         return NULL;
127     }
128 
129     self->tls_mode      = SHOUT_TLS_AUTO;
130 #endif
131 
132     self->port      = LIBSHOUT_DEFAULT_PORT;
133     self->format    = LIBSHOUT_DEFAULT_FORMAT;
134     self->usage     = LIBSHOUT_DEFAULT_USAGE;
135     self->protocol  = LIBSHOUT_DEFAULT_PROTOCOL;
136 
137     return self;
138 }
139 
shout_free(shout_t * self)140 void shout_free(shout_t *self)
141 {
142     if (!self)
143         return;
144 
145     if (!self->connection)
146         return;
147 
148     if (self->host)
149         free(self->host);
150     if (self->password)
151         free(self->password);
152     if (self->content_language)
153         free(self->content_language);
154     if (self->mount)
155         free(self->mount);
156     if (self->user)
157         free(self->user);
158     if (self->useragent)
159         free(self->useragent);
160     if (self->audio_info)
161         _shout_util_dict_free (self->audio_info);
162     if (self->meta)
163         _shout_util_dict_free (self->meta);
164 
165 #ifdef HAVE_OPENSSL
166     if (self->ca_directory)
167         free(self->ca_directory);
168     if (self->ca_file)
169         free(self->ca_file);
170     if (self->allowed_ciphers)
171         free(self->allowed_ciphers);
172     if (self->client_certificate)
173         free(self->client_certificate);
174 #endif
175 
176     free(self);
177 }
178 
shout_open(shout_t * self)179 int shout_open(shout_t *self)
180 {
181     /* sanity check */
182     if (!self)
183         return SHOUTERR_INSANE;
184     if (self->connection)
185         return SHOUTERR_CONNECTED;
186     if (!self->host || !self->password || !self->port)
187         return self->error = SHOUTERR_INSANE;
188     if (self->format == SHOUT_FORMAT_OGG &&  (self->protocol != SHOUT_PROTOCOL_HTTP && self->protocol != SHOUT_PROTOCOL_ROARAUDIO))
189         return self->error = SHOUTERR_UNSUPPORTED;
190 
191     return self->error = try_connect(self);
192 }
193 
194 
shout_close(shout_t * self)195 int shout_close(shout_t *self)
196 {
197     if (!self)
198         return SHOUTERR_INSANE;
199 
200     if (!self->connection)
201         return self->error = SHOUTERR_UNCONNECTED;
202 
203     if (self->connection && self->connection->current_message_state == SHOUT_MSGSTATE_SENDING1 && self->close) {
204         self->close(self);
205         self->format_data = NULL;
206         self->send = NULL;
207         self->close = NULL;
208     }
209 
210     shout_connection_unref(self->connection);
211     self->connection = NULL;
212     self->starttime = 0;
213     self->senttime = 0;
214 
215     return self->error = SHOUTERR_SUCCESS;
216 }
217 
shout_send(shout_t * self,const unsigned char * data,size_t len)218 int shout_send(shout_t *self, const unsigned char *data, size_t len)
219 {
220     if (!self)
221         return SHOUTERR_INSANE;
222 
223     if (!self->connection || self->connection->current_message_state != SHOUT_MSGSTATE_SENDING1)
224         return self->error = SHOUTERR_UNCONNECTED;
225 
226     if (self->starttime <= 0)
227         self->starttime = timing_get_time();
228 
229     if (!len)
230         return shout_connection_iter(self->connection, self);
231 
232     return self->send(self, data, len);
233 }
234 
shout_send_raw(shout_t * self,const unsigned char * data,size_t len)235 ssize_t shout_send_raw(shout_t *self, const unsigned char *data, size_t len)
236 {
237     ssize_t ret;
238 
239     if (!self)
240         return SHOUTERR_INSANE;
241 
242     if (!self->connection || self->connection->current_message_state != SHOUT_MSGSTATE_SENDING1)
243         return SHOUTERR_UNCONNECTED;
244 
245     ret = shout_connection_send(self->connection, self, data, len);
246     if (ret < 0)
247        shout_connection_transfer_error(self->connection, self);
248     return ret;
249 }
250 
shout_queuelen(shout_t * self)251 ssize_t shout_queuelen(shout_t *self)
252 {
253     if (!self)
254         return -1;
255 
256     return shout_connection_get_sendq(self->connection, self);
257 }
258 
259 
shout_sync(shout_t * self)260 void shout_sync(shout_t *self)
261 {
262     int64_t sleep;
263 
264     if (!self)
265         return;
266 
267     if (self->senttime == 0)
268         return;
269 
270     sleep = self->senttime / 1000 - (timing_get_time() - self->starttime);
271     if (sleep > 0)
272         timing_sleep((uint64_t)sleep);
273 
274 }
275 
shout_delay(shout_t * self)276 int shout_delay(shout_t *self)
277 {
278 
279     if (!self)
280         return 0;
281 
282     if (self->senttime == 0)
283         return 0;
284 
285     return self->senttime / 1000 - (timing_get_time() - self->starttime);
286 }
287 
shout_metadata_new(void)288 shout_metadata_t *shout_metadata_new(void)
289 {
290     return _shout_util_dict_new();
291 }
292 
shout_metadata_free(shout_metadata_t * self)293 void shout_metadata_free(shout_metadata_t *self)
294 {
295     if (!self)
296         return;
297 
298     _shout_util_dict_free(self);
299 }
300 
shout_metadata_add(shout_metadata_t * self,const char * name,const char * value)301 int shout_metadata_add(shout_metadata_t *self, const char *name, const char *value)
302 {
303     if (!self || !name)
304         return SHOUTERR_INSANE;
305 
306     return _shout_util_dict_set(self, name, value);
307 }
308 
shout_set_metadata(shout_t * self,shout_metadata_t * metadata)309 int shout_set_metadata(shout_t *self, shout_metadata_t *metadata)
310 {
311     shout_connection_t *connection;
312     shout_http_plan_t plan;
313     size_t param_len;
314     char *param = NULL;
315     char *encvalue;
316     char *encpassword;
317     char *encmount;
318     const char *param_template;
319     int ret;
320     int error;
321 
322     if (!self || !metadata)
323         return SHOUTERR_INSANE;
324 
325     encvalue = _shout_util_dict_urlencode(metadata, '&');
326     if (!encvalue)
327         return self->error = SHOUTERR_MALLOC;
328 
329     memset(&plan, 0, sizeof(plan));
330 
331     plan.is_source = 0;
332 
333     switch (self->protocol) {
334         case SHOUT_PROTOCOL_ICY:
335             if (!(encpassword = _shout_util_url_encode(self->password))) {
336                 free(encvalue);
337                 return self->error = SHOUTERR_MALLOC;
338             }
339 
340             param_template = "mode=updinfo&pass=%s&%s";
341             param_len = strlen(param_template) + strlen(encvalue) + 1 + strlen(encpassword);
342             param = malloc(param_len);
343             if (!param) {
344                 free(encpassword);
345                 free(encvalue);
346                 return self->error = SHOUTERR_MALLOC;
347             }
348             snprintf(param, param_len, param_template, encpassword, encvalue);
349             free(encpassword);
350 
351             plan.param = param;
352             plan.fake_ua = 1;
353             plan.auth = 0;
354             plan.method = "GET";
355             plan.resource = "/admin.cgi";
356         break;
357         case SHOUT_PROTOCOL_HTTP:
358             if (!(encmount = _shout_util_url_encode(self->mount))) {
359                 free(encvalue);
360                 return self->error = SHOUTERR_MALLOC;
361             }
362 
363             param_template = "mode=updinfo&mount=%s&%s";
364             param_len = strlen(param_template) + strlen(encvalue) + 1 + strlen(encmount);
365             param = malloc(param_len);
366             if (!param) {
367                 free(encmount);
368                 free(encvalue);
369                 return self->error = SHOUTERR_MALLOC;
370             }
371             snprintf(param, param_len, param_template, encmount, encvalue);
372             free(encmount);
373 
374             plan.param = param;
375             plan.auth = 1;
376             plan.resource = "/admin/metadata";
377         break;
378         case SHOUT_PROTOCOL_XAUDIOCAST:
379             if (!(encmount = _shout_util_url_encode(self->mount))) {
380                 free(encvalue);
381                 return self->error = SHOUTERR_MALLOC;
382             }
383             if (!(encpassword = _shout_util_url_encode(self->password))) {
384                 free(encmount);
385                 free(encvalue);
386                 return self->error = SHOUTERR_MALLOC;
387             }
388 
389             param_template = "mode=updinfo&pass=%s&mount=%s&%s";
390             param_len = strlen(param_template) + strlen(encvalue) + 1 + strlen(encpassword) + strlen(self->mount);
391             param = malloc(param_len);
392             if (!param) {
393                 free(encpassword);
394                 free(encmount);
395                 free(encvalue);
396                 return self->error = SHOUTERR_MALLOC;
397             }
398             snprintf(param, param_len, param_template, encpassword, encmount, encvalue);
399             free(encpassword);
400             free(encmount);
401 
402             plan.param = param;
403             plan.auth = 0;
404             plan.method = "GET";
405             plan.resource = "/admin.cgi";
406         break;
407         default:
408             free(encvalue);
409             return self->error = SHOUTERR_UNSUPPORTED;
410         break;
411     }
412 
413     free(encvalue);
414 
415     connection = shout_connection_new(self, shout_http_impl, &plan);
416     if (!connection) {
417         free(param);
418         return self->error = SHOUTERR_MALLOC;
419     }
420 
421     shout_connection_set_callback(self->connection, shout_cb_connection_callback, self);
422 
423 #ifdef HAVE_OPENSSL
424     shout_connection_select_tlsmode(connection, self->tls_mode);
425 #endif
426     shout_connection_set_nonblocking(connection, SHOUT_BLOCKING_FULL);
427 
428     connection->target_message_state = SHOUT_MSGSTATE_PARSED_FINAL;
429 
430     shout_connection_connect(connection, self);
431 
432     ret = shout_connection_iter(connection, self);
433     error = shout_connection_get_error(connection);
434 
435     shout_connection_unref(connection);
436 
437     free(param);
438 
439     if (ret == 0) {
440         return SHOUTERR_SUCCESS;
441     } else {
442         return error;
443     }
444 }
445 
446 /* getters/setters */
shout_version(int * major,int * minor,int * patch)447 const char *shout_version(int *major, int *minor, int *patch)
448 {
449     if (major)
450         *major = LIBSHOUT_MAJOR;
451     if (minor)
452         *minor = LIBSHOUT_MINOR;
453     if (patch)
454         *patch = LIBSHOUT_MICRO;
455 
456     return VERSION;
457 }
458 
shout_get_errno(shout_t * self)459 int shout_get_errno(shout_t *self)
460 {
461     return self->error;
462 }
463 
shout_get_error(shout_t * self)464 const char *shout_get_error(shout_t *self)
465 {
466     if (!self)
467         return "Invalid shout_t";
468 
469     switch (self->error) {
470     case SHOUTERR_SUCCESS:
471         return "No error";
472     case SHOUTERR_INSANE:
473         return "Nonsensical arguments";
474     case SHOUTERR_NOCONNECT:
475         return "Couldn't connect";
476     case SHOUTERR_NOLOGIN:
477         return "Login failed";
478     case SHOUTERR_SOCKET:
479         return "Socket error";
480     case SHOUTERR_MALLOC:
481         return "Out of memory";
482     case SHOUTERR_CONNECTED:
483         return "Cannot set parameter while connected";
484     case SHOUTERR_UNCONNECTED:
485         return "Not connected";
486     case SHOUTERR_BUSY:
487         return "Socket is busy";
488     case SHOUTERR_UNSUPPORTED:
489         return "This libshout doesn't support the requested option";
490     case SHOUTERR_NOTLS:
491         return "TLS requested but not supported by peer";
492     case SHOUTERR_TLSBADCERT:
493         return "TLS connection can not be established because of bad certificate";
494     case SHOUTERR_RETRY:
495         return "Please retry current operation.";
496     default:
497         return "Unknown error";
498     }
499 }
500 
501 /* Returns:
502  *   SHOUTERR_CONNECTED if the connection is open,
503  *   SHOUTERR_UNCONNECTED if it has not yet been opened,
504  *   or an error from try_connect, including SHOUTERR_BUSY
505  */
shout_get_connected(shout_t * self)506 int shout_get_connected(shout_t *self)
507 {
508     int rc;
509 
510     if (!self)
511         return SHOUTERR_INSANE;
512 
513     if (self->connection && self->connection->current_message_state == SHOUT_MSGSTATE_SENDING1)
514         return SHOUTERR_CONNECTED;
515     if (self->connection && self->connection->current_message_state != SHOUT_MSGSTATE_SENDING1) {
516         if ((rc = try_connect(self)) == SHOUTERR_SUCCESS)
517             return SHOUTERR_CONNECTED;
518         return rc;
519     }
520 
521     return SHOUTERR_UNCONNECTED;
522 }
523 
shout_set_host(shout_t * self,const char * host)524 int shout_set_host(shout_t *self, const char *host)
525 {
526     if (!self)
527         return SHOUTERR_INSANE;
528 
529     if (self->connection)
530         return self->error = SHOUTERR_CONNECTED;
531 
532     if (self->host)
533         free(self->host);
534 
535     if ( !(self->host = _shout_util_strdup(host)) )
536         return self->error = SHOUTERR_MALLOC;
537 
538     return self->error = SHOUTERR_SUCCESS;
539 }
540 
shout_get_host(shout_t * self)541 const char *shout_get_host(shout_t *self)
542 {
543     if (!self)
544         return NULL;
545 
546     return self->host;
547 }
548 
shout_set_port(shout_t * self,unsigned short port)549 int shout_set_port(shout_t *self, unsigned short port)
550 {
551     if (!self)
552         return SHOUTERR_INSANE;
553 
554     if (self->connection)
555         return self->error = SHOUTERR_CONNECTED;
556 
557     self->port = port;
558 
559     return self->error = SHOUTERR_SUCCESS;
560 }
561 
shout_get_port(shout_t * self)562 unsigned short shout_get_port(shout_t *self)
563 {
564     if (!self)
565         return 0;
566 
567     return self->port;
568 }
569 
shout_set_password(shout_t * self,const char * password)570 int shout_set_password(shout_t *self, const char *password)
571 {
572     if (!self)
573         return SHOUTERR_INSANE;
574 
575     if (self->connection)
576         return self->error = SHOUTERR_CONNECTED;
577 
578     if (self->password)
579         free(self->password);
580 
581     if ( !(self->password = _shout_util_strdup(password)) )
582         return self->error = SHOUTERR_MALLOC;
583 
584     return self->error = SHOUTERR_SUCCESS;
585 }
586 
shout_get_password(shout_t * self)587 const char* shout_get_password(shout_t *self)
588 {
589     if (!self)
590         return NULL;
591 
592     return self->password;
593 }
594 
shout_set_mount(shout_t * self,const char * mount)595 int shout_set_mount(shout_t *self, const char *mount)
596 {
597     size_t len;
598 
599     if (!self || !mount)
600         return SHOUTERR_INSANE;
601 
602     if (self->connection)
603         return self->error = SHOUTERR_CONNECTED;
604 
605     if (self->mount)
606         free(self->mount);
607 
608     len = strlen(mount) + 1;
609     if (mount[0] != '/')
610         len++;
611 
612     if ( !(self->mount = malloc(len)) )
613         return self->error = SHOUTERR_MALLOC;
614 
615     snprintf(self->mount, len, "%s%s", mount[0] == '/' ? "" : "/", mount);
616 
617     return self->error = SHOUTERR_SUCCESS;
618 }
619 
shout_get_mount(shout_t * self)620 const char *shout_get_mount(shout_t *self)
621 {
622     if (!self)
623         return NULL;
624 
625     return self->mount;
626 }
627 
shout_set_name(shout_t * self,const char * name)628 int shout_set_name(shout_t *self, const char *name)
629 {
630     return shout_set_meta(self, "name", name);
631 }
632 
shout_get_name(shout_t * self)633 const char *shout_get_name(shout_t *self)
634 {
635     return shout_get_meta(self, "name");
636 }
637 
shout_set_url(shout_t * self,const char * url)638 int shout_set_url(shout_t *self, const char *url)
639 {
640     return shout_set_meta(self, "url", url);
641 }
642 
shout_get_url(shout_t * self)643 const char *shout_get_url(shout_t *self)
644 {
645     return shout_get_meta(self, "url");
646 }
647 
shout_set_genre(shout_t * self,const char * genre)648 int shout_set_genre(shout_t *self, const char *genre)
649 {
650     return shout_set_meta(self, "genre", genre);
651 }
652 
shout_get_genre(shout_t * self)653 const char *shout_get_genre(shout_t *self)
654 {
655     return shout_get_meta(self, "genre");
656 }
657 
shout_set_agent(shout_t * self,const char * agent)658 int shout_set_agent(shout_t *self, const char *agent)
659 {
660     if (!self)
661         return SHOUTERR_INSANE;
662 
663     if (self->connection)
664         return self->error = SHOUTERR_CONNECTED;
665 
666     if (self->useragent)
667         free(self->useragent);
668 
669     if ( !(self->useragent = _shout_util_strdup(agent)) )
670         return self->error = SHOUTERR_MALLOC;
671 
672     return self->error = SHOUTERR_SUCCESS;
673 }
674 
shout_get_agent(shout_t * self)675 const char *shout_get_agent(shout_t *self)
676 {
677     if (!self)
678         return NULL;
679 
680     return self->useragent;
681 }
682 
shout_set_content_language(shout_t * self,const char * content_language)683 int shout_set_content_language(shout_t *self, const char *content_language)
684 {
685     const char *p;
686 
687     if (!self)
688         return SHOUTERR_INSANE;
689 
690     if (!content_language) {
691         if (self->content_language)
692             free(self->content_language);
693         return self->error = SHOUTERR_SUCCESS;
694     }
695 
696     // we do at least a simple check for the correct format. This will at least detect unsafe chars.
697     for (p = content_language; *p; p++) {
698         if (*p == ' ' || *p == ',')
699             continue;
700         if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9'))
701             continue;
702         if (*p == '-')
703             continue;
704 
705         return self->error = SHOUTERR_INSANE;
706     }
707 
708 
709     if (self->content_language)
710         free(self->content_language);
711 
712     if ( !(self->content_language = _shout_util_strdup(content_language)) )
713         return self->error = SHOUTERR_MALLOC;
714 
715     return self->error = SHOUTERR_SUCCESS;
716 }
717 
shout_get_content_language(shout_t * self)718 const char *shout_get_content_language(shout_t *self)
719 {
720     if (!self)
721         return NULL;
722     return self->content_language;
723 }
724 
shout_set_user(shout_t * self,const char * username)725 int shout_set_user(shout_t *self, const char *username)
726 {
727     if (!self)
728         return SHOUTERR_INSANE;
729 
730     if (self->connection)
731         return self->error = SHOUTERR_CONNECTED;
732 
733     if (self->user)
734         free(self->user);
735 
736     if ( !(self->user = _shout_util_strdup(username)) )
737         return self->error = SHOUTERR_MALLOC;
738 
739     return self->error = SHOUTERR_SUCCESS;
740 }
741 
shout_get_user(shout_t * self)742 const char *shout_get_user(shout_t *self)
743 {
744     if (!self)
745         return NULL;
746 
747     return self->user;
748 }
749 
shout_set_description(shout_t * self,const char * description)750 int shout_set_description(shout_t *self, const char *description)
751 {
752     return shout_set_meta(self, "description", description);
753 }
754 
shout_get_description(shout_t * self)755 const char *shout_get_description(shout_t *self)
756 {
757     return shout_get_meta(self, "description");
758 }
759 
shout_set_dumpfile(shout_t * self,const char * dumpfile)760 int shout_set_dumpfile(shout_t *self, const char *dumpfile)
761 {
762     if (!self)
763         return SHOUTERR_INSANE;
764 
765     if (self->connection)
766         return SHOUTERR_CONNECTED;
767 
768     if (self->dumpfile)
769         free(self->dumpfile);
770 
771     if ( !(self->dumpfile = _shout_util_strdup(dumpfile)) )
772         return self->error = SHOUTERR_MALLOC;
773 
774     return self->error = SHOUTERR_SUCCESS;
775 }
776 
shout_get_dumpfile(shout_t * self)777 const char *shout_get_dumpfile(shout_t *self)
778 {
779     if (!self)
780         return NULL;
781 
782     return self->dumpfile;
783 }
784 
shout_set_audio_info(shout_t * self,const char * name,const char * value)785 int shout_set_audio_info(shout_t *self, const char *name, const char *value)
786 {
787     if (!self)
788         return SHOUTERR_INSANE;
789 
790     return self->error = _shout_util_dict_set(self->audio_info, name, value);
791 }
792 
shout_get_audio_info(shout_t * self,const char * name)793 const char *shout_get_audio_info(shout_t *self, const char *name)
794 {
795     if (!self)
796         return NULL;
797 
798     return _shout_util_dict_get(self->audio_info, name);
799 }
800 
shout_set_meta(shout_t * self,const char * name,const char * value)801 int shout_set_meta(shout_t *self, const char *name, const char *value)
802 {
803     size_t i;
804     char c;
805 
806     if (!self || !name)
807         return SHOUTERR_INSANE;
808 
809     if (self->connection)
810         return self->error = SHOUTERR_CONNECTED;
811 
812     for (i = 0; (c = name[i]); i++) {
813         if ((c < 'a' || c > 'z') && (c < '0' || c > '9'))
814             return self->error = SHOUTERR_INSANE;
815     }
816 
817     for (i = 0; (c = value[i]); i++) {
818         if (c == '\r' || c == '\n')
819             return self->error = SHOUTERR_INSANE;
820     }
821 
822     return self->error = _shout_util_dict_set(self->meta, name, value);
823 }
824 
shout_get_meta(shout_t * self,const char * name)825 const char *shout_get_meta(shout_t *self, const char *name)
826 {
827     if (!self)
828         return NULL;
829 
830     return _shout_util_dict_get(self->meta, name);
831 }
832 
shout_set_public(shout_t * self,unsigned int public)833 int shout_set_public(shout_t *self, unsigned int public)
834 {
835     if (!self || (public != 0 && public != 1))
836         return SHOUTERR_INSANE;
837 
838     if (self->connection)
839         return self->error = SHOUTERR_CONNECTED;
840 
841     self->public = public;
842 
843     return self->error = SHOUTERR_SUCCESS;
844 }
845 
shout_get_public(shout_t * self)846 unsigned int shout_get_public(shout_t *self)
847 {
848     if (!self)
849         return 0;
850 
851     return self->public;
852 }
853 
shout_set_format(shout_t * self,unsigned int format)854 int shout_set_format(shout_t *self, unsigned int format)
855 {
856     if (!self)
857         return SHOUTERR_INSANE;
858 
859     if (self->connection)
860         return self->error = SHOUTERR_CONNECTED;
861 
862     switch (format) {
863         case SHOUT_FORMAT_OGG:
864             return shout_set_content_format(self, SHOUT_FORMAT_OGG, SHOUT_USAGE_UNKNOWN, NULL);
865         break;
866         case SHOUT_FORMAT_MP3:
867             return shout_set_content_format(self, SHOUT_FORMAT_MP3, SHOUT_USAGE_AUDIO, NULL);
868         break;
869         case SHOUT_FORMAT_WEBM:
870             return shout_set_content_format(self, SHOUT_FORMAT_WEBM, SHOUT_USAGE_AUDIO|SHOUT_USAGE_VISUAL, NULL);
871         break;
872         case SHOUT_FORMAT_WEBMAUDIO:
873             return shout_set_content_format(self, SHOUT_FORMAT_WEBM, SHOUT_USAGE_AUDIO, NULL);
874         break;
875     }
876 
877     return self->error = SHOUTERR_UNSUPPORTED;
878 }
879 
shout_get_format(shout_t * self)880 unsigned int shout_get_format(shout_t* self)
881 {
882     if (!self)
883         return 0;
884 
885     if (self->format == SHOUT_FORMAT_WEBM && self->usage == SHOUT_USAGE_AUDIO) {
886         return SHOUT_FORMAT_WEBMAUDIO;
887     }
888 
889     return self->format;
890 }
891 
remove_bits(unsigned int value,unsigned int to_remove)892 static inline unsigned int remove_bits(unsigned int value, unsigned int to_remove)
893 {
894     value |= to_remove;
895     value -= to_remove;
896 
897     return value;
898 }
899 
is_audio(unsigned int usage)900 static inline int is_audio(unsigned int usage)
901 {
902     if (!(usage & SHOUT_USAGE_AUDIO))
903         return 0;
904 
905     if (remove_bits(usage, SHOUT_USAGE_AUDIO|SHOUT_USAGE_SUBTITLE))
906         return 0;
907 
908     return 1;
909 }
910 
is_video(unsigned int usage)911 static inline int is_video(unsigned int usage)
912 {
913     if (!(usage & SHOUT_USAGE_VISUAL))
914         return 0;
915 
916     if (remove_bits(usage, SHOUT_USAGE_VISUAL|SHOUT_USAGE_AUDIO|SHOUT_USAGE_SUBTITLE|SHOUT_USAGE_3D|SHOUT_USAGE_4D))
917         return 0;
918 
919     return 1;
920 }
921 
shout_get_mimetype(unsigned int format,unsigned int usage,const char * codecs)922 static const char *shout_get_mimetype(unsigned int format, unsigned int usage, const char *codecs)
923 {
924     if (codecs)
925         return NULL;
926 
927     switch (format) {
928         case SHOUT_FORMAT_OGG:
929             if (is_audio(usage)) {
930                 return "audio/ogg";
931             } else if (is_video(usage)) {
932                 return "video/ogg";
933             } else {
934                 return "application/ogg";
935             }
936         break;
937 
938         case SHOUT_FORMAT_MP3:
939             /* MP3 *ONLY* support Audio. So all other values are outright invalid */
940             if (usage == SHOUT_USAGE_AUDIO) {
941                 return "audio/mpeg";
942             }
943         break;
944         case SHOUT_FORMAT_WEBM:
945             if (is_audio(usage)) {
946                 return "audio/webm";
947             } else if (is_video(usage)) {
948                 return "video/webm";
949             }
950         break;
951         case SHOUT_FORMAT_MATROSKA:
952             if (is_audio(usage)) {
953                 return "audio/x-matroska";
954             } else if (is_video(usage) && (usage & SHOUT_USAGE_3D)) {
955                 return "video/x-matroska-3d";
956             } else if (is_video(usage)) {
957                 return "video/x-matroska";
958             }
959         break;
960     }
961 
962     return NULL;
963 }
964 
shout_get_mimetype_from_self(shout_t * self)965 const char *shout_get_mimetype_from_self(shout_t *self)
966 {
967     return shout_get_mimetype(self->format, self->usage, NULL);
968 }
969 
shout_set_content_format(shout_t * self,unsigned int format,unsigned int usage,const char * codecs)970 int shout_set_content_format(shout_t *self, unsigned int format, unsigned int usage, const char *codecs)
971 {
972     if (!self)
973         return SHOUTERR_INSANE;
974 
975     if (self->connection)
976         return self->error = SHOUTERR_CONNECTED;
977 
978     if (codecs) {
979         return self->error = SHOUTERR_UNSUPPORTED;
980     }
981 
982     if (!shout_get_mimetype(format, usage, codecs)) {
983         return self->error = SHOUTERR_UNSUPPORTED;
984     }
985 
986     self->format = format;
987     self->usage  = usage;
988 
989     return self->error = SHOUTERR_SUCCESS;
990 }
991 
shout_get_content_format(shout_t * self,unsigned int * format,unsigned int * usage,const char ** codecs)992 int shout_get_content_format(shout_t *self, unsigned int *format, unsigned int *usage, const char **codecs)
993 {
994     if (!self)
995         return SHOUTERR_INSANE;
996 
997     if (format)
998         *format = self->format;
999 
1000     if (usage)
1001         *usage = self->usage;
1002 
1003     if (codecs)
1004         *codecs = NULL;
1005 
1006     return self->error = SHOUTERR_SUCCESS;
1007 }
1008 
shout_set_protocol(shout_t * self,unsigned int protocol)1009 int shout_set_protocol(shout_t *self, unsigned int protocol)
1010 {
1011     if (!self)
1012         return SHOUTERR_INSANE;
1013 
1014     if (self->connection)
1015         return self->error = SHOUTERR_CONNECTED;
1016 
1017     if (protocol != SHOUT_PROTOCOL_HTTP &&
1018         protocol != SHOUT_PROTOCOL_XAUDIOCAST &&
1019         protocol != SHOUT_PROTOCOL_ICY &&
1020         protocol != SHOUT_PROTOCOL_ROARAUDIO) {
1021         return self->error = SHOUTERR_UNSUPPORTED;
1022     }
1023 
1024     self->protocol = protocol;
1025 
1026     return self->error = SHOUTERR_SUCCESS;
1027 }
1028 
shout_get_protocol(shout_t * self)1029 unsigned int shout_get_protocol(shout_t *self)
1030 {
1031     if (!self)
1032         return 0;
1033 
1034     return self->protocol;
1035 }
1036 
shout_set_nonblocking(shout_t * self,unsigned int nonblocking)1037 int shout_set_nonblocking(shout_t *self, unsigned int nonblocking)
1038 {
1039     if (nonblocking == SHOUT_BLOCKING_DEFAULT)
1040         nonblocking = SHOUT_BLOCKING_FULL;
1041 
1042     if (!self || (nonblocking != SHOUT_BLOCKING_FULL && nonblocking != SHOUT_BLOCKING_NONE))
1043         return SHOUTERR_INSANE;
1044 
1045     if (self->connection)
1046         return self->error = SHOUTERR_CONNECTED;
1047 
1048     self->nonblocking = nonblocking;
1049 
1050     return SHOUTERR_SUCCESS;
1051 }
1052 
shout_get_nonblocking(shout_t * self)1053 unsigned int shout_get_nonblocking(shout_t *self)
1054 {
1055     if (!self)
1056         return 0;
1057 
1058     return self->nonblocking;
1059 }
1060 
1061 /* TLS functions */
1062 #ifdef HAVE_OPENSSL
shout_set_tls(shout_t * self,int mode)1063 int shout_set_tls(shout_t *self, int mode)
1064 {
1065     if (!self)
1066         return SHOUTERR_INSANE;
1067 
1068     if (mode != SHOUT_TLS_DISABLED &&
1069         mode != SHOUT_TLS_AUTO &&
1070         mode != SHOUT_TLS_AUTO_NO_PLAIN &&
1071         mode != SHOUT_TLS_RFC2818 &&
1072         mode != SHOUT_TLS_RFC2817)
1073         return self->error = SHOUTERR_UNSUPPORTED;
1074 
1075     self->tls_mode = mode;
1076     return SHOUTERR_SUCCESS;
1077 }
shout_get_tls(shout_t * self)1078 int shout_get_tls(shout_t *self)
1079 {
1080     if (!self)
1081         return SHOUTERR_INSANE;
1082 
1083     return self->tls_mode;
1084 }
1085 
shout_set_ca_directory(shout_t * self,const char * directory)1086 int shout_set_ca_directory(shout_t *self, const char *directory)
1087 {
1088     if (!self)
1089         return SHOUTERR_INSANE;
1090 
1091     if (self->connection)
1092         return self->error = SHOUTERR_CONNECTED;
1093 
1094     if (self->ca_directory)
1095         free(self->ca_directory);
1096 
1097     if (!(self->ca_directory = _shout_util_strdup(directory)))
1098         return self->error = SHOUTERR_MALLOC;
1099 
1100     return self->error = SHOUTERR_SUCCESS;
1101 }
1102 
shout_get_ca_directory(shout_t * self)1103 const char *shout_get_ca_directory(shout_t *self)
1104 {
1105     if (!self)
1106         return NULL;
1107 
1108     return self->ca_directory;
1109 }
1110 
shout_set_ca_file(shout_t * self,const char * file)1111 int shout_set_ca_file(shout_t *self, const char *file)
1112 {
1113     if (!self)
1114         return SHOUTERR_INSANE;
1115 
1116     if (self->connection)
1117         return self->error = SHOUTERR_CONNECTED;
1118 
1119     if (self->ca_file)
1120         free(self->ca_file);
1121 
1122     if (!(self->ca_file = _shout_util_strdup(file)))
1123         return self->error = SHOUTERR_MALLOC;
1124 
1125     return self->error = SHOUTERR_SUCCESS;
1126 }
1127 
shout_get_ca_file(shout_t * self)1128 const char *shout_get_ca_file(shout_t *self)
1129 {
1130     if (!self)
1131         return NULL;
1132 
1133     return self->ca_file;
1134 }
1135 
shout_set_allowed_ciphers(shout_t * self,const char * ciphers)1136 int shout_set_allowed_ciphers(shout_t *self, const char *ciphers)
1137 {
1138     if (!self)
1139         return SHOUTERR_INSANE;
1140 
1141     if (self->connection)
1142         return self->error = SHOUTERR_CONNECTED;
1143 
1144     if (self->allowed_ciphers)
1145         free(self->allowed_ciphers);
1146 
1147     if (!(self->allowed_ciphers = _shout_util_strdup(ciphers)))
1148         return self->error = SHOUTERR_MALLOC;
1149 
1150     return self->error = SHOUTERR_SUCCESS;
1151 }
1152 
shout_get_allowed_ciphers(shout_t * self)1153 const char *shout_get_allowed_ciphers(shout_t *self)
1154 {
1155     if (!self)
1156         return NULL;
1157 
1158     return self->allowed_ciphers;
1159 }
1160 
shout_set_client_certificate(shout_t * self,const char * certificate)1161 int shout_set_client_certificate(shout_t *self, const char *certificate)
1162 {
1163     if (!self)
1164         return SHOUTERR_INSANE;
1165 
1166     if (self->connection)
1167         return self->error = SHOUTERR_CONNECTED;
1168 
1169     if (self->client_certificate)
1170         free(self->client_certificate);
1171 
1172     if (!(self->client_certificate = _shout_util_strdup(certificate)))
1173         return self->error = SHOUTERR_MALLOC;
1174 
1175     return self->error = SHOUTERR_SUCCESS;
1176 }
1177 
shout_get_client_certificate(shout_t * self)1178 const char *shout_get_client_certificate(shout_t *self)
1179 {
1180     if (!self)
1181         return NULL;
1182 
1183     return self->client_certificate;
1184 }
1185 #else
shout_set_tls(shout_t * self,int mode)1186 int shout_set_tls(shout_t *self, int mode)
1187 {
1188     if (!self)
1189         return SHOUTERR_INSANE;
1190 
1191     if (mode != SHOUT_TLS_DISABLED &&
1192         mode != SHOUT_TLS_AUTO)
1193         return self->error = SHOUTERR_UNSUPPORTED;
1194 
1195     return SHOUTERR_SUCCESS;
1196 }
1197 
shout_get_tls(shout_t * self)1198 int shout_get_tls(shout_t *self)
1199 {
1200     return SHOUT_TLS_DISABLED;
1201 }
1202 
shout_set_ca_directory(shout_t * self,const char * directory)1203 int shout_set_ca_directory(shout_t *self, const char *directory)
1204 {
1205     if (!self)
1206         return SHOUTERR_INSANE;
1207 
1208     return self->error = SHOUTERR_UNSUPPORTED;
1209 }
1210 
shout_get_ca_directory(shout_t * self)1211 const char *shout_get_ca_directory(shout_t *self)
1212 {
1213     return NULL;
1214 }
1215 
shout_set_ca_file(shout_t * self,const char * file)1216 int shout_set_ca_file(shout_t *self, const char *file)
1217 {
1218     if (!self)
1219         return SHOUTERR_INSANE;
1220 
1221     return self->error = SHOUTERR_UNSUPPORTED;
1222 }
1223 
shout_get_ca_file(shout_t * self)1224 const char *shout_get_ca_file(shout_t *self)
1225 {
1226     return NULL;
1227 }
1228 
shout_set_allowed_ciphers(shout_t * self,const char * ciphers)1229 int shout_set_allowed_ciphers(shout_t *self, const char *ciphers)
1230 {
1231     if (!self)
1232         return SHOUTERR_INSANE;
1233 
1234     return self->error = SHOUTERR_UNSUPPORTED;
1235 }
shout_get_allowed_ciphers(shout_t * self)1236 const char *shout_get_allowed_ciphers(shout_t *self)
1237 {
1238     return NULL;
1239 }
1240 
shout_set_client_certificate(shout_t * self,const char * certificate)1241 int shout_set_client_certificate(shout_t *self, const char *certificate)
1242 {
1243     if (!self)
1244         return SHOUTERR_INSANE;
1245     return self->error = SHOUTERR_UNSUPPORTED;
1246 }
1247 
shout_get_client_certificate(shout_t * self)1248 const char *shout_get_client_certificate(shout_t *self)
1249 {
1250     return NULL;
1251 }
1252 #endif
1253 
shout_control(shout_t * self,shout_control_t control,...)1254 int shout_control(shout_t *self, shout_control_t control, ...)
1255 {
1256     int ret = SHOUTERR_INSANE;
1257     va_list ap;
1258 
1259     if (!self)
1260         return SHOUTERR_INSANE;
1261 
1262     va_start(ap, control);
1263 
1264     switch (control) {
1265 #ifdef HAVE_OPENSSL
1266         case SHOUT_CONTROL_GET_SERVER_CERTIFICATE_AS_PEM:
1267         case SHOUT_CONTROL_GET_SERVER_CERTIFICATE_CHAIN_AS_PEM:
1268             if (self->connection->tls) {
1269                 void **vpp = va_arg(ap, void **);
1270                 if (vpp) {
1271                     ret = shout_connection_control(self->connection, control, vpp);
1272                 } else {
1273                     ret = SHOUTERR_INSANE;
1274                 }
1275             } else {
1276                 ret = SHOUTERR_BUSY;
1277             }
1278         break;
1279 #else
1280         case SHOUT_CONTROL_GET_SERVER_CERTIFICATE_AS_PEM:
1281         case SHOUT_CONTROL_GET_SERVER_CERTIFICATE_CHAIN_AS_PEM:
1282             ret = SHOUTERR_UNSUPPORTED;
1283         break;
1284 #endif
1285         case SHOUT_CONTROL__MIN:
1286         case SHOUT_CONTROL__MAX:
1287             ret = SHOUTERR_INSANE;
1288         break;
1289     }
1290 
1291     va_end(ap);
1292 
1293     return self->error = ret;
1294 }
shout_set_callback(shout_t * self,shout_callback_t callback,void * userdata)1295 int shout_set_callback(shout_t *self, shout_callback_t callback, void *userdata)
1296 {
1297     if (!self)
1298         return SHOUTERR_INSANE;
1299 
1300     self->callback = callback;
1301     self->callback_userdata = userdata;
1302 
1303     return self->error = SHOUTERR_SUCCESS;
1304 }
1305 
1306 /* -- static function definitions -- */
shout_call_callback(shout_t * self,shout_event_t event,...)1307 static int shout_call_callback(shout_t *self, shout_event_t event, ...)
1308 {
1309     va_list ap;
1310     int ret;
1311 
1312     if (!self->callback)
1313         return SHOUT_CALLBACK_PASS;
1314 
1315     va_start(ap, event);
1316     ret = self->callback(self, event, self->callback_userdata, ap);
1317     va_end(ap);
1318 
1319     return ret;
1320 }
shout_cb_connection_callback(shout_connection_t * con,shout_event_t event,void * userdata,va_list ap)1321 static int shout_cb_connection_callback(shout_connection_t *con, shout_event_t event, void *userdata, va_list ap)
1322 {
1323     shout_t *self = userdata;
1324 
1325     /* Avoid going up if not needed */
1326     if (!self->callback)
1327         return SHOUT_CALLBACK_PASS;
1328 
1329     switch (event) {
1330         case SHOUT_EVENT_TLS_CHECK_PEER_CERTIFICATE:
1331             return shout_call_callback(self, event, con);
1332         break;
1333         case SHOUT_EVENT__MIN:
1334         case SHOUT_EVENT__MAX:
1335             return SHOUTERR_INSANE;
1336         break;
1337     }
1338 
1339     return SHOUT_CALLBACK_PASS;
1340 }
1341 
try_connect(shout_t * self)1342 static int try_connect(shout_t *self)
1343 {
1344     int ret;
1345 
1346     if (!self->connection) {
1347         const shout_protocol_impl_t *impl = NULL;
1348 
1349         switch (shout_get_protocol(self)) {
1350             case SHOUT_PROTOCOL_HTTP:
1351                 impl = shout_http_impl;
1352                 memset(&(self->source_plan.http), 0, sizeof(self->source_plan.http));
1353                 self->source_plan.http.is_source = 1;
1354                 self->source_plan.http.auth = 1;
1355                 self->source_plan.http.resource = self->mount;
1356             break;
1357             case SHOUT_PROTOCOL_XAUDIOCAST:
1358                 impl = shout_xaudiocast_impl;
1359             break;
1360             case SHOUT_PROTOCOL_ICY:
1361                 impl = shout_icy_impl;
1362             break;
1363             case SHOUT_PROTOCOL_ROARAUDIO:
1364                 impl = shout_roaraudio_impl;
1365             break;
1366         }
1367 
1368         self->connection = shout_connection_new(self, impl, &(self->source_plan));
1369         if (!self->connection)
1370             return self->error = SHOUTERR_MALLOC;
1371 
1372         shout_connection_set_callback(self->connection, shout_cb_connection_callback, self);
1373 
1374 #ifdef HAVE_OPENSSL
1375         shout_connection_select_tlsmode(self->connection, self->tls_mode);
1376 #endif
1377         self->connection->target_message_state = SHOUT_MSGSTATE_SENDING1;
1378         shout_connection_connect(self->connection, self);
1379     }
1380 
1381     ret = shout_connection_iter(self->connection, self);
1382     self->error = ret;
1383 
1384     if (self->connection->current_message_state == SHOUT_MSGSTATE_SENDING1 && !self->send) {
1385         int rc;
1386         switch (self->format) {
1387             case SHOUT_FORMAT_OGG:
1388                 rc = self->error = shout_open_ogg(self);
1389                 break;
1390             case SHOUT_FORMAT_MP3:
1391                 rc = self->error = shout_open_mp3(self);
1392                 break;
1393             case SHOUT_FORMAT_WEBM:
1394             case SHOUT_FORMAT_MATROSKA:
1395                 rc = self->error = shout_open_webm(self);
1396                 break;
1397 
1398             default:
1399                 rc = SHOUTERR_INSANE;
1400                 break;
1401         }
1402         if (rc != SHOUTERR_SUCCESS) {
1403             return ret;
1404         }
1405     }
1406 
1407     return ret;
1408 }
1409