source: roaraudio/plugins/roard/protocol-gopher.c @ 4714:c8403b42e127

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

added support for paramterized selectors

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