shout.c 31.9 KB
Newer Older
1
/* -*- c-basic-offset: 8; -*- */
2
3
/* shout.c: Implementation of public libshout interface shout.h
 *
Philipp Schafft's avatar
Philipp Schafft committed
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
8
9
10
11
12
13
14
15
16
17
18
19
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the Free
 *  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
27
#endif

28
#include <stdarg.h>
Jack Moffitt's avatar
Jack Moffitt committed
29
#include <stdio.h>
30
#include <stdlib.h>
Jack Moffitt's avatar
Jack Moffitt committed
31
#include <string.h>
32
33
34
#ifdef HAVE_STRINGS_H
#   include <strings.h>
#endif
Michael Smith's avatar
Michael Smith committed
35
#include <errno.h>
Jack Moffitt's avatar
Jack Moffitt committed
36

37
#include <shout/shout.h>
38

Marvin Scholz's avatar
Marvin Scholz committed
39
#include <common/net/sock.h>
40
41
#include <common/timing/timing.h>
#include <common/httpp/httpp.h>
Jack Moffitt's avatar
Jack Moffitt committed
42

43
#include "shout_private.h"
44
#include "util.h"
Jack Moffitt's avatar
Jack Moffitt committed
45

brendan's avatar
brendan committed
46
#ifdef _MSC_VER
47
48
49
50
51
#   ifndef va_copy
#       define va_copy(ap1, ap2) memcpy(&ap1, &ap2, sizeof(va_list))
#   endif
#   define vsnprintf      _vsnprintf
#   define inline         _inline
52
53
#endif

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);
Philipp Schafft's avatar
Philipp Schafft committed
57

58
59
60
/* -- static data -- */
static int _initialized = 0;

61
/* -- public functions -- */
Jack Moffitt's avatar
Jack Moffitt committed
62

63
64
void shout_init(void)
{
65
    if (_initialized)
66
        return;
67

68
69
    sock_initialize();
    _initialized = 1;
70
71
72
73
}

void shout_shutdown(void)
{
74
    if (!_initialized)
75
        return;
76

77
78
    sock_shutdown();
    _initialized = 0;
79
80
}

81
82
shout_t *shout_new(void)
{
83
    shout_t *self;
Jack Moffitt's avatar
Jack Moffitt committed
84

85
86
87
    /* in case users haven't done this explicitly. Should we error
     * if not initialized instead? */
    shout_init();
Jack Moffitt's avatar
Jack Moffitt committed
88

89
90
91
92
93
94
95
96
    if (!(self = (shout_t*)calloc(1, sizeof(shout_t)))) {
        return NULL;
    }

    if (shout_set_host(self, LIBSHOUT_DEFAULT_HOST) != SHOUTERR_SUCCESS) {
        shout_free(self);
        return NULL;
    }
97

98
99
100
101
    if (shout_set_user(self, LIBSHOUT_DEFAULT_USER) != SHOUTERR_SUCCESS) {
        shout_free(self);
        return NULL;
    }
102

103
104
105
106
    if (shout_set_agent(self, LIBSHOUT_DEFAULT_USERAGENT) != SHOUTERR_SUCCESS) {
        shout_free(self);
        return NULL;
    }
107

108
109
110
111
    if (!(self->audio_info = _shout_util_dict_new())) {
        shout_free(self);
        return NULL;
    }
112

113
114
115
116
    if (!(self->meta = _shout_util_dict_new())) {
        shout_free(self);
        return NULL;
    }
117

118
119
120
121
    if (shout_set_meta(self, "name", "no name") != SHOUTERR_SUCCESS) {
        shout_free(self);
        return NULL;
    }
Philipp Schafft's avatar
Philipp Schafft committed
122

Philipp Schafft's avatar
Philipp Schafft committed
123
#ifdef HAVE_OPENSSL
124
125
126
127
    if (shout_set_allowed_ciphers(self, LIBSHOUT_DEFAULT_ALLOWED_CIPHERS) != SHOUTERR_SUCCESS) {
        shout_free(self);
        return NULL;
    }
128

129
    self->tls_mode      = SHOUT_TLS_AUTO;
Philipp Schafft's avatar
Philipp Schafft committed
130
#endif
Jack Moffitt's avatar
Jack Moffitt committed
131

132
133
    self->port      = LIBSHOUT_DEFAULT_PORT;
    self->format    = LIBSHOUT_DEFAULT_FORMAT;
134
    self->usage     = LIBSHOUT_DEFAULT_USAGE;
135
    self->protocol  = LIBSHOUT_DEFAULT_PROTOCOL;
Jack Moffitt's avatar
Jack Moffitt committed
136

137
    return self;
Jack Moffitt's avatar
Jack Moffitt committed
138
139
}

