]> 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 2c956a7e279dfad034ed35ce548a3290bd7dc3cd..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);
@@ -217,7 +222,8 @@ 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);
 }
 
@@ -315,7 +321,7 @@ static int esd_proto_connect(struct connection *c, PA_GCC_UNUSED esd_proto_t req
 }
 
 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];
+    char name[ESD_NAME_MAX], *utf8_name;
     int32_t format, rate;
     pa_sink *sink;
     pa_sample_spec ss;
@@ -340,12 +346,16 @@ static int esd_proto_stream_play(struct connection *c, PA_GCC_UNUSED esd_proto_t
 
     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);
 
-    c->sink_input = pa_sink_input_new(sink, __FILE__, name, &ss, NULL, 0, -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.");
 
@@ -378,7 +388,7 @@ 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];
+    char name[ESD_NAME_MAX], *utf8_name;
     int32_t format, rate;
     pa_source *source;
     pa_sample_spec ss;
@@ -423,11 +433,15 @@ static int esd_proto_stream_record(struct connection *c, esd_proto_t request, co
     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))) {
+    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;
     }
@@ -501,7 +515,6 @@ static int esd_proto_server_info(struct connection *c, PA_GCC_UNUSED esd_proto_t
 }
 
 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;
     uint32_t idx = PA_IDXSET_INVALID;
@@ -532,9 +545,10 @@ static int esd_proto_all_info(struct connection *c, esd_proto_t request, const v
         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);
         }
         
@@ -543,8 +557,11 @@ static int esd_proto_all_info(struct connection *c, esd_proto_t request, const v
         connection_write(c, &id, sizeof(int32_t));
 
         /* name */
-        assert(conn->client);
-        strncpy(name, conn->client->name, 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 */
@@ -567,7 +584,6 @@ static int esd_proto_all_info(struct connection *c, esd_proto_t request, const v
     }
 
     assert(t == s*(nsamples+1)+k);
-    response += k;
     t -= k;
 
     connection_write(c, terminator, k);
@@ -587,6 +603,7 @@ static int esd_proto_all_info(struct connection *c, esd_proto_t request, const v
             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(name, ce->name+sizeof(SCACHE_PREFIX)-1, ESD_NAME_MAX);
             else
@@ -643,11 +660,12 @@ static int esd_proto_stream_pan(struct connection *c, PA_GCC_UNUSED esd_proto_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))) {
-        assert(conn->sink_input);
-        conn->sink_input->volume.values[0] = (lvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE;
-        conn->sink_input->volume.values[1] = (rvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE;
-        conn->sink_input->volume.channels = 2;
+    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;
@@ -680,12 +698,15 @@ static int esd_proto_sample_cache(struct connection *c, PA_GCC_UNUSED esd_proto_
 
     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, 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);
@@ -694,7 +715,7 @@ 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);
@@ -716,6 +737,8 @@ static int esd_proto_sample_get_id(struct connection *c, PA_GCC_UNUSED esd_proto
     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 = idx + 1;
@@ -788,7 +811,7 @@ static int do_read(struct connection *c) {
         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", r < 0 ? strerror(errno) : "EOF");
+            pa_log_debug(__FILE__": read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
             return -1;
         }
 
@@ -836,7 +859,7 @@ 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", r < 0 ? strerror(errno) : "EOF");
+            pa_log_debug(__FILE__": read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
             return -1;
         }
 
@@ -856,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", r < 0 ? strerror(errno) : "EOF");
+            pa_log_debug(__FILE__": read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
             return -1;
         }
 
@@ -911,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", r < 0 ? strerror(errno) : "EOF");
+            pa_log_debug(__FILE__": read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
             return -1;
         }
         
@@ -941,7 +964,7 @@ static int do_write(struct connection *c) {
         
         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", strerror(errno));
+            pa_log(__FILE__": write(): %s", pa_cstrerror(errno));
             return -1;
         }
         
@@ -960,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", strerror(errno));
+            pa_log(__FILE__": write(): %s", pa_cstrerror(errno));
             return -1;
         }
 
@@ -985,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))
@@ -1008,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");  */
-    
     do_work(c);
 }
 
@@ -1163,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);