Drop from DIAL-1.0.6-72ab437.tar.gz

The binaries and object files were removed from the drop.

Change-Id: I1a9d22e4e4af0aa8ec20bdcfd560e8fc0aebfca6
diff --git a/Changelog b/Changelog
index 85f5bfe..c112427 100644
--- a/Changelog
+++ b/Changelog
@@ -1,7 +1,14 @@
 ---------------------------------------------------------------------
+Version 1.0.6 (72ab437)
+---------------------------------------------------------------------
+Server changes:
+	- Update to DIAL specificiation 2.0
+
+---------------------------------------------------------------------
 Version 1.0.5 (06edbce)
 ---------------------------------------------------------------------
 Server changes:
+	- Update to DIAL specificiation 1.7.2
 	- CORE compliance security patch
 	- Added CORS compliance test scripts
 
diff --git a/Version.h b/build/Version.h
similarity index 91%
rename from Version.h
rename to build/Version.h
index 484abaa..39b021d 100644
--- a/Version.h
+++ b/build/Version.h
@@ -1,5 +1,5 @@
 /*
- * (c) 1997-2012 Netflix, Inc.  All content herein is protected by
+ * (c) 1997-2015 Netflix, Inc.  All content herein is protected by
  * U.S. copyright and other applicable intellectual property laws and
  * may not be copied without the express permission of Netflix, Inc.,
  * which reserves all rights.  Reuse of any of this content for any
@@ -17,7 +17,7 @@
 #define DIAL_VERSION_MINOR 0
 
 #undef DIAL_VERSION_PATCH
-#define DIAL_VERSION_PATCH 3
+#define DIAL_VERSION_PATCH 5
 
 #undef DIAL_VERSION_NUMBER_STR2
 #define DIAL_VERSION_NUMBER_STR2(M) #M
diff --git a/build/dial_build.sh b/build/dial_build.sh
new file mode 100755
index 0000000..4667f05
--- /dev/null
+++ b/build/dial_build.sh
@@ -0,0 +1,66 @@
+#!/bin/bash -e
+#
+# (c) 1997-2012 Netflix, Inc.  All content herein is protected by
+# U.S. copyright and other applicable intellectual property laws and
+# may not be copied without the express permission of Netflix, Inc.,
+# which reserves all rights.  Reuse of any of this content for any
+# purpose without the permission of Netflix, Inc. is strictly
+# prohibited.
+
+BUILD_PREFIX="DIAL"
+BUILD_MAJOR=
+BUILD_MINOR=
+BUILD_PATCH=
+BUILD_SUFFIX=
+BUILD_NUMBER=
+COPY_SOURCE_VERSION_H=
+
+if [ -n "${P4_CHANGELIST}" ]; then
+  #echo "Using information from Jenkins environment to determine RELEASE number: $P4_CHANGELIST"
+  BUILD_NUMBER="${P4_CHANGELIST}"
+fi
+
+if [ -z "$COPY_SOURCE_VERSION_H" ]; then
+    VERSION_H="Version.h"
+    COPY_SOURCE_VERSION_H=`find ${COPY_SOURCE_DIR} -name ${VERSION_H}`
+    if [ -z "$COPY_SOURCE_VERSION_H" ]; then
+        echo "${VERSION_H} cannot be found!"
+        exit 1
+    elif echo "$COPY_VERSION" | grep --quiet ' '; then
+        echo "Too many ${VERSION_H} found!"
+        exit 1
+    fi
+fi
+
+BUILD_MAJOR=`grep -m1 -i "^# *define *${BUILD_PREFIX}_VERSION_MAJOR" "$COPY_SOURCE_VERSION_H" | sed "s,/[/*].*$,," | awk '{print $(NF)}'`
+if [ -z "$BUILD_MAJOR" ]; then
+    echo "Failed to determine version major of current source." >&2
+    exit 1
+fi
+
+if [ -z "$BUILD_MINOR" ]; then
+    BUILD_MINOR=`grep -m1 -i "^# *define *${BUILD_PREFIX}_VERSION_MINOR" "$COPY_SOURCE_VERSION_H" | sed "s,/[/*].*$,," | awk '{print $(NF)}'`
+    if [ -z "$BUILD_MINOR" ]; then
+        echo "Failed to determine version minor of current source." >&2
+        exit 1
+    fi
+fi
+
+if [ -z "$BUILD_PATCH" ]; then
+    BUILD_PATCH=`grep -m1 -i "^# *define *${BUILD_PREFIX}_VERSION_PATCH" "$COPY_SOURCE_VERSION_H" | sed "s,/[/*].*$,," | awk '{print $(NF)}'`
+    if [ -z "$BUILD_PATCH" ]; then
+        echo "Failed to determine version minor of current source." >&2
+        exit 1
+    fi
+fi
+
+BUILD_STRING="${BUILD_MAJOR}.${BUILD_MINOR}.${BUILD_PATCH}-${BUILD_NUMBER}"
+echo "${BUILD_STRING}"
+
+tar czvhf ${WORKSPACE}/DIAL-$BUILD_STRING.tar.gz *
+
+# Package the binaries
+cp ${WORKSPACE}/src/dial/client/dialclient ${WORKSPACE}/build
+cp ${WORKSPACE}/src/dial/server/dialserver ${WORKSPACE}/build
+cd ${WORKSPACE}/build
+tar czvhf ${WORKSPACE}/DIAL-$BUILD_STRING-binaries.tar.gz *
diff --git a/src/client/DialClientInput.cpp b/client/DialClientInput.cpp
similarity index 100%
rename from src/client/DialClientInput.cpp
rename to client/DialClientInput.cpp
diff --git a/src/client/DialClientInput.h b/client/DialClientInput.h
similarity index 100%
rename from src/client/DialClientInput.h
rename to client/DialClientInput.h
diff --git a/src/client/DialConformance.cpp b/client/DialConformance.cpp
similarity index 100%
rename from src/client/DialConformance.cpp
rename to client/DialConformance.cpp
diff --git a/src/client/DialConformance.h b/client/DialConformance.h
similarity index 100%
rename from src/client/DialConformance.h
rename to client/DialConformance.h
diff --git a/src/client/DialDiscovery.cpp b/client/DialDiscovery.cpp
similarity index 70%
rename from src/client/DialDiscovery.cpp
rename to client/DialDiscovery.cpp
index efb37af..3d04c39 100644
--- a/src/client/DialDiscovery.cpp
+++ b/client/DialDiscovery.cpp
@@ -40,6 +40,7 @@
 #include <vector>
 #include "DialDiscovery.h"
 #include <assert.h>
+#include <ctime>
 
 #define SSDP_TIMEOUT 30
 #define SSDP_RESPONSE_TIMEOUT 3
@@ -97,6 +98,45 @@
     return retUrl;
 }
 
+static string getMac(char *pResponse)
+{
+    string response(pResponse);
+    string mac("");
+    size_t index = response.find("WAKEUP:");
+
+    if (index==string::npos){
+        return mac;
+    }else{
+        size_t start = response.find("MAC=", index);
+        if (start==string::npos) return mac;
+        start+=4;
+        size_t end = response.find(";", start);
+        if (end==string::npos) return mac;
+        mac = response.substr(start, end-start);
+        return mac;
+    }
+}
+
+static int getTimeout(char *pResponse)
+{
+    string response(pResponse);
+    int timeOut=0;
+    string timeOutStr;
+    size_t index = response.find("WAKEUP:");
+    if (index==string::npos){
+        return timeOut;
+    }else{
+        size_t start = response.find("Timeout=", index);
+        if (start==string::npos) return timeOut;
+        start+=8;
+        size_t end = response.find("\r\n", start);
+        if (end==string::npos) return timeOut;
+        timeOutStr = response.substr(start, end-start);
+        timeOut = (int)(strtol(timeOutStr.c_str(), NULL, 10));
+        return timeOut;
+    }
+}
+
 static size_t header_cb(void* ptr, size_t size, size_t nmemb, void* userdata)
 {
     if ((size * nmemb) != 0) {
@@ -132,7 +172,6 @@
 {
     CURL *curl;
     CURLcode res = CURLE_OK;
-
     if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
         fprintf(stderr, "curl_global_init() failed\n");
         return; 
@@ -146,6 +185,7 @@
 
     ATRACE("Sending ##%s##\n", server.c_str());
     curl_easy_setopt(curl, CURLOPT_URL, server.c_str());
+    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 1);
     curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_cb);
     curl_easy_setopt(curl, CURLOPT_HEADERDATA, &appsUrl);
     curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, receiveData);
@@ -157,7 +197,7 @@
     //curl_global_cleanup();
 }
 
-void DialDiscovery::updateServerList(string& server)
+void DialDiscovery::updateServerList(string& server, string mac, int timeOut)
 {
     ServerMap::const_iterator it;
     it = mServerMap.find(server);
@@ -165,7 +205,7 @@
         // not found, add it
         string appsUrl, ddxml;
         getServerInfo(server, appsUrl, ddxml);
-        mServerMap[server] = new DialServer(server, appsUrl, ddxml);
+        mServerMap[server] = new DialServer(server, appsUrl, ddxml, mac, timeOut);
     } else {
         // just mark that we found it
         (*it).second->setFound(true);
@@ -181,9 +221,12 @@
         // parse for LOCATION header
         server = getLocation(pResponse);
         ATRACE("FOUND server: %s\n", server.c_str());
-    
+
+        string mac=getMac(pResponse);
+        int timeOut=getTimeout(pResponse);
+
         // save the appURL in the server
-        updateServerList(server);
+        updateServerList(server, mac, timeOut);
 #ifndef DEBUG
     }
 #else
@@ -196,17 +239,39 @@
 void *DialDiscovery::receiveResponses(void *p) 
 {
     search_conn *pConn = (search_conn*)p;
+
+    fd_set readfds;
+    struct timeval tv;
+    int selectRt;
+    // clear the set ahead of time
+    FD_ZERO(&readfds);
+    // add our descriptors to the set
+    FD_SET(pConn->sock, &readfds);
+    // wait until either socket has data ready to be recv()d (timeout 1.5 secs)
+    tv.tv_sec = 1;
+    tv.tv_usec = 500000;
+    
     int bytes;
     char buf[4096] = {0,};
-    while (1) {
-        if (-1 == (bytes = recvfrom(pConn->sock, buf, sizeof(buf) - 1, 0,
-                                  (struct sockaddr *)&pConn->saddr, &pConn->addrlen))) {
-            perror("recvfrom");
+    time_t start=std::time(NULL);
+    while (std::time(NULL)-start<RESPONSE_TIMEOUT) {
+        //        printf("beat %ld %ld %ld\n", std::time(NULL), start, std::time(NULL)-start);
+        selectRt=select(pConn->sock+1, &readfds, NULL, NULL, &tv);
+        if (selectRt==-1){
+            perror("select"); // error occurred in select()
             break;
+        }else if (selectRt == 0){
+            //            printf("Timeout occurred!  No data after 1.5 seconds.\n");
+        }else{
+            if (-1 == (bytes = recvfrom(pConn->sock, buf, sizeof(buf) - 1, 0,
+                                        (struct sockaddr *)&pConn->saddr, &pConn->addrlen))) {
+                perror("recvfrom");
+                break;
+            }
+            buf[bytes] = 0;
+            ATRACE("Received [%s:%d] %s\n", __FUNCTION__, __LINE__, buf);
+            DialDiscovery::instance()->processServer(buf);
         }
-        buf[bytes] = 0;
-        ATRACE("Received [%s:%d] %s\n", __FUNCTION__, __LINE__, buf);
-        DialDiscovery::instance()->processServer(buf);
     }
     return 0;
 }
@@ -244,17 +309,17 @@
     }
 }
 
-void * DialDiscovery::send_mcast(void *p) 
+void *DialDiscovery::send_mcast() 
 {
     int one = 1, my_sock;
     socklen_t addrlen;
     //struct ip_mreq mreq;
     char send_buf[strlen((char*)ssdp_msearch) + INET_ADDRSTRLEN + 256] = {0,};
     int send_size;
-    pthread_attr_t attr;
     search_conn connection;
 
-    //    send_size = snprintf(send_buf, sizeof(send_buf), ssdp_msearch, ip_addr, my_port);
+    printf("Sending mcast for discovery.  Please wait for %d seconds for the response.\n", RESPONSE_TIMEOUT);
+
     send_size = snprintf(send_buf, sizeof(send_buf), ssdp_msearch);
     ATRACE("[%s:%d] %s\n", __FUNCTION__, __LINE__, send_buf);
 
@@ -270,45 +335,38 @@
     saddr.sin_addr.s_addr = inet_addr("239.255.255.250");
     saddr.sin_port = htons(1900);
 
-    while (1) {
-        addrlen = sizeof(saddr);
-        ATRACE("Sending SSDP M-SEARCH to %s:%d\n",
-             inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
-        if (-1 == sendto(my_sock, send_buf, send_size, 0, (struct sockaddr *)&saddr, addrlen)) {
-          perror("sendto");
-            continue;
-        }
-
-        // set all servers to not found
-        DialDiscovery::instance()->resetDiscovery();
-
-        // spawn a response thread
-        connection.saddr = saddr;
-        connection.sock = my_sock;
-        connection.addrlen = addrlen;
-        pthread_attr_init(&attr);
-        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
-        pthread_create(&DialDiscovery::instance()->_responseThread, &attr, DialDiscovery::receiveResponses, &connection);
-
-        // sleep SSDP_RESPONSE_TIMEOUT seconds to allow clients to response
-        sleep(SSDP_RESPONSE_TIMEOUT);
-        DialDiscovery::instance()->cleanServerList();
-
-        sleep(SSDP_TIMEOUT-SSDP_RESPONSE_TIMEOUT);
-        pthread_cancel(DialDiscovery::instance()->_responseThread);
+    addrlen = sizeof(saddr);
+    ATRACE("Sending SSDP M-SEARCH to %s:%d\n",
+           inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
+    if (-1 == sendto(my_sock, send_buf, send_size, 0, (struct sockaddr *)&saddr, addrlen)) {
+        perror("sendto");
+        return 0;
     }
+
+    // set all servers to not found
+    DialDiscovery::instance()->resetDiscovery();
+
+    connection.saddr = saddr;
+    connection.sock = my_sock;
+    connection.addrlen = addrlen;
+
+    receiveResponses(&connection);
+
+    // // sleep SSDP_RESPONSE_TIMEOUT seconds to allow clients to response
+    // sleep(SSDP_RESPONSE_TIMEOUT);
+    DialDiscovery::instance()->cleanServerList();
+
+    return 0;
 }
 
 void DialDiscovery::init() 
 {
-    pthread_attr_t attr;
-    pthread_attr_init(&attr);
-    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
-    pthread_create(&_mcastThread, &attr, DialDiscovery::send_mcast, (void*)ssdp_msearch );
+    send_mcast();
 }
 
 void DialDiscovery::getServerList( vector<DialServer*>& list )
 {
+    list.clear();
     for( ServerMap::iterator it = mServerMap.begin(); it != mServerMap.end(); ++it ) {
         list.push_back( it->second );
     }
diff --git a/src/client/DialDiscovery.h b/client/DialDiscovery.h
similarity index 95%
rename from src/client/DialDiscovery.h
rename to client/DialDiscovery.h
index c6ba01f..f33ec75 100644
--- a/src/client/DialDiscovery.h
+++ b/client/DialDiscovery.h
@@ -73,11 +73,11 @@
         const string& friendlyName,
         DialServer &server );
 
+    void *send_mcast();
 private:
     DialDiscovery();
-    void updateServerList(string& server);
+    void updateServerList(string& server, string mac, int timeOut);
     static void *receiveResponses(void *p);
-    static void *send_mcast(void *p);
     void processServer(char *pResponse);
     void cleanServerList();
     void resetDiscovery();
@@ -89,6 +89,7 @@
     ServerMap mServerMap;
 
     static DialDiscovery* sDiscovery;
+    static const int RESPONSE_TIMEOUT = 5;
 };
 
 #endif // DIALDISCOVERY_H
diff --git a/src/client/DialServer.cpp b/client/DialServer.cpp
similarity index 91%
rename from src/client/DialServer.cpp
rename to client/DialServer.cpp
index 951727d..d1def42 100644
--- a/src/client/DialServer.cpp
+++ b/client/DialServer.cpp
@@ -185,6 +185,16 @@
     return retval;
 }
 
+string DialServer::getMacAddress()
+{
+    return m_macAddr;
+}
+
+int  DialServer::getWakeOnLanTimeout()
+{
+    return m_wakeUpTimeout;
+}
+
 int DialServer::launchApplication(
     string &application,
     string &payload, 
@@ -193,8 +203,8 @@
 {
     ATRACE("%s: Launch %s\n", __FUNCTION__, application.c_str());
     string appUrl = m_appsUrl;
-    sendCommand( appUrl.append(application), COMMAND_LAUNCH, payload, responseHeaders, responseBody);
-    return 0;
+    int status = sendCommand( appUrl.append(application), COMMAND_LAUNCH, payload, responseHeaders, responseBody);
+    return status;
 }
 
 int DialServer::getStatus(
@@ -205,8 +215,9 @@
     ATRACE("%s: GetStatus %s\n", __FUNCTION__, application.c_str());
     string emptyPayload;
     string appUrl = m_appsUrl;
-    sendCommand( appUrl.append(application), COMMAND_STATUS, emptyPayload, responseHeaders, responseBody );
+    int status = sendCommand( appUrl.append(application), COMMAND_STATUS, emptyPayload, responseHeaders, responseBody );
 
+    if (!status) return 0;
     ATRACE("Body: %s\n", responseBody.c_str());
     unsigned found = responseBody.find("href=");
     if( found != string::npos )
@@ -234,10 +245,10 @@
     // just call status to update the run endpoint
     getStatus( application, responseHeaders, responseBody );
 
-    sendCommand( 
-            (appUrl.append(application)).append("/"+m_stopEndPoint), 
-            COMMAND_KILL, emptyPayload, responseHeaders, responseBody );
-    return 0;
+    int status = sendCommand( 
+                             (appUrl.append(application)).append("/"+m_stopEndPoint), 
+                             COMMAND_KILL, emptyPayload, responseHeaders, responseBody );   
+    return status;
 }
 
 int DialServer::getHttpResponseHeader( 
diff --git a/src/client/DialServer.h b/client/DialServer.h
similarity index 90%
rename from src/client/DialServer.h
rename to client/DialServer.h
index 59b5aa7..ccd37e4 100644
--- a/src/client/DialServer.h
+++ b/client/DialServer.h
@@ -50,11 +50,13 @@
      * empty string to find any server
      *
      */
