mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-10-29 05:40:23 -04:00
raop: Add BA (Basic) and DA (Digest) HTTP authentication helpers
RAOP authentication is using standard HTTP challenge-response authentication scheme. This patch adds two helper functions that generate the proper hash (for both techniques) given a username, a password and session related tokens.
This commit is contained in:
parent
a33c04c0cc
commit
5ff21c3bdd
6 changed files with 365 additions and 49 deletions
|
|
@ -65,6 +65,9 @@
|
|||
#define VOLUME_MIN -144
|
||||
#define VOLUME_MAX 0
|
||||
|
||||
#define USER_AGENT "iTunes/11.0.4 (Windows; N)"
|
||||
#define USER_NAME "iTunes"
|
||||
|
||||
#define DEFAULT_RAOP_PORT 5000
|
||||
#define UDP_DEFAULT_AUDIO_PORT 6000
|
||||
#define UDP_DEFAULT_CONTROL_PORT 6001
|
||||
|
|
@ -85,13 +88,15 @@ struct pa_raop_client {
|
|||
pa_core *core;
|
||||
char *host;
|
||||
uint16_t port;
|
||||
char *sid;
|
||||
pa_rtsp_client *rtsp;
|
||||
pa_raop_protocol_t protocol;
|
||||
char *sci, *sid;
|
||||
char *pwd;
|
||||
|
||||
uint8_t jack_type;
|
||||
uint8_t jack_status;
|
||||
|
||||
pa_raop_protocol_t protocol;
|
||||
|
||||
int encryption; /* Enable encryption? */
|
||||
pa_raop_secret *aes;
|
||||
|
||||
|
|
@ -125,6 +130,9 @@ struct pa_raop_client {
|
|||
uint32_t udp_sync_interval;
|
||||
uint32_t udp_sync_count;
|
||||
|
||||
pa_raop_client_auth_cb_t udp_auth_callback;
|
||||
void *udp_auth_userdata;
|
||||
|
||||
pa_raop_client_setup_cb_t udp_setup_callback;
|
||||
void *udp_setup_userdata;
|
||||
|
||||
|
|
@ -576,7 +584,7 @@ static void do_rtsp_announce(pa_raop_client *c) {
|
|||
pa_xfree(sdp);
|
||||
}
|
||||
|
||||
static void tcp_rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist* headers, void *userdata) {
|
||||
static void tcp_rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_rtsp_status status, pa_headerlist* headers, void *userdata) {
|
||||
pa_raop_client* c = userdata;
|
||||
pa_assert(c);
|
||||
pa_assert(rtsp);
|
||||
|
|
@ -675,12 +683,13 @@ static void tcp_rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist
|
|||
}
|
||||
}
|
||||
|
||||
static void udp_rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist *headers, void *userdata) {
|
||||
static void udp_rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_rtsp_status status, pa_headerlist *headers, void *userdata) {
|
||||
pa_raop_client *c = userdata;
|
||||
|
||||
pa_assert(c);
|
||||
pa_assert(rtsp);
|
||||
pa_assert(rtsp == c->rtsp);
|
||||
pa_assert(STATUS_OK == status);
|
||||
|
||||
switch (state) {
|
||||
case STATE_CONNECT: {
|
||||
|
|
@ -982,6 +991,178 @@ static void udp_rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist
|
|||
}
|
||||
}
|
||||
|
||||
static void rtsp_authentication_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_rtsp_status status, pa_headerlist *headers, void *userdata) {
|
||||
pa_raop_client *c = userdata;
|
||||
|
||||
pa_assert(c);
|
||||
pa_assert(rtsp);
|
||||
pa_assert(rtsp == c->rtsp);
|
||||
|
||||
switch (state) {
|
||||
case STATE_CONNECT: {
|
||||
char *sci = NULL, *sac = NULL;
|
||||
uint16_t rac;
|
||||
struct {
|
||||
uint32_t ci1;
|
||||
uint32_t ci2;
|
||||
} rci;
|
||||
|
||||
pa_random(&rci, sizeof(rci));
|
||||
/* Generate a random Client-Instance number */
|
||||
sci = pa_sprintf_malloc("%08x%08x",rci.ci1, rci.ci2);
|
||||
pa_rtsp_add_header(c->rtsp, "Client-Instance", sci);
|
||||
|
||||
pa_random(&rac, sizeof(rac));
|
||||
/* Generate a random Apple-Challenge key */
|
||||
pa_raop_base64_encode(&rac, 8*sizeof(rac), &sac);
|
||||
pa_log_debug ("APPLECHALLENGE >> %s | %d", sac, (int) sizeof(rac));
|
||||
rtrimchar(sac, '=');
|
||||
pa_rtsp_add_header(c->rtsp, "Apple-Challenge", sac);
|
||||
|
||||
pa_rtsp_options(c->rtsp);
|
||||
|
||||
pa_xfree(sac);
|
||||
pa_xfree(sci);
|
||||
break;
|
||||
}
|
||||
|
||||
case STATE_OPTIONS: {
|
||||
static bool waiting = false;
|
||||
const char *current = NULL;
|
||||
char space[] = " ";
|
||||
char *token,*ath = NULL;
|
||||
char *publ, *wath, *mth, *val;
|
||||
char *realm = NULL, *nonce = NULL, *response = NULL;
|
||||
char comma[] = ",";
|
||||
|
||||
pa_log_debug("RAOP: OPTIONS");
|
||||
/* We do not consider the Apple-Response */
|
||||
pa_rtsp_remove_header(c->rtsp, "Apple-Challenge");
|
||||
|
||||
if (STATUS_UNAUTHORIZED == status) {
|
||||
wath = pa_xstrdup(pa_headerlist_gets(headers, "WWW-Authenticate"));
|
||||
if (true == waiting) {
|
||||
pa_xfree(wath);
|
||||
goto failure;
|
||||
}
|
||||
|
||||
if (wath)
|
||||
mth = pa_split(wath, space, ¤t);
|
||||
while ((token = pa_split(wath, comma, ¤t))) {
|
||||
val = NULL;
|
||||
if ((val = strstr(token, "="))) {
|
||||
if (NULL == realm && val > strstr(token, "realm"))
|
||||
realm = pa_xstrdup(val + 2);
|
||||
else if (NULL == nonce && val > strstr(token, "nonce"))
|
||||
nonce = pa_xstrdup(val + 2);
|
||||
val = NULL;
|
||||
}
|
||||
|
||||
pa_xfree(token);
|
||||
}
|
||||
|
||||
if (pa_safe_streq(mth, "Basic")) {
|
||||
rtrimchar(realm, '\"');
|
||||
|
||||
pa_raop_basic_response(USER_NAME, c->pwd, &response);
|
||||
ath = pa_sprintf_malloc("Basic %s",
|
||||
response);
|
||||
|
||||
pa_xfree(response);
|
||||
pa_xfree(realm);
|
||||
} else if (pa_safe_streq(mth, "Digest")) {
|
||||
rtrimchar(realm, '\"');
|
||||
rtrimchar(nonce, '\"');
|
||||
|
||||
pa_raop_digest_response(USER_NAME, realm, c->pwd, nonce, "*", &response);
|
||||
ath = pa_sprintf_malloc("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"*\", response=\"%s\"",
|
||||
USER_NAME, realm, nonce,
|
||||
response);
|
||||
|
||||
pa_xfree(response);
|
||||
pa_xfree(realm);
|
||||
pa_xfree(nonce);
|
||||
} else {
|
||||
pa_log_error("unsupported authentication method: %s", mth);
|
||||
pa_xfree(wath);
|
||||
pa_xfree(mth);
|
||||
goto error;
|
||||
}
|
||||
|
||||
pa_xfree(wath);
|
||||
pa_xfree(mth);
|
||||
|
||||
pa_rtsp_add_header(c->rtsp, "Authorization", ath);
|
||||
pa_xfree(ath);
|
||||
|
||||
waiting = true;
|
||||
pa_rtsp_options(c->rtsp);
|
||||
break;
|
||||
}
|
||||
|
||||
if (STATUS_OK == status) {
|
||||
publ = pa_xstrdup(pa_headerlist_gets(headers, "Public"));
|
||||
c->sci = pa_xstrdup(pa_rtsp_get_header(c->rtsp, "Client-Instance"));
|
||||
|
||||
if (c->pwd)
|
||||
pa_xfree(c->pwd);
|
||||
pa_xfree(publ);
|
||||
c->pwd = NULL;
|
||||
}
|
||||
|
||||
if (c->udp_auth_callback)
|
||||
c->udp_auth_callback((int) status, c->udp_auth_userdata);
|
||||
pa_rtsp_client_free(c->rtsp);
|
||||
c->rtsp = NULL;
|
||||
|
||||
waiting = false;
|
||||
break;
|
||||
|
||||
failure:
|
||||
if (c->udp_auth_callback)
|
||||
c->udp_auth_callback((int) STATUS_UNAUTHORIZED, c->udp_auth_userdata);
|
||||
pa_rtsp_client_free(c->rtsp);
|
||||
c->rtsp = NULL;
|
||||
|
||||
pa_log_error("aborting RTSP authentication, wrong password");
|
||||
|
||||
waiting = false;
|
||||
break;
|
||||
|
||||
error:
|
||||
if (c->udp_auth_callback)
|
||||
c->udp_auth_callback((int) status, c->udp_auth_userdata);
|
||||
pa_rtsp_client_free(c->rtsp);
|
||||
c->rtsp = NULL;
|
||||
|
||||
pa_log_error("aborting RTSP authentication, unexpected failure");
|
||||
|
||||
waiting = false;
|
||||
break;
|
||||
}
|
||||
|
||||
case STATE_ANNOUNCE:
|
||||
case STATE_SETUP:
|
||||
case STATE_RECORD:
|
||||
case STATE_SET_PARAMETER:
|
||||
case STATE_FLUSH:
|
||||
case STATE_TEARDOWN:
|
||||
case STATE_DISCONNECTED:
|
||||
default: {
|
||||
if (c->udp_auth_callback)
|
||||
c->udp_auth_callback((int) STATUS_BAD_REQUEST, c->udp_auth_userdata);
|
||||
pa_rtsp_client_free(c->rtsp);
|
||||
c->rtsp = NULL;
|
||||
|
||||
if (c->sci)
|
||||
pa_xfree(c->sci);
|
||||
c->sci = NULL;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pa_raop_client* pa_raop_client_new(pa_core *core, const char *host, pa_raop_protocol_t protocol) {
|
||||
pa_raop_client* c;
|
||||
pa_parsed_address a;
|
||||
|
|
@ -1047,15 +1228,40 @@ void pa_raop_client_free(pa_raop_client *c) {
|
|||
|
||||
if (c->sid)
|
||||
pa_xfree(c->sid);
|
||||
if (c->sci)
|
||||
pa_xfree(c->sci);
|
||||
c->sci = c->sid = NULL;
|
||||
|
||||
if (c->aes)
|
||||
pa_raop_secret_free(c->aes);
|
||||
if (c->rtsp)
|
||||
pa_rtsp_client_free(c->rtsp);
|
||||
pa_xfree(c->host);
|
||||
c->rtsp = NULL;
|
||||
|
||||
pa_xfree(c->host);
|
||||
pa_xfree(c);
|
||||
}
|
||||
|
||||
int pa_raop_client_authenticate (pa_raop_client *c, const char *password) {
|
||||
|
||||
pa_assert(c);
|
||||
|
||||
if (c->rtsp || c->pwd) {
|
||||
pa_log_debug("Connection already in progress");
|
||||
return 0;
|
||||
}
|
||||
|
||||
c->pwd = NULL;
|
||||
if (password)
|
||||
c->pwd = pa_xstrdup(password);
|
||||
c->rtsp = pa_rtsp_client_new(c->core->mainloop, c->host, c->port, USER_AGENT);
|
||||
|
||||
pa_assert(c->rtsp);
|
||||
|
||||
pa_rtsp_set_callback(c->rtsp, rtsp_authentication_cb, c);
|
||||
return pa_rtsp_connect(c->rtsp);
|
||||
}
|
||||
|
||||
int pa_raop_client_connect(pa_raop_client *c) {
|
||||
char *sci;
|
||||
struct {
|
||||
|
|
@ -1074,7 +1280,7 @@ int pa_raop_client_connect(pa_raop_client *c) {
|
|||
if (c->protocol == RAOP_TCP)
|
||||
c->rtsp = pa_rtsp_client_new(c->core->mainloop, c->host, c->port, "iTunes/4.6 (Macintosh; U; PPC Mac OS X 10.3)");
|
||||
else
|
||||
c->rtsp = pa_rtsp_client_new(c->core->mainloop, c->host, c->port, "iTunes/7.6.2 (Windows; N;)");
|
||||
c->rtsp = pa_rtsp_client_new(c->core->mainloop, c->host, c->port, USER_AGENT);
|
||||
|
||||
/* Generate random instance id. */
|
||||
pa_random(&rand_data, sizeof(rand_data));
|
||||
|
|
@ -1116,6 +1322,17 @@ int pa_raop_client_teardown(pa_raop_client *c) {
|
|||
return rv;
|
||||
}
|
||||
|
||||
int pa_raop_client_udp_is_authenticated(pa_raop_client *c) {
|
||||
int rv = 0;
|
||||
|
||||
pa_assert(c);
|
||||
|
||||
if (c->sci != NULL)
|
||||
rv = 1;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
int pa_raop_client_udp_is_alive(pa_raop_client *c) {
|
||||
int rv = 0;
|
||||
|
||||
|
|
@ -1147,7 +1364,6 @@ int pa_raop_client_udp_stream(pa_raop_client *c) {
|
|||
if (!c->is_recording) {
|
||||
c->udp_first_packet = true;
|
||||
c->udp_sync_count = 0;
|
||||
|
||||
c->is_recording = true;
|
||||
}
|
||||
|
||||
|
|
@ -1326,6 +1542,14 @@ ssize_t pa_raop_client_udp_send_audio_packet(pa_raop_client *c, pa_memchunk *blo
|
|||
return len;
|
||||
}
|
||||
|
||||
void pa_raop_client_set_encryption(pa_raop_client *c, int encryption) {
|
||||
pa_assert(c);
|
||||
|
||||
c->encryption = encryption;
|
||||
if (c->encryption)
|
||||
c->aes = pa_raop_secret_new();
|
||||
}
|
||||
|
||||
/* Adjust volume so that it fits into VOLUME_DEF <= v <= 0 dB */
|
||||
pa_volume_t pa_raop_client_adjust_volume(pa_raop_client *c, pa_volume_t volume) {
|
||||
double minv, maxv;
|
||||
|
|
@ -1469,12 +1693,11 @@ void pa_raop_client_tcp_set_closed_callback(pa_raop_client *c, pa_raop_client_cl
|
|||
c->tcp_closed_userdata = userdata;
|
||||
}
|
||||
|
||||
void pa_raop_client_set_encryption(pa_raop_client *c, int encryption) {
|
||||
void pa_raop_client_udp_set_auth_callback(pa_raop_client *c, pa_raop_client_auth_cb_t callback, void *userdata) {
|
||||
pa_assert(c);
|
||||
|
||||
c->encryption = encryption;
|
||||
if (c->encryption)
|
||||
c->aes = pa_raop_secret_new();
|
||||
c->udp_auth_callback = callback;
|
||||
c->udp_auth_userdata = userdata;
|
||||
}
|
||||
|
||||
void pa_raop_client_udp_set_setup_callback(pa_raop_client *c, pa_raop_client_setup_cb_t callback, void *userdata) {
|
||||
|
|
|
|||
|
|
@ -37,10 +37,12 @@ typedef struct pa_raop_client pa_raop_client;
|
|||
pa_raop_client* pa_raop_client_new(pa_core *core, const char *host, pa_raop_protocol_t protocol);
|
||||
void pa_raop_client_free(pa_raop_client *c);
|
||||
|
||||
int pa_raop_client_authenticate (pa_raop_client *c, const char *password);
|
||||
int pa_raop_client_connect(pa_raop_client *c);
|
||||
int pa_raop_client_flush(pa_raop_client *c);
|
||||
int pa_raop_client_teardown(pa_raop_client *c);
|
||||
|
||||
int pa_raop_client_udp_is_authenticated(pa_raop_client *c);
|
||||
int pa_raop_client_udp_is_alive(pa_raop_client *c);
|
||||
int pa_raop_client_udp_can_stream(pa_raop_client *c);
|
||||
int pa_raop_client_udp_stream(pa_raop_client *c);
|
||||
|
|
@ -61,6 +63,8 @@ void pa_raop_client_tcp_set_callback(pa_raop_client *c, pa_raop_client_cb_t call
|
|||
typedef void (*pa_raop_client_closed_cb_t)(void *userdata);
|
||||
void pa_raop_client_tcp_set_closed_callback(pa_raop_client *c, pa_raop_client_closed_cb_t callback, void *userdata);
|
||||
|
||||
typedef void (*pa_raop_client_auth_cb_t)(int status, void *userdata);
|
||||
void pa_raop_client_udp_set_auth_callback(pa_raop_client *c, pa_raop_client_auth_cb_t callback, void *userdata);
|
||||
|
||||
typedef void (*pa_raop_client_setup_cb_t)(int control_fd, int timing_fd, void *userdata);
|
||||
void pa_raop_client_udp_set_setup_callback(pa_raop_client *c, pa_raop_client_setup_cb_t callback, void *userdata);
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
|
||||
#include <pulse/xmalloc.h>
|
||||
|
||||
#include <pulsecore/core-util.h>
|
||||
#include <pulsecore/macro.h>
|
||||
|
||||
#include "raop_util.h"
|
||||
|
|
@ -168,3 +169,42 @@ int pa_raop_md5_hash(const char *data, int len, char **str) {
|
|||
s[MD5_HASH_LENGTH] = 0;
|
||||
return strlen(s);
|
||||
}
|
||||
|
||||
int pa_raop_basic_response(const char *user, const char *pwd, char **str) {
|
||||
char *tmp, *B = NULL;
|
||||
|
||||
pa_assert(str);
|
||||
|
||||
tmp = pa_sprintf_malloc("%s:%s", user, pwd);
|
||||
pa_raop_base64_encode(tmp, strlen(tmp), &B);
|
||||
pa_xfree(tmp);
|
||||
|
||||
*str = B;
|
||||
return strlen(B);
|
||||
}
|
||||
|
||||
int pa_raop_digest_response(const char *user, const char *realm, const char *password,
|
||||
const char *nonce, const char *uri, char **str) {
|
||||
char *A1, *HA1, *A2, *HA2;
|
||||
char *tmp, *KD = NULL;
|
||||
|
||||
pa_assert(str);
|
||||
|
||||
A1 = pa_sprintf_malloc("%s:%s:%s", user, realm, password);
|
||||
pa_raop_md5_hash(A1, strlen(A1), &HA1);
|
||||
pa_xfree(A1);
|
||||
|
||||
A2 = pa_sprintf_malloc("OPTIONS:%s", uri);
|
||||
pa_raop_md5_hash(A2, strlen(A2), &HA2);
|
||||
pa_xfree(A2);
|
||||
|
||||
tmp = pa_sprintf_malloc("%s:%s:%s", HA1, nonce, HA2);
|
||||
pa_raop_md5_hash(tmp, strlen(tmp), &KD);
|
||||
pa_xfree(tmp);
|
||||
|
||||
pa_xfree(HA1);
|
||||
pa_xfree(HA2);
|
||||
|
||||
*str = KD;
|
||||
return strlen(KD);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,4 +32,8 @@ int pa_raop_base64_decode(const char *str, void *data);
|
|||
|
||||
int pa_raop_md5_hash(const char *data, int len, char **str);
|
||||
|
||||
int pa_raop_basic_response(const char *user, const char *pwd, char **str);
|
||||
int pa_raop_digest_response(const char *user, const char *realm, const char *password,
|
||||
const char *nonce, const char *uri, char **str);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ struct pa_rtsp_client {
|
|||
const char *useragent;
|
||||
|
||||
pa_rtsp_state state;
|
||||
pa_rtsp_status status;
|
||||
uint8_t waiting;
|
||||
|
||||
pa_headerlist* headers;
|
||||
|
|
@ -119,8 +120,8 @@ void pa_rtsp_client_free(pa_rtsp_client *c) {
|
|||
}
|
||||
|
||||
static void headers_read(pa_rtsp_client *c) {
|
||||
char* token;
|
||||
char delimiters[] = ";";
|
||||
char* token;
|
||||
|
||||
pa_assert(c);
|
||||
pa_assert(c->response_headers);
|
||||
|
|
@ -165,14 +166,14 @@ static void headers_read(pa_rtsp_client *c) {
|
|||
}
|
||||
|
||||
/* Call our callback */
|
||||
c->callback(c, c->state, c->response_headers, c->userdata);
|
||||
c->callback(c, c->state, c->status, c->response_headers, c->userdata);
|
||||
}
|
||||
|
||||
static void line_callback(pa_ioline *line, const char *s, void *userdata) {
|
||||
pa_rtsp_client *c = userdata;
|
||||
char *delimpos;
|
||||
char *s2, *s2p;
|
||||
|
||||
pa_rtsp_client *c = userdata;
|
||||
pa_assert(line);
|
||||
pa_assert(c);
|
||||
pa_assert(c->callback);
|
||||
|
|
@ -180,7 +181,7 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
|
|||
if (!s) {
|
||||
/* Keep the ioline/iochannel open as they will be freed automatically */
|
||||
c->ioline = NULL;
|
||||
c->callback(c, STATE_DISCONNECTED, NULL, c->userdata);
|
||||
c->callback(c, STATE_DISCONNECTED, STATUS_NO_RESPONSE, NULL, c->userdata);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -191,17 +192,35 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
|
|||
*s2p = '\0';
|
||||
s2p -= 1;
|
||||
}
|
||||
|
||||
if (c->waiting && pa_streq(s2, "RTSP/1.0 200 OK")) {
|
||||
c->waiting = 0;
|
||||
if (c->response_headers)
|
||||
pa_headerlist_free(c->response_headers);
|
||||
c->response_headers = pa_headerlist_new();
|
||||
|
||||
c->status = STATUS_OK;
|
||||
c->waiting = 0;
|
||||
goto exit;
|
||||
} else if (c->waiting && pa_streq(s2, "RTSP/1.0 401 Unauthorized")) {
|
||||
if (c->response_headers)
|
||||
pa_headerlist_free(c->response_headers);
|
||||
c->response_headers = pa_headerlist_new();
|
||||
|
||||
c->status = STATUS_UNAUTHORIZED;
|
||||
c->waiting = 0;
|
||||
goto exit;
|
||||
} else if (c->waiting) {
|
||||
pa_log_warn("Unexpected/Unhandled response: %s", s2);
|
||||
|
||||
if (pa_streq(s2, "RTSP/1.0 400 Bad Request"))
|
||||
c->status = STATUS_BAD_REQUEST;
|
||||
else if (pa_streq(s2, "RTSP/1.0 500 Internal Server Error"))
|
||||
c->status = STATUS_INTERNAL_ERROR;
|
||||
else
|
||||
c->status = STATUS_NO_RESPONSE;
|
||||
goto exit;
|
||||
}
|
||||
if (c->waiting) {
|
||||
pa_log_warn("Unexpected response: %s", s2);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (!strlen(s2)) {
|
||||
/* End of headers */
|
||||
/* We will have a header left from our looping iteration, so add it in :) */
|
||||
|
|
@ -228,7 +247,7 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
|
|||
if (c->last_header && ' ' == s2[0]) {
|
||||
pa_assert(c->header_buffer);
|
||||
|
||||
/* Add this line to the buffer (sans the space. */
|
||||
/* Add this line to the buffer (sans the space) */
|
||||
pa_strbuf_puts(c->header_buffer, &(s2[1]));
|
||||
goto exit;
|
||||
}
|
||||
|
|
@ -270,6 +289,7 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
|
|||
|
||||
/* Save the header name */
|
||||
c->last_header = pa_xstrdup(s2);
|
||||
|
||||
exit:
|
||||
pa_xfree(s2);
|
||||
}
|
||||
|
|
@ -317,7 +337,7 @@ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata
|
|||
pa_log_debug("Established RTSP connection from local ip %s", c->localip);
|
||||
|
||||
if (c->callback)
|
||||
c->callback(c, c->state, NULL, c->userdata);
|
||||
c->callback(c, c->state, STATUS_OK, NULL, c->userdata);
|
||||
}
|
||||
|
||||
int pa_rtsp_connect(pa_rtsp_client *c) {
|
||||
|
|
@ -336,6 +356,7 @@ int pa_rtsp_connect(pa_rtsp_client *c) {
|
|||
pa_socket_client_set_callback(c->sc, on_connection, c);
|
||||
c->waiting = 1;
|
||||
c->state = STATE_CONNECT;
|
||||
c->status = STATUS_NO_RESPONSE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -368,12 +389,25 @@ uint32_t pa_rtsp_serverport(pa_rtsp_client *c) {
|
|||
return c->rtp_port;
|
||||
}
|
||||
|
||||
bool pa_rtsp_exec_ready(const pa_rtsp_client *c) {
|
||||
pa_assert(c);
|
||||
|
||||
return c->url != NULL && c->ioline != NULL;
|
||||
}
|
||||
|
||||
void pa_rtsp_set_url(pa_rtsp_client *c, const char *url) {
|
||||
pa_assert(c);
|
||||
|
||||
c->url = pa_xstrdup(url);
|
||||
}
|
||||
|
||||
bool pa_rtsp_has_header(pa_rtsp_client *c, const char *key) {
|
||||
pa_assert(c);
|
||||
pa_assert(key);
|
||||
|
||||
return pa_headerlist_contains(c->headers, key);
|
||||
}
|
||||
|
||||
void pa_rtsp_add_header(pa_rtsp_client *c, const char *key, const char *value) {
|
||||
pa_assert(c);
|
||||
pa_assert(key);
|
||||
|
|
@ -382,6 +416,13 @@ void pa_rtsp_add_header(pa_rtsp_client *c, const char *key, const char *value) {
|
|||
pa_headerlist_puts(c->headers, key, value);
|
||||
}
|
||||
|
||||
const char* pa_rtsp_get_header(pa_rtsp_client *c, const char *key) {
|
||||
pa_assert(c);
|
||||
pa_assert(key);
|
||||
|
||||
return pa_headerlist_gets(c->headers, key);
|
||||
}
|
||||
|
||||
void pa_rtsp_remove_header(pa_rtsp_client *c, const char *key) {
|
||||
pa_assert(c);
|
||||
pa_assert(key);
|
||||
|
|
@ -389,12 +430,6 @@ void pa_rtsp_remove_header(pa_rtsp_client *c, const char *key) {
|
|||
pa_headerlist_remove(c->headers, key);
|
||||
}
|
||||
|
||||
bool pa_rtsp_exec_ready(const pa_rtsp_client *c) {
|
||||
pa_assert(c);
|
||||
|
||||
return c->url != NULL && c->ioline != NULL;
|
||||
}
|
||||
|
||||
static int rtsp_exec(pa_rtsp_client *c, const char *cmd,
|
||||
const char *content_type, const char *content,
|
||||
int expect_response,
|
||||
|
|
@ -527,17 +562,6 @@ int pa_rtsp_record(pa_rtsp_client *c, uint16_t *seq, uint32_t *rtptime) {
|
|||
return rv;
|
||||
}
|
||||
|
||||
int pa_rtsp_teardown(pa_rtsp_client *c) {
|
||||
int rv;
|
||||
|
||||
pa_assert(c);
|
||||
|
||||
c->state = STATE_TEARDOWN;
|
||||
rv = rtsp_exec(c, "TEARDOWN", NULL, NULL, 0, NULL);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
int pa_rtsp_setparameter(pa_rtsp_client *c, const char *param) {
|
||||
int rv;
|
||||
|
||||
|
|
@ -570,3 +594,14 @@ int pa_rtsp_flush(pa_rtsp_client *c, uint16_t seq, uint32_t rtptime) {
|
|||
pa_headerlist_free(headers);
|
||||
return rv;
|
||||
}
|
||||
|
||||
int pa_rtsp_teardown(pa_rtsp_client *c) {
|
||||
int rv;
|
||||
|
||||
pa_assert(c);
|
||||
|
||||
c->state = STATE_TEARDOWN;
|
||||
rv = rtsp_exec(c, "TEARDOWN", NULL, NULL, 0, NULL);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,43 +31,53 @@
|
|||
#include "headerlist.h"
|
||||
|
||||
typedef struct pa_rtsp_client pa_rtsp_client;
|
||||
|
||||
typedef enum {
|
||||
STATE_CONNECT,
|
||||
STATE_OPTIONS,
|
||||
STATE_ANNOUNCE,
|
||||
STATE_SETUP,
|
||||
STATE_RECORD,
|
||||
STATE_SET_PARAMETER,
|
||||
STATE_FLUSH,
|
||||
STATE_TEARDOWN,
|
||||
STATE_SET_PARAMETER,
|
||||
STATE_DISCONNECTED
|
||||
} pa_rtsp_state;
|
||||
typedef void (*pa_rtsp_cb_t)(pa_rtsp_client *c, pa_rtsp_state state, pa_headerlist *hl, void *userdata);
|
||||
|
||||
typedef enum {
|
||||
STATUS_OK = 200,
|
||||
STATUS_BAD_REQUEST = 400,
|
||||
STATUS_UNAUTHORIZED = 401,
|
||||
STATUS_NO_RESPONSE = 444,
|
||||
STATUS_INTERNAL_ERROR = 500
|
||||
} pa_rtsp_status;
|
||||
|
||||
typedef void (*pa_rtsp_cb_t)(pa_rtsp_client *c, pa_rtsp_state state, pa_rtsp_status code, pa_headerlist *headers, void *userdata);
|
||||
|
||||
pa_rtsp_client* pa_rtsp_client_new(pa_mainloop_api *mainloop, const char *hostname, uint16_t port, const char *useragent);
|
||||
void pa_rtsp_client_free(pa_rtsp_client *c);
|
||||
|
||||
int pa_rtsp_connect(pa_rtsp_client *c);
|
||||
void pa_rtsp_set_callback(pa_rtsp_client *c, pa_rtsp_cb_t callback, void *userdata);
|
||||
|
||||
void pa_rtsp_disconnect(pa_rtsp_client *c);
|
||||
|
||||
const char* pa_rtsp_localip(pa_rtsp_client *c);
|
||||
uint32_t pa_rtsp_serverport(pa_rtsp_client *c);
|
||||
void pa_rtsp_set_url(pa_rtsp_client *c, const char *url);
|
||||
void pa_rtsp_add_header(pa_rtsp_client *c, const char *key, const char *value);
|
||||
void pa_rtsp_remove_header(pa_rtsp_client *c, const char *key);
|
||||
|
||||
bool pa_rtsp_exec_ready(const pa_rtsp_client *c);
|
||||
|
||||
void pa_rtsp_set_url(pa_rtsp_client *c, const char *url);
|
||||
|
||||
bool pa_rtsp_has_header(pa_rtsp_client *c, const char *key);
|
||||
void pa_rtsp_add_header(pa_rtsp_client *c, const char *key, const char *value);
|
||||
const char* pa_rtsp_get_header(pa_rtsp_client *c, const char *key);
|
||||
void pa_rtsp_remove_header(pa_rtsp_client *c, const char *key);
|
||||
|
||||
int pa_rtsp_options(pa_rtsp_client *c);
|
||||
int pa_rtsp_announce(pa_rtsp_client *c, const char *sdp);
|
||||
|
||||
int pa_rtsp_setup(pa_rtsp_client *c, const char *transport);
|
||||
int pa_rtsp_record(pa_rtsp_client *c, uint16_t *seq, uint32_t *rtptime);
|
||||
int pa_rtsp_teardown(pa_rtsp_client *c);
|
||||
|
||||
int pa_rtsp_setparameter(pa_rtsp_client *c, const char *param);
|
||||
int pa_rtsp_flush(pa_rtsp_client *c, uint16_t seq, uint32_t rtptime);
|
||||
int pa_rtsp_teardown(pa_rtsp_client *c);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue