예제 #1
0
파일: ssdpcache.cpp 프로젝트: microe/mythtv
int SSDPCache::RemoveStale()
{
    int          nCount = 0;
    TaskTime     ttNow;
    QStringList  lstKeys;

    gettimeofday( (&ttNow), NULL );

    Lock();

    // ----------------------------------------------------------------------
    // Iterate through all Type URI's and build list of stale entries keys
    // ----------------------------------------------------------------------

    for (SSDPCacheEntriesMap::Iterator it  = m_cache.begin();
                                       it != m_cache.end();
                                     ++it )
    {
        SSDPCacheEntries *pEntries = *it;

        if (pEntries != NULL)
        {
            pEntries->AddRef();

            nCount += pEntries->RemoveStale( ttNow );
     
            if (pEntries->Count() == 0)
                lstKeys.append( it.key() );

            pEntries->Release();
        }
    }

    Unlock();

    nCount = lstKeys.count();

    // ----------------------------------------------------------------------
    // Iterate through list of keys and remove them.
    // (This avoids issues when removing from a QMap while iterating it)
    // ----------------------------------------------------------------------

    for ( QStringList::Iterator itKey = lstKeys.begin();
                                itKey != lstKeys.end();
                              ++itKey ) 
    {
        SSDPCacheEntriesMap::iterator it = m_cache.find( *itKey );
        if (it == m_cache.end())
            continue;

        if (*it)
        {
            (*it)->Release();
            m_cache.erase(it);
        }
    }

    return nCount;
}
예제 #2
0
void BackendSelection::Init(void)
{
    SSDPCacheEntries *pEntries = SSDPCache::Instance()->Find(gBackendURI);
    if (pEntries)
    {
        EntryMap ourMap;
        pEntries->GetEntryMap(ourMap);
        pEntries->Release();

        EntryMap::const_iterator it;
        for (it = ourMap.begin(); it != ourMap.end(); ++it)
            AddItem(*it);   // this does an (*it)->Release()
    }
}
예제 #3
0
파일: ssdpcache.cpp 프로젝트: microe/mythtv
DeviceLocation *SSDPCache::Find( const QString &sURI, const QString &sUSN )
{
    DeviceLocation   *pEntry   = NULL;
    SSDPCacheEntries *pEntries = Find( sURI );

    if (pEntries != NULL)
    {
        pEntries->AddRef();
        pEntry = pEntries->Find( sUSN );
        pEntries->Release();
    }

    return pEntry;
}
예제 #4
0
파일: ssdpcache.cpp 프로젝트: microe/mythtv
void SSDPCache::Add( const QString &sURI,
                     const QString &sUSN,
                     const QString &sLocation,
                     long           sExpiresInSecs )
{    
    // --------------------------------------------------------------
    // Calculate when this cache entry should expire.
    // --------------------------------------------------------------

    TaskTime ttExpires;
    gettimeofday        ( (&ttExpires), NULL );
    AddSecondsToTaskTime(  ttExpires, sExpiresInSecs );

    // --------------------------------------------------------------
    // Get a Pointer to a Entries QDict... (Create if not found)
    // --------------------------------------------------------------

    SSDPCacheEntries *pEntries = Find( sURI );

    if (pEntries == NULL)
    {
        pEntries = new SSDPCacheEntries();
        pEntries->AddRef();
        m_cache.insert( sURI, pEntries );
    }

    pEntries->AddRef();

    // --------------------------------------------------------------
    // See if the Entries Collection contains our USN... (Create if not found)
    // --------------------------------------------------------------

    DeviceLocation *pEntry = pEntries->Find( sUSN );

    if (pEntry == NULL)
    {
        pEntry = new DeviceLocation( sURI, sUSN, sLocation, ttExpires );

        Lock();
        pEntries->Insert( sUSN, pEntry );
        Unlock();

        NotifyAdd( sURI, sUSN, sLocation );
    }
    else
    {
        pEntry->AddRef();
        pEntry->m_sLocation = sLocation;
        pEntry->m_ttExpires = ttExpires;
        pEntry->Release();
    }

    pEntries->Release();
}
예제 #5
0
void BackendSelection::Init(void)
{
    EntryMap::Iterator  it;
    EntryMap            ourMap;
    DeviceLocation     *pDevLoc;

    SSDPCacheEntries *pEntries = SSDPCache::Instance()->Find( gBackendURI );

    if (!pEntries)
    {
        VERBOSE(VB_GENERAL, "Found zero backends, bailing");
        return;
    }

    pEntries->AddRef();
    pEntries->Lock();

    EntryMap *pMap = pEntries->GetEntryMap();

    for (it = pMap->begin(); it != pMap->end(); ++it)
    {
        pDevLoc = (DeviceLocation *)*it;

        if (!pDevLoc)
            continue;

        pDevLoc->AddRef();
        ourMap.insert(pDevLoc->m_sUSN, pDevLoc);
    }

    pEntries->Unlock();
    pEntries->Release();

    for (it = ourMap.begin(); it != ourMap.end(); ++it)
    {
        pDevLoc = (DeviceLocation *)*it;
        AddItem(pDevLoc);   // this does a Release()
    }
}
예제 #6
0
파일: ssdpcache.cpp 프로젝트: microe/mythtv
void SSDPCache::Remove( const QString &sURI, const QString &sUSN )
{    
    Lock();

    // --------------------------------------------------------------
    // Get a Pointer to a Entries QDict... (Create if not found)
    // --------------------------------------------------------------

    SSDPCacheEntriesMap::Iterator it = m_cache.find( sURI );

    if (it != m_cache.end())
    {
        SSDPCacheEntries *pEntries = *it;

        if (pEntries != NULL)
        {
            pEntries->AddRef();

            pEntries->Remove( sUSN );

            if (pEntries->Count() == 0)
            {
                pEntries->Release();
                m_cache.erase(it);
            }

            pEntries->Release();
        }
    }

    Unlock();

    // -=>TODO:
    // Should this only by notified if we actually had any entry removed?

    NotifyRemove( sURI, sUSN );
}
예제 #7
0
파일: ssdpcache.cpp 프로젝트: microe/mythtv
void SSDPCache::Dump()
{
    int nCount = 0;

    if (VERBOSE_LEVEL_CHECK(VB_UPNP))
    {

        Lock();

        // ----------------------------------------------------------------------
        // Build List of items to be removed
        // ----------------------------------------------------------------------

        VERBOSE( VB_UPNP, "===============================================================================" );
        VERBOSE( VB_UPNP, QString(  " URI (type) - Found: %1 Entries - %2 have been Allocated. " )
                             .arg( m_cache.count() )
                             .arg( SSDPCacheEntries::g_nAllocated ));
        VERBOSE( VB_UPNP, "   \t\tUSN (unique id)\t\t | Expires\t | Location" );
        VERBOSE( VB_UPNP, "-------------------------------------------------------------------------------" );

        for (SSDPCacheEntriesMap::Iterator it  = m_cache.begin();
                                           it != m_cache.end();
                                         ++it )
        {
            SSDPCacheEntries *pEntries = *it;

            if (pEntries != NULL)
            {
                VERBOSE( VB_UPNP, it.key() );

                pEntries->Lock();

                EntryMap *pMap = pEntries->GetEntryMap();

                for (EntryMap::Iterator itEntry  = pMap->begin();
                                        itEntry != pMap->end();
                                      ++itEntry )
                {

                    DeviceLocation *pEntry = *itEntry;

                    if (pEntry != NULL)
                    {
                        nCount++;

                        pEntry->AddRef();

                        VERBOSE( VB_UPNP, QString( " * \t\t%1\t | %2\t | %3 " ) 
                                             .arg( pEntry->m_sUSN )
                                             .arg( pEntry->ExpiresInSecs() )
                                             .arg( pEntry->m_sLocation ));

                        pEntry->Release();
                    }
                }

                VERBOSE( VB_UPNP, " "); 

                pEntries->Unlock();
            }
        }

        VERBOSE( VB_UPNP, "-------------------------------------------------------------------------------" );
        VERBOSE( VB_UPNP, QString(  " Found: %1 Entries - %2 have been Allocated. " )
                             .arg( nCount )
                             .arg( DeviceLocation::g_nAllocated ));
        VERBOSE( VB_UPNP, "===============================================================================" );

        Unlock();
    }
}
예제 #8
0
/**
 * If there is only a single UPnP backend, use it.
 *
 * This does <i>not</i> prompt for PIN entry. If the backend requires one,
 * it will fail, and the caller needs to put up a UI to ask for one.
 */