140
void shout_free(shout_t *self)
Jack Moffitt's avatar
Jack Moffitt committed
141
{
142
143
    if (!self)
        return;
144

145
    if (!self->connection)
146
        return;
Philipp Schafft's avatar
Philipp Schafft committed
147

148
149
150
151
152
153
154
155
156
157
158
159
160
161
    if (self->host)
        free(self->host);
    if (self->password)
        free(self->password);
    if (self->mount)
        free(self->mount);
    if (self->user)
        free(self->user);
    if (self->useragent)
        free(self->useragent);
    if (self->audio_info)
        _shout_util_dict_free (self->audio_info);
    if (self->meta)
        _shout_util_dict_free (self->meta);
162

Philipp Schafft's avatar
Philipp Schafft committed
163
#ifdef HAVE_OPENSSL
164
165
166
167
168
169
170
171
    if (self->ca_directory)
        free(self->ca_directory);
    if (self->ca_file)
        free(self->ca_file);
    if (self->allowed_ciphers)
        free(self->allowed_ciphers);
    if (self->client_certificate)
        free(self->client_certificate);
Philipp Schafft's avatar
Philipp Schafft committed
172
173
#endif

174
    free(self);
Jack Moffitt's avatar
Jack Moffitt committed
175
176
}

177
int shout_open(shout_t *self)
Jack Moffitt's avatar
Jack Moffitt committed
178
{
179
    /* sanity check */
180
    if (!self)
181
        return SHOUTERR_INSANE;
182
    if (self->connection)
183
        return SHOUTERR_CONNECTED;
184
    if (!self->host || !self->password || !self->port)
185
        return self->error = SHOUTERR_INSANE;
186
    if (self->format == SHOUT_FORMAT_OGG &&  (self->protocol != SHOUT_PROTOCOL_HTTP && self->protocol != SHOUT_PROTOCOL_ROARAUDIO))
187
        return self->error = SHOUTERR_UNSUPPORTED;
188

189
    return self->error = try_connect(self);
Jack Moffitt's avatar
Jack Moffitt committed
190
191
}

192
193

int shout_close(shout_t *self)
Jack Moffitt's avatar
Jack Moffitt committed
194
{
195
    if (!self)
196
        return SHOUTERR_INSANE;
197

198
    if (!self->connection)
199
        return self->error = SHOUTERR_UNCONNECTED;
Jack Moffitt's avatar
Jack Moffitt committed
200

201
    if (self->connection && self->connection->current_message_state == SHOUT_MSGSTATE_SENDING1 && self->close)
202
        self->close(self);
Jack Moffitt's avatar
Jack Moffitt committed
203

204
    shout_connection_unref(self->connection);
205
    self->connection = NULL;
206
207
    self->starttime = 0;
    self->senttime = 0;
Jack Moffitt's avatar
Jack Moffitt committed
208

209
    return self->error = SHOUTERR_SUCCESS;
210
}
Jack Moffitt's avatar
Jack Moffitt committed
211

