1
0
Fork 0
mirror of https://github.com/transmission/transmission synced 2025-02-20 13:16:53 +00:00

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* static const char*
mimetype_guess (const char * path) mimetype_guess (const char * path)
{ {
@ -541,29 +613,43 @@ rpc_response_func (tr_session * session UNUSED,
tr_free (data); 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 static void
handle_rpc (struct evhttp_request * req, struct tr_rpc_server * server) 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; if (req->type == EVHTTP_REQ_POST)
data->server = server;
if (req->type == EVHTTP_REQ_GET)
{ {
const char * q; handle_rpc_from_json (req, server,
if ((q = strchr (req->uri, '?'))) (const char *) evbuffer_pullup (req->input_buffer, -1),
tr_rpc_request_exec_uri (server->session, q+1, -1, rpc_response_func, data); 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, struct rpc_response_data * data = tr_new0 (struct rpc_response_data, 1);
evbuffer_pullup (req->input_buffer, -1), data->req = req;
evbuffer_get_length (req->input_buffer), data->server = server;
rpc_response_func, data); tr_rpc_request_exec_uri (server->session, q+1, -1, rpc_response_func, data);
}
else
{
send_simple_response (req, 405, NULL);
} }
} }
static bool static bool
@ -644,7 +730,7 @@ handle_request (struct evhttp_request * req, void * arg)
{ {
handle_web_client (req, server); 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); handle_upload (req, server);
} }
@ -669,6 +755,10 @@ handle_request (struct evhttp_request * req, void * arg)
tr_free (tmp); tr_free (tmp);
} }
#endif #endif
else if (!strcmp (req->uri + strlen (server->url), "upload2"))
{
handle_upload2 (req, server);
}
else if (!strncmp (req->uri + strlen (server->url), "rpc", 3)) else if (!strncmp (req->uri + strlen (server->url), "rpc", 3))
{ {
handle_rpc (req, server); handle_rpc (req, server);

View file

@ -21,7 +21,6 @@
<![endif]--> <![endif]-->
<script type="text/javascript" src="./javascript/jquery/jquery.transmenu.min.js"></script> <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.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/jquery/json2.min.js"></script>
<script type="text/javascript" src="./javascript/common.js"></script> <script type="text/javascript" src="./javascript/common.js"></script>
<script type="text/javascript" src="./javascript/inspector.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" /> <input type="file" name="torrent_files[]" id="torrent_upload_file" multiple="multiple" />
<label for="torrent_upload_url">Or enter a URL:</label> <label for="torrent_upload_url">Or enter a URL:</label>
<input type="url" id="torrent_upload_url"/> <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" /> <input type="checkbox" id="torrent_auto_start" />
<label for="torrent_auto_start" id="auto_start_label">Start when added</label> <label for="torrent_auto_start" id="auto_start_label">Start when added</label>
</div> </div>

Binary file not shown.

Binary file not shown.

View file

@ -4,7 +4,5 @@ dist_data_DATA = \
jqueryui-1.8.16.min.js \ jqueryui-1.8.16.min.js \
jquery.min.js \ jquery.min.js \
jquery.contextmenu.min.js \ jquery.contextmenu.min.js \
jquery.form.js \
jquery.form.min.js \
jquery.transmenu.min.js \ jquery.transmenu.min.js \
json2.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) { changeFileCommand: function(torrentId, fileIndices, command) {
var remote = this, var remote = this,
args = { ids: [torrentId] }; args = { ids: [torrentId] };

View file

@ -224,6 +224,32 @@ Transmission.prototype =
$('#unlimited_upload_rate').selectMenuItem(); $('#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 * Select a torrent file to upload
* FIXME
*/ */
uploadTorrentFile: function(confirmed) uploadTorrentFile: function(confirmed)
{ {
// Display the upload dialog var formData,
if (! confirmed) { fileInput = $('input#torrent_upload_file'),
$('input#torrent_upload_file').attr('value', ''); folderInput = $('input#add-dialog-folder-input'),
$('input#torrent_upload_url').attr('value', ''); startInput = $('input#torrent_auto_start'),
$('input#torrent_auto_start').attr('checked', this.shouldAddedTorrentsStart()); urlInput = $('input#torrent_upload_url');
$('#upload_container').show();
$('#torrent_upload_url').focus();
// Submit the upload form if (!confirmed)
} else { {
var args = {}; // update the upload dialog's fields
var remote = this.remote; fileInput.attr('value', '');
var paused = !$('#torrent_auto_start').is(':checked'); urlInput.attr('value', '');
if ('' != $('#torrent_upload_url').val()) { startInput.attr('checked', this.shouldAddedTorrentsStart());
remote.addTorrentByUrl($('#torrent_upload_url').val(), { paused: paused }); folderInput.attr('value', $("#download-dir").val());
} else { folderInput.change($.proxy(this.updateFreeSpaceInAddDialog,this));
args.url = '../upload?paused=' + paused; this.updateFreeSpaceInAddDialog();
args.type = 'POST';
args.data = { 'X-Transmission-Session-Id' : remote._token }; // show the dialog
args.dataType = 'xml'; $('#upload_container').show();
args.iframe = true; urlInput.focus();
$('#torrent_upload_form').ajaxSubmit(args); }
} 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'
});
} }
}, },