|
MythTV
0.26-pre
|
00001 00002 // Program Name: httprequest.cpp 00003 // Created : Oct. 21, 2005 00004 // 00005 // Purpose : Http Request/Response 00006 // 00007 // Copyright (c) 2005 David Blain <dblain@mythtv.org> 00008 // 00009 // Licensed under the GPL v2 or later, see COPYING for details 00010 // 00012 00013 #include "httprequest.h" 00014 00015 #include <QFile> 00016 #include <QFileInfo> 00017 #include <QTextCodec> 00018 #include <QStringList> 00019 #include <QCryptographicHash> 00020 #include <QDateTime> 00021 00022 #include "mythconfig.h" 00023 #if !( CONFIG_DARWIN || CONFIG_CYGWIN || defined(__FreeBSD__) || defined(USING_MINGW)) 00024 #define USE_SETSOCKOPT 00025 #include <sys/sendfile.h> 00026 #endif 00027 #include <sys/types.h> 00028 #include <sys/stat.h> 00029 #include <stdlib.h> 00030 #include <fcntl.h> 00031 #include <cerrno> 00032 00033 #ifndef USING_MINGW 00034 #include <netinet/tcp.h> 00035 #endif 00036 00037 #include "upnp.h" 00038 00039 #include "compat.h" 00040 #include "mythlogging.h" 00041 #include "mythversion.h" 00042 00043 #include "serializers/xmlSerializer.h" 00044 #include "serializers/soapSerializer.h" 00045 #include "serializers/jsonSerializer.h" 00046 #include "serializers/xmlplistSerializer.h" 00047 00048 #ifndef O_LARGEFILE 00049 #define O_LARGEFILE 0 00050 #endif 00051 00052 static MIMETypes g_MIMETypes[] = 00053 { 00054 { "gif" , "image/gif" }, 00055 { "jpg" , "image/jpeg" }, 00056 { "jpeg", "image/jpeg" }, 00057 { "png" , "image/png" }, 00058 { "htm" , "text/html" }, 00059 { "html", "text/html" }, 00060 { "qsp" , "text/html" }, 00061 { "js" , "application/javascript" }, 00062 { "qjs" , "application/javascript" }, 00063 { "txt" , "text/plain" }, 00064 { "xml" , "text/xml" }, 00065 { "xslt", "text/xml" }, 00066 { "pdf" , "application/pdf" }, 00067 { "avi" , "video/avi" }, 00068 { "css" , "text/css" }, 00069 { "swf" , "application/x-shockwave-flash" }, 00070 { "xls" , "application/vnd.ms-excel" }, 00071 { "doc" , "application/vnd.ms-word" }, 00072 { "mid" , "audio/midi" }, 00073 { "mp3" , "audio/mpeg" }, 00074 { "rm" , "application/vnd.rn-realmedia" }, 00075 { "wav" , "audio/wav" }, 00076 { "zip" , "application/x-tar" }, 00077 { "gz" , "application/x-tar" }, 00078 { "mpg" , "video/mpeg" }, 00079 { "mpg2", "video/mpeg" }, 00080 { "mpeg", "video/mpeg" }, 00081 { "mpeg2","video/mpeg" }, 00082 { "vob" , "video/mpeg" }, 00083 { "asf" , "video/x-ms-asf" }, 00084 { "nuv" , "video/nupplevideo" }, 00085 { "mov" , "video/quicktime" }, 00086 { "mp4" , "video/mp4" }, 00087 // This formerly was video/x-matroska, but got changed due to #8643 00088 { "mkv" , "video/x-mkv" }, 00089 { "mka" , "audio/x-matroska" }, 00090 { "wmv" , "video/x-ms-wmv" }, 00091 // Defined: http://wiki.xiph.org/index.php/MIME_Types_and_File_Extensions 00092 { "ogg" , "audio/ogg" }, 00093 { "ogv" , "video/ogg" }, 00094 { "ogx" , "application/ogg" }, 00095 { "oga" , "audio/ogg" }, 00096 // Similarly, this could be audio/flac or application/flac: 00097 { "flac", "audio/x-flac" }, 00098 { "m4a" , "audio/x-m4a" }, 00099 // HTTP Live Streaming 00100 { "m3u8", "application/x-mpegurl" }, 00101 { "ts" , "video/mp2t" }, 00102 }; 00103 00104 static const char *Static401Error = 00105 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"" 00106 "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">" 00107 "<HTML>" 00108 "<HEAD>" 00109 "<TITLE>Error</TITLE>" 00110 "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=ISO-8859-1\">" 00111 "</HEAD>" 00112 "<BODY><H1>401 Unauthorized.</H1></BODY>" 00113 "</HTML>"; 00114 00115 static const int g_nMIMELength = sizeof( g_MIMETypes) / sizeof( MIMETypes ); 00116 static const int g_on = 1; 00117 static const int g_off = 0; 00118 00119 const char *HTTPRequest::m_szServerHeaders = "Accept-Ranges: bytes\r\n"; 00120 00122 // 00124 00125 HTTPRequest::HTTPRequest() : m_procReqLineExp ( "[ \r\n][ \r\n]*" ), 00126 m_parseRangeExp ( "(\\d|\\-)" ), 00127 m_eType ( RequestTypeUnknown ), 00128 m_eContentType ( ContentType_Unknown), 00129 m_nMajor ( 0 ), 00130 m_nMinor ( 0 ), 00131 m_bSOAPRequest ( false ), 00132 m_eResponseType ( ResponseTypeUnknown), 00133 m_nResponseStatus( 200 ), 00134 m_pPostProcess ( NULL ) 00135 { 00136 m_response.open( QIODevice::ReadWrite ); 00137 } 00138 00140 // 00142 00143 RequestType HTTPRequest::SetRequestType( const QString &sType ) 00144 { 00145 if (sType == "GET" ) return( m_eType = RequestTypeGet ); 00146 if (sType == "HEAD" ) return( m_eType = RequestTypeHead ); 00147 if (sType == "POST" ) return( m_eType = RequestTypePost ); 00148 if (sType == "M-SEARCH" ) return( m_eType = RequestTypeMSearch ); 00149 00150 if (sType == "SUBSCRIBE" ) return( m_eType = RequestTypeSubscribe ); 00151 if (sType == "UNSUBSCRIBE") return( m_eType = RequestTypeUnsubscribe ); 00152 if (sType == "NOTIFY" ) return( m_eType = RequestTypeNotify ); 00153 00154 if (sType.startsWith( QString("HTTP/") )) return( m_eType = RequestTypeResponse ); 00155 00156 LOG(VB_UPNP, LOG_INFO, 00157 QString("HTTPRequest::SentRequestType( %1 ) - returning Unknown.") 00158 .arg(sType)); 00159 00160 return( m_eType = RequestTypeUnknown); 00161 } 00162 00164 // 00166 00167 QString HTTPRequest::BuildHeader( long long nSize ) 00168 { 00169 QString sHeader; 00170 QString sContentType = (m_eResponseType == ResponseTypeOther) ? 00171 m_sResponseTypeText : GetResponseType(); 00172 00173 sHeader = QString( "HTTP/%1.%2 %3\r\n" 00174 "Date: %4\r\n" 00175 "Server: %5, UPnP/1.0, MythTV %6\r\n" ) 00176 .arg(m_nMajor).arg(m_nMinor).arg(GetResponseStatus()) 00177 .arg(QDateTime::currentDateTime().toString("d MMM yyyy hh:mm:ss")) 00178 .arg(HttpServer::GetPlatform()).arg(MYTH_BINARY_VERSION); 00179 00180 sHeader += GetAdditionalHeaders(); 00181 00182 sHeader += QString( "Connection: %1\r\n" 00183 "Content-Type: %2\r\n" 00184 "Content-Length: %3\r\n" ) 00185 .arg( GetKeepAlive() ? "Keep-Alive" : "Close" ) 00186 .arg( sContentType ) 00187 .arg( nSize ); 00188 00189 // ---------------------------------------------------------------------- 00190 // Temp Hack to process DLNA header 00191 00192 QString sValue = GetHeaderValue( "getcontentfeatures.dlna.org", "0" ); 00193 00194 if (sValue == "1") 00195 sHeader += "contentFeatures.dlna.org: DLNA.ORG_OP=01;DLNA.ORG_CI=0;" 00196 "DLNA.ORG_FLAGS=01500000000000000000000000000000\r\n"; 00197 00198 // ---------------------------------------------------------------------- 00199 00200 sHeader += "\r\n"; 00201 00202 return sHeader; 00203 } 00204 00206 // 00208 00209 long HTTPRequest::SendResponse( void ) 00210 { 00211 long nBytes = 0; 00212 00213 switch( m_eResponseType ) 00214 { 00215 case ResponseTypeUnknown: 00216 case ResponseTypeNone: 00217 LOG(VB_UPNP, LOG_INFO, 00218 QString("HTTPRequest::SendResponse( None ) :%1 -> %2:") 00219 .arg(GetResponseStatus()) .arg(GetPeerAddress())); 00220 return( -1 ); 00221 00222 case ResponseTypeFile: 00223 LOG(VB_UPNP, LOG_INFO, 00224 QString("HTTPRequest::SendResponse( File ) :%1 -> %2:") 00225 .arg(GetResponseStatus()) .arg(GetPeerAddress())); 00226 return( SendResponseFile( m_sFileName )); 00227 00228 case ResponseTypeXML: 00229 case ResponseTypeHTML: 00230 case ResponseTypeOther: 00231 default: 00232 break; 00233 } 00234 00235 LOG(VB_UPNP, LOG_INFO, 00236 QString("HTTPRequest::SendResponse(xml/html) (%1) :%2 -> %3: %4") 00237 .arg(m_sFileName) .arg(GetResponseStatus()) 00238 .arg(GetPeerAddress()) .arg(m_eResponseType)); 00239 00240 // ---------------------------------------------------------------------- 00241 // Make it so the header is sent with the data 00242 // ---------------------------------------------------------------------- 00243 00244 #ifdef USE_SETSOCKOPT 00245 // Never send out partially complete segments 00246 setsockopt( getSocketHandle(), SOL_TCP, TCP_CORK, &g_on, sizeof( g_on )); 00247 #endif 00248 00249 // ---------------------------------------------------------------------- 00250 // Check for ETag match... 00251 // ---------------------------------------------------------------------- 00252 00253 QString sETag = GetHeaderValue( "If-None-Match", "" ); 00254 00255 if ( !sETag.isEmpty() && sETag == m_mapRespHeaders[ "ETag" ] ) 00256 { 00257 LOG(VB_UPNP, LOG_INFO, 00258 QString("HTTPRequest::SendResponse(%1) - Cached") 00259 .arg(sETag)); 00260 00261 m_nResponseStatus = 304; 00262 00263 // no content can be returned. 00264 m_response.buffer().clear(); 00265 } 00266 00267 // ---------------------------------------------------------------------- 00268 00269 int nContentLen = m_response.buffer().length(); 00270 00271 QBuffer *pBuffer = &m_response; 00272 00273 // ---------------------------------------------------------------------- 00274 // Should we try to return data gzip'd? 00275 // ---------------------------------------------------------------------- 00276 00277 QBuffer compBuffer; 00278 00279 if (( nContentLen > 0 ) && m_mapHeaders[ "accept-encoding" ].contains( "gzip" )) 00280 { 00281 QByteArray compressed = gzipCompress( m_response.buffer() ); 00282 compBuffer.setData( compressed ); 00283 00284 if (compBuffer.buffer().length() > 0) 00285 { 00286 pBuffer = &compBuffer; 00287 00288 m_mapRespHeaders[ "Content-Encoding" ] = "gzip"; 00289 } 00290 } 00291 00292 // ---------------------------------------------------------------------- 00293 // Write out Header. 00294 // ---------------------------------------------------------------------- 00295 00296 nContentLen = pBuffer->buffer().length(); 00297 00298 QString rHeader = BuildHeader( nContentLen ); 00299 00300 QByteArray sHeader = rHeader.toUtf8(); 00301 nBytes = WriteBlockDirect( sHeader.constData(), sHeader.length() ); 00302 00303 // ---------------------------------------------------------------------- 00304 // Write out Response buffer. 00305 // ---------------------------------------------------------------------- 00306 00307 if (( m_eType != RequestTypeHead ) && ( nContentLen > 0 )) 00308 { 00309 nBytes += SendData( pBuffer, 0, nContentLen ); 00310 } 00311 00312 // ---------------------------------------------------------------------- 00313 // Turn off the option so any small remaining packets will be sent 00314 // ---------------------------------------------------------------------- 00315 00316 #ifdef USE_SETSOCKOPT 00317 setsockopt( getSocketHandle(), SOL_TCP, TCP_CORK, &g_off, sizeof( g_off )); 00318 #endif 00319 00320 return( nBytes ); 00321 } 00322 00324 // 00326 00327 long HTTPRequest::SendResponseFile( QString sFileName ) 00328 { 00329 long nBytes = 0; 00330 long long llSize = 0; 00331 long long llStart = 0; 00332 long long llEnd = 0; 00333 00334 LOG(VB_UPNP, LOG_INFO, QString("SendResponseFile ( %1 )").arg(sFileName)); 00335 00336 m_eResponseType = ResponseTypeOther; 00337 m_sResponseTypeText = "text/plain"; 00338 00339 #if 0 00340 // Dump request header 00341 for ( QStringMap::iterator it = m_mapHeaders.begin(); 00342 it != m_mapHeaders.end(); 00343 ++it ) 00344 { 00345 LOG(VB_GENERAL, LOG_DEBUG, it.key() + ": " + *it); 00346 } 00347 #endif 00348 00349 // ---------------------------------------------------------------------- 00350 // Make it so the header is sent with the data 00351 // ---------------------------------------------------------------------- 00352 00353 #ifdef USE_SETSOCKOPT 00354 // Never send out partially complete segments 00355 setsockopt( getSocketHandle(), SOL_TCP, TCP_CORK, &g_on, sizeof( g_on )); 00356 #endif 00357 00358 QFile tmpFile( sFileName ); 00359 if (tmpFile.exists( ) && tmpFile.open( QIODevice::ReadOnly )) 00360 { 00361 00362 m_sResponseTypeText = TestMimeType( sFileName ); 00363 00364 // ------------------------------------------------------------------ 00365 // Get File size 00366 // ------------------------------------------------------------------ 00367 00368 llSize = llEnd = tmpFile.size( ); 00369 00370 m_nResponseStatus = 200; 00371 00372 // ------------------------------------------------------------------ 00373 // Process any Range Header 00374 // ------------------------------------------------------------------ 00375 00376 bool bRange = false; 00377 QString sRange = GetHeaderValue( "range", "" ); 00378 00379 if (sRange.length() > 0) 00380 { 00381 bRange = ParseRange( sRange, llSize, &llStart, &llEnd ); 00382 00383 // Adjust ranges that are too long. 00384 00385 if (llEnd >= llSize) 00386 llEnd = llSize-1; 00387 00388 if ((llSize > llStart) && (llSize > llEnd) && (llEnd > llStart)) 00389 { 00390 if (bRange) 00391 { 00392 m_nResponseStatus = 206; 00393 m_mapRespHeaders[ "Content-Range" ] = QString("bytes %1-%2/%3") 00394 .arg( llStart ) 00395 .arg( llEnd ) 00396 .arg( llSize ); 00397 llSize = (llEnd - llStart) + 1; 00398 } 00399 } 00400 else 00401 { 00402 m_nResponseStatus = 416; 00403 llSize = 0; 00404 LOG(VB_UPNP, LOG_INFO, 00405 QString("HTTPRequest::SendResponseFile(%1) - " 00406 "invalid byte range %2-%3/%4") 00407 .arg(sFileName) .arg(llStart) .arg(llEnd) 00408 .arg(llSize)); 00409 } 00410 } 00411 00412 // DSM-?20 specific response headers 00413 if (bRange == false) 00414 m_mapRespHeaders[ "User-Agent" ] = "redsonic"; 00415 00416 // ------------------------------------------------------------------ 00417 // 00418 // ------------------------------------------------------------------ 00419 00420 } 00421 else 00422 { 00423 LOG(VB_UPNP, LOG_INFO, 00424 QString("HTTPRequest::SendResponseFile(%1) - cannot find file!") 00425 .arg(sFileName)); 00426 m_nResponseStatus = 404; 00427 } 00428 00429 // -=>TODO: Should set "Content-Length: *" if file is still recording 00430 00431 // ---------------------------------------------------------------------- 00432 // Write out Header. 00433 // ---------------------------------------------------------------------- 00434 00435 QString rHeader = BuildHeader( llSize ); 00436 QByteArray sHeader = rHeader.toUtf8(); 00437 nBytes = WriteBlockDirect( sHeader.constData(), sHeader.length() ); 00438 00439 // ---------------------------------------------------------------------- 00440 // Write out File. 00441 // ---------------------------------------------------------------------- 00442 00443 #if 0 00444 LOG(VB_UPNP, LOG_DEBUG, 00445 QString("SendResponseFile : size = %1, start = %2, end = %3") 00446 .arg(llSize).arg(llStart).arg(llEnd)); 00447 #endif 00448 if (( m_eType != RequestTypeHead ) && (llSize != 0)) 00449 { 00450 long long sent = SendFile( tmpFile, llStart, llSize ); 00451 00452 if (sent == -1) 00453 { 00454 LOG(VB_UPNP, LOG_INFO, 00455 QString("SendResponseFile( %1 ) Error: %2 [%3]" ) 00456 .arg(sFileName) .arg(errno) .arg(strerror(errno))); 00457 00458 nBytes = -1; 00459 } 00460 } 00461 00462 // ---------------------------------------------------------------------- 00463 // Turn off the option so any small remaining packets will be sent 00464 // ---------------------------------------------------------------------- 00465 00466 #ifdef USE_SETSOCKOPT 00467 setsockopt( getSocketHandle(), SOL_TCP, TCP_CORK, &g_off, sizeof( g_off )); 00468 #endif 00469 00470 // -=>TODO: Only returns header length... 00471 // should we change to return total bytes? 00472 00473 return nBytes; 00474 } 00475 00477 // 00479 00480 #define SENDFILE_BUFFER_SIZE 65536 00481 00482 qint64 HTTPRequest::SendData( QIODevice *pDevice, qint64 llStart, qint64 llBytes ) 00483 { 00484 bool bShouldClose = false; 00485 qint64 sent = 0; 00486 00487 if (!pDevice->isOpen()) 00488 { 00489 pDevice->open( QIODevice::ReadOnly ); 00490 bShouldClose = true; 00491 } 00492 00493 // ---------------------------------------------------------------------- 00494 // Set out file position to requested start location. 00495 // ---------------------------------------------------------------------- 00496 00497 if ( pDevice->seek( llStart ) == false) 00498 return -1; 00499 00500 char aBuffer[ SENDFILE_BUFFER_SIZE ]; 00501 00502 qint64 llBytesRemaining = llBytes; 00503 qint64 llBytesToRead = 0; 00504 qint64 llBytesRead = 0; 00505 qint64 llBytesWritten = 0; 00506 00507 while ((sent < llBytes) && !pDevice->atEnd()) 00508 { 00509 llBytesToRead = std::min( (qint64)SENDFILE_BUFFER_SIZE, llBytesRemaining ); 00510 00511 if (( llBytesRead = pDevice->read( aBuffer, llBytesToRead )) != -1 ) 00512 { 00513 if (( llBytesWritten = WriteBlockDirect( aBuffer, llBytesRead )) == -1) 00514 return -1; 00515 00516 // -=>TODO: We don't handle the situation where we read more than was sent. 00517 00518 sent += llBytesRead; 00519 llBytesRemaining -= llBytesRead; 00520 } 00521 } 00522 00523 if (bShouldClose) 00524 pDevice->close(); 00525 00526 return sent; 00527 } 00528 00530 // 00532 00533 qint64 HTTPRequest::SendFile( QFile &file, qint64 llStart, qint64 llBytes ) 00534 { 00535 qint64 sent = 0; 00536 00537 #ifndef __linux__ 00538 sent = SendData( (QIODevice *)(&file), llStart, llBytes ); 00539 #else 00540 __off64_t offset = llStart; 00541 int fd = file.handle( ); 00542 00543 if ( fd == -1 ) 00544 { 00545 LOG(VB_UPNP, LOG_INFO, 00546 QString("SendResponseFile( %1 ) Error: %2 [%3]") 00547 .arg(file.fileName()) .arg(file.error()) 00548 .arg(strerror(file.error()))); 00549 sent = -1; 00550 } 00551 else 00552 { 00553 qint64 llSent = 0; 00554 00555 do 00556 { 00557 // SSIZE_MAX should work in kernels 2.6.16 and later. 00558 // The loop is needed in any case. 00559 00560 sent = sendfile64(getSocketHandle(), fd, &offset, 00561 (size_t)MIN(llBytes, INT_MAX)); 00562 00563 if (sent >= 0) 00564 { 00565 llBytes -= sent; 00566 llSent += sent; 00567 LOG(VB_UPNP, LOG_INFO, 00568 QString("SendResponseFile : --- size = %1, " 00569 "offset = %2, sent = %3") 00570 .arg(llBytes).arg(offset).arg(sent)); 00571 } 00572 } 00573 while (( sent >= 0 ) && ( llBytes > 0 )); 00574 00575 sent = llSent; 00576 } 00577 #endif 00578 00579 return( sent ); 00580 } 00581 00582 00584 // 00586 00587 void HTTPRequest::FormatErrorResponse( bool bServerError, 00588 const QString &sFaultString, 00589 const QString &sDetails ) 00590 { 00591 m_eResponseType = ResponseTypeXML; 00592 m_nResponseStatus = 500; 00593 00594 QTextStream stream( &m_response ); 00595 00596 stream << "<?xml version=\"1.0\" encoding=\"utf-8\"?>"; 00597 00598 QString sWhere = ( bServerError ) ? "s:Server" : "s:Client"; 00599 00600 if (m_bSOAPRequest) 00601 { 00602 m_mapRespHeaders[ "EXT" ] = ""; 00603 00604 stream << SOAP_ENVELOPE_BEGIN 00605 << "<s:Fault>" 00606 << "<faultcode>" << sWhere << "</faultcode>" 00607 << "<faultstring>" << sFaultString << "</faultstring>"; 00608 } 00609 00610 if (sDetails.length() > 0) 00611 { 00612 stream << "<detail>" << sDetails << "</detail>"; 00613 } 00614 00615 if (m_bSOAPRequest) 00616 stream << "</s:Fault>" << SOAP_ENVELOPE_END; 00617 00618 stream.flush(); 00619 } 00620 00622 // 00624 00625 void HTTPRequest::FormatActionResponse( Serializer *pSer ) 00626 { 00627 m_eResponseType = ResponseTypeOther; 00628 m_sResponseTypeText = pSer->GetContentType(); 00629 m_nResponseStatus = 200; 00630 00631 pSer->AddHeaders( m_mapRespHeaders ); 00632 00633 //m_response << pFormatter->ToString(); 00634 } 00635 00637 // 00639 00640 void HTTPRequest::FormatActionResponse(const NameValues &args) 00641 { 00642 m_eResponseType = ResponseTypeXML; 00643 m_nResponseStatus = 200; 00644 00645 QTextStream stream( &m_response ); 00646 00647 stream << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n"; 00648 00649 if (m_bSOAPRequest) 00650 { 00651 m_mapRespHeaders[ "EXT" ] = ""; 00652 00653 stream << SOAP_ENVELOPE_BEGIN 00654 << "<u:" << m_sMethod << "Response xmlns:u=\"" 00655 << m_sNameSpace << "\">\r\n"; 00656 } 00657 else 00658 stream << "<" << m_sMethod << "Response>\r\n"; 00659 00660 NameValues::const_iterator nit = args.begin(); 00661 for (; nit != args.end(); ++nit) 00662 { 00663 stream << "<" << (*nit).sName; 00664 00665 if ((*nit).pAttributes) 00666 { 00667 NameValues::const_iterator nit2 = (*nit).pAttributes->begin(); 00668 for (; nit2 != (*nit).pAttributes->end(); ++nit2) 00669 { 00670 stream << " " << (*nit2).sName << "='" 00671 << Encode( (*nit2).sValue ) << "'"; 00672 } 00673 } 00674 00675 stream << ">"; 00676 00677 if (m_bSOAPRequest) 00678 stream << Encode( (*nit).sValue ); 00679 else 00680 stream << (*nit).sValue; 00681 00682 stream << "</" << (*nit).sName << ">\r\n"; 00683 } 00684 00685 if (m_bSOAPRequest) 00686 { 00687 stream << "</u:" << m_sMethod << "Response>\r\n" 00688 << SOAP_ENVELOPE_END; 00689 } 00690 else 00691 stream << "</" << m_sMethod << "Response>\r\n"; 00692 00693 stream.flush(); 00694 } 00695 00697 // 00699 00700 void HTTPRequest::FormatRawResponse(const QString &sXML) 00701 { 00702 m_eResponseType = ResponseTypeXML; 00703 m_nResponseStatus = 200; 00704 00705 QTextStream stream( &m_response ); 00706 00707 stream << sXML; 00708 00709 stream.flush(); 00710 } 00712 // 00714 00715 void HTTPRequest::FormatFileResponse( const QString &sFileName ) 00716 { 00717 m_sFileName = sFileName; 00718 00719 if (QFile::exists( m_sFileName )) 00720 { 00721 00722 m_eResponseType = ResponseTypeFile; 00723 m_nResponseStatus = 200; 00724 m_mapRespHeaders["Cache-Control"] = "no-cache=\"Ext\", max-age = 5000"; 00725 } 00726 else 00727 { 00728 m_eResponseType = ResponseTypeHTML; 00729 m_nResponseStatus = 404; 00730 LOG(VB_UPNP, LOG_INFO, 00731 QString("HTTPRequest::FormatFileResponse(%1) - cannot find file") 00732 .arg(sFileName)); 00733 } 00734 } 00735 00737 // 00739 00740 void HTTPRequest::SetRequestProtocol( const QString &sLine ) 00741 { 00742 m_sProtocol = sLine.section( '/', 0, 0 ).trimmed(); 00743 QString sVersion = sLine.section( '/', 1 ).trimmed(); 00744 00745 m_nMajor = sVersion.section( '.', 0, 0 ).toInt(); 00746 m_nMinor = sVersion.section( '.', 1 ).toInt(); 00747 } 00748 00750 // 00752 00753 ContentType HTTPRequest::SetContentType( const QString &sType ) 00754 { 00755 if ((sType == "application/x-www-form-urlencoded" ) || 00756 (sType.startsWith("application/x-www-form-urlencoded;"))) 00757 return( m_eContentType = ContentType_Urlencoded ); 00758 00759 if ((sType == "text/xml" ) || 00760 (sType.startsWith("text/xml;") )) 00761 return( m_eContentType = ContentType_XML ); 00762 00763 return( m_eContentType = ContentType_Unknown ); 00764 } 00765 00766 00768 // 00770 00771 QString HTTPRequest::GetResponseStatus( void ) 00772 { 00773 switch( m_nResponseStatus ) 00774 { 00775 case 200: return( "200 OK" ); 00776 case 201: return( "201 Created" ); 00777 case 202: return( "202 Accepted" ); 00778 case 206: return( "206 Partial Content" ); 00779 case 304: return( "304 Not Modified" ); 00780 case 400: return( "400 Bad Request" ); 00781 case 401: return( "401 Unauthorized" ); 00782 case 403: return( "403 Forbidden" ); 00783 case 404: return( "404 Not Found" ); 00784 case 405: return( "405 Method Not Allowed" ); 00785 case 406: return( "406 Not Acceptable" ); 00786 case 408: return( "408 Request Timeout" ); 00787 case 412: return( "412 Precondition Failed" ); 00788 case 413: return( "413 Request Entity Too Large" ); 00789 case 414: return( "414 Request-URI Too Long" ); 00790 case 415: return( "415 Unsupported Media Type" ); 00791 case 416: return( "416 Requested Range Not Satisfiable" ); 00792 case 417: return( "417 Expectation Failed" ); 00793 case 500: return( "500 Internal Server Error" ); 00794 case 501: return( "501 Not Implemented" ); 00795 case 502: return( "502 Bad Gateway" ); 00796 case 503: return( "503 Service Unavailable" ); 00797 case 504: return( "504 Gateway Timeout" ); 00798 case 505: return( "505 HTTP Version Not Supported" ); 00799 case 510: return( "510 Not Extended" ); 00800 } 00801 00802 return( QString( "%1 Unknown" ).arg( m_nResponseStatus )); 00803 } 00804 00806 // 00808 00809 QString HTTPRequest::GetResponseType( void ) 00810 { 00811 switch( m_eResponseType ) 00812 { 00813 case ResponseTypeXML : return( "text/xml; charset=\"UTF-8\"" ); 00814 case ResponseTypeHTML : return( "text/html; charset=\"UTF-8\"" ); 00815 default: break; 00816 } 00817 00818 return( "text/plain" ); 00819 } 00820 00822 // 00824 00825 QString HTTPRequest::GetMimeType( const QString &sFileExtension ) 00826 { 00827 QString ext; 00828 00829 for (int i = 0; i < g_nMIMELength; i++) 00830 { 00831 ext = g_MIMETypes[i].pszExtension; 00832 00833 if ( sFileExtension.toUpper() == ext.toUpper() ) 00834 return( g_MIMETypes[i].pszType ); 00835 } 00836 00837 return( "text/plain" ); 00838 } 00839 00841 // 00843 00844 QString HTTPRequest::TestMimeType( const QString &sFileName ) 00845 { 00846 QFileInfo info( sFileName ); 00847 QString sLOC = "HTTPRequest::TestMimeType(" + sFileName + ") - "; 00848 QString sSuffix = info.suffix().toLower(); 00849 QString sMIME = GetMimeType( sSuffix ); 00850 00851 if ( sSuffix == "nuv" ) // If a very old recording, might be an MPEG? 00852 { 00853 // Read the header to find out: 00854 QFile file( sFileName ); 00855 00856 if ( file.open(QIODevice::ReadOnly | QIODevice::Text) ) 00857 { 00858 QByteArray head = file.read(8); 00859 QString sHex = head.toHex(); 00860 00861 LOG(VB_UPNP, LOG_DEBUG, sLOC + "file starts with " + sHex); 00862 00863 if ( sHex == "000001ba44000400" ) // MPEG2 PS 00864 sMIME = "video/mpeg"; 00865 00866 if ( head == "MythTVVi" ) 00867 { 00868 file.seek(100); 00869 head = file.read(4); 00870 00871 if ( head == "DIVX" ) 00872 { 00873 LOG(VB_UPNP, LOG_DEBUG, sLOC + "('MythTVVi...DIVXLAME')"); 00874 sMIME = "video/mp4"; 00875 } 00876 // NuppelVideo is "RJPG" at byte 612 00877 // We could also check the audio (LAME or RAWA), 00878 // but since most UPnP clients choke on Nuppel, no need 00879 } 00880 00881 file.close(); 00882 } 00883 else 00884 LOG(VB_GENERAL, LOG_ERR, sLOC + "Could not read file"); 00885 } 00886 00887 LOG(VB_UPNP, LOG_INFO, sLOC + "type is " + sMIME); 00888 return sMIME; 00889 } 00890 00892 // 00894 00895 long HTTPRequest::GetParameters( QString sParams, QStringMap &mapParams ) 00896 { 00897 long nCount = 0; 00898 00899 LOG(VB_UPNP, LOG_DEBUG, QString("sParams: '%1'").arg(sParams)); 00900 00901 // This looks odd, but it is here to cope with stupid UPnP clients that 00902 // forget to de-escape the URLs. We can't map %26 here as well, as that 00903 // breaks anything that is trying to pass & as part of a name or value. 00904 sParams.replace( "&", "&" ); 00905 00906 if (sParams.length() > 0) 00907 { 00908 QStringList params = sParams.split('&', QString::SkipEmptyParts); 00909 00910 for ( QStringList::Iterator it = params.begin(); 00911 it != params.end(); ++it ) 00912 { 00913 QString sName = (*it).section( '=', 0, 0 ); 00914 QString sValue = (*it).section( '=', 1 ); 00915 sValue.replace("+"," "); 00916 00917 if ((sName.length() != 0) && (sValue.length() !=0)) 00918 { 00919 sName = QUrl::fromPercentEncoding(sName.toUtf8()); 00920 sValue = QUrl::fromPercentEncoding(sValue.toUtf8()); 00921 00922 mapParams.insert( sName.trimmed(), sValue ); 00923 nCount++; 00924 } 00925 } 00926 } 00927 00928 return nCount; 00929 } 00930 00931 00933 // 00935 00936 QString HTTPRequest::GetHeaderValue( const QString &sKey, QString sDefault ) 00937 { 00938 QStringMap::iterator it = m_mapHeaders.find( sKey.toLower() ); 00939 00940 if ( it == m_mapHeaders.end()) 00941 return( sDefault ); 00942 00943 return *it; 00944 } 00945 00946 00948 // 00950 00951 QString HTTPRequest::GetAdditionalHeaders( void ) 00952 { 00953 QString sHeader = m_szServerHeaders; 00954 00955 // Override the cache-control header on protected resources. 00956 00957 if (m_bProtected) 00958 m_mapRespHeaders[ "Cache-control" ] = "no-cache"; 00959 00960 for ( QStringMap::iterator it = m_mapRespHeaders.begin(); 00961 it != m_mapRespHeaders.end(); 00962 ++it ) 00963 { 00964 sHeader += it.key() + ": "; 00965 sHeader += *it + "\r\n"; 00966 } 00967 00968 return( sHeader ); 00969 } 00970 00972 // 00974 00975 bool HTTPRequest::GetKeepAlive() 00976 { 00977 bool bKeepAlive = true; 00978 00979 // if HTTP/1.0... must default to false 00980 00981 if ((m_nMajor == 1) && (m_nMinor == 0)) 00982 bKeepAlive = false; 00983 00984 // Read Connection Header... 00985 00986 QString sConnection = GetHeaderValue( "connection", "default" ).toLower(); 00987 00988 if ( sConnection == "close" ) 00989 bKeepAlive = false; 00990 else if (sConnection == "keep-alive") 00991 bKeepAlive = true; 00992 00993 return bKeepAlive; 00994 } 00995 00997 // 00999 01000 bool HTTPRequest::ParseRequest() 01001 { 01002 bool bSuccess = false; 01003 01004 try 01005 { 01006 // Read first line to determin requestType 01007 QString sRequestLine = ReadLine( 2000 ); 01008 01009 if ( sRequestLine.length() == 0) 01010 { 01011 LOG(VB_GENERAL, LOG_ERR, "Timeout reading first line of request." ); 01012 return false; 01013 } 01014 01015 // -=>TODO: Should read lines until a valid request??? 01016 01017 ProcessRequestLine( sRequestLine ); 01018 01019 // Make sure there are a few default values 01020 01021 m_mapHeaders[ "content-length" ] = "0"; 01022 m_mapHeaders[ "content-type" ] = "unknown"; 01023 01024 // Read Header 01025 01026 bool bDone = false; 01027 QString sLine = ReadLine( 2000 ); 01028 01029 while (( sLine.length() > 0 ) && !bDone ) 01030 { 01031 if (sLine != "\r\n") 01032 { 01033 QString sName = sLine.section( ':', 0, 0 ).trimmed(); 01034 QString sValue = sLine.section( ':', 1 ); 01035 01036 sValue.truncate( sValue.length() - 2 ); 01037 01038 if ((sName.length() != 0) && (sValue.length() !=0)) 01039 { 01040 m_mapHeaders.insert(sName.toLower(), sValue.trimmed()); 01041 01042 if (sName.contains( "dlna", Qt::CaseInsensitive )) 01043 { 01044 LOG(VB_UPNP, LOG_INFO, 01045 QString( "HTTPRequest::ParseRequest - Header: %1:%2") 01046 .arg(sName) .arg(sValue)); 01047 } 01048 } 01049 01050 sLine = ReadLine( 2000 ); 01051 } 01052 else 01053 bDone = true; 01054 } 01055 01056 // Check to see if we found the end of the header or we timed out. 01057 01058 if (!bDone) 01059 { 01060 LOG(VB_GENERAL, LOG_INFO, "Timeout waiting for request header." ); 01061 return false; 01062 } 01063 01064 m_bProtected = false; 01065 01066 if (IsUrlProtected( m_sBaseUrl )) 01067 { 01068 if (!Authenticated()) 01069 { 01070 m_eResponseType = ResponseTypeHTML; 01071 m_nResponseStatus = 401; 01072 m_mapRespHeaders["WWW-Authenticate"] = "Basic realm=\"MythTV\""; 01073 01074 m_response.write( Static401Error ); 01075 01076 return true; 01077 } 01078 01079 m_bProtected = true; 01080 } 01081 01082 bSuccess = true; 01083 01084 SetContentType( m_mapHeaders[ "content-type" ] ); 01085 01086 // Lets load payload if any. 01087 long nPayloadSize = m_mapHeaders[ "content-length" ].toLong(); 01088 01089 if (nPayloadSize > 0) 01090 { 01091 char *pszPayload = new char[ nPayloadSize + 2 ]; 01092 long nBytes = 0; 01093 01094 nBytes = ReadBlock( pszPayload, nPayloadSize, 5000 ); 01095 if (nBytes == nPayloadSize ) 01096 { 01097 m_sPayload = QString::fromUtf8( pszPayload, nPayloadSize ); 01098 01099 // See if the payload is just data from a form post 01100 if (m_eContentType == ContentType_Urlencoded) 01101 GetParameters( m_sPayload, m_mapParams ); 01102 } 01103 else 01104 { 01105 LOG(VB_GENERAL, LOG_ERR, 01106 QString("Unable to read entire payload (read %1 of %2 bytes)") 01107 .arg( nBytes ) .arg( nPayloadSize ) ); 01108 bSuccess = false; 01109 } 01110 01111 delete [] pszPayload; 01112 } 01113 01114 // Check to see if this is a SOAP encoded message 01115 01116 QString sSOAPAction = GetHeaderValue( "SOAPACTION", "" ); 01117 01118 if (sSOAPAction.length() > 0) 01119 bSuccess = ProcessSOAPPayload( sSOAPAction ); 01120 else 01121 ExtractMethodFromURL(); 01122 01123 #if 0 01124 if (m_sMethod != "*" ) 01125 LOG(VB_UPNP, LOG_DEBUG, 01126 QString("HTTPRequest::ParseRequest - Socket (%1) Base (%2) " 01127 "Method (%3) - Bytes in Socket Buffer (%4)") 01128 .arg(getSocketHandle()) .arg(m_sBaseUrl) 01129 .arg(m_sMethod) .arg(BytesAvailable())); 01130 #endif 01131 } 01132 catch(...) 01133 { 01134 LOG(VB_GENERAL, LOG_WARNING, 01135 "Unexpected exception in HTTPRequest::ParseRequest" ); 01136 } 01137 01138 return bSuccess; 01139 } 01140 01142 // 01144 01145 void HTTPRequest::ProcessRequestLine( const QString &sLine ) 01146 { 01147 m_sRawRequest = sLine; 01148 01149 QString sToken; 01150 QStringList tokens = sLine.split(m_procReqLineExp, QString::SkipEmptyParts); 01151 int nCount = tokens.count(); 01152 01153 // ---------------------------------------------------------------------- 01154 01155 if ( sLine.startsWith( QString("HTTP/") )) 01156 m_eType = RequestTypeResponse; 01157 else 01158 m_eType = RequestTypeUnknown; 01159 01160 // ---------------------------------------------------------------------- 01161 // if this is actually a response, then sLine's format will be: 01162 // HTTP/m.n <response code> <response text> 01163 // otherwise: 01164 // <method> <Resource URI> HTTP/m.n 01165 // ---------------------------------------------------------------------- 01166 01167 if (m_eType != RequestTypeResponse) 01168 { 01169 // ------------------------------------------------------------------ 01170 // Process as a request 01171 // ------------------------------------------------------------------ 01172 01173 if (nCount > 0) 01174 SetRequestType( tokens[0].trimmed() ); 01175 01176 if (nCount > 1) 01177 { 01178 //m_sBaseUrl = tokens[1].section( '?', 0, 0).trimmed(); 01179 m_sBaseUrl = (QUrl::fromPercentEncoding(tokens[1].toUtf8())) 01180 .section( '?', 0, 0).trimmed(); 01181 01182 m_sResourceUrl = m_sBaseUrl; // Save complete url without parameters 01183 01184 // Process any Query String Parameters 01185 QString sQueryStr = (QUrl::fromPercentEncoding(tokens[1].toUtf8())) 01186 .section( '?', 1, 1 ); 01187 01188 if (sQueryStr.length() > 0) 01189 GetParameters( sQueryStr, m_mapParams ); 01190 } 01191 01192 if (nCount > 2) 01193 SetRequestProtocol( tokens[2].trimmed() ); 01194 } 01195 else 01196 { 01197 // ------------------------------------------------------------------ 01198 // Process as a Response 01199 // ------------------------------------------------------------------ 01200 if (nCount > 0) 01201 SetRequestProtocol( tokens[0].trimmed() ); 01202 01203 if (nCount > 1) 01204 m_nResponseStatus = tokens[1].toInt(); 01205 } 01206 } 01207 01209 // 01211 01212 bool HTTPRequest::ParseRange( QString sRange, 01213 long long llSize, 01214 long long *pllStart, 01215 long long *pllEnd ) 01216 { 01217 // ---------------------------------------------------------------------- 01218 // -=>TODO: Only handle 1 range at this time... 01219 // should make work with full spec. 01220 // ---------------------------------------------------------------------- 01221 01222 if (sRange.length() == 0) 01223 return false; 01224 01225 // ---------------------------------------------------------------------- 01226 // remove any "bytes=" 01227 // ---------------------------------------------------------------------- 01228 int nIdx = sRange.indexOf(m_parseRangeExp); 01229 01230 if (nIdx < 0) 01231 return false; 01232 01233 if (nIdx > 0) 01234 sRange.remove( 0, nIdx ); 01235 01236 // ---------------------------------------------------------------------- 01237 // Split multiple ranges 01238 // ---------------------------------------------------------------------- 01239 01240 QStringList ranges = sRange.split(',', QString::SkipEmptyParts); 01241 01242 if (ranges.count() == 0) 01243 return false; 01244 01245 // ---------------------------------------------------------------------- 01246 // Split first range into its components 01247 // ---------------------------------------------------------------------- 01248 01249 QStringList parts = ranges[0].split('-'); 01250 01251 if (parts.count() != 2) 01252 return false; 01253 01254 if (parts[0].isNull() && parts[1].isNull()) 01255 return false; 01256 01257 // ---------------------------------------------------------------------- 01258 // 01259 // ---------------------------------------------------------------------- 01260 01261 bool conv_ok; 01262 if (parts[0].isNull()) 01263 { 01264 // ------------------------------------------------------------------ 01265 // Does it match "-####" 01266 // ------------------------------------------------------------------ 01267 01268 long long llValue = parts[1].toLongLong(&conv_ok); 01269 if (!conv_ok) return false; 01270 01271 *pllStart = llSize - llValue; 01272 *pllEnd = llSize - 1; 01273 } 01274 else if (parts[1].isNull()) 01275 { 01276 // ------------------------------------------------------------------ 01277 // Does it match "####-" 01278 // ------------------------------------------------------------------ 01279 01280 *pllStart = parts[0].toLongLong(&conv_ok); 01281 01282 if (!conv_ok) 01283 return false; 01284 01285 *pllEnd = llSize - 1; 01286 } 01287 else 01288 { 01289 // ------------------------------------------------------------------ 01290 // Must be "####-####" 01291 // ------------------------------------------------------------------ 01292 01293 *pllStart = parts[0].toLongLong(&conv_ok); 01294 if (!conv_ok) return false; 01295 *pllEnd = parts[1].toLongLong(&conv_ok); 01296 if (!conv_ok) return false; 01297 01298 if (*pllStart > *pllEnd) 01299 return false; 01300 } 01301 01302 #if 0 01303 LOG(VB_GENERAL, LOG_DEBUG, QString("%1 Range Requested %2 - %3") 01304 .arg(getSocketHandle()) .arg(*pllStart) .arg(*pllEnd)); 01305 #endif 01306 01307 return true; 01308 } 01309 01311 // 01313 01314 void HTTPRequest::ExtractMethodFromURL() 01315 { 01316 // Strip out leading http://192.168.1.1:6544/ -> / 01317 // Should fix #8678 01318 QRegExp sRegex("^http://.*/"); 01319 sRegex.setMinimal(true); 01320 m_sBaseUrl.replace(sRegex, "/"); 01321 01322 QStringList sList = m_sBaseUrl.split('/', QString::SkipEmptyParts); 01323 01324 m_sMethod = ""; 01325 01326 if (sList.size() > 0) 01327 { 01328 m_sMethod = sList.last(); 01329 sList.pop_back(); 01330 } 01331 01332 m_sBaseUrl = '/' + sList.join( "/" ); 01333 LOG(VB_UPNP, LOG_INFO, QString("ExtractMethodFromURL(end) : %1 : %2") 01334 .arg(m_sMethod).arg(m_sBaseUrl)); 01335 } 01336 01338 // 01340 01341 bool HTTPRequest::ProcessSOAPPayload( const QString &sSOAPAction ) 01342 { 01343 bool bSuccess = false; 01344 01345 // ---------------------------------------------------------------------- 01346 // Open Supplied XML uPnp Description file. 01347 // ---------------------------------------------------------------------- 01348 01349 LOG(VB_UPNP, LOG_DEBUG, 01350 QString("HTTPRequest::ProcessSOAPPayload : %1 : ").arg(sSOAPAction)); 01351 QDomDocument doc ( "request" ); 01352 01353 QString sErrMsg; 01354 int nErrLine = 0; 01355 int nErrCol = 0; 01356 01357 if (!doc.setContent( m_sPayload, true, &sErrMsg, &nErrLine, &nErrCol )) 01358 { 01359 LOG(VB_GENERAL, LOG_ERR, 01360 QString( "Error parsing request at line: %1 column: %2 : %3" ) 01361 .arg(nErrLine) .arg(nErrCol) .arg(sErrMsg)); 01362 return( false ); 01363 } 01364 01365 // -------------------------------------------------------------- 01366 // XML Document Loaded... now parse it 01367 // -------------------------------------------------------------- 01368 01369 QString sService; 01370 01371 if (sSOAPAction.contains( '#' )) 01372 { 01373 m_sNameSpace = sSOAPAction.section( '#', 0, 0).remove( 0, 1); 01374 m_sMethod = sSOAPAction.section( '#', 1 ); 01375 m_sMethod.remove( m_sMethod.length()-1, 1 ); 01376 } 01377 else 01378 { 01379 if (sSOAPAction.contains( '/' )) 01380 { 01381 int nPos = sSOAPAction.lastIndexOf( '/' ); 01382 m_sNameSpace = sSOAPAction.mid(1, nPos); 01383 m_sMethod = sSOAPAction.mid(nPos + 1, 01384 sSOAPAction.length() - nPos - 2); 01385 01386 nPos = m_sNameSpace.lastIndexOf( '/', -2); 01387 sService = m_sNameSpace.mid(nPos + 1, 01388 m_sNameSpace.length() - nPos - 2); 01389 m_sNameSpace = m_sNameSpace.mid( 0, nPos ); 01390 } 01391 else 01392 { 01393 m_sNameSpace = QString::null; 01394 m_sMethod = sSOAPAction; 01395 m_sMethod.remove( QChar( '\"' ) ); 01396 } 01397 } 01398 01399 QDomNodeList oNodeList = doc.elementsByTagNameNS( m_sNameSpace, m_sMethod ); 01400 01401 if (oNodeList.count() == 0) 01402 oNodeList = 01403 doc.elementsByTagNameNS("http://schemas.xmlsoap.org/soap/envelope/", 01404 "Body"); 01405 01406 if (oNodeList.count() > 0) 01407 { 01408 QDomNode oMethod = oNodeList.item(0); 01409 01410 if (!oMethod.isNull()) 01411 { 01412 m_bSOAPRequest = true; 01413 01414 for ( QDomNode oNode = oMethod.firstChild(); !oNode.isNull(); 01415 oNode = oNode.nextSibling() ) 01416 { 01417 QDomElement e = oNode.toElement(); 01418 01419 if (!e.isNull()) 01420 { 01421 QString sName = e.tagName(); 01422 QString sValue = ""; 01423 01424 QDomText oText = oNode.firstChild().toText(); 01425 01426 if (!oText.isNull()) 01427 sValue = oText.nodeValue(); 01428 01429 sName = QUrl::fromPercentEncoding(sName.toUtf8()); 01430 sValue = QUrl::fromPercentEncoding(sValue.toUtf8()); 01431 01432 m_mapParams.insert( sName.trimmed(), sValue ); 01433 } 01434 } 01435 01436 bSuccess = true; 01437 } 01438 } 01439 01440 return bSuccess; 01441 } 01442 01444 // 01446 01447 Serializer *HTTPRequest::GetSerializer() 01448 { 01449 Serializer *pSerializer = NULL; 01450 01451 if (m_bSOAPRequest) 01452 pSerializer = (Serializer *)new SoapSerializer(&m_response, 01453 m_sNameSpace, m_sMethod); 01454 else 01455 { 01456 QString sAccept = GetHeaderValue( "Accept", "*/*" ); 01457 01458 if (sAccept.contains( "application/json", Qt::CaseInsensitive )) 01459 pSerializer = (Serializer *)new JSONSerializer(&m_response, 01460 m_sMethod); 01461 else if (sAccept.contains( "text/javascript", Qt::CaseInsensitive )) 01462 pSerializer = (Serializer *)new JSONSerializer(&m_response, 01463 m_sMethod); 01464 else if (sAccept.contains( "text/x-apple-plist+xml", Qt::CaseInsensitive )) 01465 pSerializer = (Serializer *)new XmlPListSerializer(&m_response); 01466 } 01467 01468 // Default to XML 01469 01470 if (pSerializer == NULL) 01471 pSerializer = (Serializer *)new XmlSerializer(&m_response, m_sMethod); 01472 01473 return pSerializer; 01474 } 01475 01477 // 01479 01480 QString HTTPRequest::Encode(const QString &sIn) 01481 { 01482 QString sStr = sIn; 01483 #if 0 01484 LOG(VB_UPNP, LOG_DEBUG, 01485 QString("HTTPRequest::Encode Input : %1").arg(sStr)); 01486 #endif 01487 sStr.replace('&', "&" ); // This _must_ come first 01488 sStr.replace('<', "<" ); 01489 sStr.replace('>', ">" ); 01490 sStr.replace('"', """); 01491 sStr.replace("'", "'"); 01492 01493 #if 0 01494 LOG(VB_UPNP, LOG_DEBUG, 01495 QString("HTTPRequest::Encode Output : %1").arg(sStr)); 01496 #endif 01497 return sStr; 01498 } 01499 01501 // 01503 01504 QString HTTPRequest::GetETagHash(const QByteArray &data) 01505 { 01506 QByteArray hash = QCryptographicHash::hash( data.data(), QCryptographicHash::Sha1); 01507 01508 return ("\"" + hash.toHex() + "\""); 01509 } 01510 01512 // 01514 01515 bool HTTPRequest::IsUrlProtected( const QString &sBaseUrl ) 01516 { 01517 QString sProtected = UPnp::GetConfiguration()->GetValue( "HTTP/Protected/Urls", "/setup;/Config" ); 01518 01519 QStringList oList = sProtected.split( ';' ); 01520 01521 for( int nIdx = 0; nIdx < oList.count(); nIdx++) 01522 { 01523 if (sBaseUrl.startsWith( oList[nIdx], Qt::CaseInsensitive )) 01524 return true; 01525 } 01526 01527 return false; 01528 } 01529 01531 // 01533 01534 bool HTTPRequest::Authenticated() 01535 { 01536 QStringList oList = m_mapHeaders[ "authorization" ].split( ' ' ); 01537 01538 if (oList.count() < 2) 01539 return false; 01540 01541 if (oList[0].compare( "basic", Qt::CaseInsensitive ) != 0) 01542 return false; 01543 01544 QString sCredentials = QByteArray::fromBase64( oList[1].toUtf8() ); 01545 01546 oList = sCredentials.split( ':' ); 01547 01548 if (oList.count() < 2) 01549 return false; 01550 01551 QString sUserName = UPnp::GetConfiguration()->GetValue( "HTTP/Protected/UserName", "admin" ); 01552 01553 01554 if (oList[0].compare( sUserName, Qt::CaseInsensitive ) != 0) 01555 return false; 01556 01557 QString sPassword = UPnp::GetConfiguration()->GetValue( "HTTP/Protected/Password", 01558 /* mythtv */ "8hDRxR1+E/n3/s3YUOhF+lUw7n4=" ); 01559 01560 QCryptographicHash crypto( QCryptographicHash::Sha1 ); 01561 01562 crypto.addData( oList[1].toUtf8() ); 01563 01564 QString sPasswordHash( crypto.result().toBase64() ); 01565 01566 if (sPasswordHash != sPassword ) 01567 return false; 01568 01569 return true; 01570 } 01571 01572 01575 // 01576 // BufferedSocketDeviceRequest Class Implementation 01577 // 01580 01581 BufferedSocketDeviceRequest::BufferedSocketDeviceRequest( BufferedSocketDevice *pSocket ) 01582 { 01583 m_pSocket = pSocket; 01584 } 01585 01587 // 01589 01590 qlonglong BufferedSocketDeviceRequest::BytesAvailable(void) 01591 { 01592 if (m_pSocket) 01593 return( m_pSocket->BytesAvailable() ); 01594 01595 return( 0 ); 01596 } 01597 01599 // 01601 01602 qulonglong BufferedSocketDeviceRequest::WaitForMore( int msecs, bool *timeout ) 01603 { 01604 if (m_pSocket) 01605 return( m_pSocket->WaitForMore( msecs, timeout )); 01606 01607 return( 0 ); 01608 } 01609 01611 // 01613 01614 bool BufferedSocketDeviceRequest::CanReadLine() 01615 { 01616 if (m_pSocket) 01617 return( m_pSocket->CanReadLine() ); 01618 01619 return( false ); 01620 } 01621 01623 // 01625 01626 QString BufferedSocketDeviceRequest::ReadLine( int msecs ) 01627 { 01628 QString sLine; 01629 01630 if (m_pSocket) 01631 sLine = m_pSocket->ReadLine( msecs ); 01632 01633 return( sLine ); 01634 } 01635 01637 // 01639 01640 qlonglong BufferedSocketDeviceRequest::ReadBlock( 01641 char *pData, qulonglong nMaxLen, int msecs) 01642 { 01643 if (m_pSocket) 01644 { 01645 if (msecs == 0) 01646 return( m_pSocket->ReadBlock( pData, nMaxLen )); 01647 else 01648 { 01649 bool bTimeout = false; 01650 01651 while ( (BytesAvailable() < (int)nMaxLen) && !bTimeout ) 01652 m_pSocket->WaitForMore( msecs, &bTimeout ); 01653 01654 // Just return what we have even if timed out. 01655 01656 return( m_pSocket->ReadBlock( pData, nMaxLen )); 01657 } 01658 } 01659 01660 return( -1 ); 01661 } 01662 01664 // 01666 01667 qlonglong BufferedSocketDeviceRequest::WriteBlock( 01668 const char *pData, qulonglong nLen) 01669 { 01670 if (m_pSocket) 01671 return( m_pSocket->WriteBlock( pData, nLen )); 01672 01673 return( -1 ); 01674 } 01675 01677 // 01679 01680 qlonglong BufferedSocketDeviceRequest::WriteBlockDirect( 01681 const char *pData, qulonglong nLen) 01682 { 01683 if (m_pSocket) 01684 return( m_pSocket->WriteBlockDirect( pData, nLen )); 01685 01686 return( -1 ); 01687 } 01688 01690 // 01692 01693 QString BufferedSocketDeviceRequest::GetHostAddress() 01694 { 01695 return( m_pSocket->SocketDevice()->address().toString() ); 01696 } 01697 01699 // 01701 01702 QString BufferedSocketDeviceRequest::GetPeerAddress() 01703 { 01704 return( m_pSocket->SocketDevice()->peerAddress().toString() ); 01705 } 01706 01708 // 01710 01711 void BufferedSocketDeviceRequest::SetBlocking( bool bBlock ) 01712 { 01713 if (m_pSocket) 01714 return( m_pSocket->SocketDevice()->setBlocking( bBlock )); 01715 } 01716 01718 // 01720 01721 bool BufferedSocketDeviceRequest::IsBlocking() 01722 { 01723 if (m_pSocket) 01724 return( m_pSocket->SocketDevice()->blocking()); 01725 01726 return false; 01727 } 01728
1.7.6.1