212
213
int shout_send(shout_t *self, const unsigned char *data, size_t len)
{
214
    if (!self)
215
        return SHOUTERR_INSANE;
216

217
    if (!self->connection || self->connection->current_message_state != SHOUT_MSGSTATE_SENDING1)
218
        return self->error = SHOUTERR_UNCONNECTED;
219

220
    if (self->starttime <= 0)
221
        self->starttime = timing_get_time();
222

223
    if (!len)
224
        return shout_connection_iter(self->connection, self);
225

226
    return self->send(self, data, len);
227
228
229
230
}

ssize_t shout_send_raw(shout_t *self, const unsigned char *data, size_t len)
{
231
232
    ssize_t ret;

233
    if (!self)
234
        return SHOUTERR_INSANE;
235

236
    if (!self->connection || self->connection->current_message_state != SHOUT_MSGSTATE_SENDING1)
237
        return SHOUTERR_UNCONNECTED;
238

239
240
241
242
    ret = shout_connection_send(self->connection, self, data, len);
    if (ret < 0)
       shout_connection_transfer_error(self->connection, self);
    return ret;
Jack Moffitt's avatar
Jack Moffitt committed
243
244
}

245
246
ssize_t shout_queuelen(shout_t *self)
{
247
    if (!self)
248
        return -1;
249

250
    return shout_connection_get_sendq(self->connection, self);
251
252
253
}


254
void shout_sync(shout_t *self)
Jack Moffitt's avatar
Jack Moffitt committed
255
{
256
    int64_t sleep;
Jack Moffitt's avatar
Jack Moffitt committed
257

258
    if (!self)
259
        return;
260

261
    if (self->senttime == 0)
262
        return;
Jack Moffitt's avatar
Jack Moffitt committed
263

264
    sleep = self->senttime / 1000 - (timing_get_time() - self->starttime);
265
    if (sleep > 0)
266
        timing_sleep((uint64_t)sleep);
267

Jack Moffitt's avatar
Jack Moffitt committed
268
269
}

270
271
int shout_delay(shout_t *self)
{
272

273
    if (!self)
274
        return 0;
275

276
    if (self->senttime == 0)
277
        return 0;
278

279
    return self->senttime / 1000 - (timing_get_time() - self->starttime);
280
}
281

brendan's avatar
brendan committed
282
shout_metadata_t *shout_metadata_new(void)
Michael Smith's avatar
Michael Smith committed
283
{
284
    return _shout_util_dict_new();
Michael Smith's avatar
Michael Smith committed
285
286
}

brendan's avatar
brendan committed
287
288
void shout_metadata_free(shout_metadata_t *self)
{
289
    if (!self)
290
        return;
brendan's avatar
brendan committed
291

292
    _shout_util_dict_free(self);
brendan's avatar
brendan committed
293
294
295
296
}

int shout_metadata_add(shout_metadata_t *self, const char *name, const char *value)
{
297
    if (!self || !name)
298
        return SHOUTERR_INSANE;
brendan's avatar
brendan committed
299

300
    return _shout_util_dict_set(self, name, value);
brendan's avatar
brendan committed
301
302
303
304
}

