Index: /trunk/ntripserver/NtripLinuxServer.c
===================================================================
--- /trunk/ntripserver/NtripLinuxServer.c	(revision 31)
+++ /trunk/ntripserver/NtripLinuxServer.c	(revision 32)
@@ -41,5 +41,5 @@
  */
 
-/* $Id: NtripLinuxServer.c,v 1.13 2005/06/07 14:47:52 stoecker Exp $
+/* $Id: NtripLinuxServer.c,v 1.12 2005/06/02 09:33:11 stoecker Exp $
  * Changes - Version 0.7
  * Sep 22 2003  Steffen Tschirpke <St.Tschirpke@actina.de>
@@ -71,4 +71,13 @@
  * Jun 07 2005  Dirk Stoecker <soft@dstoecker.de>
  *           - added UDP bindmode
+ *
+ * Changes - Version 0.13
+ * Apr 25 2006  Andrea Stuerze <andrea.stuerze@bkg.bund.de>
+ *           - added stream retrieval from caster
+ *
+ * Changes - Version 0.14
+ * Apr 27 2006  Dirk Stoecker <soft@dstoecker.de>
+ *           - fixed some problems with caster download
+ *
  */
 
@@ -96,7 +105,8 @@
 #endif
 
-enum MODE { SERIAL = 1, TCPSOCKET = 2, INFILE = 3, SISNET = 4, UDPSOCKET = 5 };
-
-#define VERSION         "NTRIP NtripServerLinux/0.12"
+enum MODE { SERIAL = 1, TCPSOCKET = 2, INFILE = 3, SISNET = 4, UDPSOCKET = 5,
+CASTER = 6};
+
+#define VERSION         "NTRIP NtripServerLinux/0.13"
 #define BUFSZ           1024
 
@@ -126,4 +136,5 @@
 static void send_receive_loop(int sock, int fd, int sisnet);
 static void usage(int);
+static int encode(char *buf, int size, const char *user, const char *pwd);
 
 #ifdef __GNUC__
@@ -146,6 +157,6 @@
 * Parameters:
 *     argc : integer        : Number of command-line arguments.
-*     argv : array of char  : Command-line arguments as an array of zero-terminated
-*                             pointers to strings.
+*     argv : array of char  : Command-line arguments as an array of
+*                             zero-terminated pointers to strings.
 *
 * Return Value:
@@ -169,4 +180,9 @@
   const char *sisnetpassword = "";
   const char *sisnetuser = "";
+  
+  const char *stream_name=0;
+  const char *stream_user=0;
+  const char *stream_password=0;
+  
   const char *initfile = NULL;
   int bindmode = 0;
@@ -185,5 +201,6 @@
     exit(1);
   }
-  while((c = getopt(argc, argv, "M:i:h:b:p:s:a:m:c:H:P:f:l:u:V:B")) != EOF)
+  while((c = getopt(argc, argv, "M:i:h:b:p:s:a:m:c:H:P:f:l:u:V:D:U:W:B"))
+  != EOF)
   {
     switch (c)
@@ -195,6 +212,7 @@
       else if(!strcmp(optarg, "sisnet")) mode = 4;
       else if(!strcmp(optarg, "udpsocket")) mode = 5;
+      else if(!strcmp(optarg, "caster")) mode = 6;
       else mode = atoi(optarg);
-      if((mode == 0) || (mode > 5))
+      if((mode == 0) || (mode > 6))
       {
         fprintf(stderr, "ERROR: can't convert %s to a valid mode\n", optarg);
@@ -219,5 +237,6 @@
       if(ttybaud <= 1)
       {
-        fprintf(stderr, "ERROR: can't convert %s to valid serial speed\n", optarg);
+        fprintf(stderr, "ERROR: can't convert %s to valid serial speed\n",
+          optarg);
         usage(1);
       }
@@ -230,6 +249,6 @@
       if(outport <= 1 || outport > 65535)
       {
-        fprintf(stderr, "ERROR: can't convert %s to a valid HTTP server port\n",
-          optarg);
+        fprintf(stderr,
+          "ERROR: can't convert %s to a valid HTTP server port\n", optarg);
         usage(1);
       }
@@ -265,4 +284,13 @@
       }
       break;
+    case 'D':
+     stream_name=optarg;        /* desired stream from SourceCaster */
+     break; 
+    case 'U':
+     stream_user=optarg;        /* username for desired stream */
+     break;
+    case 'W':
+     stream_password=optarg;    /* passwd for desired stream */
+     break;
     case 'h':                  /* help */
     case '?':
@@ -296,5 +324,12 @@
   if(!password[0])
   {
-    fprintf(stderr, "WARNING: Missing password argument - are you really sure?\n");
+    fprintf(stderr,
+      "WARNING: Missing password argument - are you really sure?\n");
+  }
+
+  if(stream_name && (!stream_user || stream_password))
+  {
+    fprintf(stderr, "WARNING: Missing password argument for download"
+      " - are you really sure?\n");
   }
 
@@ -312,5 +347,6 @@
         exit(1);
       }
