Merge branch 'drops' into HEAD

Change-Id: Ia785f7c867db52797ce0c8e31601881f149d6125
diff --git a/server/dial_data.c b/server/dial_data.c
index b31b5f9..1a1d7ca 100644
--- a/server/dial_data.c
+++ b/server/dial_data.c
@@ -74,10 +74,8 @@
     char value[256];
     while (fscanf(f, "%255s %255s\n", key, value) != EOF) {
         DIALData *newNode = (DIALData *) malloc(sizeof(DIALData));
-        newNode->key = (char *) malloc(strlen(key));
-        strcpy(newNode->key, key);
-        newNode->value = (char *) malloc(strlen(value));
-        strcpy(newNode->value, value);
+        newNode->key = strdup(key);
+        newNode->value = strdup(value);
         newNode->next = result;
         result = newNode;
     }
diff --git a/server/dial_options.h b/server/dial_options.h
index 6f6b970..a31a884 100644
--- a/server/dial_options.h
+++ b/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"
+
 #define WAKE_OPTION "-W"
 #define WAKE_OPTION_LONG "--wake-on-wifi-len"
 #define WAKE_DESCRIPTION "Enable wake on wifi/len.  Value: on/off.  Default (on)"
@@ -60,16 +56,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,
@@ -85,10 +71,10 @@
         UUID_DESCRIPTION
     },
     {
-        WAKE_OPTION,
-        WAKE_OPTION_LONG,
-        WAKE_DESCRIPTION
-    }
+        UI_TYPE_OPTION,
+        UI_TYPE_OPTION_LONG,
+        UI_TYPE_DESCRIPTION
+    },
 };
 
 #endif
