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