int MythContextPrivate::UPnPautoconf(const int milliSeconds)
{
    SSDPCacheEntries *backends = NULL;
    int               count;
    QString           loc = "UPnPautoconf() - ";
    QTime             timer;

    SSDP::Instance()->PerformSearch( gBackendURI );

    for (timer.start(); timer.elapsed() < milliSeconds; usleep(25000))
    {
        backends = SSDP::Instance()->Find( gBackendURI );
        if (backends)
        {
            backends->AddRef();
            break;
        }
        putchar('.');
    }
    putchar('\n');

    if (!backends)
    {
        VERBOSE(VB_GENERAL, loc + "No UPnP backends found");
        return 0;
    }

    count = backends->Count();
    switch (count)
    {
        case 0:
            VERBOSE(VB_IMPORTANT,
                    loc + "No UPnP backends found, but SSDP::Find() not NULL!");
            break;
        case 1:
            VERBOSE(VB_GENERAL, loc + "Found one UPnP backend");
            break;
        default:
            VERBOSE(VB_GENERAL,
                    (loc + "More than one UPnP backend found (%1)").arg(count));
    }

    if (count != 1)
    {
        backends->Release();
        return count;
    }


    // Get this backend's location:
    backends->Lock();
    DeviceLocation *BE = *(backends->GetEntryMap()->begin());
    backends->Unlock();
    backends->Release();

    // We don't actually know the backend's access PIN, so this will
    // only work for ones that have PIN access disabled (i.e. 0000)
    if (UPnPconnect(BE, QString::null))
        return 1;

    return -1;   // Try to force chooser & PIN
}
예제 #9
0
    virtual bool ProcessRequest(HttpWorkerThread *pThread,
                                HTTPRequest *pRequest)
    {
        (void)pThread;

        if (!pRequest)
            return false;

        if (pRequest->m_sBaseUrl != "/")
            return false;

        if (pRequest->m_sMethod == "getDeviceDesc")
            return false;

        pRequest->m_eResponseType = ResponseTypeHTML;
        pRequest->m_mapRespHeaders["Cache-Control"] =
            "no-cache=\"Ext\", max-age = 5000";

        SSDPCacheEntries* cache = NULL;
        QString ipaddress = QString();
        if (!UPnp::g_IPAddrList.isEmpty())
            ipaddress = UPnp::g_IPAddrList.at(0);

        QString shortdateformat = gCoreContext->GetSetting("ShortDateFormat",
                                                           "M/d");
        QString timeformat      = gCoreContext->GetSetting("TimeFormat",
                                                           "h:mm AP");
        QString hostname   = gCoreContext->GetHostName();
        QDateTime qdtNow   = QDateTime::currentDateTime();
        QString masterhost = gCoreContext->GetMasterHostName();
        QString masterip   = gCoreContext->GetSetting("MasterServerIP");
        QString masterport = gCoreContext->GetSettingOnHost("BackendStatusPort",
                                                            masterhost, "6544");

        QTextStream stream ( &pRequest->m_response );
        stream.setCodec("UTF-8");

        stream
           << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" "
           << "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\r\n"
           << "<html xmlns=\"http://www.w3.org/1999/xhtml\""
           << " xml:lang=\"en\" lang=\"en\">\r\n"
           << "<head>\r\n"
           << "  <meta http-equiv=\"Content-Type\""
           << "content=\"text/html; charset=UTF-8\" />\r\n"
           << "  <title>MythFrontend Status - "
           << qdtNow.toString(shortdateformat) << " "
           << qdtNow.toString(timeformat) << " - "
           << MYTH_BINARY_VERSION << "</title>\r\n"
           << "  <link rel=\"stylesheet\" href=\"/css/site.css\"   type=\"text/css\">\r\n"
           << "  <link rel=\"stylesheet\" href=\"/css/Status.css\" type=\"text/css\">\r\n"
           << "</head>\r\n"
           << "<body>\r\n\r\n"
           << "  <h1 class=\"status\">MythFrontend Status</h1>\r\n";

        // This frontend
        stream
           << "  <div class=\"content\">\r\n"
           << "    <h2 class=\"status\">This Frontend</h2>\r\n"
           << "Name : " << hostname << "<br />\r\n"
           << "Version : " << MYTH_BINARY_VERSION << "\r\n"
           << "  </div>\r\n";

        // Other frontends

        // This will not work with multiple frontends on the same machine (upnp
        // setup will fail on a second frontend anyway) and the ip address
        // filtering of the current frontend may not work in all situations

        cache = SSDP::Find("urn:schemas-mythtv-org:service:MythFrontend:1");
        if (cache)
        {
            stream
               << "  <div class=\"content\">\r\n"
               << "    <h2 class=\"status\">Other Frontends</h2>\r\n";

            EntryMap map;
            cache->GetEntryMap(map);
            cache->Release();
            cache = NULL;

            QMapIterator< QString, DeviceLocation * > i(map);
            while (i.hasNext())
            {
                i.next();
                QUrl url(i.value()->m_sLocation);
                if (url.host() != ipaddress)
                {
                    stream << "<br />" << url.host() << "&nbsp(<a href=\""
                           << url.toString(QUrl::RemovePath)
                           << "\">Status page</a>)\r\n";
                }
                i.value()->Release();
            }
            stream << "  </div>\r\n";
        }

        // Master backend
        stream
           << "  <div class=\"content\">\r\n"
           << "    <h2 class=\"status\">MythTV Backends</h2>\r\n"
           << "Master: " << masterhost << "&nbsp(<a href=\"http://"
           << masterip << ":" << masterport
           << "\">Status page</a>)\r\n";

        // Slave backends
        cache = SSDP::Find("urn:schemas-mythtv-org:device:SlaveMediaServer:1");
        if (cache)
        {
            EntryMap map;
            cache->GetEntryMap(map);
            cache->Release();
            cache = NULL;

            QMapIterator< QString, DeviceLocation * > i(map);
            while (i.hasNext())
            {
                i.next();
                QUrl url(i.value()->m_sLocation);
                stream << "<br />" << "Slave: " << url.host()
                       << "&nbsp(<a href=\""
                       << url.toString(QUrl::RemovePath)
                       << "\">Status page</a>)\r\n";
                i.value()->Release();
            }
        }

        stream
           << "  </div>\r\n";

        stream
           << "  <div class=\"content\">\r\n"
           << "    <h2 class=\"status\">Services</h2>\r\n"
           << "    <a href=\"MythFE/GetRemote\">Remote Control</a>\r\n"
           << "  </div>\r\n";

        double load[3];
        if (getloadavg(load, 3) != -1)
        {
            stream
               << "  <div class=\"content\">\r\n"
               << "    <h2 class=\"status\">Machine Information</h2>\r\n"
               << "    <div class=\"loadstatus\">\r\n"
               << "      This machine's load average:"
               << "\r\n      <ul>\r\n        <li>"
               << "1 Minute: " << QString::number(load[0]) << "</li>\r\n"
               << "        <li>5 Minutes: " << QString::number(load[1]) << "</li>\r\n"
               << "        <li>15 Minutes: " << QString::number(load[2])
               << "</li>\r\n      </ul>\r\n"
               << "    </div>\r\n"
               << "  </div>\r\n";
        }

        stream << "</body>\r\n</html>\r\n";
        stream.flush();

        return true;
    }
