]> code.delx.au - pulseaudio/blob - src/pulsecore/protocol-http.c
Merge commit '12db687acf3befe485bfff3700111999c95247fa'
[pulseaudio] / src / pulsecore / protocol-http.c
1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2005-2006 Lennart Poettering
5
6 PulseAudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2 of the License,
9 or (at your option) any later version.
10
11 PulseAudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with PulseAudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19 USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29
30 #include <pulse/util.h>
31 #include <pulse/xmalloc.h>
32
33 #include <pulsecore/ioline.h>
34 #include <pulsecore/macro.h>
35 #include <pulsecore/log.h>
36 #include <pulsecore/namereg.h>
37 #include <pulsecore/cli-text.h>
38 #include <pulsecore/shared.h>
39
40 #include "protocol-http.h"
41
42 /* Don't allow more than this many concurrent connections */
43 #define MAX_CONNECTIONS 10
44
45 #define internal_server_error(c) http_message((c), 500, "Internal Server Error", NULL)
46
47 #define URL_ROOT "/"
48 #define URL_CSS "/style"
49 #define URL_STATUS "/status"
50
51 struct connection {
52 pa_http_protocol *protocol;
53 pa_ioline *line;
54 enum {
55 REQUEST_LINE,
56 MIME_HEADER,
57 DATA
58 } state;
59 char *url;
60 pa_module *module;
61 };
62
63 struct pa_http_protocol {
64 PA_REFCNT_DECLARE;
65
66 pa_core *core;
67 pa_idxset *connections;
68 };
69
70 static void http_response(struct connection *c, int code, const char *msg, const char *mime) {
71 char s[256];
72
73 pa_assert(c);
74 pa_assert(msg);
75 pa_assert(mime);
76
77 pa_snprintf(s, sizeof(s),
78 "HTTP/1.0 %i %s\n"
79 "Connection: close\n"
80 "Content-Type: %s\n"
81 "Cache-Control: no-cache\n"
82 "Expires: 0\n"
83 "Server: "PACKAGE_NAME"/"PACKAGE_VERSION"\n"
84 "\n", code, msg, mime);
85
86 pa_ioline_puts(c->line, s);
87 }
88
89 static void http_message(struct connection *c, int code, const char *msg, const char *text) {
90 char s[256];
91 pa_assert(c);
92
93 http_response(c, code, msg, "text/html");
94
95 if (!text)
96 text = msg;
97
98 pa_snprintf(s, sizeof(s),
99 "<?xml version=\"1.0\"?>\n"
100 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
101 "<html xmlns=\"http://www.w3.org/1999/xhtml\"><head><title>%s</title></head>\n"
102 "<body>%s</body></html>\n",
103 text, text);
104
105 pa_ioline_puts(c->line, s);
106 pa_ioline_defer_close(c->line);
107 }
108
109
110 static void connection_unlink(struct connection *c) {
111 pa_assert(c);
112
113 if (c->url)
114 pa_xfree(c->url);
115
116 pa_idxset_remove_by_data(c->protocol->connections, c, NULL);
117
118 pa_ioline_unref(c->line);
119 pa_xfree(c);
120 }
121
122 static void line_callback(pa_ioline *line, const char *s, void *userdata) {
123 struct connection *c = userdata;
124 pa_assert(line);
125 pa_assert(c);
126
127 if (!s) {
128 /* EOF */
129 connection_unlink(c);
130 return;
131 }
132
133 switch (c->state) {
134 case REQUEST_LINE: {
135 if (memcmp(s, "GET ", 4))
136 goto fail;
137
138 s +=4;
139
140 c->url = pa_xstrndup(s, strcspn(s, " \r\n\t?"));
141 c->state = MIME_HEADER;
142 break;
143
144 }
145
146 case MIME_HEADER: {
147
148 /* Ignore MIME headers */
149 if (strcspn(s, " \r\n") != 0)
150 break;
151
152 /* We're done */
153 c->state = DATA;
154
155 pa_log_info("request for %s", c->url);
156
157 if (!strcmp(c->url, URL_ROOT)) {
158 char txt[256];
159 pa_sink *def_sink;
160 pa_source *def_source;
161 http_response(c, 200, "OK", "text/html");
162
163 pa_ioline_puts(c->line,
164 "<?xml version=\"1.0\"?>\n"
165 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
166 "<html xmlns=\"http://www.w3.org/1999/xhtml\"><title>"PACKAGE_NAME" "PACKAGE_VERSION"</title>\n"
167 "<link rel=\"stylesheet\" type=\"text/css\" href=\"style\"/></head><body>\n");
168
169 pa_ioline_puts(c->line,
170 "<h1>"PACKAGE_NAME" "PACKAGE_VERSION"</h1>\n"
171 "<table>");
172
173 #define PRINTF_FIELD(a,b) pa_ioline_printf(c->line, "<tr><td><b>%s</b></td><td>%s</td></tr>\n",(a),(b))
174
175 PRINTF_FIELD("User Name:", pa_get_user_name(txt, sizeof(txt)));
176 PRINTF_FIELD("Host name:", pa_get_host_name(txt, sizeof(txt)));
177 PRINTF_FIELD("Default Sample Specification:", pa_sample_spec_snprint(txt, sizeof(txt), &c->protocol->core->default_sample_spec));
178
179 def_sink = pa_namereg_get_default_sink(c->protocol->core);
180 def_source = pa_namereg_get_default_source(c->protocol->core);
181
182 PRINTF_FIELD("Default Sink:", def_sink ? def_sink->name : "n/a");
183 PRINTF_FIELD("Default Source:", def_source ? def_source->name : "n/a");
184
185 pa_ioline_puts(c->line, "</table>");
186
187 pa_ioline_puts(c->line, "<p><a href=\"/status\">Click here</a> for an extensive server status report.</p>");
188
189 pa_ioline_puts(c->line, "</body></html>\n");
190
191 pa_ioline_defer_close(c->line);
192 } else if (!strcmp(c->url, URL_CSS)) {
193 http_response(c, 200, "OK", "text/css");
194
195 pa_ioline_puts(c->line,
196 "body { color: black; background-color: white; margin: 0.5cm; }\n"
197 "a:link, a:visited { color: #900000; }\n"
198 "p { margin-left: 0.5cm; margin-right: 0.5cm; }\n"
199 "h1 { color: #00009F; }\n"
200 "h2 { color: #00009F; }\n"
201 "ul { margin-left: .5cm; }\n"
202 "ol { margin-left: .5cm; }\n"
203 "pre { margin-left: .5cm; background-color: #f0f0f0; padding: 0.4cm;}\n"
204 ".grey { color: #afafaf; }\n"
205 "table { margin-left: 1cm; border:1px solid lightgrey; padding: 0.2cm; }\n"
206 "td { padding-left:10px; padding-right:10px; }\n");
207
208 pa_ioline_defer_close(c->line);
209 } else if (!strcmp(c->url, URL_STATUS)) {
210 char *r;
211
212 http_response(c, 200, "OK", "text/plain");
213 r = pa_full_status_string(c->protocol->core);
214 pa_ioline_puts(c->line, r);
215 pa_xfree(r);
216
217 pa_ioline_defer_close(c->line);
218 } else
219 http_message(c, 404, "Not Found", NULL);
220
221 break;
222 }
223
224 default:
225 ;
226 }
227
228 return;
229
230 fail:
231 internal_server_error(c);
232 }
233
234 void pa_http_protocol_connect(pa_http_protocol *p, pa_iochannel *io, pa_module *m) {
235 struct connection *c;
236
237 pa_assert(p);
238 pa_assert(io);
239 pa_assert(m);
240
241 if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
242 pa_log("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
243 pa_iochannel_free(io);
244 return;
245 }
246
247 c = pa_xnew(struct connection, 1);
248 c->protocol = p;
249 c->line = pa_ioline_new(io);
250 c->state = REQUEST_LINE;
251 c->url = NULL;
252 c->module = m;
253
254 pa_ioline_set_callback(c->line, line_callback, c);
255
256 pa_idxset_put(p->connections, c, NULL);
257 }
258
259 void pa_http_protocol_disconnect(pa_http_protocol *p, pa_module *m) {
260 struct connection *c;
261 void *state = NULL;
262
263 pa_assert(p);
264 pa_assert(m);
265
266 while ((c = pa_idxset_iterate(p->connections, &state, NULL)))
267 if (c->module == m)
268 connection_unlink(c);
269 }
270
271 static pa_http_protocol* http_protocol_new(pa_core *c) {
272 pa_http_protocol *p;
273
274 pa_assert(c);
275
276 p = pa_xnew(pa_http_protocol, 1);
277 PA_REFCNT_INIT(p);
278 p->core = c;
279 p->connections = pa_idxset_new(NULL, NULL);
280
281 pa_assert_se(pa_shared_set(c, "http-protocol", p) >= 0);
282
283 return p;
284 }
285
286 pa_http_protocol* pa_http_protocol_get(pa_core *c) {
287 pa_http_protocol *p;
288
289 if ((p = pa_shared_get(c, "http-protocol")))
290 return pa_http_protocol_ref(p);
291
292 return http_protocol_new(c);
293 }
294
295 pa_http_protocol* pa_http_protocol_ref(pa_http_protocol *p) {
296 pa_assert(p);
297 pa_assert(PA_REFCNT_VALUE(p) >= 1);
298
299 PA_REFCNT_INC(p);
300
301 return p;
302 }
303
304 void pa_http_protocol_unref(pa_http_protocol *p) {
305 struct connection *c;
306
307 pa_assert(p);
308 pa_assert(PA_REFCNT_VALUE(p) >= 1);
309
310 if (PA_REFCNT_DEC(p) > 0)
311 return;
312
313 while ((c = pa_idxset_first(p->connections, NULL)))
314 connection_unlink(c);
315
316 pa_idxset_free(p->connections, NULL, NULL);
317
318 pa_assert_se(pa_shared_remove(p->core, "http-protocol") >= 0);
319
320 pa_xfree(p);
321 }