source: roaraudio/plugins/roard/protocol-gopher.c @ 4712:f3f7ab98b6ac

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

some preparement for parameterized selector usage

File size: 12.0 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 struct item;
37
38static int scb_status_txt (int client, struct roar_vio_calls * vio, const char * selector, char ** text, struct item * sitem);
39static int scb_clients    (int client, struct roar_vio_calls * vio, const char * selector, char ** text, struct item * sitem);
40static int scb_streams    (int client, struct roar_vio_calls * vio, const char * selector, char ** text, struct item * sitem);
41
42static struct roar_gopher_menu_item g_gopher_root_menu[] = {
43 {.type = _INFO, .name = "roard Root Menu"},
44 {.type = _FILE, .name = "Server Info",   .selector = "/info.txt",   .host = NULL, .port = 0},
45 {.type = _FILE, .name = "Server Status", .selector = "/status.txt", .host = NULL, .port = 0},
46 {.type = _DIR,  .name = "Clients",       .selector = "/clients/",   .host = NULL, .port = 0},
47 {.type = _DIR,  .name = "Streams",       .selector = "/streams/",   .host = NULL, .port = 0},
48};
49
50static struct item {
51 const char * selector;
52 char type;
53 struct roar_gopher_menu menu;
54 struct roar_audio_info  info;
55 int dir;
56 const char * text;
57 int (*cb)(int client, struct roar_vio_calls * vio, const char * selector, char ** text, struct item * sitem);
58} g_gopher_items[] = {
59 {.selector = "", .type = _DIR,
60  .menu = {.items = g_gopher_root_menu, .items_len = sizeof(g_gopher_root_menu)/sizeof(*g_gopher_root_menu)},
61  .cb = NULL
62 },
63 // and again as selector '/' as some clients seems to require it:
64 {.selector = "/", .type = _DIR,
65  .menu = {.items = g_gopher_root_menu, .items_len = sizeof(g_gopher_root_menu)/sizeof(*g_gopher_root_menu)},
66  .cb = NULL
67 },
68 {.selector = "/info.txt",   .type = _FILE, .text = "Some\nText.", .cb = NULL},
69 {.selector = "/status.txt", .type = _FILE, .cb = scb_status_txt},
70 {.selector = "/clients/",   .type = _DIR,  .cb = scb_clients},
71 {.selector = "/streams/",   .type = _DIR,  .cb = scb_streams}
72};
73
74static int strselcmp(const char *s1, const char *s2);
75static ssize_t strseltok(const char *s1, char *s2, char ** tok, size_t toks);
76
77static int send_menu (int client, struct roar_gopher_menu * menu, struct roar_vio_calls * vio);
78static int send_text (int client, const char * text, struct roar_vio_calls * vio);
79
80
81// SCBs:
82static int scb_status_txt (int client, struct roar_vio_calls * vio, const char * selector, char ** text, struct item * sitem) {
83 const size_t len = 1024;
84 const char * server_version = NULL;
85
86 if ( DISTRIBUTION_VERSION_STRING[0] == 0 ) {
87  server_version = "roard/" PACKAGE_VERSION " <" DEVICE_VENDOR_STRING ">";
88 } else {
89  server_version = "roard/" PACKAGE_VERSION " <" DEVICE_VENDOR_STRING "> (" DISTRIBUTION_VERSION_STRING ")";
90 }
91
92 *text = roar_mm_malloc(1024);
93 if ( *text == NULL )
94  return -1;
95
96 **text = 0;
97
98 snprintf(*text, len,
99          "Server version:     %s\r\n"
100          "Server location:    %s\r\n"
101          "Server description: %s\r\n"
102          "\r\n"
103          "Counters current:   %llu clients, %llu streams\r\n",
104          server_version,
105          g_config->location,
106          g_config->description,
107          (long long unsigned int)g_counters.cur.clients,
108          (long long unsigned int)g_counters.cur.streams
109         );
110
111 (*text)[len-1] = 0;
112 return 0;
113}
114
115static int scb_clients    (int client, struct roar_vio_calls * vio, const char * selector, char ** text, struct item * sitem) {
116 struct roar_gopher_menu_item items[ROAR_CLIENTS_MAX];
117 struct roar_gopher_menu menu = {.flags = 0, .items = items, .items_len = 0};
118 struct roar_gopher_menu_item * item;
119 struct roar_client_server * cs;
120 struct roar_client        * c;
121 const size_t len = 80;
122 char * d;
123 size_t i;
124 int ret;
125
126 memset(items, 0, sizeof(items));
127
128 for (i = 0; i < ROAR_CLIENTS_MAX; i++) {
129  if ( (c = ROAR_CLIENT((cs = g_clients[i]))) != NULL ) {
130   item = &(items[menu.items_len++]);
131   item->type = _DIR;
132   d = roar_mm_malloc(len);
133   if ( d == NULL ) {
134    menu.items_len--;
135    continue;
136   }
137   if ( c->name != NULL && c->name[0] != 0 ) {
138    snprintf(d, len, "Client %i: %s", (int)i, c->name);
139   } else {
140    snprintf(d, len, "Client %i", (int)i);
141   }
142   item->name = d;
143
144   d = roar_mm_malloc(len);
145   if ( d == NULL ) {
146    if ( item->name != NULL )
147     roar_mm_free((void*)item->name);
148    menu.items_len--;
149    continue;
150   }
151
152   snprintf(d, len, "/clients/%i/", (int)i);
153   item->selector = d;
154  }
155 }
156
157 ret = send_menu(client, &menu, vio);
158
159 for (i = 0; i < menu.items_len; i++) {
160  if ( items[i].name != NULL )
161   roar_mm_free((void*)items[i].name);
162  if ( items[i].selector != NULL )
163   roar_mm_free((void*)items[i].selector);
164 }
165
166 return ret;
167}
168
169static int scb_streams    (int client, struct roar_vio_calls * vio, const char * selector, char ** text, struct item * sitem) {
170 struct roar_gopher_menu_item items[ROAR_STREAMS_MAX];
171 struct roar_gopher_menu menu = {.flags = 0, .items = items, .items_len = 0};
172 struct roar_gopher_menu_item * item;
173 struct roar_stream_server * ss;
174 struct roar_stream        * s;
175 const size_t len = 80;
176 char * d;
177 size_t i;
178 int ret;
179
180 memset(items, 0, sizeof(items));
181
182 for (i = 0; i < ROAR_STREAMS_MAX; i++) {
183  if ( (s = ROAR_STREAM((ss = g_streams[i]))) != NULL ) {
184   item = &(items[menu.items_len++]);
185   item->type = _DIR;
186   d = roar_mm_malloc(len);
187   if ( d == NULL ) {
188    menu.items_len--;
189    continue;
190   }
191   if ( ss->name != NULL && ss->name[0] != 0 ) {
192    snprintf(d, len, "Stream %i: %s", (int)i, ss->name);
193   } else {
194    snprintf(d, len, "Stream %i", (int)i);
195   }
196   item->name = d;
197
198   d = roar_mm_malloc(len);
199   if ( d == NULL ) {
200    if ( item->name != NULL )
201     roar_mm_free((void*)item->name);
202    menu.items_len--;
203    continue;
204   }
205
206   snprintf(d, len, "/streams/%i/", (int)i);
207   item->selector = d;
208  }
209 }
210
211 ret = send_menu(client, &menu, vio);
212
213 for (i = 0; i < menu.items_len; i++) {
214  if ( items[i].name != NULL )
215   roar_mm_free((void*)items[i].name);
216  if ( items[i].selector != NULL )
217   roar_mm_free((void*)items[i].selector);
218 }
219
220 return ret;
221}
222
223// other code:
224static int strip_nl (char * str) {
225 register char c;
226
227 for (; (c = *str) != 0; str++) {
228  if ( c == '\r' || c == '\n' ) {
229   *str = 0;
230   return 1;
231  }
232 }
233
234 return 0;
235}
236
237static int strselcmp(const char *s1, const char *s2) {
238 register char a, b;
239
240 if ( s1 == s2 )
241  return 0;
242
243 if ( s1 == NULL || s2 == NULL )
244  return -1;
245
246 for (; ; s1++, s2++) {
247  a = *s1;
248  b = *s2;
249
250  if ( a == '*' ) {
251   s1++;
252   a = *s1;
253   if ( a == 0 ) {
254    if ( b == 0 ) {
255     return 1; // no match! ('*' does not mach no-chars)
256    } else {
257     return 0; // match! (string ends with '*' and something not EOS is in b)
258    }
259   } else {
260    for (; *s2 != 0 && *s2 != a; s2++);
261    if ( a != *s2 )
262     return 1; // no match! (did not find correct char)
263   }
264  } else if ( a == 0 || b == 0 ) {
265   if ( a == b ) {
266    return 0; // match!
267   } else {
268    return 1; // no match! (dffrent length)
269   }
270  } else if ( a != b ) {
271   return 1; // no match! (diffrent chars)
272  }
273 }
274
275 return -1;
276}
277
278static ssize_t strseltok(const char *s1, char *s2, char ** tok, size_t toks) {
279 register char a, b;
280 size_t idx = 0;
281
282 if ( s1 == NULL || s2 == NULL )
283  return -1;
284
285 for (; ; s1++, s2++) {
286  a = *s1;
287  b = *s2;
288
289  if ( a == 0 || b == 0 ) {
290   if ( a == b ) {
291    return idx;
292   } else {
293    return -1;
294   }
295  } else if ( a == '*' ) {
296   s1++;
297   a = *s1;
298   if ( idx == toks )
299    return -1;
300
301   tok[idx] = s2;
302   idx++;
303
304   for (; *s2 != 0 && *s2 != a; s2++);
305
306   if ( a == 0 )
307    return idx;
308
309   if ( *s1 == 0 )
310    return -1;
311
312   *s2 = 0;
313  }
314 }
315
316 return -1;
317}
318
319static int send_menu (int client, struct roar_gopher_menu * menu, struct roar_vio_calls * vio) {
320 struct roar_buffer * buf;
321 struct roar_gopher_menu_item * item;
322 const size_t len = 80;
323 size_t i;
324 void * data;
325 char * chardata;
326 const char * host;
327 unsigned int port;
328 struct roar_sockname sockaddr;
329
330 if ( roar_vio_ctl(vio, ROAR_VIO_CTL_GET_SOCKNAME, &sockaddr) == -1 ) {
331  memset(&sockaddr, 0, sizeof(sockaddr));
332 }
333
334 for (i = 0; i < menu->items_len; i++) {
335  item = &(menu->items[i]);
336  if ( roar_buffer_new_data(&buf, len, &data) == -1 ) {
337   if ( sockaddr.addr != NULL )
338    roar_mm_free(sockaddr.addr);
339   return -1;
340  }
341
342  chardata = data;
343
344  switch (item->type) {
345   case _INFO:
346     snprintf(data, len-1, "i%s\tfake\t(NULL)\t0\r\n", item->name);
347    break;
348   default:
349     host = item->host == NULL ? sockaddr.addr : item->host;
350     port = item->port ==    0 ? sockaddr.port : item->port;
351     snprintf(data, len-1, "%c%s\t%s\t%s\t%u\r\n", item->type, item->name, item->selector, host, port);
352    break;
353  }
354
355  chardata[len-1] = 0;
356
357  roar_buffer_set_len(buf, strlen(data));
358
359  clients_add_output(client, buf);
360 }
361
362 if ( sockaddr.addr != NULL )
363  roar_mm_free(sockaddr.addr);
364
365 return 0;
366}
367
368static int send_text (int client, const char * text, struct roar_vio_calls * vio) {
369 struct roar_buffer * buf;
370 void * data;
371 size_t len = strlen(text);
372
373 if ( roar_buffer_new_data(&buf, len+6, &data) == -1 )
374  return -1;
375
376 memcpy(data, text, len);
377 //memcpy(data+len, "\r\n.\r\n\0", 6);
378 memcpy(data+len, "\0", 1);
379 clients_add_output(client, buf);
380
381 return 0;
382}
383
384int emul_gopher_check_client(int client, struct roar_vio_calls * vio) {
385 struct roar_client_server * cs;
386 struct roar_vio_calls     rvio;
387 struct item * c = NULL;
388 char inbuf[1024];
389 ssize_t ret;
390 size_t i;
391 int funcret = -1;
392 size_t len = 0;
393 void * data;
394 char * text;
395
396 if ( clients_get_server(client, &cs) == -1 ) {
397  clients_delete(client);
398  return -1;
399 }
400
401 if ( vio == NULL ) {
402  vio = &rvio;
403  roar_vio_open_fh_socket(vio, clients_get_fh(client));
404 }
405
406 if ( cs->inbuf != NULL ) {
407  len = sizeof(inbuf)-1;
408  if ( roar_buffer_shift_out(&(cs->inbuf), inbuf, &len) == -1 ) {
409   clients_delete(client);
410   return -1;
411  }
412
413  if ( cs->inbuf != NULL ) {
414   roar_buffer_free(cs->inbuf);
415   clients_delete(client);
416   return -1;
417  }
418
419  // test if we have still buffer space left.
420  if ( len == (sizeof(inbuf)-1) ) {
421   clients_delete(client);
422   return -1;
423  }
424 }
425
426 ret = roar_vio_read(vio, inbuf+len, sizeof(inbuf)-len-1);
427 if ( ret < 1 ) {
428  clients_delete(client);
429  return -1;
430 }
431
432 ret += len;
433
434 inbuf[ret] = 0;
435
436 if ( !strip_nl(inbuf) ) {
437  if ( roar_buffer_new_data(&(cs->inbuf), ret, &data) == -1 ) {
438   clients_delete(client);
439   return -1;
440  }
441
442  memcpy(data, inbuf, ret);
443  return 0;
444 }
445
446 for (i = 0; i < sizeof(g_gopher_items)/sizeof(*g_gopher_items); i++) {
447  if ( !strselcmp(g_gopher_items[i].selector, inbuf) ) {
448   c = &(g_gopher_items[i]);
449   break;
450  }
451 }
452
453 if ( c == NULL ) {
454  clients_delete(client);
455  return -1;
456 }
457
458 if ( c->cb != NULL ) {
459  text = NULL;
460  funcret = c->cb(client, vio, inbuf, &text, c);
461
462  if ( funcret == 0 && text != NULL )
463   funcret = send_text(client, text, vio);
464
465  if ( text != NULL )
466   roar_mm_free(text);
467 } else {
468  switch (c->type) {
469   case _DIR:
470     funcret = send_menu(client, &(c->menu), vio);
471    break;
472   case _FILE:
473     funcret = send_text(client, c->text, vio);
474    break;
475   default:
476     funcret = -1;
477    break;
478  }
479 }
480
481 if ( funcret == -1 ) {
482  clients_delete(client);
483  return -1;
484 }
485
486 return 0;
487}
488
489int emul_gopher_flushed_client(int client, struct roar_vio_calls * vio) {
490 return clients_delete(client);
491}
492
493#endif
494
495//ll
Note: See TracBrowser for help on using the repository browser.