-    DialServer( string location, string appsUrl, string dd_xml ) : 
+    DialServer( string location, string appsUrl, string dd_xml, string macAddr, int wakeUpTimeout) : 
     m_location(location),
     m_appsUrl(appsUrl),
     found(true),
-    m_ddxml(dd_xml) 
+    m_ddxml(dd_xml),
+    m_macAddr(macAddr),
+    m_wakeUpTimeout(wakeUpTimeout)
     {}
 
     ~DialServer() { ATRACE("%s\n", __FUNCTION__); }
@@ -95,6 +97,20 @@
     bool getUuid( string& uuid );
 
     /**
+     * Get the dial server mac address
+     *
+     * @return mac address if avaliable, '' if not.
+     */    
+    string getMacAddress();
+
+    /**
+     * Get the wake on lan timeout in seconds
+     *
+     * @return timeout value if avaliable, zero if not.
+     */
+    int  getWakeOnLanTimeout();
+
+    /**
      * Launch a DIAL application
      *
      * @param[in] application Name of the application to launch
@@ -176,6 +192,9 @@
     bool found;
     string m_ddxml; // information about the device
     string m_stopEndPoint;
+    string m_macAddr;
+    int    m_wakeUpTimeout;
+
 };
 
 #endif // DIALSERVER_H
diff --git a/client/build.sh b/client/build.sh
new file mode 100755
index 0000000..b7e2c4f
--- /dev/null
+++ b/client/build.sh
@@ -0,0 +1,5 @@
+TARGET=/usr/local/i686-netflix-linux-gnu-4.2/bin/i686-netflix-linux-gnu- \
+LDFLAGS="-L/usr/local/i686-netflix-linux-gnu-4.2/netflix/lib \
+-Wl,-rpath,/usr/local/i686-netflix-linux-gnu-4.2/netflix/lib" \
+INCLUDES=-I/usr/local/i686-netflix-linux-gnu-4.2/netflix/include \
+make
diff --git a/src/client/dialclient_input.txt b/client/dialclient_input.txt
similarity index 100%
rename from src/client/dialclient_input.txt
rename to client/dialclient_input.txt
diff --git a/client/main.cpp b/client/main.cpp
new file mode 100644
index 0000000..a1a85f0
--- /dev/null
+++ b/client/main.cpp
@@ -0,0 +1,358 @@
+/*
+ * 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 <string>
+#include "DialDiscovery.h"
+#include "DialConformance.h"
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+using namespace std;
+
+static DialDiscovery* gpDiscovery;
+static bool gUseMenu = true;
+
+// TODO: Make it possible to pass applications from the command line
+static vector<string> gAppList;
+static string gOutputFile;
+static string gInputFile;
+
+// IP address of the DIAL server
+static string gIpAddress;
+
+static void printServerList( vector<DialServer*> list )
+{
+    int i;
+    vector<DialServer*>::iterator it;
+    for( i = 0, it = list.begin(); it < list.end(); it++, i++ )
+    {
+        string uuid, name, macAddr;
+        int wolTimeOut;
+        (*it)->getFriendlyName( name );
+        (*it)->getUuid( uuid );
+        macAddr    =(*it)->getMacAddress();
+        wolTimeOut =(*it)->getWakeOnLanTimeout();
+        printf("%Zu: Server IP[%s] UUID[%s] FriendlyName[%s] MacAddress[%s] WakeOnLanTimeout[%d]\n", 
+               i+1, (*it)->getIpAddress().c_str(),
+               uuid.c_str(), name.c_str(), macAddr.c_str(), wolTimeOut);
+    }
+}
+
+static DialServer* getServerFromUser( vector<DialServer*> list )
+{
+    DialServer* pServer;
+    // show a list to the user
+    char buf[80] = {0,};
+    vector<DialServer*>::iterator it;
+
+    printf("Found Multiple servers\n");
+    printf("0: Rescan and list DIAL servers\n");
+    printServerList(list);
+    printf("Enter server: ");
+    scanf("%s", buf);
+    unsigned int server = atoi(buf);
+    if( server > 0 && server <= list.size()){
+        pServer = list[server-1];
+    }else{
+        pServer = NULL;
+    }
+    return pServer;
+}
+
+static void runConformance()
+{
+    vector<DialServer*> list;
+    gpDiscovery->getServerList(list);
+
+    if( list.size() )
+    {
+        DialServer *pServer = NULL;
+        if( !gIpAddress.empty() )
+        {
+            pServer = NULL;
+            vector<DialServer*>::iterator it;
+            for( it = list.begin(); it < list.end(); it ++ )
+            {
+                if( gIpAddress.compare((*it)->getIpAddress()) == 0 )
+                {
+                    ATRACE("Found server %s in the list of servers\n", 
+                                gIpAddress.c_str() );
+                    pServer = (*it);
+                    break;
+                }
+            }
+        }
+        else
+        {
+            pServer = getServerFromUser( list );
+        }
+
+        if( pServer )
+        {
+            string name;
+            bool serverExists = pServer->getFriendlyName(name);
+            assert( serverExists );
+            string uuid;
+            pServer->getUuid( uuid );
+            printf("\nRunning conformance against: IP[%s] UUID[%s] FriendlyName[%s] \n", 
+                    pServer->getIpAddress().c_str(),
+                    uuid.c_str(), name.c_str() );
+            DialConformance::instance()->run( 
+                pServer, 
+                gAppList, 
+                gInputFile, 
+                gOutputFile );
+        }
+        else 
+        {
+            printf("DIAL server not found\n");
+            printf("%Zu available server(s): \n", list.size());
+            printServerList(list);
+        }
+    }
+    else
+    {
+        printf("No servers available\n");
+    }
+}
+
+static void sendMagic(string macAddr)
+{
+    unsigned char tosend[102];
+    unsigned char mac[6];
+
+    /** first 6 bytes of 255 **/
+    for(int i = 0; i < 6; i++) {
+        tosend[i] = 0xFF;
+    }
+
+    /** store mac address **/
+    printf("sending magic packet to: ");
+    for (int i=0; i<6; i++){
+        mac[i]=(unsigned char)(strtoul(macAddr.substr(i*3, 2).c_str(), NULL, 16));
+        printf("%02x:", mac[i]);
+    }
+    printf("\n");
+
+    /** append it 16 times to packet **/
+    for(int i = 1; i <= 16; i++) {
+        memcpy(&tosend[i * 6], &mac, 6 * sizeof(unsigned char));
+    }
+
+    int udpSocket;
+    struct sockaddr_in udpClient, udpServer;
+    int broadcast = 1;
+
+    udpSocket = socket(AF_INET, SOCK_DGRAM, 0);
+
+    /** you need to set this so you can broadcast **/
+    if (setsockopt(udpSocket, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof broadcast) == -1) {
+        perror("setsockopt (SO_BROADCAST)");
+        exit(1);
+    }
+    udpClient.sin_family = AF_INET;
+    udpClient.sin_addr.s_addr = INADDR_ANY;
+    udpClient.sin_port = 0;
+
+    bind(udpSocket, (struct sockaddr*)&udpClient, sizeof(udpClient));
+
+    /** set server end point (the broadcast addres)**/
+    udpServer.sin_family = AF_INET;
+    udpServer.sin_addr.s_addr = inet_addr("255.255.255.255");
+    udpServer.sin_port = htons(9);
+
+    /** send the packet **/
+    sendto(udpSocket, &tosend, sizeof(unsigned char) * 102, 0, (struct sockaddr*)&udpServer, sizeof(udpServer));
+}
+
+
+int handleUser(DialDiscovery *pDial) {
+    int processInputOuter = 1;        
+    char buf[80];
+    vector<DialServer*> list;
+    
+    while (processInputOuter){
+        pDial->getServerList(list);
+        if( list.size() == 0 ){
+            printf("No servers available\n");
+            return 1;
+        }
+        DialServer* pServer = getServerFromUser( list );
+        if (pServer==NULL){
+            pDial->send_mcast();
+            continue;
+        }
+
+        int processInput = 1;        
+        while(processInput){
+            string responseHeaders, responseBody, payload;
+            string netflix = "Netflix";
+            string youtube = "YouTube";
+            
+            memset(buf, 0, 80);
+            printf("0. Rescan and list DIAL servers\n");
+            printf("1. Launch Netflix\n");
+            printf("2. Kill Netflix\n");
+            printf("3. Netflix status\n");
+            printf("4. Launch YouTube\n");
+            printf("5. Kill YouTube\n");
+            printf("6. YouTube status\n");
+            printf("7. Run conformance tests\n");
+            printf("8. Wake up on lan/wlan\n");
+            printf("9. QUIT\n");
+            printf("Command (0:1:2:3:4:5:6:7:8:9): ");
+            scanf("%s", buf);
+            switch( atoi(buf) )
+                {
+                case 0:
+                    {
+                        pDial->send_mcast();
+                        processInput=0;
+                    }break;
+                case 1:
+                    printf("Launch Netflix\n");
+                    pServer->launchApplication( netflix, payload, responseHeaders, responseBody );
+                    break;
+                case 2:
+                    printf("Kill Netflix\n");
+                    pServer->stopApplication( netflix, responseHeaders );
+                    break;
+                case 3:
+                    printf("Netflix Status: \n");
+                    pServer->getStatus( netflix, responseHeaders, responseBody );
+                    printf("RESPONSE: \n%s\n", responseBody.c_str());
+                    break;
+                case 4:
+                    printf("Launch YouTube\n");
+                    pServer->launchApplication( youtube, payload, responseHeaders, responseBody );
+                    break;
+                case 5:
+                    printf("Kill YouTube\n");
+                    pServer->stopApplication( youtube, responseHeaders );
+                    break;
+                case 6:
+                    printf("YouTube Status: \n");
+                    pServer->getStatus( youtube, responseHeaders, responseBody );
+                    break;
+                case 7:
+                    runConformance();
+                    break;
+                case 8:
+                    printf("Sending the magic packet\n");
+                    sendMagic(pServer->getMacAddress());
+                    break;
+                case 9:
+                    processInput = 0;
+                    processInputOuter = 0;
+                    break;
+                default:
+                    printf("Invalid, try again\n");
+                }
+        } 
+    }
+    return 0;
+}
+
+static const char usage[] = "\n"
+" If no option is specified, the program will run a conformance test.\n"
+"\n"
+"usage: dialclient <option>\n"
+" Option Parameter              Description\n"
+" -h     none                   Usage menu\n"
+" -s     filename (optional)    Run conformance test.  Use filename as\n"
+"                               the input, if provided\n"
+" -o     filename               Reporter output file (./report.html)\n"
+" -a     ip_address             IP address of DIAL server (used for conformance\n"
+"                               testing)\n"
+"\n";
+
+inline void notSupported( string s )
+{
+    printf( "%s not supported", s.c_str() );
+    printf( "%s\n", usage );
+    exit(0);
+}
+
+int parseArgs( int argc, char* argv[] )
+{
+    for( int i = 1; i < argc; i++ )
+    {
+        string input(argv[i]);
+        if( input[0] != '-' ) notSupported(input);
+        switch(input[1])
+        {
+        case 's':
+            gUseMenu = false;
+            if( argv[i+1] != NULL && argv[i+1][0] != '-' )
+            {
+                //filename provided
+                gInputFile = argv[++i];
+            }
+            break;
+        case 'o':
+            gOutputFile = argv[++i];
+            break;
+        case 'a':
+            gIpAddress = argv[++i];
+            break;
+        case 'h':
+            printf("%s", usage);
+            exit(0);
+            break;
+        default:
+            notSupported(input);
+        }
+    }
+    return 0;
+}
+
+int main(int argc, char* argv[]) {
+    parseArgs(argc, argv);
+
+    gpDiscovery = DialDiscovery::create();
+    gpDiscovery->init();
+    DialConformance::create();
+
+    // Sleep for 2 seconds to allow DIAL servers to response to MSEARCH.
+    sleep(2);
+
+    if ( gUseMenu )
+    {
+        return handleUser(gpDiscovery);
+    }
+
+    // not using the menu, just run the conformance test.
+    runConformance();
+    return 0;
+}
+
diff --git a/src/client/makefile b/client/makefile
similarity index 100%
rename from src/client/makefile
rename to client/makefile
diff --git a/src/makefile b/makefile
similarity index 100%
rename from src/makefile
rename to makefile
diff --git a/server/LinuxInterfaces.c b/server/LinuxInterfaces.c
new file mode 100644
index 0000000..ba9153a
--- /dev/null
+++ b/server/LinuxInterfaces.c
@@ -0,0 +1,79 @@
+#include "LinuxInterfaces.h"
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/* Fetch the MACAddress of a network interface */
+int getMACAddress(char* ifname, char* macBuf, unsigned int macBufSize)
+{
+    struct ifreq ifr;
+    int sock;
+    unsigned char chMAC[6];
+
+    sock=socket(AF_INET,SOCK_DGRAM,0);
+    strcpy( ifr.ifr_name, ifname );
+    ifr.ifr_addr.sa_family = AF_INET;
+    if (ioctl( sock, SIOCGIFHWADDR, &ifr ) < 0) {
+        close(sock);
+        return -1;
+    }
+    memcpy(chMAC, ifr.ifr_hwaddr.sa_data, 6);
+    if (macBufSize<32){
+        close(sock);
+        return -1;
+    }else{
+        sprintf(macBuf,"%02X:%02X:%02X:%02X:%02X:%02X",chMAC[0],chMAC[1],chMAC[2],chMAC[3],chMAC[4],chMAC[5]);
+    }
+    close(sock);
+    return 0;
+}
+
+bool hasRoute(const char *nf, int flag)
+{
+    bool result = false;
+    const char* fileName = "/proc/net/route";
+    int r;
+    FILE* fp = fopen(fileName, "r");
+
+    if (fp) {
+        char line[1024];
+        int nfBegin = 0, nfEnd = 0, nfFlag = 0;
+        const int nfLen = strlen(nf);
+        while (!feof(fp)) {
+            if (!fgets(line, sizeof(line), fp))
+                break;
+            r = sscanf(line, "%n%*s%n %*s %*s %d %*s %*s %*s %*s %*s %*s %*s", &nfBegin, &nfEnd, &nfFlag);
+            if (r == 1 && nfBegin == 0 && nfEnd == nfLen && !strncmp(nf, line, nfLen) && (flag == -1 || nfFlag == flag)) {
+                result = true;
+                break;
+            }
+        }
+        fclose(fp);
+    }
+    return result;
+}
+
+bool isDefault(char *nf)
+{
+    return hasRoute(nf, 0x03);
+}
+
+NetInterface getDefaultNetworkInterfaces()
+{
+    DIR *d;
+    struct dirent *dir;
+    d = opendir("/sys/class/net/");
+    NetInterface defaultNet;
+    if (d){
+        while ((dir = readdir(d)) != NULL){
+            if(strcmp(dir->d_name,".") && strcmp(dir->d_name,"..")){
+                if (hasRoute(dir->d_name, -1) && isDefault(dir->d_name)){
+                    getMACAddress(dir->d_name, defaultNet.macAddress, MACBUFSIZE);
+                    break;                
+                }
+            }
+        }
+        closedir(d);
+    }
+    return defaultNet;
+}
diff --git a/server/LinuxInterfaces.h b/server/LinuxInterfaces.h
new file mode 100644
index 0000000..1ec41ee
--- /dev/null
+++ b/server/LinuxInterfaces.h
@@ -0,0 +1,26 @@
+#ifndef LINUXINTERFACES_H_
+#define LINUXINTERFACES_H_
+
+#include <arpa/inet.h>
+#include <dirent.h>
+#include <linux/limits.h>
+#include <linux/wireless.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <stdio.h>
+
+/* Fetch the MACAddress of a network interface */
+#define MACBUFSIZE 32
+#define WAKEUPTIMEOUT 15
+
+typedef struct
+{
+    char macAddress[MACBUFSIZE];
+}NetInterface;
+
+NetInterface getDefaultNetworkInterfaces();
+
+#endif 
diff --git a/server/build.sh b/server/build.sh
new file mode 100755
index 0000000..8fc4d16
--- /dev/null
+++ b/server/build.sh
@@ -0,0 +1,2 @@
+export TARGET=/usr/local/i686-netflix-linux-gnu-4.3/bin/i686-netflix-linux-gnu-
+make
diff --git a/src/server/dial_data.c b/server/dial_data.c
similarity index 89%
rename from src/server/dial_data.c
rename to server/dial_data.c
index 9587d1c..b31b5f9 100644
--- a/src/server/dial_data.c
+++ b/server/dial_data.c
@@ -31,15 +31,22 @@
 #include <stdio.h>
 #include <string.h>
 
