source: roaraudio/plugins/roard/protocol-gopher.c @ 4708:c9d40761088a

Last change on this file since 4708:c9d40761088a was 4708:c9d40761088a, checked in by phi, 13 years ago

updated copyright statements

File size: 10.4 KB
Line 
1//emul_gopher.c:
2
3/*
4 *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2010-2011
5 *
6 *  This file is part of roard a part of RoarAudio,
7 *  a cross-platform sound system for both, home and professional use.
8 *  See README for details.
9 *
10 *  This file is free software; you can redistribute it and/or modify
11 *  it under the terms of the GNU General Public License version 3
12 *  as published by the Free Software Foundation.
13 *
14 *  RoarAudio is distributed in the hope that it will be useful,
15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 *  GNU General Public License for more details.
18 *
19 *  You should have received a copy of the GNU General Public License
20 *  along with this software; see the file COPYING.  If not, write to
21 *  the Free Software Foundation, 51 Franklin Street, Fifth Floor,
22 *  Boston, MA 02110-1301, USA.
23 *
24 */
25
26#include "roard.h"
27
28#ifndef ROAR_WITHOUT_DCOMP_EMUL_GOPHER
29#include <roaraudio/proto_gopher.h>
30
31#define _INFO  ROAR_GOPHER_TYPE_INFO
32#define _DIR   ROAR_GOPHER_TYPE_DIR
33#define _FILE  ROAR_GOPHER_TYPE_FILE
34#define _SOUND ROAR_GOPHER_TYPE_SOUND
35
36static int scb_status_txt (int client, struct roar_vio_calls * vio, const char * selector, char ** text);
37static int scb_clients    (int client, struct roar_vio_calls * vio, const char * selector, char ** text);
38static int scb_streams    (int client, struct roar_vio_calls * vio, const char * selector, char ** text);
39
40static struct roar_gopher_menu_item g_gopher_root_menu[] = {
41 {.type = _INFO, .name = "roard Root Menu"},
42 {.type = _FILE, .name = "Server Info",   .selector = "/info.txt",   .host = NULL, .port = 0},
43 {.type = _FILE, .name = "Server Status", .selector = "/status.txt", .host = NULL, .port = 0},
44 {.type = _DIR,  .name = "Clients",       .selector = "/clients/",   .host = NULL, .port = 0},
45 {.type = _DIR,  .name = "Streams",       .selector = "/streams/",   .host = NULL, .port = 0},
46};
47
48static struct item {
49 const char * selector;
50 char type;
51 struct roar_gopher_menu menu;
52 struct roar_audio_info  info;
53 int dir;
54 const char * text;
55 int (*cb)(int client, struct roar_vio_calls * vio, const char * selector, char ** text);
56} g_gopher_items[] = {
57 {.selector = "", .type = _DIR,
58  .menu = {.items = g_gopher_root_menu, .items_len = sizeof(g_gopher_root_menu)/sizeof(*g_gopher_root_menu)},
59  .cb = NULL
60 },
61 // and again as selector '/' as some clients seems to require it:
62 {.selector = "/", .type = _DIR,
63  .menu = {.items = g_gopher_root_menu, .items_len = sizeof(g_gopher_root_menu)/sizeof(*g_gopher_root_menu)},
64  .cb = NULL
65 },
66 {.selector = "/info.txt",   .type = _FILE, .text = "Some\nText.", .cb = NULL},
67 {.selector = "/status.txt", .type = _FILE, .cb = scb_status_txt},
68 {.selector = "/clients/",   .type = _DIR,  .cb = scb_clients},
69 {.selector = "/streams/",   .type = _DIR,  .cb = scb_streams}
70};
71
72static int send_menu (int client, struct roar_gopher_menu * menu, struct roar_vio_calls * vio);
73static int send_text (int client, const char * text, struct roar_vio_calls * vio);
74
75
76// SCBs:
77static int scb_status_txt (int client, struct roar_vio_calls * vio, const char * selector, char ** text) {
78 const size_t len = 1024;
79 const char * server_version = NULL;
80
81 if ( DISTRIBUTION_VERSION_STRING[0] == 0 ) {
82  server_version = "roard/" PACKAGE_VERSION " <" DEVICE_VENDOR_STRING ">";
83 } else {
84  server_version = "roard/" PACKAGE_VERSION " <" DEVICE_VENDOR_STRING "> (" DISTRIBUTION_VERSION_STRING ")";
85 }
86
87 *text = roar_mm_malloc(1024);
88 if ( *text == NULL )
89  return -1;
90
91 **text = 0;
92
93 snprintf(*text, len,
94          "Server version:     %s\r\n"
95          "Server location:    %s\r\n"
96          "Server description: %s\r\n"
97          "\r\n"
98          "Counters current:   %llu clients, %llu streams\r\n",
99          server_version,
100          g_config->location,
101          g_config->description,
102          (long long unsigned int)g_counters.cur.clients,
103          (long long unsigned int)g_counters.cur.streams
104         );
105
106 (*text)[len-1] = 0;
107 return 0;
108}
109
110static int scb_clients    (int client, struct roar_vio_calls * vio, const char * selector, char ** text) {
111 struct roar_gopher_menu_item items[ROAR_CLIENTS_MAX];
112 struct roar_gopher_menu menu = {.flags = 0, .items = items, .items_len = 0};
113 struct roar_gopher_menu_item * item;
114 struct roar_client_server * cs;
115 struct roar_client        * c;
116 const size_t len = 80;
117 char * d;
118 size_t i;
119 int ret;
120
121 memset(items, 0, sizeof(items));
122
123 for (i = 0; i < ROAR_CLIENTS_MAX; i++) {
124  if ( (c = ROAR_CLIENT((cs = g_clients[i]))) != NULL ) {
125   item = &(items[menu.items_len++]);
126   item->type = _DIR;
127   d = roar_mm_malloc(len);
128   if ( d == NULL ) {
129    menu.items_len--;
130    continue;
131   }
132   if ( c->name != NULL && c->name[0] != 0 ) {
133    snprintf(d, len, "Client %i: %s", (int)i, c->name);
134   } else {
135    snprintf(d, len, "Client %i", (int)i);
136   }
137   item->name = d;
138
139   d = roar_mm_malloc(len);
140   if ( d == NULL ) {
141    if ( item->name != NULL )
142     roar_mm_free((void*)item->name);
143    menu.items_len--;
144    continue;
145   }
146
147   snprintf(d, len, "/clients/%i/", (int)i);
148   item->selector = d;
149  }
150 }
151
152 ret = send_menu(client, &menu, vio);
153
154 for (i = 0; i < menu.items_len; i++) {
155  if ( items[i].name != NULL )
156   roar_mm_free((void*)items[i].name);
157  if ( items[i].selector != NULL )
158   roar_mm_free((void*)items[i].selector);
159 }
160
161 return ret;
162}
163
164static int scb_streams    (int client, struct roar_vio_calls * vio, const char * selector, char ** text) {
165 struct roar_gopher_menu_item items[ROAR_STREAMS_MAX];
166 struct roar_gopher_menu menu = {.flags = 0, .items = items, .items_len = 0};
167 struct roar_gopher_menu_item * item;
168 struct roar_stream_server * ss;
169 struct roar_stream        * s;
170 const size_t len = 80;
171 char * d;
172 size_t i;
173 int ret;
174
175 memset(items, 0, sizeof(items));
176
177 for (i = 0; i < ROAR_STREAMS_MAX; i++) {
178  if ( (s = ROAR_STREAM((ss = g_streams[i]))) != NULL ) {
179   item = &(items[menu.items_len++]);
180   item->type = _DIR;
181   d = roar_mm_malloc(len);
182   if ( d == NULL ) {
183    menu.items_len--;
184    continue;
185   }
186   if ( ss->name != NULL && ss->name[0] != 0 ) {
187    snprintf(d, len, "Stream %i: %s", (int)i, ss->name);
188   } else {
189    snprintf(d, len, "Stream %i", (int)i);
190   }
191   item->name = d;
192
193   d = roar_mm_malloc(len);
194   if ( d == NULL ) {
195    if ( item->name != NULL )
196     roar_mm_free((void*)item->name);
197    menu.items_len--;
198    continue;
199   }
200
201   snprintf(d, len, "/streams/%i/", (int)i);
202   item->selector = d;
203  }
204 }
205
206 ret = send_menu(client, &menu, vio);
207
208 for (i = 0; i < menu.items_len; i++) {
209  if ( items[i].name != NULL )
210   roar_mm_free((void*)items[i].name);
211  if ( items[i].selector != NULL )
212   roar_mm_free((void*)items[i].selector);
213 }
214
215 return ret;
216}
217
218// other code:
219static int strip_nl (char * str) {
220 register char c;
221
222 for (; (c = *str) != 0; str++) {
223  if ( c == '\r' || c == '\n' ) {
224   *str = 0;
225   return 1;
226  }
227 }
228
229 return 0;
230}
231
232static int send_menu (int client, struct roar_gopher_menu * menu, struct roar_vio_calls * vio) {
233 struct roar_buffer * buf;
234 struct roar_gopher_menu_item * item;
235 const size_t len = 80;
236 size_t i;
237 void * data;
238 char * chardata;
239 const char * host;
240 unsigned int port;
241 struct roar_sockname sockaddr;
242
243 if ( roar_vio_ctl(vio, ROAR_VIO_CTL_GET_SOCKNAME, &sockaddr) == -1 ) {
244  memset(&sockaddr, 0, sizeof(sockaddr));
245 }
246
247 for (i = 0; i < menu->items_len; i++) {
248  item = &(menu->items[i]);
249  if ( roar_buffer_new_data(&buf, len, &data) == -1 ) {
250   if ( sockaddr.addr != NULL )
251    roar_mm_free(sockaddr.addr);
252   return -1;
253  }
254
255  chardata = data;
256
257  switch (item->type) {
258   case _INFO:
259     snprintf(data, len-1, "i%s\tfake\t(NULL)\t0\r\n", item->name);
260    break;
261   default:
262     host = item->host == NULL ? sockaddr.addr : item->host;
263     port = item->port ==    0 ? sockaddr.port : item->port;
264     snprintf(data, len-1, "%c%s\t%s\t%s\t%u\r\n", item->type, item->name, item->selector, host, port);
265    break;
266  }
267
268  chardata[len-1] = 0;
269
270  roar_buffer_set_len(buf, strlen(data));
271
272  clients_add_output(client, buf);
273 }
274
275 if ( sockaddr.addr != NULL )
276  roar_mm_free(sockaddr.addr);
277
278 return 0;
279}
280
281static int send_text (int client, const char * text, struct roar_vio_calls * vio) {
282 struct roar_buffer * buf;
283 void * data;
284 size_t len = strlen(text);
285
286 if ( roar_buffer_new_data(&buf, len+6, &data) == -1 )
287  return -1;
288
289 memcpy(data, text, len);
290 //memcpy(data+len, "\r\n.\r\n\0", 6);
291 memcpy(data+len, "\0", 1);
292 clients_add_output(client, buf);
293
294 return 0;
295}
296
297int emul_gopher_check_client(int client, struct roar_vio_calls * vio) {
298 struct roar_client_server * cs;
299 struct roar_vio_calls     rvio;
300 struct item * c = NULL;
301 char inbuf[1024];
302 ssize_t ret;
303 size_t i;
304 int funcret = -1;
305 size_t len = 0;
306 void * data;
307 char * text;
308
309 if ( clients_get_server(client, &cs) == -1 ) {
310  clients_delete(client);
311  return -1;
312 }
313
314 if ( vio == NULL ) {
315  vio = &rvio;
316  roar_vio_open_fh_socket(vio, clients_get_fh(client));
317 }
318
319 if ( cs->inbuf != NULL ) {
320  len = sizeof(inbuf)-1;
321  if ( roar_buffer_shift_out(&(cs->inbuf), inbuf, &len) == -1 ) {
322   clients_delete(client);
323   return -1;
324  }
325
326  if ( cs->inbuf != NULL ) {
327   roar_buffer_free(cs->inbuf);
328   clients_delete(client);
329   return -1;
330  }
331
332  // test if we have still buffer space left.
333  if ( len == (sizeof(inbuf)-1) ) {
334   clients_delete(client);
335   return -1;
336  }
337 }
338
339 ret = roar_vio_read(vio, inbuf+len, sizeof(inbuf)-len-1);
340 if ( ret < 1 ) {
341  clients_delete(client);
342  return -1;
343 }
344
345 ret += len;
346
347 inbuf[ret] = 0;
348
349 if ( !strip_nl(inbuf) ) {
350  if ( roar_buffer_new_data(&(cs->inbuf), ret, &data) == -1 ) {
351   clients_delete(client);
352   return -1;
353  }
354
355  memcpy(data, inbuf, ret);
356  return 0;
357 }
358
359 for (i = 0; i < sizeof(g_gopher_items)/sizeof(*g_gopher_items); i++) {
360  if ( !strcmp(g_gopher_items[i].selector, inbuf) ) {
361   c = &(g_gopher_items[i]);
362   break;
363  }
364 }
365
366 if ( c == NULL ) {
367  clients_delete(client);
368  return -1;
369 }
370
371 if ( c->cb != NULL ) {
372  text = NULL;
373  funcret = c->cb(client, vio, inbuf, &text);
374
375  if ( funcret == 0 && text != NULL )
376   funcret = send_text(client, text, vio);
377
378  if ( text != NULL )
379   roar_mm_free(text);
380 } else {
381  switch (c->type) {
382   case _DIR:
383     funcret = send_menu(client, &(c->menu), vio);
384    break;
385   case _FILE:
386     funcret = send_text(client, c->text, vio);
387    break;
388   default:
389     funcret = -1;
390    break;
391  }
392 }
393
394 if ( funcret == -1 ) {
395  clients_delete(client);
396  return -1;
397 }
398
399 return 0;
400}
401
402int emul_gopher_flushed_client(int client, struct roar_vio_calls * vio) {
403 return clients_delete(client);
404}
405
406#endif
407
408//ll
Note: See TracBrowser for help on using the repository browser.