Browse Source

Add support for adding multiple local torrents at once (Web UI)

adaptive-webui-19844
Christophe Dumez 13 years ago
parent
commit
a98ad63d8b
  1. 1
      Changelog
  2. 49
      src/webui/html/upload.html
  3. 5
      src/webui/httpconnection.cpp
  4. 52
      src/webui/httprequestparser.cpp
  5. 4
      src/webui/httprequestparser.h
  6. 2
      src/webui/scripts/mocha-init.js

1
Changelog

@ -1,6 +1,7 @@
* Unreleased - Christophe Dumez <chris@qbittorrent.org> - v3.0.0 * Unreleased - Christophe Dumez <chris@qbittorrent.org> - v3.0.0
- FEATURE: Brand new torrent addition dialog - FEATURE: Brand new torrent addition dialog
- FEATURE: Add the ability to choose the save path when using magnet links (mutoso) - FEATURE: Add the ability to choose the save path when using magnet links (mutoso)
- FEATURE: Add support for adding multiple local torrents at once (Web UI)
- COSMETIC: Improve style of left panel - COSMETIC: Improve style of left panel
- BUGFIX: Lower panels no longer gets disabled - BUGFIX: Lower panels no longer gets disabled
- OTHER: Drop support for libtorrent v0.14.x - OTHER: Drop support for libtorrent v0.14.x

49
src/webui/html/upload.html

@ -6,19 +6,58 @@
<link rel="stylesheet" href="css/style.css" type="text/css" /> <link rel="stylesheet" href="css/style.css" type="text/css" />
<script type="text/javascript" src="scripts/mootools-1.2-core-yc.js" charset="utf-8"></script> <script type="text/javascript" src="scripts/mootools-1.2-core-yc.js" charset="utf-8"></script>
<script type="text/javascript"> <script type="text/javascript">
function hideAll() { function hideAll() {
window.parent.closeWindows(); window.parent.closeWindows();
} }
function uploadFiles(files) {
var xhr = new XMLHttpRequest();
if (xhr.upload) {
// file received/failed
xhr.onreadystatechange = function(e) {
if (xhr.readyState == 4) {
if (xhr.status == 200)
hideAll();
else
alert("Upload Failed!");
}
};
// start upload
var formData = new FormData();
for (var i = 0, file; file = files[i]; ++i) {
formData.append(file.name, file);
}
xhr.open("POST", "command/upload", true);
xhr.send(formData);
}
}
// file selection
function fileSelectHandler(e) {
// fetch FileList object
var files = e.target.files || e.dataTransfer.files;
// process all File objects
uploadFiles(files);
}
window.addEvent('load', function() {
$('fileselect').addEvent('change', fileSelectHandler);
// is XHR2 available?
var xhr = new XMLHttpRequest();
if (xhr.upload) {
$('submitbutton').addClass("invisible");
}
});
</script> </script>
</head> </head>
<body> <body>
<br/>
<iframe id="upload_frame" name="upload_frame" style="width:1px;height:1px;border:0px;" src="javascript:false;"></iframe> <iframe id="upload_frame" name="upload_frame" style="width:1px;height:1px;border:0px;" src="javascript:false;"></iframe>
<center>
<h1 class="vcenter"><img class="vcenter" title="Download local torrent" src="theme/list-add"/>_(Download local torrent)</h1>
<form action="command/upload" enctype="multipart/form-data" method="post" id="uploadForm" target="upload_frame"> <form action="command/upload" enctype="multipart/form-data" method="post" id="uploadForm" target="upload_frame">
<input type="file" name="torrentfile" id="torrentfile" size="40"/><br/><br/> <input type="file" id="fileselect" name="fileselect[]" multiple="multiple" /><br/>
<input type="submit" value="_(Download)" id="upButton"/> <div id="submitbutton">
<button type="submit">_(Upload Torrents)</button>
</div
</form> </form>
</center>
</body> </body>
</html> </html>

5
src/webui/httpconnection.cpp

