Source: utils.js

  1. /**
  2. * @namespace utils
  3. * @description Common utils module
  4. * @author Andrew D.Laptev <a.d.laptev@gmail.com>
  5. * @licence MIT
  6. */
  7. const xml2js = require('xml2js'),
  8. numberRE = /^-?([1-9]\d*|0)(\.\d*)?$/,
  9. dateRE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(.\d+)?Z$/,
  10. prefixMatch = /(?!xmlns)^.*:/;
  11. /**
  12. * Parse SOAP object to pretty JS-object
  13. * @param {object} xml
  14. * @returns {object}
  15. */
  16. const linerase = function(xml) {
  17. if (Array.isArray(xml)) {
  18. if (xml.length > 1) {
  19. return xml.map(linerase);
  20. } else {
  21. xml = xml[0];
  22. }
  23. }
  24. if (typeof xml === 'object') {
  25. var obj = {};
  26. Object.keys(xml).forEach(function(key) {
  27. obj[key] = linerase(xml[key]);
  28. });
  29. return obj;
  30. } else {
  31. if (xml === 'true') { return true; }
  32. if (xml === 'false') { return false; }
  33. if (numberRE.test(xml)) { return parseFloat(xml); }
  34. if (dateRE.test(xml)) { return new Date(xml); }
  35. return xml;
  36. }
  37. };
  38. /**
  39. * @callback ParseSOAPStringCallback
  40. * @property {?Error} error
  41. * @property {object} SOAP response
  42. * @property {string} raw XML
  43. * @property {number} HTTP Status Code
  44. */
  45. /**
  46. * Parse SOAP response
  47. * @param {string} xml
  48. * @param {ParseSOAPStringCallback} callback
  49. * @param {number} statusCode. This is passed in so it can be passed back out to the callback
  50. */
  51. const parseSOAPString = function(xml, callback, statusCode) {
  52. /* Filter out xml name spaces */
  53. xml = xml.replace(/xmlns([^=]*?)=(".*?")/g, '');
  54. try {
  55. xml2js.parseString(
  56. xml, {
  57. tagNameProcessors: [function(str) {
  58. str = str.replace(prefixMatch, '');
  59. var secondLetter = str.charAt(1);
  60. if (secondLetter && secondLetter.toUpperCase() !== secondLetter) {
  61. return str.charAt(0).toLowerCase() + str.slice(1);
  62. } else {
  63. return str;
  64. }
  65. }]
  66. },
  67. function(err, result) {
  68. if (!result || !result['envelope'] || !result['envelope']['body']) {
  69. callback(new Error('Wrong ONVIF SOAP response'), null, xml, statusCode);
  70. } else {
  71. if (!err && result['envelope']['body'][0]['fault']) {
  72. var fault = result['envelope']['body'][0]['fault'][0];
  73. var reason;
  74. try {
  75. if (fault.reason[0].text[0]._) {
  76. reason = fault.reason[0].text[0]._;
  77. }
  78. } catch (e) {
  79. reason = '';
  80. }
  81. if (!reason) {
  82. try {
  83. reason = JSON.stringify(linerase(fault.code[0]));
  84. } catch (e) {
  85. reason = '';
  86. }
  87. }
  88. var detail = '';
  89. try {
  90. detail = fault.detail[0].text[0];
  91. } catch (e) {
  92. detail = '';
  93. }
  94. // console.error('Fault:', reason, detail);
  95. err = new Error('ONVIF SOAP Fault: ' + (reason) + (detail));
  96. }
  97. callback(err, result['envelope']['body'], xml, statusCode);
  98. }
  99. }
  100. );
  101. } catch (err) {
  102. callback(err, '', xml, statusCode);
  103. }
  104. };
  105. const s4 = function() {
  106. return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
  107. };
  108. /**
  109. * Generate GUID
  110. * @returns {string}
  111. */
  112. const guid = function() {
  113. return (s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4());
  114. };
  115. module.exports = {
  116. linerase: linerase,
  117. parseSOAPString: parseSOAPString,
  118. guid: guid
  119. };