Index: plugins/roard/protocol-gopher.c
===================================================================
--- plugins/roard/protocol-gopher.c	(revision 4742)
+++ plugins/roard/protocol-gopher.c	(revision 4744)
@@ -42,4 +42,6 @@
 static int scb_client_info(int client, struct roar_vio_calls * vio, char * selector, char ** text, struct item * sitem);
 static int scb_stream_info(int client, struct roar_vio_calls * vio, char * selector, char ** text, struct item * sitem);
+static int scb_listen_menu(int client, struct roar_vio_calls * vio, char * selector, char ** text, struct item * sitem);
+static int scb_listen     (int client, struct roar_vio_calls * vio, char * selector, char ** text, struct item * sitem);
 
 static struct roar_gopher_menu_item g_gopher_root_menu[] = {
@@ -49,4 +51,5 @@
  {.type = _DIR,  .name = "Clients",       .selector = "/clients/",   .host = NULL, .port = 0},
  {.type = _DIR,  .name = "Streams",       .selector = "/streams/",   .host = NULL, .port = 0},
+ {.type = _DIR,  .name = "Listen!",       .selector = "/listen/",    .host = NULL, .port = 0},
 };
 
@@ -82,5 +85,7 @@
  {.selector = "/streams/",   .type = _DIR,  .cb = scb_streams},
  {.selector = "/clients/*/", .type = _DIR,  .cb = scb_client_info},
- {.selector = "/streams/*/", .type = _DIR,  .cb = scb_stream_info}
+ {.selector = "/streams/*/", .type = _DIR,  .cb = scb_stream_info},
+ {.selector = "/listen/",    .type = _DIR,  .cb = scb_listen_menu},
+ {.selector = "/listen/*/*/*/*/*", .type = _FILE,  .cb = scb_listen}
 };
 
@@ -417,4 +422,113 @@
 #undef _MAX_ITEMS
  return ret;
+}
+
+static int scb_listen_menu(int client, struct roar_vio_calls * vio, char * selector, char ** text, struct item * sitem) {
+#define _MAX_ITEMS 12
+ struct roar_gopher_menu_item items[_MAX_ITEMS];
+ struct roar_gopher_menu menu = {.flags = 0, .items = items, .items_len = 0};
+ struct roar_gopher_menu_item * item;
+ const char * codec;
+ int ret;
+ int codecs[] = {ROAR_CODEC_DEFAULT, ROAR_CODEC_RIFF_WAVE, ROAR_CODEC_AU, ROAR_CODEC_OGG_VORBIS};
+ size_t i;
+
+ memset(items, 0, sizeof(items));
+
+ item = &(items[menu.items_len++]);
+ item->type = _INFO;
+ item->name = roar_mm_strdup("Select a format you want to listen in:");
+
+ item = &(items[menu.items_len++]);
+ item->type = _INFO;
+ item->name = NULL; // empty lion
+
+ for (i = 0; i < (sizeof(codecs)/sizeof(*codecs)); i++) {
+  item = &(items[menu.items_len++]);
+  item->type = _SOUND;
+  codec = roar_codec2str(codecs[i]);
+  item->name = _aprintf(64, "%u channels with %u bits at %uHz, %s",
+                            g_sa->channels, g_sa->bits, g_sa->rate, codec);
+  item->selector = _aprintf(64, "/listen/monitor/%u/%u/%u/%s", g_sa->rate, g_sa->bits, g_sa->channels, codec);
+ }
+
+ ret = send_menu(client, &menu, vio);
+
+ for (i = 0; i < menu.items_len; i++) {
+  if ( items[i].name != NULL )
+   roar_mm_free((void*)items[i].name);
+  if ( items[i].selector != NULL )
+   roar_mm_free((void*)items[i].selector);
+ }
+
+#undef _MAX_ITEMS
+ return ret;
+}
+
+static int scb_listen     (int client, struct roar_vio_calls * vio, char * selector, char ** text, struct item * sitem) {
+ struct roar_stream_server * ss;
+ struct roar_stream        * s;
+ struct roar_audio_info info;
+ int dir = -1;
+ ssize_t toks;
+ char * tok[5];
+ int stream = -1;
+
+ toks = strseltok(sitem->selector, selector, tok, 5);
+
+ if ( toks != 5 )
+  return -1;
+
+ memset(&info, 0, sizeof(info));
+
+ if ( (dir = roar_str2dir(tok[0])) == -1 )
+  return -1;
+
+ // test for unsupported dirs:
+ switch (dir) {
+  case ROAR_DIR_THRU:
+  case ROAR_DIR_RAW_IN:
+  case ROAR_DIR_FILTER:
+    return -1;
+   break;
+ }
+
+ info.rate     = atoi(tok[1]);
+ info.bits     = atoi(tok[2]);
+ info.channels = atoi(tok[3]);
+ info.codec    = roar_str2codec(tok[4]);
+
+ if ( info.codec == -1 )
+  return -1;
+
+ if ((stream = streams_new()) == -1 )
+  return -1;
+
+ if ( streams_get(stream, &ss) == -1 ) {
+  streams_delete(stream);
+  return -1;
+ }
+
+ s = ROAR_STREAM(ss);
+
+ if ( client_stream_add(client, stream) == -1 ) {
+  streams_delete(stream);
+  return -1;
+ }
+
+ memcpy(&(s->info), &info, sizeof(info));
+ ss->codec_orgi = info.codec;
+
+ if ( streams_set_dir(stream, dir, 1) == -1 ) {
+  streams_delete(stream);
+  return -1;
+ }
+
+ if ( client_stream_exec(client, stream) == -1 ) {
+  streams_delete(stream);
+  return -1;
+ }
+
+ return 0;
 }
 
@@ -537,5 +651,5 @@
  struct roar_buffer * buf;
  struct roar_gopher_menu_item * item;
- const size_t len = 80;
+ const size_t len = 256;
  size_t i;
  void * data;
@@ -561,5 +675,5 @@
   switch (item->type) {
    case _INFO:
-     snprintf(data, len-1, "i%s\tfake\t(NULL)\t0\r\n", item->name);
+     snprintf(data, len-1, "i%s\tfake\t(NULL)\t0\r\n", item->name == NULL ? "" : item->name);
     break;
    default:
