/* * 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 "DialServer.h" #include using namespace std; enum DIAL_COMMAND{ COMMAND_LAUNCH, COMMAND_STATUS, COMMAND_KILL, COMMAND_HIDE }; const char* DIAL_COMMAND_STR[] = {"LAUNCH", "STATUS", "KILL", "HIDE"}; static size_t header_cb(void* ptr, size_t size, size_t nmemb, void* userdata) { if ((size * nmemb) != 0) { string newHeader((char*)ptr); string *header = static_cast(userdata); header->append(newHeader); ATRACE("%s: Adding header: %s", __FUNCTION__, newHeader.c_str()); } return (size * nmemb); } static size_t receiveData(void *ptr, size_t size, size_t nmemb, void *userdata) { if ((size * nmemb) != 0) { string *body= static_cast(userdata); body->append((char*)ptr); ATRACE("%s: Adding to Body: %s", __FUNCTION__, (char*)ptr); } return (size * nmemb); } int DialServer::sendCommand( string &url, int command, string &payload, string &responseHeaders, string &responseBody ) { CURL *curl; CURLcode res = CURLE_OK; struct curl_slist *slist = NULL; // since we are Dial Version 2.1, added query paramter to indicate Dial Version url += "?clientDialVer="; url += CLIENT_VERSION; if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { fprintf(stderr, "curl_global_init() failed\n"); return 0; } if ((curl = curl_easy_init()) == NULL) { fprintf(stderr, "curl_easy_init() failed\n"); curl_global_cleanup(); return 0; } if (command == COMMAND_LAUNCH || command == COMMAND_HIDE) { curl_easy_setopt(curl, CURLOPT_POST, true); curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, payload.size()); if( payload.size() ) { slist = curl_slist_append(slist, "Expect:"); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, payload.c_str()); } #ifdef DEBUG else ATRACE("Sending empty POST\n"); #endif } else if (command == COMMAND_KILL) { curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); } ATRACE("Sending %s:%s\n", DIAL_COMMAND_STR[command], url.c_str()); curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_cb); curl_easy_setopt(curl, CURLOPT_HEADERDATA, &responseHeaders); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, receiveData); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &responseBody); res = curl_easy_perform(curl); if (slist!=NULL) curl_slist_free_all(slist); curl_easy_cleanup(curl); curl_global_cleanup(); return (res == CURLE_OK); } string DialServer::getIpAddress() { // m_appsUrl=http://192.168.1.103:36269/apps/ if( m_ipAddr.empty() ) { size_t begin = m_appsUrl.find("//"); if( begin != m_appsUrl.npos ) { begin += 2; // move to the start of the IP address size_t end = m_appsUrl.find(":", begin); if( end != m_appsUrl.npos ) { m_ipAddr = m_appsUrl.substr( begin, end-begin ); ATRACE("IP ADDRESS: %s\n", m_ipAddr.c_str() ); } } } return m_ipAddr; } bool DialServer::getFriendlyName( string& name ) { bool retval = false; if( !m_ddxml.empty() ) { string friendlyName = ""; size_t pos; if( ( pos = m_ddxml.find( friendlyName ) ) != m_ddxml.npos ) { string friendlyNameEnd = ""; size_t end = m_ddxml.find( friendlyNameEnd ); name = m_ddxml.substr( pos + friendlyName.size(), (end - (pos + friendlyName.size())) ); ATRACE("***Friendly name=%s***", name.c_str()); retval = true; } #ifdef DEBUG else { ATRACE("Friendly name not found\n%s\n", m_ddxml.c_str()); } #endif } return retval; } bool DialServer::getUuid( string& uuid ) { bool retval = false; if( !m_ddxml.empty() ) { string udn = ""; size_t pos; if( ( pos = m_ddxml.find( udn ) ) != m_ddxml.npos ) { string udnEnd = ""; size_t end = m_ddxml.find( udnEnd ); uuid = m_ddxml.substr( pos + udn.size(), (end - (pos + udn.size())) ); ATRACE("***UUID=%s***", uuid.c_str() ); retval = true; } #ifdef DEBUG else { ATRACE("Friendly name not found\n%s\n", m_ddxml.c_str()); } #endif } return retval; } string DialServer::getMacAddress() { return m_macAddr; } int DialServer::getWakeOnLanTimeout() { return m_wakeUpTimeout; } int DialServer::launchApplication( string &application, string &payload, string &responseHeaders, string &responseBody ) { ATRACE("%s: Launch %s\n", __FUNCTION__, application.c_str()); string appUrl = m_appsUrl; int status = sendCommand( appUrl.append(application), COMMAND_LAUNCH, payload, responseHeaders, responseBody); return status; } int DialServer::getStatus( string &application, string &responseHeaders, string &responseBody ) { ATRACE("%s: GetStatus %s\n", __FUNCTION__, application.c_str()); string emptyPayload; string appUrl = m_appsUrl; 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 ) { // The start of href is after href= and the quote unsigned href_start = found + 5 + 1; // get the body from the start of href to the end, then find // the last quote delimiter. string tmp = responseBody.substr( href_start ); unsigned href_end = tmp.find("\""); m_stopEndPoint = responseBody.substr( href_start, href_end ); } return 0; } int DialServer::stopApplication( string &application, string &responseHeaders ) { ATRACE("%s: Quit %s\n", __FUNCTION__, application.c_str()); string emptyPayload, responseBody; // dropping this string appUrl = m_appsUrl; // just call status to update the run endpoint getStatus( application, responseHeaders, responseBody ); int status = sendCommand( (appUrl.append(application)).append("/"+m_stopEndPoint), COMMAND_KILL, emptyPayload, responseHeaders, responseBody ); return status; } int DialServer::hideApplication( string &application, string &responseHeaders, string &responseBody ) { ATRACE("%s: Hide %s\n", __FUNCTION__, application.c_str()); string emptyPayload; string appUrl = m_appsUrl; // just call status to update the run endpoint getStatus( application, responseHeaders, responseBody ); int status = sendCommand( (appUrl.append(application)).append("/"+m_stopEndPoint).append("/hide"), COMMAND_HIDE, emptyPayload, responseHeaders, responseBody ); return status; } int DialServer::getHttpResponseHeader( string &responseHeaders, string &header, string &value ) { return 0; }