MythTV  0.26-pre
httpcomms.cpp
Go to the documentation of this file.
00001 #include <iostream>
00002 using namespace std;
00003 
00004 #include <QCoreApplication>
00005 #include <QRegExp>
00006 #include <QTimer>
00007 #include <QFile>
00008 
00009 #include "mythlogging.h"
00010 #include "compat.h"
00011 #include "mcodecs.h"
00012 #include "httpcomms.h"
00013 
00014 HttpComms::HttpComms()
00015          : http(0)
00016 {
00017     init();
00018 }
00019 
00020 
00021 HttpComms::HttpComms(QUrl &url, int timeoutms)
00022          : http(0)
00023 {
00024     init();
00025     request(url, timeoutms);
00026 }
00027 
00028 HttpComms::HttpComms(QUrl &url, QHttpRequestHeader &header, int timeoutms)
00029 {
00030     init();
00031     request(url, header, timeoutms);
00032 }
00033 
00034 HttpComms::~HttpComms()
00035 {
00036     if (m_timer)
00037     {
00038         m_timer->disconnect();
00039         m_timer->deleteLater();
00040         m_timer = NULL;
00041     }
00042 
00043     delete http;
00044 }
00045 
00046 void HttpComms::init()
00047 {
00048  //   m_curRequest = NULL;
00049     m_authNeeded = false;
00050     http = new QHttp();
00051     m_done = false;
00052     m_statusCode = 0;
00053     m_timer = NULL;
00054     m_timeout = false;
00055     m_progress = m_total = 0;
00056 
00057 
00058     connect(http, SIGNAL(done(bool)), this, SLOT(done(bool)));
00059     connect(http, SIGNAL(stateChanged(int)), this, SLOT(stateChanged(int)));
00060     connect(http, SIGNAL(responseHeaderReceived(const QHttpResponseHeader &)),
00061             this, SLOT(headerReceived(const QHttpResponseHeader &)));
00062     connect(http, SIGNAL(dataReadProgress(int, int)), this, SLOT(dataReadProgress(int, int)));
00063 
00064 }
00065 
00066 void HttpComms::request(QUrl &url, int timeoutms, bool allowGzip)
00067 {
00068     QString path = url.path();
00069 
00070     if (url.hasQuery())
00071         path += '?' + url.encodedQuery();
00072 
00073     QHttpRequestHeader header("GET", path);
00074     QString userAgent = "Mozilla/9.876 (X11; U; Linux 2.2.12-20 i686, en) "
00075                         "Gecko/25250101 Netscape/5.432b1";
00076 
00077     header.setValue("Host", url.host());
00078     header.setValue("User-Agent", userAgent);
00079 
00080     if (allowGzip)
00081         header.setValue( "Accept-Encoding", "gzip");
00082 
00083     request(url, header, timeoutms);
00084 }
00085 
00086 
00087 
00088 void HttpComms::request(QUrl               &url,
00089                         QHttpRequestHeader &header,
00090                         int                 timeoutms,
00091                         QIODevice          *pData /* = NULL*/ )
00092 {
00093     quint16 port = 80;
00094 
00095     if (url.port() != -1)
00096         port = url.port();
00097 
00098     http->setHost(url.host(), port);
00099 
00100     m_url = url.toString();
00101     m_curRequest = header;
00102 
00103     if (m_timer)
00104         m_timer->stop();
00105 
00106     if (timeoutms > 0 )
00107     {
00108         if (!m_timer)
00109         {
00110             m_timer = new QTimer();
00111             connect(m_timer, SIGNAL(timeout()), SLOT(timeout()));
00112         }
00113         m_timeoutInterval = timeoutms;
00114         m_timer->setSingleShot(true);
00115         m_timer->start(timeoutms);
00116     }
00117 
00118     if (!m_cookie.isEmpty())
00119     {
00120         header.setValue("Cookie", m_cookie);
00121     }
00122 
00123     http->request(header, pData);
00124 }
00125 
00126 void HttpComms::stop()
00127 {
00128     disconnect(http, 0, 0, 0);
00129     http->abort();
00130 
00131     if (m_timer)
00132         m_timer->stop();
00133 }
00134 
00135 void HttpComms::done(bool error)
00136 {
00137     if (error)
00138     {
00139        LOG(VB_GENERAL, LOG_ERR,
00140            QString("NetworkOperation Error on Finish: %1 (%2): url: '%3'")
00141            .arg(http->errorString()) .arg(error) .arg(m_url.toString()));
00142     }
00143     else if (m_authNeeded)
00144     {
00145         LOG(VB_NETWORK, LOG_ERR, QString("Authentication pending, ignoring "
00146                                          "done from first request."));
00147         return;
00148     }
00149     else if (http->bytesAvailable())
00150     {
00151         m_data.resize(http->bytesAvailable());
00152         m_data = http->readAll();
00153     }
00154 
00155     LOG(VB_NETWORK, LOG_DEBUG, QString("done: %1 bytes").arg(m_data.size()));
00156 
00157     if (m_timer)
00158         m_timer->stop();
00159 
00160     m_done = true;
00161 }
00162 
00163 void HttpComms::stateChanged(int state)
00164 {
00165     QString stateStr;
00166 
00167     switch (state)
00168     {
00169         case QHttp::Unconnected: stateStr = "unconnected"; break;
00170         case QHttp::HostLookup: stateStr =  "host lookup"; break;
00171         case QHttp::Connecting: stateStr = "connecting"; break;
00172         case QHttp::Sending: stateStr = "sending"; break;
00173         case QHttp::Reading: stateStr = "reading"; break;
00174         case QHttp::Connected: stateStr =  "connected"; break;
00175         case QHttp::Closing: stateStr = "closing"; break;
00176         default: stateStr =  "unknown state: "; break;
00177     }
00178 
00179     LOG(VB_NETWORK, LOG_DEBUG, QString("HttpComms::stateChanged: %1 (%2)")
00180                                 .arg(stateStr)
00181                                 .arg(state));
00182 }
00183 
00184 void HttpComms::headerReceived(const QHttpResponseHeader &resp)
00185 {
00186     m_statusCode = resp.statusCode();
00187     m_responseReason = resp.reasonPhrase();
00188 
00189     QString sidkey = "set-cookie";
00190 
00191     if (resp.hasKey(sidkey))
00192     {
00193         QRegExp rx("PHPSESSID=(.+);");
00194         rx.setMinimal(true);
00195         rx.setCaseSensitivity(Qt::CaseInsensitive);
00196         if (rx.indexIn(resp.value(sidkey)) >= 0)
00197         {
00198             m_cookie = "PHPSESSID=" + rx.cap(1);
00199             LOG(VB_NETWORK, LOG_DEBUG,
00200                 QString("HttpComms found cookie: %1").arg(m_cookie));
00201         }
00202     }
00203 
00204 
00205     LOG(VB_NETWORK, LOG_DEBUG, QString("Got HTTP response: %1:%2")
00206                         .arg(m_statusCode)
00207                         .arg(m_responseReason));
00208     LOG(VB_NETWORK, LOG_DEBUG, QString("Keys: %1")
00209                         .arg(resp.keys().join(",") ));
00210 
00211 
00212     if (resp.statusCode() >= 300 && resp.statusCode() <= 400)
00213     {
00214         // redirection
00215         QString uri = resp.value("LOCATION");
00216         LOG(VB_NETWORK, LOG_DEBUG, QString("Redirection to: '%1'").arg(uri));
00217 
00218         m_redirectedURL = resp.value("LOCATION");
00219         m_authNeeded = false;
00220     }
00221     else if ((resp.statusCode() == 401))
00222     {
00223         // Toggle the state of our authentication pending flag
00224         // if we've gotten this after having tried to authenticate
00225         // once then we've failed authentication and turning the pending off will allow us to exit.
00226         m_authNeeded = !m_authNeeded;
00227         if (m_authNeeded)
00228         {
00229             QString authHeader(resp.value("www-authenticate"));
00230 
00231             if (authHeader.startsWith("Digest") )
00232             {
00233                 if (!createDigestAuth(false, authHeader, &m_curRequest) )
00234                 {
00235                     m_authNeeded = false;
00236                     return;
00237                 }
00238             }
00239             else
00240             {
00241                 QString sUser(m_webCredentials.user + ':' + m_webCredentials.pass);
00242                 QByteArray auth = QCodecs::base64Encode(sUser.toLocal8Bit());
00243                 m_curRequest.setValue( "Authorization", QString( "Basic " ).append( auth ) );
00244             }
00245 
00246             if (m_timer)
00247             {
00248                 m_timer->stop();
00249                 m_timer->setSingleShot(true);
00250                 m_timer->start(m_timeoutInterval);
00251             }
00252 
00253             // Not sure if it's possible to receive a session ID or other cookie
00254             // before authenticating or not.
00255             if (!m_cookie.isEmpty())
00256             {
00257                 m_curRequest.setValue("Cookie", m_cookie);
00258             }
00259 
00260             http->request(m_curRequest);
00261         }
00262     }
00263     else
00264     {
00265         m_authNeeded = false;
00266     }
00267 }
00268 
00269 void HttpComms::timeout()
00270 {
00271    LOG(VB_GENERAL, LOG_ERR, QString("HttpComms::Timeout for url: %1")
00272                                 .arg(m_url.toString()));
00273    m_timeout = true;
00274    m_done = true;
00275 }
00276 
00277 void HttpComms::dataReadProgress(int done, int total)
00278 {
00279    m_progress = done;
00280    m_total = total;
00281 }
00282 
00283 
00289 QString HttpComms::getHttp(QString     &url,
00290                            int          timeoutMS,    int  maxRetries,
00291                            int          maxRedirects, bool allowGzip,
00292                            Credentials *webCred,      bool isInQtEventThread)
00293 {
00294     int redirectCount = 0;
00295     int timeoutCount = 0;
00296     QString res;
00297     HttpComms *httpGrabber = NULL;
00298     QString hostname;
00299 
00300     while (1)
00301     {
00302         QUrl qurl(url);
00303         if (hostname.isEmpty())
00304             hostname = qurl.host();  // hold onto original host
00305         if (qurl.host().isEmpty())   // can occur on redirects to partial paths
00306             qurl.setHost(hostname);
00307 
00308         LOG(VB_NETWORK, LOG_DEBUG,
00309             QString("getHttp: grabbing: %1").arg(qurl.toString()));
00310 
00311         delete httpGrabber;
00312 
00313         httpGrabber = new HttpComms;
00314 
00315         if (webCred)
00316             httpGrabber->setCredentials(*webCred, CRED_WEB);
00317 
00318         httpGrabber->request(qurl, timeoutMS, allowGzip);
00319 
00320 
00321         while (!httpGrabber->isDone())
00322         {
00323             if (isInQtEventThread)
00324                 qApp->processEvents();
00325             usleep(10000);
00326         }
00327 
00328         // Handle timeout
00329         if (httpGrabber->isTimedout())
00330         {
00331             LOG(VB_NETWORK, LOG_INFO, QString("timeout for url: %1").arg(url));
00332 
00333             // Increment the counter and check we're not over the limit
00334             if (timeoutCount++ >= maxRetries)
00335             {
00336                 LOG(VB_GENERAL, LOG_ERR,
00337                     QString("Failed to contact server for url: %1").arg(url));
00338                 break;
00339             }
00340 
00341             // Try again
00342             LOG(VB_NETWORK, LOG_DEBUG, QString("Attempt # %1/%2 for url: %3")
00343                                         .arg(timeoutCount + 1)
00344                                         .arg(maxRetries)
00345                                         .arg(url));
00346 
00347             continue;
00348         }
00349 
00350         // Check for redirection
00351         if (!httpGrabber->getRedirectedURL().isEmpty())
00352         {
00353             LOG(VB_NETWORK, LOG_DEBUG,
00354                 QString("Redirection: %1, count: %2, max: %3")
00355                                 .arg(httpGrabber->getRedirectedURL())
00356                                 .arg(redirectCount)
00357                                 .arg(maxRedirects));
00358 
00359             // Increment the counter and check we're not over the limit
00360             if (redirectCount++ >= maxRedirects)
00361             {
00362                 LOG(VB_GENERAL, LOG_ERR,
00363                     QString("Maximum redirections reached for url: %1")
00364                         .arg(url));
00365                 break;
00366             }
00367 
00368             url = httpGrabber->getRedirectedURL();
00369 
00370             // Try again
00371             timeoutCount = 0;
00372             continue;
00373         }
00374 
00375         res = httpGrabber->getData();
00376         break;
00377     }
00378 
00379     delete httpGrabber;
00380 
00381     LOG(VB_NETWORK, LOG_DEBUG, QString("Got %1 bytes from url: '%2'")
00382                                 .arg(res.length())
00383                                 .arg(url));
00384     LOG(VB_NETWORK, LOG_DEBUG, res);
00385 
00386     return res;
00387 }
00388 
00389 // getHttpFile - static function for grabbing a file from a http url
00390 //      this is a synchronous function, it will block according to the vars
00391 bool HttpComms::getHttpFile(const QString& filename, QString& url, int timeoutMS,
00392                             int maxRetries, int maxRedirects,
00393                             bool allowGzip, Credentials* webCred)
00394 {
00395     int redirectCount = 0;
00396     int timeoutCount = 0;
00397     QByteArray data(0);
00398     bool res = false;
00399     HttpComms *httpGrabber = NULL;
00400     QString hostname;
00401 
00402     while (1)
00403     {
00404         QUrl qurl(url);
00405         if (hostname.isEmpty())
00406             hostname = qurl.host();  // hold onto original host
00407 
00408         if (qurl.host().isEmpty())   // can occur on redirects to partial paths
00409             qurl.setHost(hostname);
00410 
00411         LOG(VB_NETWORK, LOG_DEBUG, QString("getHttp: grabbing: '%1'")
00412                                     .arg(qurl.toString()));
00413 
00414         delete httpGrabber;
00415 
00416         httpGrabber = new HttpComms;
00417 
00418         if (webCred)
00419             httpGrabber->setCredentials(*webCred, CRED_WEB);
00420 
00421         httpGrabber->request(qurl, timeoutMS, allowGzip);
00422 
00423         while (!httpGrabber->isDone())
00424         {
00425             qApp->processEvents();
00426             usleep(10000);
00427         }
00428 
00429         int statusCode = httpGrabber->getStatusCode();
00430         if (statusCode < 200 || statusCode > 401)
00431         {
00432             LOG(VB_GENERAL, LOG_ERR,
00433                 QString("Server returned an error status code %1 for url: %2")
00434                                             .arg(statusCode)
00435                                             .arg(url));
00436             break;
00437         }
00438 
00439         // Handle timeout
00440         if (httpGrabber->isTimedout())
00441         {
00442             LOG(VB_NETWORK, LOG_DEBUG, QString("Timeout for url: '%1'")
00443                                         .arg(url));
00444 
00445             // Increment the counter and check we're not over the limit
00446             if (timeoutCount++ >= maxRetries)
00447             {
00448                 LOG(VB_GENERAL, LOG_ERR,
00449                     QString("Failed to contact server for url: '%1'")
00450                                               .arg(url));
00451                 break;
00452             }
00453 
00454             // Try again
00455             LOG(VB_NETWORK, LOG_DEBUG, QString("Attempt # %1/%2 for url: %3")
00456                                         .arg(timeoutCount + 1)
00457                                         .arg(maxRetries)
00458                                         .arg(url));
00459 
00460             continue;
00461         }
00462 
00463         // Check for redirection
00464         if (!httpGrabber->getRedirectedURL().isEmpty())
00465         {
00466             LOG(VB_NETWORK, LOG_DEBUG,
00467                 QString("redirection: '%1', count: %2, max: %3")
00468                                         .arg(httpGrabber->getRedirectedURL())
00469                                         .arg(redirectCount)
00470                                         .arg(maxRedirects));
00471 
00472             // Increment the counter and check we're not over the limit
00473             if (redirectCount++ >= maxRedirects)
00474             {
00475                 LOG(VB_GENERAL, LOG_ERR,
00476                     QString("Maximum redirections reached for url: %1")
00477                         .arg(url));
00478                 break;
00479             }
00480 
00481             url = httpGrabber->getRedirectedURL();
00482 
00483             // Try again
00484             timeoutCount = 0;
00485             continue;
00486         }
00487 
00488         data = httpGrabber->getRawData();
00489 
00490         if (data.size() > 0)
00491         {
00492             LOG(VB_NETWORK, LOG_DEBUG,
00493                 QString("getHttpFile: saving to file: '%1'") .arg(filename));
00494 
00495             QFile file(filename);
00496             if (file.open( QIODevice::WriteOnly ))
00497             {
00498                 QDataStream stream(& file);
00499                 stream.writeRawData((const char*)(data), data.size() );
00500                 file.close();
00501                 res = true;
00502                 LOG(VB_NETWORK, LOG_DEBUG,
00503                     QString("getHttpFile: File saved OK"));
00504             }
00505             else
00506                 LOG(VB_NETWORK, LOG_DEBUG,
00507                     "getHttpFile: Failed to open file for writing");
00508         }
00509         else
00510            LOG(VB_NETWORK, LOG_DEBUG, "getHttpFile: nothing to save to file!");
00511 
00512         break;
00513     }
00514 
00515     LOG(VB_NETWORK, LOG_DEBUG, QString("Got %1 bytes from url: '%2'")
00516                                 .arg(data.size()) .arg(url));
00517 
00518     delete httpGrabber;
00519 
00520     return res;
00521 }
00522 
00528 QString HttpComms::postHttp(
00529     QUrl               &url,
00530     QHttpRequestHeader *pAddlHdr,     QIODevice *pData,
00531     int                 timeoutMS,    int        maxRetries,
00532     int                 maxRedirects, bool       allowGzip,
00533     Credentials        *webCred,      bool       isInQtEventThread,
00534     QString             userAgent)
00535 {
00536     int redirectCount = 0;
00537     int timeoutCount = 0;
00538     QString res;
00539     HttpComms *httpGrabber = NULL;
00540     QString hostname;
00541 
00542     QHttpRequestHeader header("POST", url.path() + url.encodedQuery());
00543 
00544     // Add main header values
00545     header.setValue("Host", url.host());
00546 
00547     if (userAgent.toLower() == "<default>")
00548     {
00549         userAgent = "Mozilla/9.876 (X11; U; Linux 2.2.12-20 i686, en) "
00550             "Gecko/25250101 Netscape/5.432b1";
00551     }
00552     if (!userAgent.isEmpty())
00553         header.setValue("User-Agent", userAgent);
00554 
00555     if (allowGzip)
00556         header.setValue( "Accept-Encoding", "gzip");
00557 
00558     // Merge any Additional Headers passed by caller.
00559 
00560     if (pAddlHdr)
00561     {
00562         QList< QPair< QString, QString > > values = pAddlHdr->values();
00563 
00564         for (QList< QPair< QString, QString > >::iterator it  = values.begin();
00565                                                           it != values.end();
00566                                                         ++it )
00567         {
00568             header.setValue( (*it).first, (*it).second );
00569         }
00570     }
00571 
00572     while (1)
00573     {
00574         if (hostname.isEmpty())
00575             hostname = url.host();  // hold onto original host
00576         if (url.host().isEmpty())   // can occur on redirects to partial paths
00577             url.setHost(hostname);
00578 
00579         LOG(VB_NETWORK, LOG_DEBUG,
00580             QString("postHttp: grabbing: %1").arg(url.toString()));
00581 
00582         delete httpGrabber;
00583 
00584         httpGrabber = new HttpComms;
00585 
00586         if (webCred)
00587             httpGrabber->setCredentials(*webCred, CRED_WEB);
00588 
00589         httpGrabber->request(url, header, timeoutMS, pData );
00590 
00591         while (!httpGrabber->isDone())
00592         {
00593             if (isInQtEventThread)
00594                 qApp->processEvents();
00595             usleep(10000);
00596         }
00597 
00598         // Handle timeout
00599         if (httpGrabber->isTimedout())
00600         {
00601             LOG(VB_NETWORK, LOG_DEBUG,
00602                 QString("timeout for url: %1").arg(url.toString()));
00603 
00604             // Increment the counter and check we're not over the limit
00605             if (timeoutCount++ >= maxRetries)
00606             {
00607                 LOG(VB_GENERAL, LOG_ERR,
00608                     QString("Failed to contact server for url: %1")
00609                         .arg(url.toString()));
00610                 break;
00611             }
00612 
00613             // Try again
00614             LOG(VB_NETWORK, LOG_DEBUG, QString("Attempt # %1/%2 for url: %3")
00615                                         .arg(timeoutCount + 1)
00616                                         .arg(maxRetries)
00617                                         .arg(url.toString()));
00618 
00619             continue;
00620         }
00621 
00622         // Check for redirection
00623         if (!httpGrabber->getRedirectedURL().isEmpty())
00624         {
00625             LOG(VB_NETWORK, LOG_DEBUG,
00626                 QString("Redirection: %1, count: %2, max: %3")
00627                                 .arg(httpGrabber->getRedirectedURL())
00628                                 .arg(redirectCount)
00629                                 .arg(maxRedirects));
00630 
00631             // Increment the counter and check we're not over the limit
00632             if (redirectCount++ >= maxRedirects)
00633             {
00634                 LOG(VB_GENERAL, LOG_ERR,
00635                     QString("Maximum redirections reached for url: %1")
00636                         .arg(url.toString()));
00637                 break;
00638             }
00639 
00640             url = QUrl( httpGrabber->getRedirectedURL() );
00641 
00642             // Try again
00643             timeoutCount = 0;
00644             continue;
00645         }
00646 
00647         res = httpGrabber->getData();
00648         break;
00649     }
00650 
00651     delete httpGrabber;
00652 
00653     LOG(VB_NETWORK, LOG_DEBUG, QString("Got %1 bytes from url: '%2'")
00654                                 .arg(res.length())
00655                                 .arg(url.toString()));
00656     LOG(VB_NETWORK, LOG_DEBUG, res);
00657 
00658     return res;
00659 }
00660 
00661 
00662 bool HttpComms::createDigestAuth ( bool isForProxy, const QString& authStr, QHttpRequestHeader* request)
00663 {
00664     const char *p;
00665     QString header;
00666     QString auth;
00667     QByteArray opaque;
00668     QByteArray Response;
00669 
00670     DigestAuthInfo info;
00671 
00672     opaque = "";
00673 
00674     if ( isForProxy )
00675     {
00676         header = "Proxy-Authorization";
00677         auth = "Digest ";
00678         info.username = qPrintable(m_proxyCredentials.user);
00679         info.password = qPrintable(m_proxyCredentials.pass);
00680         p = qPrintable(authStr);
00681     }
00682     else
00683     {
00684         header = "Authorization";
00685         auth = "Digest ";
00686         info.username = qPrintable(m_webCredentials.user);
00687         info.password = qPrintable(m_webCredentials.pass);
00688         p = qPrintable(authStr);
00689     }
00690 
00691     if (!p || !*p)
00692         return false;
00693 
00694     p += 6; // Skip "Digest"
00695 
00696     if ( info.username.isEmpty() || info.password.isEmpty() || !p )
00697         return false;
00698 
00699 
00700     info.realm = "";
00701     info.algorithm = "MD5";
00702     info.nonce = "";
00703     info.qop = "";
00704 
00705     // cnonce is recommended to contain about 64 bits of entropy
00706     // TODO: Fix this
00707     info.cnonce =  "QPDExMTQ";
00708 
00709     // HACK: Should be fixed according to RFC 2617 section 3.2.2
00710     info.nc = "00000001";
00711 
00712     // Set the method used...
00713     info.method.clear();
00714     info.method += request->method();
00715 
00716     // Parse the Digest response....
00717     while (*p)
00718     {
00719         int i = 0;
00720         while ( (*p == ' ') || (*p == ',') || (*p == '\t')) {p++;}
00721 
00722         if (strncasecmp(p, "realm=", 6 )==0)
00723         {
00724             p+=6;
00725             while ( *p == '"' ) p++;  // Go past any number of " mark(s) first
00726             while ( p[i] != '"' ) i++;  // Read everything until the last " mark
00727             info.realm = QByteArray( p, i+1 );
00728         }
00729         else if (strncasecmp(p, "algorith=", 9)==0)
00730         {
00731             p+=9;
00732             while ( *p == '"' ) p++;  // Go past any number of " mark(s) first
00733             while ( ( p[i] != '"' ) && ( p[i] != ',' ) && ( p[i] != '\0' ) ) i++;
00734             info.algorithm = QByteArray(p, i+1);
00735         }
00736         else if (strncasecmp(p, "algorithm=", 10)==0)
00737         {
00738             p+=10;
00739             while ( *p == '"' ) p++;  // Go past any " mark(s) first
00740             while ( ( p[i] != '"' ) && ( p[i] != ',' ) && ( p[i] != '\0' ) ) i++;
00741             info.algorithm = QByteArray(p,i+1);
00742         }
00743         else if (strncasecmp(p, "domain=", 7)==0)
00744         {
00745             p+=7;
00746             while ( *p == '"' ) p++;  // Go past any " mark(s) first
00747             while ( p[i] != '"' ) i++;  // Read everything until the last " mark
00748             int pos;
00749             int idx = 0;
00750             QByteArray uri = QByteArray(p,i+1);
00751             do
00752             {
00753                 pos = uri.indexOf( ' ', idx );
00754 
00755                 if ( pos != -1 )
00756                 {
00757                     QString sUrl = m_url.toString() + "//" + uri.mid(idx, pos-idx);
00758                     QUrl u(sUrl);
00759                     if (u.isValid ())
00760                         info.digestURI.append(qPrintable(u.toString()));
00761                 }
00762                 else
00763                 {
00764                     QString sUrl = m_url.toString() + "//" + uri.mid(idx, uri.length()-idx);
00765                     QUrl u(sUrl);
00766                     if (u.isValid ())
00767                         info.digestURI.append(qPrintable(u.toString()));
00768                 }
00769                 idx = pos+1;
00770             } while ( pos != -1 );
00771         }
00772         else if (strncasecmp(p, "nonce=", 6)==0)
00773         {
00774             p+=6;
00775             while ( *p == '"' ) p++;  // Go past any " mark(s) first
00776             while ( p[i] != '"' ) i++;  // Read everything until the last " mark
00777             info.nonce = QByteArray(p,i+1);
00778         }
00779         else if (strncasecmp(p, "opaque=", 7)==0)
00780         {
00781             p+=7;
00782             while ( *p == '"' ) p++;  // Go past any " mark(s) first
00783             while ( p[i] != '"' ) i++;  // Read everything until the last " mark
00784             opaque = QByteArray(p,i+1);
00785         }
00786         else if (strncasecmp(p, "qop=", 4)==0)
00787         {
00788             p+=4;
00789             while ( *p == '"' ) p++;  // Go past any " mark(s) first
00790             while ( p[i] != '"' ) i++;  // Read everything until the last " mark
00791             info.qop = QByteArray(p,i+1);
00792         }
00793         p+=(i+1);
00794     }
00795 
00796     if (info.realm.isEmpty() || info.nonce.isEmpty())
00797         return false;
00798 
00799     info.digestURI.append (m_url.path() + m_url.encodedQuery());
00800 
00801 #if 0
00802     LOG(VB_GENERAL, LOG_DEBUG, " RESULT OF PARSING:");
00803     LOG(VB_GENERAL, LOG_DEBUG, QString("   algorithm: ") .arg(info.algorithm));
00804     LOG(VB_GENERAL, LOG_DEBUG, QString("   realm:     ") .arg(info.realm));
00805     LOG(VB_GENERAL, LOG_DEBUG, QString("   nonce:     ") .arg(info.nonce));
00806     LOG(VB_GENERAL, LOG_DEBUG, QString("   opaque:    ") .arg(opaque));
00807     LOG(VB_GENERAL, LOG_DEBUG, QString("   qop:       ") .arg(info.qop));
00808 #endif
00809 
00810     // Calculate the response...
00811     calculateDigestResponse( info, Response );
00812 
00813     auth += "username=\"";
00814     auth += info.username;
00815 
00816     auth += "\", realm=\"";
00817     auth += info.realm;
00818     auth += "\"";
00819 
00820     auth += ", nonce=\"";
00821     auth += info.nonce;
00822 
00823     auth += "\", uri=\"";
00824     auth += m_url.path() + m_url.encodedQuery();
00825 
00826     auth += "\", algorithm=\"";
00827     auth += info.algorithm;
00828     auth +="\"";
00829 
00830     if ( !info.qop.isEmpty() )
00831     {
00832         auth += ", qop=\"";
00833         auth += info.qop;
00834         auth += "\", cnonce=\"";
00835         auth += info.cnonce;
00836         auth += "\", nc=";
00837         auth += info.nc;
00838     }
00839 
00840     auth += ", response=\"";
00841     auth += Response;
00842     if ( !opaque.isEmpty() )
00843     {
00844         auth += "\", opaque=\"";
00845         auth += opaque;
00846     }
00847 
00848     auth += "\"";
00849 
00850     LOG(VB_NETWORK, LOG_DEBUG, QString("Setting auth header %1 to '%2'")
00851                                 .arg(header).arg(auth));
00852 
00853     if (request)
00854         request->setValue(header, auth);
00855 
00856     return true;
00857 }
00858 
00859 
00860 void HttpComms::calculateDigestResponse( DigestAuthInfo& info, QByteArray& Response )
00861 {
00862     QMD5 md;
00863     QByteArray HA1;
00864     QByteArray HA2;
00865 
00866     // Calculate H(A1)
00867     QByteArray authStr = info.username;
00868     authStr += ':';
00869     authStr += info.realm;
00870     authStr += ':';
00871     authStr += info.password;
00872     md.update( authStr );
00873 
00874     if (info.algorithm.toLower() == "md5-sess" )
00875     {
00876         authStr = md.hexDigest();
00877         authStr += ':';
00878         authStr += info.nonce;
00879         authStr += ':';
00880         authStr += info.cnonce;
00881         md.reset();
00882         md.update( authStr );
00883     }
00884 
00885     HA1 = md.hexDigest();
00886 
00887 #if 0
00888     LOG(VB_GENERAL, LOG_DEBUG, QString(" calculateResponse(): A1 => %1")
00889                                    .arg(HA1));
00890 #endif
00891 
00892     QString sEncodedPathAndQuery = m_url.path() + m_url.encodedQuery();
00893 
00894     // Calculate H(A2)
00895     authStr = info.method;
00896     authStr += ':';
00897     authStr += qPrintable(sEncodedPathAndQuery);
00898 
00899     if ( info.qop == "auth-int" )
00900     {
00901         authStr += ':';
00902         authStr += info.entityBody;
00903     }
00904 
00905     md.reset();
00906     md.update( authStr );
00907     HA2 = md.hexDigest();
00908 
00909 #if 0
00910     LOG(VB_GENERAL, LOG_DEBUG, QString(" calculateResponse(): A2 => %1")
00911                                    .arg(HA2));
00912 #endif
00913 
00914     // Calculate the response.
00915     authStr = HA1;
00916     authStr += ':';
00917     authStr += info.nonce;
00918     authStr += ':';
00919     if ( !info.qop.isEmpty() )
00920     {
00921         authStr += info.nc;
00922         authStr += ':';
00923         authStr += info.cnonce;
00924         authStr += ':';
00925         authStr += info.qop;
00926         authStr += ':';
00927     }
00928 
00929     authStr += HA2;
00930     md.reset();
00931     md.update( authStr );
00932     Response = md.hexDigest();
00933 
00934 #if 0
00935     LOG(VB_GENERAL, LOG_DEBUG. QString(" calculateResponse(): Response => %1")
00936                                    .arg(Response));
00937 #endif
00938 }
00939 
00940 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends