source: roaraudio/plugins/roard/protocol-gopher.c @ 4716:617c3db2d14b

Last change on this file since 4716:617c3db2d14b was 4716:617c3db2d14b, checked in by phi, 13 years ago

added support for stream infos

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