Merge branch 'drops' into master

Conflicts:
	src/server/dial_server.c
	src/server/main.c
	src/server/quick_ssdp.c

Change-Id: I0e1e4bedf8206dcf422564b465864e3de2570567
diff --git a/src/server/dial_options.h b/src/server/dial_options.h
index 9d67392..3c32e29 100644
--- a/src/server/dial_options.h
+++ b/src/server/dial_options.h
@@ -26,14 +26,6 @@
 #ifndef DIAL_OPTIONS_H
 #define DIAL_OPTIONS_H
 
-#define DATA_PATH_OPTION "-D"
-#define DATA_PATH_OPTION_LONG "--data-path"
-#define DATA_PATH_DESCRIPTION "Path to netflix secure store"
-
-#define NETFLIX_PATH_OPTION "-N"
-#define NETFLIX_PATH_OPTION_LONG "--netflix-path"
-#define NETFLIX_PATH_DESCRIPTION "Path to Netflix application"
-
 #define FRIENDLY_NAME_OPTION "-F"
 #define FRIENDLY_NAME_OPTION_LONG "--friendly-name"
 #define FRIENDLY_NAME_DESCRIPTION "Device Friendly Name"
@@ -46,6 +38,10 @@
 #define UUID_OPTION_LONG "--uuid-name"
 #define UUID_DESCRIPTION "UUID of the device"
 
+#define UI_TYPE_OPTION "-I"
+#define UI_TYPE_OPTION_LONG "--ui-type"
+#define UI_TYPE_DESCRIPTION "UI type"
+
 struct dial_options
 {
     const char * pOption;
@@ -56,16 +52,6 @@
 struct dial_options gDialOptions[] = 
 {
     {
-        DATA_PATH_OPTION,
-        DATA_PATH_OPTION_LONG,
-        DATA_PATH_DESCRIPTION
-    },
-    {
-        NETFLIX_PATH_OPTION,
-        NETFLIX_PATH_OPTION_LONG,
-        NETFLIX_PATH_DESCRIPTION
-    },
-    {
         FRIENDLY_NAME_OPTION,
         FRIENDLY_NAME_OPTION_LONG,
         FRIENDLY_NAME_DESCRIPTION,
@@ -79,6 +65,11 @@
         UUID_OPTION,
         UUID_OPTION_LONG,
         UUID_DESCRIPTION
+    },
+    {
+        UI_TYPE_OPTION,
+        UI_TYPE_OPTION_LONG,
+        UI_TYPE_DESCRIPTION
     }
 };
 
diff --git a/src/server/dial_server.c b/src/server/dial_server.c
index ddb5db2..9efff06 100644
--- a/src/server/dial_server.c
+++ b/src/server/dial_server.c
@@ -233,8 +233,7 @@
             "%s"
             "  <additionalData>\n"
             "%s"
-            "\n  </additionalData>\n"
-            "</service>\r\n",
+            "\n  </additionalData>\n",
             origin_header,
             DIAL_VERSION,
             app->name,
@@ -243,6 +242,28 @@
             app->state == kDIALStatusStopped ?
                     "" : "  <link rel=\"run\" href=\"run\"/>\r\n",
             dial_data);
+    if (app->callbacks.service_data_cb != NULL) {
+        struct CastServiceData serviceData = app->callbacks.service_data_cb(ds, app_name, app->run_id, app->callback_data);
+        mg_printf(conn,
+                  "  <servicedata xmlns=\"urn:chrome.google.com:cast\">\r\n"
+                  "    <connectionSvcURL>http://%s:%d%s</connectionSvcURL>\r\n"
+                  "    <protocols>\r\n"
+                  "      <protocol>%s</protocol>\r\n"
+                  "    </protocols>\r\n"
+                  "  </servicedata>\r\n",
+                  serviceData.connection_svc_host,
+                  serviceData.connection_svc_port,
+                  serviceData.connection_svc_path,
+                  serviceData.protocol);
+    }
+    if (app->callbacks.activity_status_cb != NULL) {
+        struct CastActivityStatus activityStatus = app->callbacks.activity_status_cb(ds, app_name, app->run_id, app->callback_data);
+        mg_printf(conn,
+                  "  <activity-status xmlns=\"urn:chrome.google.com:cast\">\r\n"
+                  "    <description>%s</description>\r\n"
+                  "  </activity-status>\r\n", activityStatus.description);
+    }
+    mg_printf(conn, "</service>\r\n");
     ds_unlock(ds);
 }
 
