void ID3_Parser_Private::feedData(UInt8 *data, UInt32 numBytes) { if (!wantData()) { return; } m_bytesReceived += numBytes; ID3_TRACE("received %i bytes, total bytes %i\n", numBytes, m_bytesReceived); for (CFIndex i=0; i < numBytes; i++) { m_tagData.push_back(data[i]); } bool enoughBytesToParse = true; while (enoughBytesToParse) { switch (m_state) { case ID3_Parser_State_Initial: { // Do we have enough bytes to determine if this is an ID3 tag or not? if (m_bytesReceived <= 9) { enoughBytesToParse = false; break; } if (!(m_tagData[0] == 'I' && m_tagData[1] == 'D' && m_tagData[2] == '3')) { ID3_TRACE("Not an ID3 tag, bailing out\n"); // Does not begin with the tag header; not an ID3 tag setState(ID3_Parser_State_Not_Valid_Tag); enoughBytesToParse = false; break; } UInt8 majorVersion = m_tagData[3]; // Currently support only id3v2.3 if (majorVersion != 3) { ID3_TRACE("ID3v2.%i not supported by the parser\n", majorVersion); setState(ID3_Parser_State_Not_Valid_Tag); enoughBytesToParse = false; break; } // Ignore the revision // Parse the flags if ((m_tagData[5] & 0x80) != 0) { m_usesUnsynchronisation = true; } else if ((m_tagData[5] & 0x40) != 0) { m_usesExtendedHeader = true; } else if ((m_tagData[5] & 0x10) != 0) { m_hasFooter = true; } m_tagSize = (m_tagData[6] << 21) + (m_tagData[7] << 14) + (m_tagData[8] << 7) + m_tagData[9]; if (m_tagSize > 0) { if (m_hasFooter) { m_tagSize += 10; } m_tagSize += 10; ID3_TRACE("tag size: %i\n", m_tagSize); setState(ID3_Parser_State_Parse_Frames); break; } setState(ID3_Parser_State_Not_Valid_Tag); enoughBytesToParse = false; break; } case ID3_Parser_State_Parse_Frames: { // Do we have enough data to parse the frames? if (m_tagData.size() < m_tagSize) { ID3_TRACE("Not enough data received for parsing, have %lu bytes, need %i bytes\n", m_tagData.size(), m_tagSize); enoughBytesToParse = false; break; } UInt32 pos = 10; // Do we have an extended header? If we do, skip it if (m_usesExtendedHeader) { UInt32 extendedHeaderSize = ((m_tagData[pos] << 21) | (m_tagData[pos+1] << 14) | (m_tagData[pos+2] << 7) | m_tagData[pos+3]); if (pos + extendedHeaderSize >= m_tagSize) { setState(ID3_Parser_State_Not_Valid_Tag); enoughBytesToParse = false; break; } ID3_TRACE("Skipping extended header, size %i\n", extendedHeaderSize); pos += extendedHeaderSize; } while (pos < m_tagSize) { char frameName[5]; frameName[0] = m_tagData[pos]; frameName[1] = m_tagData[pos+1]; frameName[2] = m_tagData[pos+2]; frameName[3] = m_tagData[pos+3]; frameName[4] = 0; pos += 4; UInt32 framesize = ((m_tagData[pos] << 21) | (m_tagData[pos+1] << 14) | (m_tagData[pos+2] << 7) | m_tagData[pos+3]); if (framesize == 0) { setState(ID3_Parser_State_Not_Valid_Tag); enoughBytesToParse = false; break; } pos += 6; CFStringEncoding encoding; bool byteOrderMark = false; if (m_tagData[pos] == 3) { encoding = kCFStringEncodingUTF8; } else if (m_tagData[pos] == 2) { encoding = kCFStringEncodingUTF16BE; } else if (m_tagData[pos] == 1) { encoding = kCFStringEncodingUTF16; byteOrderMark = true; } else { // ISO-8859-1 is the default encoding encoding = kCFStringEncodingISOLatin1; } if (!strcmp(frameName, "TIT2")) { if (m_title) { CFRelease(m_title); } m_title = parseContent(framesize, pos + 1, encoding, byteOrderMark); ID3_TRACE("ID3 title parsed: '%s'\n", CFStringGetCStringPtr(m_title, CFStringGetSystemEncoding())); } else if (!strcmp(frameName, "TPE1")) { if (m_performer) { CFRelease(m_performer); } m_performer = parseContent(framesize, pos + 1, encoding, byteOrderMark); ID3_TRACE("ID3 performer parsed: '%s'\n", CFStringGetCStringPtr(m_performer, CFStringGetSystemEncoding())); } else { // Unknown/unhandled frame ID3_TRACE("Unknown/unhandled frame: %s, size %i\n", frameName, framesize); } pos += framesize; } // Push out the metadata if (m_parser->m_delegate) { std::map<CFStringRef,CFStringRef> metadataMap; if (m_performer && CFStringGetLength(m_performer) > 0) { metadataMap[CFSTR("MPMediaItemPropertyArtist")] = CFStringCreateCopy(kCFAllocatorDefault, m_performer); } if (m_title && CFStringGetLength(m_title) > 0) { metadataMap[CFSTR("MPMediaItemPropertyTitle")] = CFStringCreateCopy(kCFAllocatorDefault, m_title); } m_parser->m_delegate->id3metaDataAvailable(metadataMap); } setState(ID3_Parser_State_Tag_Parsed); enoughBytesToParse = false; break; } default: enoughBytesToParse = false; break; } } }
void QNetworkAccessHttpBackend::postRequest() { QThread *thread = 0; if (isSynchronous()) { // A synchronous HTTP request uses its own thread thread = new QThread(); QObject::connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); thread->start(); } else if (!manager->httpThread) { // We use the manager-global thread. // At some point we could switch to having multiple threads if it makes sense. manager->httpThread = new QThread(); QObject::connect(manager->httpThread, SIGNAL(finished()), manager->httpThread, SLOT(deleteLater())); manager->httpThread->start(); #ifndef QT_NO_NETWORKPROXY qRegisterMetaType<QNetworkProxy>("QNetworkProxy"); #endif #ifndef QT_NO_OPENSSL qRegisterMetaType<QList<QSslError> >("QList<QSslError>"); qRegisterMetaType<QSslConfiguration>("QSslConfiguration"); #endif qRegisterMetaType<QList<QPair<QByteArray,QByteArray> > >("QList<QPair<QByteArray,QByteArray> >"); qRegisterMetaType<QHttpNetworkRequest>("QHttpNetworkRequest"); qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError"); qRegisterMetaType<QSharedPointer<char> >("QSharedPointer<char>"); thread = manager->httpThread; } else { // Asynchronous request, thread already exists thread = manager->httpThread; } QUrl url = request().url(); httpRequest.setUrl(url); bool ssl = url.scheme().toLower() == QLatin1String("https"); setAttribute(QNetworkRequest::ConnectionEncryptedAttribute, ssl); httpRequest.setSsl(ssl); #ifndef QT_NO_NETWORKPROXY QNetworkProxy transparentProxy, cacheProxy; foreach (const QNetworkProxy &p, proxyList()) { // use the first proxy that works // for non-encrypted connections, any transparent or HTTP proxy // for encrypted, only transparent proxies if (!ssl && (p.capabilities() & QNetworkProxy::CachingCapability) && (p.type() == QNetworkProxy::HttpProxy || p.type() == QNetworkProxy::HttpCachingProxy)) { cacheProxy = p; transparentProxy = QNetworkProxy::NoProxy; break; } if (p.isTransparentProxy()) { transparentProxy = p; cacheProxy = QNetworkProxy::NoProxy; break; } } // check if at least one of the proxies if (transparentProxy.type() == QNetworkProxy::DefaultProxy && cacheProxy.type() == QNetworkProxy::DefaultProxy) { // unsuitable proxies QMetaObject::invokeMethod(this, "error", isSynchronous() ? Qt::DirectConnection : Qt::QueuedConnection, Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ProxyNotFoundError), Q_ARG(QString, tr("No suitable proxy found"))); QMetaObject::invokeMethod(this, "finished", isSynchronous() ? Qt::DirectConnection : Qt::QueuedConnection); return; } #endif bool loadedFromCache = false; httpRequest.setPriority(convert(request().priority())); switch (operation()) { case QNetworkAccessManager::GetOperation: httpRequest.setOperation(QHttpNetworkRequest::Get); loadedFromCache = loadFromCacheIfAllowed(httpRequest); break; case QNetworkAccessManager::HeadOperation: httpRequest.setOperation(QHttpNetworkRequest::Head); loadedFromCache = loadFromCacheIfAllowed(httpRequest); break; case QNetworkAccessManager::PostOperation: invalidateCache(); httpRequest.setOperation(QHttpNetworkRequest::Post); createUploadByteDevice(); break; case QNetworkAccessManager::PutOperation: invalidateCache(); httpRequest.setOperation(QHttpNetworkRequest::Put); createUploadByteDevice(); break; case QNetworkAccessManager::DeleteOperation: invalidateCache(); httpRequest.setOperation(QHttpNetworkRequest::Delete); break; case QNetworkAccessManager::CustomOperation: invalidateCache(); // for safety reasons, we don't know what the operation does httpRequest.setOperation(QHttpNetworkRequest::Custom); createUploadByteDevice(); httpRequest.setCustomVerb(request().attribute( QNetworkRequest::CustomVerbAttribute).toByteArray()); break; default: break; // can't happen } if (loadedFromCache) { // commented this out since it will be called later anyway // by copyFinished() //QNetworkAccessBackend::finished(); return; // no need to send the request! :) } QList<QByteArray> headers = request().rawHeaderList(); if (resumeOffset != 0) { if (headers.contains("Range")) { // Need to adjust resume offset for user specified range headers.removeOne("Range"); // We've already verified that requestRange starts with "bytes=", see canResume. QByteArray requestRange = request().rawHeader("Range").mid(6); int index = requestRange.indexOf('-'); quint64 requestStartOffset = requestRange.left(index).toULongLong(); quint64 requestEndOffset = requestRange.mid(index + 1).toULongLong(); requestRange = "bytes=" + QByteArray::number(resumeOffset + requestStartOffset) + '-' + QByteArray::number(requestEndOffset); httpRequest.setHeaderField("Range", requestRange); } else { httpRequest.setHeaderField("Range", "bytes=" + QByteArray::number(resumeOffset) + '-'); } } foreach (const QByteArray &header, headers) httpRequest.setHeaderField(header, request().rawHeader(header)); if (request().attribute(QNetworkRequest::HttpPipeliningAllowedAttribute).toBool() == true) httpRequest.setPipeliningAllowed(true); if (static_cast<QNetworkRequest::LoadControl> (request().attribute(QNetworkRequest::AuthenticationReuseAttribute, QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Manual) httpRequest.setWithCredentials(false); // Create the HTTP thread delegate QHttpThreadDelegate *delegate = new QHttpThreadDelegate; #ifndef QT_NO_BEARERMANAGEMENT QVariant v(property("_q_networksession")); if (v.isValid()) delegate->networkSession = qvariant_cast<QSharedPointer<QNetworkSession> >(v); #endif // For the synchronous HTTP, this is the normal way the delegate gets deleted // For the asynchronous HTTP this is a safety measure, the delegate deletes itself when HTTP is finished connect(thread, SIGNAL(finished()), delegate, SLOT(deleteLater())); // Set the properties it needs delegate->httpRequest = httpRequest; #ifndef QT_NO_NETWORKPROXY delegate->cacheProxy = cacheProxy; delegate->transparentProxy = transparentProxy; #endif delegate->ssl = ssl; #ifndef QT_NO_OPENSSL if (ssl) delegate->incomingSslConfiguration = request().sslConfiguration(); #endif // Do we use synchronous HTTP? delegate->synchronous = isSynchronous(); // The authentication manager is used to avoid the BlockingQueuedConnection communication // from HTTP thread to user thread in some cases. delegate->authenticationManager = manager->authenticationManager; if (!isSynchronous()) { // Tell our zerocopy policy to the delegate delegate->downloadBufferMaximumSize = request().attribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute).toLongLong(); // These atomic integers are used for signal compression delegate->pendingDownloadData = pendingDownloadDataEmissions; delegate->pendingDownloadProgress = pendingDownloadProgressEmissions; // Connect the signals of the delegate to us connect(delegate, SIGNAL(downloadData(QByteArray)), this, SLOT(replyDownloadData(QByteArray)), Qt::QueuedConnection); connect(delegate, SIGNAL(downloadFinished()), this, SLOT(replyFinished()), Qt::QueuedConnection); connect(delegate, SIGNAL(downloadMetaData(QList<QPair<QByteArray,QByteArray> >,int,QString,bool,QSharedPointer<char>,qint64)), this, SLOT(replyDownloadMetaData(QList<QPair<QByteArray,QByteArray> >,int,QString,bool,QSharedPointer<char>,qint64)), Qt::QueuedConnection); connect(delegate, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(replyDownloadProgressSlot(qint64,qint64)), Qt::QueuedConnection); connect(delegate, SIGNAL(error(QNetworkReply::NetworkError,QString)), this, SLOT(httpError(QNetworkReply::NetworkError, const QString)), Qt::QueuedConnection); #ifndef QT_NO_OPENSSL connect(delegate, SIGNAL(sslConfigurationChanged(QSslConfiguration)), this, SLOT(replySslConfigurationChanged(QSslConfiguration)), Qt::QueuedConnection); #endif // Those need to report back, therefire BlockingQueuedConnection connect(delegate, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)), this, SLOT(httpAuthenticationRequired(QHttpNetworkRequest,QAuthenticator*)), Qt::BlockingQueuedConnection); #ifndef QT_NO_NETWORKPROXY connect (delegate, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), Qt::BlockingQueuedConnection); #endif #ifndef QT_NO_OPENSSL connect(delegate, SIGNAL(sslErrors(QList<QSslError>,bool*,QList<QSslError>*)), this, SLOT(replySslErrors(const QList<QSslError> &, bool *, QList<QSslError> *)), Qt::BlockingQueuedConnection); #endif // This signal we will use to start the request. connect(this, SIGNAL(startHttpRequest()), delegate, SLOT(startRequest())); connect(this, SIGNAL(abortHttpRequest()), delegate, SLOT(abortRequest())); // To throttle the connection. QObject::connect(this, SIGNAL(readBufferSizeChanged(qint64)), delegate, SLOT(readBufferSizeChanged(qint64))); QObject::connect(this, SIGNAL(readBufferFreed(qint64)), delegate, SLOT(readBufferFreed(qint64))); if (uploadByteDevice) { QNonContiguousByteDeviceThreadForwardImpl *forwardUploadDevice = new QNonContiguousByteDeviceThreadForwardImpl(uploadByteDevice->atEnd(), uploadByteDevice->size()); if (uploadByteDevice->isResetDisabled()) forwardUploadDevice->disableReset(); forwardUploadDevice->setParent(delegate); // needed to make sure it is moved on moveToThread() delegate->httpRequest.setUploadByteDevice(forwardUploadDevice); // From main thread to user thread: QObject::connect(this, SIGNAL(haveUploadData(QByteArray, bool, qint64)), forwardUploadDevice, SLOT(haveDataSlot(QByteArray, bool, qint64)), Qt::QueuedConnection); QObject::connect(uploadByteDevice.data(), SIGNAL(readyRead()), forwardUploadDevice, SIGNAL(readyRead()), Qt::QueuedConnection); // From http thread to user thread: QObject::connect(forwardUploadDevice, SIGNAL(wantData(qint64)), this, SLOT(wantUploadDataSlot(qint64))); QObject::connect(forwardUploadDevice, SIGNAL(processedData(qint64)), this, SLOT(sentUploadDataSlot(qint64))); connect(forwardUploadDevice, SIGNAL(resetData(bool*)), this, SLOT(resetUploadDataSlot(bool*)), Qt::BlockingQueuedConnection); // this is the only one with BlockingQueued! } } else if (isSynchronous()) {