int shout_set_metadata(shout_t *self, shout_metadata_t *metadata)
{
305
306
307
308
309
    shout_connection_t *connection;
    shout_http_plan_t plan;
    size_t param_len;
    char *param = NULL;
    char *encvalue;
310
311
    char *encpassword;
    char *encmount;
312
    const char *param_template;
313
314
    int ret;
    int error;
315

316
    if (!self || !metadata)
317
        return SHOUTERR_INSANE;
brendan's avatar
brendan committed
318

319
320
321
    encvalue = _shout_util_dict_urlencode(metadata, '&');
    if (!encvalue)
        return self->error = SHOUTERR_MALLOC;
322

323
324
325
    memset(&plan, 0, sizeof(plan));

    plan.is_source = 0;
326

327
    switch (self->protocol) {
328
        case SHOUT_PROTOCOL_ICY:
329
330
331
332
333
            if (!(encpassword = _shout_util_url_encode(self->password))) {
                free(encvalue);
                return self->error = SHOUTERR_MALLOC;
            }

334
            param_template = "mode=updinfo&pass=%s&%s";
335
            param_len = strlen(param_template) + strlen(encvalue) + 1 + strlen(encpassword);
336
337
            param = malloc(param_len);
            if (!param) {
338
                free(encpassword);
339
340
341
                free(encvalue);
                return self->error = SHOUTERR_MALLOC;
            }
342
343
            snprintf(param, param_len, param_template, encpassword, encvalue);
            free(encpassword);
344
345
346
347
348
349

            plan.param = param;
            plan.fake_ua = 1;
            plan.auth = 0;
            plan.method = "GET";
            plan.resource = "/admin.cgi";
350
        break;
351
        case SHOUT_PROTOCOL_HTTP:
352
353
354
355
356
            if (!(encmount = _shout_util_url_encode(self->mount))) {
                free(encvalue);
                return self->error = SHOUTERR_MALLOC;
            }

357
            param_template = "mode=updinfo&mount=%s&%s";
358
            param_len = strlen(param_template) + strlen(encvalue) + 1 + strlen(encmount);
359
360
            param = malloc(param_len);
            if (!param) {
361
                free(encmount);
362
363
364
                free(encvalue);
                return self->error = SHOUTERR_MALLOC;
            }
365
366
            snprintf(param, param_len, param_template, encmount, encvalue);
            free(encmount);
367
368
369
370

            plan.param = param;
            plan.auth = 1;
            plan.resource = "/admin/metadata";
371
        break;
372
        case SHOUT_PROTOCOL_XAUDIOCAST:
373
374
375
376
377
378
379
380
381
382
            if (!(encmount = _shout_util_url_encode(self->mount))) {
                free(encvalue);
                return self->error = SHOUTERR_MALLOC;
            }
            if (!(encpassword = _shout_util_url_encode(self->password))) {
                free(encmount);
                free(encvalue);
                return self->error = SHOUTERR_MALLOC;
            }

383
            param_template = "mode=updinfo&pass=%s&mount=%s&%s";
384
            param_len = strlen(param_template) + strlen(encvalue) + 1 + strlen(encpassword) + strlen(self->mount);
385
386
            param = malloc(param_len);
            if (!param) {
387
388
                free(encpassword);
                free(encmount);
389
390
391
                free(encvalue);
                return self->error = SHOUTERR_MALLOC;
            }
392
393
394
            snprintf(param, param_len, param_template, encpassword, encmount, encvalue);
            free(encpassword);
            free(encmount);
395
396
397
398
399

            plan.param = param;
            plan.auth = 0;
            plan.method = "GET";
            plan.resource = "/admin.cgi";
400
        break;
401
402
403
404
        default:
            free(encvalue);
            return self->error = SHOUTERR_UNSUPPORTED;
        break;
405
    }
406

407
    free(encvalue);
408

409
410
411
412
413
    connection = shout_connection_new(self, shout_http_impl, &plan);
    if (!connection) {
        free(param);
        return self->error = SHOUTERR_MALLOC;
    }
414

415
416
    shout_connection_set_callback(self->connection, shout_cb_connection_callback, self);

417
#ifdef HAVE_OPENSSL
418
    shout_connection_select_tlsmode(connection, self->tls_mode);
419
#endif
420
    shout_connection_set_nonblocking(connection, 0);
421

422
    connection->target_message_state = SHOUT_MSGSTATE_PARSED_FINAL;
423
424

    shout_connection_connect(connection, self);
425

426
    ret = shout_connection_iter(connection, self);
427
    error = shout_connection_get_error(connection);
428

429
    shout_connection_unref(connection);
430

431
    free(param);
432

433
434
435
436
437
    if (ret == 0) {
        return SHOUTERR_SUCCESS;
    } else {
        return error;
    }
brendan's avatar
brendan committed
438
439
440
441
442
}