+
+char dial_data_dir[256] = DIAL_DATA_DIR;
+
+void set_dial_data_dir(const char *data_dir) {
+    strncpy(dial_data_dir, data_dir, 255);
+}
+
 /**
  * Returns the path where data is stored for the given app.
  */
 static char* getAppPath(char app_name[]) {
-    size_t name_size = strlen(app_name) + sizeof(DIAL_DATA_DIR) + 1;
+    size_t name_size = strlen(app_name) + sizeof(dial_data_dir) + 1;
     char* filename = (char*) malloc(name_size);
     filename[0] = 0;
-    strncat(filename, DIAL_DATA_DIR, name_size);
-    strncat(filename, app_name, name_size - sizeof(DIAL_DATA_DIR));
+    strncat(filename, dial_data_dir, name_size);
+    strncat(filename, app_name, name_size - sizeof(dial_data_dir));
     return filename;
 }
 
diff --git a/src/server/dial_data.h b/server/dial_data.h
similarity index 97%
rename from src/server/dial_data.h
rename to server/dial_data.h
index faf5ba2..e7fd9fe 100644
--- a/src/server/dial_data.h
+++ b/server/dial_data.h
@@ -58,4 +58,6 @@
 
 DIALData *retrieve_dial_data(char *app_name);
 
+void set_dial_data_dir(const char *data_dir);
+
 #endif /* SRC_SERVER_DIAL_DATA_H_ */
diff --git a/src/server/dial_options.h b/server/dial_options.h
similarity index 91%
rename from src/server/dial_options.h
rename to server/dial_options.h
index 52bd052..6f6b970 100644
--- a/src/server/dial_options.h
+++ b/server/dial_options.h
@@ -46,6 +46,10 @@
 #define UUID_OPTION_LONG "--uuid-name"
 #define UUID_DESCRIPTION "UUID of the device"
 
+#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)"
+
 struct dial_options
 {
     const char * pOption;
@@ -79,6 +83,11 @@
         UUID_OPTION,
         UUID_OPTION_LONG,
         UUID_DESCRIPTION
+    },
+    {
+        WAKE_OPTION,
+        WAKE_OPTION_LONG,
+        WAKE_DESCRIPTION
     }
 };
 
diff --git a/src/server/dial_server.c b/server/dial_server.c
similarity index 98%
rename from src/server/dial_server.c
rename to server/dial_server.c
index 8cec387..a357907 100644
--- a/src/server/dial_server.c
+++ b/server/dial_server.c
@@ -37,6 +37,7 @@
 
 #include "mongoose.h"
 #include "url_lib.h"
+#include "LinuxInterfaces.h"
 
 // TODO: Partners should define this port
 #define DIAL_PORT (56789)
@@ -143,18 +144,14 @@
             in_port_t dial_port = DIAL_get_port(ds);
 
             if (app->useAdditionalData) {
-                if (body_size != 0) {
-                    strcat(body, "&");
-                }
                 // 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",
                         dial_port, app_name);
-                strcat(body, additional_data_param);
-                body_size = strlen(body);
             }
             fprintf(stderr, "Starting the app with params %s\n", body);
