]> code.delx.au - pulseaudio/blobdiff - polyp/protocol-esound.c
* fix include file names in installed header files
[pulseaudio] / polyp / protocol-esound.c
index 4da7e388b20ea8ae6dfd2424eb3127ec655ea8c3..d9ed66b986165864ee5197cb704f29d50223d020 100644 (file)
 #include "scache.h"
 #include "sample-util.h"
 #include "authkey.h"
-#include "debug.h"
 #include "namereg.h"
 #include "xmalloc.h"
 #include "log.h"
 
+/* Don't accept more connection than this */
+#define MAX_CONNECTIONS 10
+
+/* Kick a client if it doesn't authenticate within this time */
+#define AUTH_TIMEOUT 5
+
 #define DEFAULT_COOKIE_FILE ".esd_auth"
 
 #define PLAYBACK_BUFFER_SECONDS (.5)
@@ -58,6 +63,8 @@
 
 #define SCACHE_PREFIX "esound."
 
+#define PA_TYPEID_ESOUND PA_TYPEID_MAKE('E', 'S', 'D', 'P')
+
 /* This is heavily based on esound's code */
 
 struct connection {
@@ -88,6 +95,8 @@ struct connection {
         char *name;
         struct pa_sample_spec sample_spec;
     } scache;
+
+    struct pa_time_event *auth_timeout_event;
 };
 
 struct pa_protocol_esound {
@@ -203,6 +212,9 @@ static void connection_free(struct connection *c) {
     if (c->scache.memchunk.memblock)
         pa_memblock_unref(c->scache.memchunk.memblock);
     pa_xfree(c->scache.name);
+
+    if (c->auth_timeout_event)
+        c->protocol->core->mainloop->time_free(c->auth_timeout_event);
     
     pa_xfree(c);
 }
@@ -257,6 +269,10 @@ static int esd_proto_connect(struct connection *c, esd_proto_t request, const vo
         }
 
         c->authorized = 1;
+        if (c->auth_timeout_event) {
+            c->protocol->core->mainloop->time_free(c->auth_timeout_event);
+            c->auth_timeout_event = NULL;
+        }
     }
     
     ekey = *(uint32_t*)((uint8_t*) data+ESD_KEY_LEN);
@@ -289,11 +305,13 @@ static int esd_proto_stream_play(struct connection *c, esd_proto_t request, cons
     ss.rate = rate;
     format_esd2native(format, &ss);
 
-    if (!pa_sample_spec_valid(&ss))
+    if (!pa_sample_spec_valid(&ss)) {
+        pa_log(__FILE__": invalid sample specification\n");
         return -1;
+    }
 
     if (!(sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1))) {
-        pa_log(__FILE__": No output sink\n");
+        pa_log(__FILE__": no such sink\n");
         return -1;
     }
     
@@ -302,17 +320,17 @@ static int esd_proto_stream_play(struct connection *c, esd_proto_t request, cons
 
     pa_client_set_name(c->client, name);
 
-    assert(!c->input_memblockq);
+    assert(!c->sink_input && !c->input_memblockq);
+
+    if (!(c->sink_input = pa_sink_input_new(sink, PA_TYPEID_ESOUND, name, &ss, 0, -1))) {
+        pa_log(__FILE__": failed to create sink input.\n");
+        return -1;
+    }
 
     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);
-    assert(c->input_memblockq);
     pa_iochannel_socket_set_rcvbuf(c->io, l/PLAYBACK_BUFFER_FRAGMENTS*2);
     c->playback.fragment_size = l/10;
-    
-    assert(!c->sink_input);
-    c->sink_input = pa_sink_input_new(sink, name, &ss, 0, -1);
-    assert(c->sink_input);
 
     c->sink_input->owner = c->protocol->module;
     c->sink_input->client = c->client;
@@ -343,22 +361,30 @@ static int esd_proto_stream_record(struct connection *c, esd_proto_t request, co
     ss.rate = rate;
     format_esd2native(format, &ss);
 
-    if (!pa_sample_spec_valid(&ss))
+    if (!pa_sample_spec_valid(&ss)) {
+        pa_log(__FILE__": invalid sample specification.\n");
         return -1;
+    }
 
     if (request == ESD_PROTO_STREAM_MON) {
         struct pa_sink* sink;
 
-        if (!(sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1)))
+        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;
+        }
 
-        if (!(source = sink->monitor_source))
+        if (!(source = sink->monitor_source)) {
+            pa_log(__FILE__": no such monitor source.\n");
             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)))
+        if (!(source = pa_namereg_get(c->protocol->core, c->protocol->source_name, PA_NAMEREG_SOURCE, 1))) {
+            pa_log(__FILE__": no such source.\n");
             return -1;
+        }
     }
     
     strncpy(name, (char*) data + sizeof(int)*2, sizeof(name));
