153a5a1b3Sopenharmony_ci/***
253a5a1b3Sopenharmony_ci  This file is part of PulseAudio.
353a5a1b3Sopenharmony_ci
453a5a1b3Sopenharmony_ci  Copyright 2005-2009 Lennart Poettering
553a5a1b3Sopenharmony_ci
653a5a1b3Sopenharmony_ci  PulseAudio is free software; you can redistribute it and/or modify
753a5a1b3Sopenharmony_ci  it under the terms of the GNU Lesser General Public License as published
853a5a1b3Sopenharmony_ci  by the Free Software Foundation; either version 2.1 of the License,
953a5a1b3Sopenharmony_ci  or (at your option) any later version.
1053a5a1b3Sopenharmony_ci
1153a5a1b3Sopenharmony_ci  PulseAudio is distributed in the hope that it will be useful, but
1253a5a1b3Sopenharmony_ci  WITHOUT ANY WARRANTY; without even the implied warranty of
1353a5a1b3Sopenharmony_ci  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1453a5a1b3Sopenharmony_ci  General Public License for more details.
1553a5a1b3Sopenharmony_ci
1653a5a1b3Sopenharmony_ci  You should have received a copy of the GNU Lesser General Public License
1753a5a1b3Sopenharmony_ci  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
1853a5a1b3Sopenharmony_ci***/
1953a5a1b3Sopenharmony_ci
2053a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H
2153a5a1b3Sopenharmony_ci#include <config.h>
2253a5a1b3Sopenharmony_ci#endif
2353a5a1b3Sopenharmony_ci
2453a5a1b3Sopenharmony_ci#include <stdlib.h>
2553a5a1b3Sopenharmony_ci#include <stdio.h>
2653a5a1b3Sopenharmony_ci#include <string.h>
2753a5a1b3Sopenharmony_ci#include <errno.h>
2853a5a1b3Sopenharmony_ci
2953a5a1b3Sopenharmony_ci#include <pulse/util.h>
3053a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h>
3153a5a1b3Sopenharmony_ci#include <pulse/timeval.h>
3253a5a1b3Sopenharmony_ci
3353a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h>
3453a5a1b3Sopenharmony_ci#include <pulsecore/ioline.h>
3553a5a1b3Sopenharmony_ci#include <pulsecore/thread-mq.h>
3653a5a1b3Sopenharmony_ci#include <pulsecore/macro.h>
3753a5a1b3Sopenharmony_ci#include <pulsecore/log.h>
3853a5a1b3Sopenharmony_ci#include <pulsecore/namereg.h>
3953a5a1b3Sopenharmony_ci#include <pulsecore/cli-text.h>
4053a5a1b3Sopenharmony_ci#include <pulsecore/shared.h>
4153a5a1b3Sopenharmony_ci#include <pulsecore/core-error.h>
4253a5a1b3Sopenharmony_ci#include <pulsecore/mime-type.h>
4353a5a1b3Sopenharmony_ci
4453a5a1b3Sopenharmony_ci#include "protocol-http.h"
4553a5a1b3Sopenharmony_ci
4653a5a1b3Sopenharmony_ci/* Don't allow more than this many concurrent connections */
4753a5a1b3Sopenharmony_ci#define MAX_CONNECTIONS 10
4853a5a1b3Sopenharmony_ci
4953a5a1b3Sopenharmony_ci#define URL_ROOT "/"
5053a5a1b3Sopenharmony_ci#define URL_CSS "/style"
5153a5a1b3Sopenharmony_ci#define URL_STATUS "/status"
5253a5a1b3Sopenharmony_ci#define URL_LISTEN "/listen"
5353a5a1b3Sopenharmony_ci#define URL_LISTEN_SOURCE "/listen/source/"
5453a5a1b3Sopenharmony_ci
5553a5a1b3Sopenharmony_ci#define MIME_HTML "text/html; charset=utf-8"
5653a5a1b3Sopenharmony_ci#define MIME_TEXT "text/plain; charset=utf-8"
5753a5a1b3Sopenharmony_ci#define MIME_CSS "text/css"
5853a5a1b3Sopenharmony_ci
5953a5a1b3Sopenharmony_ci#define HTML_HEADER(t)                                                  \
6053a5a1b3Sopenharmony_ci    "<?xml version=\"1.0\"?>\n"                                         \
6153a5a1b3Sopenharmony_ci    "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n" \
6253a5a1b3Sopenharmony_ci    "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"                   \
6353a5a1b3Sopenharmony_ci    "        <head>\n"                                                  \
6453a5a1b3Sopenharmony_ci    "                <title>"t"</title>\n"                              \
6553a5a1b3Sopenharmony_ci    "                <link rel=\"stylesheet\" type=\"text/css\" href=\"style\"/>\n" \
6653a5a1b3Sopenharmony_ci    "        </head>\n"                                                 \
6753a5a1b3Sopenharmony_ci    "        <body>\n"
6853a5a1b3Sopenharmony_ci
6953a5a1b3Sopenharmony_ci#define HTML_FOOTER                                                     \
7053a5a1b3Sopenharmony_ci    "        </body>\n"                                                 \
7153a5a1b3Sopenharmony_ci    "</html>\n"
7253a5a1b3Sopenharmony_ci
7353a5a1b3Sopenharmony_ci#define RECORD_BUFFER_SECONDS (5)
7453a5a1b3Sopenharmony_ci#define DEFAULT_SOURCE_LATENCY (300*PA_USEC_PER_MSEC)
7553a5a1b3Sopenharmony_ci
7653a5a1b3Sopenharmony_cienum state {
7753a5a1b3Sopenharmony_ci    STATE_REQUEST_LINE,
7853a5a1b3Sopenharmony_ci    STATE_MIME_HEADER,
7953a5a1b3Sopenharmony_ci    STATE_DATA
8053a5a1b3Sopenharmony_ci};
8153a5a1b3Sopenharmony_ci
8253a5a1b3Sopenharmony_cienum method {
8353a5a1b3Sopenharmony_ci    METHOD_GET,
8453a5a1b3Sopenharmony_ci    METHOD_HEAD
8553a5a1b3Sopenharmony_ci};
8653a5a1b3Sopenharmony_ci
8753a5a1b3Sopenharmony_cistruct connection {
8853a5a1b3Sopenharmony_ci    pa_http_protocol *protocol;
8953a5a1b3Sopenharmony_ci    pa_iochannel *io;
9053a5a1b3Sopenharmony_ci    pa_ioline *line;
9153a5a1b3Sopenharmony_ci    pa_memblockq *output_memblockq;
9253a5a1b3Sopenharmony_ci    pa_source_output *source_output;
9353a5a1b3Sopenharmony_ci    pa_client *client;
9453a5a1b3Sopenharmony_ci    enum state state;
9553a5a1b3Sopenharmony_ci    char *url;
9653a5a1b3Sopenharmony_ci    enum method method;
9753a5a1b3Sopenharmony_ci    pa_module *module;
9853a5a1b3Sopenharmony_ci};
9953a5a1b3Sopenharmony_ci
10053a5a1b3Sopenharmony_cistruct pa_http_protocol {
10153a5a1b3Sopenharmony_ci    PA_REFCNT_DECLARE;
10253a5a1b3Sopenharmony_ci
10353a5a1b3Sopenharmony_ci    pa_core *core;
10453a5a1b3Sopenharmony_ci    pa_idxset *connections;
10553a5a1b3Sopenharmony_ci
10653a5a1b3Sopenharmony_ci    pa_strlist *servers;
10753a5a1b3Sopenharmony_ci};
10853a5a1b3Sopenharmony_ci
10953a5a1b3Sopenharmony_cienum {
11053a5a1b3Sopenharmony_ci    SOURCE_OUTPUT_MESSAGE_POST_DATA = PA_SOURCE_OUTPUT_MESSAGE_MAX
11153a5a1b3Sopenharmony_ci};
11253a5a1b3Sopenharmony_ci
11353a5a1b3Sopenharmony_ci/* Called from main context */
11453a5a1b3Sopenharmony_cistatic void connection_unlink(struct connection *c) {
11553a5a1b3Sopenharmony_ci    pa_assert(c);
11653a5a1b3Sopenharmony_ci
11753a5a1b3Sopenharmony_ci    if (c->source_output) {
11853a5a1b3Sopenharmony_ci        pa_source_output_unlink(c->source_output);
11953a5a1b3Sopenharmony_ci        c->source_output->userdata = NULL;
12053a5a1b3Sopenharmony_ci        pa_source_output_unref(c->source_output);
12153a5a1b3Sopenharmony_ci    }
12253a5a1b3Sopenharmony_ci
12353a5a1b3Sopenharmony_ci    if (c->client)
12453a5a1b3Sopenharmony_ci        pa_client_free(c->client);
12553a5a1b3Sopenharmony_ci
12653a5a1b3Sopenharmony_ci    pa_xfree(c->url);
12753a5a1b3Sopenharmony_ci
12853a5a1b3Sopenharmony_ci    if (c->line)
12953a5a1b3Sopenharmony_ci        pa_ioline_unref(c->line);
13053a5a1b3Sopenharmony_ci
13153a5a1b3Sopenharmony_ci    if (c->io)
13253a5a1b3Sopenharmony_ci        pa_iochannel_free(c->io);
13353a5a1b3Sopenharmony_ci
13453a5a1b3Sopenharmony_ci    if (c->output_memblockq)
13553a5a1b3Sopenharmony_ci        pa_memblockq_free(c->output_memblockq);
13653a5a1b3Sopenharmony_ci
13753a5a1b3Sopenharmony_ci    pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
13853a5a1b3Sopenharmony_ci
13953a5a1b3Sopenharmony_ci    pa_xfree(c);
14053a5a1b3Sopenharmony_ci}
14153a5a1b3Sopenharmony_ci
14253a5a1b3Sopenharmony_ci/* Called from main context */
14353a5a1b3Sopenharmony_cistatic int do_write(struct connection *c) {
14453a5a1b3Sopenharmony_ci    pa_memchunk chunk;
14553a5a1b3Sopenharmony_ci    ssize_t r;
14653a5a1b3Sopenharmony_ci    void *p;
14753a5a1b3Sopenharmony_ci
14853a5a1b3Sopenharmony_ci    pa_assert(c);
14953a5a1b3Sopenharmony_ci
15053a5a1b3Sopenharmony_ci    if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0)
15153a5a1b3Sopenharmony_ci        return 0;
15253a5a1b3Sopenharmony_ci
15353a5a1b3Sopenharmony_ci    pa_assert(chunk.memblock);
15453a5a1b3Sopenharmony_ci    pa_assert(chunk.length > 0);
15553a5a1b3Sopenharmony_ci
15653a5a1b3Sopenharmony_ci    p = pa_memblock_acquire(chunk.memblock);
15753a5a1b3Sopenharmony_ci    r = pa_iochannel_write(c->io, (uint8_t*) p+chunk.index, chunk.length);
15853a5a1b3Sopenharmony_ci    pa_memblock_release(chunk.memblock);
15953a5a1b3Sopenharmony_ci
16053a5a1b3Sopenharmony_ci    pa_memblock_unref(chunk.memblock);
16153a5a1b3Sopenharmony_ci
16253a5a1b3Sopenharmony_ci    if (r < 0) {
16353a5a1b3Sopenharmony_ci        pa_log("write(): %s", pa_cstrerror(errno));
16453a5a1b3Sopenharmony_ci        return -1;
16553a5a1b3Sopenharmony_ci    }
16653a5a1b3Sopenharmony_ci
16753a5a1b3Sopenharmony_ci    pa_memblockq_drop(c->output_memblockq, (size_t) r);
16853a5a1b3Sopenharmony_ci
16953a5a1b3Sopenharmony_ci    return 1;
17053a5a1b3Sopenharmony_ci}
17153a5a1b3Sopenharmony_ci
17253a5a1b3Sopenharmony_ci/* Called from main context */
17353a5a1b3Sopenharmony_cistatic void do_work(struct connection *c) {
17453a5a1b3Sopenharmony_ci    pa_assert(c);
17553a5a1b3Sopenharmony_ci
17653a5a1b3Sopenharmony_ci    if (pa_iochannel_is_hungup(c->io))
17753a5a1b3Sopenharmony_ci        goto fail;
17853a5a1b3Sopenharmony_ci
17953a5a1b3Sopenharmony_ci    while (pa_iochannel_is_writable(c->io)) {
18053a5a1b3Sopenharmony_ci        int r = do_write(c);
18153a5a1b3Sopenharmony_ci        if (r < 0)
18253a5a1b3Sopenharmony_ci            goto fail;
18353a5a1b3Sopenharmony_ci        if (r == 0)
18453a5a1b3Sopenharmony_ci            break;
18553a5a1b3Sopenharmony_ci    }
18653a5a1b3Sopenharmony_ci
18753a5a1b3Sopenharmony_ci    return;
18853a5a1b3Sopenharmony_ci
18953a5a1b3Sopenharmony_cifail:
19053a5a1b3Sopenharmony_ci    connection_unlink(c);
19153a5a1b3Sopenharmony_ci}
19253a5a1b3Sopenharmony_ci
19353a5a1b3Sopenharmony_ci/* Called from thread context, except when it is not */
19453a5a1b3Sopenharmony_cistatic int source_output_process_msg(pa_msgobject *m, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
19553a5a1b3Sopenharmony_ci    pa_source_output *o = PA_SOURCE_OUTPUT(m);
19653a5a1b3Sopenharmony_ci    struct connection *c;
19753a5a1b3Sopenharmony_ci
19853a5a1b3Sopenharmony_ci    pa_source_output_assert_ref(o);
19953a5a1b3Sopenharmony_ci
20053a5a1b3Sopenharmony_ci    if (!(c = o->userdata))
20153a5a1b3Sopenharmony_ci        return -1;
20253a5a1b3Sopenharmony_ci
20353a5a1b3Sopenharmony_ci    switch (code) {
20453a5a1b3Sopenharmony_ci
20553a5a1b3Sopenharmony_ci        case SOURCE_OUTPUT_MESSAGE_POST_DATA:
20653a5a1b3Sopenharmony_ci            /* While this function is usually called from IO thread
20753a5a1b3Sopenharmony_ci             * context, this specific command is not! */
20853a5a1b3Sopenharmony_ci            pa_memblockq_push_align(c->output_memblockq, chunk);
20953a5a1b3Sopenharmony_ci            do_work(c);
21053a5a1b3Sopenharmony_ci            break;
21153a5a1b3Sopenharmony_ci
21253a5a1b3Sopenharmony_ci        default:
21353a5a1b3Sopenharmony_ci            return pa_source_output_process_msg(m, code, userdata, offset, chunk);
21453a5a1b3Sopenharmony_ci    }
21553a5a1b3Sopenharmony_ci
21653a5a1b3Sopenharmony_ci    return 0;
21753a5a1b3Sopenharmony_ci}
21853a5a1b3Sopenharmony_ci
21953a5a1b3Sopenharmony_ci/* Called from thread context */
22053a5a1b3Sopenharmony_cistatic void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
22153a5a1b3Sopenharmony_ci    struct connection *c;
22253a5a1b3Sopenharmony_ci
22353a5a1b3Sopenharmony_ci    pa_source_output_assert_ref(o);
22453a5a1b3Sopenharmony_ci    pa_assert_se(c = o->userdata);
22553a5a1b3Sopenharmony_ci    pa_assert(chunk);
22653a5a1b3Sopenharmony_ci
22753a5a1b3Sopenharmony_ci    pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(o), SOURCE_OUTPUT_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);
22853a5a1b3Sopenharmony_ci}
22953a5a1b3Sopenharmony_ci
23053a5a1b3Sopenharmony_ci/* Called from main context */
23153a5a1b3Sopenharmony_cistatic void source_output_kill_cb(pa_source_output *o) {
23253a5a1b3Sopenharmony_ci    struct connection*c;
23353a5a1b3Sopenharmony_ci
23453a5a1b3Sopenharmony_ci    pa_source_output_assert_ref(o);
23553a5a1b3Sopenharmony_ci    pa_assert_se(c = o->userdata);
23653a5a1b3Sopenharmony_ci
23753a5a1b3Sopenharmony_ci    connection_unlink(c);
23853a5a1b3Sopenharmony_ci}
23953a5a1b3Sopenharmony_ci
24053a5a1b3Sopenharmony_ci/* Called from main context */
24153a5a1b3Sopenharmony_cistatic pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
24253a5a1b3Sopenharmony_ci    struct connection*c;
24353a5a1b3Sopenharmony_ci
24453a5a1b3Sopenharmony_ci    pa_source_output_assert_ref(o);
24553a5a1b3Sopenharmony_ci    pa_assert_se(c = o->userdata);
24653a5a1b3Sopenharmony_ci
24753a5a1b3Sopenharmony_ci    return pa_bytes_to_usec(pa_memblockq_get_length(c->output_memblockq), &c->source_output->sample_spec);
24853a5a1b3Sopenharmony_ci}
24953a5a1b3Sopenharmony_ci
25053a5a1b3Sopenharmony_ci/*** client callbacks ***/
25153a5a1b3Sopenharmony_cistatic void client_kill_cb(pa_client *client) {
25253a5a1b3Sopenharmony_ci    struct connection*c;
25353a5a1b3Sopenharmony_ci
25453a5a1b3Sopenharmony_ci    pa_assert(client);
25553a5a1b3Sopenharmony_ci    pa_assert_se(c = client->userdata);
25653a5a1b3Sopenharmony_ci
25753a5a1b3Sopenharmony_ci    connection_unlink(c);
25853a5a1b3Sopenharmony_ci}
25953a5a1b3Sopenharmony_ci
26053a5a1b3Sopenharmony_ci/*** pa_iochannel callbacks ***/
26153a5a1b3Sopenharmony_cistatic void io_callback(pa_iochannel*io, void *userdata) {
26253a5a1b3Sopenharmony_ci    struct connection *c = userdata;
26353a5a1b3Sopenharmony_ci
26453a5a1b3Sopenharmony_ci    pa_assert(c);
26553a5a1b3Sopenharmony_ci    pa_assert(io);
26653a5a1b3Sopenharmony_ci
26753a5a1b3Sopenharmony_ci    do_work(c);
26853a5a1b3Sopenharmony_ci}
26953a5a1b3Sopenharmony_ci
27053a5a1b3Sopenharmony_cistatic char *escape_html(const char *t) {
27153a5a1b3Sopenharmony_ci    pa_strbuf *sb;
27253a5a1b3Sopenharmony_ci    const char *p, *e;
27353a5a1b3Sopenharmony_ci
27453a5a1b3Sopenharmony_ci    sb = pa_strbuf_new();
27553a5a1b3Sopenharmony_ci
27653a5a1b3Sopenharmony_ci    for (e = p = t; *p; p++) {
27753a5a1b3Sopenharmony_ci
27853a5a1b3Sopenharmony_ci        if (*p == '>' || *p == '<' || *p == '&') {
27953a5a1b3Sopenharmony_ci
28053a5a1b3Sopenharmony_ci            if (p > e) {
28153a5a1b3Sopenharmony_ci                pa_strbuf_putsn(sb, e, p-e);
28253a5a1b3Sopenharmony_ci                e = p + 1;
28353a5a1b3Sopenharmony_ci            }
28453a5a1b3Sopenharmony_ci
28553a5a1b3Sopenharmony_ci            if (*p == '>')
28653a5a1b3Sopenharmony_ci                pa_strbuf_puts(sb, "&gt;");
28753a5a1b3Sopenharmony_ci            else if (*p == '<')
28853a5a1b3Sopenharmony_ci                pa_strbuf_puts(sb, "&lt;");
28953a5a1b3Sopenharmony_ci            else
29053a5a1b3Sopenharmony_ci                pa_strbuf_puts(sb, "&amp;");
29153a5a1b3Sopenharmony_ci        }
29253a5a1b3Sopenharmony_ci    }
29353a5a1b3Sopenharmony_ci
29453a5a1b3Sopenharmony_ci    if (p > e)
29553a5a1b3Sopenharmony_ci        pa_strbuf_putsn(sb, e, p-e);
29653a5a1b3Sopenharmony_ci
29753a5a1b3Sopenharmony_ci    return pa_strbuf_to_string_free(sb);
29853a5a1b3Sopenharmony_ci}
29953a5a1b3Sopenharmony_ci
30053a5a1b3Sopenharmony_cistatic void http_response(
30153a5a1b3Sopenharmony_ci        struct connection *c,
30253a5a1b3Sopenharmony_ci        int code,
30353a5a1b3Sopenharmony_ci        const char *msg,
30453a5a1b3Sopenharmony_ci        const char *mime) {
30553a5a1b3Sopenharmony_ci
30653a5a1b3Sopenharmony_ci    char *s;
30753a5a1b3Sopenharmony_ci
30853a5a1b3Sopenharmony_ci    pa_assert(c);
30953a5a1b3Sopenharmony_ci    pa_assert(msg);
31053a5a1b3Sopenharmony_ci    pa_assert(mime);
31153a5a1b3Sopenharmony_ci
31253a5a1b3Sopenharmony_ci    s = pa_sprintf_malloc(
31353a5a1b3Sopenharmony_ci            "HTTP/1.0 %i %s\n"
31453a5a1b3Sopenharmony_ci            "Connection: close\n"
31553a5a1b3Sopenharmony_ci            "Content-Type: %s\n"
31653a5a1b3Sopenharmony_ci            "Cache-Control: no-cache\n"
31753a5a1b3Sopenharmony_ci            "Expires: 0\n"
31853a5a1b3Sopenharmony_ci            "Server: "PACKAGE_NAME"/"PACKAGE_VERSION"\n"
31953a5a1b3Sopenharmony_ci            "\n", code, msg, mime);
32053a5a1b3Sopenharmony_ci    pa_ioline_puts(c->line, s);
32153a5a1b3Sopenharmony_ci    pa_xfree(s);
32253a5a1b3Sopenharmony_ci}
32353a5a1b3Sopenharmony_ci
32453a5a1b3Sopenharmony_cistatic void html_response(
32553a5a1b3Sopenharmony_ci        struct connection *c,
32653a5a1b3Sopenharmony_ci        int code,
32753a5a1b3Sopenharmony_ci        const char *msg,
32853a5a1b3Sopenharmony_ci        const char *text) {
32953a5a1b3Sopenharmony_ci
33053a5a1b3Sopenharmony_ci    char *s;
33153a5a1b3Sopenharmony_ci    pa_assert(c);
33253a5a1b3Sopenharmony_ci
33353a5a1b3Sopenharmony_ci    http_response(c, code, msg, MIME_HTML);
33453a5a1b3Sopenharmony_ci
33553a5a1b3Sopenharmony_ci    if (c->method == METHOD_HEAD) {
33653a5a1b3Sopenharmony_ci        pa_ioline_defer_close(c->line);
33753a5a1b3Sopenharmony_ci        return;
33853a5a1b3Sopenharmony_ci    }
33953a5a1b3Sopenharmony_ci
34053a5a1b3Sopenharmony_ci    if (!text)
34153a5a1b3Sopenharmony_ci        text = msg;
34253a5a1b3Sopenharmony_ci
34353a5a1b3Sopenharmony_ci    s = pa_sprintf_malloc(
34453a5a1b3Sopenharmony_ci            HTML_HEADER("%s")
34553a5a1b3Sopenharmony_ci            "%s"
34653a5a1b3Sopenharmony_ci            HTML_FOOTER,
34753a5a1b3Sopenharmony_ci            text, text);
34853a5a1b3Sopenharmony_ci
34953a5a1b3Sopenharmony_ci    pa_ioline_puts(c->line, s);
35053a5a1b3Sopenharmony_ci    pa_xfree(s);
35153a5a1b3Sopenharmony_ci
35253a5a1b3Sopenharmony_ci    pa_ioline_defer_close(c->line);
35353a5a1b3Sopenharmony_ci}
35453a5a1b3Sopenharmony_ci
35553a5a1b3Sopenharmony_cistatic void html_print_field(pa_ioline *line, const char *left, const char *right) {
35653a5a1b3Sopenharmony_ci    char *eleft, *eright;
35753a5a1b3Sopenharmony_ci
35853a5a1b3Sopenharmony_ci    eleft = escape_html(left);
35953a5a1b3Sopenharmony_ci    eright = escape_html(right);
36053a5a1b3Sopenharmony_ci
36153a5a1b3Sopenharmony_ci    pa_ioline_printf(line,
36253a5a1b3Sopenharmony_ci                     "<tr><td><b>%s</b></td>"
36353a5a1b3Sopenharmony_ci                     "<td>%s</td></tr>\n", eleft, eright);
36453a5a1b3Sopenharmony_ci
36553a5a1b3Sopenharmony_ci    pa_xfree(eleft);
36653a5a1b3Sopenharmony_ci    pa_xfree(eright);
36753a5a1b3Sopenharmony_ci}
36853a5a1b3Sopenharmony_ci
36953a5a1b3Sopenharmony_cistatic void handle_root(struct connection *c) {
37053a5a1b3Sopenharmony_ci    char *t;
37153a5a1b3Sopenharmony_ci
37253a5a1b3Sopenharmony_ci    pa_assert(c);
37353a5a1b3Sopenharmony_ci
37453a5a1b3Sopenharmony_ci    http_response(c, 200, "OK", MIME_HTML);
37553a5a1b3Sopenharmony_ci
37653a5a1b3Sopenharmony_ci    if (c->method == METHOD_HEAD) {
37753a5a1b3Sopenharmony_ci        pa_ioline_defer_close(c->line);
37853a5a1b3Sopenharmony_ci        return;
37953a5a1b3Sopenharmony_ci    }
38053a5a1b3Sopenharmony_ci
38153a5a1b3Sopenharmony_ci    pa_ioline_puts(c->line,
38253a5a1b3Sopenharmony_ci                   HTML_HEADER(PACKAGE_NAME" "PACKAGE_VERSION)
38353a5a1b3Sopenharmony_ci                   "<h1>"PACKAGE_NAME" "PACKAGE_VERSION"</h1>\n"
38453a5a1b3Sopenharmony_ci                   "<table>\n");
38553a5a1b3Sopenharmony_ci
38653a5a1b3Sopenharmony_ci    t = pa_get_user_name_malloc();
38753a5a1b3Sopenharmony_ci    html_print_field(c->line, "User Name:", t);
38853a5a1b3Sopenharmony_ci    pa_xfree(t);
38953a5a1b3Sopenharmony_ci
39053a5a1b3Sopenharmony_ci    t = pa_get_host_name_malloc();
39153a5a1b3Sopenharmony_ci    html_print_field(c->line, "Host name:", t);
39253a5a1b3Sopenharmony_ci    pa_xfree(t);
39353a5a1b3Sopenharmony_ci
39453a5a1b3Sopenharmony_ci    t = pa_machine_id();
39553a5a1b3Sopenharmony_ci    html_print_field(c->line, "Machine ID:", t);
39653a5a1b3Sopenharmony_ci    pa_xfree(t);
39753a5a1b3Sopenharmony_ci
39853a5a1b3Sopenharmony_ci    t = pa_uname_string();
39953a5a1b3Sopenharmony_ci    html_print_field(c->line, "System:", t);
40053a5a1b3Sopenharmony_ci    pa_xfree(t);
40153a5a1b3Sopenharmony_ci
40253a5a1b3Sopenharmony_ci    t = pa_sprintf_malloc("%lu", (unsigned long) getpid());
40353a5a1b3Sopenharmony_ci    html_print_field(c->line, "Process ID:", t);
40453a5a1b3Sopenharmony_ci    pa_xfree(t);
40553a5a1b3Sopenharmony_ci
40653a5a1b3Sopenharmony_ci    pa_ioline_puts(c->line,
40753a5a1b3Sopenharmony_ci                   "</table>\n"
40853a5a1b3Sopenharmony_ci                   "<p><a href=\"" URL_STATUS "\">Show an extensive server status report</a></p>\n"
40953a5a1b3Sopenharmony_ci                   "<p><a href=\"" URL_LISTEN "\">Monitor sinks and sources</a></p>\n"
41053a5a1b3Sopenharmony_ci                   HTML_FOOTER);
41153a5a1b3Sopenharmony_ci
41253a5a1b3Sopenharmony_ci    pa_ioline_defer_close(c->line);
41353a5a1b3Sopenharmony_ci}
41453a5a1b3Sopenharmony_ci
41553a5a1b3Sopenharmony_cistatic void handle_css(struct connection *c) {
41653a5a1b3Sopenharmony_ci    pa_assert(c);
41753a5a1b3Sopenharmony_ci
41853a5a1b3Sopenharmony_ci    http_response(c, 200, "OK", MIME_CSS);
41953a5a1b3Sopenharmony_ci
42053a5a1b3Sopenharmony_ci    if (c->method == METHOD_HEAD) {
42153a5a1b3Sopenharmony_ci        pa_ioline_defer_close(c->line);
42253a5a1b3Sopenharmony_ci        return;
42353a5a1b3Sopenharmony_ci    }
42453a5a1b3Sopenharmony_ci
42553a5a1b3Sopenharmony_ci    pa_ioline_puts(c->line,
42653a5a1b3Sopenharmony_ci                   "body { color: black; background-color: white; }\n"
42753a5a1b3Sopenharmony_ci                   "a:link, a:visited { color: #900000; }\n"
42853a5a1b3Sopenharmony_ci                   "div.news-date { font-size: 80%; font-style: italic; }\n"
42953a5a1b3Sopenharmony_ci                   "pre { background-color: #f0f0f0; padding: 0.4cm; }\n"
43053a5a1b3Sopenharmony_ci                   ".grey { color: #8f8f8f; font-size: 80%; }"
43153a5a1b3Sopenharmony_ci                   "table {  margin-left: 1cm; border:1px solid lightgrey; padding: 0.2cm; }\n"
43253a5a1b3Sopenharmony_ci                   "td { padding-left:10px; padding-right:10px; }\n");
43353a5a1b3Sopenharmony_ci
43453a5a1b3Sopenharmony_ci    pa_ioline_defer_close(c->line);
43553a5a1b3Sopenharmony_ci}
43653a5a1b3Sopenharmony_ci
43753a5a1b3Sopenharmony_cistatic void handle_status(struct connection *c) {
43853a5a1b3Sopenharmony_ci    char *r;
43953a5a1b3Sopenharmony_ci
44053a5a1b3Sopenharmony_ci    pa_assert(c);
44153a5a1b3Sopenharmony_ci
44253a5a1b3Sopenharmony_ci    http_response(c, 200, "OK", MIME_TEXT);
44353a5a1b3Sopenharmony_ci
44453a5a1b3Sopenharmony_ci    if (c->method == METHOD_HEAD) {
44553a5a1b3Sopenharmony_ci        pa_ioline_defer_close(c->line);
44653a5a1b3Sopenharmony_ci        return;
44753a5a1b3Sopenharmony_ci    }
44853a5a1b3Sopenharmony_ci
44953a5a1b3Sopenharmony_ci    r = pa_full_status_string(c->protocol->core);
45053a5a1b3Sopenharmony_ci    pa_ioline_puts(c->line, r);
45153a5a1b3Sopenharmony_ci    pa_xfree(r);
45253a5a1b3Sopenharmony_ci
45353a5a1b3Sopenharmony_ci    pa_ioline_defer_close(c->line);
45453a5a1b3Sopenharmony_ci}
45553a5a1b3Sopenharmony_ci
45653a5a1b3Sopenharmony_cistatic void handle_listen(struct connection *c) {
45753a5a1b3Sopenharmony_ci    pa_source *source;
45853a5a1b3Sopenharmony_ci    pa_sink *sink;
45953a5a1b3Sopenharmony_ci    uint32_t idx;
46053a5a1b3Sopenharmony_ci
46153a5a1b3Sopenharmony_ci    http_response(c, 200, "OK", MIME_HTML);
46253a5a1b3Sopenharmony_ci
46353a5a1b3Sopenharmony_ci    pa_ioline_puts(c->line,
46453a5a1b3Sopenharmony_ci                   HTML_HEADER("Listen")
46553a5a1b3Sopenharmony_ci                   "<h2>Sinks</h2>\n"
46653a5a1b3Sopenharmony_ci                   "<p>\n");
46753a5a1b3Sopenharmony_ci
46853a5a1b3Sopenharmony_ci    if (c->method == METHOD_HEAD) {
46953a5a1b3Sopenharmony_ci        pa_ioline_defer_close(c->line);
47053a5a1b3Sopenharmony_ci        return;
47153a5a1b3Sopenharmony_ci    }
47253a5a1b3Sopenharmony_ci
47353a5a1b3Sopenharmony_ci    PA_IDXSET_FOREACH(sink, c->protocol->core->sinks, idx) {
47453a5a1b3Sopenharmony_ci        char *t, *m;
47553a5a1b3Sopenharmony_ci
47653a5a1b3Sopenharmony_ci        t = escape_html(pa_strna(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)));
47753a5a1b3Sopenharmony_ci        m = pa_sample_spec_to_mime_type_mimefy(&sink->sample_spec, &sink->channel_map);
47853a5a1b3Sopenharmony_ci
47953a5a1b3Sopenharmony_ci        pa_ioline_printf(c->line,
48053a5a1b3Sopenharmony_ci                         "<a href=\"" URL_LISTEN_SOURCE "%s\" title=\"%s\">%s</a><br/>\n",
48153a5a1b3Sopenharmony_ci                         sink->monitor_source->name, m, t);
48253a5a1b3Sopenharmony_ci
48353a5a1b3Sopenharmony_ci        pa_xfree(t);
48453a5a1b3Sopenharmony_ci        pa_xfree(m);
48553a5a1b3Sopenharmony_ci    }
48653a5a1b3Sopenharmony_ci
48753a5a1b3Sopenharmony_ci    pa_ioline_puts(c->line,
48853a5a1b3Sopenharmony_ci                   "</p>\n"
48953a5a1b3Sopenharmony_ci                   "<h2>Sources</h2>\n"
49053a5a1b3Sopenharmony_ci                   "<p>\n");
49153a5a1b3Sopenharmony_ci
49253a5a1b3Sopenharmony_ci    PA_IDXSET_FOREACH(source, c->protocol->core->sources, idx) {
49353a5a1b3Sopenharmony_ci        char *t, *m;
49453a5a1b3Sopenharmony_ci
49553a5a1b3Sopenharmony_ci        if (source->monitor_of)
49653a5a1b3Sopenharmony_ci            continue;
49753a5a1b3Sopenharmony_ci
49853a5a1b3Sopenharmony_ci        t = escape_html(pa_strna(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)));
49953a5a1b3Sopenharmony_ci        m = pa_sample_spec_to_mime_type_mimefy(&source->sample_spec, &source->channel_map);
50053a5a1b3Sopenharmony_ci
50153a5a1b3Sopenharmony_ci        pa_ioline_printf(c->line,
50253a5a1b3Sopenharmony_ci                         "<a href=\"" URL_LISTEN_SOURCE "%s\" title=\"%s\">%s</a><br/>\n",
50353a5a1b3Sopenharmony_ci                         source->name, m, t);
50453a5a1b3Sopenharmony_ci
50553a5a1b3Sopenharmony_ci        pa_xfree(m);
50653a5a1b3Sopenharmony_ci        pa_xfree(t);
50753a5a1b3Sopenharmony_ci
50853a5a1b3Sopenharmony_ci    }
50953a5a1b3Sopenharmony_ci
51053a5a1b3Sopenharmony_ci    pa_ioline_puts(c->line,
51153a5a1b3Sopenharmony_ci                   "</p>\n"
51253a5a1b3Sopenharmony_ci                   HTML_FOOTER);
51353a5a1b3Sopenharmony_ci
51453a5a1b3Sopenharmony_ci    pa_ioline_defer_close(c->line);
51553a5a1b3Sopenharmony_ci}
51653a5a1b3Sopenharmony_ci
51753a5a1b3Sopenharmony_cistatic void line_drain_callback(pa_ioline *l, void *userdata) {
51853a5a1b3Sopenharmony_ci    struct connection *c;
51953a5a1b3Sopenharmony_ci
52053a5a1b3Sopenharmony_ci    pa_assert(l);
52153a5a1b3Sopenharmony_ci    pa_assert_se(c = userdata);
52253a5a1b3Sopenharmony_ci
52353a5a1b3Sopenharmony_ci    /* We don't need the line reader anymore, instead we need a real
52453a5a1b3Sopenharmony_ci     * binary io channel */
52553a5a1b3Sopenharmony_ci    pa_assert_se(c->io = pa_ioline_detach_iochannel(c->line));
52653a5a1b3Sopenharmony_ci    pa_iochannel_set_callback(c->io, io_callback, c);
52753a5a1b3Sopenharmony_ci
52853a5a1b3Sopenharmony_ci    pa_iochannel_socket_set_sndbuf(c->io, pa_memblockq_get_length(c->output_memblockq));
52953a5a1b3Sopenharmony_ci
53053a5a1b3Sopenharmony_ci    pa_ioline_unref(c->line);
53153a5a1b3Sopenharmony_ci    c->line = NULL;
53253a5a1b3Sopenharmony_ci}
53353a5a1b3Sopenharmony_ci
53453a5a1b3Sopenharmony_cistatic void handle_listen_prefix(struct connection *c, const char *source_name) {
53553a5a1b3Sopenharmony_ci    pa_source *source;
53653a5a1b3Sopenharmony_ci    pa_source_output_new_data data;
53753a5a1b3Sopenharmony_ci    pa_sample_spec ss;
53853a5a1b3Sopenharmony_ci    pa_channel_map cm;
53953a5a1b3Sopenharmony_ci    char *t;
54053a5a1b3Sopenharmony_ci    size_t l;
54153a5a1b3Sopenharmony_ci
54253a5a1b3Sopenharmony_ci    pa_assert(c);
54353a5a1b3Sopenharmony_ci    pa_assert(source_name);
54453a5a1b3Sopenharmony_ci
54553a5a1b3Sopenharmony_ci    pa_assert(c->line);
54653a5a1b3Sopenharmony_ci    pa_assert(!c->io);
54753a5a1b3Sopenharmony_ci
54853a5a1b3Sopenharmony_ci    if (!(source = pa_namereg_get(c->protocol->core, source_name, PA_NAMEREG_SOURCE))) {
54953a5a1b3Sopenharmony_ci        html_response(c, 404, "Source not found", NULL);
55053a5a1b3Sopenharmony_ci        return;
55153a5a1b3Sopenharmony_ci    }
55253a5a1b3Sopenharmony_ci
55353a5a1b3Sopenharmony_ci    ss = source->sample_spec;
55453a5a1b3Sopenharmony_ci    cm = source->channel_map;
55553a5a1b3Sopenharmony_ci
55653a5a1b3Sopenharmony_ci    pa_sample_spec_mimefy(&ss, &cm);
55753a5a1b3Sopenharmony_ci
55853a5a1b3Sopenharmony_ci    pa_source_output_new_data_init(&data);
55953a5a1b3Sopenharmony_ci    data.driver = __FILE__;
56053a5a1b3Sopenharmony_ci    data.module = c->module;
56153a5a1b3Sopenharmony_ci    data.client = c->client;
56253a5a1b3Sopenharmony_ci    pa_source_output_new_data_set_source(&data, source, false, true);
56353a5a1b3Sopenharmony_ci    pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
56453a5a1b3Sopenharmony_ci    pa_source_output_new_data_set_sample_spec(&data, &ss);
56553a5a1b3Sopenharmony_ci    pa_source_output_new_data_set_channel_map(&data, &cm);
56653a5a1b3Sopenharmony_ci
56753a5a1b3Sopenharmony_ci    pa_source_output_new(&c->source_output, c->protocol->core, &data);
56853a5a1b3Sopenharmony_ci    pa_source_output_new_data_done(&data);
56953a5a1b3Sopenharmony_ci
57053a5a1b3Sopenharmony_ci    if (!c->source_output) {
57153a5a1b3Sopenharmony_ci        html_response(c, 403, "Cannot create source output", NULL);
57253a5a1b3Sopenharmony_ci        return;
57353a5a1b3Sopenharmony_ci    }
57453a5a1b3Sopenharmony_ci
57553a5a1b3Sopenharmony_ci    c->source_output->parent.process_msg = source_output_process_msg;
57653a5a1b3Sopenharmony_ci    c->source_output->push = source_output_push_cb;
57753a5a1b3Sopenharmony_ci    c->source_output->kill = source_output_kill_cb;
57853a5a1b3Sopenharmony_ci    c->source_output->get_latency = source_output_get_latency_cb;
57953a5a1b3Sopenharmony_ci    c->source_output->userdata = c;
58053a5a1b3Sopenharmony_ci
58153a5a1b3Sopenharmony_ci    pa_source_output_set_requested_latency(c->source_output, DEFAULT_SOURCE_LATENCY);
58253a5a1b3Sopenharmony_ci
58353a5a1b3Sopenharmony_ci    l = (size_t) (pa_bytes_per_second(&ss)*RECORD_BUFFER_SECONDS);
58453a5a1b3Sopenharmony_ci    c->output_memblockq = pa_memblockq_new(
58553a5a1b3Sopenharmony_ci            "http protocol connection output_memblockq",
58653a5a1b3Sopenharmony_ci            0,
58753a5a1b3Sopenharmony_ci            l,
58853a5a1b3Sopenharmony_ci            0,
58953a5a1b3Sopenharmony_ci            &ss,
59053a5a1b3Sopenharmony_ci            1,
59153a5a1b3Sopenharmony_ci            0,
59253a5a1b3Sopenharmony_ci            0,
59353a5a1b3Sopenharmony_ci            NULL);
59453a5a1b3Sopenharmony_ci
59553a5a1b3Sopenharmony_ci    pa_source_output_put(c->source_output);
59653a5a1b3Sopenharmony_ci
59753a5a1b3Sopenharmony_ci    t = pa_sample_spec_to_mime_type(&ss, &cm);
59853a5a1b3Sopenharmony_ci    http_response(c, 200, "OK", t);
59953a5a1b3Sopenharmony_ci    pa_xfree(t);
60053a5a1b3Sopenharmony_ci
60153a5a1b3Sopenharmony_ci    if (c->method == METHOD_HEAD) {
60253a5a1b3Sopenharmony_ci        connection_unlink(c);
60353a5a1b3Sopenharmony_ci        return;
60453a5a1b3Sopenharmony_ci    }
60553a5a1b3Sopenharmony_ci    pa_ioline_set_callback(c->line, NULL, NULL);
60653a5a1b3Sopenharmony_ci
60753a5a1b3Sopenharmony_ci    if (pa_ioline_is_drained(c->line))
60853a5a1b3Sopenharmony_ci        line_drain_callback(c->line, c);
60953a5a1b3Sopenharmony_ci    else
61053a5a1b3Sopenharmony_ci        pa_ioline_set_drain_callback(c->line, line_drain_callback, c);
61153a5a1b3Sopenharmony_ci}
61253a5a1b3Sopenharmony_ci
61353a5a1b3Sopenharmony_cistatic void handle_url(struct connection *c) {
61453a5a1b3Sopenharmony_ci    pa_assert(c);
61553a5a1b3Sopenharmony_ci
61653a5a1b3Sopenharmony_ci    pa_log_debug("Request for %s", c->url);
61753a5a1b3Sopenharmony_ci
61853a5a1b3Sopenharmony_ci    if (pa_streq(c->url, URL_ROOT))
61953a5a1b3Sopenharmony_ci        handle_root(c);
62053a5a1b3Sopenharmony_ci    else if (pa_streq(c->url, URL_CSS))
62153a5a1b3Sopenharmony_ci        handle_css(c);
62253a5a1b3Sopenharmony_ci    else if (pa_streq(c->url, URL_STATUS))
62353a5a1b3Sopenharmony_ci        handle_status(c);
62453a5a1b3Sopenharmony_ci    else if (pa_streq(c->url, URL_LISTEN))
62553a5a1b3Sopenharmony_ci        handle_listen(c);
62653a5a1b3Sopenharmony_ci    else if (pa_startswith(c->url, URL_LISTEN_SOURCE))
62753a5a1b3Sopenharmony_ci        handle_listen_prefix(c, c->url + sizeof(URL_LISTEN_SOURCE)-1);
62853a5a1b3Sopenharmony_ci    else
62953a5a1b3Sopenharmony_ci        html_response(c, 404, "Not Found", NULL);
63053a5a1b3Sopenharmony_ci}
63153a5a1b3Sopenharmony_ci
63253a5a1b3Sopenharmony_cistatic void line_callback(pa_ioline *line, const char *s, void *userdata) {
63353a5a1b3Sopenharmony_ci    struct connection *c = userdata;
63453a5a1b3Sopenharmony_ci    pa_assert(line);
63553a5a1b3Sopenharmony_ci    pa_assert(c);
63653a5a1b3Sopenharmony_ci
63753a5a1b3Sopenharmony_ci    if (!s) {
63853a5a1b3Sopenharmony_ci        /* EOF */
63953a5a1b3Sopenharmony_ci        connection_unlink(c);
64053a5a1b3Sopenharmony_ci        return;
64153a5a1b3Sopenharmony_ci    }
64253a5a1b3Sopenharmony_ci
64353a5a1b3Sopenharmony_ci    switch (c->state) {
64453a5a1b3Sopenharmony_ci        case STATE_REQUEST_LINE: {
64553a5a1b3Sopenharmony_ci            if (pa_startswith(s, "GET ")) {
64653a5a1b3Sopenharmony_ci                c->method = METHOD_GET;
64753a5a1b3Sopenharmony_ci                s +=4;
64853a5a1b3Sopenharmony_ci            } else if (pa_startswith(s, "HEAD ")) {
64953a5a1b3Sopenharmony_ci                c->method = METHOD_HEAD;
65053a5a1b3Sopenharmony_ci                s +=5;
65153a5a1b3Sopenharmony_ci            } else {
65253a5a1b3Sopenharmony_ci                goto fail;
65353a5a1b3Sopenharmony_ci            }
65453a5a1b3Sopenharmony_ci
65553a5a1b3Sopenharmony_ci            c->url = pa_xstrndup(s, strcspn(s, " \r\n\t?"));
65653a5a1b3Sopenharmony_ci            c->state = STATE_MIME_HEADER;
65753a5a1b3Sopenharmony_ci            break;
65853a5a1b3Sopenharmony_ci        }
65953a5a1b3Sopenharmony_ci
66053a5a1b3Sopenharmony_ci        case STATE_MIME_HEADER: {
66153a5a1b3Sopenharmony_ci
66253a5a1b3Sopenharmony_ci            /* Ignore MIME headers */
66353a5a1b3Sopenharmony_ci            if (strcspn(s, " \r\n") != 0)
66453a5a1b3Sopenharmony_ci                break;
66553a5a1b3Sopenharmony_ci
66653a5a1b3Sopenharmony_ci            /* We're done */
66753a5a1b3Sopenharmony_ci            c->state = STATE_DATA;
66853a5a1b3Sopenharmony_ci
66953a5a1b3Sopenharmony_ci            handle_url(c);
67053a5a1b3Sopenharmony_ci            break;
67153a5a1b3Sopenharmony_ci        }
67253a5a1b3Sopenharmony_ci
67353a5a1b3Sopenharmony_ci        default:
67453a5a1b3Sopenharmony_ci            ;
67553a5a1b3Sopenharmony_ci    }
67653a5a1b3Sopenharmony_ci
67753a5a1b3Sopenharmony_ci    return;
67853a5a1b3Sopenharmony_ci
67953a5a1b3Sopenharmony_cifail:
68053a5a1b3Sopenharmony_ci    html_response(c, 500, "Internal Server Error", NULL);
68153a5a1b3Sopenharmony_ci}
68253a5a1b3Sopenharmony_ci
68353a5a1b3Sopenharmony_civoid pa_http_protocol_connect(pa_http_protocol *p, pa_iochannel *io, pa_module *m) {
68453a5a1b3Sopenharmony_ci    struct connection *c;
68553a5a1b3Sopenharmony_ci    pa_client_new_data client_data;
68653a5a1b3Sopenharmony_ci    char pname[128];
68753a5a1b3Sopenharmony_ci
68853a5a1b3Sopenharmony_ci    pa_assert(p);
68953a5a1b3Sopenharmony_ci    pa_assert(io);
69053a5a1b3Sopenharmony_ci    pa_assert(m);
69153a5a1b3Sopenharmony_ci
69253a5a1b3Sopenharmony_ci    if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
69353a5a1b3Sopenharmony_ci        pa_log("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
69453a5a1b3Sopenharmony_ci        pa_iochannel_free(io);
69553a5a1b3Sopenharmony_ci        return;
69653a5a1b3Sopenharmony_ci    }
69753a5a1b3Sopenharmony_ci
69853a5a1b3Sopenharmony_ci    c = pa_xnew0(struct connection, 1);
69953a5a1b3Sopenharmony_ci    c->protocol = p;
70053a5a1b3Sopenharmony_ci    c->state = STATE_REQUEST_LINE;
70153a5a1b3Sopenharmony_ci    c->module = m;
70253a5a1b3Sopenharmony_ci
70353a5a1b3Sopenharmony_ci    c->line = pa_ioline_new(io);
70453a5a1b3Sopenharmony_ci    pa_ioline_set_callback(c->line, line_callback, c);
70553a5a1b3Sopenharmony_ci
70653a5a1b3Sopenharmony_ci    pa_client_new_data_init(&client_data);
70753a5a1b3Sopenharmony_ci    client_data.module = c->module;
70853a5a1b3Sopenharmony_ci    client_data.driver = __FILE__;
70953a5a1b3Sopenharmony_ci    pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
71053a5a1b3Sopenharmony_ci    pa_proplist_setf(client_data.proplist, PA_PROP_APPLICATION_NAME, "HTTP client (%s)", pname);
71153a5a1b3Sopenharmony_ci    pa_proplist_sets(client_data.proplist, "http-protocol.peer", pname);
71253a5a1b3Sopenharmony_ci    c->client = pa_client_new(p->core, &client_data);
71353a5a1b3Sopenharmony_ci    pa_client_new_data_done(&client_data);
71453a5a1b3Sopenharmony_ci
71553a5a1b3Sopenharmony_ci    if (!c->client)
71653a5a1b3Sopenharmony_ci        goto fail;
71753a5a1b3Sopenharmony_ci
71853a5a1b3Sopenharmony_ci    c->client->kill = client_kill_cb;
71953a5a1b3Sopenharmony_ci    c->client->userdata = c;
72053a5a1b3Sopenharmony_ci
72153a5a1b3Sopenharmony_ci    pa_idxset_put(p->connections, c, NULL);
72253a5a1b3Sopenharmony_ci
72353a5a1b3Sopenharmony_ci    return;
72453a5a1b3Sopenharmony_ci
72553a5a1b3Sopenharmony_cifail:
72653a5a1b3Sopenharmony_ci    if (c)
72753a5a1b3Sopenharmony_ci        connection_unlink(c);
72853a5a1b3Sopenharmony_ci}
72953a5a1b3Sopenharmony_ci
73053a5a1b3Sopenharmony_civoid pa_http_protocol_disconnect(pa_http_protocol *p, pa_module *m) {
73153a5a1b3Sopenharmony_ci    struct connection *c;
73253a5a1b3Sopenharmony_ci    uint32_t idx;
73353a5a1b3Sopenharmony_ci
73453a5a1b3Sopenharmony_ci    pa_assert(p);
73553a5a1b3Sopenharmony_ci    pa_assert(m);
73653a5a1b3Sopenharmony_ci
73753a5a1b3Sopenharmony_ci    PA_IDXSET_FOREACH(c, p->connections, idx)
73853a5a1b3Sopenharmony_ci        if (c->module == m)
73953a5a1b3Sopenharmony_ci            connection_unlink(c);
74053a5a1b3Sopenharmony_ci}
74153a5a1b3Sopenharmony_ci
74253a5a1b3Sopenharmony_cistatic pa_http_protocol* http_protocol_new(pa_core *c) {
74353a5a1b3Sopenharmony_ci    pa_http_protocol *p;
74453a5a1b3Sopenharmony_ci
74553a5a1b3Sopenharmony_ci    pa_assert(c);
74653a5a1b3Sopenharmony_ci
74753a5a1b3Sopenharmony_ci    p = pa_xnew0(pa_http_protocol, 1);
74853a5a1b3Sopenharmony_ci    PA_REFCNT_INIT(p);
74953a5a1b3Sopenharmony_ci    p->core = c;
75053a5a1b3Sopenharmony_ci    p->connections = pa_idxset_new(NULL, NULL);
75153a5a1b3Sopenharmony_ci
75253a5a1b3Sopenharmony_ci    pa_assert_se(pa_shared_set(c, "http-protocol", p) >= 0);
75353a5a1b3Sopenharmony_ci
75453a5a1b3Sopenharmony_ci    return p;
75553a5a1b3Sopenharmony_ci}
75653a5a1b3Sopenharmony_ci
75753a5a1b3Sopenharmony_cipa_http_protocol* pa_http_protocol_get(pa_core *c) {
75853a5a1b3Sopenharmony_ci    pa_http_protocol *p;
75953a5a1b3Sopenharmony_ci
76053a5a1b3Sopenharmony_ci    if ((p = pa_shared_get(c, "http-protocol")))
76153a5a1b3Sopenharmony_ci        return pa_http_protocol_ref(p);
76253a5a1b3Sopenharmony_ci
76353a5a1b3Sopenharmony_ci    return http_protocol_new(c);
76453a5a1b3Sopenharmony_ci}
76553a5a1b3Sopenharmony_ci
76653a5a1b3Sopenharmony_cipa_http_protocol* pa_http_protocol_ref(pa_http_protocol *p) {
76753a5a1b3Sopenharmony_ci    pa_assert(p);
76853a5a1b3Sopenharmony_ci    pa_assert(PA_REFCNT_VALUE(p) >= 1);
76953a5a1b3Sopenharmony_ci
77053a5a1b3Sopenharmony_ci    PA_REFCNT_INC(p);
77153a5a1b3Sopenharmony_ci
77253a5a1b3Sopenharmony_ci    return p;
77353a5a1b3Sopenharmony_ci}
77453a5a1b3Sopenharmony_ci
77553a5a1b3Sopenharmony_civoid pa_http_protocol_unref(pa_http_protocol *p) {
77653a5a1b3Sopenharmony_ci    struct connection *c;
77753a5a1b3Sopenharmony_ci
77853a5a1b3Sopenharmony_ci    pa_assert(p);
77953a5a1b3Sopenharmony_ci    pa_assert(PA_REFCNT_VALUE(p) >= 1);
78053a5a1b3Sopenharmony_ci
78153a5a1b3Sopenharmony_ci    if (PA_REFCNT_DEC(p) > 0)
78253a5a1b3Sopenharmony_ci        return;
78353a5a1b3Sopenharmony_ci
78453a5a1b3Sopenharmony_ci    while ((c = pa_idxset_first(p->connections, NULL)))
78553a5a1b3Sopenharmony_ci        connection_unlink(c);
78653a5a1b3Sopenharmony_ci
78753a5a1b3Sopenharmony_ci    pa_idxset_free(p->connections, NULL);
78853a5a1b3Sopenharmony_ci
78953a5a1b3Sopenharmony_ci    pa_strlist_free(p->servers);
79053a5a1b3Sopenharmony_ci
79153a5a1b3Sopenharmony_ci    pa_assert_se(pa_shared_remove(p->core, "http-protocol") >= 0);
79253a5a1b3Sopenharmony_ci
79353a5a1b3Sopenharmony_ci    pa_xfree(p);
79453a5a1b3Sopenharmony_ci}
79553a5a1b3Sopenharmony_ci
79653a5a1b3Sopenharmony_civoid pa_http_protocol_add_server_string(pa_http_protocol *p, const char *name) {
79753a5a1b3Sopenharmony_ci    pa_assert(p);
79853a5a1b3Sopenharmony_ci    pa_assert(PA_REFCNT_VALUE(p) >= 1);
79953a5a1b3Sopenharmony_ci    pa_assert(name);
80053a5a1b3Sopenharmony_ci
80153a5a1b3Sopenharmony_ci    p->servers = pa_strlist_prepend(p->servers, name);
80253a5a1b3Sopenharmony_ci}
80353a5a1b3Sopenharmony_ci
80453a5a1b3Sopenharmony_civoid pa_http_protocol_remove_server_string(pa_http_protocol *p, const char *name) {
80553a5a1b3Sopenharmony_ci    pa_assert(p);
80653a5a1b3Sopenharmony_ci    pa_assert(PA_REFCNT_VALUE(p) >= 1);
80753a5a1b3Sopenharmony_ci    pa_assert(name);
80853a5a1b3Sopenharmony_ci
80953a5a1b3Sopenharmony_ci    p->servers = pa_strlist_remove(p->servers, name);
81053a5a1b3Sopenharmony_ci}
81153a5a1b3Sopenharmony_ci
81253a5a1b3Sopenharmony_cipa_strlist *pa_http_protocol_servers(pa_http_protocol *p) {
81353a5a1b3Sopenharmony_ci    pa_assert(p);
81453a5a1b3Sopenharmony_ci    pa_assert(PA_REFCNT_VALUE(p) >= 1);
81553a5a1b3Sopenharmony_ci
81653a5a1b3Sopenharmony_ci    return p->servers;
81753a5a1b3Sopenharmony_ci}
818