diff --git a/server/dial_server.c b/server/dial_server.c
index a357907..1d5441f 100644
--- a/server/dial_server.c
+++ b/server/dial_server.c
@@ -119,7 +119,7 @@
                              const char *app_name,
                              const char *origin_header) {
     char additional_data_param[256] = {0, };
-    char body[DIAL_MAX_PAYLOAD + sizeof(additional_data_param) + 2] = {0, };
+    char body[DIAL_MAX_PAYLOAD] = {0, };
     DIALApp *app;
     DIALServer *ds = request_info->user_data;
     int body_size;
@@ -145,8 +145,9 @@
 
             if (app->useAdditionalData) {
                 // Construct additionalDataUrl=http://host:port/apps/app_name/dial_data
-                sprintf(additional_data_param,
-                        "additionalDataUrl=http%%3A%%2F%%2Flocalhost%%3A%d%%2Fapps%%2F%s%%2Fdial_data%%3F",
+                snprintf(additional_data_param, sizeof(additional_data_param),
+                        "%sadditionalDataUrl=http%%3A%%2F%%2Flocalhost%%3A%d%%2Fapps%%2F%s%%2Fdial_data%%3F",
+                        (body_size != 0) ? "&" : "",
                         dial_port, app_name);
             }
             fprintf(stderr, "Starting the app with params %s\n", body);
@@ -165,7 +166,11 @@
                         laddr, dial_port, app_name, origin_header);
                 // copy the payload into the application struct
                 memset(app->payload, 0, DIAL_MAX_PAYLOAD);
-                memcpy(app->payload, body, body_size);
+                if (body_size<=DIAL_MAX_PAYLOAD) {
+                    memcpy(app->payload, body, body_size);
+                } else {
+                    fprintf(stderr, "payload too small for body of %d bytes\n", body_size);
+                }
             } else {
                 mg_send_http_error(conn, 503, "Service Unavailable",
                                    "Service Unavailable");
@@ -228,19 +233,44 @@
             "  <name>%s</name>\r\n"
             "  <options allowStop=\"%s\"/>\r\n"
             "  <state>%s</state>\r\n"
-            "%s"
-            "  <additionalData>\n"
-            "%s"
-            "\n  </additionalData>\n"
-            "</service>\r\n",
-            origin_header,            
+            "%s",
+            origin_header,
             DIAL_VERSION,
             app->name,
             canStop ? "true" : "false",
             app->state ? "running" : "stopped",
             app->state == kDIALStatusStopped ?
-                    "" : "  <link rel=\"run\" href=\"run\"/>\r\n",
+                    "" : "  <link rel=\"run\" href=\"run\"/>\r\n");
+    if (strlen(dial_data)>0) {
+        mg_printf(
+            conn,
+            "  <additionalData>\r\n"
+            "%s"
+            "\r\n  </additionalData>\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);
 }
 
@@ -435,7 +465,8 @@
                              const struct mg_request_info *request_info) {
     DIALServer *ds = request_info->user_data;
 
-    fprintf(stderr, "Received request %s\n", request_info->uri);
+    fprintf(stderr, "Received %s request %s\n", request_info->request_method,
+            request_info->uri);
     char *host_header = {0,};
     char *origin_header = {0,};
     for (int i = 0; i < request_info->num_headers; ++i) {
@@ -539,8 +570,9 @@
     return ds;
 }
 
-void DIAL_start(DIALServer *ds) {
+int DIAL_start(DIALServer *ds) {
     ds->ctx = mg_start(&request_handler, ds, DIAL_PORT);
+    return (ds->ctx == NULL) ? 1 : 0;
 }
 
 void DIAL_stop(DIALServer *ds) {
diff --git a/server/dial_server.h b/server/dial_server.h
index 09f68b3..3d33343 100644
--- a/server/dial_server.h
+++ b/server/dial_server.h
@@ -44,7 +44,25 @@
 /*
  * The maximum DIAL payload accepted per the DIAL 1.6.1 specification.
  */
-#define DIAL_MAX_PAYLOAD (4096)
+/* Increased size in 1.7 to allow 256 bytes of additional data */
+#define DIAL_MAX_PAYLOAD (4352)
+
+/*
+ * 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;
+};
 
 /*
  * The maximum additionalDataUrl length
@@ -80,6 +98,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
@@ -88,6 +117,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;
 };
 
 /*
@@ -96,11 +127,11 @@
 DIALServer *DIAL_create();
 
 /*
- * Starts the DIAL server.
+ * Starts the DIAL server. Returns 0 on success, !0 on failure.
  *
  * @param[in] ds DIAL server handle
  */
-void DIAL_start(DIALServer *ds);
+int DIAL_start(DIALServer *ds);
 
 /*
  * Stop the DIAL server.
diff --git a/server/main.c b/server/main.c
index 0a7af31..f011960 100644
--- a/server/main.c
+++ b/server/main.c
@@ -42,33 +42,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];
-extern bool wakeOnWifiLan;
+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 = "browser_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+DIAL_MAX_ADDITIONALURL+40];
 
+static const char *spIpAddress;
+
 static int doesMatch( char* pzExp, char* pzStr)
 {
     regex_t exp;
@@ -88,6 +84,9 @@
     return match;
 }
 
+// For Fiber we do not let DIAL handle SIGTERM, as we consider that a killing
+// signal (pkillwait uses it first).
+/*
 void signalHandler(int signal)
 {
     switch(signal)
@@ -97,6 +96,7 @@
             break;
     }
 }
+*/
 
 /* The URL encoding source code was obtained here:
  * http://www.geekhideout.com/urlcode.shtml
@@ -141,6 +141,24 @@
  * End of URL ENCODE source
  */
 
+// Returns 0 for false, non-zero for true.
+static int isWirelessTV() {
+  FILE* fp;
+  char active_wan[6];
+
+  fp = popen("activewan", "r");
+  if (fp == NULL) {
+    fprintf(stderr, "Could not run activewan; assuming wired connection\n");
+    return 0;
+  }
+
+  fgets(active_wan, sizeof(active_wan) - 1, fp);
+  pclose(fp);
+
+  return strncmp(active_wan, "wcli0", 5) == 0 ||
+         strncmp(active_wan, "wcli1", 5) == 0;
+}
+
 /*
  * This function will walk /proc and look for the application in
  * /proc/<PID>/comm. and /proc/<PID>/cmdline to find it's command (executable
@@ -196,26 +214,62 @@
 
     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.tmp","w");
+  if (!runapp) {
+    fprintf(stderr, "Couldn't open /tmp/runapp.tmp 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.tmp file\n");
+      fclose(runapp);
+      return kDIALStatusStopped;
+    }
+  }
+  fsync(fileno(runapp));
+  fclose(runapp);
+  runapp=NULL;
+  int appclientUid = 201;
+  int videoGid = 200;
+  if (chown("/tmp/runapp.tmp", appclientUid, videoGid)) {
+    fprintf(stderr, "Error chowning /tmp/runapp.tmp file.  App launch may fail.");
+  }
+  if (rename("/tmp/runapp.tmp", "/tmp/runapp")) {
+    fprintf(stderr, "Error renaming /tmp/runapp.tmp file\n");
+    return kDIALStatusStopped;
+  }
+
+
   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);
       }
-      if( execv(*args, (char * const *) args) == -1) {
-		printf("%s failed to launch\n", *args);
-		perror("Failed to Launch \n");
-      }
+      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 {
@@ -241,19 +295,26 @@
                                 DIAL_run_t *run_id, void *callback_data) {
     printf("\n\n ** LAUNCH YouTube ** with payload %s\n\n", payload);
 
-    char url[512] = {0,}, data[512] = {0,};
+    char url[512] = {0,};
+    int urlLength;
     if (strlen(payload) && strlen(additionalDataUrl)){
-        sprintf( url, "https://www.youtube.com/tv?%s&%s", payload, additionalDataUrl);
+        urlLength = snprintf( url, sizeof(url),
+            "https://www.youtube.com/tv?%s&%s&%s",
+            isWirelessTV() ? "wireless" : "wired", payload, additionalDataUrl);
     }else if (strlen(payload)){
-        sprintf( url, "https://www.youtube.com/tv?%s", payload);
+        urlLength = snprintf( url, sizeof(url),
+            "https://www.youtube.com/tv?%s&%s",
+            isWirelessTV() ? "wireless" : "wired", payload);
     }else{
-        sprintf( url, "https://www.youtube.com/tv");
+        urlLength = snprintf( url, sizeof(url), "https://www.youtube.com/tv?%s",
+            isWirelessTV() ? "wireless" : "wired");
     }
-    sprintf( data, "--user-data-dir=%s/.config/google-chrome-dial", getenv("HOME") );
+    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 );
 
@@ -269,7 +330,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);
@@ -300,13 +361,12 @@
             free( pUrlEncodedParams );
         }
     }
-
     if(strlen(additionalDataUrl)){
         strcat(sQueryParam, "&");
         strcat(sQueryParam, additionalDataUrl);
     }
 
-    printf("appPid = %s, shouldRelaunch = %s queryParams = %s\n",
+    fprintf(stderr, "appPid = %s, shouldRelaunch = %s queryParams = %s\n",
           appPid?"TRUE":"FALSE",
           shouldRelaunchApp?"TRUE":"FALSE",
           sQueryParam );
@@ -315,7 +375,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;
@@ -323,10 +383,8 @@
 
 static DIALStatus netflix_status(DIALServer *ds, const char *appname,
                                  DIAL_run_t run_id, int* pCanStop, void *callback_data) {
-    // Netflix application can stop
-    *pCanStop = 1;
-
-    waitpid((pid_t)(long)run_id, NULL, WNOHANG); // reap child
+    // Netflix application can't stop
+    *pCanStop = 0;
 
     return isAppRunning( spAppNetflix, NULL ) ? kDIALStatusRunning : kDIALStatusStopped;
 }
@@ -337,22 +395,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 *payload, const char *additionalDataUrl,
+                                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 *payload, const char *additionalDataUrl,
+                                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 );
@@ -363,29 +476,33 @@
 {
     // 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");
-    DIAL_start(ds);
+    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);
+    }
+    if (DIAL_start(ds)) {
+        fprintf(stderr, "DIAL_start failed, exiting.\n");
+        exit(1);
+    }
 
     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);
@@ -395,61 +512,53 @@
 {
     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
-        setValue( pOption, spFriendlyName );
-        break;
-    case 3: // Model Name
-        setValue( pOption, spModelName );
-        break;
-    case 4: // UUID
-        setValue( pOption, spUuid );
-        break;
-    case 5:
-        if (strcmp(pOption, "on")==0){
-            wakeOnWifiLan=true;
-        }else if (strcmp(pOption, "off")==0){
-            wakeOnWifiLan=false;
-        }else{
-            fprintf(stderr, "Option %s is not valid for %s",
-                    pOption, WAKE_OPTION_LONG);
+        case 0: // Friendly name
+            setValue( pOption, spFriendlyName );
+            break;
+        case 1: // Model Name
+            setValue( pOption, spModelName );
+            break;
+        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);
             exit(1);
-        }
-        break;
-    default:
-        // Should not get here
-        fprintf( stderr, "Option %d not valid\n", index);
-        exit(1);
     }
 }
 
 int main(int argc, char* argv[])
 {
+    // We want DIAL to die on a SIGTERM - pkillwait sends SIGTERM first.
+    /*
     struct sigaction action;
     action.sa_handler = signalHandler;
     sigemptyset(&action.sa_mask);
     action.sa_flags = 0;
     sigaction(SIGTERM, &action, NULL);
+    */
 
     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/server/quick_ssdp.c b/server/quick_ssdp.c
index 689419e..253db54 100644
--- a/server/quick_ssdp.c
+++ b/server/quick_ssdp.c
@@ -1,27 +1,27 @@
 /*
-* Copyright (c) 2014 Netflix, Inc.
-* All rights reserved.
-*
-* Redistribution and use in source and binary forms, with or without
-* modification, are permitted provided that the following conditions are met:
-*
-* Redistributions of source code must retain the above copyright notice, this
-* list of conditions and the following disclaimer.
-* Redistributions in binary form must reproduce the above copyright notice,
-* this list of conditions and the following disclaimer in the documentation
-* and/or other materials provided with the distribution.
-*
-* THIS SOFTWARE IS PROVIDED BY NETFLIX, INC. AND CONTRIBUTORS "AS IS" AND ANY
-* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-* DISCLAIMED. IN NO EVENT SHALL NETFLIX OR CONTRIBUTORS BE LIABLE FOR ANY
-* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
+ * Copyright (c) 2014 Netflix, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY NETFLIX, INC. AND CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL NETFLIX OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
 
 #include <arpa/inet.h>
 #include <net/if.h>
@@ -32,54 +32,49 @@
 #include <sys/ioctl.h>
 #include <sys/socket.h>
 #include <unistd.h>
+
 #include "mongoose.h"
-#include <stdbool.h>
+#include "url_lib.h"
 
 // TODO: Partners should define this port
 #define SSDP_PORT (56790)
+
+
 static char gBuf[4096];
 
 // TODO: Partners should get the friendlyName from the system and insert here.
 // TODO: Partners should get the UUID from the system and insert here.
 static const char ddxml[] = ""
-    "<?xml version=\"1.0\"?>"
-    "<root"
-    " xmlns=\"urn:schemas-upnp-org:device-1-0\""
-    " xmlns:r=\"urn:restful-tv-org:schemas:upnp-dd\">"
-    " <specVersion>"
-    " <major>1</major>"
-    " <minor>0</minor>"
-    " </specVersion>"
-    " <device>"
-    " <deviceType>urn:schemas-upnp-org:device:tvdevice:1</deviceType>"
-    " <friendlyName>%s</friendlyName>"
-    " <manufacturer> </manufacturer>"
-    " <modelName>%s</modelName>"
-    " <UDN>uuid:%s</UDN>"
-    " </device>"
-    "</root>";
-
-// TODO: Partners should use appropriate timeout (in seconds) if hardware supports WoL.
-static const short wakeup_timeout = 10;
+"<?xml version=\"1.0\"?>"
+"<root"
+"  xmlns=\"urn:schemas-upnp-org:device-1-0\""
+"  xmlns:r=\"urn:restful-tv-org:schemas:upnp-dd\">"
+"  <specVersion>"
+"    <major>1</major>"
+"    <minor>0</minor>"
+"  </specVersion>"
+"  <device>"
+"    <deviceType>urn:schemas-upnp-org:device:tvdevice:1</deviceType>"
+"    <friendlyName>%s</friendlyName>"
+"    <manufacturer> </manufacturer>"
+"    <modelName>%s</modelName>"
+"    <UDN>uuid:%s</UDN>"
+"  </device>"
+"</root>";
 
 // TODO: Partners should get the UUID from the system and insert here.
 static const char ssdp_reply[] = "HTTP/1.1 200 OK\r\n"
-    "LOCATION: http://%s:%d/dd.xml\r\n"
-    "CACHE-CONTROL: max-age=1800\r\n"
-    "EXT:\r\n"
-    "BOOTID.UPNP.ORG: 1\r\n"
-    "SERVER: Linux/2.6 UPnP/1.0 quick_ssdp/1.0\r\n"
-    "ST: urn:dial-multiscreen-org:service:dial:1\r\n"
-    "USN: uuid:%s::"
-    "urn:dial-multiscreen-org:service:dial:1\r\n"
-    "%s"
-    "\r\n";
+                         "LOCATION: http://%s:%d/dd.xml\r\n"
+                         "CACHE-CONTROL: max-age=1800\r\n"
+                         "EXT:\r\n"
+                         "BOOTID.UPNP.ORG: 1\r\n"
+                         "SERVER: Linux/2.6 UPnP/1.0 quick_ssdp/1.0\r\n"
+                         "ST: urn:dial-multiscreen-org:service:dial:1\r\n"
+                         "USN: uuid:%s::"
+                         "urn:dial-multiscreen-org:service:dial:1\r\n\r\n";
 
-static const char wakeup_header[] = "WAKEUP: MAC=%s;Timeout=%d\r\n";
-#define STR_TIMEOUTLEN 6 /* Longest is 32767 */
-#define HW_ADDRSTRLEN 18
+
 static char ip_addr[INET_ADDRSTRLEN] = "127.0.0.1";
-static char hw_addr[HW_ADDRSTRLEN] = "00:00:00:00:00:00";
 static int dial_port = 0;
 static int my_port = 0;
 static char friendly_name[256];
@@ -87,167 +82,151 @@
 static char model_name[256];
 static struct mg_context *ctx;
 
-bool wakeOnWifiLan=true;
-
 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 (!strcmp(request_info->uri, "/dd.xml") &&
-            !strcmp(request_info->request_method, "GET")) {
-            mg_printf(conn, "HTTP/1.1 200 OK\r\n"
+  if (event == MG_NEW_REQUEST) {
+    if (!strcmp(request_info->uri, "/dd.xml") &&
+        !strcmp(request_info->request_method, "GET")) {
+      mg_printf(conn, "HTTP/1.1 200 OK\r\n"
                       "Content-Type: application/xml\r\n"
                       "Application-URL: http://%s:%d/apps/\r\n"
                       "\r\n", ip_addr, dial_port);
-            mg_printf(conn, ddxml, friendly_name, model_name, uuid);
-        } else {
-            mg_send_http_error(conn, 404, "Not Found", "Not Found");
-        }
-        return "done";
+      mg_printf(conn, ddxml, friendly_name, model_name, uuid);
+    } else {
+      mg_send_http_error(conn, 404, "Not Found", "Not Found");
     }
-    return NULL;
+    return "done";
+  }
+  return NULL;
 }
 
 static void get_local_address() {
-    struct ifconf ifc;
-    char buf[4096];
-    int s, i;
-    if (-1 == (s = socket(AF_INET, SOCK_DGRAM, 0))) {
-        perror("socket");
-        exit(1);
+  struct ifconf ifc;
+  char buf[4096];
+  int s, i;
+  if (-1 == (s = socket(AF_INET, SOCK_DGRAM, 0))) {
+    perror("socket");
+    exit(1);
+  }
+  ifc.ifc_len = sizeof(buf);
+  ifc.ifc_buf = buf;
+  if (0 > ioctl(s, SIOCGIFCONF, &ifc)) {
+    perror("SIOCGIFCONF");
+    exit(1);
+  }
+  if (ifc.ifc_len == sizeof(buf)) {
+    fprintf(stderr, "SIOCGIFCONF output too long");
+    exit(1);
+  }
+  close(s);
+  for (i = 0; i < ifc.ifc_len/sizeof(ifc.ifc_req[0]); i++) {
+    strcpy(ip_addr,
+      inet_ntoa(((struct sockaddr_in *)(&ifc.ifc_req[i].ifr_addr))->sin_addr));
+    // exit if we found a non-loopback address
+    if (strcmp("127.0.0.1", ip_addr)) {
+      break;
     }
-    ifc.ifc_len = sizeof(buf);
-    ifc.ifc_buf = buf;
-    if (0 > ioctl(s, SIOCGIFCONF, &ifc)) {
-        perror("SIOCGIFCONF");
-        exit(1);
-    }
-    if (ifc.ifc_len == sizeof(buf)) {
-        fprintf(stderr, "SIOCGIFCONF output too long");
-        exit(1);
-    }
-    for (i = 0; i < ifc.ifc_len/sizeof(ifc.ifc_req[0]); i++) {
-        strcpy(ip_addr,
-               inet_ntoa(((struct sockaddr_in *)(&ifc.ifc_req[i].ifr_addr))->sin_addr));
-        if (0 > ioctl(s, SIOCGIFFLAGS, &ifc.ifc_req[i])) {
-            perror("SIOCGIFFLAGS");
-            exit(1);
-        }
-        if (ifc.ifc_req[i].ifr_flags & IFF_LOOPBACK) {
-            // don't use loopback interfaces
-            continue;
-        }
-        if (0 > ioctl(s, SIOCGIFHWADDR, &ifc.ifc_req[i])) {
-            perror("SIOCGIFHWADDR");
-            exit(1);
-        }
-        sprintf(hw_addr, "%02x:%02x:%02x:%02x:%02x:%02x",
-                (unsigned char)ifc.ifc_req[i].ifr_hwaddr.sa_data[0],
-                (unsigned char)ifc.ifc_req[i].ifr_hwaddr.sa_data[1],
-                (unsigned char)ifc.ifc_req[i].ifr_hwaddr.sa_data[2],
-                (unsigned char)ifc.ifc_req[i].ifr_hwaddr.sa_data[3],
-                (unsigned char)ifc.ifc_req[i].ifr_hwaddr.sa_data[4],
-                (unsigned char)ifc.ifc_req[i].ifr_hwaddr.sa_data[5]);
-        break;
-    }
-    close(s);
+  }
 }
 
 static void handle_mcast() {
-    int s, one = 1, bytes;
-    socklen_t addrlen;
-    struct sockaddr_in saddr;
-    struct ip_mreq mreq;
-    char wakeup_buf[sizeof(wakeup_header) + HW_ADDRSTRLEN + STR_TIMEOUTLEN] = {0, };
-    char send_buf[sizeof(ssdp_reply) + INET_ADDRSTRLEN + 256 + 256 + sizeof(wakeup_buf)] = {0,};
-    int send_size;
-    if (-1 < wakeup_timeout && wakeOnWifiLan) {
-        snprintf(wakeup_buf, sizeof(wakeup_buf), wakeup_header, hw_addr, wakeup_timeout);
+  int s, one = 1, bytes;
+  socklen_t addrlen;
+  struct sockaddr_in saddr;
+  struct ip_mreq mreq;
+  char send_buf[sizeof(ssdp_reply) + INET_ADDRSTRLEN + 256 + 256] = {0,};
+  int send_size;
+
+  send_size = snprintf(send_buf, sizeof(send_buf), ssdp_reply, ip_addr, my_port, uuid);
+
+  if (-1 == (s = socket(AF_INET, SOCK_DGRAM, 0))) {
+    perror("socket");
+    exit(1);
+  }
+  if (-1 == setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) {
+    perror("reuseaddr");
+    exit(1);
+  }
+  saddr.sin_family = AF_INET;
+  saddr.sin_addr.s_addr = inet_addr("239.255.255.250");
+  saddr.sin_port = htons(1900);
+  if (-1 == bind(s, (struct sockaddr *)&saddr, sizeof(saddr))) {
+    perror("bind");
+    exit(1);
+  }
+  mreq.imr_multiaddr.s_addr = inet_addr("239.255.255.250");
+  mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+  if (-1 == setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+                       &mreq, sizeof(mreq))) {
+    perror("add_membership");
+    exit(1);
+  }
+  //printf("Starting Multicast handling on 239.255.255.250\n");
+  while (1) {
+    addrlen = sizeof(saddr);
+    if (-1 == (bytes = recvfrom(s, gBuf, sizeof(gBuf) - 1, 0,
+                                (struct sockaddr *)&saddr, &addrlen))) {
+      perror("recvfrom");
+      continue;
     }
-    send_size = snprintf(send_buf, sizeof(send_buf), ssdp_reply, ip_addr, my_port, uuid, wakeup_buf);
-    if (-1 == (s = socket(AF_INET, SOCK_DGRAM, 0))) {
-        perror("socket");
-        exit(1);
-    }
-    if (-1 == setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) {
-        perror("reuseaddr");
-        exit(1);
-    }
-    saddr.sin_family = AF_INET;
-    saddr.sin_addr.s_addr = inet_addr("239.255.255.250");
-    saddr.sin_port = htons(1900);
-    if (-1 == bind(s, (struct sockaddr *)&saddr, sizeof(saddr))) {
-        perror("bind");
-        exit(1);
-    }
-    mreq.imr_multiaddr.s_addr = inet_addr("239.255.255.250");
-    mreq.imr_interface.s_addr = inet_addr(ip_addr);
-    if (-1 == setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,
-                         &mreq, sizeof(mreq))) {
-        perror("add_membership");
-        exit(1);
-    }
-    //printf("Starting Multicast handling on 239.255.255.250\n");
-    while (1) {
-        addrlen = sizeof(saddr);
-        if (-1 == (bytes = recvfrom(s, gBuf, sizeof(gBuf) - 1, 0,
-                                    (struct sockaddr *)&saddr, &addrlen))) {
-            perror("recvfrom");
-            continue;
-        }
-        gBuf[bytes] = 0;
-        // sophisticated SSDP parsing algorithm
-        if (!strstr(gBuf, "urn:dial-multiscreen-org:service:dial:1"))
-            {
-#if 0 // use for debugging
-                printf("Dropping: \n");
-                {
-                    int i;
-                    for (i = 0; i < bytes; i++)
-                        {
-                            putchar(gBuf[i]);
-                        }
-                }
-                printf("\n##### End of DROP #######\n");
+    gBuf[bytes] = 0;
+
+    // sophisticated SSDP parsing algorithm
+    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
+      fprintf(stderr, "Dropping: \n");
+      {
+          int i;
+          for (i = 0; i < bytes; i++)
+          {
+              putchar(gBuf[i]);
+          }
+      }
+      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;
-        }
+      continue;
     }
+    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) {
-    struct sockaddr sa;
-    socklen_t len = sizeof(sa);
-    if(pFriendlyName) {
-        strncpy(friendly_name, pFriendlyName, sizeof(friendly_name));
-        friendly_name[255] = '\0';
-    } else {
-        strcpy(friendly_name, "DIAL server sample");
-    }
-    if(pModelName) {
-        strncpy(model_name, pModelName, sizeof(model_name));
-        uuid[255] = '\0';
-    } else {
-        strcpy(model_name, "deadbeef-dead-beef-dead-beefdeadbeef");
-    }
-    if(pUuid) {
-        strncpy(uuid, pUuid, sizeof(uuid));
-        uuid[255] = '\0';
-    } else {
-        strcpy(uuid, "deadbeef-dead-beef-dead-beefdeadbeef");
-    }
-    dial_port = port;
-    get_local_address();
-    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);
-    handle_mcast();
+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);
+
+  if(pFriendlyName) {
+      xmlencode(friendly_name, pFriendlyName, sizeof(friendly_name));
+  } else {
+      strcpy(friendly_name, "DIAL server sample");
+  }
+  if(pModelName) {
+      xmlencode(model_name, pModelName, sizeof(model_name));
+  } else {
+      strcpy(model_name, "deadbeef-dead-beef-dead-beefdeadbeef");
+  }
+  if(pUuid) {
+      xmlencode(uuid, pUuid, sizeof(uuid));
+  } else {
+      strcpy(uuid, "deadbeef-dead-beef-dead-beefdeadbeef");
+  }
+
+  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);
+  }
+
+  fprintf(stderr, "SSDP listening on %s:%d\n", ip_addr, my_port);
+  handle_mcast();
 }