-      /* set blocking mode in case it was not set (seems to be sometimes for fifo's) */
+      /* set blocking mode in case it was not set
+        (seems to be sometimes for fifo's) */
       fcntl(gpsfd, F_SETFL, 0);
       printf("file input: file = %s\n", filepath);
@@ -327,5 +363,5 @@
     }
     break;
-  case TCPSOCKET: case UDPSOCKET: case SISNET:
+  case TCPSOCKET: case UDPSOCKET: case SISNET: case CASTER:
     {
       if(mode == SISNET)
@@ -334,9 +370,14 @@
         if(!inport) inport = SISNET_PORT;
       }
-      else
+      else if(mode == CASTER)
+      {
+        if(!inport) inport = NTRIP_PORT;
+        if(!inhost) inhost = NTRIP_CASTER;
+      }
+      else if((mode == TCPSOCKET) || (mode == UDPSOCKET))
       {
         if(!inport) inport = SERV_TCP_PORT;
         if(!inhost) inhost = "127.0.0.1";
-      }
+      }      
 
       if(!(he = gethostbyname(inhost)))
@@ -346,5 +387,6 @@
       }
 
-      if((gpsfd = socket(AF_INET, mode == UDPSOCKET ? SOCK_DGRAM : SOCK_STREAM, 0)) < 0)
+      if((gpsfd = socket(AF_INET, mode == UDPSOCKET
+      ? SOCK_DGRAM : SOCK_STREAM, 0)) < 0)
       {
         fprintf(stderr, "ERROR: can't create socket\n");
@@ -358,9 +400,11 @@
       addr.sin_port = htons(inport);
 
-      printf("%s input: host = %s, port = %d%s%s%s\n", mode == SISNET ? "sisnet"
-        : mode == TCPSOCKET ? "tcp socket" : "udp socket",
-        bindmode ? "127.0.0.1" : inet_ntoa(addr.sin_addr), inport,
-        initfile ? ", initfile = " : "",
-        initfile ? initfile : "", bindmode ? " binding mode" : "");
+      printf("%s input: host = %s, port = %d, %s%s%s%s%s\n",
+      mode == CASTER ? "caster" : mode == SISNET ? "sisnet" :
+      mode == TCPSOCKET ? "tcp socket" : "udp socket",
+      bindmode ? "127.0.0.1" : inet_ntoa(addr.sin_addr),
+      inport, stream_name ? "stream = " : "", stream_name ? stream_name : "",
+      initfile ? ", initfile = " : "", initfile ? initfile : "",
+      bindmode ? " binding mode" : "");
 
       if(bindmode)
@@ -378,4 +422,80 @@
         exit(1);
       }
