source: roaraudio/plugins/roard/protocol-gopher.c @ 4715:7fce204ba6a8

Last change on this file since 4715:7fce204ba6a8 was 4715:7fce204ba6a8, checked in by phi, 13 years ago

added some basic client infos

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