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:
Jordan Lee 2013-02-10 18:33:04 +00:00
parent 32fc85641a
commit a7c6e78985
9 changed files with 184 additions and 1092 deletions

View File

@ -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);

View File

@ -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.

View File

@ -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

View File

@ -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] };

View File

@ -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'
});
}
},