+            
+      if(stream_name) /* data stream from caster */
+      {
+        int init = 0;
+
+        /* set socket buffer size */
+        setsockopt(gpsfd, SOL_SOCKET, SO_SNDBUF, (const char *) &size,
+          sizeof(const char *));
+        if(stream_user && stream_password)
+        {
+          /* leave some space for login */
+          nBufferBytes=snprintf(szSendBuffer, sizeof(szSendBuffer)-40,
+          "GET /%s HTTP/1.0\r\n"
+          "User-Agent: %s\r\n"
+          "Authorization: Basic ", stream_name, VERSION);
+          /* second check for old glibc */
+          if(nBufferBytes > (int)sizeof(szSendBuffer)-40 || nBufferBytes < 0)
+          {
+            fprintf(stderr, "Requested data too long\n");
+            exit(1);
+          }
+          nBufferBytes += encode(szSendBuffer+nBufferBytes,
+            sizeof(szSendBuffer)-nBufferBytes-5, stream_user, stream_password);
+          if(nBufferBytes > (int)sizeof(szSendBuffer)-5)
+          {
+            fprintf(stderr, "Username and/or password too long\n");
+            exit(1);
+          }
+          snprintf(szSendBuffer+nBufferBytes, 5, "\r\n\r\n");
+          nBufferBytes += 5;
+        }
+        else
+        {
+          nBufferBytes = snprintf(szSendBuffer, sizeof(szSendBuffer),
+          "GET /%s HTTP/1.0\r\n"
+          "User-Agent: %s\r\n"
+          "\r\n", stream_name, VERSION);
+        }
+        if((send(gpsfd, szSendBuffer, (size_t)nBufferBytes, 0))
+        != nBufferBytes)
+        {
+          fprintf(stderr, "ERROR: could not send to caster\n");
+          exit(1);
+        }
+        nBufferBytes = 0;
+        /* check caster's response */ 
+        while(!init && nBufferBytes < (int)sizeof(szSendBuffer)
+        && (nBufferBytes += recv(gpsfd, szSendBuffer,
+        sizeof(szSendBuffer)-nBufferBytes, 0)) > 0)
+        {
+          if(strstr(szSendBuffer, "\r\n"))
+          {
+            if(!strncmp(szSendBuffer, "ICY 200 OK\r\n", 10))
+              init = 1;
+            else
+            {
+              int k;
+              fprintf(stderr, "Could not get the requested data: ");
+              for(k = 0; k < nBufferBytes && szSendBuffer[k] != '\n'
+              && szSendBuffer[k] != '\r'; ++k)
+              {
+                fprintf(stderr, "%c", isprint(szSendBuffer[k])
+                ? szSendBuffer[k] : '.');
+              }
+              fprintf(stderr, "\n");
+              exit(1);
+            }
+          }
+        }
+        if(!init)
+        {
+          fprintf(stderr, "Could not init caster download.");
+          exit(1);
+        }
+      } /* end data stream from caster */
+
       if(initfile && mode != SISNET)
       {
@@ -413,6 +533,6 @@
       char buffer[1024];
 
-      i = snprintf(buffer, sizeof(buffer), sisnetv3 ? "AUTH,%s,%s\r\n" : "AUTH,%s,%s",
-      sisnetuser,sisnetpassword);
+      i = snprintf(buffer, sizeof(buffer), sisnetv3 ? "AUTH,%s,%s\r\n"
+        : "AUTH,%s,%s", sisnetuser, sisnetpassword);
       if((send(gpsfd, buffer, (size_t)i, 0)) != i)
       {
@@ -543,5 +663,5 @@
       }
       /* receiving data */
-      nBufferBytes = read(fd, buffer, BUFSZ);
+      nBufferBytes = read(fd, buffer, sizeof(buffer));
       if(!nBufferBytes)
       {
@@ -554,6 +674,7 @@
         exit(1);
       }
-      /* we can compare the whole buffer, as the additional bytes remain unchanged */
-      if(!memcmp(sisnetbackbuffer, buffer, sizeof(sisnetbackbuffer)))
+      /* we can compare the whole buffer, as the additional bytes
+         remain unchanged */
+      if(sisnet && !memcmp(sisnetbackbuffer, buffer, sizeof(sisnetbackbuffer)))
       {
         nBufferBytes = 0;
@@ -565,5 +686,5 @@
       /* send data */
       if((i = send(sock, buffer, (size_t)nBufferBytes, MSG_DONTWAIT))
-      != nBufferBytes)
+        != nBufferBytes)
       {
         if(i < 0 && errno != EAGAIN)
@@ -730,13 +851,14 @@
 {
   fprintf(stderr, "Usage: %s [OPTIONS]\n", VERSION);
-  fprintf(stderr, "  Options are:\n");
-  fprintf(stderr, "    -a caster name or address (default: %s)\n",
+  fprintf(stderr, "  Options are: [-]           \n");
+  fprintf(stderr, "    -a DestinationCaster name or address (default: %s)\n",
     NTRIP_CASTER);
-  fprintf(stderr, "    -p caster port (default: %d)\n", NTRIP_PORT);
-  fprintf(stderr, "    -m caster mountpoint\n");
-  fprintf(stderr, "    -c password for caster login\n");
+  fprintf(stderr, "    -p DestinationCaster port (default: %d)\n", NTRIP_PORT);
+  fprintf(stderr, "    -m DestinationCaster mountpoint\n");
+  fprintf(stderr, "    -c DestinationCaster password\n");
   fprintf(stderr, "    -h|? print this help screen\n");
-  fprintf(stderr, "    -M <mode>  sets the mode\n");
-  fprintf(stderr, "               (1=serial, 2=tcpsocket, 3=file, 4=sisnet, 5=udpsocket)\n");
+  fprintf(stderr, "    -M <mode>  sets the input mode\n");
+  fprintf(stderr, "               (1=serial, 2=tcpsocket, 3=file, 4=sisnet"
+    ", 5=udpsocket, 6=caster)\n");
   fprintf(stderr, "  Mode = file:\n");
   fprintf(stderr, "    -s file, simulate data stream by reading log file\n");
@@ -750,14 +872,72 @@
   fprintf(stderr, "  Mode = tcpsocket or udpsocket:\n");
   fprintf(stderr, "    -P receiver port (default: %d)\n", SERV_TCP_PORT);
-  fprintf(stderr, "    -H hostname of TCP server (default: %s)\n", SERV_HOST_ADDR);
+  fprintf(stderr, "    -H hostname of TCP server (default: %s)\n",
+    SERV_HOST_ADDR);
   fprintf(stderr, "    -f initfile send to server\n");
-  fprintf(stderr, "    -B bindmode (bind to incoming UDP stream)\n");
+  fprintf(stderr, "    -B bindmode: bind to incoming UDP stream\n");
   fprintf(stderr, "  Mode = sisnet:\n");
   fprintf(stderr, "    -P receiver port (default: %d)\n", SISNET_PORT);
-  fprintf(stderr, "    -H hostname of TCP server (default: %s)\n", SISNET_SERVER);
+  fprintf(stderr, "    -H hostname of TCP server (default: %s)\n",
+    SISNET_SERVER);
   fprintf(stderr, "    -u username\n");
   fprintf(stderr, "    -l password\n");
   fprintf(stderr, "    -V version [2.1 or 3.0] (default: 2.1)\n");
+  fprintf(stderr, "  Mode = caster:\n");
+  fprintf(stderr, "    -P SourceCaster port (default: %d)\n", NTRIP_PORT);
+  fprintf(stderr, "    -H SourceCaster hostname (default: %s)\n",
+    NTRIP_CASTER);
+  fprintf(stderr, "    -D SourceCaster mountpoint\n");
+  fprintf(stderr, "    -U SourceCaster mountpoint username\n");
+  fprintf(stderr, "    -W SourceCaster mountpoint password\n");  
   fprintf(stderr, "\n");
   exit(rc);
 }
+
+static const char encodingTable [64] = {
+  'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
+  'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
+  'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
+  'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
+};
+
+/* does not buffer overrun, but breaks directly after an error */
+/* returns the number of required bytes */
+static int encode(char *buf, int size, const char *user, const char *pwd)
+{
+  unsigned char inbuf[3];
+  char *out = buf;
+  int i, sep = 0, fill = 0, bytes = 0;
+
+  while(*user || *pwd)
+  {
+    i = 0;
+    while(i < 3 && *user) inbuf[i++] = *(user++);
+    if(i < 3 && !sep)    {inbuf[i++] = ':'; ++sep; }
+    while(i < 3 && *pwd)  inbuf[i++] = *(pwd++);
+    while(i < 3)         {inbuf[i++] = 0; ++fill; }
+    if(out-buf < size-1)
+      *(out++) = encodingTable[(inbuf [0] & 0xFC) >> 2];
+    if(out-buf < size-1)
+      *(out++) = encodingTable[((inbuf [0] & 0x03) << 4)
+               | ((inbuf [1] & 0xF0) >> 4)];
+    if(out-buf < size-1)
+    {
+      if(fill == 2)
+        *(out++) = '=';
+      else
+        *(out++) = encodingTable[((inbuf [1] & 0x0F) << 2)
+                 | ((inbuf [2] & 0xC0) >> 6)];
+    }
+    if(out-buf < size-1)
+    {
+      if(fill >= 1)
+        *(out++) = '=';
+      else
+        *(out++) = encodingTable[inbuf [2] & 0x3F];
+    }
+    bytes += 4;
+  }
+  if(out-buf < size)
+    *out = 0;
+  return bytes;
+}
