|
MythTV
0.26-pre
|
00001 00002 // Program Name: servicehost.cpp 00003 // Created : Jan. 19, 2010 00004 // 00005 // Purpose : Service Host Abstract Class 00006 // 00007 // Copyright (c) 2010 David Blain <dblain@mythtv.org> 00008 // 00009 // Licensed under the GPL v2 or later, see COPYING for details 00010 // 00012 00013 #include <QDomDocument> 00014 00015 #include "mythlogging.h" 00016 #include "servicehost.h" 00017 #include "wsdl.h" 00018 #include "xsd.h" 00019 00020 #define _MAX_PARAMS 256 00021 00023 // 00025 00026 MethodInfo::MethodInfo() 00027 { 00028 m_eRequestType = (RequestType)(RequestTypeGet | RequestTypePost | 00029 RequestTypeHead); 00030 } 00031 00033 // 00035 00036 QVariant MethodInfo::Invoke( Service *pService, const QStringMap &reqParams ) 00037 { 00038 HttpRedirectException exception; 00039 bool bExceptionThrown = false; 00040 00041 if (!pService) 00042 throw; 00043 00044 QList<QByteArray> paramNames = m_oMethod.parameterNames(); 00045 QList<QByteArray> paramTypes = m_oMethod.parameterTypes(); 00046 00047 // ---------------------------------------------------------------------- 00048 // Create Parameter array (Can't have more than _MAX_PARAMS parameters).... 00049 // switched to static array for performance. 00050 // ---------------------------------------------------------------------- 00051 00052 void *param[ _MAX_PARAMS ]; 00053 int types[ _MAX_PARAMS ]; 00054 00055 memset( param, 0, _MAX_PARAMS * sizeof(void *)); 00056 memset( types, 0, _MAX_PARAMS * sizeof(int)); 00057 00058 try 00059 { 00060 // -------------------------------------------------------------- 00061 // Add a place for the Return value 00062 // -------------------------------------------------------------- 00063 00064 int nRetIdx = QMetaType::type( m_oMethod.typeName() ); 00065 00066 if (nRetIdx != 0) 00067 { 00068 param[ 0 ] = QMetaType::construct( nRetIdx ); 00069 types[ 0 ] = nRetIdx; 00070 } 00071 else 00072 { 00073 param[ 0 ] = NULL; 00074 types[ 0 ] = 0; 00075 } 00076 00077 // -------------------------------------------------------------- 00078 // Fill in parameters from request values 00079 // -------------------------------------------------------------- 00080 00081 for( int nIdx = 0; nIdx < paramNames.length(); nIdx++ ) 00082 { 00083 QString sValue = reqParams[ paramNames[ nIdx ] ]; 00084 QString sParamType = paramTypes[ nIdx ]; 00085 00086 int nId = QMetaType::type( paramTypes[ nIdx ] ); 00087 void *pParam = NULL; 00088 00089 if (nId != 0) 00090 { 00091 pParam = QMetaType::construct( nId ); 00092 } 00093 else 00094 { 00095 LOG(VB_GENERAL, LOG_ERR, 00096 QString("MethodInfo::Invoke - Type unknown '%1'") 00097 .arg(sParamType)); 00098 } 00099 00100 types[nIdx+1] = nId; 00101 param[nIdx+1] = pService->ConvertToParameterPtr( nId, sParamType, 00102 pParam, sValue ); 00103 } 00104 00105 #if 0 00106 QThread *currentThread = QThread::currentThread(); 00107 QThread *objectThread = pService->thread(); 00108 00109 if (currentThread == objectThread) 00110 LOG(VB_UPNP, LOG_DEBUG, "*** Threads are same ***"); 00111 else 00112 LOG(VB_UPNP, LOG_DEBUG, "*** Threads are Different!!! ***"); 00113 #endif 00114 00115 pService->qt_metacall( QMetaObject::InvokeMetaMethod, 00116 m_nMethodIndex, 00117 param ); 00118 00119 // -------------------------------------------------------------- 00120 // Delete param array, skip return parameter since not dynamically 00121 // created. 00122 // -------------------------------------------------------------- 00123 00124 for (int nIdx=1; nIdx < paramNames.length()+1; nIdx++) 00125 { 00126 if ((types[ nIdx ] != 0) && (param[ nIdx ] != NULL)) 00127 QMetaType::destroy( types[ nIdx ], param[ nIdx ] ); 00128 } 00129 } 00130 catch (QString &sMsg) 00131 { 00132 LOG(VB_GENERAL, LOG_ERR, 00133 QString("MethodInfo::Invoke - An Exception Occurred: %1") 00134 .arg(sMsg)); 00135 00136 if ((types[ 0 ] != 0) && (param[ 0 ] != NULL )) 00137 QMetaType::destroy( types[ 0 ], param[ 0 ] ); 00138 00139 throw; 00140 } 00141 catch (HttpRedirectException &ex) 00142 { 00143 bExceptionThrown = true; 00144 exception = ex; 00145 } 00146 catch (...) 00147 { 00148 LOG(VB_GENERAL, LOG_INFO, 00149 "MethodInfo::Invoke - An Exception Occurred" ); 00150 } 00151 00152 // -------------------------------------------------------------- 00153 // return the result after converting to a QVariant 00154 // -------------------------------------------------------------- 00155 00156 QVariant vReturn; 00157 00158 if ( param[ 0 ] != NULL) 00159 { 00160 vReturn = pService->ConvertToVariant( types[ 0 ], param[ 0 ] ); 00161 00162 if (types[ 0 ] != 0) 00163 QMetaType::destroy( types[ 0 ], param[ 0 ] ); 00164 } 00165 00166 // -------------------------------------------------------------- 00167 // Re-throw exception if needed. 00168 // -------------------------------------------------------------- 00169 00170 if (bExceptionThrown) 00171 throw exception; 00172 00173 return vReturn; 00174 } 00175 00178 // 00179 // 00180 // 00183 00184 ServiceHost::ServiceHost(const QMetaObject &metaObject, 00185 const QString &sExtensionName, 00186 const QString &sBaseUrl, 00187 const QString &sSharePath ) 00188 : HttpServerExtension ( sExtensionName, sSharePath ) 00189 { 00190 m_oMetaObject = metaObject; 00191 m_sBaseUrl = sBaseUrl; 00192 00193 // -------------------------------------------------------------- 00194 // Read in all callable methods and cache information about them 00195 // -------------------------------------------------------------- 00196 00197 for (int nIdx = 0; nIdx < m_oMetaObject.methodCount(); nIdx++) 00198 { 00199 QMetaMethod method = m_oMetaObject.method( nIdx ); 00200 00201 if ((method.methodType() == QMetaMethod::Slot ) && 00202 (method.access() == QMetaMethod::Public )) 00203 { 00204 QString sName( method.signature() ); 00205 00206 // ------------------------------------------------------ 00207 // Ignore the following methods... 00208 // ------------------------------------------------------ 00209 00210 if (sName == "deleteLater()") 00211 continue; 00212 00213 // ------------------------------------------------------ 00214 00215 MethodInfo oInfo; 00216 00217 oInfo.m_nMethodIndex = nIdx; 00218 oInfo.m_sName = sName.section( '(', 0, 0 ); 00219 oInfo.m_oMethod = method; 00220 oInfo.m_eRequestType = (RequestType)(RequestTypeGet | 00221 RequestTypePost | 00222 RequestTypeHead); 00223 00224 QString sMethodClassInfo = oInfo.m_sName + "_Method"; 00225 00226 int nClassIdx = 00227 m_oMetaObject.indexOfClassInfo(sMethodClassInfo.toAscii()); 00228 00229 if (nClassIdx >=0) 00230 { 00231 QString sRequestType = 00232 m_oMetaObject.classInfo(nClassIdx).value(); 00233 00234 if (sRequestType == "POST") 00235 oInfo.m_eRequestType = RequestTypePost; 00236 else if (sRequestType == "GET" ) 00237 oInfo.m_eRequestType = (RequestType)(RequestTypeGet | 00238 RequestTypeHead); 00239 } 00240 00241 m_Methods.insert( oInfo.m_sName, oInfo ); 00242 } 00243 } 00244 } 00245 00247 // 00249 00250 ServiceHost::~ServiceHost() 00251 { 00252 } 00253 00255 // 00257 00258 QStringList ServiceHost::GetBasePaths() 00259 { 00260 return QStringList( m_sBaseUrl ); 00261 } 00262 00264 // 00266 00267 bool ServiceHost::ProcessRequest( HTTPRequest *pRequest ) 00268 { 00269 bool bHandled = false; 00270 Service *pService = NULL; 00271 00272 try 00273 { 00274 if (pRequest) 00275 { 00276 if (pRequest->m_sBaseUrl != m_sBaseUrl) 00277 return false; 00278 00279 LOG(VB_UPNP, LOG_INFO, 00280 QString("ServiceHost::ProcessRequest: %1 : %2") 00281 .arg(pRequest->m_sMethod) .arg(pRequest->m_sRawRequest)); 00282 00283 // -------------------------------------------------------------- 00284 // Check to see if they are requesting the WSDL service Definition 00285 // -------------------------------------------------------------- 00286 00287 if (( pRequest->m_eType == RequestTypeGet ) && 00288 ( pRequest->m_sMethod == "wsdl" )) 00289 { 00290 pService = qobject_cast<Service*>(m_oMetaObject.newInstance()); 00291 00292 Wsdl wsdl( this ); 00293 00294 wsdl.GetWSDL( pRequest ); 00295 00296 delete pService; 00297 return true; 00298 } 00299 00300 // -------------------------------------------------------------- 00301 // Check to see if they are requesting XSD - Type Definition 00302 // -------------------------------------------------------------- 00303 00304 if (( pRequest->m_eType == RequestTypeGet ) && 00305 ( pRequest->m_sMethod == "xsd" )) 00306 { 00307 if ( pRequest->m_mapParams.count() > 0) 00308 { 00309 pService = qobject_cast<Service*>(m_oMetaObject.newInstance()); 00310 00311 Xsd xsd; 00312 00313 xsd.GetXSD( pRequest, pRequest->m_mapParams[ "type" ] ); 00314 00315 delete pService; 00316 } 00317 00318 return true; 00319 } 00320 00321 if (( pRequest->m_eType == RequestTypeGet ) && 00322 ( pRequest->m_sMethod == "version" )) 00323 { 00324 00325 int nClassIdx = m_oMetaObject.indexOfClassInfo( "version" ); 00326 00327 if (nClassIdx >=0) 00328 { 00329 QString sVersion = 00330 m_oMetaObject.classInfo(nClassIdx).value(); 00331 00332 return FormatResponse( pRequest, QVariant( sVersion )); 00333 } 00334 } 00335 00336 // -------------------------------------------------------------- 00337 // Allow a more REST like calling convention. If the Method 00338 // Name isn't found, search for one with the request method 00339 // appended to the name ( "Get" or "Put" for POST) 00340 // -------------------------------------------------------------- 00341 00342 QString sMethodName = pRequest->m_sMethod; 00343 bool bMethodFound = false; 00344 00345 if (m_Methods.contains(sMethodName)) 00346 bMethodFound = true; 00347 else 00348 { 00349 switch( pRequest->m_eType ) 00350 { 00351 case RequestTypeHead: 00352 case RequestTypeGet : 00353 sMethodName = "Get" + sMethodName; 00354 break; 00355 case RequestTypePost: 00356 sMethodName = "Put" + sMethodName; 00357 break; 00358 } 00359 00360 if (m_Methods.contains(sMethodName)) 00361 bMethodFound = true; 00362 } 00363 00364 if (bMethodFound) 00365 { 00366 MethodInfo oInfo = m_Methods.value( sMethodName ); 00367 00368 if (( pRequest->m_eType & oInfo.m_eRequestType ) != 0) 00369 { 00370 // ------------------------------------------------------ 00371 // Create new Instance of the Service Class so 00372 // it's guaranteed to be on the same thread 00373 // since we are making direct calls into it. 00374 // ------------------------------------------------------ 00375 00376 pService = 00377 qobject_cast<Service*>(m_oMetaObject.newInstance()); 00378 00379 QVariant vResult = oInfo.Invoke(pService, 00380 pRequest->m_mapParams); 00381 00382 bHandled = FormatResponse( pRequest, vResult ); 00383 } 00384 } 00385 00386 if (!bHandled) 00387 UPnp::FormatErrorResponse( pRequest, UPnPResult_InvalidAction ); 00388 } 00389 } 00390 catch (HttpRedirectException &ex) 00391 { 00392 UPnp::FormatRedirectResponse( pRequest, ex.hostName ); 00393 bHandled = true; 00394 } 00395 catch (HttpException &ex) 00396 { 00397 LOG(VB_GENERAL, LOG_ERR, ex.msg); 00398 UPnp::FormatErrorResponse( pRequest, UPnPResult_ActionFailed, ex.msg ); 00399 00400 bHandled = true; 00401 00402 } 00403 catch (QString &sMsg) 00404 { 00405 LOG(VB_GENERAL, LOG_ERR, sMsg); 00406 UPnp::FormatErrorResponse( pRequest, UPnPResult_ActionFailed, sMsg ); 00407 00408 bHandled = true; 00409 } 00410 catch ( ...) 00411 { 00412 QString sMsg( "ServiceHost::ProcessRequest - Unexpected Exception" ); 00413 00414 LOG(VB_GENERAL, LOG_ERR, sMsg); 00415 UPnp::FormatErrorResponse( pRequest, UPnPResult_ActionFailed, sMsg ); 00416 00417 bHandled = true; 00418 } 00419 00420 if (pService != NULL) 00421 delete pService; 00422 00423 return bHandled; 00424 } 00425 00427 // 00429 00430 bool ServiceHost::FormatResponse( HTTPRequest *pRequest, QObject *pResults ) 00431 { 00432 if (pResults != NULL) 00433 { 00434 Serializer *pSer = pRequest->GetSerializer(); 00435 00436 pSer->Serialize( pResults ); 00437 00438 pRequest->FormatActionResponse( pSer ); 00439 00440 delete pResults; 00441 00442 return true; 00443 } 00444 else 00445 UPnp::FormatErrorResponse( pRequest, UPnPResult_ActionFailed, "Call to method failed" ); 00446 00447 return false; 00448 } 00449 00451 // 00453 00454 bool ServiceHost::FormatResponse( HTTPRequest *pRequest, QFileInfo oInfo ) 00455 { 00456 QString sName = oInfo.absoluteFilePath(); 00457 00458 if (oInfo.exists()) 00459 { 00460 if (oInfo.isSymLink()) 00461 pRequest->FormatFileResponse( oInfo.symLinkTarget() ); 00462 else 00463 pRequest->FormatFileResponse( oInfo.absoluteFilePath() ); 00464 } 00465 else 00466 { 00467 // force return as a 404... 00468 pRequest->FormatFileResponse( "" ); 00469 } 00470 00471 return true; 00472 } 00473 00474 00476 // 00478 00479 bool ServiceHost::FormatResponse( HTTPRequest *pRequest, QVariant vValue ) 00480 { 00481 if ( vValue.canConvert< QObject* >()) 00482 { 00483 const QObject *pObject = vValue.value< QObject* >(); 00484 00485 return FormatResponse( pRequest, (QObject *)pObject ); 00486 } 00487 00488 if ( vValue.canConvert< QFileInfo >()) 00489 { 00490 const QFileInfo oFileInfo = vValue.value< QFileInfo >(); 00491 00492 return FormatResponse( pRequest, oFileInfo ); 00493 } 00494 00495 // ---------------------------------------------------------------------- 00496 // Simple Variant... serialize it. 00497 // ---------------------------------------------------------------------- 00498 00499 Serializer *pSer = pRequest->GetSerializer(); 00500 00501 pSer->Serialize( vValue, vValue.typeName() ); 00502 00503 pRequest->FormatActionResponse( pSer ); 00504 00505 return true; 00506 }
1.7.6.1