Advertisement
Guest User

Untitled

a guest
May 17th, 2018
1,497
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /*
  2.  MyJDownloader API Javascript Client Library
  3.  - - -
  4.  @version 1.0.1
  5.  @author AppWork GmbH
  6.  
  7.  Dependencies:
  8.  - jQuery
  9.  - CryptoJS (core, hmac-sha256, aes)
  10.  - requirejs
  11.  
  12.  (c) 2013-2015 by AppWork GmbH. All rights reserved.
  13.  */
  14.  
  15. define("coreCrypto", [],function () {
  16.     /**
  17.      * Extend CryptoJS with function to split WordArray
  18.      */
  19.     CryptoJS.lib.WordArray.firstHalf = function () {
  20.         if (!this._firstHalf) {
  21.             this._firstHalf = new CryptoJS.lib.WordArray.init(this.words.slice(0, this.words.length / 2));
  22.         }
  23.         return this._firstHalf;
  24.     };
  25.     CryptoJS.lib.WordArray.secondHalf = function () {
  26.         if (!this._secondHalf) this._secondHalf = new CryptoJS.lib.WordArray.init(this.words
  27.             .slice(this.words.length / 2, this.words.length));
  28.         return this._secondHalf;
  29.     };
  30.     return CryptoJS;
  31. });
  32. define("coreCryptoUtils",["coreCrypto"], function(CoreCrypto) {
  33.     /**
  34.      * Utility Object for cryptography functions
  35.      */
  36.     var CryptoUtils = {
  37.         /* hash a password with the given pass and domain as salt */
  38.         hashPassword: function(email, pass, domain) {
  39.             return CoreCrypto.SHA256(CoreCrypto.enc.Utf8.parse(email.toLowerCase() + pass + domain.toLowerCase()));
  40.         },
  41.         /* convert pass to secret hashes and delete it afterwards */
  42.         processPassword: function(options) {
  43.             if (!options.email || !options.pass) {
  44.                 throw "processPassword requires set options.email and options.pass";
  45.             }
  46.  
  47.             options.loginSecret = this.hashPassword(options.email, options.pass, "server");
  48.             options.deviceSecret = this.hashPassword(options.email, options.pass, "device");
  49.             delete options.pass;
  50.         },
  51.         /* initialise the tokens after a successful handshake or update tokens after a reconnect */
  52.         initialiseConnection: function(options, sessiontoken, regaintoken) {
  53.             if (!options.loginSecret && !options.serverEncryptionToken) {
  54.                 throw "either loginSecret or serverEncryptionToken must be set, probably should call processPassword(options) first";
  55.             } else if(!options.deviceSecret){
  56.                 throw "deviceSecret not set";
  57.             } else if(!sessiontoken || !regaintoken){
  58.                 throw "sessiontoken and regaintoken are required to initialise connection";
  59.             }
  60.             options.sessiontoken = sessiontoken;
  61.             options.regaintoken = regaintoken;
  62.  
  63.             var ses = CoreCrypto.enc.Hex.parse(options.sessiontoken);
  64.             if (options.loginSecret) {
  65.                 // calculate initial secret
  66.                 var tot = options.loginSecret.concat(ses);
  67.                 // keep old token to decrypt requests that got sent before a reconnect
  68.                 options.serverEncryptionTokenOld = options.serverEncryptionToken;
  69.                 options.serverEncryptionToken = CoreCrypto.SHA256(tot);
  70.                 delete options.loginSecret;
  71.             } else {
  72.                 // calculate new secret
  73.                 options.serverEncryptionTokenOld = options.serverEncryptionToken;
  74.                 var tot = options.serverEncryptionToken.concat(ses);
  75.                 options.serverEncryptionToken = CoreCrypto.SHA256(tot);
  76.             }
  77.  
  78.             var deviceSecret = options.deviceSecret.clone();
  79.             var totDev = deviceSecret.concat(ses);
  80.             // keep old device encryption token for decrypting old
  81.             // requests after reconnect
  82.             options.deviceEncryptionTokenOld = options.deviceEncryptionToken;
  83.             // set new device encryption token
  84.             options.deviceEncryptionToken = CoreCrypto.SHA256(totDev);
  85.         },
  86.         /*
  87.          * @param secret: CoreCrypto.lib.WordArray used as secret
  88.          * @param plain: the JSON object to encrypt
  89.          */
  90.         encryptJSON: function (secret, plain, rsaPublicKey) {
  91.             if (!secret) {
  92.                 var result = {"data": plain};
  93.                 return result;
  94.             }
  95.  
  96.             var iv;
  97.             var key;
  98.             if (rsaPublicKey) {
  99.                 var keySize = 32; // AES256
  100.                 var cryptoObj = window.crypto || window.msCrypto;
  101.                 if (Uint8Array && cryptoObj && cryptoObj.getRandomValues) {
  102.                     try {
  103.                         iv = new Uint8Array(16);
  104.                         key = new Uint8Array(keySize);
  105.                         cryptoObj.getRandomValues(iv);
  106.                         cryptoObj.getRandomValues(key);
  107.  
  108.                         // No WordArray constructor that takes Uint8Array, thus Hex as intermediate (Uint8Array -> Hex String -> WordArray)
  109.                         var ivHex = this.ua2hex(iv);
  110.                         var keyHex = this.ua2hex(key);
  111.                         iv = CryptoJS.enc.Hex.parse(ivHex);
  112.                         key = CryptoJS.enc.Hex.parse(keyHex);
  113.                     } catch (exception) {
  114.                         // Browser failed to do his job
  115.                     }
  116.                 }
  117.                 if (!(iv && key)) {
  118.                     // we still need keys
  119.                     iv = CoreCrypto.lib.WordArray.random(16);
  120.                     key = CoreCrypto.lib.WordArray.random(keySize);
  121.                 }
  122.             } else {
  123.                 iv = secret.firstHalf();
  124.                 key = secret.secondHalf();
  125.             }
  126.  
  127.             var aesEncrypted = CoreCrypto.AES.encrypt(JSON.stringify(plain), key, {
  128.                 mode: CoreCrypto.mode.CBC,
  129.                 iv: iv
  130.             });
  131.  
  132.             var result = {};
  133.  
  134.             if (rsaPublicKey) {
  135.                 var encrypt = new JSEncrypt();
  136.                 encrypt.setPublicKey(rsaPublicKey);
  137.                 var stringHexIv = CryptoJS.enc.Hex.stringify(iv);
  138.                 var stringHexKey = CryptoJS.enc.Hex.stringify(key);
  139.                 var rsaEncrypted = encrypt.encrypt(stringHexIv + stringHexKey);
  140.                 result["rsa"] = rsaEncrypted;
  141.                 result["contentType"] = "application/rsajson; charset=utf-8";
  142.                 result["iv"] = iv;
  143.                 result["key"] = key;
  144.                 // Delimiter between rsa key and aes encrypted content is |
  145.                 result["data"] = rsaEncrypted + "|" + aesEncrypted.toString();
  146.             } else {
  147.                 result["contentType"] = "application/aesjson; charset=utf-8";
  148.                 result["iv"] = secret.firstHalf();
  149.                 result["key"] = secret.secondHalf();
  150.                 result["data"] = aesEncrypted.toString();
  151.             }
  152.             return result;
  153.         },
  154.         ua2hex: function (ua) {
  155.             // Converts Uint8Array to Hex String
  156.             var h = '';
  157.             for (var i = 0; i < ua.length; i++) {
  158.                 var temp = ua[i].toString(16);
  159.                 if (temp.length < 2) {
  160.                     temp = "0" + temp;
  161.                 }
  162.                 h += temp;
  163.             }
  164.             return h;
  165.         },
  166.         decryptJSON: function (iv, key, encrypted) {
  167.             try {
  168.                 var plain = null;
  169.                 var plain_raw = CoreCrypto.AES.decrypt(encrypted, key, {
  170.                     mode: CoreCrypto.mode.CBC,
  171.                     iv: iv
  172.                 }).toString(CoreCrypto.enc.Utf8);
  173.                 if (plain_raw && typeof plain_raw === "string") {
  174.                     plain = JSON.parse(plain_raw);
  175.                 }
  176.                 return plain;
  177.             } catch (e) {
  178.                 return encrypted;
  179.             }
  180.         },
  181.  
  182.         decryptRsaJSON: function (rsaIv, rsaKey, encrypted) {
  183.             var jsEncrypt = new JSEncrypt();
  184.             jsEncrypt.setPrivateKey(rsaIv + rsaKey);
  185.             var rsaDecrypted = jsEncrypt.decrypt(encrypted);
  186.             try {
  187.                 return this.decryptJSON(rsaDecrypted.token.firstHalf(), rsaDecrypted.token.secondHalf(), rsaDecrypted.data);
  188.             } catch (e) {
  189.                 return encrypted;
  190.             }
  191.         }
  192.  
  193.     };
  194.  
  195.  
  196.     return CryptoUtils;
  197. });
  198. define("coreRequest", ["coreCrypto", "coreCryptoUtils"], function (CoreCrypto, CryptoUtils) {
  199.  
  200.     /**
  201.      * Static variables and functions
  202.      */
  203.     var requestCount = 0;
  204.  
  205.     var processOptions = function (options) {
  206.         // SERVER_CALL
  207.         // TODO: options.jdAction should be absolute e.g. /my/feedback vs. /feedback
  208.         // if URL params are included build querystring with
  209.         // sessiontoken and signature
  210.         if (options.jdParams) {
  211.             if (options.serverEncryptionToken !== undefined) {
  212.                 options = addConverters(options, options.serverEncryptionToken.firstHalf(), options.serverEncryptionToken.secondHalf());
  213.             }
  214.             if (options.type === "GET") {
  215.                 var queryString = options.jdAction + "?sessiontoken=" + options.sessiontoken + "&rid=" + options.rid + "&" + $.param(options.jdParams);
  216.             } else {
  217.                 if (options.serverEncryptionToken) {
  218.                     options.contentType = "application/json; charset=utf-8";
  219.                     var queryString;
  220.                     var unencrypted;
  221.                     if (options.jdAction === "requestterminationemail") {
  222.                         queryString = "/my/" + options.jdAction + "?sessiontoken=" + options.sessiontoken + "&" + $
  223.                             .param(options.jdParams) + "&rid=" + options.rid;
  224.                     } else {
  225.                         queryString = options.jdAction + "?sessiontoken=" + options.sessiontoken + "&rid=" + options.rid;
  226.                         unencrypted = {
  227.                             "apiVer": 1,
  228.                             "params": [],
  229.                             "url": queryString,
  230.                             "rid": options.rid
  231.                         };
  232.  
  233.                         if (JSON.stringify(options.jdParams) !== "{}") {
  234.                             // Do net send empty param
  235.                             unencrypted.params = [options.jdParams];
  236.                         }
  237.  
  238.                         var encryptedJSON = CryptoUtils.encryptJSON(options.serverEncryptionToken, unencrypted);
  239.                         // server expects application/json 26.06.2017 TODO add support for application/aesjson server side
  240.                         encryptedJSON.contentType = "application/json; charset=UTF-8";
  241.                         options.data = encryptedJSON.data;
  242.                         options.contentType = encryptedJSON.contentType;
  243.                         options = addConverters(options, encryptedJSON.iv, encryptedJSON.key);
  244.  
  245.                         queryString += "&signature=" + CoreCrypto
  246.                             .HmacSHA256(CoreCrypto.enc.Utf8.parse(queryString), options.serverEncryptionToken)
  247.                             .toString(options.TRANSFER_ENCODING);
  248.                     }
  249.                     options.url = options.API_ROOT + queryString;
  250.                 } else {
  251.                     logger.error("[MYJD] [JSAPI] [REQUEST] [FAILED] Server encryption token missing. Action: " + JSON.stringify(options ? options.jdAction : "NO_ACTION"));
  252.                 }
  253.             }
  254.  
  255.         } else {
  256.             if (options.deviceEncryptionToken) {
  257.                 var encryptedJSON = CryptoUtils.encryptJSON(options.deviceEncryptionToken, options.jdData, options.rsaPublicKey);
  258.                 options.contentType = encryptedJSON.contentType;
  259.                 options.data = encryptedJSON.data;
  260.                 options = addConverters(options, encryptedJSON.iv, encryptedJSON.key);
  261.             options.url = options.API_ROOT + "/t_" + options.sessiontoken + "_" + options.deviceId + options.jdAction;
  262.             } else {
  263.                 logger.error("[MYJD] [JSAPI] [REQUEST] [FAILED] " + JSON.stringify((options ? options.type : "NO_OPTIONS")) + " Error: Device encryption token missing!");
  264.             }
  265.         }
  266.     };
  267.  
  268.     var addConverters = function (options, aesIv, aesKey) {
  269.         options.converters = {
  270.             "* aesjson-server": CryptoUtils.decryptJSON.bind(this, aesIv, aesKey),
  271.             "* aesjson": CryptoUtils.decryptJSON.bind(this, aesIv, aesKey)
  272.         };
  273.         return options;
  274.     };
  275.  
  276.     /**
  277.      * Constructor for new JDAPIRequest Object
  278.      */
  279.     var JDAPIRequest = function (options) {
  280.  
  281.         // options.jdParams = options.jdParams || {};
  282.         var self = this;
  283.         /*
  284.          * processes the options object e.g. encrypting everything
  285.          * with the required secrets
  286.          */
  287.         processOptions(options);
  288.         this.options = options;
  289.     };
  290.     $.extend(JDAPIRequest.prototype, {
  291.         send: function () {
  292.             var filter = $.Deferred();
  293.             var self = this;
  294.             var options = this.options;
  295.             var requestStart;
  296.             options.beforeSend = function () {
  297.                 requestStart = Date.now();
  298.             };
  299.             var apiRequest = $.ajax(this.options);
  300.  
  301.             apiRequest.done(function (response) {
  302.                 logger.log("[MYJD] [JSAPI] [REQUEST] " + JSON.stringify(options ? options.type : "NO_TYPE") + " " + JSON.stringify(options ? options.url : "NO_URL") + "\nOPTIONS:\n" + JSON.stringify(options ? options : "NO_OPTIONS") + "\n\nRESPONSE:\n" + JSON.stringify(response));
  303.                 response.requestDuration = Date.now() - requestStart;
  304.                 filter.resolve(response);
  305.             });
  306.             apiRequest.fail(function (error) {
  307.                 error.requestDuration = Date.now() - requestStart;
  308.                 try {
  309.                     if (error.responseText !== undefined) {
  310.                         filter.reject(JSON.parse(error.responseText));
  311.                     } else {
  312.                         if (error.statusText !== undefined) {
  313.                             if (error.statusText === "error") {
  314.                                 filter.reject({
  315.                                     type: "CONNECTION_REFUSED"
  316.                                 });
  317.                             } else if (error.statusText === "timeout") {
  318.                                 filter.reject({
  319.                                     type: "TIMEOUT"
  320.                                 });
  321.                             } else {
  322.                                 filter.reject({
  323.                                     type: "UNKNOWN_ERROR"
  324.                                 });
  325.                             }
  326.                         } else {
  327.                             filter.reject({
  328.                                 type: "UNKNOWN_ERROR"
  329.                             });
  330.                         }
  331.                     }
  332.                 } catch (e) {
  333.                     filter.reject({
  334.                         type: "UNKNOWN_ERROR"
  335.                     });
  336.                 }
  337.                 logger.error("[MYJD] [JSAPI] [REQUEST] [FAILED] " + JSON.stringify(options ? options.type : "NO_TYPE") + " " + JSON.stringify(options ? options.url : "NO_URL") + "\nOPTIONS:\n" + JSON.stringify(options ? options : "NO_OPTIONS") + "\n\nRESPONSE:\n" + JSON.stringify(error));
  338.             });
  339.             return filter;
  340.         }
  341.     });
  342.     return JDAPIRequest;
  343. });
  344. define("coreRequestHandler", ["coreCrypto", "coreCryptoUtils"], function (CoreCrypto, CryptoUtils) {
  345.     var LOCAL_STORAGE_RECONNECT_LOCK_KEY = "jdapi/src/core/coreRequestHandler.js";
  346.     var JDAPICoreRequestHandler = function (appKey, transferEncoding, LOCAL_STORAGE_KEY, apiRoot) {
  347.         this.appKey = appKey;
  348.         this.transferEncoding = transferEncoding;
  349.         this.LOCAL_STORAGE_KEY = LOCAL_STORAGE_KEY;
  350.         this.apiRoot = apiRoot;
  351.     };
  352.     $.extend(JDAPICoreRequestHandler.prototype, {
  353.         connect: function (options) {
  354.             var handshake = $.Deferred();
  355.             //PERFORM LOGIN
  356.             if (options && options.email && options.pass) {
  357.                 // Generate login secret
  358.                 CryptoUtils.processPassword(options);
  359.  
  360.                 // craft query string
  361.                 var params = {};
  362.                 var action;
  363.  
  364.                 params.email = options.email;
  365.                 action = "/my/connect";
  366.                 params.appkey = this.appKey;
  367.                 params.rid = 0;
  368.  
  369.                 var queryString = action + "?" + $.param(params);
  370.                 queryString += "&signature=" + CoreCrypto.HmacSHA256(CoreCrypto.enc.Utf8.parse(queryString), options.loginSecret).toString(this.transferEncoding);
  371.  
  372.                 // issue authentication request
  373.                 var auth = $.ajax({
  374.                     url: this.apiRoot + queryString,
  375.                     type: "POST",
  376.                     async: true,
  377.                     dataType: "aesjson-server",
  378.                     converters: {
  379.                         "* aesjson-server": CryptoUtils.decryptJSON.bind(this, options.loginSecret.firstHalf(),options.loginSecret.secondHalf())
  380.                     }
  381.                 });
  382.                 // if authentication fails, reject the handshake
  383.                 auth.fail(handshake.reject);
  384.                 // if the handshake gets rejected beforehand (e.g. because of a disconnect() call), abort the authentication request
  385.                 handshake.fail(auth.abort);
  386.                 // if authentication is successful, initialize connection
  387.                 auth.done((function (data) {
  388.                     if (data.rid !== params.rid) return handshake.reject(undefined, "replay attack");
  389.                     CryptoUtils.initialiseConnection(options, data.sessiontoken, data.regaintoken);
  390.                     dataStore.setItem(this.LOCAL_STORAGE_KEY, JSON.stringify(options)).then(function () {
  391.                         handshake.resolve(options);
  392.                     });
  393.                 }).bind(this));
  394.  
  395.                 // ATTEMPT RESUME FROM LOCALSTORAGE
  396.             } else {
  397.                 var localStorageKey = this.LOCAL_STORAGE_KEY;
  398.                 dataStore.getItem(localStorageKey).then(function (result) {
  399.                     var restoredOptions;
  400.                     try {
  401.                         restoredOptions = JSON.parse(result[localStorageKey]);
  402.                         if (!restoredOptions) {
  403.                             handshake.reject();
  404.                         } else {
  405.                             options = $.extend({}, restoredOptions);
  406.                             //Convert JSON back to CoreCrypto.lib.WordArray instance
  407.                             if (restoredOptions.serverEncryptionToken) {
  408.                                 options.serverEncryptionToken = CoreCrypto.lib.WordArray.random(restoredOptions.serverEncryptionToken.sigBytes);
  409.                                 options.serverEncryptionToken.words = restoredOptions.serverEncryptionToken.words;
  410.                             }
  411.                             if (restoredOptions.deviceEncryptionToken) {
  412.                                 options.deviceEncryptionToken = CoreCrypto.lib.WordArray.random(restoredOptions.deviceEncryptionToken.sigBytes);
  413.                                 options.deviceEncryptionToken.words = restoredOptions.deviceEncryptionToken.words;
  414.                             }
  415.                             if (restoredOptions.deviceSecret) {
  416.                                 options.deviceSecret = CoreCrypto.lib.WordArray.random(restoredOptions.deviceSecret.sigBytes);
  417.                                 options.deviceSecret.words = restoredOptions.deviceSecret.words;
  418.                             }
  419.                             handshake.resolve(options);
  420.                         }
  421.                     } catch (e) {
  422.                         handshake.reject();
  423.                     }
  424.                 });
  425.             }
  426.             return handshake;
  427.         },
  428.         reconnect: function (options, rid) {
  429.             var handshake = $.Deferred();
  430.             var self = this;
  431.             /*
  432.              * ReconnectLock localStorage functionality to handle multiple browser tabs
  433.              */
  434.             // If lock can be acquired reconnect yourself, else wait
  435.             // for other tab to finish reconnect
  436.             var reconnectLock;
  437.             dataStore.getItem(LOCAL_STORAGE_RECONNECT_LOCK_KEY).then(function (result) {
  438.                 try {
  439.                     reconnectLock = JSON.parse(result[LOCAL_STORAGE_RECONNECT_LOCK_KEY]);
  440.                 } catch (e) {
  441.                 }
  442.                 //lock exists and is not timed out
  443.                 if (reconnectLock && (new Date().getTime() - reconnectLock.time) < 5000) {
  444.                     // wait for reconnect
  445.                     // listen to local storage
  446.                     window.addEventListener('storage', function (event) {
  447.                         if (event.key === this.LOCAL_STORAGE_KEY) {
  448.                             if (!e.newValue) {
  449.                                 handshake.reject();
  450.                             } else {
  451.                                 handshake.resolve();
  452.                             }
  453.                         }
  454.                     }, false);
  455.                 } else {
  456.                     //create lock
  457.                     dataStore.setItem(LOCAL_STORAGE_RECONNECT_LOCK_KEY, JSON.stringify({
  458.                         time: new Date().getTime()
  459.                     })).then(function () {
  460.                         // do the reconnect in this tab
  461.                         // craft query string
  462.                         var params = {};
  463.                         var action;
  464.  
  465.                         action = "/my/reconnect";
  466.                         params.appkey = self.appKey;
  467.                         params.sessiontoken = options.sessiontoken;
  468.                         params.regainToken = options.regaintoken;
  469.                         params.rid = rid;
  470.  
  471.                         var queryString = action + "?" + $.param(params);
  472.                         queryString += "&signature=" + CoreCrypto.HmacSHA256(CoreCrypto.enc.Utf8.parse(queryString), options.serverEncryptionToken).toString(this.transferEncoding);
  473.  
  474.                         //reconnect yourself
  475.                         var reauth = $.ajax({
  476.                             url: options.API_ROOT + queryString,
  477.                             type: "POST",
  478.                             async: true,
  479.                             dataType: "aesjson-server",
  480.                             converters: {
  481.                                 "* aesjson-server": CryptoUtils.decryptJSON.bind(this, options.serverEncryptionToken.firstHalf(), options.serverEncryptionToken.secondHalf())
  482.                             }
  483.                         });
  484.                         // if authentication fails, reject the handshake
  485.                         reauth.fail(handshake.reject.bind(handshake));
  486.                         // if the handshake gets rejected beforehand (e.g. because of a disconnect() call), abort the authentication request
  487.                         handshake.fail(reauth.abort.bind(reauth));
  488.                         // if authentication is successful, initialize connection
  489.                         reauth.done((function (data) {
  490.                             CryptoUtils.initialiseConnection(options, data.sessiontoken, data.regaintoken);
  491.                             dataStore.removeItem(LOCAL_STORAGE_RECONNECT_LOCK_KEY);
  492.                             dataStore.setItem(this.LOCAL_STORAGE_KEY, JSON.stringify(options)).then(handshake.resolve);
  493.                         }).bind(self));
  494.                     });
  495.                 }
  496.             });
  497.  
  498.             return handshake;
  499.         },
  500.         disconnect: function (options) {
  501.             var params =
  502.             {
  503.                 rid: options.rid,
  504.                 sessiontoken: options.sessiontoken
  505.             };
  506.             var queryString = "/my/disconnect?" + $.param(params);
  507.             queryString += "&signature=" + CoreCrypto.HmacSHA256(CoreCrypto.enc.Utf8.parse(queryString), options.serverEncryptionToken).toString(this.transferEncoding);
  508.  
  509.             var disconnect = $.ajax({
  510.                 url: this.apiRoot + queryString,
  511.                 async: true,
  512.                 type: "POST",
  513.                 dataType: "aesjson-server",
  514.                 converters: {
  515.                     "* aesjson-server": CryptoUtils.decryptJSON.bind(this, options.serverEncryptionToken.firstHalf(),options.serverEncryptionToken.secondHalf())
  516.                 }
  517.             }).done((function (data) {
  518.                 if (data.rid !== options.rid) return this.connection.reject(undefined, "replay attack");
  519.             }).bind(this));
  520.  
  521.             return disconnect;
  522.         }
  523.     });
  524.  
  525.     return JDAPICoreRequestHandler;
  526. });
  527. define("coreCore", ["coreCrypto", "coreCryptoUtils", "coreRequest", "coreRequestHandler"], function (CoreCrypto, CryptoUtils, JDAPIRequest, JDAPICoreRequestHandler) {
  528.     "use strict";
  529.  
  530.     var TRANSFER_ENCODING = CoreCrypto.enc.Hex;
  531.     var API_ROOT;
  532.     if (window && window.location && window.location.protocol) {
  533.         if (window.location.protocol === "https:" || window.location.protocol === "http:") {
  534.             API_ROOT = window.location.protocol + "//api.jdownloader.org";
  535.         } else if (window.location.protocol === "chrome-extension:" || window.location.protocol === "moz-extension:") {
  536.             // default to https
  537.             API_ROOT = "https://api.jdownloader.org"
  538.         } else {
  539.             throw new Error("MyJDownloader JS API: Unknown host protocol " + window.location.protocol);
  540.         }
  541.     } else {
  542.         throw new Error("MyJDownloader JS API: Failed to initialize API Root");
  543.     }
  544.  
  545.     var LOCAL_STORAGE_KEY = "jdapi/src/core/core.js";
  546.  
  547.     // API States
  548.     var CONNECTED_STATE = 0;
  549.     var PENDING_STATE = 1;
  550.     var RECONNECT_STATE = 2;
  551.     var DISCONNECTED_STATE = 3;
  552.  
  553.     /**
  554.      * GLOBAL STATE VARIABLES
  555.      * TODO: Not compatible with multiple JDAPICore instances as it is global singleton
  556.      */
  557.     var APIState = (function () {
  558.         // Private variables
  559.         var apiState = PENDING_STATE;
  560.         var onChangeCallbacks = new Array();
  561.         // Public interface
  562.         return {
  563.             setAPIState: function (STATE) {
  564.                 apiState = STATE;
  565.                 onChangeCallbacks.forEach(function (callback) {
  566.                     try {
  567.                         callback(apiState);
  568.                     } catch (e) {
  569.                         // console.error(e);
  570.                     }
  571.                 });
  572.             },
  573.             getAPIState: function () {
  574.                 return apiState;
  575.             },
  576.             addAPIStateChangeListener: function (callback) {
  577.                 if (callback && typeof callback === "function") {
  578.                     if (onChangeCallbacks.indexOf(callback) === -1) {
  579.                         onChangeCallbacks.push(callback);
  580.                     }
  581.                 } else {
  582.                     throw new TypeError("APIStateChangeListener must be of type function");
  583.                 }
  584.             }
  585.         };
  586.     })();
  587.     //Default App Key
  588.     var APP_KEY = "com.appwork.jdownloader"; //"org.jdownloader.my_web_0.1.657"
  589.  
  590.     // Global Request Queue
  591.     var jdapiRequestQueue = new Array();
  592.     var coreRequestHandler = new JDAPICoreRequestHandler(APP_KEY, TRANSFER_ENCODING, LOCAL_STORAGE_KEY, API_ROOT);
  593.  
  594.     // RID
  595.     var lastrid = 0;
  596.     var getRID = function () {
  597.         var rid = (new Date()).getTime();
  598.         if (lastrid === rid) {
  599.             rid++;
  600.         }
  601.         lastrid = rid;
  602.         return rid;
  603.     };
  604.  
  605.     /**
  606.      * Constructor for Main JDAPICore Object, that gets exported
  607.      */
  608.     var JDAPICore = function (options, onConnected, APP_KEY_PARAM) {
  609.         if (APP_KEY_PARAM) APP_KEY = APP_KEY_PARAM;
  610.         // If options object available, initialize with connect(options)
  611.         APIState.setAPIState(PENDING_STATE);
  612.         options = options || {};
  613.         this.options = options;
  614.         this.TRANSFER_ENCODING = TRANSFER_ENCODING;
  615.         if (onConnected && $.isFunction(onConnected.promise)) {
  616.             this.connect(this.options).then(function () {
  617.                     //defer to next event loop event
  618.                     setTimeout(function () {
  619.                         onConnected.resolve();
  620.                     }, 0);
  621.                 },
  622.                 function () {
  623.                     //defer to next event loop event
  624.                     setTimeout(function () {
  625.                         onConnected.reject();
  626.                     }, 0);
  627.                 });
  628.         } else if (onConnected && $.isFunction(onConnected)) {
  629.             this.connect(this.options).done(function () {
  630.                 //defer to next event loop event
  631.                 setTimeout(function () {
  632.                     onConnected();
  633.                 }, 0);
  634.             });
  635.         } else {
  636.             this.connect(this.options);
  637.         }
  638.         //initialize local storage listener to refresh options if reconnect happened in other browser tab
  639.         window.addEventListener('storage', function (e) {
  640.             if (e.key === LOCAL_STORAGE_KEY) {
  641.                 if (!e.newValue) {
  642.                     this.disconnect();
  643.                 } else {
  644.                     this.connect();
  645.                 }
  646.             }
  647.         }.bind(this), false);
  648.     };
  649.     $.extend(JDAPICore.prototype, {
  650.         /**
  651.          * Initialize Session either with given @email and @pass or with stored session
  652.          */
  653.         requestCount: 0,
  654.         connect: function (options) {
  655.             APIState.setAPIState(PENDING_STATE);
  656.             var filter = $.Deferred();
  657.             var connectCall = coreRequestHandler.connect(options);
  658.             connectCall.done(function (options) {
  659.                 options.API_ROOT = API_ROOT;
  660.                 this.options = options;
  661.                 APIState.setAPIState(CONNECTED_STATE);
  662.                 this._handleEnqueuedRequestsAndSetConnected();
  663.                 filter.resolve(options);
  664.             }.bind(this));
  665.             connectCall.fail(function (err) {
  666.                 APIState.setAPIState(DISCONNECTED_STATE);
  667.                 filter.reject(err);
  668.             }.bind(this));
  669.             return filter;
  670.         },
  671.         getRID: getRID,
  672.         reconnect: function () {
  673.             var reconnectDef = $.Deferred();
  674.             // IF RECONNECT NOT ALREADY IN PROGRESS
  675.             if (APIState.getAPIState() == CONNECTED_STATE || APIState.getAPIState() == PENDING_STATE) {
  676.                 // START RECONNECTING
  677.                 APIState.setAPIState(RECONNECT_STATE);
  678.                 var reconnect = coreRequestHandler.reconnect(this.options, getRID());
  679.                 reconnect.done(function () {
  680.                     this._handleEnqueuedRequestsAndSetConnected();
  681.                 }.bind(this));
  682.                 reconnect.fail(function () {
  683.                     // FAIL FINALLY, TRIGGER LOGOUT ACTIONS ETC
  684.                     this.disconnect();
  685.                 }.bind(this));
  686.                 reconnect.then(reconnectDef.resolve, reconnectDef.reject);
  687.             } else {
  688.                 //RECONNECT ALREADY HAPPENING -> RESOLVE
  689.                 //TODO: Not dependent on reconnect outcome
  690.                 reconnectDef.resolve();
  691.             }
  692.             return reconnectDef;
  693.         },
  694.         disconnect: function () {
  695.             //If already disconnected
  696.             if (APIState.getAPIState() === DISCONNECTED_STATE) {
  697.                 var ret = $.Deferred();
  698.                 ret.resolve();
  699.                 return ret;
  700.             }
  701.             //Else make disconnect call
  702.             var disconnectCall = coreRequestHandler.disconnect({
  703.                 serverEncryptionToken: this.options.serverEncryptionToken,
  704.                 sessiontoken: this.options.sessiontoken,
  705.                 rid: getRID()
  706.             });
  707.             disconnectCall.always(function () {
  708.                 this.options = {};
  709.                 dataStore.removeItem(LOCAL_STORAGE_KEY);
  710.                 APIState.setAPIState(DISCONNECTED_STATE);
  711.             }.bind(this));
  712.             return disconnectCall;
  713.         },
  714.         /* send request to list all available devices */
  715.         serverCall: function (action, params, urlParams, type) {
  716.             // Create request object
  717.             params = params || {};
  718.             var reqOptions = {
  719.                 jdAction: action,
  720.                 jdParams: params,
  721.                 urlParams: urlParams,
  722.                 type: type || "POST",
  723.                 dataType: "aesjson-server",
  724.                 serverEncryptionToken: this.options.serverEncryptionToken,
  725.                 TRANSFER_ENCODING: TRANSFER_ENCODING,
  726.                 API_ROOT: API_ROOT,
  727.                 sessiontoken: this.options.sessiontoken,
  728.                 rid: getRID()
  729.             };
  730.             // Send and return the deferred
  731.             return this._call(reqOptions);
  732.         },
  733.         /* wraps jd api call in reconnect handling jQuery ajax call */
  734.         deviceCall: function (deviceId, action, postData, rsaPublicKey, timeout) {
  735.             postData = postData || [];
  736.             var rID = getRID();
  737.             var reqOptions = {
  738.                 jdAction: action,
  739.                 jdData: {
  740.                     url: action,
  741.                     params: postData,
  742.                     apiVer: 1,
  743.                     rid:rID
  744.                 },
  745.                 deviceId: deviceId,
  746.                 type: "POST",
  747.                 dataType: "aesjson-server",
  748.                 deviceEncryptionToken: this.options.deviceEncryptionToken,
  749.                 //rsaPublicKey: rsaPublicKey,
  750.                 TRANSFER_ENCODING: TRANSFER_ENCODING,
  751.                 API_ROOT: API_ROOT,
  752.                 sessiontoken: this.options.sessiontoken,
  753.                 rid:rID
  754.             };
  755.             if (timeout) {
  756.                 reqOptions.timeout = timeout;
  757.             }
  758.             return this._call(reqOptions);
  759.         },
  760.         /* wraps jd local call in reconnect handling jQuery ajax call */
  761.         localDeviceCall: function (localURL, deviceId, action, postData, rsaPublicKey, timeout) {
  762.             postData = postData || [];
  763.             var rID = getRID();
  764.             var reqOptions = {
  765.                 jdAction: action,
  766.                 jdData: {
  767.                     url: action,
  768.                     params: postData,
  769.                     apiVer: 1,
  770.                     rid:rID
  771.                 },
  772.                 deviceId: deviceId,
  773.                 type: "POST",
  774.                 dataType: "aesjson-server",
  775.                 deviceEncryptionToken: this.options.deviceEncryptionToken,
  776.                 //rsaPublicKey: rsaPublicKey,
  777.                 TRANSFER_ENCODING: TRANSFER_ENCODING,
  778.                 API_ROOT: localURL,
  779.                 sessiontoken: this.options.sessiontoken,
  780.                 rid: rID
  781.             };
  782.             if (timeout) {
  783.                 reqOptions.timeout = timeout;
  784.             }
  785.             return this._call(reqOptions);
  786.         },
  787.         getSessionToken: function () {
  788.             return this.options.sessiontoken;
  789.         },
  790.         getSessionInfo: function () {
  791.             return this.options;
  792.         },
  793.         addAPIStateChangeListener: function (callback) {
  794.             APIState.addAPIStateChangeListener(callback);
  795.         },
  796.         getAPIState: function () {
  797.             return APIState.getAPIState();
  798.         },
  799.         getAPIStatePlain: function () {
  800.             switch (APIState.getAPIState()) {
  801.                 case 0:
  802.                     return "CONNECTED_STATE";
  803.                     break;
  804.                 case 1:
  805.                     return "PENDING_STATE";
  806.                     break;
  807.                 case 2:
  808.                     return "RECONNECT_STATE";
  809.                     break;
  810.                 case 3:
  811.                     return "DISCONNECTED_STATE";
  812.                     break;
  813.                 default:
  814.                     return "INVALID";
  815.             }
  816.         },
  817.         getCurrentUser: function () {
  818.             return {
  819.                 loggedIn: (this.getAPIState() !== 3),
  820.                 name: this.options.email
  821.             };
  822.         },
  823.         API_ROOT: API_ROOT,
  824.         APP_KEY: APP_KEY,
  825.         _rebuildRequestOptions: function (reqOptions) {
  826.             if (reqOptions.deviceEncryptionToken) {
  827.                 reqOptions.deviceEncryptionToken = this.options.deviceEncryptionToken;
  828.                 reqOptions.deviceEncryptionTokenOld = this.options.deviceEncryptionTokenOld;
  829.             }
  830.  
  831.             if (reqOptions.serverEncryptionToken) {
  832.                 reqOptions.serverEncryptionToken = this.options.serverEncryptionToken;
  833.             }
  834.  
  835.             reqOptions.sessiontoken = this.options.sessiontoken;
  836.  
  837.             reqOptions.rid = getRID();
  838.             return reqOptions;
  839.         },
  840.         _handleEnqueuedRequestsAndSetConnected: function () {
  841.             APIState.setAPIState(CONNECTED_STATE);
  842.             while (jdapiRequestQueue.length > 0) {
  843.                 var queuedRequest = jdapiRequestQueue.shift();
  844.                 var requestOptions = this._rebuildRequestOptions(queuedRequest.options);
  845.                 // fire async, TODO: Could fail ?!?!
  846.                 setTimeout(function () {
  847.                     var call = this._call(requestOptions, 1);
  848.                     call.done(queuedRequest.deferred.resolve);
  849.                     call.fail(queuedRequest.deferred.reject);
  850.                 }.bind(this), 10);
  851.             }
  852.         },
  853.         // function that either makes or enqueues the request dependent on API state
  854.         _call: function (reqOptions, count) {
  855.             count = count || 0;
  856.             var def = $.Deferred();
  857.             // IF CONNECTED, MAKE CALL!
  858.             if (APIState.getAPIState() === CONNECTED_STATE) {
  859.                 var initialRequest = new JDAPIRequest(reqOptions).send();
  860.                 // Initial call successful, just resolve the deferred
  861.                 initialRequest.done(def.resolve);
  862.                 initialRequest.fail(function (error) {
  863.                     if (error.type && "TOKEN_INVALID" === error.type && count === 0) {
  864.                         jdapiRequestQueue.push({
  865.                             deferred: def,
  866.                             options: reqOptions
  867.                         });
  868.                         this.reconnect();
  869.                     } else if (error.type && "AUTH_FAILED" === error.type) {
  870.                         def.reject(error);
  871.                         this.disconnect();
  872.                     } else {
  873.                         // TODO: Probably causes disc if listen fails
  874.                         def.reject(error);
  875.                         // this.disconnect();
  876.                     }
  877.                 }.bind(this));
  878.                 // IF NOT CONNECTED, ENQUEUE FOR WHEN CONNECTED
  879.             } else {
  880.                 jdapiRequestQueue.push({
  881.                     deferred: def,
  882.                     options: reqOptions
  883.                 });
  884.             }
  885.             return def;
  886.         }
  887.     });
  888.     return JDAPICore;
  889. });
  890. define("device",[], function() {
  891.     /**
  892.      * API to handle device (device == JDownloader) calls
  893.      *
  894.      * data.rsaPublicKey
  895.      */
  896.     var JDAPIDevice = function(jdAPICore, data) {
  897.         this.jdAPICore = jdAPICore;
  898.         this.state = "OFFLINE";
  899.         this.deviceId = data.id;
  900.         this.rsaPublicKey = data.rsaPublicKey;
  901.         this.deviceName = data.name;
  902.     };
  903.     $.extend(JDAPIDevice.prototype, {
  904.         call: function(action, params, localModeCallback) {
  905.             if (this.localURL) {
  906.                 if(localModeCallback)   localModeCallback(true);
  907.                 return this.jdAPICore.localDeviceCall(this.localURL, this.deviceId, action, params, this.rsaPublicKey);
  908.             } else {
  909.                 if(localModeCallback)   localModeCallback(false);
  910.                 return this.jdAPICore.deviceCall(this.deviceId, action, params , this.rsaPublicKey);
  911.             }
  912.         },
  913.         setLocalURL: function(localURL) {
  914.             this.localURL = localURL;
  915.         },
  916.         setPublicKey: function (publicKey) {
  917.             this.publicKey = publicKey;
  918.         },
  919.         getURL: function() {
  920.             if (this.localURL) {
  921.                 return this.localURL;
  922.             } else {
  923.                 return this.jdAPICore.API_ROOT;
  924.             }
  925.         },
  926.         isInLocalMode: function() {
  927.             if (this.localURL) return true;
  928.             return false;
  929.         }
  930.     });
  931.     return JDAPIDevice;
  932. });
  933. define("serverServer", ["coreCrypto", "coreCryptoUtils"], function (CoreCrypto, CryptoUtils) {
  934.     /**
  935.      * API to handle server calls
  936.      */
  937.     var JDAPIServer = function (jdapiCore) {
  938.         this.jdapiCore = jdapiCore;
  939.     };
  940.     $.extend(JDAPIServer.prototype, {
  941.         listDevices: function () {
  942.             return this.jdapiCore.serverCall("/my/listdevices");
  943.         },
  944.         /**
  945.          * Calls that don't require authentication, and thus are made directly and not via the JDAPICore
  946.          */
  947.         getCaptcha: function () {
  948.             var request = $.ajax({
  949.                 url: this.jdapiCore.API_ROOT + "/captcha/getCaptcha",
  950.                 type: "post",
  951.                 dataType: "json"
  952.             });
  953.             return request;
  954.         },
  955.         /* Register a new user account on the api */
  956.         registerUser: function (data) {
  957.             data.referrer = "webui";
  958.             var requestURL = this.jdapiCore.API_ROOT + "/my/requestregistrationemail?email=" + encodeURIComponent(data.email) + "&captchaResponse=" + encodeURIComponent(data.captchaResponse) + "&captchaChallenge=" + encodeURIComponent(data.captchaChallenge) + "&referer=" + encodeURIComponent(data.referrer);
  959.             return $.ajax({
  960.                 url: requestURL,
  961.                 type: "POST",
  962.                 dataType: "text"
  963.             });
  964.         },
  965.         /* send email validationkey to the server */
  966.         confirmEmail: function (email, validationkey, pass) {
  967.             if (!pass) throw "No credentials given";
  968.             var action = "/my/finishregistration";
  969.             var loginSecret = CryptoUtils.hashPassword(email, pass, "server");
  970.             var registerKey = CoreCrypto.enc.Hex.parse(validationkey);
  971.             var iv = registerKey.firstHalf();
  972.             var key = registerKey.secondHalf();
  973.             var encrypted = CoreCrypto.AES.encrypt(loginSecret, key, {
  974.                 mode: CoreCrypto.mode.CBC,
  975.                 iv: iv
  976.             });
  977.             var stringEnc = CoreCrypto.enc.Hex.stringify(encrypted.ciphertext);
  978.             var queryString = action + "?email=" + encodeURIComponent(email) + "&loginSecret=" + encodeURIComponent(stringEnc);
  979.             queryString += "&signature=" + encodeURIComponent(CoreCrypto
  980.                 .HmacSHA256(CoreCrypto.enc.Utf8.parse(queryString), registerKey).toString(this.jdapiCore.TRANSFER_ENCODING));
  981.             var confirm = $.ajax({
  982.                 url: this.jdapiCore.API_ROOT + queryString,
  983.                 type: "POST",
  984.                 dataType: "text"
  985.             });
  986.             return confirm;
  987.         },
  988.         /* send request to server to send password change email */
  989.         requestPasswordChangeEmail: function (email, captchaChallenge, captchaResponse) {
  990.             // craft query string
  991.             var params = {};
  992.             var action = "/my/requestpasswordresetemail";
  993.  
  994.             params.email = email;
  995.             params.captchaResponse = captchaResponse;
  996.             params.captchaChallenge = captchaChallenge;
  997.  
  998.             var queryString = action + "?" + $.param(params);
  999.  
  1000.             // issue authentication request
  1001.             var confirm = $.ajax({
  1002.                 url: this.jdapiCore.API_ROOT + queryString,
  1003.                 type: "POST",
  1004.                 dataType: "text"
  1005.             });
  1006.  
  1007.             return confirm;
  1008.         },
  1009.         requestTerminationEmail: function (captchaChallenge, captchaResponse) {
  1010.             return this.jdapiCore.serverCall("requestterminationemail", {
  1011.                 captchaResponse: captchaResponse,
  1012.                 captchaChallenge: captchaChallenge
  1013.             });
  1014.         },
  1015.         finishTermination: function (email, pw, keyParam, captchaChallenge, captchaResponse) {
  1016.             var options = {email: email, pass: pw};
  1017.             CryptoUtils.processPassword(options);
  1018.             var action = "/my/finishtermination";
  1019.             var keyHex = CoreCrypto.enc.Hex.parse(keyParam);
  1020.             var iv = keyHex.firstHalf();
  1021.             var key = keyHex.secondHalf();
  1022.             var encrypted = CoreCrypto.AES.encrypt(options.loginSecret, key, {
  1023.                 mode: CoreCrypto.mode.CBC,
  1024.                 iv: iv
  1025.             });
  1026.             var stringEnc = CoreCrypto.enc.Hex.stringify(encrypted.ciphertext);
  1027.             var queryString = action + "?email=" + encodeURIComponent(email) + "&loginSecret=" + encodeURIComponent(stringEnc) + "&captchaResponse=" + encodeURIComponent(captchaResponse) + "&captchaChallenge=" + encodeURIComponent(captchaChallenge);
  1028.             queryString += "&signature=" + encodeURIComponent(CoreCrypto
  1029.                 .HmacSHA256(CoreCrypto.enc.Utf8.parse(queryString), keyHex).toString(this.jdapiCore.transferEncoding));
  1030.             var finish = $.ajax({
  1031.                 url: this.jdapiCore.API_ROOT + queryString,
  1032.                 type: "POST",
  1033.                 dataType: "text"
  1034.             });
  1035.             return finish;
  1036.         },
  1037.         /* send request to server to change password */
  1038.         changePassword: function (email, newpass, key) {
  1039.  
  1040.             // craft query string
  1041.             var action = "/my/finishpasswordreset";
  1042.  
  1043.             var loginSecret = CryptoUtils.hashPassword(email, newpass, "server");
  1044.             var registerKey = CoreCrypto.enc.Hex.parse(key);
  1045.  
  1046.             var iv = registerKey.firstHalf();
  1047.             var key = registerKey.secondHalf();
  1048.  
  1049.             var encrypted = CoreCrypto.AES.encrypt(loginSecret, key, {
  1050.                 mode: CoreCrypto.mode.CBC,
  1051.                 iv: iv
  1052.             });
  1053.  
  1054.             var stringEnc = CoreCrypto.enc.Hex.stringify(encrypted.ciphertext);
  1055.             var queryString = action + "?email=" + encodeURIComponent(email) + "&loginSecret=" + encodeURIComponent(stringEnc);
  1056.  
  1057.             queryString += "&signature=" + encodeURIComponent(CoreCrypto
  1058.                 .HmacSHA256(CoreCrypto.enc.Utf8.parse(queryString), registerKey).toString(this.jdapiCore.TRANSFER_ENCODING));
  1059.  
  1060.             // issue authentication request
  1061.             var confirm = $.ajax({
  1062.                 url: this.jdapiCore.API_ROOT + queryString,
  1063.                 type: "POST",
  1064.                 dataType: "text"
  1065.             });
  1066.             return confirm;
  1067.         },
  1068.         /* send feedback message to the server */
  1069.         feedback: function (data) {
  1070.             return this.jdapiCore.serverCall("/my/feedback", data);
  1071.         },
  1072.         subscribePushNotifications: function (subscriptionId, deviceId, types) {
  1073.             return this.jdapiCore.serverCall("/notify/register", types, {
  1074.                 receiverid: subscriptionId,
  1075.                 deviceid: deviceId
  1076.             }, "POST");
  1077.         },
  1078.         unsubscribePushNotifications: function (subscriptionId, deviceId, types) {
  1079.  
  1080.         }
  1081.     });
  1082.     return JDAPIServer;
  1083. });
  1084. define("serviceService", ["coreCryptoUtils", "coreCrypto"], function (CryptoUtils, CoreCrypto) {
  1085.     /**
  1086.      * API to handle service calls, eg the stats server
  1087.      */
  1088.     var JDAPIService = function(jdapiCore) {
  1089.         this.jdapiCore = jdapiCore;
  1090.     };
  1091.     $.extend(JDAPIService.prototype, {
  1092.         /**
  1093.          * Services Stuff
  1094.          */
  1095.         serviceAccessTokens: {},
  1096.         requestAccessToken: function(servicename) {
  1097.             //token already available
  1098.             if (this.serviceAccessTokens[servicename]) {
  1099.                 var tokens = $.Deferred();
  1100.                 tokens.resolve(this.serviceAccessTokens[servicename]);
  1101.                 return tokens;
  1102.             }
  1103.             //else request token first!
  1104.             //create container object
  1105.             this.serviceAccessTokens[servicename] = {};
  1106.             var serviceAccessTokensContainer = this.serviceAccessTokens[servicename];
  1107.  
  1108.             if (serviceAccessTokensContainer.requestOnTheWay) {
  1109.                 var enqueueDef = $.Deferred();
  1110.                 serviceAccessTokensContainer.deferredQueue = serviceAccessTokensContainer.deferredQueue || new Array();
  1111.                 serviceAccessTokensContainer.deferredQueue.push(enqueueDef);
  1112.                 return enqueueDef;
  1113.             }
  1114.             //flag request is on the way
  1115.             serviceAccessTokensContainer.requestOnTheWay = true;
  1116.             // if token request already sent, queue return value
  1117.             var params = {};
  1118.             var action = "";
  1119.             action = "/my/requestaccesstoken";
  1120.             params.service = servicename;
  1121.             var request = this.jdapiCore.serverCall(action, params);
  1122.             request.done(function(tokens){
  1123.                 $.extend(serviceAccessTokensContainer, tokens);
  1124.             });
  1125.             return request;
  1126.         },
  1127.         send: function(servicename, url, action, params) {
  1128.             var returnDeferred = $.Deferred();
  1129.             var self = this;
  1130.             this.requestAccessToken(servicename).done(function(tokens) {
  1131.                 var queryString = action + "?" + params + "&rid=" + 12 + "&accesstoken=" + tokens.accessToken;
  1132.                 queryString = encodeURI(queryString);
  1133.                 queryString += "&signature=" + CoreCrypto.HmacSHA256(CoreCrypto.enc.Utf8.parse(queryString), CoreCrypto.enc.Hex.parse(tokens.accessSecret)).toString(CoreCrypto.enc.Hex);
  1134.  
  1135.                 var confirm = $.ajax({
  1136.                     url: url + queryString,
  1137.                     type: "GET"
  1138.                 });
  1139.                 confirm.done(function(res) {
  1140.                     //increment successful request count
  1141.                     if (self.serviceAccessTokens[servicename]) {
  1142.                         var tokens = self.serviceAccessTokens[servicename];
  1143.                         tokens.used++;
  1144.                     }
  1145.                     returnDeferred.resolve(res);
  1146.                 }).fail(function(res) {
  1147.                     if (self.serviceAccessTokens[servicename] && self.serviceAccessTokens[servicename].count > 0) {
  1148.                         //retry with new token
  1149.                         delete self.serviceAccessTokens[servicename];
  1150.                         self.sendServiceRequest(servicename, url, action, params).then(returnDeferred.resolve, returnDeferred.reject);
  1151.                     } else {
  1152.                         //fail finally
  1153.                         delete self.serviceAccessTokens[servicename];
  1154.                         returnDeferred.reject(res);
  1155.                     }
  1156.                 });
  1157.             }).fail(function(err) {
  1158.                 returnDeferred.reject(err);
  1159.             });
  1160.             return returnDeferred;
  1161.         }
  1162.     });
  1163.     return JDAPIService;
  1164. });
  1165. define("deviceController", ["device"], function (JDAPIDevice) {
  1166.  
  1167.     /* Timeouts for direct connection mode detection */
  1168.     var GET_DIRECT_CONNECTION_INFOS_TIMEOUT = 5000;
  1169.     var PING_TIMEOUT = 5000;
  1170.  
  1171.     /* Iterator http://stackoverflow.com/questions/12079417/javascript-iterators */
  1172.     var Iterator = function (items) {
  1173.         this.index = 0;
  1174.         this.items = items || [];
  1175.     };
  1176.  
  1177.     $.extend(Iterator.prototype, {
  1178.         first: function () {
  1179.             this.reset();
  1180.             return this.next();
  1181.         },
  1182.         next: function () {
  1183.             return this.items[this.index++];
  1184.         },
  1185.         hasNext: function () {
  1186.             return this.index < this.items.length;
  1187.         },
  1188.         reset: function () {
  1189.             this.index = 0;
  1190.         },
  1191.         each: function (callback) {
  1192.             for (var item = this.first(); this.hasNext(); item = this.next()) {
  1193.                 callback(item);
  1194.             }
  1195.         }
  1196.     });
  1197.  
  1198.     var JDAPIDeviceController = function (jdAPIServer, jdAPICore) {
  1199.         this.jdAPIServer = jdAPIServer;
  1200.         this.jdAPICore = jdAPICore;
  1201.         this.devices = {};
  1202.     };
  1203.  
  1204.     $.extend(JDAPIDeviceController.prototype, {
  1205.         refreshAllDeviceAPIs: function () {
  1206.             var deviceListDef = $.Deferred();
  1207.             var deviceListDefLocalMode = $.Deferred();
  1208.             var listCall = this.jdAPIServer.listDevices();
  1209.             var self = this;
  1210.  
  1211.             listCall.done(function (devices) {
  1212.                 var devicesResult = Object.create(null);
  1213.                 var listResult = [];
  1214.                 $.each(devices.list, function (index, device) {
  1215.                     devicesResult[device.id] = new JDAPIDevice(self.jdAPICore, device);
  1216.                     listResult.push(device);
  1217.                 });
  1218.                 deviceListDef.resolve(listResult);
  1219.                 self.devices = devicesResult;
  1220.                 self._checkForLocalMode(devices.list).done(deviceListDefLocalMode.resolve).fail(deviceListDefLocalMode.reject);
  1221.                 /*self._iterateAndCheckForSessionPublicKey(new Iterator(devices.list), function () {
  1222.                     for (var i = 0; i < devices.list.length; i++) {
  1223.  
  1224.                     }
  1225.                  });*/
  1226.             });
  1227.             listCall.fail(function (error) {
  1228.                 deviceListDefLocalMode.reject(error);
  1229.                 deviceListDef.reject(error);
  1230.             });
  1231.             return {
  1232.                 deviceList: deviceListDef,
  1233.                 deviceListLocalMode: deviceListDefLocalMode
  1234.             };
  1235.         },
  1236.         _checkForLocalMode: function (devices) {
  1237.             if (devices !== undefined && devices.length !== undefined && devices.length != 0) {
  1238.                 var self = this;
  1239.                 var resultDef = $.Deferred();
  1240.                 var devicePromises = [];
  1241.                 $.each(devices, function (index, dev) {
  1242.                     self.devices[dev.id] = new JDAPIDevice(self.jdAPICore, dev);
  1243.                     var deviceDef = $.Deferred();
  1244.                     self.jdAPICore.deviceCall(dev.id, "/device/getDirectConnectionInfos", [], undefined, GET_DIRECT_CONNECTION_INFOS_TIMEOUT).always(function (result) {
  1245.                         if (result !== undefined && result.data !== undefined && result.data.infos !== undefined && result.data.infos !== null && result.data.infos.length !== 0) {
  1246.                             var pingPromises = self._pingForAvailability(result.data.infos, dev);
  1247.                             $.when.apply(this, pingPromises).done(function () {
  1248.                                 deviceDef.resolve(dev);
  1249.                             }).fail(deviceDef.reject);
  1250.                         }
  1251.                     });
  1252.                     devicePromises.push(deviceDef);
  1253.                 });
  1254.                 $.when.apply(this, devicePromises).done(
  1255.                     function (result) {
  1256.                         resultDef.resolve(result)
  1257.                     }).fail(function (error) {
  1258.                     resultDef.reject(error)
  1259.                 });
  1260.                 return resultDef;
  1261.             } else {
  1262.                 var def = $.Deferred();
  1263.                 def.resolve(devices);
  1264.                 return def;
  1265.             }
  1266.         },
  1267.         _pingForAvailability: function (addresses, device) {
  1268.             if (addresses !== undefined && addresses.length !== undefined && addresses.length !== 0) {
  1269.                 var self = this;
  1270.                 var bestLocalUrl;
  1271.                 var bestLocalUrlDuration;
  1272.                 var pingPromises = [];
  1273.                 $.each(addresses, function (index, deviceAddress) {
  1274.                     var def = $.Deferred();
  1275.                     pingPromises.push(def);
  1276.                     var localURL;
  1277.                     var unknownProtocol = window.location.protocol === undefined || (window.location.protocol !== "https:" && window.location.protocol !== "http:");
  1278.                     if (unknownProtocol || window.location.protocol === "https:") {
  1279.                         // dyndns service for wildcard certificate
  1280.                         if (deviceAddress.ip.match(/^(\d+\.\d+\.\d+\.\d+)$/) != null) {
  1281.                             // IPv4
  1282.                             localURL = "https://" + deviceAddress.ip.replace(new RegExp("\\.", 'g'), "-") + ".mydns.jdownloader.org:" + deviceAddress.port;
  1283.                         } else if (deviceAddress.ip.match(/^(\[[a-f0-9A-F:]+\])$/) != null) {
  1284.                             // IPv6
  1285.                             var ipString = deviceAddress.ip.replace(/[\[\]]/g, "");
  1286.                             ipString = ipString.replace(/^:|:$/g, '');
  1287.  
  1288.                             var ipv6 = ipString.split(':');
  1289.  
  1290.                             for (var i = 0; i < ipv6.length; i++) {
  1291.                                 var hex = ipv6[i];
  1292.                                 if (hex != "") {
  1293.                                     ipv6[i] = ("0000" + hex).substr(-4);
  1294.                                 }
  1295.                                 else {
  1296.                                     hex = [];
  1297.                                     for (var j = ipv6.length; j <= 8; j++) {
  1298.                                         hex.push('0000');
  1299.                                     }
  1300.                                     ipv6[i] = hex.join(':');
  1301.                                 }
  1302.                             }
  1303.                             localURL = "https://" + ipv6.join('') + ".mydns.jdownloader.org:" + deviceAddress.port;
  1304.                         } else {
  1305.                             localURL = "https://" + deviceAddress.ip + ":" + deviceAddress.port;
  1306.                         }
  1307.  
  1308.                     } else if (window.location.protocol && window.location.protocol === "http:") {
  1309.                         localURL = "http://" + deviceAddress.ip + ":" + deviceAddress.port;
  1310.                     }
  1311.  
  1312.                     var pingCall = self.jdAPICore.localDeviceCall(localURL, device.id, "/device/ping", [], undefined, PING_TIMEOUT);
  1313.                     pingCall.done(function (response) {
  1314.                         var duration = response.requestDuration;
  1315.                         if (bestLocalUrl === undefined || duration < bestLocalUrl) {
  1316.                             bestLocalUrl = localURL;
  1317.                             bestLocalUrlDuration = duration;
  1318.                             self.devices[device.id].setLocalURL(localURL);
  1319.                         }
  1320.                         def.resolve(self.devices[device.id]);
  1321.                     });
  1322.                     pingCall.fail(function (error) {
  1323.                         def.resolve(self.devices[device.id]);
  1324.                     });
  1325.  
  1326.                 });
  1327.                 return pingPromises;
  1328.             } else {
  1329.                 var def = $.Deferred();
  1330.                 def.resolve();
  1331.                 return def;
  1332.             }
  1333.         },
  1334.         _iterateAndCheckForSessionPublicKey: function (iterator, finishedCallback) {
  1335.             var dev = iterator.next();
  1336.             //Last element reached
  1337.             if (!dev) {
  1338.                 finishedCallback();
  1339.                 return;
  1340.             }
  1341.             //put device on device list
  1342.             this.devices[dev.id] = this.devices[dev.id] || new JDAPIDevice(this.jdAPICore, dev);
  1343.             var self = this;
  1344.             this.jdAPICore.deviceCall(dev.id, "/device/getSessionPublicKey", [], undefined).done(function (result) {
  1345.                 if (!result || !result.data) {
  1346.                     self._iterateAndCheckForSessionPublicKey(iterator, finishedCallback);
  1347.                 } else {
  1348.                     self.devices[dev.id].rsaPublicKey = "-----BEGIN RSA PRIVATE KEY-----" + result.data + "-----END RSA PRIVATE KEY-----";
  1349.                 }
  1350.             }).fail(function () {
  1351.                 //on fail, just skip this device
  1352.                 self._iterateAndCheckForSessionPublicKey(iterator, finishedCallback);
  1353.             });
  1354.         },
  1355.  
  1356.         /**
  1357.          * Construct reusable deviceAPI Interface for given deviceId
  1358.          */
  1359.         getDeviceAPIForId: function (deviceId) {
  1360.             var ret = $.Deferred();
  1361.             var self = this;
  1362.  
  1363.             var deviceQuery = $.Deferred();
  1364.             if (!self.devices[deviceId]) {
  1365.                 deviceQuery = self.refreshAllDeviceAPIs().deviceList;
  1366.             } else {
  1367.                 deviceQuery.resolve();
  1368.             }
  1369.  
  1370.             deviceQuery.always(function () {
  1371.                 ret.resolve({
  1372.                     call: function (action, params) {
  1373.                         var innerRet = $.Deferred();
  1374.                         var device = self.devices[deviceId];
  1375.                         if (!device) {
  1376.                             innerRet.reject({
  1377.                                 responseText: {
  1378.                                     type: "DEVICE_NOT_EXISTING"
  1379.                                 }
  1380.                             });
  1381.                         } else {
  1382.                             device.call(action, params).then(innerRet.resolve, innerRet.reject, innerRet.progress);
  1383.                         }
  1384.                         return innerRet;
  1385.                     },
  1386.                     getURL: function () {
  1387.                         var device = self.devices[deviceId];
  1388.                         if (!device) throw "Device with id " + deviceId + " not found";
  1389.                         return device.getURL();
  1390.                     },
  1391.                     getTokenRoot: function () {
  1392.                         var device = self.devices[deviceId];
  1393.                         if (!device) throw "Device with id " + deviceId + " not found";
  1394.                         return device.getURL() + "/t_" + self.jdAPICore.getSessionToken() + "_" + deviceId;
  1395.                     },
  1396.                     isInLocalMode: function () {
  1397.                         var device = self.devices[deviceId];
  1398.                         return device.isInLocalMode();
  1399.                     },
  1400.                     deviceId: deviceId
  1401.                 });
  1402.             });
  1403.  
  1404.             return ret;
  1405.         }
  1406.     });
  1407.  
  1408.     return JDAPIDeviceController;
  1409. });
  1410. var dataStore;
  1411. if (typeof chrome !== 'undefined' && chrome && chrome.storage && chrome.storage.local) {
  1412.     // web extension storage
  1413.     var dataStore = Object.create(null);
  1414.     dataStore.setItem = function (key, value) {
  1415.         var def = $.Deferred();
  1416.         var setObject = Object.create(null);
  1417.         setObject[key] = value;
  1418.         chrome.storage.local.set(setObject, def.resolve);
  1419.         return def;
  1420.     };
  1421.     dataStore.getItem = function (key) {
  1422.         var def = $.Deferred();
  1423.         chrome.storage.local.get(key, function (result) {
  1424.             if (result) {
  1425.                 var resultObject = Object.create(null);
  1426.                 resultObject[key] = result[key];
  1427.                 def.resolve(resultObject);
  1428.             } else {
  1429.                 def.reject();
  1430.             }
  1431.         });
  1432.         return def;
  1433.     };
  1434.     dataStore.removeItem = function (key) {
  1435.         var def = $.Deferred();
  1436.         chrome.storage.local.remove(key, def.resolve);
  1437.         return def;
  1438.     };
  1439.  
  1440.     dataStore.clear = function () {
  1441.         var def = $.Deferred();
  1442.         chrome.storage.local.clear(def.resolve);
  1443.         return def;
  1444.     };
  1445. } else {
  1446.     // DOM storage
  1447.     var dataStore = Object.create(null);
  1448.     dataStore.setItem = function (key, value) {
  1449.         var result = $.Deferred();
  1450.         result.resolve(window.localStorage.setItem(key, value));
  1451.         return result;
  1452.     };
  1453.  
  1454.     dataStore.getItem = function (key) {
  1455.         var result = $.Deferred();
  1456.         var resultObject = Object.create(null);
  1457.         resultObject[key] = window.localStorage.getItem(key);
  1458.         result.resolve(resultObject);
  1459.         return result;
  1460.     };
  1461.  
  1462.     dataStore.removeItem = function (key) {
  1463.         var result = $.Deferred();
  1464.         result.resolve(window.localStorage.removeItem(key));
  1465.         return result;
  1466.     };
  1467.  
  1468.     dataStore.clearStorage = function () {
  1469.         var result = $.Deferred();
  1470.         result.resolve(window.localStorage.clear());
  1471.         return result;
  1472.     };
  1473. }
  1474.  
  1475. /**
  1476.  * Wrap console to disable .log output in production
  1477.  */
  1478.  
  1479. var LOGGING_ENABLED = false;
  1480. var logger = {
  1481.     log: function (msg) {
  1482.         if (LOGGING_ENABLED) console.log(msg);
  1483.     },
  1484.     warn: function (msg) {
  1485.         if (LOGGING_ENABLED) console.warn(msg);
  1486.     },
  1487.     error: function (msg) {
  1488.         if (LOGGING_ENABLED)  console.error(msg);
  1489.     }
  1490. };
  1491. /**
  1492.  * Global config variables
  1493.  */
  1494. define("config/config", [],function () {
  1495.     return {
  1496.         TRANSFER_ENCODING: CryptoJS.enc.Hex,
  1497.         API_ROOT: "//api.jdownloader.org",
  1498.         LOCAL_STORAGE_KEY: "api.transport.options",
  1499.         APP_KEY: "com.appwork.jdownloader" //"org.jdownloader.my_web_0.1.657"
  1500.     };
  1501. });
  1502.  
  1503. /**
  1504.  * Main Library Object, contains all functions the library makes available for clients
  1505.  */
  1506. define("jdapi", ["coreCore", "device", "serverServer", "serviceService", "deviceController"], function (JDAPICore, JDAPIDevice, JDAPIServer, JDAPIService, JDAPIDeviceController) {
  1507.     /**
  1508.      *  Constructor method of the API
  1509.      *
  1510.      *    @param options Example: {email: '[email protected]', pass: 'mysecretpass'}
  1511.      *  @param onConnectedCallback: a function or $.Deferred that gets called/resolved if connect was successful
  1512.      *  @param The AppKey to identify the application using the API
  1513.      */
  1514.     var API = function (options, onConnectedCallback, APP_KEY) {
  1515.         // Setup API Components
  1516.         this.jdAPICore = new JDAPICore(options, onConnectedCallback, APP_KEY);
  1517.         this.apiServer = new JDAPIServer(this.jdAPICore);
  1518.         this.apiService = new JDAPIService(this.jdAPICore);
  1519.         this.apiDeviceController = new JDAPIDeviceController(this.apiServer, this.jdAPICore);
  1520.     };
  1521.  
  1522.     $.extend(API.prototype, {
  1523.         /**
  1524.          * Get captcha challenge from the API used for 'Register Account' and 'Password Recovery'
  1525.          *  @returns  deferred object that resolves to captcha object which contains the captcha challenge id
  1526.          *              and the captcha challenge image as dataUrl
  1527.          */
  1528.         getCaptcha: function () {
  1529.             return this.apiServer.getCaptcha();
  1530.         },
  1531.         /**
  1532.          *   Connect to the API server and authenticate.
  1533.          *   Subscribe to handshake.done to see when the connection
  1534.          *   has been established.
  1535.          *   @param options: Example: {email: '[email protected]', pass: 'mysecretpass'}
  1536.          *   @returns handshake deferred that gets resolved /rejected
  1537.          *            when the authentication has  succeeded/failed
  1538.          */
  1539.         connect: function (options) {
  1540.             return this.jdAPICore.connect(options);
  1541.         },
  1542.         /**
  1543.          *   Register new user at API server, requires a Captcha Challenge to be solved.
  1544.          *   Email will be verified via authentication link
  1545.          *   @param data: Example: {email: '[email protected]', catpachaChallenge: '', captchaResponse: ''}
  1546.          *   @returns handshake deferred that gets resolved /rejected
  1547.          *            when the registration has succeeded/failed
  1548.          */
  1549.         registerUser: function (data) {
  1550.             return this.apiServer.registerUser(data);
  1551.         },
  1552.         reconnect: function () {
  1553.             return this.jdAPICore.reconnect();
  1554.         },
  1555.         /**
  1556.          *   Disconnect from the API server.
  1557.          *   @returns a deferred that gets resolved as soon you are disconnected.
  1558.          */
  1559.         disconnect: function () {
  1560.             return this.jdAPICore.disconnect();
  1561.         },
  1562.         confirmEmail: function (email, validationkey, pass) {
  1563.             return this.apiServer.confirmEmail(email, validationkey, pass);
  1564.         },
  1565.         requestConfirmEmail: function (pass) {
  1566.             return this.apiServer.requestConfirmEmail(pass);
  1567.         },
  1568.         requestPasswordChangeEmail: function (email, captchaChallenge, captchaResponse) {
  1569.             return this.apiServer.requestPasswordChangeEmail(email, captchaChallenge, captchaResponse);
  1570.         },
  1571.         requestTerminationEmail: function (captchaChallenge, captchaResponse) {
  1572.             return this.apiServer.requestTerminationEmail(captchaChallenge, captchaResponse);
  1573.         },
  1574.         finishTermination: function (email, pw, key, captchaChallenge, captchaResponse) {
  1575.             return this.apiServer.finishTermination(email, pw, key, captchaChallenge, captchaResponse);
  1576.         },
  1577.         /**
  1578.          * @param newpass The new password
  1579.          * @param key The password change validation key
  1580.          * @returns
  1581.          */
  1582.         changePassword: function (email, newpass, key) {
  1583.             return this.apiServer.changePassword(email, newpass, key);
  1584.         },
  1585.         sendServiceRequest: function (serviceName, url, action, params) {
  1586.             return this.apiService.send(serviceName, url, action, params);
  1587.         },
  1588.         listDevices: function (waitForLocalMode) {
  1589.             if (waitForLocalMode !== undefined && waitForLocalMode === true) {
  1590.                 return this.apiDeviceController.refreshAllDeviceAPIs().deviceListLocalMode;
  1591.             } else {
  1592.                 return this.apiDeviceController.refreshAllDeviceAPIs().deviceList;
  1593.             }
  1594.         },
  1595.         /**
  1596.          * Set the device id to what device subsequent "send" calls should go
  1597.          */
  1598.         setActiveDevice: function (deviceId) {
  1599.             return this.activeDeviceId = deviceId;
  1600.         },
  1601.         getActiveDevice: function () {
  1602.             return this.activeDeviceId;
  1603.         },
  1604.         getDirectConnectionInfos: function () {
  1605.             return this.transport.getDirectConnectionInfos();
  1606.         },
  1607.         ping: function () {
  1608.             return this.transport.ping();
  1609.         },
  1610.         /**
  1611.          *   Post feedback to the server
  1612.          *   @returns a deferred that gets resolved as feedback post returns successfully
  1613.          */
  1614.         feedback: function (data) {
  1615.             return this.apiServer.feedback(data);
  1616.         },
  1617.         subscribePushNotifications: function (subscriptionId, deviceId, types) {
  1618.             return this.apiServer.subscribePushNotifications(subscriptionId, deviceId, types);
  1619.         },
  1620.         unsubscribePushNotifications: function (subscriptionId, deviceId, types) {
  1621.  
  1622.         },
  1623.         /**
  1624.          *   Start polling events from the server.
  1625.          *   Subscribe to listener.notify to receive events.
  1626.          *   @returns the listener Deferred
  1627.          */
  1628.         listen: function (subId) {
  1629.             return this.send("/events/listen", [subId]);
  1630.         },
  1631.         subscribe: function (subscriptions, exclusions) {
  1632.             return this.send("/events/subscribe", [JSON.stringify(subscriptions), JSON.stringify(exclusions)]);
  1633.         },
  1634.         unsubscribe: function (subId) {
  1635.             return this.send("/events/unsubscribe", [subId]);
  1636.         },
  1637.         setSubscriptionId: function (subId) {
  1638.             return this.send("/events/getSubscriptionId", [subId]);
  1639.         },
  1640.         getSubscriptionId: function () {
  1641.             return this.send("/events/getSubscriptionId", []);
  1642.         },
  1643.         addSubscription: function (subId, subscriptions, exclusions) {
  1644.             return this.send("/events/addSubscription", [subId, subscriptions, exclusions]);
  1645.         },
  1646.         setSubscription: function (subId, subscriptions, exclusions) {
  1647.             return this.send("/events/setsubscription", [subId, JSON.stringify(subscriptions), JSON.stringify(exclusions)]);
  1648.         },
  1649.         getSubscription: function (subId) {
  1650.             return this.send("/events/getSubscription", [subId]);
  1651.         },
  1652.         removeSubscription: function (subId, subscriptions, exclusions) {
  1653.             return this.send("/events/removeSubscription", [subId, subscriptions, exclusions]);
  1654.         },
  1655.         changeSubscriptionTimeouts: function (subId, polltimeout, maxkeepalive) {
  1656.             return this.send("/events/changeSubscriptionTimeouts", [subId, polltimeout, maxkeepalive]);
  1657.         },
  1658.         listPublisher: function () {
  1659.             return this.transport.listPublisher();
  1660.         },
  1661.         /**
  1662.          *   Stop polling events
  1663.          */
  1664.         stopListen: function () {
  1665.         },
  1666.         /**
  1667.          *   Send a message with the given params to the server.
  1668.          *   Please make sure that you specify the parameters in the correct order.
  1669.          *
  1670.          *   @param action: The desired action (e.g. "/linkgrabber/add") as a string
  1671.          *   @param params: An array of primitives containing the parameters for the action.
  1672.          *     @param localModeCallback: OPTIONAL, function that gets called with TRUE if request is sent via localmode
  1673.          *   @returns a $.Deferred that gets resolved as soon as the action has been done.
  1674.          *   If you are interested in the results, subscribe to returnedDeferred.done
  1675.          */
  1676.         send: function (action, params, localModeCallback) {
  1677.             var ret = $.Deferred();
  1678.             this.apiDeviceController.getDeviceAPIForId(this.getActiveDevice()).done(function (deviceAPI) {
  1679.                 deviceAPI.call(action, params, localModeCallback).then(ret.resolve, ret.reject, ret.progress);
  1680.             });
  1681.             return ret;
  1682.         },
  1683.         fetchActiveDeviceURL: function () {
  1684.             var ret = $.Deferred();
  1685.             this.apiDeviceController.getDeviceAPIForId(this.getActiveDevice()).done(function (deviceAPI) {
  1686.                 var url = deviceAPI.getURL();
  1687.                 ret.resolve(url);
  1688.             });
  1689.             return ret;
  1690.         },
  1691.         fetchActiveDeviceTokenRoot: function () {
  1692.             var ret = $.Deferred();
  1693.             this.apiDeviceController.getDeviceAPIForId(this.getActiveDevice()).done(function (deviceAPI) {
  1694.                 var url = deviceAPI.getTokenRoot();
  1695.                 ret.resolve(url);
  1696.             });
  1697.             return ret;
  1698.         },
  1699.         isActiveDeviceInLocalMode: function () {
  1700.             var ret = $.Deferred();
  1701.             this.apiDeviceController.getDeviceAPIForId(this.getActiveDevice()).done(function (deviceAPI) {
  1702.                 var local = deviceAPI.isInLocalMode();
  1703.                 ret.resolve(local);
  1704.             });
  1705.             return ret;
  1706.         },
  1707.         /**
  1708.          *   @returns a dictionary (object) containing the necessary data
  1709.          *   for the next authentication.
  1710.          */
  1711.         getAuth: function () {
  1712.             return this.transport.getAuth();
  1713.         },
  1714.         // Register callback function that gets called if api logs out
  1715.         // callback function gets called with a 'reason' string object
  1716.         addAPIStateChangeListener: function (callback) {
  1717.             this.jdAPICore.addAPIStateChangeListener(callback);
  1718.         },
  1719.         getAPIState: function () {
  1720.             return this.jdAPICore.getAPIState();
  1721.         },
  1722.         getAPIStatePlain: function () {
  1723.             return this.jdAPICore.getAPIStatePlain();
  1724.         },
  1725.         getCurrentUser: function () {
  1726.             return this.jdAPICore.getCurrentUser();
  1727.         },
  1728.         getCurrentSessionInfo: function () {
  1729.             var sessionInfo = this.jdAPICore.getSessionInfo();
  1730.             var result = {
  1731.                 s: sessionInfo.sessiontoken,
  1732.                 r: sessionInfo.regaintoken,
  1733.                 e: sessionInfo.serverEncryptionToken.toString(CryptoJS.enc.Base64),
  1734.                 d: sessionInfo.deviceSecret.toString(CryptoJS.enc.Base64)
  1735.             };
  1736.             return result;
  1737.         }
  1738.     });
  1739.     /**
  1740.      * Export jdapi constructor
  1741.      */
  1742.     return API;
  1743. });
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement