/** * @license * Copyright (C) 2013 KO GmbH * * @licstart * This file is part of WebODF. * * WebODF is free software: you can redistribute it and/or modify it * under the terms of the GNU Affero General Public License (GNU AGPL) * as published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. * * WebODF is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with WebODF. If not, see . * @licend * * @source: http://www.webodf.org/ * @source: https://github.com/kogmbh/WebODF/ */ /*global XMLHttpRequest, runtime, core, ops*/ define("webodf/editor/server/pullbox/Server", [], function () { "use strict"; runtime.loadClass("core.Base64"); runtime.loadClass("core.ByteArrayWriter"); /** * @constructor * @implements ops.Server * @param {{url:string}} args */ return function PullBoxServer(args) { var self = this, token, /**@const*/serverCallTimeout = 10000, base64 = new core.Base64(); args = args || {}; args.url = args.url || "/WSER"; args.sessionStateToFileUrl = args.sessionStateToFileUrl || "/SS2F" this.getGenesisUrl = function (sessionId) { return "/session/" + sessionId + "/genesis"; }; /** * @param {!Object} message * @param {!function(!string)} cb * @param {!function(!number,!string)} cbError passes the status number * and the statustext, or -1 if there was an exception on sending * @return {undefined} */ function call(message, cb, cbError) { var xhr = new XMLHttpRequest(), messageString = JSON.stringify(message); function handleResult() { if (xhr.readyState === 4) { if (xhr.status < 200 || xhr.status >= 300) { // report error runtime.log("Status " + String(xhr.status) + ": " + xhr.responseText || xhr.statusText); cbError(xhr.status, xhr.statusText); } else { runtime.log("Status " + String(xhr.status) + ": " + xhr.responseText || xhr.statusText); cb(xhr.responseText); } } } function handleTimeout() { runtime.log("Timeout on call to server."); cbError(0, xhr.statusText); } runtime.log("Sending message to server: "+messageString); // create body data for request from metadata and payload // do the request xhr.open('POST', args.url, true); xhr.setRequestHeader("Content-type","application/json"); if (token) { xhr.setRequestHeader("requesttoken", token); } xhr.onreadystatechange = handleResult; xhr.timeout = serverCallTimeout; // TODO: seems handleResult is called on timeout as well, with xhr.status === 0 // xhr.ontimeout = handleTimeout; try { xhr.send(messageString); } catch (e) { runtime.log("Problem with calling server: " + e + " " + data); cbError(-1, e.message); } } this.call = call; this.getToken = function () { return token; }; /** * for pre-authenticated use */ this.setToken = function (a_token) { token = a_token; }; /*jslint unparam: true*/ /** * @param {!number} timeout in milliseconds * @param {!function(!string)} callback * @return {undefined} */ this.connect = function (timeout, callback) { /* var accumulatedWaitingTime = 0; // already tried connecting? if (self.networkStatus() === "ready") { return; } */ callback("ready"); }; /*jslint unparam: false*/ /** * @return {!string} */ this.networkStatus = function () { return "ready"; }; /** * @param {!string} login * @param {!string} password * @param {function(!Object)} successCb * @param {function(!string)} failCb * @return {undefined} */ this.login = function (login, password, successCb, failCb) { call({ command: "login", args: { login: base64.toBase64(login), password: base64.toBase64(password) } }, function(responseData) { var response = /**@type {{token:string}}*/(runtime.fromJson(responseData)); runtime.log("Login reply: " + responseData); if (response.hasOwnProperty("token")) { token = response.token; runtime.log("Caching token: " + self.getToken()); successCb(response); } else { failCb(responseData); } }, failCb); }; /** * @param {!string} userId * @param {!string} sessionId * @param {!function(!string)} successCb * @param {!function()} failCb * @return {undefined} */ this.joinSession = function (userId, sessionId, successCb, failCb) { call({ command: "join_session", args: { user_id: userId, es_id: sessionId } }, function(responseData) { var response = /**@type {{success:string, member_id:string}}*/(runtime.fromJson(responseData)); runtime.log("join_session reply: " + responseData); if (response.hasOwnProperty("success") && response.success) { successCb(response.member_id); } else { failCb(); } }, failCb); }; /** * @param {!string} sessionId * @param {!string} memberId * @param {!function()} successCb * @param {!function()} failCb * @return {undefined} */ this.leaveSession = function (sessionId, memberId, successCb, failCb) { call({ command: "leave_session", args: { es_id: sessionId, member_id: memberId } }, function(responseData) { var response = /**@type {{success:string, member_id:string}}*/(runtime.fromJson(responseData)); runtime.log("leave_session reply: " + responseData); if (response.hasOwnProperty("success") && response.success) { successCb(); } else { failCb(); } }, failCb); }; /** * @param {!string} sessionId * @param {!string} memberId * @param {!string} seqHead * @param {!function(!Object=)} callback * @return {undefined} */ this.writeSessionStateToFile = function(sessionId, memberId, seqHead, fileData, callback) { // code copied from BrowserRuntime.writeFile and adapted var xhr = new XMLHttpRequest(); function handleResult() { if (xhr.readyState === 4) { if (xhr.status === 0 && !xhr.responseText) {// TODO: check makes sense here as well? // for local files there is no difference between missing // and empty files, so empty files are considered as errors runtime.log("File " + args.sessionStateToFileUrl + " is empty."); } else if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 0) { // report success runtime.log(null); } else { // report error runtime.log("Status " + String(xhr.status) + ": " + xhr.responseText || xhr.statusText); } callback(); } } // do the request xhr.open('POST', args.sessionStateToFileUrl, true); if (token) { xhr.setRequestHeader("requesttoken", token); } xhr.setRequestHeader("webodf-session-id", sessionId); xhr.setRequestHeader("webodf-member-id", memberId); xhr.setRequestHeader("webodf-session-revision", seqHead); xhr.onreadystatechange = handleResult; // ArrayBufferView will have an ArrayBuffer property, in WebKit, XHR can send() // an ArrayBuffer, In Firefox, one must use sendAsBinary with a string if (fileData.buffer && !xhr.sendAsBinary) { fileData = fileData.buffer; // webkit supports sending an ArrayBuffer } else { // encode into a string, this works in FireFox >= 3 fileData = runtime.byteArrayToString(fileData, "binary"); } try { if (xhr.sendAsBinary) { xhr.sendAsBinary(fileData); } else { xhr.send(fileData); } } catch (e) { runtime.log("Problem with calling \"writeSessionStateToFile\" on server"); callback(e.message); } }; }; });