Source: jsxc.lib.xmpp.httpUpload.js

  1. /**
  2. * Implements Http File Upload (XEP-0363)
  3. *
  4. * @namespace jsxc.xmpp.httpUpload
  5. * @see {@link http://xmpp.org/extensions/xep-0363.html}
  6. */
  7. jsxc.xmpp.httpUpload = {
  8. conn: null,
  9. ready: false,
  10. CONST: {
  11. NS: {
  12. HTTPUPLOAD: 'urn:xmpp:http:upload'
  13. }
  14. }
  15. };
  16. /**
  17. * Set up http file upload.
  18. *
  19. * @memberOf jsxc.xmpp.httpUpload
  20. * @param {Object} o options
  21. */
  22. jsxc.xmpp.httpUpload.init = function(o) {
  23. var self = jsxc.xmpp.httpUpload;
  24. self.conn = jsxc.xmpp.conn;
  25. var fileTransferOptions = jsxc.options.get('fileTransfer') || {};
  26. var options = o || jsxc.options.get('httpUpload');
  27. if (!fileTransferOptions.httpUpload.enable) {
  28. jsxc.debug('http upload disabled');
  29. jsxc.options.set('httpUpload', false);
  30. return;
  31. }
  32. if (options && options.server) {
  33. self.ready = true;
  34. return;
  35. }
  36. var caps = jsxc.xmpp.conn.caps;
  37. var domain = jsxc.xmpp.conn.domain;
  38. if (!caps || !domain || typeof caps._knownCapabilities[caps._jidVerIndex[domain]] === 'undefined') {
  39. jsxc.debug('Waiting for server capabilities');
  40. $(document).on('caps.strophe', function onCaps(ev, from) {
  41. if (from !== domain) {
  42. return;
  43. }
  44. self.init();
  45. $(document).off('caps.strophe', onCaps);
  46. });
  47. return;
  48. }
  49. self.discoverUploadService();
  50. };
  51. /**
  52. * Discover upload service for http upload.
  53. *
  54. * @memberOf jsxc.xmpp.httpUpload
  55. */
  56. jsxc.xmpp.httpUpload.discoverUploadService = function() {
  57. var self = jsxc.xmpp.httpUpload;
  58. var domain = self.conn.domain;
  59. jsxc.debug('discover http upload service');
  60. if (jsxc.xmpp.conn.caps.hasFeatureByJid(domain, self.CONST.NS.HTTPUPLOAD)) {
  61. self.queryItemForUploadService(domain);
  62. }
  63. self.conn.disco.items(domain, null, function(items) {
  64. $(items).find('item').each(function() {
  65. var jid = $(this).attr('jid');
  66. if (self.ready) {
  67. // abort, because we already found a service
  68. return false;
  69. }
  70. self.queryItemForUploadService(jid);
  71. });
  72. });
  73. };
  74. /**
  75. * Query item for upload service.
  76. *
  77. * @param {String} jid
  78. * @param {Function} cb Callback on success
  79. * @memberOf jsxc.xmpp.httpUpload
  80. */
  81. jsxc.xmpp.httpUpload.queryItemForUploadService = function(jid, cb) {
  82. var self = jsxc.xmpp.httpUpload;
  83. jsxc.debug('query ' + jid + ' for upload service');
  84. self.conn.disco.info(jid, null, function(info) {
  85. var httpUploadFeature = $(info).find('feature[var="' + self.CONST.NS.HTTPUPLOAD + '"]');
  86. var httpUploadMaxSize = $(info).find('field[var="max-file-size"]');
  87. if (httpUploadFeature.length > 0) {
  88. jsxc.debug('http upload service found on ' + jid);
  89. jsxc.options.set('httpUpload', {
  90. server: jid,
  91. name: $(info).find('identity').attr('name'),
  92. maxSize: parseInt(httpUploadMaxSize.text()) || -1
  93. });
  94. self.ready = true;
  95. if (typeof cb === 'function') {
  96. cb.call(info);
  97. }
  98. }
  99. });
  100. };
  101. /**
  102. * Upload file and send link to peer.
  103. *
  104. * @memberOf jsxc.xmpp.httpUpload
  105. * @param {File} file
  106. * @param {Message} message Preview message
  107. */
  108. jsxc.xmpp.httpUpload.sendFile = function(file, message) {
  109. jsxc.debug('Send file via http upload');
  110. var self = jsxc.xmpp.httpUpload;
  111. // even if the link is encrypted the file isn't
  112. message.encrypted = false;
  113. self.requestSlot(file, function(data) {
  114. if (!data) {
  115. // general error
  116. jsxc.warn('Unknown error occured. Please check the debug log.');
  117. } else if (data.error) {
  118. // specific error
  119. jsxc.warn('The xmpp server responded with an error of the type "' + data.error.type + '"');
  120. message.getDOM().remove();
  121. jsxc.gui.window.postMessage({
  122. bid: message.bid,
  123. direction: jsxc.Message.SYS,
  124. msg: data.error.text
  125. });
  126. message.delete();
  127. } else if (data.get && data.put) {
  128. jsxc.debug('slot received, start upload to ' + data.put);
  129. self.uploadFile(data.put, file, message, function() {
  130. var attachment = message.attachment;
  131. var metaString = attachment.type + '|' + attachment.size + '|' + attachment.name;
  132. var a = $('<a>');
  133. a.attr('href', data.get);
  134. attachment.data = data.get;
  135. if (attachment.thumbnail) {
  136. var img = $('<img>');
  137. img.attr('alt', 'Preview:' + metaString);
  138. img.attr('src', attachment.thumbnail);
  139. a.prepend(img);
  140. } else {
  141. a.text(metaString);
  142. }
  143. message.msg = data.get;
  144. message.htmlMsg = $('<span>').append(a).html();
  145. message.type = jsxc.Message.HTML;
  146. jsxc.gui.window.postMessage(message);
  147. });
  148. }
  149. });
  150. };
  151. /**
  152. * Upload the given file to the given url.
  153. *
  154. * @memberOf jsxc.xmpp.httpUpload
  155. * @param {String} url upload url
  156. * @param {File} file
  157. * @param {Message} message preview message
  158. * @param {Function} success_cb callback on successful transition
  159. */
  160. jsxc.xmpp.httpUpload.uploadFile = function(url, file, message, success_cb) {
  161. $.ajax({
  162. url: url,
  163. type: 'PUT',
  164. contentType: 'application/octet-stream',
  165. data: file,
  166. processData: false,
  167. xhr: function() {
  168. var xhr = $.ajaxSettings.xhr();
  169. // track upload progress
  170. xhr.upload.onprogress = function(ev) {
  171. if (ev.lengthComputable) {
  172. jsxc.gui.window.updateProgress(message, ev.loaded, ev.total);
  173. }
  174. };
  175. return xhr;
  176. },
  177. success: function() {
  178. jsxc.debug('file successful uploaded');
  179. // In case that upload progress is not available, inform user
  180. jsxc.gui.window.updateProgress(message, 1, 1);
  181. if (success_cb) {
  182. success_cb();
  183. }
  184. },
  185. error: function() {
  186. jsxc.warn('error while uploading file to ' + url);
  187. message.error = 'Could not upload file';
  188. jsxc.gui.window.postMessage(message);
  189. }
  190. });
  191. };
  192. /**
  193. * Request upload slot.
  194. *
  195. * @memberOf jsxc.xmpp.httpUpload
  196. * @param {File} file
  197. * @param {Function} cb Callback after finished request
  198. */
  199. jsxc.xmpp.httpUpload.requestSlot = function(file, cb) {
  200. var self = jsxc.xmpp.httpUpload;
  201. var options = jsxc.options.get('httpUpload');
  202. if (!options || !options.server) {
  203. jsxc.warn('could not request upload slot, because I am not aware of a server or http upload is disabled');
  204. return;
  205. }
  206. var iq = $iq({
  207. to: options.server,
  208. type: 'get'
  209. }).c('request', {
  210. xmlns: self.CONST.NS.HTTPUPLOAD
  211. }).c('filename').t(file.name)
  212. .up()
  213. .c('size').t(file.size);
  214. self.conn.sendIQ(iq, function(stanza) {
  215. self.successfulRequestSlotCB(stanza, cb);
  216. }, function(stanza) {
  217. self.failedRequestSlotCB(stanza, cb);
  218. });
  219. };
  220. /**
  221. * Process successful response to slot request.
  222. *
  223. * @memberOf jsxc.xmpp.httpUpload
  224. * @param {String} stanza
  225. * @param {Function} cb
  226. */
  227. jsxc.xmpp.httpUpload.successfulRequestSlotCB = function(stanza, cb) {
  228. var self = jsxc.xmpp.httpUpload;
  229. var slot = $(stanza).find('slot[xmlns="' + self.CONST.NS.HTTPUPLOAD + '"]');
  230. if (slot.length > 0) {
  231. var put = slot.find('put').text();
  232. var get = slot.find('get').text();
  233. cb({
  234. put: put,
  235. get: get
  236. });
  237. } else {
  238. self.failedRequestSlotCB(stanza, cb);
  239. }
  240. };
  241. /**
  242. * Process failed response to slot request.
  243. *
  244. * @memberOf jsxc.xmpp.httpUpload
  245. * @param {String} stanza
  246. * @param {Function} cb
  247. */
  248. jsxc.xmpp.httpUpload.failedRequestSlotCB = function(stanza, cb) {
  249. if ($(stanza).find('error').length <= 0) {
  250. jsxc.warn('response does not contain a slot element');
  251. cb();
  252. return;
  253. }
  254. var error = {
  255. type: $(stanza).find('error').attr('type') || 'unknown',
  256. text: $(stanza).find('error text').text()
  257. };
  258. if ($(stanza).find('error not-acceptable')) {
  259. error.reason = 'not-acceptable';
  260. } else if ($(stanza).find('error resource-constraint')) {
  261. error.reason = 'resource-constraint';
  262. } else if ($(stanza).find('error not-allowed')) {
  263. error.reason = 'not-allowed';
  264. }
  265. cb({
  266. error: error
  267. });
  268. };
  269. $(document).on('stateUIChange.jsxc', function(ev, state) {
  270. if (state === jsxc.CONST.UISTATE.INITIATING) {
  271. jsxc.xmpp.httpUpload.init();
  272. }
  273. });