#include <limits.h>
#include <polyp/sample.h>
+#include <polyp/timeval.h>
+#include <polyp/utf8.h>
+#include <polyp/xmalloc.h>
+
#include <polypcore/esound.h>
#include <polypcore/memblock.h>
#include <polypcore/client.h>
#include <polypcore/sample-util.h>
#include <polypcore/authkey.h>
#include <polypcore/namereg.h>
-#include <polypcore/xmalloc.h>
#include <polypcore/log.h>
-#include <polypcore/util.h>
+#include <polypcore/core-util.h>
+#include <polypcore/core-error.h>
#include "endianmacros.h"
#define DEFAULT_COOKIE_FILE ".esd_auth"
-#define PLAYBACK_BUFFER_SECONDS (.5)
+#define PLAYBACK_BUFFER_SECONDS (.25)
#define PLAYBACK_BUFFER_FRAGMENTS (10)
#define RECORD_BUFFER_SECONDS (5)
#define RECORD_BUFFER_FRAGMENTS (100)
pa_source_output *source_output;
pa_memblockq *input_memblockq, *output_memblockq;
pa_defer_event *defer_event;
+
+ char *original_name;
struct {
pa_memblock *current_memblock;
{ 0, esd_proto_get_latency, "get latency" }
};
-
static void connection_free(struct connection *c) {
assert(c);
pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
if (c->auth_timeout_event)
c->protocol->core->mainloop->time_free(c->auth_timeout_event);
-
+
+ pa_xfree(c->original_name);
pa_xfree(c);
}
-static void* connection_write(struct connection *c, size_t length) {
- size_t t, i;
+static void connection_write_prepare(struct connection *c, size_t length) {
+ size_t t;
assert(c);
- assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);
- c->protocol->core->mainloop->defer_enable(c->defer_event, 1);
-
t = c->write_data_length+length;
-
+
if (c->write_data_alloc < t)
c->write_data = pa_xrealloc(c->write_data, c->write_data_alloc = t);
assert(c->write_data);
+}
+
+static void connection_write(struct connection *c, const void *data, size_t length) {
+ size_t i;
+ assert(c);
+
+ assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);
+ c->protocol->core->mainloop->defer_enable(c->defer_event, 1);
+
+ connection_write_prepare(c, length);
+
+ assert(c->write_data);
i = c->write_data_length;
c->write_data_length += length;
- return (uint8_t*) c->write_data+i;
+ memcpy((char*)c->write_data + i, data, length);
}
static void format_esd2native(int format, int swap_bytes, pa_sample_spec *ss) {
return format;
}
+#define CHECK_VALIDITY(expression, string) do { \
+ if (!(expression)) { \
+ pa_log_warn(__FILE__ ": " string); \
+ return -1; \
+ } \
+} while(0);
+
/*** esound commands ***/
static int esd_proto_connect(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
uint32_t ekey;
- int *ok;
+ int ok;
+
assert(length == (ESD_KEY_LEN + sizeof(uint32_t)));
if (!c->authorized) {
if (memcmp(data, c->protocol->esd_key, ESD_KEY_LEN) != 0) {
- pa_log(__FILE__": kicked client with invalid authorization key.\n");
+ pa_log(__FILE__": kicked client with invalid authorization key.");
return -1;
}
c->auth_timeout_event = NULL;
}
}
-
- ekey = *(const uint32_t*)((const uint8_t*) data+ESD_KEY_LEN);
+
+ data = (const char*)data + ESD_KEY_LEN;
+
+ memcpy(&ekey, data, sizeof(uint32_t));
if (ekey == ESD_ENDIAN_KEY)
c->swap_byte_order = 0;
else if (ekey == ESD_SWAP_ENDIAN_KEY)
c->swap_byte_order = 1;
else {
- pa_log(__FILE__": client sent invalid endian key\n");
+ pa_log(__FILE__": client sent invalid endian key");
return -1;
}
- ok = connection_write(c, sizeof(int));
- assert(ok);
- *ok = 1;
+ ok = 1;
+ connection_write(c, &ok, sizeof(int));
return 0;
}
static int esd_proto_stream_play(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
- char name[ESD_NAME_MAX];
- int format, rate;
+ char name[ESD_NAME_MAX], *utf8_name;
+ int32_t format, rate;
pa_sink *sink;
pa_sample_spec ss;
size_t l;
- assert(c && length == (sizeof(int)*2+ESD_NAME_MAX));
+
+ assert(c && length == (sizeof(int32_t)*2+ESD_NAME_MAX));
- format = MAYBE_INT32_SWAP(c->swap_byte_order, *(const int*)data);
- rate = MAYBE_INT32_SWAP(c->swap_byte_order, *((const int*)data + 1));
+ memcpy(&format, data, sizeof(int32_t));
+ format = MAYBE_INT32_SWAP(c->swap_byte_order, format);
+ data = (const char*)data + sizeof(int32_t);
+
+ memcpy(&rate, data, sizeof(int32_t));
+ rate = MAYBE_INT32_SWAP(c->swap_byte_order, rate);
+ data = (const char*)data + sizeof(int32_t);
ss.rate = rate;
format_esd2native(format, c->swap_byte_order, &ss);
- if (!pa_sample_spec_valid(&ss)) {
- pa_log(__FILE__": invalid sample specification\n");
- return -1;
- }
+ CHECK_VALIDITY(pa_sample_spec_valid(&ss), "Invalid sample specification");
+ sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1);
+ CHECK_VALIDITY(sink, "No such sink");
- if (!(sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1))) {
- pa_log(__FILE__": no such sink\n");
- return -1;
- }
-
- strncpy(name, (const char*) data + sizeof(int)*2, sizeof(name));
+ strncpy(name, data, sizeof(name));
name[sizeof(name)-1] = 0;
-
- pa_client_set_name(c->client, name);
+ utf8_name = pa_utf8_filter(name);
+
+ pa_client_set_name(c->client, utf8_name);
+ c->original_name = pa_xstrdup(name);
assert(!c->sink_input && !c->input_memblockq);
- if (!(c->sink_input = pa_sink_input_new(sink, __FILE__, name, &ss, NULL, 0, -1))) {
- pa_log(__FILE__": failed to create sink input.\n");
- return -1;
- }
+ c->sink_input = pa_sink_input_new(sink, __FILE__, utf8_name, &ss, NULL, NULL, 0, -1);
+
+ pa_xfree(utf8_name);
+
+ CHECK_VALIDITY(c->sink_input, "Failed to create sink input.");
l = (size_t) (pa_bytes_per_second(&ss)*PLAYBACK_BUFFER_SECONDS);
- c->input_memblockq = pa_memblockq_new(l, 0, pa_frame_size(&ss), l/2, l/PLAYBACK_BUFFER_FRAGMENTS, c->protocol->core->memblock_stat);
+ c->input_memblockq = pa_memblockq_new(
+ 0,
+ l,
+ 0,
+ pa_frame_size(&ss),
+ (size_t) -1,
+ l/PLAYBACK_BUFFER_FRAGMENTS,
+ NULL,
+ c->protocol->core->memblock_stat);
pa_iochannel_socket_set_rcvbuf(c->io, l/PLAYBACK_BUFFER_FRAGMENTS*2);
c->playback.fragment_size = l/10;
}
static int esd_proto_stream_record(struct connection *c, esd_proto_t request, const void *data, size_t length) {
- char name[ESD_NAME_MAX];
- int format, rate;
+ char name[ESD_NAME_MAX], *utf8_name;
+ int32_t format, rate;
pa_source *source;
pa_sample_spec ss;
size_t l;
- assert(c && length == (sizeof(int)*2+ESD_NAME_MAX));
+
+ assert(c && length == (sizeof(int32_t)*2+ESD_NAME_MAX));
- format = MAYBE_INT32_SWAP(c->swap_byte_order, *(const int*)data);
- rate = MAYBE_INT32_SWAP(c->swap_byte_order, *((const int*)data + 1));
+ memcpy(&format, data, sizeof(int32_t));
+ format = MAYBE_INT32_SWAP(c->swap_byte_order, format);
+ data = (const char*)data + sizeof(int32_t);
+
+ memcpy(&rate, data, sizeof(int32_t));
+ rate = MAYBE_INT32_SWAP(c->swap_byte_order, rate);
+ data = (const char*)data + sizeof(int32_t);
ss.rate = rate;
format_esd2native(format, c->swap_byte_order, &ss);
- if (!pa_sample_spec_valid(&ss)) {
- pa_log(__FILE__": invalid sample specification.\n");
- return -1;
- }
+ CHECK_VALIDITY(pa_sample_spec_valid(&ss), "Invalid sample specification.");
if (request == ESD_PROTO_STREAM_MON) {
pa_sink* sink;
if (!(sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1))) {
- pa_log(__FILE__": no such sink.\n");
+ pa_log(__FILE__": no such sink.");
return -1;
}
if (!(source = sink->monitor_source)) {
- pa_log(__FILE__": no such monitor source.\n");
+ pa_log(__FILE__": no such monitor source.");
return -1;
}
} else {
assert(request == ESD_PROTO_STREAM_REC);
if (!(source = pa_namereg_get(c->protocol->core, c->protocol->source_name, PA_NAMEREG_SOURCE, 1))) {
- pa_log(__FILE__": no such source.\n");
+ pa_log(__FILE__": no such source.");
return -1;
}
}
- strncpy(name, (const char*) data + sizeof(int)*2, sizeof(name));
+ strncpy(name, data, sizeof(name));
name[sizeof(name)-1] = 0;
- pa_client_set_name(c->client, name);
+ utf8_name = pa_utf8_filter(name);
+ pa_client_set_name(c->client, utf8_name);
+ pa_xfree(utf8_name);
+
+ c->original_name = pa_xstrdup(name);
assert(!c->output_memblockq && !c->source_output);
- if (!(c->source_output = pa_source_output_new(source, __FILE__, name, &ss, NULL, -1))) {
- pa_log(__FILE__": failed to create source output\n");
+ if (!(c->source_output = pa_source_output_new(source, __FILE__, c->client->name, &ss, NULL, -1))) {
+ pa_log(__FILE__": failed to create source output");
return -1;
}
l = (size_t) (pa_bytes_per_second(&ss)*RECORD_BUFFER_SECONDS);
- c->output_memblockq = pa_memblockq_new(l, 0, pa_frame_size(&ss), 0, 0, c->protocol->core->memblock_stat);
+ c->output_memblockq = pa_memblockq_new(
+ 0,
+ l,
+ 0,
+ pa_frame_size(&ss),
+ 1,
+ 0,
+ NULL,
+ c->protocol->core->memblock_stat);
pa_iochannel_socket_set_sndbuf(c->io, l/RECORD_BUFFER_FRAGMENTS*2);
c->source_output->owner = c->protocol->module;
static int esd_proto_get_latency(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
pa_sink *sink;
- int latency, *lag;
+ int32_t latency;
+
assert(c && !data && length == 0);
if (!(sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1)))
latency = (int) ((usec*44100)/1000000);
}
- lag = connection_write(c, sizeof(int));
- assert(lag);
- *lag = MAYBE_INT32_SWAP(c->swap_byte_order, latency);
+ latency = MAYBE_INT32_SWAP(c->swap_byte_order, latency);
+ connection_write(c, &latency, sizeof(int32_t));
return 0;
}
static int esd_proto_server_info(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
- int rate = 44100, format = ESD_STEREO|ESD_BITS16;
- int *response;
+ int32_t rate = 44100, format = ESD_STEREO|ESD_BITS16;
+ int32_t response;
pa_sink *sink;
- assert(c && data && length == sizeof(int));
+
+ assert(c && data && length == sizeof(int32_t));
if ((sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1))) {
rate = sink->sample_spec.rate;
format = format_native2esd(&sink->sample_spec);
}
-
- response = connection_write(c, sizeof(int)*3);
- assert(response);
- *(response++) = 0;
- *(response++) = MAYBE_INT32_SWAP(c->swap_byte_order, rate);
- *(response++) = MAYBE_INT32_SWAP(c->swap_byte_order, format);
+
+ connection_write_prepare(c, sizeof(int32_t) * 3);
+
+ response = 0;
+ connection_write(c, &response, sizeof(int32_t));
+ rate = MAYBE_INT32_SWAP(c->swap_byte_order, rate);
+ connection_write(c, &rate, sizeof(int32_t));
+ format = MAYBE_INT32_SWAP(c->swap_byte_order, format);
+ connection_write(c, &format, sizeof(int32_t));
+
return 0;
}
static int esd_proto_all_info(struct connection *c, esd_proto_t request, const void *data, size_t length) {
- uint8_t *response;
size_t t, k, s;
struct connection *conn;
- size_t idx = PA_IDXSET_INVALID;
+ uint32_t idx = PA_IDXSET_INVALID;
unsigned nsamples;
- assert(c && data && length == sizeof(int));
+ char terminator[sizeof(int32_t)*6+ESD_NAME_MAX];
+
+ assert(c && data && length == sizeof(int32_t));
if (esd_proto_server_info(c, request, data, length) < 0)
return -1;
- k = sizeof(int)*5+ESD_NAME_MAX;
- s = sizeof(int)*6+ESD_NAME_MAX;
+ k = sizeof(int32_t)*5+ESD_NAME_MAX;
+ s = sizeof(int32_t)*6+ESD_NAME_MAX;
nsamples = c->protocol->core->scache ? pa_idxset_size(c->protocol->core->scache) : 0;
- response = connection_write(c, (t = s*(nsamples+1) + k*(c->protocol->n_player+1)));
- assert(k);
+ t = s*(nsamples+1) + k*(c->protocol->n_player+1);
+
+ connection_write_prepare(c, t);
+
+ memset(terminator, 0, sizeof(terminator));
for (conn = pa_idxset_first(c->protocol->connections, &idx); conn; conn = pa_idxset_next(c->protocol->connections, &idx)) {
- int format = ESD_BITS16 | ESD_STEREO, rate = 44100, lvolume = ESD_VOLUME_BASE, rvolume = ESD_VOLUME_BASE;
+ int32_t id, format = ESD_BITS16 | ESD_STEREO, rate = 44100, lvolume = ESD_VOLUME_BASE, rvolume = ESD_VOLUME_BASE;
+ char name[ESD_NAME_MAX];
if (conn->state != ESD_STREAMING_DATA)
continue;
- assert(t >= s+k+k);
+ assert(t >= k*2+s);
if (conn->sink_input) {
+ pa_cvolume volume = *pa_sink_input_get_volume(conn->sink_input);
rate = conn->sink_input->sample_spec.rate;
- lvolume = (conn->sink_input->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM;
- rvolume = (conn->sink_input->volume.values[1]*ESD_VOLUME_BASE)/PA_VOLUME_NORM;
+ lvolume = (volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM;
+ rvolume = (volume.values[1]*ESD_VOLUME_BASE)/PA_VOLUME_NORM;
format = format_native2esd(&conn->sink_input->sample_spec);
}
/* id */
- *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, (int) (conn->index+1));
- response += sizeof(int);
+ id = MAYBE_INT32_SWAP(c->swap_byte_order, (int32_t) (conn->index+1));
+ connection_write(c, &id, sizeof(int32_t));
/* name */
- assert(conn->client);
- strncpy((char*) response, conn->client->name, ESD_NAME_MAX);
- response += ESD_NAME_MAX;
+ memset(name, 0, ESD_NAME_MAX); /* don't leak old data */
+ if (conn->original_name)
+ strncpy(name, conn->original_name, ESD_NAME_MAX);
+ else if (conn->client && conn->client->name)
+ strncpy(name, conn->client->name, ESD_NAME_MAX);
+ connection_write(c, name, ESD_NAME_MAX);
/* rate */
- *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, rate);
- response += sizeof(int);
+ rate = MAYBE_INT32_SWAP(c->swap_byte_order, rate);
+ connection_write(c, &rate, sizeof(int32_t));
/* left */
- *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, lvolume);
- response += sizeof(int);
+ lvolume = MAYBE_INT32_SWAP(c->swap_byte_order, lvolume);
+ connection_write(c, &lvolume, sizeof(int32_t));
/*right*/
- *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, rvolume);
- response += sizeof(int);
+ rvolume = MAYBE_INT32_SWAP(c->swap_byte_order, rvolume);
+ connection_write(c, &rvolume, sizeof(int32_t));
/*format*/
- *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, format);
- response += sizeof(int);
+ format = MAYBE_INT32_SWAP(c->swap_byte_order, format);
+ connection_write(c, &format, sizeof(int32_t));
- t-= k;
+ t -= k;
}
assert(t == s*(nsamples+1)+k);
- memset(response, 0, k);
- response += k;
t -= k;
+ connection_write(c, terminator, k);
+
if (nsamples) {
pa_scache_entry *ce;
idx = PA_IDXSET_INVALID;
for (ce = pa_idxset_first(c->protocol->core->scache, &idx); ce; ce = pa_idxset_next(c->protocol->core->scache, &idx)) {
+ int32_t id, rate, lvolume, rvolume, format, len;
+ char name[ESD_NAME_MAX];
+
assert(t >= s*2);
-
+
/* id */
- *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, (int) (ce->index+1));
- response += sizeof(int);
+ id = MAYBE_INT32_SWAP(c->swap_byte_order, (int) (ce->index+1));
+ connection_write(c, &id, sizeof(int32_t));
/* name */
+ memset(name, 0, ESD_NAME_MAX); /* don't leak old data */
if (strncmp(ce->name, SCACHE_PREFIX, sizeof(SCACHE_PREFIX)-1) == 0)
- strncpy((char*) response, ce->name+sizeof(SCACHE_PREFIX)-1, ESD_NAME_MAX);
+ strncpy(name, ce->name+sizeof(SCACHE_PREFIX)-1, ESD_NAME_MAX);
else
- snprintf((char*) response, ESD_NAME_MAX, "native.%s", ce->name);
- response += ESD_NAME_MAX;
+ snprintf(name, ESD_NAME_MAX, "native.%s", ce->name);
+ connection_write(c, name, ESD_NAME_MAX);
/* rate */
- *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, ce->sample_spec.rate);
- response += sizeof(int);
+ rate = MAYBE_UINT32_SWAP(c->swap_byte_order, ce->sample_spec.rate);
+ connection_write(c, &rate, sizeof(int32_t));
/* left */
- *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, (ce->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM);
- response += sizeof(int);
+ lvolume = MAYBE_UINT32_SWAP(c->swap_byte_order, (ce->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM);
+ connection_write(c, &lvolume, sizeof(int32_t));
/*right*/
- *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, (ce->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM);
- response += sizeof(int);
+ rvolume = MAYBE_UINT32_SWAP(c->swap_byte_order, (ce->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM);
+ connection_write(c, &rvolume, sizeof(int32_t));
/*format*/
- *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, format_native2esd(&ce->sample_spec));
- response += sizeof(int);
+ format = MAYBE_INT32_SWAP(c->swap_byte_order, format_native2esd(&ce->sample_spec));
+ connection_write(c, &format, sizeof(int32_t));
/*length*/
- *((int*) response) = MAYBE_INT32_SWAP(c->swap_byte_order, (int) ce->memchunk.length);
- response += sizeof(int);
+ len = MAYBE_INT32_SWAP(c->swap_byte_order, (int) ce->memchunk.length);
+ connection_write(c, &len, sizeof(int32_t));
t -= s;
}
}
assert(t == s);
- memset(response, 0, s);
+
+ connection_write(c, terminator, s);
return 0;
}
static int esd_proto_stream_pan(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
- int *ok;
- uint32_t idx;
- pa_volume_t lvolume, rvolume;
+ int32_t ok;
+ uint32_t idx, lvolume, rvolume;
struct connection *conn;
- assert(c && data && length == sizeof(int)*3);
+
+ assert(c && data && length == sizeof(int32_t)*3);
- idx = MAYBE_UINT32_SWAP(c->swap_byte_order, *(const int*)data)-1;
- lvolume = MAYBE_UINT32_SWAP(c->swap_byte_order, *((const int*)data + 1));
- lvolume = (lvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE;
- rvolume = MAYBE_UINT32_SWAP(c->swap_byte_order, *((const int*)data + 2));
- rvolume = (rvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE;
-
- ok = connection_write(c, sizeof(int));
- assert(ok);
-
- if ((conn = pa_idxset_get_by_index(c->protocol->connections, idx))) {
- assert(conn->sink_input);
- conn->sink_input->volume.values[0] = lvolume;
- conn->sink_input->volume.values[1] = rvolume;
- conn->sink_input->volume.channels = 2;
- *ok = 1;
+ memcpy(&idx, data, sizeof(uint32_t));
+ idx = MAYBE_UINT32_SWAP(c->swap_byte_order, idx) - 1;
+ data = (const char*)data + sizeof(uint32_t);
+
+ memcpy(&lvolume, data, sizeof(uint32_t));
+ lvolume = MAYBE_UINT32_SWAP(c->swap_byte_order, lvolume);
+ data = (const char*)data + sizeof(uint32_t);
+
+ memcpy(&rvolume, data, sizeof(uint32_t));
+ rvolume = MAYBE_UINT32_SWAP(c->swap_byte_order, rvolume);
+ data = (const char*)data + sizeof(uint32_t);
+
+ if ((conn = pa_idxset_get_by_index(c->protocol->connections, idx)) && conn->sink_input) {
+ pa_cvolume volume;
+ volume.values[0] = (lvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE;
+ volume.values[1] = (rvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE;
+ volume.channels = 2;
+ pa_sink_input_set_volume(conn->sink_input, &volume);
+ ok = 1;
} else
- *ok = 0;
+ ok = 0;
+
+ connection_write(c, &ok, sizeof(int32_t));
return 0;
}
static int esd_proto_sample_cache(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
pa_sample_spec ss;
- int format, rate;
- size_t sc_length;
+ int32_t format, rate, sc_length;
uint32_t idx;
- int *ok;
char name[ESD_NAME_MAX+sizeof(SCACHE_PREFIX)-1];
- assert(c && data && length == (ESD_NAME_MAX+3*sizeof(int)));
- format = MAYBE_INT32_SWAP(c->swap_byte_order, *(const int*)data);
- rate = MAYBE_INT32_SWAP(c->swap_byte_order, *((const int*)data + 1));
+ assert(c && data && length == (ESD_NAME_MAX+3*sizeof(int32_t)));
+
+ memcpy(&format, data, sizeof(int32_t));
+ format = MAYBE_INT32_SWAP(c->swap_byte_order, format);
+ data = (const char*)data + sizeof(int32_t);
+
+ memcpy(&rate, data, sizeof(int32_t));
+ rate = MAYBE_INT32_SWAP(c->swap_byte_order, rate);
+ data = (const char*)data + sizeof(int32_t);
ss.rate = rate;
format_esd2native(format, c->swap_byte_order, &ss);
- sc_length = (size_t) MAYBE_INT32_SWAP(c->swap_byte_order, (*((const int*)data + 2)));
+ CHECK_VALIDITY(pa_sample_spec_valid(&ss), "Invalid sample specification.");
- if (sc_length >= MAX_CACHE_SAMPLE_SIZE)
- return -1;
+ memcpy(&sc_length, data, sizeof(int32_t));
+ sc_length = MAYBE_INT32_SWAP(c->swap_byte_order, sc_length);
+ data = (const char*)data + sizeof(int32_t);
+
+ CHECK_VALIDITY(sc_length <= MAX_CACHE_SAMPLE_SIZE, "Sample too large.");
strcpy(name, SCACHE_PREFIX);
- strncpy(name+sizeof(SCACHE_PREFIX)-1, (const char*) data+3*sizeof(int), ESD_NAME_MAX);
+ strncpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX);
name[sizeof(name)-1] = 0;
+
+ CHECK_VALIDITY(pa_utf8_valid(name), "Invalid UTF8 in sample name.");
assert(!c->scache.memchunk.memblock);
c->scache.memchunk.memblock = pa_memblock_new(sc_length, c->protocol->core->memblock_stat);
c->scache.sample_spec = ss;
assert(!c->scache.name);
c->scache.name = pa_xstrdup(name);
-
+
c->state = ESD_CACHING_SAMPLE;
pa_scache_add_item(c->protocol->core, c->scache.name, NULL, NULL, NULL, &idx);
- ok = connection_write(c, sizeof(int));
- assert(ok);
-
- *ok = idx+1;
+ idx += 1;
+ connection_write(c, &idx, sizeof(uint32_t));
return 0;
}
static int esd_proto_sample_get_id(struct connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
- int *ok;
+ int32_t ok;
uint32_t idx;
char name[ESD_NAME_MAX+sizeof(SCACHE_PREFIX)-1];
- assert(c && data && length == ESD_NAME_MAX);
- ok = connection_write(c, sizeof(int));
- assert(ok);
-
- *ok = -1;
+ assert(c && data && length == ESD_NAME_MAX);
strcpy(name, SCACHE_PREFIX);
strncpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX);
name[sizeof(name)-1] = 0;
+ CHECK_VALIDITY(pa_utf8_valid(name), "Invalid UTF8 in sample name.");
+
+ ok = -1;
if ((idx = pa_scache_get_id_by_name(c->protocol->core, name)) != PA_IDXSET_INVALID)
- *ok = (int) idx +1;
+ ok = idx + 1;
+
+ connection_write(c, &ok, sizeof(int32_t));
return 0;
}
static int esd_proto_sample_free_or_play(struct connection *c, esd_proto_t request, const void *data, size_t length) {
- int *ok;
+ int32_t ok;
const char *name;
uint32_t idx;
- assert(c && data && length == sizeof(int));
- idx = (uint32_t) MAYBE_INT32_SWAP(c->swap_byte_order, *(const int*)data)-1;
+ assert(c && data && length == sizeof(int32_t));
- ok = connection_write(c, sizeof(int));
- assert(ok);
+ memcpy(&idx, data, sizeof(uint32_t));
+ idx = MAYBE_UINT32_SWAP(c->swap_byte_order, idx) - 1;
- *ok = 0;
+ ok = 0;
if ((name = pa_scache_get_name_by_id(c->protocol->core, idx))) {
if (request == ESD_PROTO_SAMPLE_PLAY) {
pa_sink *sink;
if ((sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1)))
- if (pa_scache_play_item(c->protocol->core, name, sink, NULL) >= 0)
- *ok = (int) idx+1;
+ if (pa_scache_play_item(c->protocol->core, name, sink, PA_VOLUME_NORM) >= 0)
+ ok = idx + 1;
} else {
assert(request == ESD_PROTO_SAMPLE_FREE);
if (pa_scache_remove_item(c->protocol->core, name) >= 0)
- *ok = (int) idx+1;
+ ok = idx + 1;
}
}
+ connection_write(c, &ok, sizeof(int32_t));
+
return 0;
}
static int esd_proto_standby_or_resume(struct connection *c, PA_GCC_UNUSED esd_proto_t request, PA_GCC_UNUSED const void *data, PA_GCC_UNUSED size_t length) {
- int *ok;
- ok = connection_write(c, sizeof(int)*2);
- assert(ok);
- ok[0] = 1;
- ok[1] = 1;
+ int32_t ok;
+
+ connection_write_prepare(c, sizeof(int32_t) * 2);
+
+ ok = 1;
+ connection_write(c, &ok, sizeof(int32_t));
+ connection_write(c, &ok, sizeof(int32_t));
+
return 0;
}
static int do_read(struct connection *c) {
assert(c && c->io);
-/* pa_log("READ\n"); */
+/* pa_log("READ"); */
if (c->state == ESD_NEXT_REQUEST) {
ssize_t r;
assert(c->read_data_length < sizeof(c->request));
if ((r = pa_iochannel_read(c->io, ((uint8_t*) &c->request) + c->read_data_length, sizeof(c->request) - c->read_data_length)) <= 0) {
- if (r != 0)
- pa_log_warn(__FILE__": read() failed: %s\n", strerror(errno));
+ pa_log_debug(__FILE__": read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
return -1;
}
c->request = MAYBE_INT32_SWAP(c->swap_byte_order, c->request);
if (c->request < ESD_PROTO_CONNECT || c->request > ESD_PROTO_MAX) {
- pa_log(__FILE__": recieved invalid request.\n");
+ pa_log(__FILE__": recieved invalid request.");
return -1;
}
handler = proto_map+c->request;
-/* pa_log(__FILE__": executing request #%u\n", c->request); */
+/* pa_log(__FILE__": executing request #%u", c->request); */
if (!handler->proc) {
- pa_log(__FILE__": recieved unimplemented request #%u.\n", c->request);
+ pa_log(__FILE__": recieved unimplemented request #%u.", c->request);
return -1;
}
assert(c->read_data && c->read_data_length < handler->data_length);
if ((r = pa_iochannel_read(c->io, (uint8_t*) c->read_data + c->read_data_length, handler->data_length - c->read_data_length)) <= 0) {
- if (r != 0)
- pa_log_warn(__FILE__": read() failed: %s\n", strerror(errno));
+ pa_log_debug(__FILE__": read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
return -1;
}
- if ((c->read_data_length+= r) >= handler->data_length) {
+ if ((c->read_data_length += r) >= handler->data_length) {
size_t l = c->read_data_length;
assert(handler->proc);
assert(c->scache.memchunk.memblock && c->scache.name && c->scache.memchunk.index < c->scache.memchunk.length);
if ((r = pa_iochannel_read(c->io, (uint8_t*) c->scache.memchunk.memblock->data+c->scache.memchunk.index, c->scache.memchunk.length-c->scache.memchunk.index)) <= 0) {
- if (r!= 0)
- pa_log_warn(__FILE__": read() failed: %s\n", strerror(errno));
+ pa_log_debug(__FILE__": read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
return -1;
}
if (c->scache.memchunk.index == c->scache.memchunk.length) {
uint32_t idx;
- int *ok;
c->scache.memchunk.index = 0;
pa_scache_add_item(c->protocol->core, c->scache.name, &c->scache.sample_spec, NULL, &c->scache.memchunk, &idx);
c->state = ESD_NEXT_REQUEST;
- ok = connection_write(c, sizeof(int));
- assert(ok);
- *ok = idx+1;
+ idx += 1;
+ connection_write(c, &idx, sizeof(uint32_t));
}
} else if (c->state == ESD_STREAMING_DATA && c->sink_input) {
assert(c->input_memblockq);
-/* pa_log("STREAMING_DATA\n"); */
+/* pa_log("STREAMING_DATA"); */
if (!(l = pa_memblockq_missing(c->input_memblockq)))
return 0;
}
if ((r = pa_iochannel_read(c->io, (uint8_t*) c->playback.current_memblock->data+c->playback.memblock_index, l)) <= 0) {
- if (r != 0)
- pa_log(__FILE__": read() failed: %s\n", strerror(errno));
+ pa_log_debug(__FILE__": read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
return -1;
}
-/* pa_log(__FILE__": read %u\n", r); */
-
chunk.memblock = c->playback.current_memblock;
chunk.index = c->playback.memblock_index;
chunk.length = r;
c->playback.memblock_index += r;
assert(c->input_memblockq);
- pa_memblockq_push_align(c->input_memblockq, &chunk, 0);
+ pa_memblockq_push_align(c->input_memblockq, &chunk);
assert(c->sink_input);
pa_sink_notify(c->sink_input->sink);
}
static int do_write(struct connection *c) {
assert(c && c->io);
-/* pa_log("WRITE\n"); */
+/* pa_log("WRITE"); */
if (c->write_data_length) {
ssize_t r;
assert(c->write_data_index < c->write_data_length);
if ((r = pa_iochannel_write(c->io, (uint8_t*) c->write_data+c->write_data_index, c->write_data_length-c->write_data_index)) < 0) {
- pa_log(__FILE__": write() failed: %s\n", strerror(errno));
+ pa_log(__FILE__": write(): %s", pa_cstrerror(errno));
return -1;
}
if ((r = pa_iochannel_write(c->io, (uint8_t*) chunk.memblock->data+chunk.index, chunk.length)) < 0) {
pa_memblock_unref(chunk.memblock);
- pa_log(__FILE__": write(): %s\n", strerror(errno));
+ pa_log(__FILE__": write(): %s", pa_cstrerror(errno));
return -1;
}
pa_memblockq_drop(c->output_memblockq, &chunk, r);
pa_memblock_unref(chunk.memblock);
+
+ pa_source_notify(c->source_output->source);
}
return 0;
assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);
c->protocol->core->mainloop->defer_enable(c->defer_event, 0);
-/* pa_log("DOWORK %i\n", pa_iochannel_is_hungup(c->io)); */
+ if (c->dead)
+ return;
- if (!c->dead && pa_iochannel_is_readable(c->io))
+ if (pa_iochannel_is_readable(c->io)) {
if (do_read(c) < 0)
goto fail;
+ }
- if (!c->dead && pa_iochannel_is_writable(c->io))
+ if (c->state == ESD_STREAMING_DATA && c->source_output && pa_iochannel_is_hungup(c->io))
+ /* In case we are in capture mode we will never call read()
+ * on the socket, hence we need to detect the hangup manually
+ * here, instead of simply waiting for read() to return 0. */
+ goto fail;
+
+ if (pa_iochannel_is_writable(c->io))
if (do_write(c) < 0)
goto fail;
-
- /* In case the line was hungup, make sure to rerun this function
- as soon as possible, until all data has been read. */
-
- if (!c->dead && pa_iochannel_is_hungup(c->io))
- c->protocol->core->mainloop->defer_enable(c->defer_event, 1);
return;
if (c->state == ESD_STREAMING_DATA && c->sink_input) {
c->dead = 1;
- pa_memblockq_prebuf_disable(c->input_memblockq);
pa_iochannel_free(c->io);
c->io = NULL;
-
+
+ pa_memblockq_prebuf_disable(c->input_memblockq);
+ pa_sink_notify(c->sink_input->sink);
} else
connection_free(c);
}
struct connection *c = userdata;
assert(io && c && c->io == io);
-/* pa_log("IO\n"); */
-
do_work(c);
}
struct connection *c = userdata;
assert(a && c && c->defer_event == e);
-/* pa_log("DEFER\n"); */
+/* pa_log("DEFER"); */
do_work(c);
}
struct connection*c = i->userdata;
assert(i && c && length);
-/* pa_log("DROP\n"); */
+/* pa_log("DROP"); */
pa_memblockq_drop(c->input_memblockq, chunk, length);
struct connection *c = o->userdata;
assert(o && c && chunk);
- pa_memblockq_push(c->output_memblockq, chunk, 0);
+ pa_memblockq_push(c->output_memblockq, chunk);
/* do something */
assert(c->protocol && c->protocol->core && c->protocol->core->mainloop && c->protocol->core->mainloop->defer_enable);
assert(s && io && p);
if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
- pa_log(__FILE__": Warning! Too many connections (%u), dropping incoming connection.\n", MAX_CONNECTIONS);
+ pa_log(__FILE__": Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
pa_iochannel_free(io);
return;
}
- c = pa_xmalloc(sizeof(struct connection));
+ c = pa_xnew(struct connection, 1);
c->protocol = p;
c->io = io;
pa_iochannel_set_callback(c->io, io_callback, c);
c->scache.memchunk.memblock = NULL;
c->scache.name = NULL;
+ c->original_name = NULL;
+
if (!c->authorized) {
struct timeval tv;
pa_gettimeofday(&tv);
int public = 0;
assert(core && server && ma);
- p = pa_xmalloc(sizeof(pa_protocol_esound));
+ p = pa_xnew(pa_protocol_esound, 1);
- if (pa_modargs_get_value_boolean(ma, "public", &public) < 0) {
- pa_log(__FILE__": public= expects a boolean argument.\n");
+ if (pa_modargs_get_value_boolean(ma, "auth-anonymous", &public) < 0) {
+ pa_log(__FILE__": auth-anonymous= expects a boolean argument.");
return NULL;
}