-            app->state = app->callbacks.start_cb(ds, app_name, body, body_size,
+            app->state = app->callbacks.start_cb(ds, app_name, body,
+                                                 additional_data_param, 
                                                  &app->run_id,
                                                  app->callback_data);
             if (app->state == kDIALStatusRunning) {
@@ -217,6 +214,7 @@
         free(encoded_key);
         free(encoded_value);
     }
+
     app->state = app->callbacks.status_cb(ds, app_name, app->run_id, &canStop,
                                           app->callback_data);
     mg_printf(
@@ -235,7 +233,7 @@
             "%s"
             "\n  </additionalData>\n"
             "</service>\r\n",
-            origin_header,
+            origin_header,            
             DIAL_VERSION,
             app->name,
             canStop ? "true" : "false",
@@ -514,7 +512,6 @@
                     free(app_name);
                     return ret;
                 }
-
                 int use_payload =
                     strcmp(request_info->request_method, "POST") ? 0 : 1;
                 handle_dial_data(conn, request_info, app_name, origin_header,
diff --git a/src/server/dial_server.h b/server/dial_server.h
similarity index 95%
rename from src/server/dial_server.h
rename to server/dial_server.h
index 42721fb..09f68b3 100644
--- a/src/server/dial_server.h
+++ b/server/dial_server.h
@@ -39,7 +39,7 @@
 /*
  * DIAL version that is reported via in the status response.
  */
-#define DIAL_VERSION ("\"1.7\"")
+#define DIAL_VERSION ("\"2.0\"")
 
 /*
  * The maximum DIAL payload accepted per the DIAL 1.6.1 specification.
@@ -47,6 +47,12 @@
 #define DIAL_MAX_PAYLOAD (4096)
 
 /*
+ * The maximum additionalDataUrl length
+ */
+
+#define DIAL_MAX_ADDITIONALURL (1024)
+
+/*
  * Opaque DIAL server handle
  */
 struct DIALServer_;
@@ -61,7 +67,7 @@
  * DIAL start callback
  */
 typedef DIALStatus (*DIAL_app_start_cb)(DIALServer *ds, const char *app_name,
-                                        const char *args, size_t arglen,
+                                        const char *payload, const char *additionalDataUrl,
                                         DIAL_run_t *run_id, void *callback_data);
 /*
  * DIAL stop callback
diff --git a/src/server/main.c b/server/main.c
similarity index 86%
rename from src/server/main.c
rename to server/main.c
index 51e1fba..0a7af31 100644
--- a/src/server/main.c
+++ b/server/main.c
@@ -38,6 +38,7 @@
 #include "dial_server.h"
 #include "dial_options.h"
 #include <signal.h>
+#include <stdbool.h>
 
 #define BUFSIZE 256
 
@@ -54,6 +55,7 @@
 static char spFriendlyName[BUFSIZE];
 static char spModelName[BUFSIZE];
 static char spUuid[BUFSIZE];
+extern bool wakeOnWifiLan;
 static int gDialPort;
 
 static char *spAppYouTube = "chrome";
@@ -65,7 +67,7 @@
     "(PS3, , no, CH)";
 
 // Adding 20 bytes for prepended source_type for Netflix
-static char sQueryParam[DIAL_MAX_PAYLOAD+20];
+static char sQueryParam[DIAL_MAX_PAYLOAD+DIAL_MAX_ADDITIONALURL+40];
 
 static int doesMatch( char* pzExp, char* pzStr)
 {
@@ -208,7 +210,10 @@
       for(int i = 0; args[i]; ++i) {
         printf(" %d) %s\n", i, args[i]);
       }
-      execv(*args, (char * const *) args);
+      if( execv(*args, (char * const *) args) == -1) {
+		printf("%s failed to launch\n", *args);
+		perror("Failed to Launch \n");
+      }
     } else {
       *run_id = (void *)(long)pid; // parent PID
     }
@@ -232,12 +237,18 @@
 }
 
 static DIALStatus youtube_start(DIALServer *ds, const char *appname,
-                                const char *args, size_t arglen,
+                                const char *payload, const char *additionalDataUrl,
                                 DIAL_run_t *run_id, void *callback_data) {
-    printf("\n\n ** LAUNCH YouTube ** with args %s\n\n", args);
+    printf("\n\n ** LAUNCH YouTube ** with payload %s\n\n", payload);
 
     char url[512] = {0,}, data[512] = {0,};
-    sprintf( url, "https://www.youtube.com/tv?%s", args);
+    if (strlen(payload) && strlen(additionalDataUrl)){
+        sprintf( url, "https://www.youtube.com/tv?%s&%s", payload, additionalDataUrl);
+    }else if (strlen(payload)){
+        sprintf( url, "https://www.youtube.com/tv?%s", payload);
+    }else{
+        sprintf( url, "https://www.youtube.com/tv");
+    }
     sprintf( data, "--user-data-dir=%s/.config/google-chrome-dial", getenv("HOME") );
 
     const char * const youtube_args[] = { spAppYouTubeExecutable,
@@ -266,24 +277,22 @@
 }
 
 static DIALStatus netflix_start(DIALServer *ds, const char *appname,
-                                const char *args, size_t arglen,
+                                const char *payload, const char *additionalDataUrl,
                                 DIAL_run_t *run_id, void *callback_data) {
     int shouldRelaunchApp = 0;
-    int payloadLen = 0;
     int appPid = 0;
 
     // only launch Netflix if it isn't running
     appPid = isAppRunning( spAppNetflix, NULL );
-    shouldRelaunchApp = shouldRelaunch( ds, appname, args );
+    shouldRelaunchApp = shouldRelaunch( ds, appname, payload );
 
     // construct the payload to determine if it has changed from the previous launch
-    payloadLen = strlen(args);
-    memset( sQueryParam, 0, DIAL_MAX_PAYLOAD );
+    memset( sQueryParam, 0, sizeof(sQueryParam) );
     strcat( sQueryParam, defaultLaunchParam );
-    if( payloadLen )
+    if(strlen(payload))
     {
         char * pUrlEncodedParams;
-        pUrlEncodedParams = url_encode( args );
+        pUrlEncodedParams = url_encode( payload );
         if( pUrlEncodedParams )
         {
             strcat( sQueryParam, "&dial=");
@@ -292,6 +301,11 @@
         }
     }
 
+    if(strlen(additionalDataUrl)){
+        strcat(sQueryParam, "&");
+        strcat(sQueryParam, additionalDataUrl);
+    }
+
     printf("appPid = %s, shouldRelaunch = %s queryParams = %s\n",
           appPid?"TRUE":"FALSE",
           shouldRelaunchApp?"TRUE":"FALSE",
@@ -381,26 +395,37 @@
 {
     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;
-        default:
-            // Should not get here
-            fprintf( stderr, "Option %d not valid\n", 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);
             exit(1);
+        }
+        break;
+    default:
+        // Should not get here
+        fprintf( stderr, "Option %d not valid\n", index);
+        exit(1);
     }
 }
 
diff --git a/src/server/makefile b/server/makefile
similarity index 93%
rename from src/server/makefile
rename to server/makefile
index 8b4d663..ec9cd95 100644
--- a/src/server/makefile
+++ b/server/makefile
@@ -3,7 +3,7 @@
 .PHONY: clean
 .DEFAULT_GOAL=all
 
-OBJS := main.o dial_server.o mongoose.o quick_ssdp.o url_lib.o dial_data.o
+OBJS := main.o dial_server.o mongoose.o quick_ssdp.o url_lib.o dial_data.o LinuxInterfaces.o
 HEADERS := $(wildcard *.h)
 
 %.c: $(HEADERS)
diff --git a/src/server/mongoose.c b/server/mongoose.c
similarity index 99%
rename from src/server/mongoose.c
rename to server/mongoose.c
index a8957ce..cee543e 100644
--- a/src/server/mongoose.c
+++ b/server/mongoose.c
@@ -1,953 +1,956 @@
-// Copyright (c) 2004-2010 Sergey Lyubka

-//

-// Permission is hereby granted, free of charge, to any person obtaining a copy

-// of this software and associated documentation files (the "Software"), to deal

-// in the Software without restriction, including without limitation the rights

-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell

-// copies of the Software, and to permit persons to whom the Software is

-// furnished to do so, subject to the following conditions:

-//

-// The above copyright notice and this permission notice shall be included in

-// all copies or substantial portions of the Software.

-//

-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR

-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,

-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE

-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER

-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,

-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN

-// THE SOFTWARE.

-

-#include <sys/types.h>

-#include <sys/stat.h>

-#include <errno.h>

-#include <signal.h>

-#include <fcntl.h>

-#include <time.h>

-#include <stdlib.h>

-#include <stdarg.h>

-#include <assert.h>

-#include <string.h>

-#include <ctype.h>

-#include <limits.h>

-#include <stddef.h>

-#include <stdio.h>

-

-#ifndef BUFSIZ

-#define BUFSIZ  4096

-#endif

-

-#define MAX_REQUEST_SIZE 4096

-#define NUM_THREADS 4

-#include <sys/wait.h>

-#include <sys/socket.h>

-#include <sys/select.h>

-#include <netinet/in.h>

-#include <arpa/inet.h>

-#include <sys/time.h>

-#include <stdint.h>

-#include <inttypes.h>

-#include <netdb.h>

-#include <unistd.h>

-#include <pthread.h>

-

-

-#define ERRNO errno

-#define INVALID_SOCKET (-1)

-

-typedef int SOCKET;

-

-#include "mongoose.h"

-

-#define MONGOOSE_VERSION "3.0"

-#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))

-

-#if defined(DEBUG)

-#define DEBUG_TRACE(x) do { \

-  flockfile(stdout); \

-  printf("*** %lu.%p.%s.%d: ", \

-         (unsigned long) time(NULL), (void *) pthread_self(), \

-         __func__, __LINE__); \

-  printf x; \

-  putchar('\n'); \

-  fflush(stdout); \

-  funlockfile(stdout); \

-} while (0)

-#else

-#define DEBUG_TRACE(x)

-#endif // DEBUG

-

-typedef void * (*mg_thread_func_t)(void *);

-

-

-// Describes a socket which was accept()-ed by the master thread and queued for

-// future handling by the worker thread.

-struct socket {

-  SOCKET sock;          // Listening socket

-  struct sockaddr_in local_addr;  // Local socket address

-  struct sockaddr_in remote_addr;  // Remote socket address

-};

-

-struct mg_context {

-  volatile int stop_flag;       // Should we stop event loop

-  mg_callback_t user_callback;  // User-defined callback function

-  void *user_data;              // User-defined data

-

-  SOCKET             local_socket;

-  struct sockaddr_in local_address;

-

-  volatile int num_threads;  // Number of threads

-  pthread_mutex_t mutex;     // Protects (max|num)_threads

-  pthread_cond_t  cond;      // Condvar for tracking workers terminations

-

-  struct socket queue[20];   // Accepted sockets

-  volatile int sq_head;      // Head of the socket queue

-  volatile int sq_tail;      // Tail of the socket queue

-  pthread_cond_t sq_full;    // Singaled when socket is produced

-  pthread_cond_t sq_empty;   // Signaled when socket is consumed

-};

-

-struct mg_connection {

-  struct mg_request_info request_info;

-  struct mg_context *ctx;

-  struct socket client;       // Connected client

-  time_t birth_time;          // Time connection was accepted

-  int64_t num_bytes_sent;     // Total bytes sent to client

-  int64_t content_len;        // Content-Length header value

-  int64_t consumed_content;   // How many bytes of content is already read

-  char *buf;                  // Buffer for received data

-  int buf_size;               // Buffer size

-  int request_len;            // Size of the request + headers in a buffer

-  int data_len;               // Total size of data in a buffer

-};

-

-static void *call_user(struct mg_connection *conn, enum mg_event event) {

-  conn->request_info.user_data = conn->ctx->user_data;

-  return conn->ctx->user_callback == NULL ? NULL :

-    conn->ctx->user_callback(event, conn, &conn->request_info);

-}

-

-// Print error message to the opened error log stream.

-static void cry(struct mg_connection *conn, const char *fmt, ...) {

-  char buf[BUFSIZ];

-  va_list ap;

-

-  va_start(ap, fmt);

-  (void) vsnprintf(buf, sizeof(buf), fmt, ap);

-  va_end(ap);

-

-  // Do not lock when getting the callback value, here and below.

-  // I suppose this is fine, since function cannot disappear in the

-  // same way string option can.

-  conn->request_info.log_message = buf;

-  if (call_user(conn, MG_EVENT_LOG) == NULL) {

-      DEBUG_TRACE(("[%s]", buf));

-  }

-  conn->request_info.log_message = NULL;

-}

-

-// Return fake connection structure. Used for logging, if connection

-// is not applicable at the moment of logging.

-static struct mg_connection *fc(struct mg_context *ctx) {

-  static struct mg_connection fake_connection;

-  fake_connection.ctx = ctx;

-  return &fake_connection;

-}

-

-const char *mg_version(void) {

-  return MONGOOSE_VERSION;

-}

-

-static int lowercase(const char *s) {

-  return tolower(* (const unsigned char *) s);

-}

-

-static int mg_strcasecmp(const char *s1, const char *s2) {

-  int diff;

-

-  do {

-    diff = lowercase(s1++) - lowercase(s2++);

-  } while (diff == 0 && s1[-1] != '\0');

-

-  return diff;

-}

-

-// Like snprintf(), but never returns negative value, or the value

-// that is larger than a supplied buffer.

-// Thanks to Adam Zeldis to pointing snprintf()-caused vulnerability

-// in his audit report.

-static int mg_vsnprintf(struct mg_connection *conn, char *buf, size_t buflen,

-                        const char *fmt, va_list ap) {

-  int n;

-

-  if (buflen == 0)

-    return 0;

-

-  n = vsnprintf(buf, buflen, fmt, ap);

-

-  if (n < 0) {

-    cry(conn, "vsnprintf error");

-    n = 0;

-  } else if (n >= (int) buflen) {

-    cry(conn, "truncating vsnprintf buffer: [%.*s]",

-        n > 200 ? 200 : n, buf);

-    n = (int) buflen - 1;

-  }

-  buf[n] = '\0';

-

-  return n;

-}

-

-static int mg_snprintf(struct mg_connection *conn, char *buf, size_t buflen,

-                       const char *fmt, ...) {

-  va_list ap;

-  int n;

-

-  va_start(ap, fmt);

-  n = mg_vsnprintf(conn, buf, buflen, fmt, ap);

-  va_end(ap);

-

-  return n;

-}

-

-// Skip the characters until one of the delimiters characters found.

-// 0-terminate resulting word. Skip the delimiter and following whitespaces if any.

-// Advance pointer to buffer to the next word. Return found 0-terminated word.

-// Delimiters can be quoted with quotechar.

-static char *skip_quoted(char **buf, const char *delimiters, const char *whitespace, char quotechar) {

-  char *p, *begin_word, *end_word, *end_whitespace;

-

-  begin_word = *buf;

-  end_word = begin_word + strcspn(begin_word, delimiters);

-

-  /* Check for quotechar */

-  if (end_word > begin_word) {

-    p = end_word - 1;

-    while (*p == quotechar) {

-      /* If there is anything beyond end_word, copy it */

-      if (*end_word == '\0') {

-        *p = '\0';

-        break;

-      } else {

-        size_t end_off = strcspn(end_word + 1, delimiters);

-        memmove (p, end_word, end_off + 1);

-        p += end_off; /* p must correspond to end_word - 1 */

-        end_word += end_off + 1;

-      }

-    }

-    for (p++; p < end_word; p++) {

-      *p = '\0';

-    }

-  }

-

-  if (*end_word == '\0') {

-    *buf = end_word;

-  } else {

-    end_whitespace = end_word + 1 + strspn(end_word + 1, whitespace);

-

-    for (p = end_word; p < end_whitespace; p++) {

-      *p = '\0';

-    }

-

-    *buf = end_whitespace;

-  }

-

-  return begin_word;

-}

-

-// Simplified version of skip_quoted without quote char

-// and whitespace == delimiters

-static char *skip(char **buf, const char *delimiters) {

-  return skip_quoted(buf, delimiters, delimiters, 0);

-}

-

-

-// Return HTTP header value, or NULL if not found.

-static const char *get_header(const struct mg_request_info *ri,

-                              const char *name) {

-  int i;

-

-  for (i = 0; i < ri->num_headers; i++)

-    if (!mg_strcasecmp(name, ri->http_headers[i].name))

-      return ri->http_headers[i].value;

-

-  return NULL;

-}

-

-const char *mg_get_header(const struct mg_connection *conn, const char *name) {

-  return get_header(&conn->request_info, name);

-}

-

-static const char *suggest_connection_header(const struct mg_connection *conn) {

-  return "close";

-}

-

-void mg_send_http_error(struct mg_connection *conn, int status,

-                        const char *reason, const char *fmt, ...) {

-  char buf[BUFSIZ];

-  va_list ap;

-  int len;

-

-  conn->request_info.status_code = status;

-

-  buf[0] = '\0';

-  len = 0;

-

-  /* Errors 1xx, 204 and 304 MUST NOT send a body */

-  if (status > 199 && status != 204 && status != 304) {

-    len = mg_snprintf(conn, buf, sizeof(buf), "Error %d: %s", status, reason);

-    cry(conn, "%s", buf);

-    buf[len++] = '\n';

-

-    va_start(ap, fmt);

-    len += mg_vsnprintf(conn, buf + len, sizeof(buf) - len, fmt, ap);

-    va_end(ap);

-  }

-  DEBUG_TRACE(("[%s]", buf));

-

-  mg_printf(conn, "HTTP/1.1 %d %s\r\n"

-      "Content-Type: text/plain\r\n"

-      "Content-Length: %d\r\n"

-      "Connection: %s\r\n\r\n", status, reason, len,

-      suggest_connection_header(conn));

-  conn->num_bytes_sent += mg_printf(conn, "%s", buf);

-}

-

-static int start_thread(struct mg_context *ctx, mg_thread_func_t func,

-                        void *param) {

-  pthread_t thread_id;

-  pthread_attr_t attr;

-  int retval;

-

-  (void) pthread_attr_init(&attr);

-  (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

-  // TODO(lsm): figure out why mongoose dies on Linux if next line is enabled

-  // (void) pthread_attr_setstacksize(&attr, sizeof(struct mg_connection) * 5);

-

-  if ((retval = pthread_create(&thread_id, &attr, func, param)) != 0) {

-    cry(fc(ctx), "%s: %s", __func__, strerror(retval));

-  }

-

-  return retval;

-}

-

-static int set_non_blocking_mode(SOCKET sock) {

-  int flags;

-

-  flags = fcntl(sock, F_GETFL, 0);

-  (void) fcntl(sock, F_SETFL, flags | O_NONBLOCK);

-

-  return 0;

-}

-

-// Write data to the IO channel - opened file descriptor, socket or SSL

-// descriptor. Return number of bytes written.

-static int64_t push(FILE *fp, SOCKET sock, const char *buf, int64_t len) {

-  int64_t sent;

-  int n, k;

-

-  sent = 0;

-  while (sent < len) {

-

-    /* How many bytes we send in this iteration */

-    k = len - sent > INT_MAX ? INT_MAX : (int) (len - sent);

-

-    if (fp != NULL) {

-      n = fwrite(buf + sent, 1, (size_t)k, fp);

-      if (ferror(fp))

-        n = -1;

-    } else {

-      n = send(sock, buf + sent, (size_t)k, 0);

-    }

-

-    if (n < 0)

-      break;

-

-    sent += n;

-  }

-

-  return sent;

-}

-

-// Read from IO channel - opened file descriptor, socket, or SSL descriptor.

-// Return number of bytes read.

-static int pull(SOCKET sock, char *buf, int len) {

-  int nread;

-

-  nread = recv(sock, buf, (size_t) len, 0);

-

-  return nread;

-}

-

-int mg_read(struct mg_connection *conn, void *buf, size_t len) {

-  int n, buffered_len, nread;

-  const char *buffered;

-

-  assert((conn->content_len == -1 && conn->consumed_content == 0) ||

-         conn->consumed_content <= conn->content_len);

-  DEBUG_TRACE(("%p %zu %" PRId64 " %" PRId64, buf, len,

-               conn->content_len, conn->consumed_content));

-  nread = 0;

-  if (conn->consumed_content < conn->content_len) {

-

-    // Adjust number of bytes to read.

-    int64_t to_read = conn->content_len - conn->consumed_content;

-    if (to_read < (int64_t) len) {

-      len = (int) to_read;

-    }

-

-    // How many bytes of data we have buffered in the request buffer?

-    buffered = conn->buf + conn->request_len + conn->consumed_content;

-    buffered_len = conn->data_len - conn->request_len;

-    assert(buffered_len >= 0);

-

-    // Return buffered data back if we haven't done that yet.

-    if (conn->consumed_content < (int64_t) buffered_len) {

-      buffered_len -= (int) conn->consumed_content;

-      if (len < (size_t) buffered_len) {

-        buffered_len = len;

-      }

-      memcpy(buf, buffered, (size_t)buffered_len);

-      len -= buffered_len;

-      buf = (char *) buf + buffered_len;

-      conn->consumed_content += buffered_len;

-      nread = buffered_len;

-    }

-

-    // We have returned all buffered data. Read new data from the remote socket.

-    while (len > 0) {

-      n = pull(conn->client.sock, (char *) buf, (int) len);

-      if (n <= 0) {

-        break;

-      }

-      buf = (char *) buf + n;

-      conn->consumed_content += n;

-      nread += n;

-      len -= n;

-    }

-  }

-  return nread;

-}

-

-int mg_write(struct mg_connection *conn, const void *buf, size_t len) {

-  return (int) push(NULL, conn->client.sock, (const char *) buf, (int64_t) len);

-}

-

-int mg_printf(struct mg_connection *conn, const char *fmt, ...) {

-  char buf[BUFSIZ];

-  int len;

-  va_list ap;

-

-  va_start(ap, fmt);

-  len = mg_vsnprintf(conn, buf, sizeof(buf), fmt, ap);

-  va_end(ap);

-

-  return mg_write(conn, buf, (size_t)len);

-}

-

-// URL-decode input buffer into destination buffer.

-// 0-terminate the destination buffer. Return the length of decoded data.

-// form-url-encoded data differs from URI encoding in a way that it

-// uses '+' as character for space, see RFC 1866 section 8.2.1

-// http://ftp.ics.uci.edu/pub/ietf/html/rfc1866.txt

-static size_t url_decode(const char *src, size_t src_len, char *dst,

-                         size_t dst_len, int is_form_url_encoded) {

-  size_t i, j;

-  int a, b;

-#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')

-

-  for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) {

-    if (src[i] == '%' &&

-        isxdigit(* (const unsigned char *) (src + i + 1)) &&

-        isxdigit(* (const unsigned char *) (src + i + 2))) {

-      a = tolower(* (const unsigned char *) (src + i + 1));

-      b = tolower(* (const unsigned char *) (src + i + 2));

-      dst[j] = (char) ((HEXTOI(a) << 4) | HEXTOI(b));

-      i += 2;

-    } else if (is_form_url_encoded && src[i] == '+') {

-      dst[j] = ' ';

-    } else {

-      dst[j] = src[i];

-    }

-  }

-

-  dst[j] = '\0'; /* Null-terminate the destination */

-

-  return j;

-}

-

-// Check whether full request is buffered. Return:

-//   -1  if request is malformed

-//    0  if request is not yet fully buffered

-//   >0  actual request length, including last \r\n\r\n

-static int get_request_len(const char *buf, int buflen) {

-  const char *s, *e;

-  int len = 0;

-

-  DEBUG_TRACE(("buf: %p, len: %d", buf, buflen));

-  for (s = buf, e = s + buflen - 1; len <= 0 && s < e; s++)

-    // Control characters are not allowed but >=128 is.

-    if (!isprint(* (const unsigned char *) s) && *s != '\r' &&

-        *s != '\n' && * (const unsigned char *) s < 128) {

-      len = -1;

-    } else if (s[0] == '\n' && s[1] == '\n') {

-      len = (int) (s - buf) + 2;

-    } else if (s[0] == '\n' && &s[1] < e &&

-        s[1] == '\r' && s[2] == '\n') {

-      len = (int) (s - buf) + 3;

-    }

-

-  return len;

-}

-

-// Protect against directory disclosure attack by removing '..',

-// excessive '/' and '\' characters

-static void remove_double_dots_and_double_slashes(char *s) {

-  char *p = s;

-

-  while (*s != '\0') {

-    *p++ = *s++;

-    if (s[-1] == '/' || s[-1] == '\\') {

-      // Skip all following slashes and backslashes

-      while (*s == '/' || *s == '\\') {

-        s++;

-      }

-

-      // Skip all double-dots

-      while (*s == '.' && s[1] == '.') {

-        s += 2;

-      }

-    }

-  }

-  *p = '\0';

-}

-

-// Parse HTTP headers from the given buffer, advance buffer to the point

-// where parsing stopped.

-static void parse_http_headers(char **buf, struct mg_request_info *ri) {

-  int i;

-

-  for (i = 0; i < (int) ARRAY_SIZE(ri->http_headers); i++) {

-    ri->http_headers[i].name = skip_quoted(buf, ":", " ", 0);

-    ri->http_headers[i].value = skip(buf, "\r\n");

-    if (ri->http_headers[i].name[0] == '\0')

-      break;

-    ri->num_headers = i + 1;

-  }

-}

-

-static int is_valid_http_method(const char *method) {

-  fprintf(stderr, "Received HTTP method %s\n", method);

-  return !strcmp(method, "GET") || !strcmp(method, "POST") ||

-    !strcmp(method, "DELETE") || !strcmp(method, "OPTIONS");

-}

-

-// Parse HTTP request, fill in mg_request_info structure.

-static int parse_http_request(char *buf, struct mg_request_info *ri) {

-  int status = 0;

-

-  // RFC says that all initial whitespaces should be ingored

-  while (*buf != '\0' && isspace(* (unsigned char *) buf)) {

-    buf++;

-  }

-

-  ri->request_method = skip(&buf, " ");

-  ri->uri = skip(&buf, " ");

-  ri->http_version = skip(&buf, "\r\n");

-

-  if (is_valid_http_method(ri->request_method) &&

-      strncmp(ri->http_version, "HTTP/", 5) == 0) {

-    ri->http_version += 5;   /* Skip "HTTP/" */

-    parse_http_headers(&buf, ri);

-    status = 1;

-  }

-

-  return status;

-}

-

-// Keep reading the input from socket sock

-// into buffer buf, until \r\n\r\n appears in the buffer (which marks the end

-// of HTTP request). Buffer buf may already have some data. The length of the

-// data is stored in nread.  Upon every read operation, increase nread by the

-// number of bytes read.

-static int read_request(SOCKET sock, char *buf, int bufsiz,

-                        int *nread) {

-  int n, request_len;

-

-  request_len = 0;

-  while (*nread < bufsiz && request_len == 0) {

-    n = pull(sock, buf + *nread, bufsiz - *nread);

-    if (n <= 0) {

-      break;

-    } else {

-      *nread += n;

-      request_len = get_request_len(buf, *nread);

-    }

-  }

-

-  return request_len;

-}

-

-// This is the heart of the Mongoose's logic.

-// This function is called when the request is read, parsed and validated,

-// and Mongoose must decide what action to take: serve a file, or

-// a directory, or call embedded function, etcetera.

-static void handle_request(struct mg_connection *conn) {

-  struct mg_request_info *ri = &conn->request_info;

-  int uri_len;

-

-  if ((conn->request_info.query_string = strchr(ri->uri, '?')) != NULL) {

-    * conn->request_info.query_string++ = '\0';

-  }

-  uri_len = strlen(ri->uri);

-  (void) url_decode(ri->uri, (size_t)uri_len, ri->uri, (size_t)(uri_len + 1), 0);

-  remove_double_dots_and_double_slashes(ri->uri);

-

-  DEBUG_TRACE(("%s", ri->uri));

-  if (call_user(conn, MG_NEW_REQUEST) == NULL) {

-    mg_send_http_error(conn, 404, "Not Found", "%s", "File not found");

-  }

-}

-

-static void close_all_listening_sockets(struct mg_context *ctx) {

-  (void) close(ctx->local_socket);

-}

-

-// only reports address of the first listening socket

-int mg_get_listen_addr(struct mg_context *ctx,

-    struct sockaddr *addr, socklen_t *addrlen) {

-  size_t len = sizeof(ctx->local_address);

-  if (*addrlen < len) return 0;

-  *addrlen = len;

-  memcpy(addr, &ctx->local_address, len);

-  return 1;

-}

-

-static int set_ports_option(struct mg_context *ctx, int port) {

-  int reuseaddr = 1, success = 1;

-  socklen_t sock_len = sizeof(ctx->local_address);

-  // MacOS needs that. If we do not zero it, subsequent bind() will fail.

-  memset(&ctx->local_address, 0, sock_len);

-  ctx->local_address.sin_family = AF_INET;

-  ctx->local_address.sin_port = htons((uint16_t) port);

-  ctx->local_address.sin_addr.s_addr = htonl(INADDR_ANY);

-

-  struct timeval tv;

-  tv.tv_sec = 0;

-  tv.tv_usec = 500 * 1000;

-

-  if ((ctx->local_socket = socket(PF_INET, SOCK_STREAM, 6)) == INVALID_SOCKET ||

-      setsockopt(ctx->local_socket, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)) != 0 ||

-      setsockopt(ctx->local_socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) != 0 ||

-      bind(ctx->local_socket, (const struct sockaddr *) &ctx->local_address, sock_len) != 0 ||

-      // TODO(steineldar): Replace 20 (max socket backlog len in connections).

-      listen(ctx->local_socket, 20) != 0) {

-    close(ctx->local_socket);

-    cry(fc(ctx), "%s: cannot bind to port %d: %s", __func__,

-        port, strerror(ERRNO));

-    success = 0;

-  } else if (getsockname(ctx->local_socket, (struct sockaddr *) &ctx->local_address, &sock_len)) {

-    close(ctx->local_socket);

-    cry(fc(ctx), "%s: %s", __func__, strerror(ERRNO));

-    success = 0;

-  }

-

-  if (!success) {

-    ctx->local_socket = INVALID_SOCKET;

-    close_all_listening_sockets(ctx);

-  }

-

-  return success;

-}