/* getters/setters */
const char *shout_version(int *major, int *minor, int *patch)
{
443
    if (major)
444
        *major = LIBSHOUT_MAJOR;
445
    if (minor)
446
        *minor = LIBSHOUT_MINOR;
447
    if (patch)
448
        *patch = LIBSHOUT_MICRO;
brendan's avatar
brendan committed
449

450
    return VERSION;
brendan's avatar
brendan committed
451
452
453
454
}

int shout_get_errno(shout_t *self)
{
455
    return self->error;
brendan's avatar
brendan committed
456
}
Michael Smith's avatar
Michael Smith committed
457

458
const char *shout_get_error(shout_t *self)
Jack Moffitt's avatar
Jack Moffitt committed
459
{
460
    if (!self)
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
        return "Invalid shout_t";

    switch (self->error) {
    case SHOUTERR_SUCCESS:
        return "No error";
    case SHOUTERR_INSANE:
        return "Nonsensical arguments";
    case SHOUTERR_NOCONNECT:
        return "Couldn't connect";
    case SHOUTERR_NOLOGIN:
        return "Login failed";
    case SHOUTERR_SOCKET:
        return "Socket error";
    case SHOUTERR_MALLOC:
        return "Out of memory";
    case SHOUTERR_CONNECTED:
        return "Cannot set parameter while connected";
    case SHOUTERR_UNCONNECTED:
        return "Not connected";
    case SHOUTERR_BUSY:
        return "Socket is busy";
    case SHOUTERR_UNSUPPORTED:
        return "This libshout doesn't support the requested option";
    case SHOUTERR_NOTLS:
        return "TLS requested but not supported by peer";
    case SHOUTERR_TLSBADCERT:
        return "TLS connection can not be established because of bad certificate";
    case SHOUTERR_RETRY:
        return "Please retry current operation.";
    default:
        return "Unknown error";
    }
Jack Moffitt's avatar
Jack Moffitt committed
493
}
494

495
496
497
498
499
/* Returns:
 *   SHOUTERR_CONNECTED if the connection is open,
 *   SHOUTERR_UNCONNECTED if it has not yet been opened,
 *   or an error from try_connect, including SHOUTERR_BUSY
 */
brendan's avatar
brendan committed
500
501
int shout_get_connected(shout_t *self)
{
502
    int rc;
503

504
    if (!self)
505
        return SHOUTERR_INSANE;
506

507
    if (self->connection && self->connection->current_message_state == SHOUT_MSGSTATE_SENDING1)
508
        return SHOUTERR_CONNECTED;
509
    if (self->connection && self->connection->current_message_state != SHOUT_MSGSTATE_SENDING1) {
510
        if ((rc = try_connect(self)) == SHOUTERR_SUCCESS)
511
512
513
            return SHOUTERR_CONNECTED;
        return rc;
    }
514

515
    return SHOUTERR_UNCONNECTED;
brendan's avatar
brendan committed
516
517
}

518
519
int shout_set_host(shout_t *self, const char *host)
{
520
    if (!self)
521
        return SHOUTERR_INSANE;
522

523
    if (self->connection)
524
        return self->error = SHOUTERR_CONNECTED;
525

526
    if (self->host)
527
        free(self->host);
528

529
    if ( !(self->host = _shout_util_strdup(host)) )
530
        return self->error = SHOUTERR_MALLOC;
531

532
    return self->error = SHOUTERR_SUCCESS;
533
534
535
536
}

const char *shout_get_host(shout_t *self)
{
537
    if (!self)
538
        return NULL;
539

540
    return self->host;
541
542
543
544
}

int shout_set_port(shout_t *self, unsigned short port)
{
545
    if (!self)
546
        return SHOUTERR_INSANE;
547

548
    if (self->connection)
549
        return self->error = SHOUTERR_CONNECTED;
550

551
    self->port = port;
552

553
    return self->error = SHOUTERR_SUCCESS;
554
555
556
557
}

