source: roaraudio/plugins/roard/protocol-gopher.c @ 4711:5ae94193666e

Last change on this file since 4711:5ae94193666e was 4711:5ae94193666e, checked in by phi, 13 years ago

wrote new string matching function for selector meatching

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