@ -419,12 +419,14 @@ void HttpConnection::respondCommand(const QString& command) {
} }
if (command == "upload") { if (command == "upload") {
qDebug() << Q_FUNC_INFO << "upload"; qDebug() << Q_FUNC_INFO << "upload";
const QList<QByteArray>& torrents = m_parser.torrents();
foreach(const QByteArray& torrentContent, torrents) {
// Get a unique filename // Get a unique filename
QTemporaryFile *tmpfile = new QTemporaryFile (QDir::temp().absoluteFilePath("qBT-XXXXXX.torrent")); QTemporaryFile *tmpfile = new QTemporaryFile (QDir::temp().absoluteFilePath("qBT-XXXXXX.torrent"));
tmpfile->setAutoRemove(false); tmpfile->setAutoRemove(false);
if (tmpfile->open()) { if (tmpfile->open()) {
QString filePath = tmpfile->fileName(); QString filePath = tmpfile->fileName();
tmpfile->write(m_parser.torrent()); tmpfile->write(torrentContent);
tmpfile->close(); tmpfile->close();
// XXX: tmpfile needs to be deleted on Windows before using the file // XXX: tmpfile needs to be deleted on Windows before using the file
// or it will complain that the file is used by another process. // or it will complain that the file is used by another process.
@ -437,6 +439,7 @@ void HttpConnection::respondCommand(const QString& command) {
delete tmpfile; delete tmpfile;
return; return;
} }
}
// Prepare response // Prepare response
m_generator.setStatusLine(200, "OK"); m_generator.setStatusLine(200, "OK");
m_generator.setContentTypeByExt("html"); m_generator.setContentTypeByExt("html");

52
src/webui/httprequestparser.cpp

@ -61,8 +61,8 @@ QString HttpRequestParser::post(const QString& key) const {
return m_postMap.value(key); return m_postMap.value(key);
} }
const QByteArray& HttpRequestParser::torrent() const { const QList<QByteArray>& HttpRequestParser::torrents() const {
return m_torrentContent; return m_torrents;
} }
void HttpRequestParser::writeHeader(const QByteArray& ba) { void HttpRequestParser::writeHeader(const QByteArray& ba) {
@ -79,6 +79,18 @@ void HttpRequestParser::writeHeader(const QByteArray& ba) {
} }
} }
static QList<QByteArray> splitRawData(QByteArray rawData, const QByteArray& sep)
{
QList<QByteArray> ret;
const int sepLength = sep.size();
int index = 0;
while ((index = rawData.indexOf(sep)) >= 0) {
ret << rawData.left(index);
rawData = rawData.mid(index + sepLength);
}
return ret;
}
void HttpRequestParser::writeMessage(const QByteArray& ba) { void HttpRequestParser::writeMessage(const QByteArray& ba) {
// Parse message content // Parse message content
Q_ASSERT (m_header.hasContentLength()); Q_ASSERT (m_header.hasContentLength());
@ -119,21 +131,29 @@ Submit Query
**/ **/
if (m_header.contentType().startsWith("multipart/form-data")) { if (m_header.contentType().startsWith("multipart/form-data")) {
qDebug() << Q_FUNC_INFO << "header is: " << m_header.toString(); qDebug() << Q_FUNC_INFO << "header is: " << m_header.toString();
static QRegExp boundaryRegexQuoted("boundary=\"([ \\w'()+,-\\./:=\\?]+)\"");
int filename_index = m_data.indexOf("filename="); static QRegExp boundaryRegexNotQuoted("boundary=([\\w'()+,-\\./:=\\?]+)");
if (filename_index >= 0) { QByteArray boundary;
QByteArray boundary = m_data.left(m_data.indexOf("\r\n")); if (boundaryRegexQuoted.indexIn(m_header.toString()) < 0) {
qDebug() << "Boundary is " << boundary << "\n\n"; if (boundaryRegexNotQuoted.indexIn(m_header.toString()) < 0) {
qDebug() << "Before binary data: " << m_data.left(m_data.indexOf("\r\n\r\n", filename_index+9)) << "\n\n"; qWarning() << "Could not find boundary in multipart/form-data header!";
m_torrentContent = m_data.mid(m_data.indexOf("\r\n\r\n", filename_index+9) + 4);
int binaryend_index = m_torrentContent.indexOf("\r\n"+boundary);
if (binaryend_index >= 0) {
qDebug() << "found end boundary :)";
m_torrentContent = m_torrentContent.left(binaryend_index);
}
qDebug() << Q_FUNC_INFO << "m_torrentContent.size(): " << m_torrentContent.size()<< "\n\n";
} else {
m_error = true; m_error = true;
return;
} else {
boundary = "--" + boundaryRegexNotQuoted.cap(1).toAscii();
}
} else {
boundary = "--" + boundaryRegexQuoted.cap(1).toAscii();
}
qDebug() << "Boundary is " << boundary;
QList<QByteArray> parts = splitRawData(m_data, boundary);
qDebug() << parts.size() << "parts in data";
foreach (const QByteArray& part, parts) {
const int filenameIndex = part.indexOf("filename=");
if (filenameIndex < 0)
continue;
qDebug() << "Found a torrent";
m_torrents << part.mid(part.indexOf("\r\n\r\n", filenameIndex + 9) + 4);
} }
} }
} }

4
src/webui/httprequestparser.h

@ -45,7 +45,7 @@ public:
const QByteArray& message() const; const QByteArray& message() const;
QString get(const QString& key) const; QString get(const QString& key) const;
QString post(const QString& key) const; QString post(const QString& key) const;
const QByteArray& torrent() const; const QList<QByteArray>& torrents() const;
void writeHeader(const QByteArray& ba); void writeHeader(const QByteArray& ba);
void writeMessage(const QByteArray& ba); void writeMessage(const QByteArray& ba);
inline const QHttpRequestHeader& header() const { return m_header; } inline const QHttpRequestHeader& header() const { return m_header; }
@ -57,7 +57,7 @@ private:
QString m_path; QString m_path;
QHash<QString, QString> m_postMap; QHash<QString, QString> m_postMap;
QHash<QString, QString> m_getMap; QHash<QString, QString> m_getMap;
QByteArray m_torrentContent; QList<QByteArray> m_torrents;
}; };
#endif #endif

2
src/webui/scripts/mocha-init.js

@ -72,7 +72,7 @@ initializeWindows = function(){
paddingVertical: 0, paddingVertical: 0,
paddingHorizontal: 0, paddingHorizontal: 0,
width: 600, width: 600,
height: 170 height: 130
}); });
}); });

Loading…
Cancel
Save