-

-

-static void reset_per_request_attributes(struct mg_connection *conn) {

-  struct mg_request_info *ri = &conn->request_info;

-

-  ri->request_method = ri->uri = ri->http_version = NULL;

-  ri->num_headers = 0;

-  ri->status_code = -1;

-

-  conn->num_bytes_sent = conn->consumed_content = 0;

-  conn->content_len = -1;

-  conn->request_len = conn->data_len = 0;

-}

-

-static void close_socket_gracefully(SOCKET sock) {

-  char buf[BUFSIZ];

-  int n;

-

-  // Send FIN to the client

-  (void) shutdown(sock, SHUT_WR);

-  set_non_blocking_mode(sock);

-

-  // Read and discard pending data. If we do not do that and close the

-  // socket, the data in the send buffer may be discarded. This

-  // behaviour is seen on Windows, when client keeps sending data

-  // when server decide to close the connection; then when client

-  // does recv() it gets no data back.

-  do {

-    n = pull(sock, buf, sizeof(buf));

-  } while (n > 0);

-

-  // Now we know that our FIN is ACK-ed, safe to close

-  (void) close(sock);

-}

-

-static void close_connection(struct mg_connection *conn) {

-  if (conn->client.sock != INVALID_SOCKET) {

-    close_socket_gracefully(conn->client.sock);

-  }

-}

-

-static void discard_current_request_from_buffer(struct mg_connection *conn) {

-  int buffered_len, body_len;

-

-  buffered_len = conn->data_len - conn->request_len;

-  assert(buffered_len >= 0);

-

-  if (conn->content_len == -1) {

-    body_len = 0;

-  } else if (conn->content_len < (int64_t) buffered_len) {

-    body_len = (int) conn->content_len;

-  } else {

-    body_len = buffered_len;

-  }

-

-  conn->data_len -= conn->request_len + body_len;

-  memmove(conn->buf, conn->buf + conn->request_len + body_len,

-          (size_t) conn->data_len);

-}

-

-static void process_new_connection(struct mg_connection *conn) {

-  struct mg_request_info *ri = &conn->request_info;

-  const char *cl;

-

-  reset_per_request_attributes(conn);

-

-  // If next request is not pipelined, read it in

-  if ((conn->request_len = get_request_len(conn->buf, conn->data_len)) == 0) {

-    conn->request_len = read_request(conn->client.sock,

-        conn->buf, conn->buf_size, &conn->data_len);

-  }

-  assert(conn->data_len >= conn->request_len);

-  if (conn->request_len == 0 && conn->data_len == conn->buf_size) {

-    mg_send_http_error(conn, 413, "Request Too Large", "");

-    return;

-  } if (conn->request_len <= 0) {

-    return;  // Remote end closed the connection

-  }

-

-  // Nul-terminate the request cause parse_http_request() uses sscanf

-  conn->buf[conn->request_len - 1] = '\0';

-  if (!parse_http_request(conn->buf, ri)) {

-    // Do not put garbage in the access log, just send it back to the client

-    mg_send_http_error(conn, 400, "Bad Request",

-        "Cannot parse HTTP request: [%.*s]", conn->data_len, conn->buf);

-  } else if (strcmp(ri->http_version, "1.0") && strcmp(ri->http_version, "1.1")) {

-    // Request seems valid, but HTTP version is strange

-    mg_send_http_error(conn, 505, "HTTP version not supported", "");

-  } else {

-    // Request is valid, handle it

-    cl = get_header(ri, "Content-Length");

-    conn->content_len = cl == NULL ? -1 : strtoll(cl, NULL, 10);

-    conn->birth_time = time(NULL);

-    handle_request(conn);

-    discard_current_request_from_buffer(conn);

-  }

-}

-

-// Worker threads take accepted socket from the queue

-static int consume_socket(struct mg_context *ctx, struct socket *sp) {

-  (void) pthread_mutex_lock(&ctx->mutex);

-  DEBUG_TRACE(("going idle"));

-

-  // If the queue is empty, wait. We're idle at this point.

-  while (ctx->sq_head == ctx->sq_tail && ctx->stop_flag == 0) {

-    pthread_cond_wait(&ctx->sq_full, &ctx->mutex);

-  }

-  // Master thread could wake us up without putting a socket.

-  // If this happens, it is time to exit.

-  if (ctx->stop_flag) {

-    (void) pthread_mutex_unlock(&ctx->mutex);

-    return 0;

-  }

-  assert(ctx->sq_head > ctx->sq_tail);

-

-  // Copy socket from the queue and increment tail

-  *sp = ctx->queue[ctx->sq_tail % ARRAY_SIZE(ctx->queue)];

-  ctx->sq_tail++;

-  DEBUG_TRACE(("grabbed socket %d, going busy", sp->sock));

-

-  // Wrap pointers if needed

-  while (ctx->sq_tail > (int) ARRAY_SIZE(ctx->queue)) {

-    ctx->sq_tail -= ARRAY_SIZE(ctx->queue);

-    ctx->sq_head -= ARRAY_SIZE(ctx->queue);

-  }

-

-  (void) pthread_cond_signal(&ctx->sq_empty);

-  (void) pthread_mutex_unlock(&ctx->mutex);

-

-  return 1;

-}

-

-

-static void worker_thread(struct mg_context *ctx) {

-  struct mg_connection *conn;

-  // This is the specified request size limit for DIAL requests.  Note that

-  // this will effectively make the request limit one byte *smaller* than the

-  // required in the DIAL specification.

-  int buf_size = MAX_REQUEST_SIZE;

-

-  conn = (struct mg_connection *) calloc(1, sizeof(*conn) + buf_size);

-  conn->buf_size = buf_size;

-  conn->buf = (char *) (conn + 1);

-  assert(conn != NULL);

-

-  while (ctx->stop_flag == 0 && consume_socket(ctx, &conn->client)) {

-    conn->birth_time = time(NULL);

-    conn->ctx = ctx;

-

-    // Fill in IP, port info early so even if SSL setup below fails,

-    // error handler would have the corresponding info.

-    // Thanks to Johannes Winkelmann for the patch.

-    memcpy(&conn->request_info.remote_addr,

-           &conn->client.remote_addr, sizeof(conn->client.remote_addr));

-

-    // Fill in local IP info

-    socklen_t addr_len = sizeof(conn->request_info.local_addr);

-    getsockname(conn->client.sock,

-        (struct sockaddr *) &conn->request_info.local_addr, &addr_len);

-

-    process_new_connection(conn);

-

-    close_connection(conn);

-  }

-  free(conn);

-

-  // Signal master that we're done with connection and exiting

-  (void) pthread_mutex_lock(&ctx->mutex);

-  ctx->num_threads--;

-  (void) pthread_cond_signal(&ctx->cond);

-  assert(ctx->num_threads >= 0);

-  (void) pthread_mutex_unlock(&ctx->mutex);

-

-  DEBUG_TRACE(("exiting"));

-}

-

-// Master thread adds accepted socket to a queue

-static void produce_socket(struct mg_context *ctx, const struct socket *sp) {

-  (void) pthread_mutex_lock(&ctx->mutex);

-

-  // If the queue is full, wait

-  while (ctx->sq_head - ctx->sq_tail >= (int) ARRAY_SIZE(ctx->queue)) {

-    (void) pthread_cond_wait(&ctx->sq_empty, &ctx->mutex);

-  }

-  assert(ctx->sq_head - ctx->sq_tail < (int) ARRAY_SIZE(ctx->queue));

-

-  // Copy socket to the queue and increment head

-  ctx->queue[ctx->sq_head % ARRAY_SIZE(ctx->queue)] = *sp;

-  ctx->sq_head++;

-  DEBUG_TRACE(("queued socket %d", sp->sock));

-

-  (void) pthread_cond_signal(&ctx->sq_full);

-  (void) pthread_mutex_unlock(&ctx->mutex);

-}

-

-

-static void master_thread(struct mg_context *ctx) {

-  struct socket accepted;

-

-  socklen_t sock_len = sizeof(accepted.local_addr);

-  memcpy(&accepted.local_addr, &ctx->local_address, sock_len);

-

-  while (ctx->stop_flag == 0) {

-    memset(&accepted.remote_addr, 0, sock_len);

-

-    accepted.sock = accept(ctx->local_socket,

-        (struct sockaddr *) &accepted.remote_addr, &sock_len);

-

-    if (accepted.sock != INVALID_SOCKET) {

-      // Put accepted socket structure into the queue.

-      DEBUG_TRACE(("accepted socket %d", accepted.sock));

-      produce_socket(ctx, &accepted);

-    }

-  }

-  DEBUG_TRACE(("stopping workers"));

-

-  // Stop signal received: somebody called mg_stop. Quit.

-  close_all_listening_sockets(ctx);

-

-  // Wakeup workers that are waiting for connections to handle.

-  pthread_cond_broadcast(&ctx->sq_full);

-

-  // Wait until all threads finish

-  (void) pthread_mutex_lock(&ctx->mutex);

-  while (ctx->num_threads > 0) {

-    (void) pthread_cond_wait(&ctx->cond, &ctx->mutex);

-  }

-  (void) pthread_mutex_unlock(&ctx->mutex);

-

-  // All threads exited, no sync is needed. Destroy mutex and condvars

-  (void) pthread_mutex_destroy(&ctx->mutex);

-  (void) pthread_cond_destroy(&ctx->cond);

-  (void) pthread_cond_destroy(&ctx->sq_empty);

-  (void) pthread_cond_destroy(&ctx->sq_full);

-

-  // Signal mg_stop() that we're done

-  ctx->stop_flag = 2;

-

-  DEBUG_TRACE(("exiting"));

-}