unsigned short shout_get_port(shout_t *self)
{
558
    if (!self)
559
        return 0;
560

561
    return self->port;
562
563
564
565
}

int shout_set_password(shout_t *self, const char *password)
{
566
    if (!self)
567
        return SHOUTERR_INSANE;
568

569
    if (self->connection)
570
        return self->error = SHOUTERR_CONNECTED;
571

572
    if (self->password)
573
        free(self->password);
574

575
    if ( !(self->password = _shout_util_strdup(password)) )
576
        return self->error = SHOUTERR_MALLOC;
577

578
    return self->error = SHOUTERR_SUCCESS;
579
580
581
582
}

const char* shout_get_password(shout_t *self)
{
583
    if (!self)
584
        return NULL;
585

586
    return self->password;
587
588
589
590
}

int shout_set_mount(shout_t *self, const char *mount)
{
591
    size_t len;
592

593
    if (!self || !mount)
594
        return SHOUTERR_INSANE;
595

596
    if (self->connection)
597
598
        return self->error = SHOUTERR_CONNECTED;

599
    if (self->mount)
600
        free(self->mount);
601

602
    len = strlen(mount) + 1;
603
    if (mount[0] != '/')
604
        len++;
605

606
    if ( !(self->mount = malloc(len)) )
607
        return self->error = SHOUTERR_MALLOC;
608

609
    snprintf(self->mount, len, "%s%s", mount[0] == '/' ? "" : "/", mount);
610

611
    return self->error = SHOUTERR_SUCCESS;
612
613
614
615
}

const char *shout_get_mount(shout_t *self)
{
616
    if (!self)
617
        return NULL;
618

619
    return self->mount;
620
621
622
623
}

int shout_set_name(shout_t *self, const char *name)
{
624
    return shout_set_meta(self, "name", name);
625
626
627
628
}

const char *shout_get_name(shout_t *self)
{
629
    return shout_get_meta(self, "name");
630
631
632
633
}

int shout_set_url(shout_t *self, const char *url)
{
634
    return shout_set_meta(self, "url", url);
635
636
637
638
}

const char *shout_get_url(shout_t *self)
{
639
    return shout_get_meta(self, "url");
640
641
642
643
}

int shout_set_genre(shout_t *self, const char *genre)
{
644
    return shout_set_meta(self, "genre", genre);
645
646
647
648
}

const char *shout_get_genre(shout_t *self)
{
649
    return shout_get_meta(self, "genre");
650
651
}

652
653
int shout_set_agent(shout_t *self, const char *agent)
{
654
    if (!self)
655
        return SHOUTERR_INSANE;
656

657
    if (self->connection)
658
        return self->error = SHOUTERR_CONNECTED;
659

660
    if (self->useragent)
661
        free(self->useragent);
662

663
    if ( !(self->useragent = _shout_util_strdup(agent)) )
664
        return self->error = SHOUTERR_MALLOC;
665

666
    return self->error = SHOUTERR_SUCCESS;
667
668
669
670
}

const char *shout_get_agent(shout_t *self)
{
671
    if (!self)
672
        return NULL;
673

674
    return self->useragent;
675
676
677
}


678
679
int shout_set_user(shout_t *self, const char *username)
{
680
    if (!self)
681
        return SHOUTERR_INSANE;
682

683
    if (self->connection)
684
        return self->error = SHOUTERR_CONNECTED;
685

686
    if (self->user)
687
        free(self->user);
688

689
    if ( !(self->user = _shout_util_strdup(username)) )
690
        return self->error = SHOUTERR_MALLOC;
691

692
    return self->error = SHOUTERR_SUCCESS;
693
694
695
696
}

const char *shout_get_user(shout_t *self)
{
697
    if (!self)
698
        return NULL;
699

700
    return self->user;
701
702
}

