1 /* 2 * ***** BEGIN LICENSE BLOCK ***** 3 * Zimbra Collaboration Suite Web Client 4 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2013, 2014, 2016 Synacor, Inc. 5 * 6 * The contents of this file are subject to the Common Public Attribution License Version 1.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at: https://www.zimbra.com/license 9 * The License is based on the Mozilla Public License Version 1.1 but Sections 14 and 15 10 * have been added to cover use of software over a computer network and provide for limited attribution 11 * for the Original Developer. In addition, Exhibit A has been modified to be consistent with Exhibit B. 12 * 13 * Software distributed under the License is distributed on an "AS IS" basis, 14 * WITHOUT WARRANTY OF ANY KIND, either express or implied. 15 * See the License for the specific language governing rights and limitations under the License. 16 * The Original Code is Zimbra Open Source Web Client. 17 * The Initial Developer of the Original Code is Zimbra, Inc. All rights to the Original Code were 18 * transferred by Zimbra, Inc. to Synacor, Inc. on September 14, 2015. 19 * 20 * All portions of the code are Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2013, 2014, 2016 Synacor, Inc. All Rights Reserved. 21 * ***** END LICENSE BLOCK ***** 22 */ 23 24 25 /** 26 * Resets the AjxPost object. 27 * @constructor 28 * @class 29 * This singleton class makes an HTTP POST to the server and receives the response, passing returned data 30 * to a callback. This class is used to upload files from the client browser to the server using the file 31 * upload feature of POST. 32 * 33 * @param {string} iframeId the iframe ID 34 * 35 * @author Conrad Damon 36 * 37 * @private 38 */ 39 AjxPost = function(iframeId) { 40 this._callback = null; 41 this._iframeId = iframeId; 42 } 43 44 45 // Globals 46 47 AjxPost._reqIds = 0; 48 AjxPost._outStandingRequests = new Object(); 49 50 51 // Consts 52 53 // Common HttpServletResponse error codes 54 // - see full list: http://java.sun.com/products/servlet/2.2/javadoc/javax/servlet/http/HttpServletResponse.html 55 AjxPost.SC_CONTINUE = 100; 56 AjxPost.SC_OK = 200; 57 AjxPost.SC_ACCEPTED = 202; 58 AjxPost.SC_NO_CONTENT = 204; 59 AjxPost.SC_BAD_REQUEST = 400; 60 AjxPost.SC_UNAUTHORIZED = 401; 61 AjxPost.SC_REQUEST_TIMEOUT = 408; 62 AjxPost.SC_CONFLICT = 409; 63 AjxPost.SC_REQUEST_ENTITY_TOO_LARGE = 413; 64 AjxPost.SC_INTERNAL_SERVER_ERROR = 500; 65 AjxPost.SC_BAD_GATEWAY = 502; 66 AjxPost.SC_SERVICE_UNAVAILABLE = 503; 67 68 69 // Public methods 70 71 /** 72 * Submits the form. 73 * 74 * @param callback function to return to after the HTTP response is received 75 * @param formId DOM ID of the form 76 */ 77 AjxPost.prototype.execute = 78 function(callback, form, optionalTimeout) { 79 // bug fix #7361 80 var tags = form.getElementsByTagName("input"); 81 var inputs = new Array(); 82 for (var i = 0; i < tags.length; i++) { 83 var tag = tags[i]; 84 if (tag.type == "file") { 85 inputs.push(tag); 86 continue; 87 } 88 // clean up form from previous posts 89 if (tag.name && tag.name.match(/^filename\d+$/)) { 90 tag.parentNode.removeChild(tag); 91 i--; // list is live, so stay on same index 92 continue; 93 } 94 } 95 if (window.csrfToken) { 96 this._addHiddenField(inputs[0], "csrfToken", window.csrfToken); 97 } 98 this._addHiddenFileNames(inputs); 99 100 form.target = this._iframeId; 101 this._callback = callback; 102 var req = new AjxPostRequest(form); 103 var failureAction = new AjxTimedAction(this, this._onFailure, [req.id]); 104 var timeout = optionalTimeout? optionalTimeout: 5000; 105 AjxPost._outStandingRequests[req.id] = req; 106 try { 107 req.send(failureAction, timeout); 108 } catch (ex) { 109 if (AjxEnv.isIE) { 110 if (ex.number == -2147024891) { // 0x80070005: E_ACCESSDENIED (Couldn't open file) 111 throw new AjxException(ZmMsg.uploadErrorAccessDenied, ex.number); 112 } 113 } 114 throw ex; 115 } 116 }; 117 118 AjxPost.prototype._addHiddenFileNames = 119 function(inputs){ 120 var m = 0; 121 for (var i = 0; i < inputs.length; i++) { 122 var fileInput = inputs[i]; 123 if(fileInput.files && fileInput.files.length > 1){ 124 var files = fileInput.files, fileStr=[]; 125 for(var j=0; j<files.length; j++){ 126 var f = files[j]; 127 fileStr.push(f.name || f.fileName); 128 } 129 this._addHiddenFileName(inputs[i], fileStr.join('\n'), ++m); 130 }else{ 131 this._addHiddenFileName(inputs[i], inputs[i].value, ++m); 132 } 133 } 134 135 }; 136 137 AjxPost.prototype._addHiddenFileName = 138 function(inputField, fileName, index){ 139 this._addHiddenField(inputField, "filename" + (index), fileName); 140 }; 141 AjxPost.prototype._addHiddenField = function(referenceElement, fieldName, fieldValue){ 142 var hidden = document.createElement("input"); 143 hidden.type = "hidden"; 144 hidden.name = fieldName; 145 hidden.value = fieldValue; 146 referenceElement.parentNode.insertBefore(hidden, referenceElement); 147 }; 148 149 150 // Private methods 151 152 AjxPost.prototype._onFailure = 153 function (reqId){ 154 var req = AjxPost._outStandingRequests[reqId]; 155 req.cancel(); 156 delete AjxPost._outStandingRequests[reqId]; 157 if (this._callback) { 158 this._callback.run([404]); 159 this._callback = null; 160 } 161 }; 162 163 164 165 /** 166 * Processes the HTTP response from the form post. The server needs to make sure this function is 167 * called and passed the appropriate args. Something like the following should do the trick: 168 * <code> 169 * out.println("<html><head></head><body onload=\"window.parent._uploadManager.loaded(" + results +");\"></body></html>"); 170 * </code> 171 * 172 * @param status an HTTP status 173 * @param id the id for any attachments that were uploaded 174 */ 175 AjxPost.prototype.loaded = 176 function(status, reqId, id) { 177 //alert(document.getElementById(this._iframeId).contentWindow.document.documentElement.innerHTML); 178 var req = AjxPost._outStandingRequests[reqId]; 179 if (req && !req.hasBeenCancelled()) { 180 req.cancelTimeout(); 181 } 182 delete AjxPost._outStandingRequests[reqId]; 183 if (this._callback) { 184 this._callback.run(status, id); 185 this._callback = null; 186 } 187 }; 188 189 /** 190 * @class 191 * 192 * @private 193 */ 194 AjxPostRequest = function(form) { 195 this.id = AjxPost._reqIds++; 196 this._cancelled = false; 197 this._form = form; 198 var inp = form.elements.namedItem("requestId"); 199 if (!inp) { 200 inp = form.ownerDocument.createElement('input'); 201 inp.type = "hidden"; 202 inp.name = "requestId"; 203 } 204 inp.value = this.id; 205 form.appendChild(inp); 206 }; 207 208 AjxPostRequest.prototype.send = 209 function(failureAction, timeout) { 210 this._form.submit(); 211 }; 212 213 AjxPostRequest.prototype.hasBeenCancelled = 214 function() { 215 return this._cancelled; 216 }; 217 218 AjxPostRequest.prototype.cancelTimeout = 219 function() { 220 AjxTimedAction.cancelAction(this._timeoutId); 221 }; 222 223 AjxPostRequest.prototype.cancel = 224 function() { 225 this._cancelled = true; 226 }; 227