void GoogleDocumentService::downloadDocument(const QString & _url, const QString & _type) { authToken = docAuthToken; QString url = _url; QString type = _type; url.replace("docId", "docID", Qt::CaseInsensitive); QString exportFormat = ""; if(QString::compare(type, "spreadsheet", Qt::CaseInsensitive) == 0) { exportFormat = "&exportFormat=ods&format=ods"; authToken = spreadAuthToken; } else if(QString::compare(type, "presentation", Qt::CaseInsensitive) == 0) { exportFormat = "&exportFormat=ppt&format=ppt"; } else if(QString::compare(type, "document", Qt::CaseInsensitive) == 0) { exportFormat = "&exportFormat=odt&format=odt"; } qDebug() << "URL = " << url + exportFormat; QUrl documentUrl(url + exportFormat); QNetworkRequest requestHeader(documentUrl); requestHeader.setRawHeader("User-Agent", "Calligra"); requestHeader.setRawHeader("GData-Version", "3.0"); requestHeader.setRawHeader("Authorization", authToken.toUtf8()); QList<QByteArray> headerlist = requestHeader.rawHeaderList(); foreach (const QByteArray &element, headerlist) qDebug() << element << requestHeader.rawHeader(element); networkManager.get(requestHeader); waitingForDoc = true; }
void GoogleDocumentService::listDocuments() { authToken = docAuthToken; QString url; switch (m_type) { case OnlineDocument::WORDS: url = "https://docs.google.com/feeds/default/private/full/-/document"; break; case OnlineDocument::STAGE: url = "https://docs.google.com/feeds/default/private/full/-/presentation"; break; case OnlineDocument::SHEETS: url = "https://docs.google.com/feeds/default/private/full/-/spreadsheet"; break; } QNetworkRequest requestHeader(QUrl(url.toUtf8())); requestHeader.setRawHeader("Host", "docs.google.com"); requestHeader.setRawHeader("User-Agent", "Calligra"); requestHeader.setRawHeader("GData-Version", "3.0"); requestHeader.setRawHeader("Content-Type", "application/atom+xml"); requestHeader.setRawHeader("Authorization", authToken.toUtf8()); networkManager.get(requestHeader); emit progressUpdate("Successfully authenticated!!! Retreiving document list..."); }
int HttpClient::requestHeader(Url &url, HttpHeader &header) { string headStr; int ret = requestHeader(url, headStr); header.setHeaderStr(headStr); return ret; }
QList<FormData> HTTPConnection::parseFormData() const { // make sure we have the correct MIME type QList<QByteArray> elements = requestHeader("Content-Type").split(';'); QString contentType = elements.at(0).trimmed(); if (contentType != "multipart/form-data") { return QList<FormData>(); } // retrieve the boundary marker QByteArray boundary; for (int ii = 1, nn = elements.size(); ii < nn; ii++) { QByteArray element = elements.at(ii).trimmed(); if (element.startsWith("boundary")) { boundary = element.mid(element.indexOf('=') + 1).trimmed(); break; } } QByteArray start = "--" + boundary; QByteArray end = "\r\n--" + boundary + "--\r\n"; QList<FormData> data; QBuffer buffer(const_cast<QByteArray*>(&_requestContent->content())); buffer.open(QIODevice::ReadOnly); while (buffer.canReadLine()) { QByteArray line = buffer.readLine().trimmed(); if (line == start) { FormData datum; while (buffer.canReadLine()) { QByteArray line = buffer.readLine().trimmed(); if (line.isEmpty()) { // content starts after this line int idx = _requestContent->content().indexOf(end, buffer.pos()); if (idx == -1) { qWarning() << "Missing end boundary." << _address; return data; } datum.second = QByteArray::fromRawData(_requestContent->content().data() + buffer.pos(), idx - buffer.pos()); data.append(datum); buffer.seek(idx + end.length()); } else { // it's a header element int idx = line.indexOf(':'); if (idx == -1) { qWarning() << "Invalid header line." << _address << line; continue; } datum.first.insert(line.left(idx).trimmed(), line.mid(idx + 1).trimmed()); } } } } return data; }
void HTTPConnection::readHeaders() { while (_socket->canReadLine()) { QByteArray line = _socket->readLine(); QByteArray trimmed = line.trimmed(); if (trimmed.isEmpty()) { _socket->disconnect(this, SLOT(readHeaders())); QByteArray clength = requestHeader("Content-Length"); if (clength.isEmpty()) { _parentManager->handleHTTPRequest(this, _requestUrl); } else { bool success = false; auto length = clength.toInt(&success); if (!success) { qWarning() << "Invalid header." << _address << trimmed; respond("400 Bad Request", "The header was malformed."); return; } // Storing big requests in memory gets expensive, especially on servers // with limited memory. So we store big requests in a temporary file on disk // and map it to faster read/write access. static const int MAX_CONTENT_SIZE_IN_MEMORY = 10 * 1000 * 1000; if (length < MAX_CONTENT_SIZE_IN_MEMORY) { _requestContent = MemoryStorage::make(length); } else { _requestContent = FileStorage::make(length); } connect(_socket, SIGNAL(readyRead()), SLOT(readContent())); // read any content immediately available readContent(); } return; } char first = line.at(0); if (first == ' ' || first == '\t') { // continuation _requestHeaders[_lastRequestHeader].append(trimmed); continue; } int idx = trimmed.indexOf(':'); if (idx == -1) { qWarning() << "Invalid header." << _address << trimmed; respond("400 Bad Request", "The header was malformed."); return; } _lastRequestHeader = trimmed.left(idx).toLower(); QByteArray& value = _requestHeaders[_lastRequestHeader]; if (!value.isEmpty()) { value.append(", "); } value.append(trimmed.mid(idx + 1).trimmed()); } }
QHash<QString, QString> HTTPConnection::parseUrlEncodedForm() { // make sure we have the correct MIME type QList<QByteArray> elements = requestHeader("Content-Type").split(';'); QString contentType = elements.at(0).trimmed(); if (contentType != "application/x-www-form-urlencoded") { return QHash<QString, QString>(); } QUrlQuery form { _requestContent->content() }; QHash<QString, QString> pairs; for (auto pair : form.queryItems()) { auto key = QUrl::fromPercentEncoding(pair.first.toLatin1().replace('+', ' ')); auto value = QUrl::fromPercentEncoding(pair.second.toLatin1().replace('+', ' ')); pairs[key] = value; } return pairs; }
/** serializes the HTTP response status line plus headers into a byte-stream. * * This method is invoked right before the response content is written or the * response is flushed at all. * * It first sets the status code (if not done yet), invoked post_process callback, * performs connection-level response header modifications and then * builds the response chunk for status line and headers. * * Post-modification done <b>after</b> the post_process hook has been invoked: * <ol> * <li>set status code to 200 (Ok) if not set yet.</li> * <li>set Content-Type header to a default if not set yet.</li> * <li>set Connection header to keep-alive or close (computed value)</li> * <li>append Transfer-Encoding chunked if no Content-Length header is set.</li> * <li>optionally enable TCP_CORK if this is no keep-alive connection and the administrator allows it.</li> * </ol> * * \note this does not serialize the message body. */ Source* HttpRequest::serialize() { Buffer buffers; if (expectingContinue) status = HttpStatus::ExpectationFailed; else if (status == static_cast<HttpStatus>(0)) status = HttpStatus::Ok; if (!responseHeaders.contains("Content-Type")) { responseHeaders.push_back("Content-Type", "text/plain"); //!< \todo pass "default" content-type instead! } if (connection.worker().server().advertise() && !connection.worker().server().tag().empty()) { if (!responseHeaders.contains("Server")) { responseHeaders.push_back("Server", connection.worker().server().tag()); } else { responseHeaders.push_back("Via", connection.worker().server().tag()); } } // post-response hook connection.worker().server().onPostProcess(this); // setup (connection-level) response transfer if (supportsProtocol(1, 1) && !responseHeaders.contains("Content-Length") && !responseHeaders.contains("Transfer-Encoding") && !isResponseContentForbidden()) { responseHeaders.push_back("Transfer-Encoding", "chunked"); outputFilters.push_back(std::make_shared<ChunkedEncoder>()); } bool keepalive = connection.shouldKeepAlive(); if (!connection.worker().server().maxKeepAlive()) keepalive = false; // remaining request count, that is allowed on a persistent connection std::size_t rlim = connection.worker().server().maxKeepAliveRequests(); if (rlim) { rlim = connection.requestCount_ <= rlim ? rlim - connection.requestCount_ + 1 : 0; if (rlim == 0) // disable keep-alive, if max request count has been reached keepalive = false; } // only set Connection-response-header if found as request-header, too if (!requestHeader("Connection").empty() || keepalive != connection.shouldKeepAlive()) { if (keepalive) { responseHeaders.overwrite("Connection", "keep-alive"); if (rlim) { // sent keep-alive timeout and remaining request count char buf[80]; snprintf(buf, sizeof(buf), "timeout=%ld, max=%ld", static_cast<time_t>(connection.worker().server().maxKeepAlive().value()), rlim); responseHeaders.overwrite("Keep-Alive", buf); } else { // sent keep-alive timeout only (infinite request count) char buf[80]; snprintf(buf, sizeof(buf), "timeout=%ld", static_cast<time_t>(connection.worker().server().maxKeepAlive().value())); responseHeaders.overwrite("Keep-Alive", buf); } } else responseHeaders.overwrite("Connection", "close"); } connection.setShouldKeepAlive(keepalive); if (!connection.worker().server().tcpCork()) connection.socket()->setTcpCork(true); if (supportsProtocol(1, 1)) buffers.push_back("HTTP/1.1 "); else if (supportsProtocol(1, 0)) buffers.push_back("HTTP/1.0 "); else buffers.push_back("HTTP/0.9 "); buffers.push_back(statusCodes_[static_cast<int>(status)]); buffers.push_back(' '); buffers.push_back(statusStr(status)); buffers.push_back("\r\n"); for (auto& i: responseHeaders) { buffers.push_back(i.name.data(), i.name.size()); buffers.push_back(": "); buffers.push_back(i.value.data(), i.value.size()); buffers.push_back("\r\n"); }; buffers.push_back("\r\n"); return new BufferSource(std::move(buffers)); }