예제 #10
0
void SSDPExtension::GetDeviceList( HTTPRequest *pRequest )
{
    SSDPCache*    pCache  = SSDPCache::Instance();
    int           nCount = 0;
    NameValues    list;

    VERBOSE( VB_UPNP, "SSDPExtension::GetDeviceList" );

    pCache->Lock();

    QString     sXML = "";
    QTextStream os( &sXML, QIODevice::WriteOnly );

    for (SSDPCacheEntriesMap::Iterator it  = pCache->Begin();
                                       it != pCache->End();
                                     ++it )
    {
        SSDPCacheEntries *pEntries = *it;

        if (pEntries != NULL)
        {
            os << "<Device uri='" << it.key() << "'>" << endl;

            pEntries->Lock();

            EntryMap *pMap = pEntries->GetEntryMap();

            for (EntryMap::Iterator itEntry  = pMap->begin();
                                    itEntry != pMap->end();
                                  ++itEntry )
            {

                DeviceLocation *pEntry = *itEntry;

                if (pEntry != NULL)
                {
                    nCount++;

                    pEntry->AddRef();

                    os << "<Service usn='" << pEntry->m_sUSN 
                       << "' expiresInSecs='" << pEntry->ExpiresInSecs()
                       << "' url='" << pEntry->m_sLocation << "' />" << endl;

                    pEntry->Release();
                }
            }

            os << "</Device>" << endl;

            pEntries->Unlock();
        }
    }
    os << flush;

    list.push_back( NameValue("DeviceCount"          , pCache->Count()               ));
    list.push_back( NameValue("DevicesAllocated"     , SSDPCacheEntries::g_nAllocated));
    list.push_back( NameValue("CacheEntriesFound"    , nCount                        ));
    list.push_back( NameValue("CacheEntriesAllocated", DeviceLocation::g_nAllocated  ));
    list.push_back( NameValue("DeviceList"           , sXML                          ));

    pCache->Unlock();

    pRequest->FormatActionResponse( list );

    pRequest->m_eResponseType   = ResponseTypeXML;
    pRequest->m_nResponseStatus = 200;
}
예제 #11
0
/**
 * If there is only a single UPnP backend, use it.
 *
 * This does <i>not</i> prompt for PIN entry. If the backend requires one,
 * it will fail, and the caller needs to put up a UI to ask for one.
 */
