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
RevLine 
[4699]1//emul_gopher.c:
2
3/*
[4708]4 *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2010-2011
[4699]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
[4703]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
[4714]36struct item;
[4712]37
[4714]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);
[4715]42static int scb_client_info(int client, struct roar_vio_calls * vio, char * selector, char ** text, struct item * sitem);
[4706]43
[4703]44static struct roar_gopher_menu_item g_gopher_root_menu[] = {
45 {.type = _INFO, .name = "roard Root Menu"},
[4706]46 {.type = _FILE, .name = "Server Info",   .selector = "/info.txt",   .host = NULL, .port = 0},
[4707]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},
[4703]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;
[4714]59 int (*cb)(int client, struct roar_vio_calls * vio, char * selector, char ** text, struct item * sitem);
[4703]60} g_gopher_items[] = {
61 {.selector = "", .type = _DIR,
[4706]62  .menu = {.items = g_gopher_root_menu, .items_len = sizeof(g_gopher_root_menu)/sizeof(*g_gopher_root_menu)},
63  .cb = NULL
[4703]64 },
65 // and again as selector '/' as some clients seems to require it:
66 {.selector = "/", .type = _DIR,
[4706]67  .menu = {.items = g_gopher_root_menu, .items_len = sizeof(g_gopher_root_menu)/sizeof(*g_gopher_root_menu)},
68  .cb = NULL
[4703]69 },
[4706]70 {.selector = "/info.txt",   .type = _FILE, .text = "Some\nText.", .cb = NULL},
[4707]71 {.selector = "/status.txt", .type = _FILE, .cb = scb_status_txt},
[4714]72 {.selector = "/test/*",     .type = _FILE, .cb = scb_test},
[4707]73 {.selector = "/clients/",   .type = _DIR,  .cb = scb_clients},
[4715]74 {.selector = "/streams/",   .type = _DIR,  .cb = scb_streams},
75 {.selector = "/clients/*/", .type = _DIR,  .cb = scb_client_info}
[4703]76};
77
[4711]78static int strselcmp(const char *s1, const char *s2);
[4712]79static ssize_t strseltok(const char *s1, char *s2, char ** tok, size_t toks);
[4711]80
[4715]81static char * _aprintf(size_t sizehint, const char * format, ...);
82
[4707]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
[4703]86
[4706]87// SCBs:
[4714]88static int scb_status_txt (int client, struct roar_vio_calls * vio, char * selector, char ** text, struct item * sitem) {
[4706]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
[4714]98 *text = roar_mm_malloc(len);
[4706]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
[4714]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) {
[4707]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
[4714]201static int scb_streams    (int client, struct roar_vio_calls * vio, char * selector, char ** text, struct item * sitem) {
[4707]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}
[4706]254
[4715]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
[4706]331// other code:
[4703]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
[4711]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
[4712]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
[4715]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
[4705]446static int send_menu (int client, struct roar_gopher_menu * menu, struct roar_vio_calls * vio) {
[4703]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;
[4705]453 const char * host;
[4704]454 unsigned int port;
[4705]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 }
[4703]460
461 for (i = 0; i < menu->items_len; i++) {
462  item = &(menu->items[i]);
[4705]463  if ( roar_buffer_new_data(&buf, len, &data) == -1 ) {
464   if ( sockaddr.addr != NULL )
465    roar_mm_free(sockaddr.addr);
[4703]466   return -1;
[4705]467  }
[4703]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:
[4705]476     host = item->host == NULL ? sockaddr.addr : item->host;
477     port = item->port ==    0 ? sockaddr.port : item->port;
[4704]478     snprintf(data, len-1, "%c%s\t%s\t%s\t%u\r\n", item->type, item->name, item->selector, host, port);
[4703]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
[4705]489 if ( sockaddr.addr != NULL )
490  roar_mm_free(sockaddr.addr);
491
[4703]492 return 0;
493}
494
[4705]495static int send_text (int client, const char * text, struct roar_vio_calls * vio) {
[4703]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);
[4706]504 //memcpy(data+len, "\r\n.\r\n\0", 6);
505 memcpy(data+len, "\0", 1);
[4703]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;
[4706]521 char * text;
[4703]522
[4714]523 ROAR_DBG("emul_gopher_check_client(client=%i, vio=%p) = ?", client, vio);
524
[4703]525 if ( clients_get_server(client, &cs) == -1 ) {
526  clients_delete(client);
527  return -1;
528 }
529
[4714]530 ROAR_DBG("emul_gopher_check_client(client=%i, vio=%p) = ?", client, vio);
531
[4703]532 if ( vio == NULL ) {
533  vio = &rvio;
534  roar_vio_open_fh_socket(vio, clients_get_fh(client));
535 }
536
[4714]537 ROAR_DBG("emul_gopher_check_client(client=%i, vio=%p) = ?", client, vio);
538
[4703]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
[4714]559 ROAR_DBG("emul_gopher_check_client(client=%i, vio=%p) = ?", client, vio);
560
[4703]561 ret = roar_vio_read(vio, inbuf+len, sizeof(inbuf)-len-1);
562 if ( ret < 1 ) {
563  clients_delete(client);
[4714]564  ROAR_DBG("emul_gopher_check_client(client=%i, vio=%p) = -1", client, vio);
[4703]565  return -1;
566 }
567
568 ret += len;
569
570 inbuf[ret] = 0;
571
[4714]572 ROAR_DBG("emul_gopher_check_client(client=%i, vio=%p) = ?", client, vio);
573
[4703]574 if ( !strip_nl(inbuf) ) {
575  if ( roar_buffer_new_data(&(cs->inbuf), ret, &data) == -1 ) {
576   clients_delete(client);
[4714]577   ROAR_DBG("emul_gopher_check_client(client=%i, vio=%p) = -1", client, vio);
[4703]578   return -1;
579  }
580
581  memcpy(data, inbuf, ret);
[4714]582
583  ROAR_DBG("emul_gopher_check_client(client=%i, vio=%p) = 0", client, vio);
584
[4703]585  return 0;
586 }
587
[4714]588 ROAR_DBG("emul_gopher_check_client(client=%i, vio=%p) = ?", client, vio);
589
[4703]590 for (i = 0; i < sizeof(g_gopher_items)/sizeof(*g_gopher_items); i++) {
[4714]591//  if ( !strselcmp(g_gopher_items[i].selector, inbuf) ) {
[4711]592  if ( !strselcmp(g_gopher_items[i].selector, inbuf) ) {
[4703]593   c = &(g_gopher_items[i]);
594   break;
595  }
596 }
597
598 if ( c == NULL ) {
599  clients_delete(client);
[4714]600  ROAR_DBG("emul_gopher_check_client(client=%i, vio=%p) = -1", client, vio);
[4703]601  return -1;
602 }
603
[4714]604 ROAR_DBG("emul_gopher_check_client(client=%i, vio=%p) = ?", client, vio);
605
[4706]606 if ( c->cb != NULL ) {
607  text = NULL;
[4712]608  funcret = c->cb(client, vio, inbuf, &text, c);
[4706]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  }
[4703]627 }
628
[4714]629 ROAR_DBG("emul_gopher_check_client(client=%i, vio=%p) = ?", client, vio);
630
[4703]631 if ( funcret == -1 ) {
632  clients_delete(client);
[4714]633  ROAR_DBG("emul_gopher_check_client(client=%i, vio=%p) = -1", client, vio);
[4703]634  return -1;
635 }
636
[4714]637 ROAR_DBG("emul_gopher_check_client(client=%i, vio=%p) = 0", client, vio);
638
[4703]639 return 0;
640}
641
642int emul_gopher_flushed_client(int client, struct roar_vio_calls * vio) {
[4714]643 ROAR_DBG("emul_gopher_flushed_client(client=%i, vio=%p) = ?", client, vio);
644
[4703]645 return clients_delete(client);
646}
647
[4699]648#endif
649
650//ll
Note: See TracBrowser for help on using the repository browser.