-

-static void free_context(struct mg_context *ctx) {

-  // Deallocate context itself

-  free(ctx);

-}

-

-void mg_stop(struct mg_context *ctx) {

-  ctx->stop_flag = 1;

-

-  // Wait until mg_fini() stops

-  while (ctx->stop_flag != 2) {

-    // TODO(steineldar): Avoid busy waiting.

-    (void) sleep(0);

-  }

-  free_context(ctx);

-}

-

-struct mg_context *mg_start(mg_callback_t user_callback, void *user_data, int port) {

-  struct mg_context *ctx;

-

-  // Allocate context and initialize reasonable general case defaults.

-  // TODO(lsm): do proper error handling here.

-  ctx = (struct mg_context *) calloc(1, sizeof(*ctx));

-  ctx->user_callback = user_callback;

-  ctx->user_data = user_data;

-

-  if (!set_ports_option(ctx, port)) {

-    free_context(ctx);

-    return NULL;

-  }

-  // Ignore SIGPIPE signal, so if browser cancels the request, it

-  // won't kill the whole process.

-  (void) signal(SIGPIPE, SIG_IGN);

-  (void) pthread_mutex_init(&ctx->mutex, NULL);

-  (void) pthread_cond_init(&ctx->cond, NULL);

-  (void) pthread_cond_init(&ctx->sq_empty, NULL);

-  (void) pthread_cond_init(&ctx->sq_full, NULL);

-

-  // Start master (listening) thread

-  start_thread(ctx, (mg_thread_func_t) master_thread, ctx);

-

-  // Start worker threads

-  for (int i = 0; i < NUM_THREADS; i++) {

-    if (start_thread(ctx, (mg_thread_func_t) worker_thread, ctx) != 0) {

-      cry(fc(ctx), "Cannot start worker thread: %d", ERRNO);

-    } else {

-      ctx->num_threads++;

-    }

-  }

-

-  return ctx;

-}