@@ -366,17 +392,17 @@ static int esd_proto_stream_record(struct connection *c, esd_proto_t request, co
 
     pa_client_set_name(c->client, name);
 
-    assert(!c->output_memblockq);
+    assert(!c->output_memblockq && !c->source_output);
+
+    if (!(c->source_output = pa_source_output_new(source, PA_TYPEID_ESOUND, name, &ss, -1))) {
+        pa_log(__FILE__": failed to create source output\n");
+        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);
-    assert(c->output_memblockq);
     pa_iochannel_socket_set_sndbuf(c->io, l/RECORD_BUFFER_FRAGMENTS*2);
     
-    assert(!c->source_output);
-    c->source_output = pa_source_output_new(source, name, &ss, -1);
-    assert(c->source_output);
-    
     c->source_output->owner = c->protocol->module;
     c->source_output->client = c->client;
     c->source_output->push = source_output_push_cb;
@@ -817,8 +843,8 @@ static int do_read(struct connection *c) {
             pa_log(__FILE__": read() failed: %s\n", r == 0 ? "EOF" : strerror(errno));
             return -1;
         }
-
-/*         pa_log(__FILE__": read %u\n", r); */
+        
+/*         pa_log(__FILE__": read %u\n", r);  */
         
         chunk.memblock = c->playback.current_memblock;
         chunk.index = c->playback.memblock_index;
@@ -868,7 +894,7 @@ static int do_write(struct connection *c) {
             pa_log(__FILE__": write(): %s\n", strerror(errno));
             return -1;
         }
-    
+
         pa_memblockq_drop(c->output_memblockq, &chunk, r);
         pa_memblock_unref(chunk.memblock);
     }
@@ -882,18 +908,21 @@ static void do_work(struct connection *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, 0);
 
-/*     pa_log("DOWORK\n");  */
-    
-    if (c->dead || !c->io)
-        return;
+/*     pa_log("DOWORK %i\n", pa_iochannel_is_hungup(c->io));   */
 
-    if (pa_iochannel_is_readable(c->io))
+    if (!c->dead && pa_iochannel_is_readable(c->io))
         if (do_read(c) < 0)
             goto fail;
-    
-    if (pa_iochannel_is_writable(c->io))
+
+    if (!c->dead && 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;
 
@@ -1004,25 +1033,40 @@ static pa_usec_t source_output_get_latency_cb(struct pa_source_output *o) {
 
 /*** socket server callback ***/
 
+static void auth_timeout(struct pa_mainloop_api*m, struct pa_time_event *e, const struct timeval *tv, void *userdata) {
+    struct connection *c = userdata;
+    assert(m && tv && c && c->auth_timeout_event == e);
+
+    if (!c->authorized)
+        connection_free(c);
+}
+
 static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, void *userdata) {
     struct connection *c;
+    struct pa_protocol_esound *p = userdata;
     char cname[256];
-    assert(s && io && userdata);
+    assert(s && io && p);
 
+    if (pa_idxset_ncontents(p->connections)+1 > MAX_CONNECTIONS) {
+        pa_log(__FILE__": Warning! Too many connections (%u), dropping incoming connection.\n", MAX_CONNECTIONS);
+        pa_iochannel_free(io);
+        return;
+    }
+    
     c = pa_xmalloc(sizeof(struct connection));
-    c->protocol = userdata;
+    c->protocol = p;
     c->io = io;
     pa_iochannel_set_callback(c->io, io_callback, c);
 
     pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname));
-    assert(c->protocol->core);
-    c->client = pa_client_new(c->protocol->core, "ESOUND", cname);
+    assert(p->core);
+    c->client = pa_client_new(p->core, PA_TYPEID_ESOUND, cname);
     assert(c->client);
-    c->client->owner = c->protocol->module;
+    c->client->owner = p->module;
     c->client->kill = client_kill_cb;
     c->client->userdata = c;
     
-    c->authorized = c->protocol->public;
+    c->authorized = p->public;
     c->swap_byte_order = 0;
     c->dead = 0;
 
@@ -1048,19 +1092,27 @@ static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, vo
     c->scache.memchunk.length = c->scache.memchunk.index = 0;
     c->scache.memchunk.memblock = NULL;
     c->scache.name = NULL;
+
+    if (!c->authorized) {
+        struct timeval tv;
+        gettimeofday(&tv, NULL);
+        tv.tv_sec += AUTH_TIMEOUT;
+        c->auth_timeout_event = p->core->mainloop->time_new(p->core->mainloop, &tv, auth_timeout, c);
+    } else
+        c->auth_timeout_event = NULL;
     
-    c->defer_event = c->protocol->core->mainloop->defer_new(c->protocol->core->mainloop, defer_callback, c);
+    c->defer_event = p->core->mainloop->defer_new(p->core->mainloop, defer_callback, c);
     assert(c->defer_event);
-    c->protocol->core->mainloop->defer_enable(c->defer_event, 0);
+    p->core->mainloop->defer_enable(c->defer_event, 0);
 
-    pa_idxset_put(c->protocol->connections, c, &c->index);
+    pa_idxset_put(p->connections, c, &c->index);
 }
 
 /*** entry points ***/
 
 struct pa_protocol_esound* pa_protocol_esound_new(struct pa_core*core, struct pa_socket_server *server, struct pa_module *m, struct pa_modargs *ma) {
     struct pa_protocol_esound *p;
-    int public;
+    int public = 0;
     assert(core && server && ma);
 
     p = pa_xmalloc(sizeof(struct pa_protocol_esound));