]> code.delx.au - pulseaudio/blobdiff - src/polypcore/protocol-esound.c
* split pa_cstrerror() into its own file polypcore/core-error.[ch]
[pulseaudio] / src / polypcore / protocol-esound.c
index 5adff57af4ae22cd97e5909d30077a9e6e3bfa65..02e140b953f05a939739aa907efa577db5a482f2 100644 (file)
 #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>
@@ -42,9 +46,9 @@
 #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"
 
@@ -58,7 +62,7 @@
 
 #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)
@@ -86,6 +90,8 @@ struct connection {
     pa_source_output *source_output;
     pa_memblockq *input_memblockq, *output_memblockq;
     pa_defer_event *defer_event;
+
+    char *original_name;
     
     struct {
         pa_memblock *current_memblock;
@@ -174,7 +180,6 @@ static struct proto_handler proto_map[ESD_PROTO_MAX] = {
     { 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);
@@ -186,7 +191,6 @@ static void connection_free(struct connection *c) {
 
     if (c->sink_input) {
         pa_sink_input_disconnect(c->sink_input);
-        pa_log("disconnect\n");
         pa_sink_input_unref(c->sink_input);
     }
     
@@ -218,28 +222,38 @@ static void connection_free(struct connection *c) {
 
     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) {
@@ -261,16 +275,24 @@ static int format_native2esd(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;
         }
 
@@ -280,58 +302,62 @@ static int esd_proto_connect(struct connection *c, PA_GCC_UNUSED esd_proto_t req
             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(
@@ -362,54 +388,61 @@ static int esd_proto_stream_play(struct connection *c, PA_GCC_UNUSED esd_proto_t
 }
 
 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;
     }
 
@@ -441,7 +474,8 @@ static int esd_proto_stream_record(struct connection *c, esd_proto_t request, co
 
 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)))
@@ -451,195 +485,228 @@ static int esd_proto_get_latency(struct connection *c, PA_GCC_UNUSED esd_proto_t
         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);
@@ -648,77 +715,80 @@ static int esd_proto_sample_cache(struct connection *c, PA_GCC_UNUSED esd_proto_
     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;
 }
 
@@ -734,14 +804,14 @@ static void client_kill_cb(pa_client *c) {
 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) {
-            pa_log_debug(__FILE__": read() failed: %s\n", r < 0 ? strerror(errno) : "EOF");
+            pa_log_debug(__FILE__": read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
             return -1;
         }
 
@@ -751,16 +821,16 @@ static int do_read(struct connection *c) {
             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;
             }
             
@@ -789,11 +859,11 @@ static int do_read(struct connection *c) {
         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) {
-            pa_log_debug(__FILE__": read() failed: %s\n", r < 0 ? strerror(errno) : "EOF");
+            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);
 
@@ -809,7 +879,7 @@ static int do_read(struct connection *c) {
         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) {
-            pa_log_debug(__FILE__": read() failed: %s\n", r < 0 ? strerror(errno) : "EOF");
+            pa_log_debug(__FILE__": read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
             return -1;
         }
 
@@ -818,7 +888,6 @@ static int do_read(struct connection *c) {
         
         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);
@@ -832,9 +901,8 @@ static int do_read(struct connection *c) {
 
             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) {
@@ -844,7 +912,7 @@ static int do_read(struct connection *c) {
 
         assert(c->input_memblockq);
 
-/*         pa_log("STREAMING_DATA\n"); */
+/*         pa_log("STREAMING_DATA"); */
 
         if (!(l = pa_memblockq_missing(c->input_memblockq)))
             return 0;
@@ -866,7 +934,7 @@ static int do_read(struct connection *c) {
         }
 
         if ((r = pa_iochannel_read(c->io, (uint8_t*) c->playback.current_memblock->data+c->playback.memblock_index, l)) <= 0) {
-            pa_log_debug(__FILE__": read() failed: %s\n", r < 0 ? strerror(errno) : "EOF");
+            pa_log_debug(__FILE__": read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
             return -1;
         }
         
@@ -889,14 +957,14 @@ static int do_read(struct connection *c) {
 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;
         }
         
@@ -915,7 +983,7 @@ static int do_write(struct connection *c) {
         
         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;
         }
 
@@ -940,7 +1008,12 @@ static void do_work(struct connection *c) {
     if (pa_iochannel_is_readable(c->io)) {
         if (do_read(c) < 0)
             goto fail;
-    } else if (pa_iochannel_is_hungup(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))
@@ -963,13 +1036,10 @@ fail:
         connection_free(c);
 }
 
-
 static void io_callback(pa_iochannel*io, void *userdata) {
     struct connection *c = userdata;
     assert(io && c && c->io == io);
 
-/*     pa_log("IO\n");  */
-    
     do_work(c);
 }
 
@@ -979,7 +1049,7 @@ static void defer_callback(pa_mainloop_api*a, pa_defer_event *e, void *userdata)
     struct connection *c = userdata;
     assert(a && c && c->defer_event == e);
 
-/*     pa_log("DEFER\n"); */
+/*     pa_log("DEFER"); */
     
     do_work(c);
 }
@@ -1006,7 +1076,7 @@ static void sink_input_drop_cb(pa_sink_input *i, const pa_memchunk *chunk, size_
     struct connection*c = i->userdata;
     assert(i && c && length);
 
-/*     pa_log("DROP\n"); */
+/*     pa_log("DROP"); */
     
     pa_memblockq_drop(c->input_memblockq, chunk, length);
 
@@ -1073,12 +1143,12 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
     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);
@@ -1118,6 +1188,8 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
     c->scache.memchunk.memblock = NULL;
     c->scache.name = NULL;
 
+    c->original_name = NULL;
+
     if (!c->authorized) {
         struct timeval tv;
         pa_gettimeofday(&tv);
@@ -1140,10 +1212,10 @@ pa_protocol_esound* pa_protocol_esound_new(pa_core*core, pa_socket_server *serve
     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;
     }