In Web Client, use jQuery.ajax() to upload files
If we use FormData and jQuery.ajax() calls to upload a torrent, we can stop bundling the jquery.form.js module. In addition, this simplifies passing arguments in the headers s.t. rpc-server.c doesn't have to look for the CSRF token as one of the multiparts. This changes the upload POST behavior, so give it a new name (upload2). The old function (upload) will be deprecated but kept until 2.90 so that third-party web clients using the old POST semantics will have time to update. Bug #5290 <https://trac.transmissionbt.com/ticket/5290>
This commit is contained in:
parent
32fc85641a
commit
a7c6e78985
|
@ -299,6 +299,78 @@ handle_upload (struct evhttp_request * req,
|
|||
}
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
static void
|
||||
handle_rpc_from_json (struct evhttp_request * req,
|
||||
struct tr_rpc_server * server,
|
||||
const char * json,
|
||||
size_t json_len);
|
||||
|
||||
static void
|
||||
handle_upload2 (struct evhttp_request * req,
|
||||
struct tr_rpc_server * server)
|
||||
{
|
||||
if (req->type != EVHTTP_REQ_POST)
|
||||
{
|
||||
send_simple_response (req, 405, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
const char * val;
|
||||
tr_variant top;
|
||||
tr_variant * args;
|
||||
char * json;
|
||||
int json_len;
|
||||
|
||||
tr_variantInitDict (&top, 2);
|
||||
tr_variantDictAddStr (&top, TR_KEY_method, "torrent-add");
|
||||
args = tr_variantDictAddDict (&top, TR_KEY_arguments, 3);
|
||||
|
||||
if ((val = evhttp_find_header (req->input_headers, "X-Transmission-Add-Paused")))
|
||||
tr_variantDictAddBool (args, TR_KEY_paused, !tr_strcmp0(val,"true"));
|
||||
|
||||
if ((val = evhttp_find_header (req->input_headers, "X-Transmission-Add-Download-Dir")) && *val)
|
||||
tr_variantDictAddStr (args, TR_KEY_download_dir, val);
|
||||
|
||||
if ((val = evhttp_find_header (req->input_headers, "X-Transmission-Add-URL")) && *val)
|
||||
{
|
||||
tr_variantDictAddStr (args, TR_KEY_filename, val);
|
||||
}
|
||||
else
|
||||
{
|
||||
int i;
|
||||
int n;
|
||||
bool have_source = false;
|
||||
tr_ptrArray parts = TR_PTR_ARRAY_INIT;
|
||||
|
||||
extract_parts_from_multipart (req->input_headers, req->input_buffer, &parts);
|
||||
n = tr_ptrArraySize (&parts);
|
||||
for (i=0; !have_source && i<n; ++i)
|
||||
{
|
||||
tr_variant test;
|
||||
const struct tr_mimepart * p = tr_ptrArrayNth (&parts, i);
|
||||
if (!tr_variantFromBenc (&test, p->body, p->body_len))
|
||||
{
|
||||
char * b64 = tr_base64_encode (p->body, p->body_len, NULL);
|
||||
tr_variantDictAddStr (args, TR_KEY_metainfo, b64);
|
||||
have_source = true;
|
||||
tr_free (b64);
|
||||
tr_variantFree (&test);
|
||||
}
|
||||
}
|
||||
tr_ptrArrayDestruct (&parts, (PtrArrayForeachFunc)tr_mimepart_free);
|
||||
}
|
||||
|
||||
json = tr_variantToStr (&top, TR_VARIANT_FMT_JSON, &json_len);
|
||||
handle_rpc_from_json (req, server, json, json_len);
|
||||
tr_free (json);
|
||||
tr_variantFree (&top);
|
||||
}
|
||||
}
|
||||
|
||||
static const char*
|
||||
mimetype_guess (const char * path)
|
||||
{
|
||||
|
@ -541,29 +613,43 @@ rpc_response_func (tr_session * session UNUSED,
|
|||
tr_free (data);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_rpc_from_json (struct evhttp_request * req,
|
||||
struct tr_rpc_server * server,
|
||||
const char * json,
|
||||
size_t json_len)
|
||||
{
|
||||
struct rpc_response_data * data;
|
||||
|
||||
data = tr_new0 (struct rpc_response_data, 1);
|
||||
data->req = req;
|
||||
data->server = server;
|
||||
|
||||
tr_rpc_request_exec_json (server->session, json, json_len, rpc_response_func, data);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_rpc (struct evhttp_request * req, struct tr_rpc_server * server)
|
||||
{
|
||||
struct rpc_response_data * data = tr_new0 (struct rpc_response_data, 1);
|
||||
const char * q;
|
||||
|
||||
data->req = req;
|
||||
data->server = server;
|
||||
|
||||
if (req->type == EVHTTP_REQ_GET)
|
||||
if (req->type == EVHTTP_REQ_POST)
|
||||
{
|
||||
const char * q;
|
||||
if ((q = strchr (req->uri, '?')))
|
||||
tr_rpc_request_exec_uri (server->session, q+1, -1, rpc_response_func, data);
|
||||
handle_rpc_from_json (req, server,
|
||||
(const char *) evbuffer_pullup (req->input_buffer, -1),
|
||||
evbuffer_get_length (req->input_buffer));
|
||||
}
|
||||
else if (req->type == EVHTTP_REQ_POST)
|
||||
else if ((req->type == EVHTTP_REQ_GET) && ((q = strchr (req->uri, '?'))))
|
||||
{
|
||||
tr_rpc_request_exec_json (server->session,
|
||||
evbuffer_pullup (req->input_buffer, -1),
|
||||
evbuffer_get_length (req->input_buffer),
|
||||
rpc_response_func, data);
|
||||
struct rpc_response_data * data = tr_new0 (struct rpc_response_data, 1);
|
||||
data->req = req;
|
||||
data->server = server;
|
||||
tr_rpc_request_exec_uri (server->session, q+1, -1, rpc_response_func, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
send_simple_response (req, 405, NULL);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@ -644,7 +730,7 @@ handle_request (struct evhttp_request * req, void * arg)
|
|||
{
|
||||
handle_web_client (req, server);
|
||||
}
|
||||
else if (!strncmp (req->uri + strlen (server->url), "upload", 6))
|
||||
else if (!strcmp (req->uri + strlen (server->url), "upload"))
|
||||
{
|
||||
handle_upload (req, server);
|
||||
}
|
||||
|
@ -669,6 +755,10 @@ handle_request (struct evhttp_request * req, void * arg)
|
|||
tr_free (tmp);
|
||||
}
|
||||
#endif
|
||||
else if (!strcmp (req->uri + strlen (server->url), "upload2"))
|
||||
{
|
||||
handle_upload2 (req, server);
|
||||
}
|
||||
else if (!strncmp (req->uri + strlen (server->url), "rpc", 3))
|
||||
{
|
||||
handle_rpc (req, server);
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
<![endif]-->
|
||||
<script type="text/javascript" src="./javascript/jquery/jquery.transmenu.min.js"></script>
|
||||
<script type="text/javascript" src="./javascript/jquery/jquery.contextmenu.min.js"></script>
|
||||
<script type="text/javascript" src="./javascript/jquery/jquery.form.min.js"></script>
|
||||
<script type="text/javascript" src="./javascript/jquery/json2.min.js"></script>
|
||||
<script type="text/javascript" src="./javascript/common.js"></script>
|
||||
<script type="text/javascript" src="./javascript/inspector.js"></script>
|
||||
|
@ -291,6 +290,8 @@
|
|||
<input type="file" name="torrent_files[]" id="torrent_upload_file" multiple="multiple" />
|
||||
<label for="torrent_upload_url">Or enter a URL:</label>
|
||||
<input type="url" id="torrent_upload_url"/>
|
||||
<label id='add-dialog-folder-label' for="add-dialog-folder-input">Destination folder:</label>
|
||||
<input type="text" id="add-dialog-folder-input"/>
|
||||
<input type="checkbox" id="torrent_auto_start" />
|
||||
<label for="torrent_auto_start" id="auto_start_label">Start when added</label>
|
||||
</div>
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -4,7 +4,5 @@ dist_data_DATA = \
|
|||
jqueryui-1.8.16.min.js \
|
||||
jquery.min.js \
|
||||
jquery.contextmenu.min.js \
|
||||
jquery.form.js \
|
||||
jquery.form.min.js \
|
||||
jquery.transmenu.min.js \
|
||||
json2.min.js
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -124,6 +124,18 @@ TransmissionRemote.prototype =
|
|||
});
|
||||
},
|
||||
|
||||
getFreeSpace: function(dir, callback, context) {
|
||||
var remote = this;
|
||||
var o = {
|
||||
method: 'free-space',
|
||||
arguments: { path: dir }
|
||||
};
|
||||
this.sendRequest(o, function(response) {
|
||||
var args = response['arguments'];
|
||||
callback.call (context, args.path, args['size-bytes']);
|
||||
});
|
||||
},
|
||||
|
||||
changeFileCommand: function(torrentId, fileIndices, command) {
|
||||
var remote = this,
|
||||
args = { ids: [torrentId] };
|
||||
|
|
|
@ -224,6 +224,32 @@ Transmission.prototype =
|
|||
$('#unlimited_upload_rate').selectMenuItem();
|
||||
},
|
||||
|
||||
/****
|
||||
*****
|
||||
****/
|
||||
|
||||
updateFreeSpaceInAddDialog: function()
|
||||
{
|
||||
var formdir = $('input#add-dialog-folder-input').val();
|
||||
this.remote.getFreeSpace (formdir, this.onFreeSpaceResponse, this);
|
||||
},
|
||||
|
||||
onFreeSpaceResponse: function(dir, bytes)
|
||||
{
|
||||
var e, str, formdir;
|
||||
|
||||
formdir = $('input#add-dialog-folder-input').val();
|
||||
if (formdir == dir)
|
||||
{
|
||||
e = $('label#add-dialog-folder-label');
|
||||
if (bytes > 0)
|
||||
str = ' <i>(' + Transmission.fmt.size(bytes) + ' Free)</i>';
|
||||
else
|
||||
str = '';
|
||||
e.html ('Destination folder' + str + ':');
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/****
|
||||
*****
|
||||
|
@ -872,33 +898,49 @@ Transmission.prototype =
|
|||
|
||||
/*
|
||||
* Select a torrent file to upload
|
||||
* FIXME
|
||||
*/
|
||||
uploadTorrentFile: function(confirmed)
|
||||
{
|
||||
// Display the upload dialog
|
||||
if (! confirmed) {
|
||||
$('input#torrent_upload_file').attr('value', '');
|
||||
$('input#torrent_upload_url').attr('value', '');
|
||||
$('input#torrent_auto_start').attr('checked', this.shouldAddedTorrentsStart());
|
||||
$('#upload_container').show();
|
||||
$('#torrent_upload_url').focus();
|
||||
var formData,
|
||||
fileInput = $('input#torrent_upload_file'),
|
||||
folderInput = $('input#add-dialog-folder-input'),
|
||||
startInput = $('input#torrent_auto_start'),
|
||||
urlInput = $('input#torrent_upload_url');
|
||||
|
||||
// Submit the upload form
|
||||
} else {
|
||||
var args = {};
|
||||
var remote = this.remote;
|
||||
var paused = !$('#torrent_auto_start').is(':checked');
|
||||
if ('' != $('#torrent_upload_url').val()) {
|
||||
remote.addTorrentByUrl($('#torrent_upload_url').val(), { paused: paused });
|
||||
} else {
|
||||
args.url = '../upload?paused=' + paused;
|
||||
args.type = 'POST';
|
||||
args.data = { 'X-Transmission-Session-Id' : remote._token };
|
||||
args.dataType = 'xml';
|
||||
args.iframe = true;
|
||||
$('#torrent_upload_form').ajaxSubmit(args);
|
||||
}
|
||||
if (!confirmed)
|
||||
{
|
||||
// update the upload dialog's fields
|
||||
fileInput.attr('value', '');
|
||||
urlInput.attr('value', '');
|
||||
startInput.attr('checked', this.shouldAddedTorrentsStart());
|
||||
folderInput.attr('value', $("#download-dir").val());
|
||||
folderInput.change($.proxy(this.updateFreeSpaceInAddDialog,this));
|
||||
this.updateFreeSpaceInAddDialog();
|
||||
|
||||
// show the dialog
|
||||
$('#upload_container').show();
|
||||
urlInput.focus();
|
||||
}
|
||||
else
|
||||
{
|
||||
formData = new FormData ();
|
||||
jQuery.each(fileInput[0].files, function(i, file) {
|
||||
formData.append ('file-'+i, file);
|
||||
});
|
||||
$.ajax ({
|
||||
url: '../upload2',
|
||||
data: formData,
|
||||
headers : {
|
||||
'X-Transmission-Session-Id': this.remote._token,
|
||||
'X-Transmission-Add-Paused': !startInput.is(':checked'),
|
||||
'X-Transmission-Add-Download-Dir': folderInput.val(),
|
||||
'X-Transmission-Add-URL': urlInput.val()
|
||||
},
|
||||
cache: false,
|
||||
contentType: false,
|
||||
processData: false,
|
||||
type: 'POST'
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
|
|
Loading…
Reference in New Issue