@@ -330,6 +351,27 @@
     ds_unlock(ds);
 }
 
+// Add logic to test if we should drop DIAL requests
+#define SAGE_PROPERTIES_PATH "/rw/sage/SageClient.properties"
+#define DIAL_DISABLED_PROPERTY "allow_dial=false"
+
+int dial_allowed() {
+    char property[1024];
+    FILE *sageProperties;
+    if (!(sageProperties = fopen(SAGE_PROPERTIES_PATH,"r"))) {
+        return 1;
+    }
+    while (!feof(sageProperties)) {
+        fgets(property, sizeof(property), sageProperties);
+        if (strncmp(property, DIAL_DISABLED_PROPERTY, strlen(DIAL_DISABLED_PROPERTY))==0) {
+            fclose(sageProperties);
+            return 0;
+        }
+    }
+    fclose(sageProperties);
+    return 1;
+}
+
 static int ends_with(const char *str, const char *suffix) {
     if (!str || !suffix)
         return 0;
@@ -380,6 +422,9 @@
     }
     fprintf(stderr, "Origin %s, Host: %s\n", origin_header, host_header);
     if (event == MG_NEW_REQUEST) {
+        // If DIAL is disabled, drop the request
+        if (!dial_allowed())
+          return "done";
         // CORS OPTIONS request
         if (!strcmp(request_info->request_method, "OPTIONS")) {
             //TODO: for extra safety, also check that host header matches origin
diff --git a/src/server/dial_server.h b/src/server/dial_server.h
index 43eeb82..1558aa2 100644
--- a/src/server/dial_server.h
+++ b/src/server/dial_server.h
@@ -47,6 +47,23 @@
 #define DIAL_MAX_PAYLOAD (4096)
 
 /*
+ * Cast service data
+ */
+struct CastServiceData {
+    const char *connection_svc_host;
+    int connection_svc_port;
+    const char *connection_svc_path;
+    const char *protocol;
+};
+
+/*
+ * Cast activity status
+ */
+struct CastActivityStatus {
+    const char *description;
+};
+
+/*
  * Opaque DIAL server handle
  */
 struct DIALServer_;
@@ -74,6 +91,17 @@
 typedef DIALStatus (*DIAL_app_status_cb)(DIALServer *ds, const char *app_name,
                                          DIAL_run_t run_id, int* pCanStop,
                                          void *callback_data);
+/*
+ * Cast service data callback
+ */
+typedef struct CastServiceData (*CAST_app_service_data_cb)(DIALServer *ds, const char *app_name,
+                                 DIAL_run_t run_id, void *callback_data);
+
+/*
+ * Cast service data callback
+ */
+typedef struct CastActivityStatus (*CAST_app_activity_status_cb)(DIALServer *ds, const char *app_name,
+                                 DIAL_run_t run_id, void *callback_data);
 
 /*
  * DIAL callbacks
@@ -82,6 +110,8 @@
     DIAL_app_start_cb start_cb;
     DIAL_app_stop_cb stop_cb;
     DIAL_app_status_cb status_cb;
+    CAST_app_service_data_cb service_data_cb;
+    CAST_app_activity_status_cb activity_status_cb;
 };
 
 /*
diff --git a/src/server/main.c b/src/server/main.c
index d52a12e..275cae6 100644
--- a/src/server/main.c
+++ b/src/server/main.c
@@ -40,32 +40,29 @@
 
 #define BUFSIZE 256
 
-static char *spAppNetflix = "netflix";      // name of the netflix executable
-static char *spDefaultNetflix = "../../../src/platform/qt/netflix";
-static char *spDefaultData="../../../src/platform/qt/data";
-static char *spNfDataDir = "NF_DATA_DIR=";
-static char *defaultLaunchParam = "source_type=12";
-static char *spDefaultFriendlyName = "DIAL server sample";
-static char *spDefaultModelName = "NOT A VALID MODEL NAME";
-static char *spDefaultUuid = "deadbeef-dead-beef-dead-beefdeadbeef";
-static char spDataDir[BUFSIZE];
-static char spNetflix[BUFSIZE];
+static char *spDefaultFriendlyName = "Google Fiber TV Box";
+static char *spDefaultModelName = "GFHD100";
+static char *spDefaultUuid = "0";
 static char spFriendlyName[BUFSIZE];
 static char spModelName[BUFSIZE];
 static char spUuid[BUFSIZE];
+static char spUiType[BUFSIZE];
 static int gDialPort;
 
-static char *spAppYouTube = "chrome";
-static char *spAppYouTubeMatch = "chrome.*google-chrome-dial";
-static char *spAppYouTubeExecutable = "/opt/google/chrome/google-chrome";
-static char *spYouTubePS3UserAgent = "--user-agent="
-    "Mozilla/5.0 (PS3; Leanback Shell) AppleWebKit/535.22 (KHTML, like Gecko) "
-    "Chrome/19.0.1048.0 LeanbackShell/01.00.01.73 QA Safari/535.22 Sony PS3/ "
-    "(PS3, , no, CH)";
+static char *spAppNetflix = "netflix";      // name of the netflix executable
+static char *defaultLaunchParam = "source_type=12";
+
+static char *spAppYouTube = "content_shell";
+static char *spAppYouTubeMatch = "www.youtube.com/tv";
+
+static char *spAppFiberTV = "miniclient";
+static char *spAppFiberTVMatch = NULL;
 
 // Adding 20 bytes for prepended source_type for Netflix
 static char sQueryParam[DIAL_MAX_PAYLOAD+20];
 
+static const char *spIpAddress;
+
 static int doesMatch( char* pzExp, char* pzStr)
 {
     regex_t exp;
@@ -184,23 +181,51 @@
 
     closedir(proc_fd);
   } else {
-    printf("/proc failed to open\n");
+    fprintf(stderr, "/proc failed to open\n");
   }
   return 0;
 }
 
+/* Running an application is done through the runminiclient script */
 static pid_t runApplication( const char * const args[], DIAL_run_t *run_id ) {
+  const char * const script_args[] = {"/etc/init.d/S99miniclient", "restart", 0};
+
+  /* Write application information to /tmp/runapp */
+  FILE *runapp = fopen("/tmp/runapp","w");
+  if (!runapp) {
+    fprintf(stderr, "Couldn't open /tmp/runapp file\n");
+    return kDIALStatusStopped;
+  }
+  for(int i = 0; args[i]; ++i) {
+    int outputCharacters = fprintf(runapp, "%s ", args[i]);
+    if (outputCharacters<0) {
+      fprintf(stderr, "Error writing to /tmp/runapp file\n");
+    }
+  }
+  fsync(fileno(runapp));
+  fclose(runapp);
+  runapp=NULL;
+
+
   pid_t pid = fork();
   if (pid != -1) {
     if (!pid) { // child
-      putenv(spDataDir);
-      printf("Execute:\n");
-      for(int i = 0; args[i]; ++i) {
-        printf(" %d) %s\n", i, args[i]);
+      // Close all descriptors except stdin,stdout,stderr
+      int fd, maxfd;
+      maxfd = sysconf(_SC_OPEN_MAX);
+      for (fd=3;fd<maxfd;fd++) {
+        close(fd);
       }
-      execv(*args, (char * const *) args);
+      execv(*script_args, (char * const *) script_args);
+      // It won't reach this unless there was an error
+      fprintf(stderr, "Error executing %s\n", *script_args);
+      exit(1);
     } else {
       *run_id = (void *)(long)pid; // parent PID
+      // Wait until the script S99miniclient is done before continuing
+      waitpid(pid, NULL, 0);
+      // TODO(jfthibert) Should we try to wait a few seconds until the actual
+      // program is started?
     }
     return kDIALStatusRunning;
   } else {
@@ -224,15 +249,16 @@
 static DIALStatus youtube_start(DIALServer *ds, const char *appname,
                                 const char *args, size_t arglen,
                                 DIAL_run_t *run_id, void *callback_data) {
-    printf("\n\n ** LAUNCH YouTube ** with args %s\n\n", args);
+    fprintf(stderr, "** LAUNCH YouTube ** with args %s\n\n", args);
 
-    char url[512] = {0,}, data[512] = {0,};
-    sprintf( url, "https://www.youtube.com/tv?%s", args);
-    sprintf( data, "--user-data-dir=%s/.config/google-chrome-dial", getenv("HOME") );
+    char url[512] = {0,};
+    int urlLength = snprintf( url, sizeof(url), "https://www.youtube.com/tv?%s", args);
+    if (urlLength>=sizeof(url)) {
+      fprintf(stderr, "Warning, YouTube URL was truncated (%d>=%d)\n", urlLength, sizeof(url));
+    }
 
-    const char * const youtube_args[] = { spAppYouTubeExecutable,
-      spYouTubePS3UserAgent,
-      data, "--app", url, NULL
+    const char * const youtube_args[] = { "youtube",
+      url, NULL
     };
     runApplication( youtube_args, run_id );
 
@@ -248,7 +274,7 @@
 
 static void youtube_stop(DIALServer *ds, const char *appname, DIAL_run_t run_id,
                          void *callback_data) {
-    printf("\n\n ** KILL YouTube **\n\n");
+    fprintf(stderr, "** KILL YouTube **\n");
     pid_t pid;
     if ((pid = isAppRunning( spAppYouTube, spAppYouTubeMatch ))) {
         kill(pid, SIGTERM);
@@ -282,7 +308,7 @@
         }
     }
 
-    printf("appPid = %s, shouldRelaunch = %s queryParams = %s\n",
+    fprintf(stderr, "appPid = %s, shouldRelaunch = %s queryParams = %s\n",
           appPid?"TRUE":"FALSE",
           shouldRelaunchApp?"TRUE":"FALSE",
           sQueryParam );
@@ -291,7 +317,7 @@
     // never be relaunched
     if( !appPid )
     {
-        const char * const netflix_args[] = {spNetflix, "-Q", sQueryParam, 0};
+        const char * const netflix_args[] = {"netflix", "-Q", sQueryParam, 0};
         return runApplication( netflix_args, run_id );
     }
     else return kDIALStatusRunning;
@@ -302,8 +328,6 @@
     // Netflix application can stop
     *pCanStop = 1;
 
-    waitpid((pid_t)(long)run_id, NULL, WNOHANG); // reap child
-
     return isAppRunning( spAppNetflix, NULL ) ? kDIALStatusRunning : kDIALStatusStopped;
 }
 
@@ -313,22 +337,77 @@
     pid = isAppRunning( spAppNetflix, NULL );
     if( pid )
     {
-        printf("Killing pid %d\n", pid);
+        fprintf(stderr, "Killing pid %d\n", pid);
         kill((pid_t)pid, SIGTERM);
-        waitpid((pid_t)pid, NULL, 0); // reap child
     }
 }
 
-void run_ssdp(int port, const char *pFriendlyName, const char * pModelName, const char *pUuid);
+static DIALStatus fibertv_start(DIALServer *ds, const char *appname,
+                                const char *args, size_t arglen,
+                                DIAL_run_t *run_id, void *callback_data) {
+    fprintf(stderr, "** LAUNCH FiberTV **\n");
+
+    const char * const miniclient_args[] = { "miniclient", NULL };
+    runApplication( miniclient_args, run_id );
+
+    return kDIALStatusRunning;
+}
+
+static DIALStatus fibertv_status(DIALServer *ds, const char *appname,
+                                 DIAL_run_t run_id, int *pCanStop, void *callback_data) {
+    // FiberTV can stop
+    *pCanStop = 1;
+    return isAppRunning( spAppFiberTV, spAppFiberTVMatch ) ? kDIALStatusRunning : kDIALStatusStopped;
+}
+
+static void fibertv_stop(DIALServer *ds, const char *appname, DIAL_run_t run_id,
+                         void *callback_data) {
+    fprintf(stderr, "** KILL FiberTV **\n");
+    pid_t pid;
+    if ((pid = isAppRunning( spAppFiberTV, spAppFiberTVMatch ))) {
+        kill(pid, SIGTERM);
+    }
+}
+
+static DIALStatus basil_start(DIALServer *ds, const char *appname,
+                                const char *args, size_t arglen,
+                                DIAL_run_t *run_id, void *callback_data) {
+    fprintf(stderr, "** LAUNCH GoogleFiberTV **\n");
+    return kDIALStatusRunning; // Basil is always running
+}
+
+static DIALStatus basil_status(DIALServer *ds, const char *appname,
+                                 DIAL_run_t run_id, int *pCanStop, void *callback_data) {
+    *pCanStop = 0;
+    return kDIALStatusRunning; // Basil is always running
+}
+
+static struct CastServiceData basil_service_data(DIALServer *ds, const char *appname,
+                                 DIAL_run_t run_id, void *callback_data) {
+    struct CastServiceData serviceData;
+    serviceData.connection_svc_host = spIpAddress;
+    serviceData.connection_svc_port = 5153;
+    serviceData.connection_svc_path = "/connections";
+    serviceData.protocol = "marjoram";
+    return serviceData;
+}
+
+static void basil_stop(DIALServer *ds, const char *appname, DIAL_run_t run_id,
+                         void *callback_data) {
+    fprintf(stderr, "** KILL GoogleFiberTV (ignored) **\n");
+    // Basil cannot be killed, but log the attempt
+}
+
+void run_ssdp(int port, const char *pFriendlyName, const char * pModelName, const char *pUuid, const char **ppIpAddress);
 
 static void printUsage()
 {
     int i, numberOfOptions = sizeof(gDialOptions) / sizeof(dial_options_t);
-    printf("usage: dialserver <options>\n");
-    printf("options:\n");
+    fprintf(stderr, "usage: dialserver <options>\n");
+    fprintf(stderr, "options:\n");
     for( i = 0; i < numberOfOptions; i++ )
     {
-        printf("        %s|%s [value]: %s\n",
+        fprintf(stderr, "        %s|%s [value]: %s\n",
             gDialOptions[i].pOption,
             gDialOptions[i].pLongOption,
             gDialOptions[i].pOptionDescription );
@@ -339,29 +418,30 @@
 {
     // Destination is always one of our static buffers with size BUFSIZE
     memset( dest, 0, BUFSIZE );
-    memcpy( dest, pSource, strlen(pSource) );
-}
-
-static void setDataDir(char *pData)
-{
-    setValue( spNfDataDir, spDataDir );
-    strcat(spDataDir, pData);
+    strncpy( dest, pSource, BUFSIZE-1 );
 }
 
 void runDial(void)
 {
     DIALServer *ds;
     ds = DIAL_create();
-    struct DIALAppCallbacks cb_nf = {netflix_start, netflix_stop, netflix_status};
-    struct DIALAppCallbacks cb_yt = {youtube_start, youtube_stop, youtube_status};
+    struct DIALAppCallbacks cb_nf = {netflix_start, netflix_stop, netflix_status, NULL, NULL};
+    struct DIALAppCallbacks cb_yt = {youtube_start, youtube_stop, youtube_status, NULL, NULL};
+    struct DIALAppCallbacks cb_ft = {fibertv_start, fibertv_stop, fibertv_status, NULL, NULL};
+    struct DIALAppCallbacks cb_gf = {basil_start, basil_stop, basil_status, basil_service_data, NULL};
 
     DIAL_register_app(ds, "Netflix", &cb_nf, NULL, 1, "netflix.com");
     DIAL_register_app(ds, "YouTube", &cb_yt, NULL, 1, "youtube.com");
+    if (strcmp(spUiType, "oregano") == 0) {
+      DIAL_register_app(ds, "GoogleFiberTV", &cb_gf, NULL, 0, NULL);
+    } else {
+      DIAL_register_app(ds, "FiberTV", &cb_ft, NULL, 0, NULL);
+    }
     DIAL_start(ds);
 
     gDialPort = DIAL_get_port(ds);
-    printf("launcher listening on gDialPort %d\n", gDialPort);
-    run_ssdp(gDialPort, spFriendlyName, spModelName, spUuid);
+    fprintf(stderr, "launcher listening on gDialPort %d\n", gDialPort);
+    run_ssdp(gDialPort, spFriendlyName, spModelName, spUuid, &spIpAddress);
 
     DIAL_stop(ds);
     free(ds);
@@ -371,22 +451,18 @@
 {
     switch(index)
     {
-        case 0: // Data path
-            memset( spDataDir, 0, sizeof(spDataDir) );
-            setDataDir( pOption );
-            break;
-        case 1: // Netflix path
-            setValue( pOption, spNetflix );
-            break;
-        case 2: // Friendly name
+        case 0: // Friendly name
             setValue( pOption, spFriendlyName );
             break;
-        case 3: // Model Name
+        case 1: // Model Name
             setValue( pOption, spModelName );
             break;
-        case 4: // UUID
+        case 2: // UUID
             setValue( pOption, spUuid );
             break;
+        case 3: // UI type
+            setValue( pOption, spUiType );
+            break;
         default:
             // Should not get here
             fprintf( stderr, "Option %d not valid\n", index);
@@ -398,17 +474,21 @@
 {
     srand(time(NULL));
     int i;
+
+    // Set stdout to unbuffered mode to avoid delayed logs
+    setvbuf(stdout, NULL, _IONBF, 0);
+
     i = isAppRunning(spAppNetflix, NULL );
-    printf("Netflix is %s\n", i ? "Running":"Not Running");
+    fprintf(stderr, "Netflix is %s\n", i ? "Running":"Not Running");
     i = isAppRunning( spAppYouTube, spAppYouTubeMatch );
-    printf("YouTube is %s\n", i ? "Running":"Not Running");
+    fprintf(stderr, "YouTube is %s\n", i ? "Running":"Not Running");
+    i = isAppRunning( spAppFiberTV, spAppFiberTVMatch );
+    fprintf(stderr, "FiberTV is %s\n", i ? "Running":"Not Running");
 
     // set all defaults
     setValue(spDefaultFriendlyName, spFriendlyName );
     setValue(spDefaultModelName, spModelName );
     setValue(spDefaultUuid, spUuid );
-    setValue(spDefaultNetflix, spNetflix );
-    setDataDir(spDefaultData);
 
     // Process command line options
     // Loop through pairs of command line options.
diff --git a/src/server/quick_ssdp.c b/src/server/quick_ssdp.c
index bd77637..064913e 100644
--- a/src/server/quick_ssdp.c
+++ b/src/server/quick_ssdp.c
@@ -85,10 +85,14 @@
 static char model_name[256];
 static struct mg_context *ctx;
 
+extern int dial_allowed();
+
 static void *request_handler(enum mg_event event,
                              struct mg_connection *conn,
                              const struct mg_request_info *request_info) {
   if (event == MG_NEW_REQUEST) {
+    if (!dial_allowed())
+          return "done";
     if (!strcmp(request_info->uri, "/dd.xml") &&
         !strcmp(request_info->request_method, "GET")) {
       mg_printf(conn, "HTTP/1.1 200 OK\r\n"
@@ -176,10 +180,11 @@
     gBuf[bytes] = 0;
 
     // sophisticated SSDP parsing algorithm
-    if (!strstr(gBuf, "urn:dial-multiscreen-org:service:dial:1"))
+    if (!strstr(gBuf, "urn:dial-multiscreen-org:service:dial:1") &&
+        !strstr(gBuf, "ST: urn:dial-multiscreen-org:device:dial:1"))
     {
 #if 0  // use for debugging
-      printf("Dropping: \n");
+      fprintf(stderr, "Dropping: \n");
       {
           int i;
           for (i = 0; i < bytes; i++)
@@ -187,20 +192,22 @@
               putchar(gBuf[i]);
           }
       }
-      printf("\n##### End of DROP #######\n");
+      fprintf(stderr, "\n##### End of DROP #######\n");
 #endif
       continue;
     }
-    printf("Sending SSDP reply to %s:%d\n",
-           inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
-    if (-1 == sendto(s, send_buf, send_size, 0, (struct sockaddr *)&saddr, addrlen)) {
-      perror("sendto");
-      continue;
+    if (dial_allowed()) {
+        fprintf(stderr, "Sending SSDP reply to %s:%d\n",
+              inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
+        if (-1 == sendto(s, send_buf, send_size, 0, (struct sockaddr *)&saddr, addrlen)) {
+          perror("sendto");
+          continue;
+        }
     }
   }
 }
 
-void run_ssdp(int port, const char *pFriendlyName, const char * pModelName, const char *pUuid) {
+void run_ssdp(int port, const char *pFriendlyName, const char * pModelName, const char *pUuid, const char **ppIpAddress) {
   struct sockaddr sa;
   socklen_t len = sizeof(sa);
 
@@ -225,12 +232,13 @@
 
   dial_port = port;
   get_local_address();
+  *ppIpAddress = ip_addr;
   ctx = mg_start(&request_handler, NULL, SSDP_PORT);
 
   if (mg_get_listen_addr(ctx, &sa, &len)) {
     my_port = ntohs(((struct sockaddr_in *)&sa)->sin_port);
   }
 
-  printf("SSDP listening on %s:%d\n", ip_addr, my_port);
+  fprintf(stderr, "SSDP listening on %s:%d\n", ip_addr, my_port);
   handle_mcast();
 }