diff --git a/server/url_lib.c b/server/url_lib.c
index 1ad45a5..70a3891 100644
--- a/server/url_lib.c
+++ b/server/url_lib.c
@@ -87,31 +87,48 @@
 void xmlencode(char *dst, const char *src, size_t max_size) {
     size_t current_size = 0;
     while (*src && current_size < max_size) {
-        switch (*src) {
-            case '&':
-                dst = smartstrcat(dst, "&amp;", max_size - current_size);
-                current_size += 5;
-                break;
-            case '\"':
-                dst = smartstrcat(dst, "&quot;", max_size - current_size);
-                current_size += 6;
-                break;
-            case '\'':
-                dst = smartstrcat(dst, "&apos;", max_size - current_size);
-                current_size += 6;
-                break;
-            case '<':
-                dst = smartstrcat(dst, "&lt;", max_size - current_size);
-                current_size += 4;
-                break;
-            case '>':
-                dst = smartstrcat(dst, "&gt;", max_size - current_size);
-                current_size += 4;
-                break;
-            default:
-                *dst++ = *src;
-                current_size++;
-                break;
+        // Discard invalid characters for xml encoding, assuming input
+        // shouldn't contain utf.
+        if(!((*src&0x80) ||
+           (*src < 0x20 && *src != 0x09 && *src != 0x0A && *src != 0x0D))) {
+            switch (*src) {
+                case '\t':
+                    dst = smartstrcat(dst, "&#x9;", max_size - current_size);
+                    current_size += 5;
+                    break;
+                case '\n':
+                    dst = smartstrcat(dst, "&#xA;", max_size - current_size);
+                    current_size += 5;
+                    break;
+                case '\r':
+                    dst = smartstrcat(dst, "&#xD;", max_size - current_size);
+                    current_size += 5;
+                    break;
+                case '&':
+                    dst = smartstrcat(dst, "&amp;", max_size - current_size);
+                    current_size += 5;
+                    break;
+                case '\"':
+                    dst = smartstrcat(dst, "&quot;", max_size - current_size);
+                    current_size += 6;
+                    break;
+                case '\'':
+                    dst = smartstrcat(dst, "&apos;", max_size - current_size);
+                    current_size += 6;
+                    break;
+                case '<':
+                    dst = smartstrcat(dst, "&lt;", max_size - current_size);
+                    current_size += 4;
+                    break;
+                case '>':
+                    dst = smartstrcat(dst, "&gt;", max_size - current_size);
+                    current_size += 4;
+                    break;
+                default:
+                    *dst++ = *src;
+                    current_size++;
+                    break;
+            }
         }
         src++;
     }