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