703
704
int shout_set_description(shout_t *self, const char *description)
{
705
    return shout_set_meta(self, "description", description);
706
707
708
709
}

const char *shout_get_description(shout_t *self)
{
710
    return shout_get_meta(self, "description");
711
712
}

713
714
int shout_set_dumpfile(shout_t *self, const char *dumpfile)
{
715
    if (!self)
716
        return SHOUTERR_INSANE;
717

718
    if (self->connection)
719
        return SHOUTERR_CONNECTED;
720

721
    if (self->dumpfile)
722
        free(self->dumpfile);
723

724
    if ( !(self->dumpfile = _shout_util_strdup(dumpfile)) )
725
        return self->error = SHOUTERR_MALLOC;
726

727
    return self->error = SHOUTERR_SUCCESS;
728
729
730
731
}

const char *shout_get_dumpfile(shout_t *self)
{
732
    if (!self)
733
        return NULL;
734

735
    return self->dumpfile;
736
737
}

brendan's avatar
brendan committed
738
int shout_set_audio_info(shout_t *self, const char *name, const char *value)
739
{
740
    if (!self)
741
        return SHOUTERR_INSANE;
Philipp Schafft's avatar
Philipp Schafft committed
742

743
    return self->error = _shout_util_dict_set(self->audio_info, name, value);
744
745
}

brendan's avatar
brendan committed
746
const char *shout_get_audio_info(shout_t *self, const char *name)
747
{
748
    if (!self)
749
        return NULL;
Philipp Schafft's avatar
Philipp Schafft committed
750

751
    return _shout_util_dict_get(self->audio_info, name);
752
753
}

Philipp Schafft's avatar
Philipp Schafft committed
754
755
int shout_set_meta(shout_t *self, const char *name, const char *value)
{
756
    size_t i;
757
    char c;
Philipp Schafft's avatar
Philipp Schafft committed
758

759
    if (!self || !name)
760
        return SHOUTERR_INSANE;
Philipp Schafft's avatar
Philipp Schafft committed
761

762
    if (self->connection)
763
        return self->error = SHOUTERR_CONNECTED;
Philipp Schafft's avatar
Philipp Schafft committed
764

765
766
    for (i = 0; (c = name[i]); i++) {
        if ((c < 'a' || c > 'z') && (c < '0' || c > '9'))
767
            return self->error = SHOUTERR_INSANE;
768
769
770
771
772
773
    }

    for (i = 0; (c = value[i]); i++) {
        if (c == '\r' || c == '\n')
            return self->error = SHOUTERR_INSANE;
    }
Philipp Schafft's avatar
Philipp Schafft committed
774

775
    return self->error = _shout_util_dict_set(self->meta, name, value);
Philipp Schafft's avatar
Philipp Schafft committed
776
777
778
779
}

const char *shout_get_meta(shout_t *self, const char *name)
{
780
    if (!self)
781
        return NULL;
Philipp Schafft's avatar
Philipp Schafft committed
782

783
    return _shout_util_dict_get(self->meta, name);
Philipp Schafft's avatar
Philipp Schafft committed
784
785
}

786
787
int shout_set_public(shout_t *self, unsigned int public)
{
788
    if (!self || (public != 0 && public != 1))
789
        return SHOUTERR_INSANE;
790

791
    if (self->connection)
792
        return self->error = SHOUTERR_CONNECTED;
793

794
    self->public = public;
795

796
    return self->error = SHOUTERR_SUCCESS;
797
798
799
800
}

unsigned int shout_get_public(shout_t *self)
{
801
    if (!self)
802
        return 0;
803

804
    return self->public;
805
806
807
808
}

int shout_set_format(shout_t *self, unsigned int format)
{
809
    if (!self)
810
        return SHOUTERR_INSANE;
811

812
    if (self->connection)
813
        return self->error = SHOUTERR_CONNECTED;
814

815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857