int MythContextPrivate::UPnPautoconf(const int milliSeconds)
{
    LOG(VB_GENERAL, LOG_INFO, QString("UPNP Search %1 secs")
        .arg(milliSeconds / 1000));

    SSDP::Instance()->PerformSearch(gBackendURI, milliSeconds / 1000);

    // Search for a total of 'milliSeconds' ms, sending new search packet
    // about every 250 ms until less than one second remains.
    MythTimer totalTime; totalTime.start();
    MythTimer searchTime; searchTime.start();
    while (totalTime.elapsed() < milliSeconds)
    {
        usleep(25000);
        int ttl = milliSeconds - totalTime.elapsed();
        if ((searchTime.elapsed() > 249) && (ttl > 1000))
        {
            LOG(VB_GENERAL, LOG_INFO, QString("UPNP Search %1 secs")
                .arg(ttl / 1000));
            SSDP::Instance()->PerformSearch(gBackendURI, ttl / 1000);
            searchTime.start();
        }
    }

    SSDPCacheEntries *backends = SSDP::Instance()->Find(gBackendURI);

    if (!backends)
    {
        LOG(VB_GENERAL, LOG_INFO, "No UPnP backends found");
        return 0;
    }

    int count = backends->Count();
    if (count)
    {
        LOG(VB_GENERAL, LOG_INFO,
            QString("Found %1 UPnP backends").arg(count));
    }
    else
    {
        LOG(VB_GENERAL, LOG_ERR,
            "No UPnP backends found, but SSDP::Find() not NULL");
    }

    if (count != 1)
    {
        backends->DecrRef();
        return count;
    }

    // Get this backend's location:
    DeviceLocation *BE = backends->GetFirst();
    backends->DecrRef();
    backends = NULL;

    // We don't actually know the backend's access PIN, so this will
    // only work for ones that have PIN access disabled (i.e. 0000)
    int ret = (UPnPconnect(BE, QString::null)) ? 1 : -1;

    BE->DecrRef();

    return ret;
}