+// Copyright (c) 2004-2010 Sergey Lyubka
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+#define _GNU_SOURCE 1
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <time.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdio.h>
+
+#ifndef BUFSIZ
+#define BUFSIZ  4096
+#endif
+
+#define MAX_REQUEST_SIZE 4096
+#define NUM_THREADS 4
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/time.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <pthread.h>
+
+
+#define ERRNO errno
+#define INVALID_SOCKET (-1)
+
+typedef int SOCKET;
+
+#include "mongoose.h"
+
+#define MONGOOSE_VERSION "3.0"
+#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
+
+#if defined(DEBUG)
+#define DEBUG_TRACE(x) do { \
+  flockfile(stdout); \
+  printf("*** %lu.%p.%s.%d: ", \
+         (unsigned long) time(NULL), (void *) pthread_self(), \
+         __func__, __LINE__); \
+  printf x; \
+  putchar('\n'); \
+  fflush(stdout); \
+  funlockfile(stdout); \
+} while (0)
+#else
+#define DEBUG_TRACE(x)
+#endif // DEBUG
+
+typedef void * (*mg_thread_func_t)(void *);
+
+
+// Describes a socket which was accept()-ed by the master thread and queued for
+// future handling by the worker thread.
+struct socket {
+  SOCKET sock;          // Listening socket
+  struct sockaddr_in local_addr;  // Local socket address
+  struct sockaddr_in remote_addr;  // Remote socket address
+};
+
+struct mg_context {
+  volatile int stop_flag;       // Should we stop event loop
+  mg_callback_t user_callback;  // User-defined callback function
+  void *user_data;              // User-defined data
+
+  SOCKET             local_socket;
+  struct sockaddr_in local_address;
+
+  volatile int num_threads;  // Number of threads
+  pthread_mutex_t mutex;     // Protects (max|num)_threads
+  pthread_cond_t  cond;      // Condvar for tracking workers terminations
+
+  struct socket queue[20];   // Accepted sockets
+  volatile int sq_head;      // Head of the socket queue
+  volatile int sq_tail;      // Tail of the socket queue
+  pthread_cond_t sq_full;    // Singaled when socket is produced
+  pthread_cond_t sq_empty;   // Signaled when socket is consumed
+};
+
+struct mg_connection {
+  struct mg_request_info request_info;
+  struct mg_context *ctx;
+  struct socket client;       // Connected client
+  time_t birth_time;          // Time connection was accepted
+  int64_t num_bytes_sent;     // Total bytes sent to client
+  int64_t content_len;        // Content-Length header value
+  int64_t consumed_content;   // How many bytes of content is already read
+  char *buf;                  // Buffer for received data
+  int buf_size;               // Buffer size
+  int request_len;            // Size of the request + headers in a buffer
+  int data_len;               // Total size of data in a buffer
+};
+
+static void *call_user(struct mg_connection *conn, enum mg_event event) {
+  conn->request_info.user_data = conn->ctx->user_data;
+  return conn->ctx->user_callback == NULL ? NULL :
+    conn->ctx->user_callback(event, conn, &conn->request_info);
+}
+
+// Print error message to the opened error log stream.
+static void cry(struct mg_connection *conn, const char *fmt, ...) {
+  char buf[BUFSIZ];
+  va_list ap;
+
+  va_start(ap, fmt);
+  (void) vsnprintf(buf, sizeof(buf), fmt, ap);
+  va_end(ap);
+
+  // Do not lock when getting the callback value, here and below.
+  // I suppose this is fine, since function cannot disappear in the
+  // same way string option can.
+  conn->request_info.log_message = buf;
+  if (call_user(conn, MG_EVENT_LOG) == NULL) {
+      DEBUG_TRACE(("[%s]", buf));
+  }
+  conn->request_info.log_message = NULL;
+}
+
+// Return fake connection structure. Used for logging, if connection
+// is not applicable at the moment of logging.
+static struct mg_connection *fc(struct mg_context *ctx) {
+  static struct mg_connection fake_connection;
+  fake_connection.ctx = ctx;
+  return &fake_connection;
+}
+
+const char *mg_version(void) {
+  return MONGOOSE_VERSION;
+}
+
+static int lowercase(const char *s) {
+  return tolower(* (const unsigned char *) s);
+}
+
+static int mg_strcasecmp(const char *s1, const char *s2) {
+  int diff;
+
+  do {
+    diff = lowercase(s1++) - lowercase(s2++);
+  } while (diff == 0 && s1[-1] != '\0');
+
+  return diff;
+}
+
+// Like snprintf(), but never returns negative value, or the value
+// that is larger than a supplied buffer.
+// Thanks to Adam Zeldis to pointing snprintf()-caused vulnerability
+// in his audit report.
+static int mg_vsnprintf(struct mg_connection *conn, char *buf, size_t buflen,
+                        const char *fmt, va_list ap) {
+  int n;
+
+  if (buflen == 0)
+    return 0;
+
+  n = vsnprintf(buf, buflen, fmt, ap);
+
+  if (n < 0) {
+    cry(conn, "vsnprintf error");
+    n = 0;
+  } else if (n >= (int) buflen) {
+    cry(conn, "truncating vsnprintf buffer: [%.*s]",
+        n > 200 ? 200 : n, buf);
+    n = (int) buflen - 1;
+  }
+  buf[n] = '\0';
+
+  return n;
+}
+
+static int mg_snprintf(struct mg_connection *conn, char *buf, size_t buflen,
+                       const char *fmt, ...) {
+  va_list ap;
+  int n;
+
+  va_start(ap, fmt);
+  n = mg_vsnprintf(conn, buf, buflen, fmt, ap);
+  va_end(ap);
+
+  return n;
+}
+
+// Skip the characters until one of the delimiters characters found.
+// 0-terminate resulting word. Skip the delimiter and following whitespaces if any.
+// Advance pointer to buffer to the next word. Return found 0-terminated word.
+// Delimiters can be quoted with quotechar.
+static char *skip_quoted(char **buf, const char *delimiters, const char *whitespace, char quotechar) {
+  char *p, *begin_word, *end_word, *end_whitespace;
+
+  begin_word = *buf;
+  end_word = begin_word + strcspn(begin_word, delimiters);
+
+  /* Check for quotechar */
+  if (end_word > begin_word) {
+    p = end_word - 1;
+    while (*p == quotechar) {
+      /* If there is anything beyond end_word, copy it */
+      if (*end_word == '\0') {
+        *p = '\0';
+        break;
+      } else {
+        size_t end_off = strcspn(end_word + 1, delimiters);
+        memmove (p, end_word, end_off + 1);
+        p += end_off; /* p must correspond to end_word - 1 */
+        end_word += end_off + 1;
+      }
+    }
+    for (p++; p < end_word; p++) {
+      *p = '\0';
+    }
+  }
+
+  if (*end_word == '\0') {
+    *buf = end_word;
+  } else {
+    end_whitespace = end_word + 1 + strspn(end_word + 1, whitespace);
+
+    for (p = end_word; p < end_whitespace; p++) {
+      *p = '\0';
+    }
+
+    *buf = end_whitespace;
+  }
+
+  return begin_word;
+}
+
+// Simplified version of skip_quoted without quote char
+// and whitespace == delimiters
+static char *skip(char **buf, const char *delimiters) {
+  return skip_quoted(buf, delimiters, delimiters, 0);
+}
+
+
+// Return HTTP header value, or NULL if not found.
+static const char *get_header(const struct mg_request_info *ri,
+                              const char *name) {
+  int i;
+
+  for (i = 0; i < ri->num_headers; i++)
+    if (!mg_strcasecmp(name, ri->http_headers[i].name))
+      return ri->http_headers[i].value;
+
+  return NULL;
+}
+
+const char *mg_get_header(const struct mg_connection *conn, const char *name) {
+  return get_header(&conn->request_info, name);
+}
+
+static const char *suggest_connection_header(const struct mg_connection *conn) {
+  return "close";
+}
+
+void mg_send_http_error(struct mg_connection *conn, int status,
+                        const char *reason, const char *fmt, ...) {
+  char buf[BUFSIZ];
+  va_list ap;
+  int len;
+
+  conn->request_info.status_code = status;
+
+  buf[0] = '\0';
+  len = 0;
+
+  /* Errors 1xx, 204 and 304 MUST NOT send a body */
+  if (status > 199 && status != 204 && status != 304) {
+    len = mg_snprintf(conn, buf, sizeof(buf), "Error %d: %s", status, reason);
+    cry(conn, "%s", buf);
+    buf[len++] = '\n';
+
+    va_start(ap, fmt);
+    len += mg_vsnprintf(conn, buf + len, sizeof(buf) - len, fmt, ap);
+    va_end(ap);
+  }
+  DEBUG_TRACE(("[%s]", buf));
+
+  mg_printf(conn, "HTTP/1.1 %d %s\r\n"
+      "Content-Type: text/plain\r\n"
+      "Content-Length: %d\r\n"
+      "Connection: %s\r\n\r\n", status, reason, len,
+      suggest_connection_header(conn));
+  conn->num_bytes_sent += mg_printf(conn, "%s", buf);
+}
+
+static int start_thread(struct mg_context *ctx, mg_thread_func_t func,
+                        void *param) {
+  pthread_t thread_id;
+  pthread_attr_t attr;
+  int retval;
+
+  (void) pthread_attr_init(&attr);
+  (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+  // TODO(lsm): figure out why mongoose dies on Linux if next line is enabled
+  // (void) pthread_attr_setstacksize(&attr, sizeof(struct mg_connection) * 5);
+
+  if ((retval = pthread_create(&thread_id, &attr, func, param)) != 0) {
+    cry(fc(ctx), "%s: %s", __func__, strerror(retval));
+  }
+
+  return retval;
+}
+
+static int set_non_blocking_mode(SOCKET sock) {
+  int flags;
+
+  flags = fcntl(sock, F_GETFL, 0);
+  (void) fcntl(sock, F_SETFL, flags | O_NONBLOCK);
+
+  return 0;
+}
+
+// Write data to the IO channel - opened file descriptor, socket or SSL
+// descriptor. Return number of bytes written.
+static int64_t push(FILE *fp, SOCKET sock, const char *buf, int64_t len) {
+  int64_t sent;
+  int n, k;
+
+  sent = 0;
+  while (sent < len) {
+
+    /* How many bytes we send in this iteration */
+    k = len - sent > INT_MAX ? INT_MAX : (int) (len - sent);
+
+    if (fp != NULL) {
+      n = fwrite(buf + sent, 1, (size_t)k, fp);
+      if (ferror(fp))
+        n = -1;
+    } else {
+      n = send(sock, buf + sent, (size_t)k, 0);
+    }
+
+    if (n < 0)
+      break;
+
+    sent += n;
+  }
+
+  return sent;
+}
+
+// Read from IO channel - opened file descriptor, socket, or SSL descriptor.
+// Return number of bytes read.
+static int pull(SOCKET sock, char *buf, int len) {
+  int nread;
+
+  nread = recv(sock, buf, (size_t) len, 0);
+
+  return nread;
+}
+
+int mg_read(struct mg_connection *conn, void *buf, size_t len) {
+  int n, buffered_len, nread;
+  const char *buffered;
+
+  assert((conn->content_len == -1 && conn->consumed_content == 0) ||
+         conn->consumed_content <= conn->content_len);
+  DEBUG_TRACE(("%p %zu %" PRId64 " %" PRId64, buf, len,
+               conn->content_len, conn->consumed_content));
+  nread = 0;
+  if (conn->consumed_content < conn->content_len) {
+
+    // Adjust number of bytes to read.
+    int64_t to_read = conn->content_len - conn->consumed_content;
+    if (to_read < (int64_t) len) {
+      len = (int) to_read;
+    }
+
+    // How many bytes of data we have buffered in the request buffer?
+    buffered = conn->buf + conn->request_len + conn->consumed_content;
+    buffered_len = conn->data_len - conn->request_len;
+    assert(buffered_len >= 0);
+
+    // Return buffered data back if we haven't done that yet.
+    if (conn->consumed_content < (int64_t) buffered_len) {
+      buffered_len -= (int) conn->consumed_content;
+      if (len < (size_t) buffered_len) {
+        buffered_len = len;
+      }
+      memcpy(buf, buffered, (size_t)buffered_len);
+      len -= buffered_len;
+      buf = (char *) buf + buffered_len;
+      conn->consumed_content += buffered_len;
+      nread = buffered_len;
+    }
+
+    // We have returned all buffered data. Read new data from the remote socket.
+    while (len > 0) {
+      n = pull(conn->client.sock, (char *) buf, (int) len);
+      if (n <= 0) {
+        break;
+      }
+      buf = (char *) buf + n;
+      conn->consumed_content += n;
+      nread += n;
+      len -= n;
+    }
+  }
+  return nread;
+}
+
+int mg_write(struct mg_connection *conn, const void *buf, size_t len) {
+  return (int) push(NULL, conn->client.sock, (const char *) buf, (int64_t) len);
+}
+
+int mg_printf(struct mg_connection *conn, const char *fmt, ...) {
+  char buf[BUFSIZ];
+  int len;
+  va_list ap;
+
+  va_start(ap, fmt);
+  len = mg_vsnprintf(conn, buf, sizeof(buf), fmt, ap);
+  va_end(ap);
+
+  return mg_write(conn, buf, (size_t)len);
+}
+
+// URL-decode input buffer into destination buffer.
+// 0-terminate the destination buffer. Return the length of decoded data.
+// form-url-encoded data differs from URI encoding in a way that it
+// uses '+' as character for space, see RFC 1866 section 8.2.1
+// http://ftp.ics.uci.edu/pub/ietf/html/rfc1866.txt
+static size_t url_decode(const char *src, size_t src_len, char *dst,
+                         size_t dst_len, int is_form_url_encoded) {
+  size_t i, j;
+  int a, b;
+#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
+
+  for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) {
+    if (src[i] == '%' &&
+        isxdigit(* (const unsigned char *) (src + i + 1)) &&
+        isxdigit(* (const unsigned char *) (src + i + 2))) {
+      a = tolower(* (const unsigned char *) (src + i + 1));
+      b = tolower(* (const unsigned char *) (src + i + 2));
+      dst[j] = (char) ((HEXTOI(a) << 4) | HEXTOI(b));
+      i += 2;
+    } else if (is_form_url_encoded && src[i] == '+') {
+      dst[j] = ' ';
+    } else {
+      dst[j] = src[i];
+    }
+  }
+
+  dst[j] = '\0'; /* Null-terminate the destination */
+
+  return j;
+}
+
+// Check whether full request is buffered. Return:
+//   -1  if request is malformed
+//    0  if request is not yet fully buffered
+//   >0  actual request length, including last \r\n\r\n
+static int get_request_len(const char *buf, int buflen) {
+  const char *s, *e;
+  int len = 0;
+
+  DEBUG_TRACE(("buf: %p, len: %d", buf, buflen));
+  for (s = buf, e = s + buflen - 1; len <= 0 && s < e; s++)
+    // Control characters are not allowed but >=128 is.
+    if (!isprint(* (const unsigned char *) s) && *s != '\r' &&
+        *s != '\n' && * (const unsigned char *) s < 128) {
+      len = -1;
+    } else if (s[0] == '\n' && s[1] == '\n') {
+      len = (int) (s - buf) + 2;
+    } else if (s[0] == '\n' && &s[1] < e &&
+        s[1] == '\r' && s[2] == '\n') {
+      len = (int) (s - buf) + 3;
+    }
+
+  return len;
+}
+
+// Protect against directory disclosure attack by removing '..',
+// excessive '/' and '\' characters
+static void remove_double_dots_and_double_slashes(char *s) {
+  char *p = s;
+
+  while (*s != '\0') {
+    *p++ = *s++;
+    if (s[-1] == '/' || s[-1] == '\\') {
+      // Skip all following slashes and backslashes
+      while (*s == '/' || *s == '\\') {
+        s++;
+      }
+
+      // Skip all double-dots
+      while (*s == '.' && s[1] == '.') {
+        s += 2;
+      }
+    }
+  }
+  *p = '\0';
+}
+
+// Parse HTTP headers from the given buffer, advance buffer to the point
+// where parsing stopped.
+static void parse_http_headers(char **buf, struct mg_request_info *ri) {
+  int i;
+
+  for (i = 0; i < (int) ARRAY_SIZE(ri->http_headers); i++) {
+    ri->http_headers[i].name = skip_quoted(buf, ":", " ", 0);
+    ri->http_headers[i].value = skip(buf, "\r\n");
+    if (ri->http_headers[i].name[0] == '\0')
+      break;
+    ri->num_headers = i + 1;
+  }
+}
+
+static int is_valid_http_method(const char *method) {
+  fprintf(stderr, "Received HTTP method %s\n", method);
+  return !strcmp(method, "GET") || !strcmp(method, "POST") ||
+    !strcmp(method, "DELETE") || !strcmp(method, "OPTIONS");
+}
+
+// Parse HTTP request, fill in mg_request_info structure.
+static int parse_http_request(char *buf, struct mg_request_info *ri) {
+  int status = 0;
+
+  // RFC says that all initial whitespaces should be ingored
+  while (*buf != '\0' && isspace(* (unsigned char *) buf)) {
+    buf++;
+  }
+
+  ri->request_method = skip(&buf, " ");
+  ri->uri = skip(&buf, " ");
+  ri->http_version = skip(&buf, "\r\n");
+
+  if (is_valid_http_method(ri->request_method) &&
+      strncmp(ri->http_version, "HTTP/", 5) == 0) {
+    ri->http_version += 5;   /* Skip "HTTP/" */
+    parse_http_headers(&buf, ri);
+    status = 1;
+  }
+
+  return status;
+}
+
+// Keep reading the input from socket sock
+// into buffer buf, until \r\n\r\n appears in the buffer (which marks the end
+// of HTTP request). Buffer buf may already have some data. The length of the
+// data is stored in nread.  Upon every read operation, increase nread by the
+// number of bytes read.
+static int read_request(SOCKET sock, char *buf, int bufsiz,
+                        int *nread) {
+  int n, request_len;
+
+  request_len = 0;
+  while (*nread < bufsiz && request_len == 0) {
+    n = pull(sock, buf + *nread, bufsiz - *nread);
+    if (n <= 0) {
+      break;
+    } else {
+      *nread += n;
+      request_len = get_request_len(buf, *nread);
+    }
+  }
+
+  return request_len;
+}
+
+// This is the heart of the Mongoose's logic.
+// This function is called when the request is read, parsed and validated,
+// and Mongoose must decide what action to take: serve a file, or
+// a directory, or call embedded function, etcetera.
+static void handle_request(struct mg_connection *conn) {
+  struct mg_request_info *ri = &conn->request_info;
+  int uri_len;
+
+  if ((conn->request_info.query_string = strchr(ri->uri, '?')) != NULL) {
+    * conn->request_info.query_string++ = '\0';
+  }
+  uri_len = strlen(ri->uri);
+  (void) url_decode(ri->uri, (size_t)uri_len, ri->uri, (size_t)(uri_len + 1), 0);
+  remove_double_dots_and_double_slashes(ri->uri);
+
+  DEBUG_TRACE(("%s", ri->uri));
+  if (call_user(conn, MG_NEW_REQUEST) == NULL) {
+    mg_send_http_error(conn, 404, "Not Found", "%s", "File not found");
+  }
+}
+
+static void close_all_listening_sockets(struct mg_context *ctx) {
+  (void) close(ctx->local_socket);
+}
+
+// only reports address of the first listening socket
+int mg_get_listen_addr(struct mg_context *ctx,
+    struct sockaddr *addr, socklen_t *addrlen) {
+  size_t len = sizeof(ctx->local_address);
+  if (*addrlen < len) return 0;
+  *addrlen = len;
+  memcpy(addr, &ctx->local_address, len);
+  return 1;
+}
+
+static int set_ports_option(struct mg_context *ctx, int port) {
+  int reuseaddr = 1, success = 1;
+  socklen_t sock_len = sizeof(ctx->local_address);
+  // MacOS needs that. If we do not zero it, subsequent bind() will fail.
+  memset(&ctx->local_address, 0, sock_len);
+  ctx->local_address.sin_family = AF_INET;
+  ctx->local_address.sin_port = htons((uint16_t) port);
+  ctx->local_address.sin_addr.s_addr = htonl(INADDR_ANY);
+
+  struct timeval tv;
+  tv.tv_sec = 0;
+  tv.tv_usec = 500 * 1000;
+
+  if ((ctx->local_socket = socket(PF_INET, SOCK_STREAM, 6)) == INVALID_SOCKET ||
+      setsockopt(ctx->local_socket, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)) != 0 ||
+      setsockopt(ctx->local_socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) != 0 ||
+      bind(ctx->local_socket, (const struct sockaddr *) &ctx->local_address, sock_len) != 0 ||
+      // TODO(steineldar): Replace 20 (max socket backlog len in connections).
+      listen(ctx->local_socket, 20) != 0) {
+    close(ctx->local_socket);
+    cry(fc(ctx), "%s: cannot bind to port %d: %s", __func__,
+        port, strerror(ERRNO));
+    success = 0;
+  } else if (getsockname(ctx->local_socket, (struct sockaddr *) &ctx->local_address, &sock_len)) {
+    close(ctx->local_socket);
+    cry(fc(ctx), "%s: %s", __func__, strerror(ERRNO));
+    success = 0;
+  }
+
+  if (!success) {
+    ctx->local_socket = INVALID_SOCKET;
+    close_all_listening_sockets(ctx);
+  }
+
+  return success;
+}
+
+
+static void reset_per_request_attributes(struct mg_connection *conn) {
+  struct mg_request_info *ri = &conn->request_info;
+
+  ri->request_method = ri->uri = ri->http_version = NULL;
+  ri->num_headers = 0;
+  ri->status_code = -1;
+
+  conn->num_bytes_sent = conn->consumed_content = 0;
+  conn->content_len = -1;
+  conn->request_len = conn->data_len = 0;
+}
+
+static void close_socket_gracefully(SOCKET sock) {
+  char buf[BUFSIZ];
+  int n;
+
+  // Send FIN to the client
+  (void) shutdown(sock, SHUT_WR);
+  set_non_blocking_mode(sock);
+
+  // Read and discard pending data. If we do not do that and close the
+  // socket, the data in the send buffer may be discarded. This
+  // behaviour is seen on Windows, when client keeps sending data
+  // when server decide to close the connection; then when client
+  // does recv() it gets no data back.
+  do {
+    n = pull(sock, buf, sizeof(buf));
+  } while (n > 0);
+
+  // Now we know that our FIN is ACK-ed, safe to close
+  (void) close(sock);
+}
+
+static void close_connection(struct mg_connection *conn) {
+  if (conn->client.sock != INVALID_SOCKET) {
+    close_socket_gracefully(conn->client.sock);
+  }
+}
+
+static void discard_current_request_from_buffer(struct mg_connection *conn) {
+  int buffered_len, body_len;
+
+  buffered_len = conn->data_len - conn->request_len;
+  assert(buffered_len >= 0);
+
+  if (conn->content_len == -1) {
+    body_len = 0;
+  } else if (conn->content_len < (int64_t) buffered_len) {
+    body_len = (int) conn->content_len;
+  } else {
+    body_len = buffered_len;
+  }
+
+  conn->data_len -= conn->request_len + body_len;
+  memmove(conn->buf, conn->buf + conn->request_len + body_len,
+          (size_t) conn->data_len);
+}
+
+static void process_new_connection(struct mg_connection *conn) {
+  struct mg_request_info *ri = &conn->request_info;
+  const char *cl;
+
+  reset_per_request_attributes(conn);
+
+  // If next request is not pipelined, read it in
+  if ((conn->request_len = get_request_len(conn->buf, conn->data_len)) == 0) {
+    conn->request_len = read_request(conn->client.sock,
+        conn->buf, conn->buf_size, &conn->data_len);
+  }
+  assert(conn->data_len >= conn->request_len);
+  if (conn->request_len == 0 && conn->data_len == conn->buf_size) {
+    mg_send_http_error(conn, 413, "Request Too Large", "");
+    return;
+  } if (conn->request_len <= 0) {
+    return;  // Remote end closed the connection
+  }
+
+  // Nul-terminate the request cause parse_http_request() uses sscanf
+  conn->buf[conn->request_len - 1] = '\0';
+  if (!parse_http_request(conn->buf, ri)) {
+    // Do not put garbage in the access log, just send it back to the client
+    mg_send_http_error(conn, 400, "Bad Request",
+        "Cannot parse HTTP request: [%.*s]", conn->data_len, conn->buf);
+  } else if (strcmp(ri->http_version, "1.0") && strcmp(ri->http_version, "1.1")) {
+    // Request seems valid, but HTTP version is strange
+    mg_send_http_error(conn, 505, "HTTP version not supported", "");
+  } else {
+    // Request is valid, handle it
+    cl = get_header(ri, "Content-Length");
+    conn->content_len = cl == NULL ? -1 : strtoll(cl, NULL, 10);
+    conn->birth_time = time(NULL);
+    handle_request(conn);
+    discard_current_request_from_buffer(conn);
+  }
+}
+
+// Worker threads take accepted socket from the queue
+static int consume_socket(struct mg_context *ctx, struct socket *sp) {
+  (void) pthread_mutex_lock(&ctx->mutex);
+  DEBUG_TRACE(("going idle"));
+
+  // If the queue is empty, wait. We're idle at this point.
+  while (ctx->sq_head == ctx->sq_tail && ctx->stop_flag == 0) {
+    pthread_cond_wait(&ctx->sq_full, &ctx->mutex);
+  }
+  // Master thread could wake us up without putting a socket.
+  // If this happens, it is time to exit.
+  if (ctx->stop_flag) {
+    (void) pthread_mutex_unlock(&ctx->mutex);
+    return 0;
+  }
+  assert(ctx->sq_head > ctx->sq_tail);
+
+  // Copy socket from the queue and increment tail
+  *sp = ctx->queue[ctx->sq_tail % ARRAY_SIZE(ctx->queue)];
+  ctx->sq_tail++;
+  DEBUG_TRACE(("grabbed socket %d, going busy", sp->sock));
+
+  // Wrap pointers if needed
+  while (ctx->sq_tail > (int) ARRAY_SIZE(ctx->queue)) {
+    ctx->sq_tail -= ARRAY_SIZE(ctx->queue);
+    ctx->sq_head -= ARRAY_SIZE(ctx->queue);
+  }
+
+  (void) pthread_cond_signal(&ctx->sq_empty);
+  (void) pthread_mutex_unlock(&ctx->mutex);
+
+  return 1;
+}
+
+
+static void worker_thread(struct mg_context *ctx) {
+  struct mg_connection *conn;
+  // This is the specified request size limit for DIAL requests.  Note that
+  // this will effectively make the request limit one byte *smaller* than the
+  // required in the DIAL specification.
+  int buf_size = MAX_REQUEST_SIZE;
+
+  pthread_setname_np( pthread_self(), __func__);
+  conn = (struct mg_connection *) calloc(1, sizeof(*conn) + buf_size);
+  conn->buf_size = buf_size;
+  conn->buf = (char *) (conn + 1);
+  assert(conn != NULL);
+
+  while (ctx->stop_flag == 0 && consume_socket(ctx, &conn->client)) {
+    conn->birth_time = time(NULL);
+    conn->ctx = ctx;
+
+    // Fill in IP, port info early so even if SSL setup below fails,
+    // error handler would have the corresponding info.
+    // Thanks to Johannes Winkelmann for the patch.
+    memcpy(&conn->request_info.remote_addr,
+           &conn->client.remote_addr, sizeof(conn->client.remote_addr));
+
+    // Fill in local IP info
+    socklen_t addr_len = sizeof(conn->request_info.local_addr);
+    getsockname(conn->client.sock,
+        (struct sockaddr *) &conn->request_info.local_addr, &addr_len);
+
+    process_new_connection(conn);
+
+    close_connection(conn);
+  }
+  free(conn);
+
+  // Signal master that we're done with connection and exiting
+  (void) pthread_mutex_lock(&ctx->mutex);
+  ctx->num_threads--;
+  (void) pthread_cond_signal(&ctx->cond);
+  assert(ctx->num_threads >= 0);
+  (void) pthread_mutex_unlock(&ctx->mutex);
+
+  DEBUG_TRACE(("exiting"));
+}
+
+// Master thread adds accepted socket to a queue
+static void produce_socket(struct mg_context *ctx, const struct socket *sp) {
+  (void) pthread_mutex_lock(&ctx->mutex);
+
+  // If the queue is full, wait
+  while (ctx->sq_head - ctx->sq_tail >= (int) ARRAY_SIZE(ctx->queue)) {
+    (void) pthread_cond_wait(&ctx->sq_empty, &ctx->mutex);
+  }
+  assert(ctx->sq_head - ctx->sq_tail < (int) ARRAY_SIZE(ctx->queue));
+
+  // Copy socket to the queue and increment head
+  ctx->queue[ctx->sq_head % ARRAY_SIZE(ctx->queue)] = *sp;
+  ctx->sq_head++;
+  DEBUG_TRACE(("queued socket %d", sp->sock));
+
+  (void) pthread_cond_signal(&ctx->sq_full);
+  (void) pthread_mutex_unlock(&ctx->mutex);
+}
+
+
+static void master_thread(struct mg_context *ctx) {
+  struct socket accepted;
+
+  pthread_setname_np( pthread_self(), __func__);
+  socklen_t sock_len = sizeof(accepted.local_addr);
+  memcpy(&accepted.local_addr, &ctx->local_address, sock_len);
+
+  while (ctx->stop_flag == 0) {
+    memset(&accepted.remote_addr, 0, sock_len);
+
+    accepted.sock = accept(ctx->local_socket,
+        (struct sockaddr *) &accepted.remote_addr, &sock_len);
+
+    if (accepted.sock != INVALID_SOCKET) {
+      // Put accepted socket structure into the queue.
+      DEBUG_TRACE(("accepted socket %d", accepted.sock));
+      produce_socket(ctx, &accepted);
+    }
+  }
+  DEBUG_TRACE(("stopping workers"));
+
+  // Stop signal received: somebody called mg_stop. Quit.
+  close_all_listening_sockets(ctx);
+
+  // Wakeup workers that are waiting for connections to handle.
+  pthread_cond_broadcast(&ctx->sq_full);
+
+  // Wait until all threads finish
+  (void) pthread_mutex_lock(&ctx->mutex);
+  while (ctx->num_threads > 0) {
+    (void) pthread_cond_wait(&ctx->cond, &ctx->mutex);
+  }
+  (void) pthread_mutex_unlock(&ctx->mutex);
+
+  // All threads exited, no sync is needed. Destroy mutex and condvars
+  (void) pthread_mutex_destroy(&ctx->mutex);
+  (void) pthread_cond_destroy(&ctx->cond);
+  (void) pthread_cond_destroy(&ctx->sq_empty);
+  (void) pthread_cond_destroy(&ctx->sq_full);
+
+  // Signal mg_stop() that we're done
+  ctx->stop_flag = 2;
+
+  DEBUG_TRACE(("exiting"));
+}
+
+static void free_context(struct mg_context *ctx) {
+  // Deallocate context itself
+  free(ctx);
+}
+
+void mg_stop(struct mg_context *ctx) {
+  ctx->stop_flag = 1;
+
+  // Wait until mg_fini() stops
+  while (ctx->stop_flag != 2) {
+    // TODO(steineldar): Avoid busy waiting.
+    (void) sleep(0);
+  }
+  free_context(ctx);
+}
+
+struct mg_context *mg_start(mg_callback_t user_callback, void *user_data, int port) {
+  struct mg_context *ctx;
+
+  // Allocate context and initialize reasonable general case defaults.
+  // TODO(lsm): do proper error handling here.
+  ctx = (struct mg_context *) calloc(1, sizeof(*ctx));
+  ctx->user_callback = user_callback;
+  ctx->user_data = user_data;
+
+  if (!set_ports_option(ctx, port)) {
+    free_context(ctx);
+    return NULL;
+  }
+  // Ignore SIGPIPE signal, so if browser cancels the request, it
+  // won't kill the whole process.
+  (void) signal(SIGPIPE, SIG_IGN);
+  (void) pthread_mutex_init(&ctx->mutex, NULL);
+  (void) pthread_cond_init(&ctx->cond, NULL);
+  (void) pthread_cond_init(&ctx->sq_empty, NULL);
+  (void) pthread_cond_init(&ctx->sq_full, NULL);
+
+  // Start master (listening) thread
+  start_thread(ctx, (mg_thread_func_t) master_thread, ctx);
+
+  // Start worker threads
+  for (int i = 0; i < NUM_THREADS; i++) {
+    if (start_thread(ctx, (mg_thread_func_t) worker_thread, ctx) != 0) {
+      cry(fc(ctx), "Cannot start worker thread: %d", ERRNO);
+    } else {
+      ctx->num_threads++;
+    }
+  }
+
+  return ctx;
+}
diff --git a/src/server/mongoose.h b/server/mongoose.h
similarity index 100%
rename from src/server/mongoose.h
rename to server/mongoose.h
diff --git a/server/quick_ssdp.c b/server/quick_ssdp.c
new file mode 100644
index 0000000..689419e
--- /dev/null
+++ b/server/quick_ssdp.c
@@ -0,0 +1,253 @@
+/*
+* 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>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include "mongoose.h"
+#include <stdbool.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;
+
+// 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";
+
+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];
+static char uuid[256];
+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"
+                      "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";
+    }
+    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);
+    }
+    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);
+    }
+    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");
+#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;
+        }
+    }
+}
+
+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();
+}
diff --git a/src/server/tests/makefile b/server/tests/makefile
similarity index 100%
rename from src/server/tests/makefile
rename to server/tests/makefile
diff --git a/src/server/tests/run_tests.c b/server/tests/run_tests.c
similarity index 100%
rename from src/server/tests/run_tests.c
rename to server/tests/run_tests.c
diff --git a/src/server/tests/test.h b/server/tests/test.h
similarity index 100%
rename from src/server/tests/test.h
rename to server/tests/test.h
diff --git a/src/server/tests/test_cors.html b/server/tests/test_cors.html
similarity index 100%
rename from src/server/tests/test_cors.html
rename to server/tests/test_cors.html
diff --git a/src/server/tests/test_cors.sh b/server/tests/test_cors.sh
similarity index 100%
rename from src/server/tests/test_cors.sh
rename to server/tests/test_cors.sh
diff --git a/src/server/tests/test_dial_data.c b/server/tests/test_dial_data.c
similarity index 100%
rename from src/server/tests/test_dial_data.c
rename to server/tests/test_dial_data.c
diff --git a/src/server/tests/test_dial_data.h b/server/tests/test_dial_data.h
similarity index 100%
rename from src/server/tests/test_dial_data.h
rename to server/tests/test_dial_data.h
diff --git a/src/server/tests/test_url_lib.c b/server/tests/test_url_lib.c
similarity index 100%
rename from src/server/tests/test_url_lib.c
rename to server/tests/test_url_lib.c
diff --git a/src/server/tests/test_url_lib.h b/server/tests/test_url_lib.h
similarity index 100%
rename from src/server/tests/test_url_lib.h
rename to server/tests/test_url_lib.h
diff --git a/src/server/url_lib.c b/server/url_lib.c
similarity index 98%
rename from src/server/url_lib.c
rename to server/url_lib.c
index 498b221..1ad45a5 100644
--- a/src/server/url_lib.c
+++ b/server/url_lib.c
@@ -130,8 +130,9 @@
     while ((begin != uri) && (*--begin != '/'))
         ;
     begin++;  // skip the slash
-    char *result = (char *) calloc(1, slash - begin);
+    char *result = (char *) calloc(1, slash - begin+1);
     strncpy(result, begin, slash - begin);
+    result[slash-begin]='\0';
     return result;
 }
 
diff --git a/src/server/url_lib.h b/server/url_lib.h
similarity index 100%
rename from src/server/url_lib.h
rename to server/url_lib.h
diff --git a/src/client/main.cpp b/src/client/main.cpp
deleted file mode 100644
index c882207..0000000
--- a/src/client/main.cpp
+++ /dev/null
@@ -1,298 +0,0 @@
-/*
- * 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 <string>
-#include "DialDiscovery.h"
-#include "DialConformance.h"
-#include <assert.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-using namespace std;
-
-static DialDiscovery* gpDiscovery;
-static bool gUseMenu = true;
-
-// TODO: Make it possible to pass applications from the command line
-static vector<string> gAppList;
-static string gOutputFile;
-static string gInputFile;
-
-// IP address of the DIAL server
-static string gIpAddress;
-
-static void printServerList( vector<DialServer*> list )
-{
-    int i;
-    vector<DialServer*>::iterator it;
-    for( i = 0, it = list.begin(); it < list.end(); it++, i++ )
-    {
-        string uuid, name;
-        (*it)->getFriendlyName( name );
-        (*it)->getUuid( uuid );
-        printf("%Zu: Server IP[%s] UUID[%s] FriendlyName[%s] \n", 
-            i+1, (*it)->getIpAddress().c_str(),
-            uuid.c_str(), name.c_str() );
-    }
-}
-
-static DialServer* getServerFromUser( vector<DialServer*> list )
-{
-    DialServer* pServer;
-    // show a list to the user
-    if( list.size() > 1 )
-    {
-        char buf[80] = {0,};
-        vector<DialServer*>::iterator it;
-
-        printf("Found Multiple servers\n");
-        printServerList(list);
-        printf("Enter server: ");
-        scanf("%s", buf);
-        unsigned int server = atoi(buf);
-        assert( server > 0 && server <= list.size() );
-        pServer = list[server-1];
-    }
-    else
-    {
-        pServer = list.front();
-    }
-    return pServer;
-}
-
-static void runConformance()
-{
-    vector<DialServer*> list;
-    gpDiscovery->getServerList(list);
-
-    if( list.size() )
-    {
-        DialServer *pServer = NULL;
-        if( !gIpAddress.empty() )
-        {
-            pServer = NULL;
-            vector<DialServer*>::iterator it;
-            for( it = list.begin(); it < list.end(); it ++ )
-            {
-                if( gIpAddress.compare((*it)->getIpAddress()) == 0 )
-                {
-                    ATRACE("Found server %s in the list of servers\n", 
-                                gIpAddress.c_str() );
-                    pServer = (*it);
-                    break;
-                }
-            }
-        }
-        else
-        {
-            pServer = getServerFromUser( list );
-        }
-
-        if( pServer )
-        {
-            string name;
-            bool serverExists = pServer->getFriendlyName(name);
-            assert( serverExists );
-            string uuid;
-            pServer->getUuid( uuid );
-            printf("\nRunning conformance against: IP[%s] UUID[%s] FriendlyName[%s] \n", 
-                    pServer->getIpAddress().c_str(),
-                    uuid.c_str(), name.c_str() );
-            DialConformance::instance()->run( 
-                pServer, 
-                gAppList, 
-                gInputFile, 
-                gOutputFile );
-        }
-        else 
-        {
-            printf("DIAL server not found\n");
-            printf("%Zu available server(s): \n", list.size());
-            printServerList(list);
-        }
-    }
-    else
-    {
-        printf("No servers available\n");
-    }
-}
-
-int handleUser(DialDiscovery *pDial) {
-    int processInput = 1;
-    char buf[80];
-    vector<DialServer*> list;
-
-    pDial->getServerList(list);
-    if( list.size() == 0 )
-    {
-        printf("No servers available\n");
-        return 1;
-    }
-    DialServer* pServer = getServerFromUser( list );
-
-    while(processInput)
-    {
-        string responseHeaders, responseBody, payload;
-        string netflix = "Netflix";
-        string youtube = "YouTube";
-
-        memset(buf, 0, 80);
-        printf("0. List DIAL servers\n");
-        printf("1. Launch Netflix\n");
-        printf("2. Kill Netflix\n");
-        printf("3. Netflix status\n");
-        printf("4. Launch YouTube\n");
-        printf("5. Kill YouTube\n");
-        printf("6. YouTube status\n");
-        printf("7. Run conformance tests\n");
-        printf("8. QUIT\n");
-        printf("Command (0:1:2:3:4:5:6:7:8): ");
-        scanf("%s", buf);
-        switch( atoi(buf) )
-        {
-            case 0:
-            {
-                printf("\n\n******** %Zu servers found ********\n\n", list.size());
-                for( unsigned int i = 0; i < list.size(); i++ )
-                {
-                    string name;
-                    list[i]->getFriendlyName(name);
-                    printf("Server %Zu: %s\n", i+1, name.c_str());
-                }
-                printf("\n*********************************\n\n");
-            }break;
-            case 1:
-                printf("Launch Netflix\n");
-                pServer->launchApplication( netflix, payload, responseHeaders, responseBody );
-                break;
-            case 2:
-                printf("Kill Netflix\n");
-                pServer->stopApplication( netflix, responseHeaders );
-                break;
-            case 3:
-                printf("Netflix Status: \n");
-                pServer->getStatus( netflix, responseHeaders, responseBody );
-                printf("RESPONSE: \n%s\n", responseBody.c_str());
-                break;
-            case 4:
-                printf("Launch YouTube\n");
-                pServer->launchApplication( youtube, payload, responseHeaders, responseBody );
-                break;
-            case 5:
-                printf("Kill YouTube\n");
-                pServer->stopApplication( youtube, responseHeaders );
-                break;
-            case 6:
-                printf("YouTube Status: \n");
-                pServer->getStatus( youtube, responseHeaders, responseBody );
-                break;
-            case 7:
-                runConformance();
-                break;
-            case 8:
-                processInput = 0;
-                break;
-            default:
-                printf("Invalid, try again\n");
-        }
-    } 
-    return 0;
-}
-
-static const char usage[] = "\n"
-" If no option is specified, the program will run a conformance test.\n"
-"\n"
-"usage: dialclient <option>\n"
-" Option Parameter              Description\n"
-" -h     none                   Usage menu\n"
-" -s     filename (optional)    Run conformance test.  Use filename as\n"
-"                               the input, if provided\n"
-" -o     filename               Reporter output file (./report.html)\n"
-" -a     ip_address             IP address of DIAL server (used for conformance\n"
-"                               testing)\n"
-"\n";
-
-inline void notSupported( string s )
-{
-    printf( "%s not supported", s.c_str() );
-    printf( "%s\n", usage );
-    exit(0);
-}
-
-int parseArgs( int argc, char* argv[] )
-{
-    for( int i = 1; i < argc; i++ )
-    {
-        string input(argv[i]);
-        if( input[0] != '-' ) notSupported(input);
-        switch(input[1])
-        {
-        case 's':
-            gUseMenu = false;
-            if( argv[i+1] != NULL && argv[i+1][0] != '-' )
-            {
-                //filename provided
-                gInputFile = argv[++i];
-            }
-            break;
-        case 'o':
-            gOutputFile = argv[++i];
-            break;
-        case 'a':
-            gIpAddress = argv[++i];
-            break;
-        case 'h':
-            printf("%s", usage);
-            exit(0);
-            break;
-        default:
-            notSupported(input);
-        }
-    }
-    return 0;
-}
-
-int main(int argc, char* argv[]) {
-    parseArgs(argc, argv);
-
-    gpDiscovery = DialDiscovery::create();
-    gpDiscovery->init();
-    DialConformance::create();
-
-    // Sleep for 2 seconds to allow DIAL servers to response to MSEARCH.
-    sleep(2);
-
-    if ( gUseMenu )
-    {
-        return handleUser(gpDiscovery);
-    }
-
-    // not using the menu, just run the conformance test.
-    runConformance();
-    return 0;
-}
-
diff --git a/src/server/quick_ssdp.c b/src/server/quick_ssdp.c
deleted file mode 100644
index 2331a04..0000000
--- a/src/server/quick_ssdp.c
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * 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>
-#include <netinet/in.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <unistd.h>
-
-#include "mongoose.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 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\r\n";
-
-
-static char ip_addr[INET_ADDRSTRLEN] = "127.0.0.1";
-static int dial_port = 0;
-static int my_port = 0;
-static char friendly_name[256];
-static char uuid[256];
-static char model_name[256];
-static struct mg_context *ctx;
-
-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"
-                      "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";
-  }
-  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);
-  }
-  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;
-    }
-  }
-}
-
-static void handle_mcast() {
-  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 = 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");
-#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;
-    }
-  }
-}
-
-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();
-}