00001
00002
00003
00004
00005
00006
00007
00008
00010
00011 #include "upnp.h"
00012 #include "eventing.h"
00013 #include "upnptaskevent.h"
00014
00015 #include "util.h"
00016
00017 #include <qtextstream.h>
00018 #include <math.h>
00019 #include <qregexp.h>
00020
00022
00024
00025 Eventing::Eventing( const QString &sExtensionName, const QString &sEventMethodName ) : HttpServerExtension( sExtensionName )
00026 {
00027 m_sEventMethodName = sEventMethodName;
00028 m_nSubscriptionDuration = UPnp::g_pConfig->GetValue( "UPnP/SubscriptionDuration", 1800 );
00029 }
00030
00032
00034
00035 Eventing::~Eventing()
00036 {
00037 }
00038
00040
00042
00043 inline short Eventing::HoldEvents()
00044 {
00045
00046
00047
00048 short nVal;
00049
00050 m_mutex.lock();
00051 nVal = (m_nHoldCount++);
00052 m_mutex.unlock();
00053
00054 return nVal;
00055 }
00056
00058
00060
00061 inline short Eventing::ReleaseEvents()
00062 {
00063
00064
00065 short nVal;
00066
00067 m_mutex.lock();
00068 nVal = (m_nHoldCount--);
00069 m_mutex.unlock();
00070
00071 if (nVal == 0)
00072 Notify();
00073
00074 return nVal;
00075 }
00076
00077
00079
00081
00082 bool Eventing::ProcessRequest( HttpWorkerThread * , HTTPRequest *pRequest )
00083 {
00084 if (pRequest)
00085 {
00086 if ( pRequest->m_sBaseUrl != "/" )
00087 return false;
00088
00089 if ( pRequest->m_sMethod != m_sEventMethodName )
00090 return false;
00091
00092 VERBOSE( VB_UPNP, QString("Eventing::ProcessRequest - Method (%1)").arg(pRequest->m_sMethod ));
00093
00094 switch( pRequest->m_eType )
00095 {
00096 case RequestTypeSubscribe : HandleSubscribe ( pRequest ); break;
00097 case RequestTypeUnsubscribe : HandleUnsubscribe ( pRequest ); break;
00098 default:
00099 UPnp::FormatErrorResponse( pRequest, UPnPResult_InvalidAction );
00100 break;
00101 }
00102 }
00103
00104 return( true );
00105
00106 }
00107
00109
00111
00112 void Eventing::ExecutePostProcess( )
00113 {
00114
00115
00116
00117 if (m_pInitializeSubscriber != NULL)
00118 {
00119 NotifySubscriber( m_pInitializeSubscriber );
00120
00121 m_pInitializeSubscriber = NULL;
00122 }
00123 }
00124
00126
00128
00129 void Eventing::HandleSubscribe( HTTPRequest *pRequest )
00130 {
00131 pRequest->m_eResponseType = ResponseTypeXML;
00132 pRequest->m_nResponseStatus = 412;
00133
00134 QString sCallBack = pRequest->GetHeaderValue( "CALLBACK", "" );
00135 QString sNT = pRequest->GetHeaderValue( "NT" , "" );
00136 QString sTimeout = pRequest->GetHeaderValue( "TIMOUT" , "" );
00137 QString sSID = pRequest->GetHeaderValue( "SID" , "" );
00138
00139 SubscriberInfo *pInfo = NULL;
00140
00141
00142
00143
00144
00145
00146
00147
00148 if ( sCallBack.length() != 0 )
00149 {
00150
00151
00152
00153
00154 if ( sSID.length() != 0 )
00155 {
00156 pRequest->m_nResponseStatus = 400;
00157 return;
00158 }
00159
00160 if ( sNT != "upnp:event" )
00161 return;
00162
00163
00164
00165
00166
00167
00168
00169 sCallBack = sCallBack.mid( 1, sCallBack.find( ">" ) - 1);
00170
00171 pInfo = new SubscriberInfo( sCallBack, m_nSubscriptionDuration );
00172
00173 m_Subscribers.insert( pInfo->sUUID, pInfo );
00174
00175
00176
00177
00178 m_pInitializeSubscriber = pInfo;
00179 pRequest->m_pPostProcess = (IPostProcess *)this;
00180
00181 }
00182 else
00183 {
00184
00185
00186
00187
00188 if ( sSID.length() != 0 )
00189 {
00190 sSID = sSID.mid( 5 );
00191 pInfo = m_Subscribers.find( sSID );
00192 }
00193
00194 }
00195
00196 if (pInfo != NULL)
00197 {
00198 pRequest->m_mapRespHeaders[ "SID" ] = QString( "uuid:%1" )
00199 .arg( pInfo->sUUID );
00200
00201 pRequest->m_mapRespHeaders[ "TIMEOUT"] = QString( "Second-%1" )
00202 .arg( pInfo->nDuration );
00203
00204 pRequest->m_nResponseStatus = 200;
00205
00206 }
00207
00208 }
00209
00211
00213
00214 void Eventing::HandleUnsubscribe( HTTPRequest *pRequest )
00215 {
00216 pRequest->m_eResponseType = ResponseTypeXML;
00217 pRequest->m_nResponseStatus = 412;
00218
00219 QString sCallBack = pRequest->GetHeaderValue( "CALLBACK", "" );
00220 QString sNT = pRequest->GetHeaderValue( "NT" , "" );
00221 QString sSID = pRequest->GetHeaderValue( "SID" , "" );
00222
00223 if ((sCallBack.length() != 0) || (sNT.length() != 0))
00224 {
00225 pRequest->m_nResponseStatus = 400;
00226 return;
00227 }
00228
00229 sSID = sSID.mid( 5 );
00230
00231 if (!m_Subscribers.remove( sSID ))
00232 return;
00233
00234 pRequest->m_nResponseStatus = 200;
00235 }
00236
00238
00240
00241 void Eventing::Notify()
00242 {
00243 TaskTime tt;
00244 gettimeofday( &tt, NULL );
00245
00246 m_mutex.lock();
00247
00248
00249 for ( SubscriberIterator it( m_Subscribers ); it.current(); )
00250 {
00251 SubscriberInfo *pInfo = it.current();
00252
00253 if ( pInfo != NULL )
00254 {
00255
00256
00257
00258
00259 if ( tt < pInfo->ttExpires )
00260 {
00261
00262
00263
00264
00265 NotifySubscriber( pInfo );
00266
00267 ++it;
00268 }
00269 else
00270 {
00271
00272
00273
00274
00275
00276 m_Subscribers.remove( pInfo->sUUID );
00277 }
00278 }
00279 }
00280
00281 m_mutex.unlock();
00282 }
00283
00285
00287
00288 void Eventing::NotifySubscriber( SubscriberInfo *pInfo )
00289 {
00290 if (pInfo == NULL)
00291 return;
00292
00293 int nCount = 0;
00294 QByteArray aBody;
00295 QTextStream tsBody( aBody, IO_WriteOnly );
00296
00297 tsBody.setEncoding( QTextStream::UnicodeUTF8 );
00298
00299
00300
00301
00302
00303 if (( nCount = BuildNotifyBody( tsBody, pInfo->ttLastNotified )) > 0)
00304 {
00305
00306
00307
00308 QByteArray *pBuffer = new QByteArray();
00309 QTextStream tsMsg( *pBuffer, IO_WriteOnly );
00310
00311 tsMsg.setEncoding( QTextStream::UnicodeUTF8 );
00312
00313
00314
00315
00316
00317 short nPort = pInfo->qURL.hasPort() ? pInfo->qURL.port() : 80;
00318 QString sHost = QString( "%1:%2" ).arg( pInfo->qURL.host() )
00319 .arg( nPort );
00320
00321 tsMsg << "NOTIFY " << pInfo->qURL.path() << " HTTP/1.1\r\n";
00322 tsMsg << "HOST: " << sHost << "\r\n";
00323 tsMsg << "CONTENT-TYPE: \"text/xml\"\r\n";
00324 tsMsg << "Content-Length: " << QString::number( aBody.size() ) << "\r\n";
00325 tsMsg << "NT: upnp:event\r\n";
00326 tsMsg << "NTS: upnp:propchange\r\n";
00327 tsMsg << "SID: uuid:" << pInfo->sUUID << "\r\n";
00328 tsMsg << "SEQ: " << QString::number( pInfo->nKey ) << "\r\n";
00329 tsMsg << "\r\n";
00330 tsMsg.writeRawBytes( aBody.data(), aBody.size() );
00331
00332
00333
00334
00335
00336 VERBOSE(VB_UPNP, QString("UPnp::Eventing::NotifySubscriber( %1 ) : %2 Variables").arg( sHost ).arg(nCount));
00337
00338 UPnpEventTask *pEventTask = new UPnpEventTask( pInfo->qURL.host(), nPort, pBuffer );
00339
00340 UPnp::g_pTaskQueue->AddTask( 250, pEventTask );
00341
00342
00343
00344
00345
00346 pInfo->IncrementKey();
00347
00348 gettimeofday( &pInfo->ttLastNotified, NULL );
00349 }
00350
00351 }
00352
00354
00356
00357 int Eventing::BuildNotifyBody( QTextStream &ts, TaskTime ttLastNotified )
00358 {
00359 int nCount = 0;
00360
00361 ts << "<?xml version=\"1.0\"?>" << endl
00362 << "<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">" << endl;
00363
00364 for( StateVariableIterator it( *((StateVariables *)this) ); it.current(); ++it )
00365 {
00366 StateVariableBase *pBase = it.current();
00367
00368 if ( ttLastNotified < pBase->m_ttLastChanged )
00369 {
00370 nCount++;
00371
00372 ts << "<e:property>" << endl;
00373 ts << "<" << pBase->m_sName << ">";
00374 ts << pBase->ToString();
00375 ts << "</" << pBase->m_sName << ">";
00376 ts << "</e:property>" << endl;
00377 }
00378 }
00379
00380 ts << "</e:propertyset>" << endl;
00381
00382 return nCount;
00383 }
00384