Changeset 14005


Ignore:
Timestamp:
Feb 10, 2013, 6:33:04 PM (8 years ago)
Author:
jordan
Message:

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>

Location:
trunk
Files:
2 added
2 deleted
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/libtransmission/rpc-server.c

    r13934 r14005  
    300300}
    301301
     302/***
     303****
     304***/
     305
     306static void
     307handle_rpc_from_json (struct evhttp_request * req,
     308                      struct tr_rpc_server  * server,
     309                      const char            * json,
     310                      size_t                  json_len);
     311
     312static void
     313handle_upload2 (struct evhttp_request * req,
     314                struct tr_rpc_server  * server)
     315{
     316  if (req->type != EVHTTP_REQ_POST)
     317    {
     318      send_simple_response (req, 405, NULL);
     319    }
     320  else
     321    {
     322      const char * val;
     323      tr_variant top;
     324      tr_variant * args;
     325      char * json;
     326      int json_len;
     327
     328      tr_variantInitDict (&top, 2);
     329      tr_variantDictAddStr (&top, TR_KEY_method, "torrent-add");
     330      args = tr_variantDictAddDict (&top, TR_KEY_arguments, 3);
     331
     332      if ((val = evhttp_find_header (req->input_headers, "X-Transmission-Add-Paused")))
     333        tr_variantDictAddBool (args, TR_KEY_paused, !tr_strcmp0(val,"true"));
     334
     335      if ((val = evhttp_find_header (req->input_headers, "X-Transmission-Add-Download-Dir")) && *val)
     336        tr_variantDictAddStr (args, TR_KEY_download_dir, val);
     337
     338      if ((val = evhttp_find_header (req->input_headers, "X-Transmission-Add-URL")) && *val)
     339        {
     340          tr_variantDictAddStr (args, TR_KEY_filename, val);
     341        }
     342      else
     343        {
     344          int i;
     345          int n;
     346          bool have_source = false;
     347          tr_ptrArray parts = TR_PTR_ARRAY_INIT;
     348
     349          extract_parts_from_multipart (req->input_headers, req->input_buffer, &parts);
     350          n = tr_ptrArraySize (&parts);
     351          for (i=0; !have_source && i<n; ++i)
     352            {
     353              tr_variant test;
     354              const struct tr_mimepart * p = tr_ptrArrayNth (&parts, i);
     355              if (!tr_variantFromBenc (&test, p->body, p->body_len))
     356                {
     357                  char * b64 = tr_base64_encode (p->body, p->body_len, NULL);
     358                  tr_variantDictAddStr (args, TR_KEY_metainfo, b64);
     359                  have_source = true;
     360                  tr_free (b64);
     361                  tr_variantFree (&test);
     362                }
     363            }
     364          tr_ptrArrayDestruct (&parts, (PtrArrayForeachFunc)tr_mimepart_free);
     365        }
     366
     367      json = tr_variantToStr (&top, TR_VARIANT_FMT_JSON, &json_len);
     368      handle_rpc_from_json (req, server, json, json_len);
     369      tr_free (json);
     370      tr_variantFree (&top);
     371    }
     372}
     373
    302374static const char*
    303375mimetype_guess (const char * path)
     
    542614}
    543615
    544 
    545 static void
    546 handle_rpc (struct evhttp_request * req, struct tr_rpc_server  * server)
    547 {
    548   struct rpc_response_data * data = tr_new0 (struct rpc_response_data, 1);
    549 
     616static void
     617handle_rpc_from_json (struct evhttp_request * req,
     618                      struct tr_rpc_server  * server,
     619                      const char            * json,
     620                      size_t                  json_len)
     621{
     622  struct rpc_response_data * data;
     623
     624  data = tr_new0 (struct rpc_response_data, 1);
    550625  data->req = req;
    551626  data->server = server;
    552627
    553   if (req->type == EVHTTP_REQ_GET)
    554     {
    555       const char * q;
    556       if ((q = strchr (req->uri, '?')))
    557         tr_rpc_request_exec_uri (server->session, q+1, -1, rpc_response_func, data);
    558     }
    559   else if (req->type == EVHTTP_REQ_POST)
    560     {
    561       tr_rpc_request_exec_json (server->session,
    562                                 evbuffer_pullup (req->input_buffer, -1),
    563                                 evbuffer_get_length (req->input_buffer),
    564                                 rpc_response_func, data);
    565     }
    566 
     628  tr_rpc_request_exec_json (server->session, json, json_len, rpc_response_func, data);
     629}
     630
     631static void
     632handle_rpc (struct evhttp_request * req, struct tr_rpc_server  * server)
     633{
     634  const char * q;
     635
     636  if (req->type == EVHTTP_REQ_POST)
     637    {
     638      handle_rpc_from_json (req, server,
     639                            (const char *) evbuffer_pullup (req->input_buffer, -1),
     640                            evbuffer_get_length (req->input_buffer));
     641    }
     642  else if ((req->type == EVHTTP_REQ_GET) && ((q = strchr (req->uri, '?'))))
     643    {
     644      struct rpc_response_data * data = tr_new0 (struct rpc_response_data, 1);
     645      data->req = req;
     646      data->server = server;
     647      tr_rpc_request_exec_uri (server->session, q+1, -1, rpc_response_func, data);
     648    }
     649  else
     650    {
     651      send_simple_response (req, 405, NULL);
     652    }
    567653}
    568654
     
    645731          handle_web_client (req, server);
    646732        }
    647       else if (!strncmp (req->uri + strlen (server->url), "upload", 6))
     733      else if (!strcmp (req->uri + strlen (server->url), "upload"))
    648734        {
    649735          handle_upload (req, server);
     
    670756        }
    671757#endif
     758      else if (!strcmp (req->uri + strlen (server->url), "upload2"))
     759        {
     760          handle_upload2 (req, server);
     761        }
    672762      else if (!strncmp (req->uri + strlen (server->url), "rpc", 3))
    673763        {
  • trunk/web/index.html

    r14003 r14005  
    2222                <script type="text/javascript" src="./javascript/jquery/jquery.transmenu.min.js"></script>
    2323                <script type="text/javascript" src="./javascript/jquery/jquery.contextmenu.min.js"></script>
    24                 <script type="text/javascript" src="./javascript/jquery/jquery.form.min.js"></script>
    2524                <script type="text/javascript" src="./javascript/jquery/json2.min.js"></script>
    2625                <script type="text/javascript" src="./javascript/common.js"></script>
     
    292291                                                <label for="torrent_upload_url">Or enter a URL:</label>
    293292                                                        <input type="url" id="torrent_upload_url"/>
     293                                                <label id='add-dialog-folder-label' for="add-dialog-folder-input">Destination folder:</label>
     294                                                        <input type="text" id="add-dialog-folder-input"/>
    294295                                                        <input type="checkbox" id="torrent_auto_start" />
    295296                                                <label for="torrent_auto_start" id="auto_start_label">Start when added</label>
  • trunk/web/javascript/jquery/Makefile.am

    r13075 r14005  
    55  jquery.min.js \
    66  jquery.contextmenu.min.js \
    7   jquery.form.js \
    8   jquery.form.min.js \
    97  jquery.transmenu.min.js \
    108  json2.min.js
  • trunk/web/javascript/remote.js

    r14003 r14005  
    125125        },
    126126
     127        getFreeSpace: function(dir, callback, context) {
     128                var remote = this;
     129                var o = {
     130                        method: 'free-space',
     131                        arguments: { path: dir }
     132                };
     133                this.sendRequest(o, function(response) {
     134                        var args = response['arguments'];
     135                        callback.call (context, args.path, args['size-bytes']);
     136                });
     137        },
     138
    127139        changeFileCommand: function(torrentId, fileIndices, command) {
    128140                var remote = this,
  • trunk/web/javascript/transmission.js

    r14003 r14005  
    223223                $('#unlimited_download_rate').selectMenuItem();
    224224                $('#unlimited_upload_rate').selectMenuItem();
     225        },
     226
     227        /****
     228        *****
     229        ****/
     230
     231        updateFreeSpaceInAddDialog: function()
     232        {
     233                var formdir = $('input#add-dialog-folder-input').val();
     234                this.remote.getFreeSpace (formdir, this.onFreeSpaceResponse, this);
     235        },
     236
     237        onFreeSpaceResponse: function(dir, bytes)
     238        {
     239                var e, str, formdir;
     240
     241                formdir = $('input#add-dialog-folder-input').val();
     242                if (formdir == dir)
     243                {
     244                        e = $('label#add-dialog-folder-label');
     245                        if (bytes > 0)
     246                                str = '  <i>(' + Transmission.fmt.size(bytes) + ' Free)</i>';
     247                        else
     248                                str = '';
     249                        e.html ('Destination folder' + str + ':');
     250                }
    225251        },
    226252
     
    873899        /*
    874900         * Select a torrent file to upload
    875          * FIXME
    876901         */
    877902        uploadTorrentFile: function(confirmed)
    878903        {
    879                 // Display the upload dialog
    880                 if (! confirmed) {
    881                         $('input#torrent_upload_file').attr('value', '');
    882                         $('input#torrent_upload_url').attr('value', '');
    883                         $('input#torrent_auto_start').attr('checked', this.shouldAddedTorrentsStart());
     904                var formData,
     905                    fileInput   = $('input#torrent_upload_file'),
     906                    folderInput = $('input#add-dialog-folder-input'),
     907                    startInput  = $('input#torrent_auto_start'),
     908                    urlInput    = $('input#torrent_upload_url');
     909
     910                if (!confirmed)
     911                {
     912                        // update the upload dialog's fields
     913                        fileInput.attr('value', '');
     914                        urlInput.attr('value', '');
     915                        startInput.attr('checked', this.shouldAddedTorrentsStart());
     916                        folderInput.attr('value', $("#download-dir").val());
     917                        folderInput.change($.proxy(this.updateFreeSpaceInAddDialog,this));
     918                        this.updateFreeSpaceInAddDialog();
     919
     920                        // show the dialog
    884921                        $('#upload_container').show();
    885                         $('#torrent_upload_url').focus();
    886 
    887                 // Submit the upload form
    888                 } else {
    889                         var args = {};
    890                         var remote = this.remote;
    891                         var paused = !$('#torrent_auto_start').is(':checked');
    892                         if ('' != $('#torrent_upload_url').val()) {
    893                                 remote.addTorrentByUrl($('#torrent_upload_url').val(), { paused: paused });
    894                         } else {
    895                                 args.url = '../upload?paused=' + paused;
    896                                 args.type = 'POST';
    897                                 args.data = { 'X-Transmission-Session-Id' : remote._token };
    898                                 args.dataType = 'xml';
    899                                 args.iframe = true;
    900                                 $('#torrent_upload_form').ajaxSubmit(args);
    901                         }
     922                        urlInput.focus();
     923                }
     924                else
     925                {
     926                        formData = new FormData ();
     927                        jQuery.each(fileInput[0].files, function(i, file) {
     928                                formData.append ('file-'+i, file);
     929                        });
     930                        $.ajax ({
     931                                url: '../upload2',
     932                                data: formData,
     933                                headers : {
     934                                        'X-Transmission-Session-Id':        this.remote._token,
     935                                        'X-Transmission-Add-Paused':        !startInput.is(':checked'),
     936                                        'X-Transmission-Add-Download-Dir':  folderInput.val(),
     937                                        'X-Transmission-Add-URL':           urlInput.val()
     938                                },
     939                                cache: false,
     940                                contentType: false,
     941                                processData: false,
     942                                type: 'POST'
     943                        });
    902944                }
    903945        },
Note: See TracChangeset for help on using the changeset viewer.