00001
00002
00003
00004
00005
00006
00007
00008
00009
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
00088 { "mkv" , "video/x-mkv" },
00089 { "mka" , "audio/x-matroska" },
00090 { "wmv" , "video/x-ms-wmv" },
00091
00092 { "ogg" , "audio/ogg" },
00093 { "ogv" , "video/ogg" },
00094 { "ogx" , "application/ogg" },
00095 { "oga" , "audio/ogg" },
00096
00097 { "flac", "audio/x-flac" },
00098 { "m4a" , "audio/x-m4a" },
00099
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
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
00242
00243
00244 #ifdef USE_SETSOCKOPT
00245
00246 setsockopt( getSocketHandle(), SOL_TCP, TCP_CORK, &g_on, sizeof( g_on ));
00247 #endif
00248
00249
00250
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
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
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
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
00305
00306
00307 if (( m_eType != RequestTypeHead ) && ( nContentLen > 0 ))
00308 {
00309 nBytes += SendData( pBuffer, 0, nContentLen );
00310 }
00311
00312
00313
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
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
00351
00352
00353 #ifdef USE_SETSOCKOPT
00354
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
00366
00367
00368 llSize = llEnd = tmpFile.size( );
00369
00370 m_nResponseStatus = 200;
00371
00372
00373
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
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
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
00430
00431
00432
00433
00434
00435 QString rHeader = BuildHeader( llSize );
00436 QByteArray sHeader = rHeader.toUtf8();
00437 nBytes = WriteBlockDirect( sHeader.constData(), sHeader.length() );
00438
00439
00440
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
00464
00465
00466 #ifdef USE_SETSOCKOPT
00467 setsockopt( getSocketHandle(), SOL_TCP, TCP_CORK, &g_off, sizeof( g_off ));
00468 #endif
00469
00470
00471
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
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
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
00558
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
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" )
00852 {
00853
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" )
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
00877
00878
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
00902
00903
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
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
00980
00981 if ((m_nMajor == 1) && (m_nMinor == 0))
00982 bKeepAlive = false;
00983
00984
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
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
01016
01017 ProcessRequestLine( sRequestLine );
01018
01019
01020
01021 m_mapHeaders[ "content-length" ] = "0";
01022 m_mapHeaders[ "content-type" ] = "unknown";
01023
01024
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
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
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
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
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
01162
01163
01164
01165
01166
01167 if (m_eType != RequestTypeResponse)
01168 {
01169
01170
01171
01172
01173 if (nCount > 0)
01174 SetRequestType( tokens[0].trimmed() );
01175
01176 if (nCount > 1)
01177 {
01178
01179 m_sBaseUrl = (QUrl::fromPercentEncoding(tokens[1].toUtf8()))
01180 .section( '?', 0, 0).trimmed();
01181
01182 m_sResourceUrl = m_sBaseUrl;
01183
01184
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
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
01219
01220
01221
01222 if (sRange.length() == 0)
01223 return false;
01224
01225
01226
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
01238
01239
01240 QStringList ranges = sRange.split(',', QString::SkipEmptyParts);
01241
01242 if (ranges.count() == 0)
01243 return false;
01244
01245
01246
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
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
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
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
01317
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
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
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
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('&', "&" );
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 "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
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
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