{
bool ret = false;
- if(ServerInstance)
+ if (ServerInstance)
{
ret = ServerInstance->SetInternalCredentials(usePassword, password);
}
m_usePassword = usePassword;
m_password = password;
return true;
-}
+}
void CAirPlayServer::StopServer(bool bWait)
{
close(m_ServerSocket);
return false;
}
-
+
if (listen(m_ServerSocket, 10) < 0)
{
CLog::Log(LOGERROR, "AIRPLAY Server: Failed to set listen");
m_httpParser = new HttpParser();
m_addrlen = sizeof(struct sockaddr);
- m_pLibPlist = new DllLibPlist();
-
+ m_pLibPlist = new DllLibPlist();
+
m_bAuthenticated = false;
}
CAirPlayServer::CTCPClient::CTCPClient(const CTCPClient& client)
{
Copy(client);
- m_pLibPlist = new DllLibPlist();
+ m_pLibPlist = new DllLibPlist();
}
CAirPlayServer::CTCPClient::~CTCPClient()
{
- if( m_pLibPlist->IsLoaded() )
+ if (m_pLibPlist->IsLoaded())
{
m_pLibPlist->Unload();
}
- delete m_pLibPlist;
+ delete m_pLibPlist;
}
CAirPlayServer::CTCPClient& CAirPlayServer::CTCPClient::operator=(const CTCPClient& client)
return *this;
}
-void CAirPlayServer::CTCPClient::PushBuffer(CAirPlayServer *host, const char *buffer,
- int length, CStdString &sessionId, std::map<CStdString,
+void CAirPlayServer::CTCPClient::PushBuffer(CAirPlayServer *host, const char *buffer,
+ int length, CStdString &sessionId, std::map<CStdString,
int> &reverseSockets)
{
HttpParser::status_t status = m_httpParser->addBytes(buffer, length);
int status = ProcessRequest(responseHeader, responseBody, reverseHeader, reverseBody, sessionId);
CStdString statusMsg = "OK";
int reverseSocket = INVALID_SOCKET;
-
+
switch(status)
{
case AIRPLAY_STATUS_NOT_IMPLEMENTED:
char *date = asctime(gmtime(<ime)); //Fri, 17 Dec 2010 11:18:01 GMT;
date[strlen(date) - 1] = '\0'; // remove \n
response.Format("HTTP/1.1 %d %s\nDate: %s\r\n", status, statusMsg.c_str(), date);
- if (responseHeader.size() > 0)
+ if (responseHeader.size() > 0)
{
response += responseHeader;
}
-
+
if (responseBody.size() > 0)
{
response.Format("%sContent-Length: %d\r\n", response.c_str(), responseBody.size());
}
response += "\r\n";
-
+
if (responseBody.size() > 0)
{
response += responseBody;
{
send(m_socket, response.c_str(), response.size(), 0);
}
-
+
// Send event status per reverse http socket (play, loading, paused)
// if we have a reverse header and a reverse socket
if (reverseHeader.size() > 0 && reverseSockets.find(sessionId) != reverseSockets.end())
response += reverseHeader;
}
response += "\r\n";
-
+
if (reverseBody.size() > 0)
{
response += reverseBody;
}
-
+
if (reverseSocket != INVALID_SOCKET)
{
send(reverseSocket, response.c_str(), response.size(), 0);//send the event status on the eventSocket
}
-void CAirPlayServer::CTCPClient::ComposeReverseEvent( CStdString& reverseHeader,
- CStdString& reverseBody,
- CStdString sessionId,
+void CAirPlayServer::CTCPClient::ComposeReverseEvent( CStdString& reverseHeader,
+ CStdString& reverseBody,
+ CStdString sessionId,
int state)
-{
+{
switch(state)
{
case EVENT_PLAYING:
case EVENT_LOADING:
- case EVENT_PAUSED:
+ case EVENT_PAUSED:
reverseBody.Format(EVENT_INFO, eventStrings[state]);
- break;
+ break;
}
reverseHeader = "Content-Type: text/x-apple-plist+xml\r\n";
- reverseHeader.Format("%sContent-Length: %d",reverseHeader.c_str(),reverseBody.size());
+ reverseHeader.Format("%sContent-Length: %d",reverseHeader.c_str(),reverseBody.size());
reverseHeader.Format("%sx-apple-session-id: %s\r\n",reverseHeader.c_str(),sessionId.c_str());
}
void CAirPlayServer::CTCPClient::ComposeAuthRequestAnswer(CStdString& responseHeader, CStdString& responseBody)
{
- CStdString randomStr;
+ CStdString randomStr;
int16_t random=rand();
randomStr.Format("%i", random);
m_authNonce=XBMC::XBMC_MD5::GetMD5(randomStr);
//as of rfc 2617
-CStdString calcResponse(const CStdString& username,
- const CStdString& password,
- const CStdString& realm,
- const CStdString& method,
- const CStdString& digestUri,
+CStdString calcResponse(const CStdString& username,
+ const CStdString& password,
+ const CStdString& realm,
+ const CStdString& method,
+ const CStdString& digestUri,
const CStdString& nonce)
{
CStdString response;
CStdString HA1;
CStdString HA2;
-
+
HA1 = XBMC::XBMC_MD5::GetMD5(username + ":" + realm + ":" + password);
HA2 = XBMC::XBMC_MD5::GetMD5(method + ":" + digestUri);
response = XBMC::XBMC_MD5::GetMD5(HA1.ToLower() + ":" + nonce + ":" + HA2.ToLower());
{
CStdString tmpStr;
CStdStringArray tmpAr1;
- CStdStringArray tmpAr2;
-
+ CStdStringArray tmpAr2;
+
StringUtils::SplitString(str, ",", tmpAr1);
-
+
for(unsigned int i = 0;i<tmpAr1.size();i++)
{
- if(tmpAr1[i].Find(field) != -1)
+ if (tmpAr1[i].Find(field) != -1)
{
- if(StringUtils::SplitString(tmpAr1[i], "=", tmpAr2) == 2)
+ if (StringUtils::SplitString(tmpAr1[i], "=", tmpAr2) == 2)
{
tmpAr2[1].Remove('\"');//remove quotes
return tmpAr2[1];
return "";
}
-bool CAirPlayServer::CTCPClient::checkAuthorization(const CStdString& authStr,
- const CStdString& method,
+bool CAirPlayServer::CTCPClient::checkAuthorization(const CStdString& authStr,
+ const CStdString& method,
const CStdString& uri)
{
bool authValid = true;
CStdString username;
-
- if(authStr.empty())
+
+ if (authStr.empty())
return false;
-
+
//first get username - we allow all usernames for airplay (usually it is AirPlay)
username = getFieldFromString(authStr, "username");
- if(username.empty())
+ if (username.empty())
{
authValid = false;
}
-
+
//second check realm
- if(authValid)
+ if (authValid)
{
- if(getFieldFromString(authStr, "realm") != AUTH_REALM)
+ if (getFieldFromString(authStr, "realm") != AUTH_REALM)
{
authValid = false;
}
}
-
+
//third check nonce
- if(authValid)
+ if (authValid)
{
- if(getFieldFromString(authStr, "nonce") != m_authNonce)
+ if (getFieldFromString(authStr, "nonce") != m_authNonce)
{
authValid = false;
}
}
-
+
//forth check uri
- if(authValid)
+ if (authValid)
{
- if(getFieldFromString(authStr, "uri") != uri)
+ if (getFieldFromString(authStr, "uri") != uri)
{
authValid = false;
- }
- }
-
+ }
+ }
+
//last check response
- if(authValid)
+ if (authValid)
{
CStdString realm = AUTH_REALM;
CStdString ourResponse = calcResponse(username, ServerInstance->m_password, realm, method, uri, m_authNonce);
CStdString theirResponse = getFieldFromString(authStr, "response");
- if(!theirResponse.Equals(ourResponse, false))
+ if (!theirResponse.Equals(ourResponse, false))
{
authValid = false;
CLog::Log(LOGDEBUG,"AirAuth: response mismatch - our: %s theirs: %s",ourResponse.c_str(), theirResponse.c_str());
}
- else
+ else
{
CLog::Log(LOGDEBUG, "AirAuth: successfull authentication from AirPlay client");
}
return m_bAuthenticated;
}
-int CAirPlayServer::CTCPClient::ProcessRequest( CStdString& responseHeader,
- CStdString& responseBody,
- CStdString& reverseHeader,
- CStdString& reverseBody,
+int CAirPlayServer::CTCPClient::ProcessRequest( CStdString& responseHeader,
+ CStdString& responseBody,
+ CStdString& reverseHeader,
+ CStdString& reverseBody,
CStdString& sessionId)
{
CStdString method = m_httpParser->getMethod();
CStdString uri = m_httpParser->getUri();
CStdString queryString = m_httpParser->getQueryString();
CStdString body = m_httpParser->getBody();
- CStdString contentType = m_httpParser->getValue("content-type");
- sessionId = m_httpParser->getValue("x-apple-session-id");
- CStdString authorization = m_httpParser->getValue("authorization");
+ CStdString contentType = m_httpParser->getValue("content-type");
+ sessionId = m_httpParser->getValue("x-apple-session-id");
+ CStdString authorization = m_httpParser->getValue("authorization");
int status = AIRPLAY_STATUS_OK;
bool needAuth = false;
-
- if( ServerInstance->m_usePassword && !m_bAuthenticated )
+
+ if (ServerInstance->m_usePassword && !m_bAuthenticated)
{
needAuth = true;
}
const char* found = strstr(queryString.c_str(), "value=");
int rate = found ? (int)(atof(found + strlen("value=")) + 0.5f) : 0;
- if( needAuth && !checkAuthorization(authorization, method, uri))
+ if (needAuth && !checkAuthorization(authorization, method, uri))
{
status = AIRPLAY_STATUS_NEED_AUTH;
}
{
CStdString location;
float position = 0.0;
-
- if( needAuth && !checkAuthorization(authorization, method, uri))
+
+ if (needAuth && !checkAuthorization(authorization, method, uri))
{
status = AIRPLAY_STATUS_NEED_AUTH;
}
else if (contentType == "application/x-apple-binary-plist")
{
-
- if (m_pLibPlist->Load())
+
+ if (m_pLibPlist->Load())
{
m_pLibPlist->EnableDelayedUnload(false);
plist_t dict = NULL;
m_pLibPlist->plist_from_bin(bodyChr, m_httpParser->getContentLength(), &dict);
-
+
if (m_pLibPlist->plist_dict_get_size(dict))
{
plist_t tmpNode = m_pLibPlist->plist_dict_get_item(dict, "Start-Position");
m_pLibPlist->plist_get_real_val(tmpNode, &tmpDouble);
position = tmpDouble;
}
-
+
tmpNode = m_pLibPlist->plist_dict_get_item(dict, "Content-Location");
if (tmpNode)
{
location=tmpStr;
free(tmpStr);
}
-
- if(dict)
+
+ if (dict)
{
m_pLibPlist->plist_free(dict);
- }
+ }
}
else
{
}
}
- if( status != AIRPLAY_STATUS_NEED_AUTH )
+ if (status != AIRPLAY_STATUS_NEED_AUTH)
{
CFileItem fileToPlay(location, false);
fileToPlay.SetProperty(CStdString("StartPercent"), CVariant(position*100.0f));
// Used to perform seeking (POST request) and to retrieve current player position (GET request).
else if (uri == "/scrub")
{
- if( needAuth && !checkAuthorization(authorization, method, uri))
+ if (needAuth && !checkAuthorization(authorization, method, uri))
{
status = AIRPLAY_STATUS_NEED_AUTH;
}
// Sent when media playback should be stopped
else if (uri == "/stop")
{
- if( needAuth && !checkAuthorization(authorization, method, uri))
+ if (needAuth && !checkAuthorization(authorization, method, uri))
{
status = AIRPLAY_STATUS_NEED_AUTH;
}
- else
+ else
{
g_application.getApplicationMessenger().MediaStop();
}
// RAW JPEG data is contained in the request body
else if (uri == "/photo")
{
- if( needAuth && !checkAuthorization(authorization, method, uri))
+ if (needAuth && !checkAuthorization(authorization, method, uri))
{
status = AIRPLAY_STATUS_NEED_AUTH;
}
int writtenBytes=0;
writtenBytes = tmpFile.Write(m_httpParser->getBody(), m_httpParser->getContentLength());
tmpFile.Close();
-
+
if (writtenBytes > 0 && (unsigned int)writtenBytes == m_httpParser->getContentLength())
{
g_application.getApplicationMessenger().PictureShow("special://temp/airplay_photo.jpg");
float duration = 0.0f;
float cacheDuration = 0.0f;
bool playing = false;
-
- if( needAuth && !checkAuthorization(authorization, method, uri))
+
+ if (needAuth && !checkAuthorization(authorization, method, uri))
{
status = AIRPLAY_STATUS_NEED_AUTH;
}
position = ((float) g_application.m_pPlayer->GetTime()) / 1000;
duration = (float) g_application.m_pPlayer->GetTotalTime();
playing = g_application.m_pPlayer ? !g_application.m_pPlayer->IsPaused() : false;
- cacheDuration = (float) g_application.m_pPlayer->GetTotalTime() * g_application.GetCachePercentage()/100.0f;
+ cacheDuration = (float) g_application.m_pPlayer->GetTotalTime() * g_application.GetCachePercentage()/100.0f;
}
responseBody.Format(PLAYBACK_INFO, duration, cacheDuration, position, (playing ? 1 : 0), duration);
responseHeader = "Content-Type: text/x-apple-plist+xml\r\n";
-
+
if (g_application.m_pPlayer->IsCaching())
{
ComposeReverseEvent(reverseHeader, reverseBody, sessionId, EVENT_LOADING);
}
- else if(playing)
+ else if (playing)
{
ComposeReverseEvent(reverseHeader, reverseBody, sessionId, EVENT_PLAYING);
}
CLog::Log(LOGERROR, "AIRPLAY Server: unhandled request [%s]\n", uri.c_str());
status = AIRPLAY_STATUS_NOT_IMPLEMENTED;
}
-
- if(status == AIRPLAY_STATUS_NEED_AUTH)
+
+ if (status == AIRPLAY_STATUS_NEED_AUTH)
{
ComposeAuthRequestAnswer(responseHeader, responseBody);
}