{"version":3,"sources":["../node_modules/pako/index.js","../node_modules/pako/lib/utils/common.js","../node_modules/pako/lib/zlib/messages.js","../node_modules/pako/lib/zlib/adler32.js","../node_modules/pako/lib/zlib/crc32.js","../node_modules/pako/lib/utils/strings.js","../node_modules/pako/lib/zlib/zstream.js","../node_modules/pako/lib/zlib/constants.js","../node_modules/pako/lib/deflate.js","../node_modules/pako/lib/zlib/deflate.js","../node_modules/pako/lib/zlib/trees.js","../node_modules/pako/lib/inflate.js","../node_modules/pako/lib/zlib/inflate.js","../node_modules/pako/lib/zlib/inffast.js","../node_modules/pako/lib/zlib/inftrees.js","../node_modules/pako/lib/zlib/gzheader.js"],"names":["pako","assign","require","module","exports","TYPED_OK","Uint8Array","Uint16Array","Int32Array","_has","obj","key","Object","prototype","hasOwnProperty","call","sources","Array","slice","arguments","length","source","shift","TypeError","p","shrinkBuf","buf","size","subarray","fnTyped","arraySet","dest","src","src_offs","len","dest_offs","set","i","flattenChunks","chunks","l","pos","chunk","result","fnUntyped","concat","apply","setTyped","on","Buf8","Buf16","Buf32","2","1","0","adler","s1","s2","n","crcTable","c","table","k","makeTable","crc","t","end","utils","STR_APPLY_OK","STR_APPLY_UIA_OK","String","fromCharCode","__","_utf8len","q","buf2binstring","string2buf","str","c2","m_pos","str_len","buf_len","charCodeAt","binstring2buf","buf2string","max","out","c_len","utf16buf","utf8border","this","input","next_in","avail_in","total_in","output","next_out","avail_out","total_out","msg","state","data_type","Z_NO_FLUSH","Z_PARTIAL_FLUSH","Z_SYNC_FLUSH","Z_FULL_FLUSH","Z_FINISH","Z_BLOCK","Z_TREES","Z_OK","Z_STREAM_END","Z_NEED_DICT","Z_ERRNO","Z_STREAM_ERROR","Z_DATA_ERROR","Z_BUF_ERROR","Z_NO_COMPRESSION","Z_BEST_SPEED","Z_BEST_COMPRESSION","Z_DEFAULT_COMPRESSION","Z_FILTERED","Z_HUFFMAN_ONLY","Z_RLE","Z_FIXED","Z_DEFAULT_STRATEGY","Z_BINARY","Z_TEXT","Z_UNKNOWN","Z_DEFLATED","zlib_deflate","strings","ZStream","toString","Deflate","options","level","method","chunkSize","windowBits","memLevel","strategy","to","opt","raw","gzip","err","ended","strm","status","deflateInit2","Error","header","deflateSetHeader","dictionary","dict","deflateSetDictionary","_dict_set","deflate","deflator","push","data","mode","_mode","onEnd","onData","deflateEnd","join","deflateRaw","configuration_table","trees","adler32","crc32","MAX_MEM_LEVEL","MAX_WBITS","DEF_MEM_LEVEL","L_CODES","LITERALS","D_CODES","BL_CODES","HEAP_SIZE","MAX_BITS","MIN_MATCH","MAX_MATCH","MIN_LOOKAHEAD","PRESET_DICT","INIT_STATE","EXTRA_STATE","NAME_STATE","COMMENT_STATE","HCRC_STATE","BUSY_STATE","FINISH_STATE","BS_NEED_MORE","BS_BLOCK_DONE","BS_FINISH_STARTED","BS_FINISH_DONE","OS_CODE","errorCode","rank","f","zero","flush_pending","s","pending","pending_buf","pending_out","flush_block_only","last","_tr_flush_block","block_start","strstart","put_byte","b","putShortMSB","read_buf","start","wrap","longest_match","cur_match","match","chain_length","max_chain_length","scan","best_len","prev_length","nice_match","limit","w_size","_win","window","wmask","w_mask","prev","strend","scan_end1","scan_end","good_match","lookahead","match_start","fill_window","m","more","_w_size","window_size","hash_size","head","insert","ins_h","hash_shift","hash_mask","deflate_fast","flush","hash_head","bflush","match_length","_tr_tally","max_lazy_match","last_lit","deflate_slow","max_insert","prev_match","match_available","Config","good_length","max_lazy","nice_length","max_chain","func","DeflateState","pending_buf_size","gzhead","gzindex","last_flush","w_bits","hash_bits","dyn_ltree","dyn_dtree","bl_tree","l_desc","d_desc","bl_desc","bl_count","heap","heap_len","heap_max","depth","l_buf","lit_bufsize","d_buf","opt_len","static_len","matches","bi_buf","bi_valid","deflateResetKeep","_tr_init","deflateReset","ret","max_block_size","max_start","deflateInit","old_flush","beg","val","text","hcrc","extra","name","comment","time","os","bstate","deflate_huff","deflate_rle","_tr_align","_tr_stored_block","avail","next","tmpDict","dictLength","deflateInfo","STORED_BLOCK","STATIC_TREES","DYN_TREES","LENGTH_CODES","Buf_size","MAX_BL_BITS","END_BLOCK","REP_3_6","REPZ_3_10","REPZ_11_138","extra_lbits","extra_dbits","extra_blbits","bl_order","static_ltree","static_dtree","_dist_code","_length_code","base_length","static_l_desc","static_d_desc","static_bl_desc","base_dist","StaticTreeDesc","static_tree","extra_bits","extra_base","elems","max_length","has_stree","TreeDesc","dyn_tree","stat_desc","max_code","d_code","dist","put_short","w","send_bits","value","send_code","tree","bi_reverse","code","res","gen_codes","bits","next_code","init_block","bi_windup","smaller","_n2","_m2","pqdownheap","v","j","compress_block","ltree","dtree","lc","lx","build_tree","desc","node","stree","h","xbits","base","overflow","gen_bitlen","scan_tree","curlen","prevlen","nextlen","count","max_count","min_count","send_tree","static_init_done","stored_len","copy_block","tr_static_init","opt_lenb","static_lenb","max_blindex","black_mask","detect_data_type","build_bl_tree","lcodes","dcodes","blcodes","send_all_trees","bi_flush","zlib_inflate","GZheader","Inflate","inflateInit2","inflateGetHeader","inflateSetDictionary","inflate","inflator","next_out_utf8","tail","utf8str","allowBufError","inflateEnd","inflateRaw","ungzip","inflate_fast","inflate_table","CODES","LENS","DISTS","Z_MEM_ERROR","HEAD","FLAGS","TIME","OS","EXLEN","EXTRA","NAME","COMMENT","HCRC","DICTID","DICT","TYPE","TYPEDO","STORED","COPY_","COPY","TABLE","LENLENS","CODELENS","LEN_","LEN","LENEXT","DIST","DISTEXT","MATCH","LIT","CHECK","LENGTH","DONE","BAD","MEM","SYNC","ENOUGH_LENS","ENOUGH_DISTS","DEF_WBITS","zswap32","InflateState","havedict","flags","dmax","check","total","wbits","wsize","whave","wnext","hold","offset","lencode","distcode","lenbits","distbits","ncode","nlen","ndist","have","lens","work","lendyn","distdyn","sane","back","was","inflateResetKeep","inflateReset","inflateReset2","lenfix","distfix","virgin","fixedtables","sym","updatewindow","copy","inflateInit","put","left","_in","_out","from","from_source","here_bits","here_op","here_val","last_bits","last_op","last_val","opts","here","hbuf","order","inf_leave","done","xflags","extra_len","inflateInfo","s_window","lcode","dcode","lmask","dmask","op","top","dolen","dodist","lbase","lext","dbase","dext","type","lens_index","codes","table_index","incr","fill","low","mask","min","root","curr","drop","used","huff","base_index","MAXBITS","offs","extra_index"],"mappings":"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","file":"static/js/3.f137faca.chunk.js","sourcesContent":["// Top level file is just a mixin of submodules & constants\n'use strict';\n\nvar assign    = require('./lib/utils/common').assign;\n\nvar deflate   = require('./lib/deflate');\nvar inflate   = require('./lib/inflate');\nvar constants = require('./lib/zlib/constants');\n\nvar pako = {};\n\nassign(pako, deflate, inflate, constants);\n\nmodule.exports = pako;\n","'use strict';\n\n\nvar TYPED_OK =  (typeof Uint8Array !== 'undefined') &&\n                (typeof Uint16Array !== 'undefined') &&\n                (typeof Int32Array !== 'undefined');\n\nfunction _has(obj, key) {\n  return Object.prototype.hasOwnProperty.call(obj, key);\n}\n\nexports.assign = function (obj /*from1, from2, from3, ...*/) {\n  var sources = Array.prototype.slice.call(arguments, 1);\n  while (sources.length) {\n    var source = sources.shift();\n    if (!source) { continue; }\n\n    if (typeof source !== 'object') {\n      throw new TypeError(source + 'must be non-object');\n    }\n\n    for (var p in source) {\n      if (_has(source, p)) {\n        obj[p] = source[p];\n      }\n    }\n  }\n\n  return obj;\n};\n\n\n// reduce buffer size, avoiding mem copy\nexports.shrinkBuf = function (buf, size) {\n  if (buf.length === size) { return buf; }\n  if (buf.subarray) { return buf.subarray(0, size); }\n  buf.length = size;\n  return buf;\n};\n\n\nvar fnTyped = {\n  arraySet: function (dest, src, src_offs, len, dest_offs) {\n    if (src.subarray && dest.subarray) {\n      dest.set(src.subarray(src_offs, src_offs + len), dest_offs);\n      return;\n    }\n    // Fallback to ordinary array\n    for (var i = 0; i < len; i++) {\n      dest[dest_offs + i] = src[src_offs + i];\n    }\n  },\n  // Join array of chunks to single array.\n  flattenChunks: function (chunks) {\n    var i, l, len, pos, chunk, result;\n\n    // calculate data length\n    len = 0;\n    for (i = 0, l = chunks.length; i < l; i++) {\n      len += chunks[i].length;\n    }\n\n    // join chunks\n    result = new Uint8Array(len);\n    pos = 0;\n    for (i = 0, l = chunks.length; i < l; i++) {\n      chunk = chunks[i];\n      result.set(chunk, pos);\n      pos += chunk.length;\n    }\n\n    return result;\n  }\n};\n\nvar fnUntyped = {\n  arraySet: function (dest, src, src_offs, len, dest_offs) {\n    for (var i = 0; i < len; i++) {\n      dest[dest_offs + i] = src[src_offs + i];\n    }\n  },\n  // Join array of chunks to single array.\n  flattenChunks: function (chunks) {\n    return [].concat.apply([], chunks);\n  }\n};\n\n\n// Enable/Disable typed arrays use, for testing\n//\nexports.setTyped = function (on) {\n  if (on) {\n    exports.Buf8  = Uint8Array;\n    exports.Buf16 = Uint16Array;\n    exports.Buf32 = Int32Array;\n    exports.assign(exports, fnTyped);\n  } else {\n    exports.Buf8  = Array;\n    exports.Buf16 = Array;\n    exports.Buf32 = Array;\n    exports.assign(exports, fnUntyped);\n  }\n};\n\nexports.setTyped(TYPED_OK);\n","'use strict';\n\n// (C) 1995-2013 Jean-loup Gailly and Mark Adler\n// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//   claim that you wrote the original software. If you use this software\n//   in a product, an acknowledgment in the product documentation would be\n//   appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//   misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n\nmodule.exports = {\n  2:      'need dictionary',     /* Z_NEED_DICT       2  */\n  1:      'stream end',          /* Z_STREAM_END      1  */\n  0:      '',                    /* Z_OK              0  */\n  '-1':   'file error',          /* Z_ERRNO         (-1) */\n  '-2':   'stream error',        /* Z_STREAM_ERROR  (-2) */\n  '-3':   'data error',          /* Z_DATA_ERROR    (-3) */\n  '-4':   'insufficient memory', /* Z_MEM_ERROR     (-4) */\n  '-5':   'buffer error',        /* Z_BUF_ERROR     (-5) */\n  '-6':   'incompatible version' /* Z_VERSION_ERROR (-6) */\n};\n","'use strict';\n\n// Note: adler32 takes 12% for level 0 and 2% for level 6.\n// It isn't worth it to make additional optimizations as in original.\n// Small size is preferable.\n\n// (C) 1995-2013 Jean-loup Gailly and Mark Adler\n// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//   claim that you wrote the original software. If you use this software\n//   in a product, an acknowledgment in the product documentation would be\n//   appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//   misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n\nfunction adler32(adler, buf, len, pos) {\n  var s1 = (adler & 0xffff) |0,\n      s2 = ((adler >>> 16) & 0xffff) |0,\n      n = 0;\n\n  while (len !== 0) {\n    // Set limit ~ twice less than 5552, to keep\n    // s2 in 31-bits, because we force signed ints.\n    // in other case %= will fail.\n    n = len > 2000 ? 2000 : len;\n    len -= n;\n\n    do {\n      s1 = (s1 + buf[pos++]) |0;\n      s2 = (s2 + s1) |0;\n    } while (--n);\n\n    s1 %= 65521;\n    s2 %= 65521;\n  }\n\n  return (s1 | (s2 << 16)) |0;\n}\n\n\nmodule.exports = adler32;\n","'use strict';\n\n// Note: we can't get significant speed boost here.\n// So write code to minimize size - no pregenerated tables\n// and array tools dependencies.\n\n// (C) 1995-2013 Jean-loup Gailly and Mark Adler\n// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//   claim that you wrote the original software. If you use this software\n//   in a product, an acknowledgment in the product documentation would be\n//   appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//   misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n\n// Use ordinary array, since untyped makes no boost here\nfunction makeTable() {\n  var c, table = [];\n\n  for (var n = 0; n < 256; n++) {\n    c = n;\n    for (var k = 0; k < 8; k++) {\n      c = ((c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1));\n    }\n    table[n] = c;\n  }\n\n  return table;\n}\n\n// Create table on load. Just 255 signed longs. Not a problem.\nvar crcTable = makeTable();\n\n\nfunction crc32(crc, buf, len, pos) {\n  var t = crcTable,\n      end = pos + len;\n\n  crc ^= -1;\n\n  for (var i = pos; i < end; i++) {\n    crc = (crc >>> 8) ^ t[(crc ^ buf[i]) & 0xFF];\n  }\n\n  return (crc ^ (-1)); // >>> 0;\n}\n\n\nmodule.exports = crc32;\n","// String encode/decode helpers\n'use strict';\n\n\nvar utils = require('./common');\n\n\n// Quick check if we can use fast array to bin string conversion\n//\n// - apply(Array) can fail on Android 2.2\n// - apply(Uint8Array) can fail on iOS 5.1 Safari\n//\nvar STR_APPLY_OK = true;\nvar STR_APPLY_UIA_OK = true;\n\ntry { String.fromCharCode.apply(null, [ 0 ]); } catch (__) { STR_APPLY_OK = false; }\ntry { String.fromCharCode.apply(null, new Uint8Array(1)); } catch (__) { STR_APPLY_UIA_OK = false; }\n\n\n// Table with utf8 lengths (calculated by first byte of sequence)\n// Note, that 5 & 6-byte values and some 4-byte values can not be represented in JS,\n// because max possible codepoint is 0x10ffff\nvar _utf8len = new utils.Buf8(256);\nfor (var q = 0; q < 256; q++) {\n  _utf8len[q] = (q >= 252 ? 6 : q >= 248 ? 5 : q >= 240 ? 4 : q >= 224 ? 3 : q >= 192 ? 2 : 1);\n}\n_utf8len[254] = _utf8len[254] = 1; // Invalid sequence start\n\n\n// convert string to array (typed, when possible)\nexports.string2buf = function (str) {\n  var buf, c, c2, m_pos, i, str_len = str.length, buf_len = 0;\n\n  // count binary size\n  for (m_pos = 0; m_pos < str_len; m_pos++) {\n    c = str.charCodeAt(m_pos);\n    if ((c & 0xfc00) === 0xd800 && (m_pos + 1 < str_len)) {\n      c2 = str.charCodeAt(m_pos + 1);\n      if ((c2 & 0xfc00) === 0xdc00) {\n        c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00);\n        m_pos++;\n      }\n    }\n    buf_len += c < 0x80 ? 1 : c < 0x800 ? 2 : c < 0x10000 ? 3 : 4;\n  }\n\n  // allocate buffer\n  buf = new utils.Buf8(buf_len);\n\n  // convert\n  for (i = 0, m_pos = 0; i < buf_len; m_pos++) {\n    c = str.charCodeAt(m_pos);\n    if ((c & 0xfc00) === 0xd800 && (m_pos + 1 < str_len)) {\n      c2 = str.charCodeAt(m_pos + 1);\n      if ((c2 & 0xfc00) === 0xdc00) {\n        c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00);\n        m_pos++;\n      }\n    }\n    if (c < 0x80) {\n      /* one byte */\n      buf[i++] = c;\n    } else if (c < 0x800) {\n      /* two bytes */\n      buf[i++] = 0xC0 | (c >>> 6);\n      buf[i++] = 0x80 | (c & 0x3f);\n    } else if (c < 0x10000) {\n      /* three bytes */\n      buf[i++] = 0xE0 | (c >>> 12);\n      buf[i++] = 0x80 | (c >>> 6 & 0x3f);\n      buf[i++] = 0x80 | (c & 0x3f);\n    } else {\n      /* four bytes */\n      buf[i++] = 0xf0 | (c >>> 18);\n      buf[i++] = 0x80 | (c >>> 12 & 0x3f);\n      buf[i++] = 0x80 | (c >>> 6 & 0x3f);\n      buf[i++] = 0x80 | (c & 0x3f);\n    }\n  }\n\n  return buf;\n};\n\n// Helper (used in 2 places)\nfunction buf2binstring(buf, len) {\n  // On Chrome, the arguments in a function call that are allowed is `65534`.\n  // If the length of the buffer is smaller than that, we can use this optimization,\n  // otherwise we will take a slower path.\n  if (len < 65534) {\n    if ((buf.subarray && STR_APPLY_UIA_OK) || (!buf.subarray && STR_APPLY_OK)) {\n      return String.fromCharCode.apply(null, utils.shrinkBuf(buf, len));\n    }\n  }\n\n  var result = '';\n  for (var i = 0; i < len; i++) {\n    result += String.fromCharCode(buf[i]);\n  }\n  return result;\n}\n\n\n// Convert byte array to binary string\nexports.buf2binstring = function (buf) {\n  return buf2binstring(buf, buf.length);\n};\n\n\n// Convert binary string (typed, when possible)\nexports.binstring2buf = function (str) {\n  var buf = new utils.Buf8(str.length);\n  for (var i = 0, len = buf.length; i < len; i++) {\n    buf[i] = str.charCodeAt(i);\n  }\n  return buf;\n};\n\n\n// convert array to string\nexports.buf2string = function (buf, max) {\n  var i, out, c, c_len;\n  var len = max || buf.length;\n\n  // Reserve max possible length (2 words per char)\n  // NB: by unknown reasons, Array is significantly faster for\n  //     String.fromCharCode.apply than Uint16Array.\n  var utf16buf = new Array(len * 2);\n\n  for (out = 0, i = 0; i < len;) {\n    c = buf[i++];\n    // quick process ascii\n    if (c < 0x80) { utf16buf[out++] = c; continue; }\n\n    c_len = _utf8len[c];\n    // skip 5 & 6 byte codes\n    if (c_len > 4) { utf16buf[out++] = 0xfffd; i += c_len - 1; continue; }\n\n    // apply mask on first byte\n    c &= c_len === 2 ? 0x1f : c_len === 3 ? 0x0f : 0x07;\n    // join the rest\n    while (c_len > 1 && i < len) {\n      c = (c << 6) | (buf[i++] & 0x3f);\n      c_len--;\n    }\n\n    // terminated by end of string?\n    if (c_len > 1) { utf16buf[out++] = 0xfffd; continue; }\n\n    if (c < 0x10000) {\n      utf16buf[out++] = c;\n    } else {\n      c -= 0x10000;\n      utf16buf[out++] = 0xd800 | ((c >> 10) & 0x3ff);\n      utf16buf[out++] = 0xdc00 | (c & 0x3ff);\n    }\n  }\n\n  return buf2binstring(utf16buf, out);\n};\n\n\n// Calculate max possible position in utf8 buffer,\n// that will not break sequence. If that's not possible\n// - (very small limits) return max size as is.\n//\n// buf[] - utf8 bytes array\n// max   - length limit (mandatory);\nexports.utf8border = function (buf, max) {\n  var pos;\n\n  max = max || buf.length;\n  if (max > buf.length) { max = buf.length; }\n\n  // go back from last position, until start of sequence found\n  pos = max - 1;\n  while (pos >= 0 && (buf[pos] & 0xC0) === 0x80) { pos--; }\n\n  // Very small and broken sequence,\n  // return max, because we should return something anyway.\n  if (pos < 0) { return max; }\n\n  // If we came to start of buffer - that means buffer is too small,\n  // return max too.\n  if (pos === 0) { return max; }\n\n  return (pos + _utf8len[buf[pos]] > max) ? pos : max;\n};\n","'use strict';\n\n// (C) 1995-2013 Jean-loup Gailly and Mark Adler\n// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//   claim that you wrote the original software. If you use this software\n//   in a product, an acknowledgment in the product documentation would be\n//   appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//   misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n\nfunction ZStream() {\n  /* next input byte */\n  this.input = null; // JS specific, because we have no pointers\n  this.next_in = 0;\n  /* number of bytes available at input */\n  this.avail_in = 0;\n  /* total number of input bytes read so far */\n  this.total_in = 0;\n  /* next output byte should be put there */\n  this.output = null; // JS specific, because we have no pointers\n  this.next_out = 0;\n  /* remaining free space at output */\n  this.avail_out = 0;\n  /* total number of bytes output so far */\n  this.total_out = 0;\n  /* last error message, NULL if no error */\n  this.msg = ''/*Z_NULL*/;\n  /* not visible by applications */\n  this.state = null;\n  /* best guess about the data type: binary or text */\n  this.data_type = 2/*Z_UNKNOWN*/;\n  /* adler32 value of the uncompressed data */\n  this.adler = 0;\n}\n\nmodule.exports = ZStream;\n","'use strict';\n\n// (C) 1995-2013 Jean-loup Gailly and Mark Adler\n// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//   claim that you wrote the original software. If you use this software\n//   in a product, an acknowledgment in the product documentation would be\n//   appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//   misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n\nmodule.exports = {\n\n  /* Allowed flush values; see deflate() and inflate() below for details */\n  Z_NO_FLUSH:         0,\n  Z_PARTIAL_FLUSH:    1,\n  Z_SYNC_FLUSH:       2,\n  Z_FULL_FLUSH:       3,\n  Z_FINISH:           4,\n  Z_BLOCK:            5,\n  Z_TREES:            6,\n\n  /* Return codes for the compression/decompression functions. Negative values\n  * are errors, positive values are used for special but normal events.\n  */\n  Z_OK:               0,\n  Z_STREAM_END:       1,\n  Z_NEED_DICT:        2,\n  Z_ERRNO:           -1,\n  Z_STREAM_ERROR:    -2,\n  Z_DATA_ERROR:      -3,\n  //Z_MEM_ERROR:     -4,\n  Z_BUF_ERROR:       -5,\n  //Z_VERSION_ERROR: -6,\n\n  /* compression levels */\n  Z_NO_COMPRESSION:         0,\n  Z_BEST_SPEED:             1,\n  Z_BEST_COMPRESSION:       9,\n  Z_DEFAULT_COMPRESSION:   -1,\n\n\n  Z_FILTERED:               1,\n  Z_HUFFMAN_ONLY:           2,\n  Z_RLE:                    3,\n  Z_FIXED:                  4,\n  Z_DEFAULT_STRATEGY:       0,\n\n  /* Possible values of the data_type field (though see inflate()) */\n  Z_BINARY:                 0,\n  Z_TEXT:                   1,\n  //Z_ASCII:                1, // = Z_TEXT (deprecated)\n  Z_UNKNOWN:                2,\n\n  /* The deflate compression method */\n  Z_DEFLATED:               8\n  //Z_NULL:                 null // Use -1 or null inline, depending on var type\n};\n","'use strict';\n\n\nvar zlib_deflate = require('./zlib/deflate');\nvar utils        = require('./utils/common');\nvar strings      = require('./utils/strings');\nvar msg          = require('./zlib/messages');\nvar ZStream      = require('./zlib/zstream');\n\nvar toString = Object.prototype.toString;\n\n/* Public constants ==========================================================*/\n/* ===========================================================================*/\n\nvar Z_NO_FLUSH      = 0;\nvar Z_FINISH        = 4;\n\nvar Z_OK            = 0;\nvar Z_STREAM_END    = 1;\nvar Z_SYNC_FLUSH    = 2;\n\nvar Z_DEFAULT_COMPRESSION = -1;\n\nvar Z_DEFAULT_STRATEGY    = 0;\n\nvar Z_DEFLATED  = 8;\n\n/* ===========================================================================*/\n\n\n/**\n * class Deflate\n *\n * Generic JS-style wrapper for zlib calls. If you don't need\n * streaming behaviour - use more simple functions: [[deflate]],\n * [[deflateRaw]] and [[gzip]].\n **/\n\n/* internal\n * Deflate.chunks -> Array\n *\n * Chunks of output data, if [[Deflate#onData]] not overridden.\n **/\n\n/**\n * Deflate.result -> Uint8Array|Array\n *\n * Compressed result, generated by default [[Deflate#onData]]\n * and [[Deflate#onEnd]] handlers. Filled after you push last chunk\n * (call [[Deflate#push]] with `Z_FINISH` / `true` param)  or if you\n * push a chunk with explicit flush (call [[Deflate#push]] with\n * `Z_SYNC_FLUSH` param).\n **/\n\n/**\n * Deflate.err -> Number\n *\n * Error code after deflate finished. 0 (Z_OK) on success.\n * You will not need it in real life, because deflate errors\n * are possible only on wrong options or bad `onData` / `onEnd`\n * custom handlers.\n **/\n\n/**\n * Deflate.msg -> String\n *\n * Error message, if [[Deflate.err]] != 0\n **/\n\n\n/**\n * new Deflate(options)\n * - options (Object): zlib deflate options.\n *\n * Creates new deflator instance with specified params. Throws exception\n * on bad params. Supported options:\n *\n * - `level`\n * - `windowBits`\n * - `memLevel`\n * - `strategy`\n * - `dictionary`\n *\n * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)\n * for more information on these.\n *\n * Additional options, for internal needs:\n *\n * - `chunkSize` - size of generated data chunks (16K by default)\n * - `raw` (Boolean) - do raw deflate\n * - `gzip` (Boolean) - create gzip wrapper\n * - `to` (String) - if equal to 'string', then result will be \"binary string\"\n *    (each char code [0..255])\n * - `header` (Object) - custom header for gzip\n *   - `text` (Boolean) - true if compressed data believed to be text\n *   - `time` (Number) - modification time, unix timestamp\n *   - `os` (Number) - operation system code\n *   - `extra` (Array) - array of bytes with extra data (max 65536)\n *   - `name` (String) - file name (binary string)\n *   - `comment` (String) - comment (binary string)\n *   - `hcrc` (Boolean) - true if header crc should be added\n *\n * ##### Example:\n *\n * ```javascript\n * var pako = require('pako')\n *   , chunk1 = Uint8Array([1,2,3,4,5,6,7,8,9])\n *   , chunk2 = Uint8Array([10,11,12,13,14,15,16,17,18,19]);\n *\n * var deflate = new pako.Deflate({ level: 3});\n *\n * deflate.push(chunk1, false);\n * deflate.push(chunk2, true);  // true -> last chunk\n *\n * if (deflate.err) { throw new Error(deflate.err); }\n *\n * console.log(deflate.result);\n * ```\n **/\nfunction Deflate(options) {\n  if (!(this instanceof Deflate)) return new Deflate(options);\n\n  this.options = utils.assign({\n    level: Z_DEFAULT_COMPRESSION,\n    method: Z_DEFLATED,\n    chunkSize: 16384,\n    windowBits: 15,\n    memLevel: 8,\n    strategy: Z_DEFAULT_STRATEGY,\n    to: ''\n  }, options || {});\n\n  var opt = this.options;\n\n  if (opt.raw && (opt.windowBits > 0)) {\n    opt.windowBits = -opt.windowBits;\n  }\n\n  else if (opt.gzip && (opt.windowBits > 0) && (opt.windowBits < 16)) {\n    opt.windowBits += 16;\n  }\n\n  this.err    = 0;      // error code, if happens (0 = Z_OK)\n  this.msg    = '';     // error message\n  this.ended  = false;  // used to avoid multiple onEnd() calls\n  this.chunks = [];     // chunks of compressed data\n\n  this.strm = new ZStream();\n  this.strm.avail_out = 0;\n\n  var status = zlib_deflate.deflateInit2(\n    this.strm,\n    opt.level,\n    opt.method,\n    opt.windowBits,\n    opt.memLevel,\n    opt.strategy\n  );\n\n  if (status !== Z_OK) {\n    throw new Error(msg[status]);\n  }\n\n  if (opt.header) {\n    zlib_deflate.deflateSetHeader(this.strm, opt.header);\n  }\n\n  if (opt.dictionary) {\n    var dict;\n    // Convert data if needed\n    if (typeof opt.dictionary === 'string') {\n      // If we need to compress text, change encoding to utf8.\n      dict = strings.string2buf(opt.dictionary);\n    } else if (toString.call(opt.dictionary) === '[object ArrayBuffer]') {\n      dict = new Uint8Array(opt.dictionary);\n    } else {\n      dict = opt.dictionary;\n    }\n\n    status = zlib_deflate.deflateSetDictionary(this.strm, dict);\n\n    if (status !== Z_OK) {\n      throw new Error(msg[status]);\n    }\n\n    this._dict_set = true;\n  }\n}\n\n/**\n * Deflate#push(data[, mode]) -> Boolean\n * - data (Uint8Array|Array|ArrayBuffer|String): input data. Strings will be\n *   converted to utf8 byte sequence.\n * - mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE modes.\n *   See constants. Skipped or `false` means Z_NO_FLUSH, `true` means Z_FINISH.\n *\n * Sends input data to deflate pipe, generating [[Deflate#onData]] calls with\n * new compressed chunks. Returns `true` on success. The last data block must have\n * mode Z_FINISH (or `true`). That will flush internal pending buffers and call\n * [[Deflate#onEnd]]. For interim explicit flushes (without ending the stream) you\n * can use mode Z_SYNC_FLUSH, keeping the compression context.\n *\n * On fail call [[Deflate#onEnd]] with error code and return false.\n *\n * We strongly recommend to use `Uint8Array` on input for best speed (output\n * array format is detected automatically). Also, don't skip last param and always\n * use the same type in your code (boolean or number). That will improve JS speed.\n *\n * For regular `Array`-s make sure all elements are [0..255].\n *\n * ##### Example\n *\n * ```javascript\n * push(chunk, false); // push one of data chunks\n * ...\n * push(chunk, true);  // push last chunk\n * ```\n **/\nDeflate.prototype.push = function (data, mode) {\n  var strm = this.strm;\n  var chunkSize = this.options.chunkSize;\n  var status, _mode;\n\n  if (this.ended) { return false; }\n\n  _mode = (mode === ~~mode) ? mode : ((mode === true) ? Z_FINISH : Z_NO_FLUSH);\n\n  // Convert data if needed\n  if (typeof data === 'string') {\n    // If we need to compress text, change encoding to utf8.\n    strm.input = strings.string2buf(data);\n  } else if (toString.call(data) === '[object ArrayBuffer]') {\n    strm.input = new Uint8Array(data);\n  } else {\n    strm.input = data;\n  }\n\n  strm.next_in = 0;\n  strm.avail_in = strm.input.length;\n\n  do {\n    if (strm.avail_out === 0) {\n      strm.output = new utils.Buf8(chunkSize);\n      strm.next_out = 0;\n      strm.avail_out = chunkSize;\n    }\n    status = zlib_deflate.deflate(strm, _mode);    /* no bad return value */\n\n    if (status !== Z_STREAM_END && status !== Z_OK) {\n      this.onEnd(status);\n      this.ended = true;\n      return false;\n    }\n    if (strm.avail_out === 0 || (strm.avail_in === 0 && (_mode === Z_FINISH || _mode === Z_SYNC_FLUSH))) {\n      if (this.options.to === 'string') {\n        this.onData(strings.buf2binstring(utils.shrinkBuf(strm.output, strm.next_out)));\n      } else {\n        this.onData(utils.shrinkBuf(strm.output, strm.next_out));\n      }\n    }\n  } while ((strm.avail_in > 0 || strm.avail_out === 0) && status !== Z_STREAM_END);\n\n  // Finalize on the last chunk.\n  if (_mode === Z_FINISH) {\n    status = zlib_deflate.deflateEnd(this.strm);\n    this.onEnd(status);\n    this.ended = true;\n    return status === Z_OK;\n  }\n\n  // callback interim results if Z_SYNC_FLUSH.\n  if (_mode === Z_SYNC_FLUSH) {\n    this.onEnd(Z_OK);\n    strm.avail_out = 0;\n    return true;\n  }\n\n  return true;\n};\n\n\n/**\n * Deflate#onData(chunk) -> Void\n * - chunk (Uint8Array|Array|String): output data. Type of array depends\n *   on js engine support. When string output requested, each chunk\n *   will be string.\n *\n * By default, stores data blocks in `chunks[]` property and glue\n * those in `onEnd`. Override this handler, if you need another behaviour.\n **/\nDeflate.prototype.onData = function (chunk) {\n  this.chunks.push(chunk);\n};\n\n\n/**\n * Deflate#onEnd(status) -> Void\n * - status (Number): deflate status. 0 (Z_OK) on success,\n *   other if not.\n *\n * Called once after you tell deflate that the input stream is\n * complete (Z_FINISH) or should be flushed (Z_SYNC_FLUSH)\n * or if an error happened. By default - join collected chunks,\n * free memory and fill `results` / `err` properties.\n **/\nDeflate.prototype.onEnd = function (status) {\n  // On success - join\n  if (status === Z_OK) {\n    if (this.options.to === 'string') {\n      this.result = this.chunks.join('');\n    } else {\n      this.result = utils.flattenChunks(this.chunks);\n    }\n  }\n  this.chunks = [];\n  this.err = status;\n  this.msg = this.strm.msg;\n};\n\n\n/**\n * deflate(data[, options]) -> Uint8Array|Array|String\n * - data (Uint8Array|Array|String): input data to compress.\n * - options (Object): zlib deflate options.\n *\n * Compress `data` with deflate algorithm and `options`.\n *\n * Supported options are:\n *\n * - level\n * - windowBits\n * - memLevel\n * - strategy\n * - dictionary\n *\n * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)\n * for more information on these.\n *\n * Sugar (options):\n *\n * - `raw` (Boolean) - say that we work with raw stream, if you don't wish to specify\n *   negative windowBits implicitly.\n * - `to` (String) - if equal to 'string', then result will be \"binary string\"\n *    (each char code [0..255])\n *\n * ##### Example:\n *\n * ```javascript\n * var pako = require('pako')\n *   , data = Uint8Array([1,2,3,4,5,6,7,8,9]);\n *\n * console.log(pako.deflate(data));\n * ```\n **/\nfunction deflate(input, options) {\n  var deflator = new Deflate(options);\n\n  deflator.push(input, true);\n\n  // That will never happens, if you don't cheat with options :)\n  if (deflator.err) { throw deflator.msg || msg[deflator.err]; }\n\n  return deflator.result;\n}\n\n\n/**\n * deflateRaw(data[, options]) -> Uint8Array|Array|String\n * - data (Uint8Array|Array|String): input data to compress.\n * - options (Object): zlib deflate options.\n *\n * The same as [[deflate]], but creates raw data, without wrapper\n * (header and adler32 crc).\n **/\nfunction deflateRaw(input, options) {\n  options = options || {};\n  options.raw = true;\n  return deflate(input, options);\n}\n\n\n/**\n * gzip(data[, options]) -> Uint8Array|Array|String\n * - data (Uint8Array|Array|String): input data to compress.\n * - options (Object): zlib deflate options.\n *\n * The same as [[deflate]], but create gzip wrapper instead of\n * deflate one.\n **/\nfunction gzip(input, options) {\n  options = options || {};\n  options.gzip = true;\n  return deflate(input, options);\n}\n\n\nexports.Deflate = Deflate;\nexports.deflate = deflate;\nexports.deflateRaw = deflateRaw;\nexports.gzip = gzip;\n","'use strict';\n\n// (C) 1995-2013 Jean-loup Gailly and Mark Adler\n// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//   claim that you wrote the original software. If you use this software\n//   in a product, an acknowledgment in the product documentation would be\n//   appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//   misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n\nvar utils   = require('../utils/common');\nvar trees   = require('./trees');\nvar adler32 = require('./adler32');\nvar crc32   = require('./crc32');\nvar msg     = require('./messages');\n\n/* Public constants ==========================================================*/\n/* ===========================================================================*/\n\n\n/* Allowed flush values; see deflate() and inflate() below for details */\nvar Z_NO_FLUSH      = 0;\nvar Z_PARTIAL_FLUSH = 1;\n//var Z_SYNC_FLUSH    = 2;\nvar Z_FULL_FLUSH    = 3;\nvar Z_FINISH        = 4;\nvar Z_BLOCK         = 5;\n//var Z_TREES         = 6;\n\n\n/* Return codes for the compression/decompression functions. Negative values\n * are errors, positive values are used for special but normal events.\n */\nvar Z_OK            = 0;\nvar Z_STREAM_END    = 1;\n//var Z_NEED_DICT     = 2;\n//var Z_ERRNO         = -1;\nvar Z_STREAM_ERROR  = -2;\nvar Z_DATA_ERROR    = -3;\n//var Z_MEM_ERROR     = -4;\nvar Z_BUF_ERROR     = -5;\n//var Z_VERSION_ERROR = -6;\n\n\n/* compression levels */\n//var Z_NO_COMPRESSION      = 0;\n//var Z_BEST_SPEED          = 1;\n//var Z_BEST_COMPRESSION    = 9;\nvar Z_DEFAULT_COMPRESSION = -1;\n\n\nvar Z_FILTERED            = 1;\nvar Z_HUFFMAN_ONLY        = 2;\nvar Z_RLE                 = 3;\nvar Z_FIXED               = 4;\nvar Z_DEFAULT_STRATEGY    = 0;\n\n/* Possible values of the data_type field (though see inflate()) */\n//var Z_BINARY              = 0;\n//var Z_TEXT                = 1;\n//var Z_ASCII               = 1; // = Z_TEXT\nvar Z_UNKNOWN             = 2;\n\n\n/* The deflate compression method */\nvar Z_DEFLATED  = 8;\n\n/*============================================================================*/\n\n\nvar MAX_MEM_LEVEL = 9;\n/* Maximum value for memLevel in deflateInit2 */\nvar MAX_WBITS = 15;\n/* 32K LZ77 window */\nvar DEF_MEM_LEVEL = 8;\n\n\nvar LENGTH_CODES  = 29;\n/* number of length codes, not counting the special END_BLOCK code */\nvar LITERALS      = 256;\n/* number of literal bytes 0..255 */\nvar L_CODES       = LITERALS + 1 + LENGTH_CODES;\n/* number of Literal or Length codes, including the END_BLOCK code */\nvar D_CODES       = 30;\n/* number of distance codes */\nvar BL_CODES      = 19;\n/* number of codes used to transfer the bit lengths */\nvar HEAP_SIZE     = 2 * L_CODES + 1;\n/* maximum heap size */\nvar MAX_BITS  = 15;\n/* All codes must not exceed MAX_BITS bits */\n\nvar MIN_MATCH = 3;\nvar MAX_MATCH = 258;\nvar MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1);\n\nvar PRESET_DICT = 0x20;\n\nvar INIT_STATE = 42;\nvar EXTRA_STATE = 69;\nvar NAME_STATE = 73;\nvar COMMENT_STATE = 91;\nvar HCRC_STATE = 103;\nvar BUSY_STATE = 113;\nvar FINISH_STATE = 666;\n\nvar BS_NEED_MORE      = 1; /* block not completed, need more input or more output */\nvar BS_BLOCK_DONE     = 2; /* block flush performed */\nvar BS_FINISH_STARTED = 3; /* finish started, need only more output at next deflate */\nvar BS_FINISH_DONE    = 4; /* finish done, accept no more input or output */\n\nvar OS_CODE = 0x03; // Unix :) . Don't detect, use this default.\n\nfunction err(strm, errorCode) {\n  strm.msg = msg[errorCode];\n  return errorCode;\n}\n\nfunction rank(f) {\n  return ((f) << 1) - ((f) > 4 ? 9 : 0);\n}\n\nfunction zero(buf) { var len = buf.length; while (--len >= 0) { buf[len] = 0; } }\n\n\n/* =========================================================================\n * Flush as much pending output as possible. All deflate() output goes\n * through this function so some applications may wish to modify it\n * to avoid allocating a large strm->output buffer and copying into it.\n * (See also read_buf()).\n */\nfunction flush_pending(strm) {\n  var s = strm.state;\n\n  //_tr_flush_bits(s);\n  var len = s.pending;\n  if (len > strm.avail_out) {\n    len = strm.avail_out;\n  }\n  if (len === 0) { return; }\n\n  utils.arraySet(strm.output, s.pending_buf, s.pending_out, len, strm.next_out);\n  strm.next_out += len;\n  s.pending_out += len;\n  strm.total_out += len;\n  strm.avail_out -= len;\n  s.pending -= len;\n  if (s.pending === 0) {\n    s.pending_out = 0;\n  }\n}\n\n\nfunction flush_block_only(s, last) {\n  trees._tr_flush_block(s, (s.block_start >= 0 ? s.block_start : -1), s.strstart - s.block_start, last);\n  s.block_start = s.strstart;\n  flush_pending(s.strm);\n}\n\n\nfunction put_byte(s, b) {\n  s.pending_buf[s.pending++] = b;\n}\n\n\n/* =========================================================================\n * Put a short in the pending buffer. The 16-bit value is put in MSB order.\n * IN assertion: the stream state is correct and there is enough room in\n * pending_buf.\n */\nfunction putShortMSB(s, b) {\n//  put_byte(s, (Byte)(b >> 8));\n//  put_byte(s, (Byte)(b & 0xff));\n  s.pending_buf[s.pending++] = (b >>> 8) & 0xff;\n  s.pending_buf[s.pending++] = b & 0xff;\n}\n\n\n/* ===========================================================================\n * Read a new buffer from the current input stream, update the adler32\n * and total number of bytes read.  All deflate() input goes through\n * this function so some applications may wish to modify it to avoid\n * allocating a large strm->input buffer and copying from it.\n * (See also flush_pending()).\n */\nfunction read_buf(strm, buf, start, size) {\n  var len = strm.avail_in;\n\n  if (len > size) { len = size; }\n  if (len === 0) { return 0; }\n\n  strm.avail_in -= len;\n\n  // zmemcpy(buf, strm->next_in, len);\n  utils.arraySet(buf, strm.input, strm.next_in, len, start);\n  if (strm.state.wrap === 1) {\n    strm.adler = adler32(strm.adler, buf, len, start);\n  }\n\n  else if (strm.state.wrap === 2) {\n    strm.adler = crc32(strm.adler, buf, len, start);\n  }\n\n  strm.next_in += len;\n  strm.total_in += len;\n\n  return len;\n}\n\n\n/* ===========================================================================\n * Set match_start to the longest match starting at the given string and\n * return its length. Matches shorter or equal to prev_length are discarded,\n * in which case the result is equal to prev_length and match_start is\n * garbage.\n * IN assertions: cur_match is the head of the hash chain for the current\n *   string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1\n * OUT assertion: the match length is not greater than s->lookahead.\n */\nfunction longest_match(s, cur_match) {\n  var chain_length = s.max_chain_length;      /* max hash chain length */\n  var scan = s.strstart; /* current string */\n  var match;                       /* matched string */\n  var len;                           /* length of current match */\n  var best_len = s.prev_length;              /* best match length so far */\n  var nice_match = s.nice_match;             /* stop if match long enough */\n  var limit = (s.strstart > (s.w_size - MIN_LOOKAHEAD)) ?\n      s.strstart - (s.w_size - MIN_LOOKAHEAD) : 0/*NIL*/;\n\n  var _win = s.window; // shortcut\n\n  var wmask = s.w_mask;\n  var prev  = s.prev;\n\n  /* Stop when cur_match becomes <= limit. To simplify the code,\n   * we prevent matches with the string of window index 0.\n   */\n\n  var strend = s.strstart + MAX_MATCH;\n  var scan_end1  = _win[scan + best_len - 1];\n  var scan_end   = _win[scan + best_len];\n\n  /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.\n   * It is easy to get rid of this optimization if necessary.\n   */\n  // Assert(s->hash_bits >= 8 && MAX_MATCH == 258, \"Code too clever\");\n\n  /* Do not waste too much time if we already have a good match: */\n  if (s.prev_length >= s.good_match) {\n    chain_length >>= 2;\n  }\n  /* Do not look for matches beyond the end of the input. This is necessary\n   * to make deflate deterministic.\n   */\n  if (nice_match > s.lookahead) { nice_match = s.lookahead; }\n\n  // Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, \"need lookahead\");\n\n  do {\n    // Assert(cur_match < s->strstart, \"no future\");\n    match = cur_match;\n\n    /* Skip to next match if the match length cannot increase\n     * or if the match length is less than 2.  Note that the checks below\n     * for insufficient lookahead only occur occasionally for performance\n     * reasons.  Therefore uninitialized memory will be accessed, and\n     * conditional jumps will be made that depend on those values.\n     * However the length of the match is limited to the lookahead, so\n     * the output of deflate is not affected by the uninitialized values.\n     */\n\n    if (_win[match + best_len]     !== scan_end  ||\n        _win[match + best_len - 1] !== scan_end1 ||\n        _win[match]                !== _win[scan] ||\n        _win[++match]              !== _win[scan + 1]) {\n      continue;\n    }\n\n    /* The check at best_len-1 can be removed because it will be made\n     * again later. (This heuristic is not always a win.)\n     * It is not necessary to compare scan[2] and match[2] since they\n     * are always equal when the other bytes match, given that\n     * the hash keys are equal and that HASH_BITS >= 8.\n     */\n    scan += 2;\n    match++;\n    // Assert(*scan == *match, \"match[2]?\");\n\n    /* We check for insufficient lookahead only every 8th comparison;\n     * the 256th check will be made at strstart+258.\n     */\n    do {\n      /*jshint noempty:false*/\n    } while (_win[++scan] === _win[++match] && _win[++scan] === _win[++match] &&\n             _win[++scan] === _win[++match] && _win[++scan] === _win[++match] &&\n             _win[++scan] === _win[++match] && _win[++scan] === _win[++match] &&\n             _win[++scan] === _win[++match] && _win[++scan] === _win[++match] &&\n             scan < strend);\n\n    // Assert(scan <= s->window+(unsigned)(s->window_size-1), \"wild scan\");\n\n    len = MAX_MATCH - (strend - scan);\n    scan = strend - MAX_MATCH;\n\n    if (len > best_len) {\n      s.match_start = cur_match;\n      best_len = len;\n      if (len >= nice_match) {\n        break;\n      }\n      scan_end1  = _win[scan + best_len - 1];\n      scan_end   = _win[scan + best_len];\n    }\n  } while ((cur_match = prev[cur_match & wmask]) > limit && --chain_length !== 0);\n\n  if (best_len <= s.lookahead) {\n    return best_len;\n  }\n  return s.lookahead;\n}\n\n\n/* ===========================================================================\n * Fill the window when the lookahead becomes insufficient.\n * Updates strstart and lookahead.\n *\n * IN assertion: lookahead < MIN_LOOKAHEAD\n * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD\n *    At least one byte has been read, or avail_in == 0; reads are\n *    performed for at least two bytes (required for the zip translate_eol\n *    option -- not supported here).\n */\nfunction fill_window(s) {\n  var _w_size = s.w_size;\n  var p, n, m, more, str;\n\n  //Assert(s->lookahead < MIN_LOOKAHEAD, \"already enough lookahead\");\n\n  do {\n    more = s.window_size - s.lookahead - s.strstart;\n\n    // JS ints have 32 bit, block below not needed\n    /* Deal with !@#$% 64K limit: */\n    //if (sizeof(int) <= 2) {\n    //    if (more == 0 && s->strstart == 0 && s->lookahead == 0) {\n    //        more = wsize;\n    //\n    //  } else if (more == (unsigned)(-1)) {\n    //        /* Very unlikely, but possible on 16 bit machine if\n    //         * strstart == 0 && lookahead == 1 (input done a byte at time)\n    //         */\n    //        more--;\n    //    }\n    //}\n\n\n    /* If the window is almost full and there is insufficient lookahead,\n     * move the upper half to the lower one to make room in the upper half.\n     */\n    if (s.strstart >= _w_size + (_w_size - MIN_LOOKAHEAD)) {\n\n      utils.arraySet(s.window, s.window, _w_size, _w_size, 0);\n      s.match_start -= _w_size;\n      s.strstart -= _w_size;\n      /* we now have strstart >= MAX_DIST */\n      s.block_start -= _w_size;\n\n      /* Slide the hash table (could be avoided with 32 bit values\n       at the expense of memory usage). We slide even when level == 0\n       to keep the hash table consistent if we switch back to level > 0\n       later. (Using level 0 permanently is not an optimal usage of\n       zlib, so we don't care about this pathological case.)\n       */\n\n      n = s.hash_size;\n      p = n;\n      do {\n        m = s.head[--p];\n        s.head[p] = (m >= _w_size ? m - _w_size : 0);\n      } while (--n);\n\n      n = _w_size;\n      p = n;\n      do {\n        m = s.prev[--p];\n        s.prev[p] = (m >= _w_size ? m - _w_size : 0);\n        /* If n is not on any hash chain, prev[n] is garbage but\n         * its value will never be used.\n         */\n      } while (--n);\n\n      more += _w_size;\n    }\n    if (s.strm.avail_in === 0) {\n      break;\n    }\n\n    /* If there was no sliding:\n     *    strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&\n     *    more == window_size - lookahead - strstart\n     * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)\n     * => more >= window_size - 2*WSIZE + 2\n     * In the BIG_MEM or MMAP case (not yet supported),\n     *   window_size == input_size + MIN_LOOKAHEAD  &&\n     *   strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.\n     * Otherwise, window_size == 2*WSIZE so more >= 2.\n     * If there was sliding, more >= WSIZE. So in all cases, more >= 2.\n     */\n    //Assert(more >= 2, \"more < 2\");\n    n = read_buf(s.strm, s.window, s.strstart + s.lookahead, more);\n    s.lookahead += n;\n\n    /* Initialize the hash value now that we have some input: */\n    if (s.lookahead + s.insert >= MIN_MATCH) {\n      str = s.strstart - s.insert;\n      s.ins_h = s.window[str];\n\n      /* UPDATE_HASH(s, s->ins_h, s->window[str + 1]); */\n      s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[str + 1]) & s.hash_mask;\n//#if MIN_MATCH != 3\n//        Call update_hash() MIN_MATCH-3 more times\n//#endif\n      while (s.insert) {\n        /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */\n        s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[str + MIN_MATCH - 1]) & s.hash_mask;\n\n        s.prev[str & s.w_mask] = s.head[s.ins_h];\n        s.head[s.ins_h] = str;\n        str++;\n        s.insert--;\n        if (s.lookahead + s.insert < MIN_MATCH) {\n          break;\n        }\n      }\n    }\n    /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage,\n     * but this is not important since only literal bytes will be emitted.\n     */\n\n  } while (s.lookahead < MIN_LOOKAHEAD && s.strm.avail_in !== 0);\n\n  /* If the WIN_INIT bytes after the end of the current data have never been\n   * written, then zero those bytes in order to avoid memory check reports of\n   * the use of uninitialized (or uninitialised as Julian writes) bytes by\n   * the longest match routines.  Update the high water mark for the next\n   * time through here.  WIN_INIT is set to MAX_MATCH since the longest match\n   * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead.\n   */\n//  if (s.high_water < s.window_size) {\n//    var curr = s.strstart + s.lookahead;\n//    var init = 0;\n//\n//    if (s.high_water < curr) {\n//      /* Previous high water mark below current data -- zero WIN_INIT\n//       * bytes or up to end of window, whichever is less.\n//       */\n//      init = s.window_size - curr;\n//      if (init > WIN_INIT)\n//        init = WIN_INIT;\n//      zmemzero(s->window + curr, (unsigned)init);\n//      s->high_water = curr + init;\n//    }\n//    else if (s->high_water < (ulg)curr + WIN_INIT) {\n//      /* High water mark at or above current data, but below current data\n//       * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up\n//       * to end of window, whichever is less.\n//       */\n//      init = (ulg)curr + WIN_INIT - s->high_water;\n//      if (init > s->window_size - s->high_water)\n//        init = s->window_size - s->high_water;\n//      zmemzero(s->window + s->high_water, (unsigned)init);\n//      s->high_water += init;\n//    }\n//  }\n//\n//  Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD,\n//    \"not enough room for search\");\n}\n\n/* ===========================================================================\n * Copy without compression as much as possible from the input stream, return\n * the current block state.\n * This function does not insert new strings in the dictionary since\n * uncompressible data is probably not useful. This function is used\n * only for the level=0 compression option.\n * NOTE: this function should be optimized to avoid extra copying from\n * window to pending_buf.\n */\nfunction deflate_stored(s, flush) {\n  /* Stored blocks are limited to 0xffff bytes, pending_buf is limited\n   * to pending_buf_size, and each stored block has a 5 byte header:\n   */\n  var max_block_size = 0xffff;\n\n  if (max_block_size > s.pending_buf_size - 5) {\n    max_block_size = s.pending_buf_size - 5;\n  }\n\n  /* Copy as much as possible from input to output: */\n  for (;;) {\n    /* Fill the window as much as possible: */\n    if (s.lookahead <= 1) {\n\n      //Assert(s->strstart < s->w_size+MAX_DIST(s) ||\n      //  s->block_start >= (long)s->w_size, \"slide too late\");\n//      if (!(s.strstart < s.w_size + (s.w_size - MIN_LOOKAHEAD) ||\n//        s.block_start >= s.w_size)) {\n//        throw  new Error(\"slide too late\");\n//      }\n\n      fill_window(s);\n      if (s.lookahead === 0 && flush === Z_NO_FLUSH) {\n        return BS_NEED_MORE;\n      }\n\n      if (s.lookahead === 0) {\n        break;\n      }\n      /* flush the current block */\n    }\n    //Assert(s->block_start >= 0L, \"block gone\");\n//    if (s.block_start < 0) throw new Error(\"block gone\");\n\n    s.strstart += s.lookahead;\n    s.lookahead = 0;\n\n    /* Emit a stored block if pending_buf will be full: */\n    var max_start = s.block_start + max_block_size;\n\n    if (s.strstart === 0 || s.strstart >= max_start) {\n      /* strstart == 0 is possible when wraparound on 16-bit machine */\n      s.lookahead = s.strstart - max_start;\n      s.strstart = max_start;\n      /*** FLUSH_BLOCK(s, 0); ***/\n      flush_block_only(s, false);\n      if (s.strm.avail_out === 0) {\n        return BS_NEED_MORE;\n      }\n      /***/\n\n\n    }\n    /* Flush if we may have to slide, otherwise block_start may become\n     * negative and the data will be gone:\n     */\n    if (s.strstart - s.block_start >= (s.w_size - MIN_LOOKAHEAD)) {\n      /*** FLUSH_BLOCK(s, 0); ***/\n      flush_block_only(s, false);\n      if (s.strm.avail_out === 0) {\n        return BS_NEED_MORE;\n      }\n      /***/\n    }\n  }\n\n  s.insert = 0;\n\n  if (flush === Z_FINISH) {\n    /*** FLUSH_BLOCK(s, 1); ***/\n    flush_block_only(s, true);\n    if (s.strm.avail_out === 0) {\n      return BS_FINISH_STARTED;\n    }\n    /***/\n    return BS_FINISH_DONE;\n  }\n\n  if (s.strstart > s.block_start) {\n    /*** FLUSH_BLOCK(s, 0); ***/\n    flush_block_only(s, false);\n    if (s.strm.avail_out === 0) {\n      return BS_NEED_MORE;\n    }\n    /***/\n  }\n\n  return BS_NEED_MORE;\n}\n\n/* ===========================================================================\n * Compress as much as possible from the input stream, return the current\n * block state.\n * This function does not perform lazy evaluation of matches and inserts\n * new strings in the dictionary only for unmatched strings or for short\n * matches. It is used only for the fast compression options.\n */\nfunction deflate_fast(s, flush) {\n  var hash_head;        /* head of the hash chain */\n  var bflush;           /* set if current block must be flushed */\n\n  for (;;) {\n    /* Make sure that we always have enough lookahead, except\n     * at the end of the input file. We need MAX_MATCH bytes\n     * for the next match, plus MIN_MATCH bytes to insert the\n     * string following the next match.\n     */\n    if (s.lookahead < MIN_LOOKAHEAD) {\n      fill_window(s);\n      if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH) {\n        return BS_NEED_MORE;\n      }\n      if (s.lookahead === 0) {\n        break; /* flush the current block */\n      }\n    }\n\n    /* Insert the string window[strstart .. strstart+2] in the\n     * dictionary, and set hash_head to the head of the hash chain:\n     */\n    hash_head = 0/*NIL*/;\n    if (s.lookahead >= MIN_MATCH) {\n      /*** INSERT_STRING(s, s.strstart, hash_head); ***/\n      s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask;\n      hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];\n      s.head[s.ins_h] = s.strstart;\n      /***/\n    }\n\n    /* Find the longest match, discarding those <= prev_length.\n     * At this point we have always match_length < MIN_MATCH\n     */\n    if (hash_head !== 0/*NIL*/ && ((s.strstart - hash_head) <= (s.w_size - MIN_LOOKAHEAD))) {\n      /* To simplify the code, we prevent matches with the string\n       * of window index 0 (in particular we have to avoid a match\n       * of the string with itself at the start of the input file).\n       */\n      s.match_length = longest_match(s, hash_head);\n      /* longest_match() sets match_start */\n    }\n    if (s.match_length >= MIN_MATCH) {\n      // check_match(s, s.strstart, s.match_start, s.match_length); // for debug only\n\n      /*** _tr_tally_dist(s, s.strstart - s.match_start,\n                     s.match_length - MIN_MATCH, bflush); ***/\n      bflush = trees._tr_tally(s, s.strstart - s.match_start, s.match_length - MIN_MATCH);\n\n      s.lookahead -= s.match_length;\n\n      /* Insert new strings in the hash table only if the match length\n       * is not too large. This saves time but degrades compression.\n       */\n      if (s.match_length <= s.max_lazy_match/*max_insert_length*/ && s.lookahead >= MIN_MATCH) {\n        s.match_length--; /* string at strstart already in table */\n        do {\n          s.strstart++;\n          /*** INSERT_STRING(s, s.strstart, hash_head); ***/\n          s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask;\n          hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];\n          s.head[s.ins_h] = s.strstart;\n          /***/\n          /* strstart never exceeds WSIZE-MAX_MATCH, so there are\n           * always MIN_MATCH bytes ahead.\n           */\n        } while (--s.match_length !== 0);\n        s.strstart++;\n      } else\n      {\n        s.strstart += s.match_length;\n        s.match_length = 0;\n        s.ins_h = s.window[s.strstart];\n        /* UPDATE_HASH(s, s.ins_h, s.window[s.strstart+1]); */\n        s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + 1]) & s.hash_mask;\n\n//#if MIN_MATCH != 3\n//                Call UPDATE_HASH() MIN_MATCH-3 more times\n//#endif\n        /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not\n         * matter since it will be recomputed at next deflate call.\n         */\n      }\n    } else {\n      /* No match, output a literal byte */\n      //Tracevv((stderr,\"%c\", s.window[s.strstart]));\n      /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/\n      bflush = trees._tr_tally(s, 0, s.window[s.strstart]);\n\n      s.lookahead--;\n      s.strstart++;\n    }\n    if (bflush) {\n      /*** FLUSH_BLOCK(s, 0); ***/\n      flush_block_only(s, false);\n      if (s.strm.avail_out === 0) {\n        return BS_NEED_MORE;\n      }\n      /***/\n    }\n  }\n  s.insert = ((s.strstart < (MIN_MATCH - 1)) ? s.strstart : MIN_MATCH - 1);\n  if (flush === Z_FINISH) {\n    /*** FLUSH_BLOCK(s, 1); ***/\n    flush_block_only(s, true);\n    if (s.strm.avail_out === 0) {\n      return BS_FINISH_STARTED;\n    }\n    /***/\n    return BS_FINISH_DONE;\n  }\n  if (s.last_lit) {\n    /*** FLUSH_BLOCK(s, 0); ***/\n    flush_block_only(s, false);\n    if (s.strm.avail_out === 0) {\n      return BS_NEED_MORE;\n    }\n    /***/\n  }\n  return BS_BLOCK_DONE;\n}\n\n/* ===========================================================================\n * Same as above, but achieves better compression. We use a lazy\n * evaluation for matches: a match is finally adopted only if there is\n * no better match at the next window position.\n */\nfunction deflate_slow(s, flush) {\n  var hash_head;          /* head of hash chain */\n  var bflush;              /* set if current block must be flushed */\n\n  var max_insert;\n\n  /* Process the input block. */\n  for (;;) {\n    /* Make sure that we always have enough lookahead, except\n     * at the end of the input file. We need MAX_MATCH bytes\n     * for the next match, plus MIN_MATCH bytes to insert the\n     * string following the next match.\n     */\n    if (s.lookahead < MIN_LOOKAHEAD) {\n      fill_window(s);\n      if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH) {\n        return BS_NEED_MORE;\n      }\n      if (s.lookahead === 0) { break; } /* flush the current block */\n    }\n\n    /* Insert the string window[strstart .. strstart+2] in the\n     * dictionary, and set hash_head to the head of the hash chain:\n     */\n    hash_head = 0/*NIL*/;\n    if (s.lookahead >= MIN_MATCH) {\n      /*** INSERT_STRING(s, s.strstart, hash_head); ***/\n      s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask;\n      hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];\n      s.head[s.ins_h] = s.strstart;\n      /***/\n    }\n\n    /* Find the longest match, discarding those <= prev_length.\n     */\n    s.prev_length = s.match_length;\n    s.prev_match = s.match_start;\n    s.match_length = MIN_MATCH - 1;\n\n    if (hash_head !== 0/*NIL*/ && s.prev_length < s.max_lazy_match &&\n        s.strstart - hash_head <= (s.w_size - MIN_LOOKAHEAD)/*MAX_DIST(s)*/) {\n      /* To simplify the code, we prevent matches with the string\n       * of window index 0 (in particular we have to avoid a match\n       * of the string with itself at the start of the input file).\n       */\n      s.match_length = longest_match(s, hash_head);\n      /* longest_match() sets match_start */\n\n      if (s.match_length <= 5 &&\n         (s.strategy === Z_FILTERED || (s.match_length === MIN_MATCH && s.strstart - s.match_start > 4096/*TOO_FAR*/))) {\n\n        /* If prev_match is also MIN_MATCH, match_start is garbage\n         * but we will ignore the current match anyway.\n         */\n        s.match_length = MIN_MATCH - 1;\n      }\n    }\n    /* If there was a match at the previous step and the current\n     * match is not better, output the previous match:\n     */\n    if (s.prev_length >= MIN_MATCH && s.match_length <= s.prev_length) {\n      max_insert = s.strstart + s.lookahead - MIN_MATCH;\n      /* Do not insert strings in hash table beyond this. */\n\n      //check_match(s, s.strstart-1, s.prev_match, s.prev_length);\n\n      /***_tr_tally_dist(s, s.strstart - 1 - s.prev_match,\n                     s.prev_length - MIN_MATCH, bflush);***/\n      bflush = trees._tr_tally(s, s.strstart - 1 - s.prev_match, s.prev_length - MIN_MATCH);\n      /* Insert in hash table all strings up to the end of the match.\n       * strstart-1 and strstart are already inserted. If there is not\n       * enough lookahead, the last two strings are not inserted in\n       * the hash table.\n       */\n      s.lookahead -= s.prev_length - 1;\n      s.prev_length -= 2;\n      do {\n        if (++s.strstart <= max_insert) {\n          /*** INSERT_STRING(s, s.strstart, hash_head); ***/\n          s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask;\n          hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];\n          s.head[s.ins_h] = s.strstart;\n          /***/\n        }\n      } while (--s.prev_length !== 0);\n      s.match_available = 0;\n      s.match_length = MIN_MATCH - 1;\n      s.strstart++;\n\n      if (bflush) {\n        /*** FLUSH_BLOCK(s, 0); ***/\n        flush_block_only(s, false);\n        if (s.strm.avail_out === 0) {\n          return BS_NEED_MORE;\n        }\n        /***/\n      }\n\n    } else if (s.match_available) {\n      /* If there was no match at the previous position, output a\n       * single literal. If there was a match but the current match\n       * is longer, truncate the previous match to a single literal.\n       */\n      //Tracevv((stderr,\"%c\", s->window[s->strstart-1]));\n      /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/\n      bflush = trees._tr_tally(s, 0, s.window[s.strstart - 1]);\n\n      if (bflush) {\n        /*** FLUSH_BLOCK_ONLY(s, 0) ***/\n        flush_block_only(s, false);\n        /***/\n      }\n      s.strstart++;\n      s.lookahead--;\n      if (s.strm.avail_out === 0) {\n        return BS_NEED_MORE;\n      }\n    } else {\n      /* There is no previous match to compare with, wait for\n       * the next step to decide.\n       */\n      s.match_available = 1;\n      s.strstart++;\n      s.lookahead--;\n    }\n  }\n  //Assert (flush != Z_NO_FLUSH, \"no flush?\");\n  if (s.match_available) {\n    //Tracevv((stderr,\"%c\", s->window[s->strstart-1]));\n    /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/\n    bflush = trees._tr_tally(s, 0, s.window[s.strstart - 1]);\n\n    s.match_available = 0;\n  }\n  s.insert = s.strstart < MIN_MATCH - 1 ? s.strstart : MIN_MATCH - 1;\n  if (flush === Z_FINISH) {\n    /*** FLUSH_BLOCK(s, 1); ***/\n    flush_block_only(s, true);\n    if (s.strm.avail_out === 0) {\n      return BS_FINISH_STARTED;\n    }\n    /***/\n    return BS_FINISH_DONE;\n  }\n  if (s.last_lit) {\n    /*** FLUSH_BLOCK(s, 0); ***/\n    flush_block_only(s, false);\n    if (s.strm.avail_out === 0) {\n      return BS_NEED_MORE;\n    }\n    /***/\n  }\n\n  return BS_BLOCK_DONE;\n}\n\n\n/* ===========================================================================\n * For Z_RLE, simply look for runs of bytes, generate matches only of distance\n * one.  Do not maintain a hash table.  (It will be regenerated if this run of\n * deflate switches away from Z_RLE.)\n */\nfunction deflate_rle(s, flush) {\n  var bflush;            /* set if current block must be flushed */\n  var prev;              /* byte at distance one to match */\n  var scan, strend;      /* scan goes up to strend for length of run */\n\n  var _win = s.window;\n\n  for (;;) {\n    /* Make sure that we always have enough lookahead, except\n     * at the end of the input file. We need MAX_MATCH bytes\n     * for the longest run, plus one for the unrolled loop.\n     */\n    if (s.lookahead <= MAX_MATCH) {\n      fill_window(s);\n      if (s.lookahead <= MAX_MATCH && flush === Z_NO_FLUSH) {\n        return BS_NEED_MORE;\n      }\n      if (s.lookahead === 0) { break; } /* flush the current block */\n    }\n\n    /* See how many times the previous byte repeats */\n    s.match_length = 0;\n    if (s.lookahead >= MIN_MATCH && s.strstart > 0) {\n      scan = s.strstart - 1;\n      prev = _win[scan];\n      if (prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan]) {\n        strend = s.strstart + MAX_MATCH;\n        do {\n          /*jshint noempty:false*/\n        } while (prev === _win[++scan] && prev === _win[++scan] &&\n                 prev === _win[++scan] && prev === _win[++scan] &&\n                 prev === _win[++scan] && prev === _win[++scan] &&\n                 prev === _win[++scan] && prev === _win[++scan] &&\n                 scan < strend);\n        s.match_length = MAX_MATCH - (strend - scan);\n        if (s.match_length > s.lookahead) {\n          s.match_length = s.lookahead;\n        }\n      }\n      //Assert(scan <= s->window+(uInt)(s->window_size-1), \"wild scan\");\n    }\n\n    /* Emit match if have run of MIN_MATCH or longer, else emit literal */\n    if (s.match_length >= MIN_MATCH) {\n      //check_match(s, s.strstart, s.strstart - 1, s.match_length);\n\n      /*** _tr_tally_dist(s, 1, s.match_length - MIN_MATCH, bflush); ***/\n      bflush = trees._tr_tally(s, 1, s.match_length - MIN_MATCH);\n\n      s.lookahead -= s.match_length;\n      s.strstart += s.match_length;\n      s.match_length = 0;\n    } else {\n      /* No match, output a literal byte */\n      //Tracevv((stderr,\"%c\", s->window[s->strstart]));\n      /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/\n      bflush = trees._tr_tally(s, 0, s.window[s.strstart]);\n\n      s.lookahead--;\n      s.strstart++;\n    }\n    if (bflush) {\n      /*** FLUSH_BLOCK(s, 0); ***/\n      flush_block_only(s, false);\n      if (s.strm.avail_out === 0) {\n        return BS_NEED_MORE;\n      }\n      /***/\n    }\n  }\n  s.insert = 0;\n  if (flush === Z_FINISH) {\n    /*** FLUSH_BLOCK(s, 1); ***/\n    flush_block_only(s, true);\n    if (s.strm.avail_out === 0) {\n      return BS_FINISH_STARTED;\n    }\n    /***/\n    return BS_FINISH_DONE;\n  }\n  if (s.last_lit) {\n    /*** FLUSH_BLOCK(s, 0); ***/\n    flush_block_only(s, false);\n    if (s.strm.avail_out === 0) {\n      return BS_NEED_MORE;\n    }\n    /***/\n  }\n  return BS_BLOCK_DONE;\n}\n\n/* ===========================================================================\n * For Z_HUFFMAN_ONLY, do not look for matches.  Do not maintain a hash table.\n * (It will be regenerated if this run of deflate switches away from Huffman.)\n */\nfunction deflate_huff(s, flush) {\n  var bflush;             /* set if current block must be flushed */\n\n  for (;;) {\n    /* Make sure that we have a literal to write. */\n    if (s.lookahead === 0) {\n      fill_window(s);\n      if (s.lookahead === 0) {\n        if (flush === Z_NO_FLUSH) {\n          return BS_NEED_MORE;\n        }\n        break;      /* flush the current block */\n      }\n    }\n\n    /* Output a literal byte */\n    s.match_length = 0;\n    //Tracevv((stderr,\"%c\", s->window[s->strstart]));\n    /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/\n    bflush = trees._tr_tally(s, 0, s.window[s.strstart]);\n    s.lookahead--;\n    s.strstart++;\n    if (bflush) {\n      /*** FLUSH_BLOCK(s, 0); ***/\n      flush_block_only(s, false);\n      if (s.strm.avail_out === 0) {\n        return BS_NEED_MORE;\n      }\n      /***/\n    }\n  }\n  s.insert = 0;\n  if (flush === Z_FINISH) {\n    /*** FLUSH_BLOCK(s, 1); ***/\n    flush_block_only(s, true);\n    if (s.strm.avail_out === 0) {\n      return BS_FINISH_STARTED;\n    }\n    /***/\n    return BS_FINISH_DONE;\n  }\n  if (s.last_lit) {\n    /*** FLUSH_BLOCK(s, 0); ***/\n    flush_block_only(s, false);\n    if (s.strm.avail_out === 0) {\n      return BS_NEED_MORE;\n    }\n    /***/\n  }\n  return BS_BLOCK_DONE;\n}\n\n/* Values for max_lazy_match, good_match and max_chain_length, depending on\n * the desired pack level (0..9). The values given below have been tuned to\n * exclude worst case performance for pathological files. Better values may be\n * found for specific files.\n */\nfunction Config(good_length, max_lazy, nice_length, max_chain, func) {\n  this.good_length = good_length;\n  this.max_lazy = max_lazy;\n  this.nice_length = nice_length;\n  this.max_chain = max_chain;\n  this.func = func;\n}\n\nvar configuration_table;\n\nconfiguration_table = [\n  /*      good lazy nice chain */\n  new Config(0, 0, 0, 0, deflate_stored),          /* 0 store only */\n  new Config(4, 4, 8, 4, deflate_fast),            /* 1 max speed, no lazy matches */\n  new Config(4, 5, 16, 8, deflate_fast),           /* 2 */\n  new Config(4, 6, 32, 32, deflate_fast),          /* 3 */\n\n  new Config(4, 4, 16, 16, deflate_slow),          /* 4 lazy matches */\n  new Config(8, 16, 32, 32, deflate_slow),         /* 5 */\n  new Config(8, 16, 128, 128, deflate_slow),       /* 6 */\n  new Config(8, 32, 128, 256, deflate_slow),       /* 7 */\n  new Config(32, 128, 258, 1024, deflate_slow),    /* 8 */\n  new Config(32, 258, 258, 4096, deflate_slow)     /* 9 max compression */\n];\n\n\n/* ===========================================================================\n * Initialize the \"longest match\" routines for a new zlib stream\n */\nfunction lm_init(s) {\n  s.window_size = 2 * s.w_size;\n\n  /*** CLEAR_HASH(s); ***/\n  zero(s.head); // Fill with NIL (= 0);\n\n  /* Set the default configuration parameters:\n   */\n  s.max_lazy_match = configuration_table[s.level].max_lazy;\n  s.good_match = configuration_table[s.level].good_length;\n  s.nice_match = configuration_table[s.level].nice_length;\n  s.max_chain_length = configuration_table[s.level].max_chain;\n\n  s.strstart = 0;\n  s.block_start = 0;\n  s.lookahead = 0;\n  s.insert = 0;\n  s.match_length = s.prev_length = MIN_MATCH - 1;\n  s.match_available = 0;\n  s.ins_h = 0;\n}\n\n\nfunction DeflateState() {\n  this.strm = null;            /* pointer back to this zlib stream */\n  this.status = 0;            /* as the name implies */\n  this.pending_buf = null;      /* output still pending */\n  this.pending_buf_size = 0;  /* size of pending_buf */\n  this.pending_out = 0;       /* next pending byte to output to the stream */\n  this.pending = 0;           /* nb of bytes in the pending buffer */\n  this.wrap = 0;              /* bit 0 true for zlib, bit 1 true for gzip */\n  this.gzhead = null;         /* gzip header information to write */\n  this.gzindex = 0;           /* where in extra, name, or comment */\n  this.method = Z_DEFLATED; /* can only be DEFLATED */\n  this.last_flush = -1;   /* value of flush param for previous deflate call */\n\n  this.w_size = 0;  /* LZ77 window size (32K by default) */\n  this.w_bits = 0;  /* log2(w_size)  (8..16) */\n  this.w_mask = 0;  /* w_size - 1 */\n\n  this.window = null;\n  /* Sliding window. Input bytes are read into the second half of the window,\n   * and move to the first half later to keep a dictionary of at least wSize\n   * bytes. With this organization, matches are limited to a distance of\n   * wSize-MAX_MATCH bytes, but this ensures that IO is always\n   * performed with a length multiple of the block size.\n   */\n\n  this.window_size = 0;\n  /* Actual size of window: 2*wSize, except when the user input buffer\n   * is directly used as sliding window.\n   */\n\n  this.prev = null;\n  /* Link to older string with same hash index. To limit the size of this\n   * array to 64K, this link is maintained only for the last 32K strings.\n   * An index in this array is thus a window index modulo 32K.\n   */\n\n  this.head = null;   /* Heads of the hash chains or NIL. */\n\n  this.ins_h = 0;       /* hash index of string to be inserted */\n  this.hash_size = 0;   /* number of elements in hash table */\n  this.hash_bits = 0;   /* log2(hash_size) */\n  this.hash_mask = 0;   /* hash_size-1 */\n\n  this.hash_shift = 0;\n  /* Number of bits by which ins_h must be shifted at each input\n   * step. It must be such that after MIN_MATCH steps, the oldest\n   * byte no longer takes part in the hash key, that is:\n   *   hash_shift * MIN_MATCH >= hash_bits\n   */\n\n  this.block_start = 0;\n  /* Window position at the beginning of the current output block. Gets\n   * negative when the window is moved backwards.\n   */\n\n  this.match_length = 0;      /* length of best match */\n  this.prev_match = 0;        /* previous match */\n  this.match_available = 0;   /* set if previous match exists */\n  this.strstart = 0;          /* start of string to insert */\n  this.match_start = 0;       /* start of matching string */\n  this.lookahead = 0;         /* number of valid bytes ahead in window */\n\n  this.prev_length = 0;\n  /* Length of the best match at previous step. Matches not greater than this\n   * are discarded. This is used in the lazy match evaluation.\n   */\n\n  this.max_chain_length = 0;\n  /* To speed up deflation, hash chains are never searched beyond this\n   * length.  A higher limit improves compression ratio but degrades the\n   * speed.\n   */\n\n  this.max_lazy_match = 0;\n  /* Attempt to find a better match only when the current match is strictly\n   * smaller than this value. This mechanism is used only for compression\n   * levels >= 4.\n   */\n  // That's alias to max_lazy_match, don't use directly\n  //this.max_insert_length = 0;\n  /* Insert new strings in the hash table only if the match length is not\n   * greater than this length. This saves time but degrades compression.\n   * max_insert_length is used only for compression levels <= 3.\n   */\n\n  this.level = 0;     /* compression level (1..9) */\n  this.strategy = 0;  /* favor or force Huffman coding*/\n\n  this.good_match = 0;\n  /* Use a faster search when the previous match is longer than this */\n\n  this.nice_match = 0; /* Stop searching when current match exceeds this */\n\n              /* used by trees.c: */\n\n  /* Didn't use ct_data typedef below to suppress compiler warning */\n\n  // struct ct_data_s dyn_ltree[HEAP_SIZE];   /* literal and length tree */\n  // struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */\n  // struct ct_data_s bl_tree[2*BL_CODES+1];  /* Huffman tree for bit lengths */\n\n  // Use flat array of DOUBLE size, with interleaved fata,\n  // because JS does not support effective\n  this.dyn_ltree  = new utils.Buf16(HEAP_SIZE * 2);\n  this.dyn_dtree  = new utils.Buf16((2 * D_CODES + 1) * 2);\n  this.bl_tree    = new utils.Buf16((2 * BL_CODES + 1) * 2);\n  zero(this.dyn_ltree);\n  zero(this.dyn_dtree);\n  zero(this.bl_tree);\n\n  this.l_desc   = null;         /* desc. for literal tree */\n  this.d_desc   = null;         /* desc. for distance tree */\n  this.bl_desc  = null;         /* desc. for bit length tree */\n\n  //ush bl_count[MAX_BITS+1];\n  this.bl_count = new utils.Buf16(MAX_BITS + 1);\n  /* number of codes at each bit length for an optimal tree */\n\n  //int heap[2*L_CODES+1];      /* heap used to build the Huffman trees */\n  this.heap = new utils.Buf16(2 * L_CODES + 1);  /* heap used to build the Huffman trees */\n  zero(this.heap);\n\n  this.heap_len = 0;               /* number of elements in the heap */\n  this.heap_max = 0;               /* element of largest frequency */\n  /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.\n   * The same heap array is used to build all trees.\n   */\n\n  this.depth = new utils.Buf16(2 * L_CODES + 1); //uch depth[2*L_CODES+1];\n  zero(this.depth);\n  /* Depth of each subtree used as tie breaker for trees of equal frequency\n   */\n\n  this.l_buf = 0;          /* buffer index for literals or lengths */\n\n  this.lit_bufsize = 0;\n  /* Size of match buffer for literals/lengths.  There are 4 reasons for\n   * limiting lit_bufsize to 64K:\n   *   - frequencies can be kept in 16 bit counters\n   *   - if compression is not successful for the first block, all input\n   *     data is still in the window so we can still emit a stored block even\n   *     when input comes from standard input.  (This can also be done for\n   *     all blocks if lit_bufsize is not greater than 32K.)\n   *   - if compression is not successful for a file smaller than 64K, we can\n   *     even emit a stored file instead of a stored block (saving 5 bytes).\n   *     This is applicable only for zip (not gzip or zlib).\n   *   - creating new Huffman trees less frequently may not provide fast\n   *     adaptation to changes in the input data statistics. (Take for\n   *     example a binary file with poorly compressible code followed by\n   *     a highly compressible string table.) Smaller buffer sizes give\n   *     fast adaptation but have of course the overhead of transmitting\n   *     trees more frequently.\n   *   - I can't count above 4\n   */\n\n  this.last_lit = 0;      /* running index in l_buf */\n\n  this.d_buf = 0;\n  /* Buffer index for distances. To simplify the code, d_buf and l_buf have\n   * the same number of elements. To use different lengths, an extra flag\n   * array would be necessary.\n   */\n\n  this.opt_len = 0;       /* bit length of current block with optimal trees */\n  this.static_len = 0;    /* bit length of current block with static trees */\n  this.matches = 0;       /* number of string matches in current block */\n  this.insert = 0;        /* bytes at end of window left to insert */\n\n\n  this.bi_buf = 0;\n  /* Output buffer. bits are inserted starting at the bottom (least\n   * significant bits).\n   */\n  this.bi_valid = 0;\n  /* Number of valid bits in bi_buf.  All bits above the last valid bit\n   * are always zero.\n   */\n\n  // Used for window memory init. We safely ignore it for JS. That makes\n  // sense only for pointers and memory check tools.\n  //this.high_water = 0;\n  /* High water mark offset in window for initialized bytes -- bytes above\n   * this are set to zero in order to avoid memory check warnings when\n   * longest match routines access bytes past the input.  This is then\n   * updated to the new high water mark.\n   */\n}\n\n\nfunction deflateResetKeep(strm) {\n  var s;\n\n  if (!strm || !strm.state) {\n    return err(strm, Z_STREAM_ERROR);\n  }\n\n  strm.total_in = strm.total_out = 0;\n  strm.data_type = Z_UNKNOWN;\n\n  s = strm.state;\n  s.pending = 0;\n  s.pending_out = 0;\n\n  if (s.wrap < 0) {\n    s.wrap = -s.wrap;\n    /* was made negative by deflate(..., Z_FINISH); */\n  }\n  s.status = (s.wrap ? INIT_STATE : BUSY_STATE);\n  strm.adler = (s.wrap === 2) ?\n    0  // crc32(0, Z_NULL, 0)\n  :\n    1; // adler32(0, Z_NULL, 0)\n  s.last_flush = Z_NO_FLUSH;\n  trees._tr_init(s);\n  return Z_OK;\n}\n\n\nfunction deflateReset(strm) {\n  var ret = deflateResetKeep(strm);\n  if (ret === Z_OK) {\n    lm_init(strm.state);\n  }\n  return ret;\n}\n\n\nfunction deflateSetHeader(strm, head) {\n  if (!strm || !strm.state) { return Z_STREAM_ERROR; }\n  if (strm.state.wrap !== 2) { return Z_STREAM_ERROR; }\n  strm.state.gzhead = head;\n  return Z_OK;\n}\n\n\nfunction deflateInit2(strm, level, method, windowBits, memLevel, strategy) {\n  if (!strm) { // === Z_NULL\n    return Z_STREAM_ERROR;\n  }\n  var wrap = 1;\n\n  if (level === Z_DEFAULT_COMPRESSION) {\n    level = 6;\n  }\n\n  if (windowBits < 0) { /* suppress zlib wrapper */\n    wrap = 0;\n    windowBits = -windowBits;\n  }\n\n  else if (windowBits > 15) {\n    wrap = 2;           /* write gzip wrapper instead */\n    windowBits -= 16;\n  }\n\n\n  if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method !== Z_DEFLATED ||\n    windowBits < 8 || windowBits > 15 || level < 0 || level > 9 ||\n    strategy < 0 || strategy > Z_FIXED) {\n    return err(strm, Z_STREAM_ERROR);\n  }\n\n\n  if (windowBits === 8) {\n    windowBits = 9;\n  }\n  /* until 256-byte window bug fixed */\n\n  var s = new DeflateState();\n\n  strm.state = s;\n  s.strm = strm;\n\n  s.wrap = wrap;\n  s.gzhead = null;\n  s.w_bits = windowBits;\n  s.w_size = 1 << s.w_bits;\n  s.w_mask = s.w_size - 1;\n\n  s.hash_bits = memLevel + 7;\n  s.hash_size = 1 << s.hash_bits;\n  s.hash_mask = s.hash_size - 1;\n  s.hash_shift = ~~((s.hash_bits + MIN_MATCH - 1) / MIN_MATCH);\n\n  s.window = new utils.Buf8(s.w_size * 2);\n  s.head = new utils.Buf16(s.hash_size);\n  s.prev = new utils.Buf16(s.w_size);\n\n  // Don't need mem init magic for JS.\n  //s.high_water = 0;  /* nothing written to s->window yet */\n\n  s.lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */\n\n  s.pending_buf_size = s.lit_bufsize * 4;\n\n  //overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2);\n  //s->pending_buf = (uchf *) overlay;\n  s.pending_buf = new utils.Buf8(s.pending_buf_size);\n\n  // It is offset from `s.pending_buf` (size is `s.lit_bufsize * 2`)\n  //s->d_buf = overlay + s->lit_bufsize/sizeof(ush);\n  s.d_buf = 1 * s.lit_bufsize;\n\n  //s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize;\n  s.l_buf = (1 + 2) * s.lit_bufsize;\n\n  s.level = level;\n  s.strategy = strategy;\n  s.method = method;\n\n  return deflateReset(strm);\n}\n\nfunction deflateInit(strm, level) {\n  return deflateInit2(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);\n}\n\n\nfunction deflate(strm, flush) {\n  var old_flush, s;\n  var beg, val; // for gzip header write only\n\n  if (!strm || !strm.state ||\n    flush > Z_BLOCK || flush < 0) {\n    return strm ? err(strm, Z_STREAM_ERROR) : Z_STREAM_ERROR;\n  }\n\n  s = strm.state;\n\n  if (!strm.output ||\n      (!strm.input && strm.avail_in !== 0) ||\n      (s.status === FINISH_STATE && flush !== Z_FINISH)) {\n    return err(strm, (strm.avail_out === 0) ? Z_BUF_ERROR : Z_STREAM_ERROR);\n  }\n\n  s.strm = strm; /* just in case */\n  old_flush = s.last_flush;\n  s.last_flush = flush;\n\n  /* Write the header */\n  if (s.status === INIT_STATE) {\n\n    if (s.wrap === 2) { // GZIP header\n      strm.adler = 0;  //crc32(0L, Z_NULL, 0);\n      put_byte(s, 31);\n      put_byte(s, 139);\n      put_byte(s, 8);\n      if (!s.gzhead) { // s->gzhead == Z_NULL\n        put_byte(s, 0);\n        put_byte(s, 0);\n        put_byte(s, 0);\n        put_byte(s, 0);\n        put_byte(s, 0);\n        put_byte(s, s.level === 9 ? 2 :\n                    (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ?\n                     4 : 0));\n        put_byte(s, OS_CODE);\n        s.status = BUSY_STATE;\n      }\n      else {\n        put_byte(s, (s.gzhead.text ? 1 : 0) +\n                    (s.gzhead.hcrc ? 2 : 0) +\n                    (!s.gzhead.extra ? 0 : 4) +\n                    (!s.gzhead.name ? 0 : 8) +\n                    (!s.gzhead.comment ? 0 : 16)\n        );\n        put_byte(s, s.gzhead.time & 0xff);\n        put_byte(s, (s.gzhead.time >> 8) & 0xff);\n        put_byte(s, (s.gzhead.time >> 16) & 0xff);\n        put_byte(s, (s.gzhead.time >> 24) & 0xff);\n        put_byte(s, s.level === 9 ? 2 :\n                    (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ?\n                     4 : 0));\n        put_byte(s, s.gzhead.os & 0xff);\n        if (s.gzhead.extra && s.gzhead.extra.length) {\n          put_byte(s, s.gzhead.extra.length & 0xff);\n          put_byte(s, (s.gzhead.extra.length >> 8) & 0xff);\n        }\n        if (s.gzhead.hcrc) {\n          strm.adler = crc32(strm.adler, s.pending_buf, s.pending, 0);\n        }\n        s.gzindex = 0;\n        s.status = EXTRA_STATE;\n      }\n    }\n    else // DEFLATE header\n    {\n      var header = (Z_DEFLATED + ((s.w_bits - 8) << 4)) << 8;\n      var level_flags = -1;\n\n      if (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2) {\n        level_flags = 0;\n      } else if (s.level < 6) {\n        level_flags = 1;\n      } else if (s.level === 6) {\n        level_flags = 2;\n      } else {\n        level_flags = 3;\n      }\n      header |= (level_flags << 6);\n      if (s.strstart !== 0) { header |= PRESET_DICT; }\n      header += 31 - (header % 31);\n\n      s.status = BUSY_STATE;\n      putShortMSB(s, header);\n\n      /* Save the adler32 of the preset dictionary: */\n      if (s.strstart !== 0) {\n        putShortMSB(s, strm.adler >>> 16);\n        putShortMSB(s, strm.adler & 0xffff);\n      }\n      strm.adler = 1; // adler32(0L, Z_NULL, 0);\n    }\n  }\n\n//#ifdef GZIP\n  if (s.status === EXTRA_STATE) {\n    if (s.gzhead.extra/* != Z_NULL*/) {\n      beg = s.pending;  /* start of bytes to update crc */\n\n      while (s.gzindex < (s.gzhead.extra.length & 0xffff)) {\n        if (s.pending === s.pending_buf_size) {\n          if (s.gzhead.hcrc && s.pending > beg) {\n            strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);\n          }\n          flush_pending(strm);\n          beg = s.pending;\n          if (s.pending === s.pending_buf_size) {\n            break;\n          }\n        }\n        put_byte(s, s.gzhead.extra[s.gzindex] & 0xff);\n        s.gzindex++;\n      }\n      if (s.gzhead.hcrc && s.pending > beg) {\n        strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);\n      }\n      if (s.gzindex === s.gzhead.extra.length) {\n        s.gzindex = 0;\n        s.status = NAME_STATE;\n      }\n    }\n    else {\n      s.status = NAME_STATE;\n    }\n  }\n  if (s.status === NAME_STATE) {\n    if (s.gzhead.name/* != Z_NULL*/) {\n      beg = s.pending;  /* start of bytes to update crc */\n      //int val;\n\n      do {\n        if (s.pending === s.pending_buf_size) {\n          if (s.gzhead.hcrc && s.pending > beg) {\n            strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);\n          }\n          flush_pending(strm);\n          beg = s.pending;\n          if (s.pending === s.pending_buf_size) {\n            val = 1;\n            break;\n          }\n        }\n        // JS specific: little magic to add zero terminator to end of string\n        if (s.gzindex < s.gzhead.name.length) {\n          val = s.gzhead.name.charCodeAt(s.gzindex++) & 0xff;\n        } else {\n          val = 0;\n        }\n        put_byte(s, val);\n      } while (val !== 0);\n\n      if (s.gzhead.hcrc && s.pending > beg) {\n        strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);\n      }\n      if (val === 0) {\n        s.gzindex = 0;\n        s.status = COMMENT_STATE;\n      }\n    }\n    else {\n      s.status = COMMENT_STATE;\n    }\n  }\n  if (s.status === COMMENT_STATE) {\n    if (s.gzhead.comment/* != Z_NULL*/) {\n      beg = s.pending;  /* start of bytes to update crc */\n      //int val;\n\n      do {\n        if (s.pending === s.pending_buf_size) {\n          if (s.gzhead.hcrc && s.pending > beg) {\n            strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);\n          }\n          flush_pending(strm);\n          beg = s.pending;\n          if (s.pending === s.pending_buf_size) {\n            val = 1;\n            break;\n          }\n        }\n        // JS specific: little magic to add zero terminator to end of string\n        if (s.gzindex < s.gzhead.comment.length) {\n          val = s.gzhead.comment.charCodeAt(s.gzindex++) & 0xff;\n        } else {\n          val = 0;\n        }\n        put_byte(s, val);\n      } while (val !== 0);\n\n      if (s.gzhead.hcrc && s.pending > beg) {\n        strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);\n      }\n      if (val === 0) {\n        s.status = HCRC_STATE;\n      }\n    }\n    else {\n      s.status = HCRC_STATE;\n    }\n  }\n  if (s.status === HCRC_STATE) {\n    if (s.gzhead.hcrc) {\n      if (s.pending + 2 > s.pending_buf_size) {\n        flush_pending(strm);\n      }\n      if (s.pending + 2 <= s.pending_buf_size) {\n        put_byte(s, strm.adler & 0xff);\n        put_byte(s, (strm.adler >> 8) & 0xff);\n        strm.adler = 0; //crc32(0L, Z_NULL, 0);\n        s.status = BUSY_STATE;\n      }\n    }\n    else {\n      s.status = BUSY_STATE;\n    }\n  }\n//#endif\n\n  /* Flush as much pending output as possible */\n  if (s.pending !== 0) {\n    flush_pending(strm);\n    if (strm.avail_out === 0) {\n      /* Since avail_out is 0, deflate will be called again with\n       * more output space, but possibly with both pending and\n       * avail_in equal to zero. There won't be anything to do,\n       * but this is not an error situation so make sure we\n       * return OK instead of BUF_ERROR at next call of deflate:\n       */\n      s.last_flush = -1;\n      return Z_OK;\n    }\n\n    /* Make sure there is something to do and avoid duplicate consecutive\n     * flushes. For repeated and useless calls with Z_FINISH, we keep\n     * returning Z_STREAM_END instead of Z_BUF_ERROR.\n     */\n  } else if (strm.avail_in === 0 && rank(flush) <= rank(old_flush) &&\n    flush !== Z_FINISH) {\n    return err(strm, Z_BUF_ERROR);\n  }\n\n  /* User must not provide more input after the first FINISH: */\n  if (s.status === FINISH_STATE && strm.avail_in !== 0) {\n    return err(strm, Z_BUF_ERROR);\n  }\n\n  /* Start a new block or continue the current one.\n   */\n  if (strm.avail_in !== 0 || s.lookahead !== 0 ||\n    (flush !== Z_NO_FLUSH && s.status !== FINISH_STATE)) {\n    var bstate = (s.strategy === Z_HUFFMAN_ONLY) ? deflate_huff(s, flush) :\n      (s.strategy === Z_RLE ? deflate_rle(s, flush) :\n        configuration_table[s.level].func(s, flush));\n\n    if (bstate === BS_FINISH_STARTED || bstate === BS_FINISH_DONE) {\n      s.status = FINISH_STATE;\n    }\n    if (bstate === BS_NEED_MORE || bstate === BS_FINISH_STARTED) {\n      if (strm.avail_out === 0) {\n        s.last_flush = -1;\n        /* avoid BUF_ERROR next call, see above */\n      }\n      return Z_OK;\n      /* If flush != Z_NO_FLUSH && avail_out == 0, the next call\n       * of deflate should use the same flush parameter to make sure\n       * that the flush is complete. So we don't have to output an\n       * empty block here, this will be done at next call. This also\n       * ensures that for a very small output buffer, we emit at most\n       * one empty block.\n       */\n    }\n    if (bstate === BS_BLOCK_DONE) {\n      if (flush === Z_PARTIAL_FLUSH) {\n        trees._tr_align(s);\n      }\n      else if (flush !== Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */\n\n        trees._tr_stored_block(s, 0, 0, false);\n        /* For a full flush, this empty block will be recognized\n         * as a special marker by inflate_sync().\n         */\n        if (flush === Z_FULL_FLUSH) {\n          /*** CLEAR_HASH(s); ***/             /* forget history */\n          zero(s.head); // Fill with NIL (= 0);\n\n          if (s.lookahead === 0) {\n            s.strstart = 0;\n            s.block_start = 0;\n            s.insert = 0;\n          }\n        }\n      }\n      flush_pending(strm);\n      if (strm.avail_out === 0) {\n        s.last_flush = -1; /* avoid BUF_ERROR at next call, see above */\n        return Z_OK;\n      }\n    }\n  }\n  //Assert(strm->avail_out > 0, \"bug2\");\n  //if (strm.avail_out <= 0) { throw new Error(\"bug2\");}\n\n  if (flush !== Z_FINISH) { return Z_OK; }\n  if (s.wrap <= 0) { return Z_STREAM_END; }\n\n  /* Write the trailer */\n  if (s.wrap === 2) {\n    put_byte(s, strm.adler & 0xff);\n    put_byte(s, (strm.adler >> 8) & 0xff);\n    put_byte(s, (strm.adler >> 16) & 0xff);\n    put_byte(s, (strm.adler >> 24) & 0xff);\n    put_byte(s, strm.total_in & 0xff);\n    put_byte(s, (strm.total_in >> 8) & 0xff);\n    put_byte(s, (strm.total_in >> 16) & 0xff);\n    put_byte(s, (strm.total_in >> 24) & 0xff);\n  }\n  else\n  {\n    putShortMSB(s, strm.adler >>> 16);\n    putShortMSB(s, strm.adler & 0xffff);\n  }\n\n  flush_pending(strm);\n  /* If avail_out is zero, the application will call deflate again\n   * to flush the rest.\n   */\n  if (s.wrap > 0) { s.wrap = -s.wrap; }\n  /* write the trailer only once! */\n  return s.pending !== 0 ? Z_OK : Z_STREAM_END;\n}\n\nfunction deflateEnd(strm) {\n  var status;\n\n  if (!strm/*== Z_NULL*/ || !strm.state/*== Z_NULL*/) {\n    return Z_STREAM_ERROR;\n  }\n\n  status = strm.state.status;\n  if (status !== INIT_STATE &&\n    status !== EXTRA_STATE &&\n    status !== NAME_STATE &&\n    status !== COMMENT_STATE &&\n    status !== HCRC_STATE &&\n    status !== BUSY_STATE &&\n    status !== FINISH_STATE\n  ) {\n    return err(strm, Z_STREAM_ERROR);\n  }\n\n  strm.state = null;\n\n  return status === BUSY_STATE ? err(strm, Z_DATA_ERROR) : Z_OK;\n}\n\n\n/* =========================================================================\n * Initializes the compression dictionary from the given byte\n * sequence without producing any compressed output.\n */\nfunction deflateSetDictionary(strm, dictionary) {\n  var dictLength = dictionary.length;\n\n  var s;\n  var str, n;\n  var wrap;\n  var avail;\n  var next;\n  var input;\n  var tmpDict;\n\n  if (!strm/*== Z_NULL*/ || !strm.state/*== Z_NULL*/) {\n    return Z_STREAM_ERROR;\n  }\n\n  s = strm.state;\n  wrap = s.wrap;\n\n  if (wrap === 2 || (wrap === 1 && s.status !== INIT_STATE) || s.lookahead) {\n    return Z_STREAM_ERROR;\n  }\n\n  /* when using zlib wrappers, compute Adler-32 for provided dictionary */\n  if (wrap === 1) {\n    /* adler32(strm->adler, dictionary, dictLength); */\n    strm.adler = adler32(strm.adler, dictionary, dictLength, 0);\n  }\n\n  s.wrap = 0;   /* avoid computing Adler-32 in read_buf */\n\n  /* if dictionary would fill window, just replace the history */\n  if (dictLength >= s.w_size) {\n    if (wrap === 0) {            /* already empty otherwise */\n      /*** CLEAR_HASH(s); ***/\n      zero(s.head); // Fill with NIL (= 0);\n      s.strstart = 0;\n      s.block_start = 0;\n      s.insert = 0;\n    }\n    /* use the tail */\n    // dictionary = dictionary.slice(dictLength - s.w_size);\n    tmpDict = new utils.Buf8(s.w_size);\n    utils.arraySet(tmpDict, dictionary, dictLength - s.w_size, s.w_size, 0);\n    dictionary = tmpDict;\n    dictLength = s.w_size;\n  }\n  /* insert dictionary into window and hash */\n  avail = strm.avail_in;\n  next = strm.next_in;\n  input = strm.input;\n  strm.avail_in = dictLength;\n  strm.next_in = 0;\n  strm.input = dictionary;\n  fill_window(s);\n  while (s.lookahead >= MIN_MATCH) {\n    str = s.strstart;\n    n = s.lookahead - (MIN_MATCH - 1);\n    do {\n      /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */\n      s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[str + MIN_MATCH - 1]) & s.hash_mask;\n\n      s.prev[str & s.w_mask] = s.head[s.ins_h];\n\n      s.head[s.ins_h] = str;\n      str++;\n    } while (--n);\n    s.strstart = str;\n    s.lookahead = MIN_MATCH - 1;\n    fill_window(s);\n  }\n  s.strstart += s.lookahead;\n  s.block_start = s.strstart;\n  s.insert = s.lookahead;\n  s.lookahead = 0;\n  s.match_length = s.prev_length = MIN_MATCH - 1;\n  s.match_available = 0;\n  strm.next_in = next;\n  strm.input = input;\n  strm.avail_in = avail;\n  s.wrap = wrap;\n  return Z_OK;\n}\n\n\nexports.deflateInit = deflateInit;\nexports.deflateInit2 = deflateInit2;\nexports.deflateReset = deflateReset;\nexports.deflateResetKeep = deflateResetKeep;\nexports.deflateSetHeader = deflateSetHeader;\nexports.deflate = deflate;\nexports.deflateEnd = deflateEnd;\nexports.deflateSetDictionary = deflateSetDictionary;\nexports.deflateInfo = 'pako deflate (from Nodeca project)';\n\n/* Not implemented\nexports.deflateBound = deflateBound;\nexports.deflateCopy = deflateCopy;\nexports.deflateParams = deflateParams;\nexports.deflatePending = deflatePending;\nexports.deflatePrime = deflatePrime;\nexports.deflateTune = deflateTune;\n*/\n","'use strict';\n\n// (C) 1995-2013 Jean-loup Gailly and Mark Adler\n// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//   claim that you wrote the original software. If you use this software\n//   in a product, an acknowledgment in the product documentation would be\n//   appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//   misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n\n/* eslint-disable space-unary-ops */\n\nvar utils = require('../utils/common');\n\n/* Public constants ==========================================================*/\n/* ===========================================================================*/\n\n\n//var Z_FILTERED          = 1;\n//var Z_HUFFMAN_ONLY      = 2;\n//var Z_RLE               = 3;\nvar Z_FIXED               = 4;\n//var Z_DEFAULT_STRATEGY  = 0;\n\n/* Possible values of the data_type field (though see inflate()) */\nvar Z_BINARY              = 0;\nvar Z_TEXT                = 1;\n//var Z_ASCII             = 1; // = Z_TEXT\nvar Z_UNKNOWN             = 2;\n\n/*============================================================================*/\n\n\nfunction zero(buf) { var len = buf.length; while (--len >= 0) { buf[len] = 0; } }\n\n// From zutil.h\n\nvar STORED_BLOCK = 0;\nvar STATIC_TREES = 1;\nvar DYN_TREES    = 2;\n/* The three kinds of block type */\n\nvar MIN_MATCH    = 3;\nvar MAX_MATCH    = 258;\n/* The minimum and maximum match lengths */\n\n// From deflate.h\n/* ===========================================================================\n * Internal compression state.\n */\n\nvar LENGTH_CODES  = 29;\n/* number of length codes, not counting the special END_BLOCK code */\n\nvar LITERALS      = 256;\n/* number of literal bytes 0..255 */\n\nvar L_CODES       = LITERALS + 1 + LENGTH_CODES;\n/* number of Literal or Length codes, including the END_BLOCK code */\n\nvar D_CODES       = 30;\n/* number of distance codes */\n\nvar BL_CODES      = 19;\n/* number of codes used to transfer the bit lengths */\n\nvar HEAP_SIZE     = 2 * L_CODES + 1;\n/* maximum heap size */\n\nvar MAX_BITS      = 15;\n/* All codes must not exceed MAX_BITS bits */\n\nvar Buf_size      = 16;\n/* size of bit buffer in bi_buf */\n\n\n/* ===========================================================================\n * Constants\n */\n\nvar MAX_BL_BITS = 7;\n/* Bit length codes must not exceed MAX_BL_BITS bits */\n\nvar END_BLOCK   = 256;\n/* end of block literal code */\n\nvar REP_3_6     = 16;\n/* repeat previous bit length 3-6 times (2 bits of repeat count) */\n\nvar REPZ_3_10   = 17;\n/* repeat a zero length 3-10 times  (3 bits of repeat count) */\n\nvar REPZ_11_138 = 18;\n/* repeat a zero length 11-138 times  (7 bits of repeat count) */\n\n/* eslint-disable comma-spacing,array-bracket-spacing */\nvar extra_lbits =   /* extra bits for each length code */\n  [0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0];\n\nvar extra_dbits =   /* extra bits for each distance code */\n  [0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13];\n\nvar extra_blbits =  /* extra bits for each bit length code */\n  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7];\n\nvar bl_order =\n  [16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15];\n/* eslint-enable comma-spacing,array-bracket-spacing */\n\n/* The lengths of the bit length codes are sent in order of decreasing\n * probability, to avoid transmitting the lengths for unused bit length codes.\n */\n\n/* ===========================================================================\n * Local data. These are initialized only once.\n */\n\n// We pre-fill arrays with 0 to avoid uninitialized gaps\n\nvar DIST_CODE_LEN = 512; /* see definition of array dist_code below */\n\n// !!!! Use flat array instead of structure, Freq = i*2, Len = i*2+1\nvar static_ltree  = new Array((L_CODES + 2) * 2);\nzero(static_ltree);\n/* The static literal tree. Since the bit lengths are imposed, there is no\n * need for the L_CODES extra codes used during heap construction. However\n * The codes 286 and 287 are needed to build a canonical tree (see _tr_init\n * below).\n */\n\nvar static_dtree  = new Array(D_CODES * 2);\nzero(static_dtree);\n/* The static distance tree. (Actually a trivial tree since all codes use\n * 5 bits.)\n */\n\nvar _dist_code    = new Array(DIST_CODE_LEN);\nzero(_dist_code);\n/* Distance codes. The first 256 values correspond to the distances\n * 3 .. 258, the last 256 values correspond to the top 8 bits of\n * the 15 bit distances.\n */\n\nvar _length_code  = new Array(MAX_MATCH - MIN_MATCH + 1);\nzero(_length_code);\n/* length code for each normalized match length (0 == MIN_MATCH) */\n\nvar base_length   = new Array(LENGTH_CODES);\nzero(base_length);\n/* First normalized length for each code (0 = MIN_MATCH) */\n\nvar base_dist     = new Array(D_CODES);\nzero(base_dist);\n/* First normalized distance for each code (0 = distance of 1) */\n\n\nfunction StaticTreeDesc(static_tree, extra_bits, extra_base, elems, max_length) {\n\n  this.static_tree  = static_tree;  /* static tree or NULL */\n  this.extra_bits   = extra_bits;   /* extra bits for each code or NULL */\n  this.extra_base   = extra_base;   /* base index for extra_bits */\n  this.elems        = elems;        /* max number of elements in the tree */\n  this.max_length   = max_length;   /* max bit length for the codes */\n\n  // show if `static_tree` has data or dummy - needed for monomorphic objects\n  this.has_stree    = static_tree && static_tree.length;\n}\n\n\nvar static_l_desc;\nvar static_d_desc;\nvar static_bl_desc;\n\n\nfunction TreeDesc(dyn_tree, stat_desc) {\n  this.dyn_tree = dyn_tree;     /* the dynamic tree */\n  this.max_code = 0;            /* largest code with non zero frequency */\n  this.stat_desc = stat_desc;   /* the corresponding static tree */\n}\n\n\n\nfunction d_code(dist) {\n  return dist < 256 ? _dist_code[dist] : _dist_code[256 + (dist >>> 7)];\n}\n\n\n/* ===========================================================================\n * Output a short LSB first on the stream.\n * IN assertion: there is enough room in pendingBuf.\n */\nfunction put_short(s, w) {\n//    put_byte(s, (uch)((w) & 0xff));\n//    put_byte(s, (uch)((ush)(w) >> 8));\n  s.pending_buf[s.pending++] = (w) & 0xff;\n  s.pending_buf[s.pending++] = (w >>> 8) & 0xff;\n}\n\n\n/* ===========================================================================\n * Send a value on a given number of bits.\n * IN assertion: length <= 16 and value fits in length bits.\n */\nfunction send_bits(s, value, length) {\n  if (s.bi_valid > (Buf_size - length)) {\n    s.bi_buf |= (value << s.bi_valid) & 0xffff;\n    put_short(s, s.bi_buf);\n    s.bi_buf = value >> (Buf_size - s.bi_valid);\n    s.bi_valid += length - Buf_size;\n  } else {\n    s.bi_buf |= (value << s.bi_valid) & 0xffff;\n    s.bi_valid += length;\n  }\n}\n\n\nfunction send_code(s, c, tree) {\n  send_bits(s, tree[c * 2]/*.Code*/, tree[c * 2 + 1]/*.Len*/);\n}\n\n\n/* ===========================================================================\n * Reverse the first len bits of a code, using straightforward code (a faster\n * method would use a table)\n * IN assertion: 1 <= len <= 15\n */\nfunction bi_reverse(code, len) {\n  var res = 0;\n  do {\n    res |= code & 1;\n    code >>>= 1;\n    res <<= 1;\n  } while (--len > 0);\n  return res >>> 1;\n}\n\n\n/* ===========================================================================\n * Flush the bit buffer, keeping at most 7 bits in it.\n */\nfunction bi_flush(s) {\n  if (s.bi_valid === 16) {\n    put_short(s, s.bi_buf);\n    s.bi_buf = 0;\n    s.bi_valid = 0;\n\n  } else if (s.bi_valid >= 8) {\n    s.pending_buf[s.pending++] = s.bi_buf & 0xff;\n    s.bi_buf >>= 8;\n    s.bi_valid -= 8;\n  }\n}\n\n\n/* ===========================================================================\n * Compute the optimal bit lengths for a tree and update the total bit length\n * for the current block.\n * IN assertion: the fields freq and dad are set, heap[heap_max] and\n *    above are the tree nodes sorted by increasing frequency.\n * OUT assertions: the field len is set to the optimal bit length, the\n *     array bl_count contains the frequencies for each bit length.\n *     The length opt_len is updated; static_len is also updated if stree is\n *     not null.\n */\nfunction gen_bitlen(s, desc)\n//    deflate_state *s;\n//    tree_desc *desc;    /* the tree descriptor */\n{\n  var tree            = desc.dyn_tree;\n  var max_code        = desc.max_code;\n  var stree           = desc.stat_desc.static_tree;\n  var has_stree       = desc.stat_desc.has_stree;\n  var extra           = desc.stat_desc.extra_bits;\n  var base            = desc.stat_desc.extra_base;\n  var max_length      = desc.stat_desc.max_length;\n  var h;              /* heap index */\n  var n, m;           /* iterate over the tree elements */\n  var bits;           /* bit length */\n  var xbits;          /* extra bits */\n  var f;              /* frequency */\n  var overflow = 0;   /* number of elements with bit length too large */\n\n  for (bits = 0; bits <= MAX_BITS; bits++) {\n    s.bl_count[bits] = 0;\n  }\n\n  /* In a first pass, compute the optimal bit lengths (which may\n   * overflow in the case of the bit length tree).\n   */\n  tree[s.heap[s.heap_max] * 2 + 1]/*.Len*/ = 0; /* root of the heap */\n\n  for (h = s.heap_max + 1; h < HEAP_SIZE; h++) {\n    n = s.heap[h];\n    bits = tree[tree[n * 2 + 1]/*.Dad*/ * 2 + 1]/*.Len*/ + 1;\n    if (bits > max_length) {\n      bits = max_length;\n      overflow++;\n    }\n    tree[n * 2 + 1]/*.Len*/ = bits;\n    /* We overwrite tree[n].Dad which is no longer needed */\n\n    if (n > max_code) { continue; } /* not a leaf node */\n\n    s.bl_count[bits]++;\n    xbits = 0;\n    if (n >= base) {\n      xbits = extra[n - base];\n    }\n    f = tree[n * 2]/*.Freq*/;\n    s.opt_len += f * (bits + xbits);\n    if (has_stree) {\n      s.static_len += f * (stree[n * 2 + 1]/*.Len*/ + xbits);\n    }\n  }\n  if (overflow === 0) { return; }\n\n  // Trace((stderr,\"\\nbit length overflow\\n\"));\n  /* This happens for example on obj2 and pic of the Calgary corpus */\n\n  /* Find the first bit length which could increase: */\n  do {\n    bits = max_length - 1;\n    while (s.bl_count[bits] === 0) { bits--; }\n    s.bl_count[bits]--;      /* move one leaf down the tree */\n    s.bl_count[bits + 1] += 2; /* move one overflow item as its brother */\n    s.bl_count[max_length]--;\n    /* The brother of the overflow item also moves one step up,\n     * but this does not affect bl_count[max_length]\n     */\n    overflow -= 2;\n  } while (overflow > 0);\n\n  /* Now recompute all bit lengths, scanning in increasing frequency.\n   * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all\n   * lengths instead of fixing only the wrong ones. This idea is taken\n   * from 'ar' written by Haruhiko Okumura.)\n   */\n  for (bits = max_length; bits !== 0; bits--) {\n    n = s.bl_count[bits];\n    while (n !== 0) {\n      m = s.heap[--h];\n      if (m > max_code) { continue; }\n      if (tree[m * 2 + 1]/*.Len*/ !== bits) {\n        // Trace((stderr,\"code %d bits %d->%d\\n\", m, tree[m].Len, bits));\n        s.opt_len += (bits - tree[m * 2 + 1]/*.Len*/) * tree[m * 2]/*.Freq*/;\n        tree[m * 2 + 1]/*.Len*/ = bits;\n      }\n      n--;\n    }\n  }\n}\n\n\n/* ===========================================================================\n * Generate the codes for a given tree and bit counts (which need not be\n * optimal).\n * IN assertion: the array bl_count contains the bit length statistics for\n * the given tree and the field len is set for all tree elements.\n * OUT assertion: the field code is set for all tree elements of non\n *     zero code length.\n */\nfunction gen_codes(tree, max_code, bl_count)\n//    ct_data *tree;             /* the tree to decorate */\n//    int max_code;              /* largest code with non zero frequency */\n//    ushf *bl_count;            /* number of codes at each bit length */\n{\n  var next_code = new Array(MAX_BITS + 1); /* next code value for each bit length */\n  var code = 0;              /* running code value */\n  var bits;                  /* bit index */\n  var n;                     /* code index */\n\n  /* The distribution counts are first used to generate the code values\n   * without bit reversal.\n   */\n  for (bits = 1; bits <= MAX_BITS; bits++) {\n    next_code[bits] = code = (code + bl_count[bits - 1]) << 1;\n  }\n  /* Check that the bit counts in bl_count are consistent. The last code\n   * must be all ones.\n   */\n  //Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,\n  //        \"inconsistent bit counts\");\n  //Tracev((stderr,\"\\ngen_codes: max_code %d \", max_code));\n\n  for (n = 0;  n <= max_code; n++) {\n    var len = tree[n * 2 + 1]/*.Len*/;\n    if (len === 0) { continue; }\n    /* Now reverse the bits */\n    tree[n * 2]/*.Code*/ = bi_reverse(next_code[len]++, len);\n\n    //Tracecv(tree != static_ltree, (stderr,\"\\nn %3d %c l %2d c %4x (%x) \",\n    //     n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1));\n  }\n}\n\n\n/* ===========================================================================\n * Initialize the various 'constant' tables.\n */\nfunction tr_static_init() {\n  var n;        /* iterates over tree elements */\n  var bits;     /* bit counter */\n  var length;   /* length value */\n  var code;     /* code value */\n  var dist;     /* distance index */\n  var bl_count = new Array(MAX_BITS + 1);\n  /* number of codes at each bit length for an optimal tree */\n\n  // do check in _tr_init()\n  //if (static_init_done) return;\n\n  /* For some embedded targets, global variables are not initialized: */\n/*#ifdef NO_INIT_GLOBAL_POINTERS\n  static_l_desc.static_tree = static_ltree;\n  static_l_desc.extra_bits = extra_lbits;\n  static_d_desc.static_tree = static_dtree;\n  static_d_desc.extra_bits = extra_dbits;\n  static_bl_desc.extra_bits = extra_blbits;\n#endif*/\n\n  /* Initialize the mapping length (0..255) -> length code (0..28) */\n  length = 0;\n  for (code = 0; code < LENGTH_CODES - 1; code++) {\n    base_length[code] = length;\n    for (n = 0; n < (1 << extra_lbits[code]); n++) {\n      _length_code[length++] = code;\n    }\n  }\n  //Assert (length == 256, \"tr_static_init: length != 256\");\n  /* Note that the length 255 (match length 258) can be represented\n   * in two different ways: code 284 + 5 bits or code 285, so we\n   * overwrite length_code[255] to use the best encoding:\n   */\n  _length_code[length - 1] = code;\n\n  /* Initialize the mapping dist (0..32K) -> dist code (0..29) */\n  dist = 0;\n  for (code = 0; code < 16; code++) {\n    base_dist[code] = dist;\n    for (n = 0; n < (1 << extra_dbits[code]); n++) {\n      _dist_code[dist++] = code;\n    }\n  }\n  //Assert (dist == 256, \"tr_static_init: dist != 256\");\n  dist >>= 7; /* from now on, all distances are divided by 128 */\n  for (; code < D_CODES; code++) {\n    base_dist[code] = dist << 7;\n    for (n = 0; n < (1 << (extra_dbits[code] - 7)); n++) {\n      _dist_code[256 + dist++] = code;\n    }\n  }\n  //Assert (dist == 256, \"tr_static_init: 256+dist != 512\");\n\n  /* Construct the codes of the static literal tree */\n  for (bits = 0; bits <= MAX_BITS; bits++) {\n    bl_count[bits] = 0;\n  }\n\n  n = 0;\n  while (n <= 143) {\n    static_ltree[n * 2 + 1]/*.Len*/ = 8;\n    n++;\n    bl_count[8]++;\n  }\n  while (n <= 255) {\n    static_ltree[n * 2 + 1]/*.Len*/ = 9;\n    n++;\n    bl_count[9]++;\n  }\n  while (n <= 279) {\n    static_ltree[n * 2 + 1]/*.Len*/ = 7;\n    n++;\n    bl_count[7]++;\n  }\n  while (n <= 287) {\n    static_ltree[n * 2 + 1]/*.Len*/ = 8;\n    n++;\n    bl_count[8]++;\n  }\n  /* Codes 286 and 287 do not exist, but we must include them in the\n   * tree construction to get a canonical Huffman tree (longest code\n   * all ones)\n   */\n  gen_codes(static_ltree, L_CODES + 1, bl_count);\n\n  /* The static distance tree is trivial: */\n  for (n = 0; n < D_CODES; n++) {\n    static_dtree[n * 2 + 1]/*.Len*/ = 5;\n    static_dtree[n * 2]/*.Code*/ = bi_reverse(n, 5);\n  }\n\n  // Now data ready and we can init static trees\n  static_l_desc = new StaticTreeDesc(static_ltree, extra_lbits, LITERALS + 1, L_CODES, MAX_BITS);\n  static_d_desc = new StaticTreeDesc(static_dtree, extra_dbits, 0,          D_CODES, MAX_BITS);\n  static_bl_desc = new StaticTreeDesc(new Array(0), extra_blbits, 0,         BL_CODES, MAX_BL_BITS);\n\n  //static_init_done = true;\n}\n\n\n/* ===========================================================================\n * Initialize a new block.\n */\nfunction init_block(s) {\n  var n; /* iterates over tree elements */\n\n  /* Initialize the trees. */\n  for (n = 0; n < L_CODES;  n++) { s.dyn_ltree[n * 2]/*.Freq*/ = 0; }\n  for (n = 0; n < D_CODES;  n++) { s.dyn_dtree[n * 2]/*.Freq*/ = 0; }\n  for (n = 0; n < BL_CODES; n++) { s.bl_tree[n * 2]/*.Freq*/ = 0; }\n\n  s.dyn_ltree[END_BLOCK * 2]/*.Freq*/ = 1;\n  s.opt_len = s.static_len = 0;\n  s.last_lit = s.matches = 0;\n}\n\n\n/* ===========================================================================\n * Flush the bit buffer and align the output on a byte boundary\n */\nfunction bi_windup(s)\n{\n  if (s.bi_valid > 8) {\n    put_short(s, s.bi_buf);\n  } else if (s.bi_valid > 0) {\n    //put_byte(s, (Byte)s->bi_buf);\n    s.pending_buf[s.pending++] = s.bi_buf;\n  }\n  s.bi_buf = 0;\n  s.bi_valid = 0;\n}\n\n/* ===========================================================================\n * Copy a stored block, storing first the length and its\n * one's complement if requested.\n */\nfunction copy_block(s, buf, len, header)\n//DeflateState *s;\n//charf    *buf;    /* the input data */\n//unsigned len;     /* its length */\n//int      header;  /* true if block header must be written */\n{\n  bi_windup(s);        /* align on byte boundary */\n\n  if (header) {\n    put_short(s, len);\n    put_short(s, ~len);\n  }\n//  while (len--) {\n//    put_byte(s, *buf++);\n//  }\n  utils.arraySet(s.pending_buf, s.window, buf, len, s.pending);\n  s.pending += len;\n}\n\n/* ===========================================================================\n * Compares to subtrees, using the tree depth as tie breaker when\n * the subtrees have equal frequency. This minimizes the worst case length.\n */\nfunction smaller(tree, n, m, depth) {\n  var _n2 = n * 2;\n  var _m2 = m * 2;\n  return (tree[_n2]/*.Freq*/ < tree[_m2]/*.Freq*/ ||\n         (tree[_n2]/*.Freq*/ === tree[_m2]/*.Freq*/ && depth[n] <= depth[m]));\n}\n\n/* ===========================================================================\n * Restore the heap property by moving down the tree starting at node k,\n * exchanging a node with the smallest of its two sons if necessary, stopping\n * when the heap property is re-established (each father smaller than its\n * two sons).\n */\nfunction pqdownheap(s, tree, k)\n//    deflate_state *s;\n//    ct_data *tree;  /* the tree to restore */\n//    int k;               /* node to move down */\n{\n  var v = s.heap[k];\n  var j = k << 1;  /* left son of k */\n  while (j <= s.heap_len) {\n    /* Set j to the smallest of the two sons: */\n    if (j < s.heap_len &&\n      smaller(tree, s.heap[j + 1], s.heap[j], s.depth)) {\n      j++;\n    }\n    /* Exit if v is smaller than both sons */\n    if (smaller(tree, v, s.heap[j], s.depth)) { break; }\n\n    /* Exchange v with the smallest son */\n    s.heap[k] = s.heap[j];\n    k = j;\n\n    /* And continue down the tree, setting j to the left son of k */\n    j <<= 1;\n  }\n  s.heap[k] = v;\n}\n\n\n// inlined manually\n// var SMALLEST = 1;\n\n/* ===========================================================================\n * Send the block data compressed using the given Huffman trees\n */\nfunction compress_block(s, ltree, dtree)\n//    deflate_state *s;\n//    const ct_data *ltree; /* literal tree */\n//    const ct_data *dtree; /* distance tree */\n{\n  var dist;           /* distance of matched string */\n  var lc;             /* match length or unmatched char (if dist == 0) */\n  var lx = 0;         /* running index in l_buf */\n  var code;           /* the code to send */\n  var extra;          /* number of extra bits to send */\n\n  if (s.last_lit !== 0) {\n    do {\n      dist = (s.pending_buf[s.d_buf + lx * 2] << 8) | (s.pending_buf[s.d_buf + lx * 2 + 1]);\n      lc = s.pending_buf[s.l_buf + lx];\n      lx++;\n\n      if (dist === 0) {\n        send_code(s, lc, ltree); /* send a literal byte */\n        //Tracecv(isgraph(lc), (stderr,\" '%c' \", lc));\n      } else {\n        /* Here, lc is the match length - MIN_MATCH */\n        code = _length_code[lc];\n        send_code(s, code + LITERALS + 1, ltree); /* send the length code */\n        extra = extra_lbits[code];\n        if (extra !== 0) {\n          lc -= base_length[code];\n          send_bits(s, lc, extra);       /* send the extra length bits */\n        }\n        dist--; /* dist is now the match distance - 1 */\n        code = d_code(dist);\n        //Assert (code < D_CODES, \"bad d_code\");\n\n        send_code(s, code, dtree);       /* send the distance code */\n        extra = extra_dbits[code];\n        if (extra !== 0) {\n          dist -= base_dist[code];\n          send_bits(s, dist, extra);   /* send the extra distance bits */\n        }\n      } /* literal or match pair ? */\n\n      /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */\n      //Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx,\n      //       \"pendingBuf overflow\");\n\n    } while (lx < s.last_lit);\n  }\n\n  send_code(s, END_BLOCK, ltree);\n}\n\n\n/* ===========================================================================\n * Construct one Huffman tree and assigns the code bit strings and lengths.\n * Update the total bit length for the current block.\n * IN assertion: the field freq is set for all tree elements.\n * OUT assertions: the fields len and code are set to the optimal bit length\n *     and corresponding code. The length opt_len is updated; static_len is\n *     also updated if stree is not null. The field max_code is set.\n */\nfunction build_tree(s, desc)\n//    deflate_state *s;\n//    tree_desc *desc; /* the tree descriptor */\n{\n  var tree     = desc.dyn_tree;\n  var stree    = desc.stat_desc.static_tree;\n  var has_stree = desc.stat_desc.has_stree;\n  var elems    = desc.stat_desc.elems;\n  var n, m;          /* iterate over heap elements */\n  var max_code = -1; /* largest code with non zero frequency */\n  var node;          /* new node being created */\n\n  /* Construct the initial heap, with least frequent element in\n   * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].\n   * heap[0] is not used.\n   */\n  s.heap_len = 0;\n  s.heap_max = HEAP_SIZE;\n\n  for (n = 0; n < elems; n++) {\n    if (tree[n * 2]/*.Freq*/ !== 0) {\n      s.heap[++s.heap_len] = max_code = n;\n      s.depth[n] = 0;\n\n    } else {\n      tree[n * 2 + 1]/*.Len*/ = 0;\n    }\n  }\n\n  /* The pkzip format requires that at least one distance code exists,\n   * and that at least one bit should be sent even if there is only one\n   * possible code. So to avoid special checks later on we force at least\n   * two codes of non zero frequency.\n   */\n  while (s.heap_len < 2) {\n    node = s.heap[++s.heap_len] = (max_code < 2 ? ++max_code : 0);\n    tree[node * 2]/*.Freq*/ = 1;\n    s.depth[node] = 0;\n    s.opt_len--;\n\n    if (has_stree) {\n      s.static_len -= stree[node * 2 + 1]/*.Len*/;\n    }\n    /* node is 0 or 1 so it does not have extra bits */\n  }\n  desc.max_code = max_code;\n\n  /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,\n   * establish sub-heaps of increasing lengths:\n   */\n  for (n = (s.heap_len >> 1/*int /2*/); n >= 1; n--) { pqdownheap(s, tree, n); }\n\n  /* Construct the Huffman tree by repeatedly combining the least two\n   * frequent nodes.\n   */\n  node = elems;              /* next internal node of the tree */\n  do {\n    //pqremove(s, tree, n);  /* n = node of least frequency */\n    /*** pqremove ***/\n    n = s.heap[1/*SMALLEST*/];\n    s.heap[1/*SMALLEST*/] = s.heap[s.heap_len--];\n    pqdownheap(s, tree, 1/*SMALLEST*/);\n    /***/\n\n    m = s.heap[1/*SMALLEST*/]; /* m = node of next least frequency */\n\n    s.heap[--s.heap_max] = n; /* keep the nodes sorted by frequency */\n    s.heap[--s.heap_max] = m;\n\n    /* Create a new node father of n and m */\n    tree[node * 2]/*.Freq*/ = tree[n * 2]/*.Freq*/ + tree[m * 2]/*.Freq*/;\n    s.depth[node] = (s.depth[n] >= s.depth[m] ? s.depth[n] : s.depth[m]) + 1;\n    tree[n * 2 + 1]/*.Dad*/ = tree[m * 2 + 1]/*.Dad*/ = node;\n\n    /* and insert the new node in the heap */\n    s.heap[1/*SMALLEST*/] = node++;\n    pqdownheap(s, tree, 1/*SMALLEST*/);\n\n  } while (s.heap_len >= 2);\n\n  s.heap[--s.heap_max] = s.heap[1/*SMALLEST*/];\n\n  /* At this point, the fields freq and dad are set. We can now\n   * generate the bit lengths.\n   */\n  gen_bitlen(s, desc);\n\n  /* The field len is now set, we can generate the bit codes */\n  gen_codes(tree, max_code, s.bl_count);\n}\n\n\n/* ===========================================================================\n * Scan a literal or distance tree to determine the frequencies of the codes\n * in the bit length tree.\n */\nfunction scan_tree(s, tree, max_code)\n//    deflate_state *s;\n//    ct_data *tree;   /* the tree to be scanned */\n//    int max_code;    /* and its largest code of non zero frequency */\n{\n  var n;                     /* iterates over all tree elements */\n  var prevlen = -1;          /* last emitted length */\n  var curlen;                /* length of current code */\n\n  var nextlen = tree[0 * 2 + 1]/*.Len*/; /* length of next code */\n\n  var count = 0;             /* repeat count of the current code */\n  var max_count = 7;         /* max repeat count */\n  var min_count = 4;         /* min repeat count */\n\n  if (nextlen === 0) {\n    max_count = 138;\n    min_count = 3;\n  }\n  tree[(max_code + 1) * 2 + 1]/*.Len*/ = 0xffff; /* guard */\n\n  for (n = 0; n <= max_code; n++) {\n    curlen = nextlen;\n    nextlen = tree[(n + 1) * 2 + 1]/*.Len*/;\n\n    if (++count < max_count && curlen === nextlen) {\n      continue;\n\n    } else if (count < min_count) {\n      s.bl_tree[curlen * 2]/*.Freq*/ += count;\n\n    } else if (curlen !== 0) {\n\n      if (curlen !== prevlen) { s.bl_tree[curlen * 2]/*.Freq*/++; }\n      s.bl_tree[REP_3_6 * 2]/*.Freq*/++;\n\n    } else if (count <= 10) {\n      s.bl_tree[REPZ_3_10 * 2]/*.Freq*/++;\n\n    } else {\n      s.bl_tree[REPZ_11_138 * 2]/*.Freq*/++;\n    }\n\n    count = 0;\n    prevlen = curlen;\n\n    if (nextlen === 0) {\n      max_count = 138;\n      min_count = 3;\n\n    } else if (curlen === nextlen) {\n      max_count = 6;\n      min_count = 3;\n\n    } else {\n      max_count = 7;\n      min_count = 4;\n    }\n  }\n}\n\n\n/* ===========================================================================\n * Send a literal or distance tree in compressed form, using the codes in\n * bl_tree.\n */\nfunction send_tree(s, tree, max_code)\n//    deflate_state *s;\n//    ct_data *tree; /* the tree to be scanned */\n//    int max_code;       /* and its largest code of non zero frequency */\n{\n  var n;                     /* iterates over all tree elements */\n  var prevlen = -1;          /* last emitted length */\n  var curlen;                /* length of current code */\n\n  var nextlen = tree[0 * 2 + 1]/*.Len*/; /* length of next code */\n\n  var count = 0;             /* repeat count of the current code */\n  var max_count = 7;         /* max repeat count */\n  var min_count = 4;         /* min repeat count */\n\n  /* tree[max_code+1].Len = -1; */  /* guard already set */\n  if (nextlen === 0) {\n    max_count = 138;\n    min_count = 3;\n  }\n\n  for (n = 0; n <= max_code; n++) {\n    curlen = nextlen;\n    nextlen = tree[(n + 1) * 2 + 1]/*.Len*/;\n\n    if (++count < max_count && curlen === nextlen) {\n      continue;\n\n    } else if (count < min_count) {\n      do { send_code(s, curlen, s.bl_tree); } while (--count !== 0);\n\n    } else if (curlen !== 0) {\n      if (curlen !== prevlen) {\n        send_code(s, curlen, s.bl_tree);\n        count--;\n      }\n      //Assert(count >= 3 && count <= 6, \" 3_6?\");\n      send_code(s, REP_3_6, s.bl_tree);\n      send_bits(s, count - 3, 2);\n\n    } else if (count <= 10) {\n      send_code(s, REPZ_3_10, s.bl_tree);\n      send_bits(s, count - 3, 3);\n\n    } else {\n      send_code(s, REPZ_11_138, s.bl_tree);\n      send_bits(s, count - 11, 7);\n    }\n\n    count = 0;\n    prevlen = curlen;\n    if (nextlen === 0) {\n      max_count = 138;\n      min_count = 3;\n\n    } else if (curlen === nextlen) {\n      max_count = 6;\n      min_count = 3;\n\n    } else {\n      max_count = 7;\n      min_count = 4;\n    }\n  }\n}\n\n\n/* ===========================================================================\n * Construct the Huffman tree for the bit lengths and return the index in\n * bl_order of the last bit length code to send.\n */\nfunction build_bl_tree(s) {\n  var max_blindex;  /* index of last bit length code of non zero freq */\n\n  /* Determine the bit length frequencies for literal and distance trees */\n  scan_tree(s, s.dyn_ltree, s.l_desc.max_code);\n  scan_tree(s, s.dyn_dtree, s.d_desc.max_code);\n\n  /* Build the bit length tree: */\n  build_tree(s, s.bl_desc);\n  /* opt_len now includes the length of the tree representations, except\n   * the lengths of the bit lengths codes and the 5+5+4 bits for the counts.\n   */\n\n  /* Determine the number of bit length codes to send. The pkzip format\n   * requires that at least 4 bit length codes be sent. (appnote.txt says\n   * 3 but the actual value used is 4.)\n   */\n  for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) {\n    if (s.bl_tree[bl_order[max_blindex] * 2 + 1]/*.Len*/ !== 0) {\n      break;\n    }\n  }\n  /* Update opt_len to include the bit length tree and counts */\n  s.opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4;\n  //Tracev((stderr, \"\\ndyn trees: dyn %ld, stat %ld\",\n  //        s->opt_len, s->static_len));\n\n  return max_blindex;\n}\n\n\n/* ===========================================================================\n * Send the header for a block using dynamic Huffman trees: the counts, the\n * lengths of the bit length codes, the literal tree and the distance tree.\n * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.\n */\nfunction send_all_trees(s, lcodes, dcodes, blcodes)\n//    deflate_state *s;\n//    int lcodes, dcodes, blcodes; /* number of codes for each tree */\n{\n  var rank;                    /* index in bl_order */\n\n  //Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, \"not enough codes\");\n  //Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES,\n  //        \"too many codes\");\n  //Tracev((stderr, \"\\nbl counts: \"));\n  send_bits(s, lcodes - 257, 5); /* not +255 as stated in appnote.txt */\n  send_bits(s, dcodes - 1,   5);\n  send_bits(s, blcodes - 4,  4); /* not -3 as stated in appnote.txt */\n  for (rank = 0; rank < blcodes; rank++) {\n    //Tracev((stderr, \"\\nbl code %2d \", bl_order[rank]));\n    send_bits(s, s.bl_tree[bl_order[rank] * 2 + 1]/*.Len*/, 3);\n  }\n  //Tracev((stderr, \"\\nbl tree: sent %ld\", s->bits_sent));\n\n  send_tree(s, s.dyn_ltree, lcodes - 1); /* literal tree */\n  //Tracev((stderr, \"\\nlit tree: sent %ld\", s->bits_sent));\n\n  send_tree(s, s.dyn_dtree, dcodes - 1); /* distance tree */\n  //Tracev((stderr, \"\\ndist tree: sent %ld\", s->bits_sent));\n}\n\n\n/* ===========================================================================\n * Check if the data type is TEXT or BINARY, using the following algorithm:\n * - TEXT if the two conditions below are satisfied:\n *    a) There are no non-portable control characters belonging to the\n *       \"black list\" (0..6, 14..25, 28..31).\n *    b) There is at least one printable character belonging to the\n *       \"white list\" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255).\n * - BINARY otherwise.\n * - The following partially-portable control characters form a\n *   \"gray list\" that is ignored in this detection algorithm:\n *   (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}).\n * IN assertion: the fields Freq of dyn_ltree are set.\n */\nfunction detect_data_type(s) {\n  /* black_mask is the bit mask of black-listed bytes\n   * set bits 0..6, 14..25, and 28..31\n   * 0xf3ffc07f = binary 11110011111111111100000001111111\n   */\n  var black_mask = 0xf3ffc07f;\n  var n;\n\n  /* Check for non-textual (\"black-listed\") bytes. */\n  for (n = 0; n <= 31; n++, black_mask >>>= 1) {\n    if ((black_mask & 1) && (s.dyn_ltree[n * 2]/*.Freq*/ !== 0)) {\n      return Z_BINARY;\n    }\n  }\n\n  /* Check for textual (\"white-listed\") bytes. */\n  if (s.dyn_ltree[9 * 2]/*.Freq*/ !== 0 || s.dyn_ltree[10 * 2]/*.Freq*/ !== 0 ||\n      s.dyn_ltree[13 * 2]/*.Freq*/ !== 0) {\n    return Z_TEXT;\n  }\n  for (n = 32; n < LITERALS; n++) {\n    if (s.dyn_ltree[n * 2]/*.Freq*/ !== 0) {\n      return Z_TEXT;\n    }\n  }\n\n  /* There are no \"black-listed\" or \"white-listed\" bytes:\n   * this stream either is empty or has tolerated (\"gray-listed\") bytes only.\n   */\n  return Z_BINARY;\n}\n\n\nvar static_init_done = false;\n\n/* ===========================================================================\n * Initialize the tree data structures for a new zlib stream.\n */\nfunction _tr_init(s)\n{\n\n  if (!static_init_done) {\n    tr_static_init();\n    static_init_done = true;\n  }\n\n  s.l_desc  = new TreeDesc(s.dyn_ltree, static_l_desc);\n  s.d_desc  = new TreeDesc(s.dyn_dtree, static_d_desc);\n  s.bl_desc = new TreeDesc(s.bl_tree, static_bl_desc);\n\n  s.bi_buf = 0;\n  s.bi_valid = 0;\n\n  /* Initialize the first block of the first file: */\n  init_block(s);\n}\n\n\n/* ===========================================================================\n * Send a stored block\n */\nfunction _tr_stored_block(s, buf, stored_len, last)\n//DeflateState *s;\n//charf *buf;       /* input block */\n//ulg stored_len;   /* length of input block */\n//int last;         /* one if this is the last block for a file */\n{\n  send_bits(s, (STORED_BLOCK << 1) + (last ? 1 : 0), 3);    /* send block type */\n  copy_block(s, buf, stored_len, true); /* with header */\n}\n\n\n/* ===========================================================================\n * Send one empty static block to give enough lookahead for inflate.\n * This takes 10 bits, of which 7 may remain in the bit buffer.\n */\nfunction _tr_align(s) {\n  send_bits(s, STATIC_TREES << 1, 3);\n  send_code(s, END_BLOCK, static_ltree);\n  bi_flush(s);\n}\n\n\n/* ===========================================================================\n * Determine the best encoding for the current block: dynamic trees, static\n * trees or store, and output the encoded block to the zip file.\n */\nfunction _tr_flush_block(s, buf, stored_len, last)\n//DeflateState *s;\n//charf *buf;       /* input block, or NULL if too old */\n//ulg stored_len;   /* length of input block */\n//int last;         /* one if this is the last block for a file */\n{\n  var opt_lenb, static_lenb;  /* opt_len and static_len in bytes */\n  var max_blindex = 0;        /* index of last bit length code of non zero freq */\n\n  /* Build the Huffman trees unless a stored block is forced */\n  if (s.level > 0) {\n\n    /* Check if the file is binary or text */\n    if (s.strm.data_type === Z_UNKNOWN) {\n      s.strm.data_type = detect_data_type(s);\n    }\n\n    /* Construct the literal and distance trees */\n    build_tree(s, s.l_desc);\n    // Tracev((stderr, \"\\nlit data: dyn %ld, stat %ld\", s->opt_len,\n    //        s->static_len));\n\n    build_tree(s, s.d_desc);\n    // Tracev((stderr, \"\\ndist data: dyn %ld, stat %ld\", s->opt_len,\n    //        s->static_len));\n    /* At this point, opt_len and static_len are the total bit lengths of\n     * the compressed block data, excluding the tree representations.\n     */\n\n    /* Build the bit length tree for the above two trees, and get the index\n     * in bl_order of the last bit length code to send.\n     */\n    max_blindex = build_bl_tree(s);\n\n    /* Determine the best encoding. Compute the block lengths in bytes. */\n    opt_lenb = (s.opt_len + 3 + 7) >>> 3;\n    static_lenb = (s.static_len + 3 + 7) >>> 3;\n\n    // Tracev((stderr, \"\\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u \",\n    //        opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len,\n    //        s->last_lit));\n\n    if (static_lenb <= opt_lenb) { opt_lenb = static_lenb; }\n\n  } else {\n    // Assert(buf != (char*)0, \"lost buf\");\n    opt_lenb = static_lenb = stored_len + 5; /* force a stored block */\n  }\n\n  if ((stored_len + 4 <= opt_lenb) && (buf !== -1)) {\n    /* 4: two words for the lengths */\n\n    /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.\n     * Otherwise we can't have processed more than WSIZE input bytes since\n     * the last block flush, because compression would have been\n     * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to\n     * transform a block into a stored block.\n     */\n    _tr_stored_block(s, buf, stored_len, last);\n\n  } else if (s.strategy === Z_FIXED || static_lenb === opt_lenb) {\n\n    send_bits(s, (STATIC_TREES << 1) + (last ? 1 : 0), 3);\n    compress_block(s, static_ltree, static_dtree);\n\n  } else {\n    send_bits(s, (DYN_TREES << 1) + (last ? 1 : 0), 3);\n    send_all_trees(s, s.l_desc.max_code + 1, s.d_desc.max_code + 1, max_blindex + 1);\n    compress_block(s, s.dyn_ltree, s.dyn_dtree);\n  }\n  // Assert (s->compressed_len == s->bits_sent, \"bad compressed size\");\n  /* The above check is made mod 2^32, for files larger than 512 MB\n   * and uLong implemented on 32 bits.\n   */\n  init_block(s);\n\n  if (last) {\n    bi_windup(s);\n  }\n  // Tracev((stderr,\"\\ncomprlen %lu(%lu) \", s->compressed_len>>3,\n  //       s->compressed_len-7*last));\n}\n\n/* ===========================================================================\n * Save the match info and tally the frequency counts. Return true if\n * the current block must be flushed.\n */\nfunction _tr_tally(s, dist, lc)\n//    deflate_state *s;\n//    unsigned dist;  /* distance of matched string */\n//    unsigned lc;    /* match length-MIN_MATCH or unmatched char (if dist==0) */\n{\n  //var out_length, in_length, dcode;\n\n  s.pending_buf[s.d_buf + s.last_lit * 2]     = (dist >>> 8) & 0xff;\n  s.pending_buf[s.d_buf + s.last_lit * 2 + 1] = dist & 0xff;\n\n  s.pending_buf[s.l_buf + s.last_lit] = lc & 0xff;\n  s.last_lit++;\n\n  if (dist === 0) {\n    /* lc is the unmatched char */\n    s.dyn_ltree[lc * 2]/*.Freq*/++;\n  } else {\n    s.matches++;\n    /* Here, lc is the match length - MIN_MATCH */\n    dist--;             /* dist = match distance - 1 */\n    //Assert((ush)dist < (ush)MAX_DIST(s) &&\n    //       (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) &&\n    //       (ush)d_code(dist) < (ush)D_CODES,  \"_tr_tally: bad match\");\n\n    s.dyn_ltree[(_length_code[lc] + LITERALS + 1) * 2]/*.Freq*/++;\n    s.dyn_dtree[d_code(dist) * 2]/*.Freq*/++;\n  }\n\n// (!) This block is disabled in zlib defaults,\n// don't enable it for binary compatibility\n\n//#ifdef TRUNCATE_BLOCK\n//  /* Try to guess if it is profitable to stop the current block here */\n//  if ((s.last_lit & 0x1fff) === 0 && s.level > 2) {\n//    /* Compute an upper bound for the compressed length */\n//    out_length = s.last_lit*8;\n//    in_length = s.strstart - s.block_start;\n//\n//    for (dcode = 0; dcode < D_CODES; dcode++) {\n//      out_length += s.dyn_dtree[dcode*2]/*.Freq*/ * (5 + extra_dbits[dcode]);\n//    }\n//    out_length >>>= 3;\n//    //Tracev((stderr,\"\\nlast_lit %u, in %ld, out ~%ld(%ld%%) \",\n//    //       s->last_lit, in_length, out_length,\n//    //       100L - out_length*100L/in_length));\n//    if (s.matches < (s.last_lit>>1)/*int /2*/ && out_length < (in_length>>1)/*int /2*/) {\n//      return true;\n//    }\n//  }\n//#endif\n\n  return (s.last_lit === s.lit_bufsize - 1);\n  /* We avoid equality with lit_bufsize because of wraparound at 64K\n   * on 16 bit machines and because stored blocks are restricted to\n   * 64K-1 bytes.\n   */\n}\n\nexports._tr_init  = _tr_init;\nexports._tr_stored_block = _tr_stored_block;\nexports._tr_flush_block  = _tr_flush_block;\nexports._tr_tally = _tr_tally;\nexports._tr_align = _tr_align;\n","'use strict';\n\n\nvar zlib_inflate = require('./zlib/inflate');\nvar utils        = require('./utils/common');\nvar strings      = require('./utils/strings');\nvar c            = require('./zlib/constants');\nvar msg          = require('./zlib/messages');\nvar ZStream      = require('./zlib/zstream');\nvar GZheader     = require('./zlib/gzheader');\n\nvar toString = Object.prototype.toString;\n\n/**\n * class Inflate\n *\n * Generic JS-style wrapper for zlib calls. If you don't need\n * streaming behaviour - use more simple functions: [[inflate]]\n * and [[inflateRaw]].\n **/\n\n/* internal\n * inflate.chunks -> Array\n *\n * Chunks of output data, if [[Inflate#onData]] not overridden.\n **/\n\n/**\n * Inflate.result -> Uint8Array|Array|String\n *\n * Uncompressed result, generated by default [[Inflate#onData]]\n * and [[Inflate#onEnd]] handlers. Filled after you push last chunk\n * (call [[Inflate#push]] with `Z_FINISH` / `true` param) or if you\n * push a chunk with explicit flush (call [[Inflate#push]] with\n * `Z_SYNC_FLUSH` param).\n **/\n\n/**\n * Inflate.err -> Number\n *\n * Error code after inflate finished. 0 (Z_OK) on success.\n * Should be checked if broken data possible.\n **/\n\n/**\n * Inflate.msg -> String\n *\n * Error message, if [[Inflate.err]] != 0\n **/\n\n\n/**\n * new Inflate(options)\n * - options (Object): zlib inflate options.\n *\n * Creates new inflator instance with specified params. Throws exception\n * on bad params. Supported options:\n *\n * - `windowBits`\n * - `dictionary`\n *\n * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)\n * for more information on these.\n *\n * Additional options, for internal needs:\n *\n * - `chunkSize` - size of generated data chunks (16K by default)\n * - `raw` (Boolean) - do raw inflate\n * - `to` (String) - if equal to 'string', then result will be converted\n *   from utf8 to utf16 (javascript) string. When string output requested,\n *   chunk length can differ from `chunkSize`, depending on content.\n *\n * By default, when no options set, autodetect deflate/gzip data format via\n * wrapper header.\n *\n * ##### Example:\n *\n * ```javascript\n * var pako = require('pako')\n *   , chunk1 = Uint8Array([1,2,3,4,5,6,7,8,9])\n *   , chunk2 = Uint8Array([10,11,12,13,14,15,16,17,18,19]);\n *\n * var inflate = new pako.Inflate({ level: 3});\n *\n * inflate.push(chunk1, false);\n * inflate.push(chunk2, true);  // true -> last chunk\n *\n * if (inflate.err) { throw new Error(inflate.err); }\n *\n * console.log(inflate.result);\n * ```\n **/\nfunction Inflate(options) {\n  if (!(this instanceof Inflate)) return new Inflate(options);\n\n  this.options = utils.assign({\n    chunkSize: 16384,\n    windowBits: 0,\n    to: ''\n  }, options || {});\n\n  var opt = this.options;\n\n  // Force window size for `raw` data, if not set directly,\n  // because we have no header for autodetect.\n  if (opt.raw && (opt.windowBits >= 0) && (opt.windowBits < 16)) {\n    opt.windowBits = -opt.windowBits;\n    if (opt.windowBits === 0) { opt.windowBits = -15; }\n  }\n\n  // If `windowBits` not defined (and mode not raw) - set autodetect flag for gzip/deflate\n  if ((opt.windowBits >= 0) && (opt.windowBits < 16) &&\n      !(options && options.windowBits)) {\n    opt.windowBits += 32;\n  }\n\n  // Gzip header has no info about windows size, we can do autodetect only\n  // for deflate. So, if window size not set, force it to max when gzip possible\n  if ((opt.windowBits > 15) && (opt.windowBits < 48)) {\n    // bit 3 (16) -> gzipped data\n    // bit 4 (32) -> autodetect gzip/deflate\n    if ((opt.windowBits & 15) === 0) {\n      opt.windowBits |= 15;\n    }\n  }\n\n  this.err    = 0;      // error code, if happens (0 = Z_OK)\n  this.msg    = '';     // error message\n  this.ended  = false;  // used to avoid multiple onEnd() calls\n  this.chunks = [];     // chunks of compressed data\n\n  this.strm   = new ZStream();\n  this.strm.avail_out = 0;\n\n  var status  = zlib_inflate.inflateInit2(\n    this.strm,\n    opt.windowBits\n  );\n\n  if (status !== c.Z_OK) {\n    throw new Error(msg[status]);\n  }\n\n  this.header = new GZheader();\n\n  zlib_inflate.inflateGetHeader(this.strm, this.header);\n\n  // Setup dictionary\n  if (opt.dictionary) {\n    // Convert data if needed\n    if (typeof opt.dictionary === 'string') {\n      opt.dictionary = strings.string2buf(opt.dictionary);\n    } else if (toString.call(opt.dictionary) === '[object ArrayBuffer]') {\n      opt.dictionary = new Uint8Array(opt.dictionary);\n    }\n    if (opt.raw) { //In raw mode we need to set the dictionary early\n      status = zlib_inflate.inflateSetDictionary(this.strm, opt.dictionary);\n      if (status !== c.Z_OK) {\n        throw new Error(msg[status]);\n      }\n    }\n  }\n}\n\n/**\n * Inflate#push(data[, mode]) -> Boolean\n * - data (Uint8Array|Array|ArrayBuffer|String): input data\n * - mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE modes.\n *   See constants. Skipped or `false` means Z_NO_FLUSH, `true` means Z_FINISH.\n *\n * Sends input data to inflate pipe, generating [[Inflate#onData]] calls with\n * new output chunks. Returns `true` on success. The last data block must have\n * mode Z_FINISH (or `true`). That will flush internal pending buffers and call\n * [[Inflate#onEnd]]. For interim explicit flushes (without ending the stream) you\n * can use mode Z_SYNC_FLUSH, keeping the decompression context.\n *\n * On fail call [[Inflate#onEnd]] with error code and return false.\n *\n * We strongly recommend to use `Uint8Array` on input for best speed (output\n * format is detected automatically). Also, don't skip last param and always\n * use the same type in your code (boolean or number). That will improve JS speed.\n *\n * For regular `Array`-s make sure all elements are [0..255].\n *\n * ##### Example\n *\n * ```javascript\n * push(chunk, false); // push one of data chunks\n * ...\n * push(chunk, true);  // push last chunk\n * ```\n **/\nInflate.prototype.push = function (data, mode) {\n  var strm = this.strm;\n  var chunkSize = this.options.chunkSize;\n  var dictionary = this.options.dictionary;\n  var status, _mode;\n  var next_out_utf8, tail, utf8str;\n\n  // Flag to properly process Z_BUF_ERROR on testing inflate call\n  // when we check that all output data was flushed.\n  var allowBufError = false;\n\n  if (this.ended) { return false; }\n  _mode = (mode === ~~mode) ? mode : ((mode === true) ? c.Z_FINISH : c.Z_NO_FLUSH);\n\n  // Convert data if needed\n  if (typeof data === 'string') {\n    // Only binary strings can be decompressed on practice\n    strm.input = strings.binstring2buf(data);\n  } else if (toString.call(data) === '[object ArrayBuffer]') {\n    strm.input = new Uint8Array(data);\n  } else {\n    strm.input = data;\n  }\n\n  strm.next_in = 0;\n  strm.avail_in = strm.input.length;\n\n  do {\n    if (strm.avail_out === 0) {\n      strm.output = new utils.Buf8(chunkSize);\n      strm.next_out = 0;\n      strm.avail_out = chunkSize;\n    }\n\n    status = zlib_inflate.inflate(strm, c.Z_NO_FLUSH);    /* no bad return value */\n\n    if (status === c.Z_NEED_DICT && dictionary) {\n      status = zlib_inflate.inflateSetDictionary(this.strm, dictionary);\n    }\n\n    if (status === c.Z_BUF_ERROR && allowBufError === true) {\n      status = c.Z_OK;\n      allowBufError = false;\n    }\n\n    if (status !== c.Z_STREAM_END && status !== c.Z_OK) {\n      this.onEnd(status);\n      this.ended = true;\n      return false;\n    }\n\n    if (strm.next_out) {\n      if (strm.avail_out === 0 || status === c.Z_STREAM_END || (strm.avail_in === 0 && (_mode === c.Z_FINISH || _mode === c.Z_SYNC_FLUSH))) {\n\n        if (this.options.to === 'string') {\n\n          next_out_utf8 = strings.utf8border(strm.output, strm.next_out);\n\n          tail = strm.next_out - next_out_utf8;\n          utf8str = strings.buf2string(strm.output, next_out_utf8);\n\n          // move tail\n          strm.next_out = tail;\n          strm.avail_out = chunkSize - tail;\n          if (tail) { utils.arraySet(strm.output, strm.output, next_out_utf8, tail, 0); }\n\n          this.onData(utf8str);\n\n        } else {\n          this.onData(utils.shrinkBuf(strm.output, strm.next_out));\n        }\n      }\n    }\n\n    // When no more input data, we should check that internal inflate buffers\n    // are flushed. The only way to do it when avail_out = 0 - run one more\n    // inflate pass. But if output data not exists, inflate return Z_BUF_ERROR.\n    // Here we set flag to process this error properly.\n    //\n    // NOTE. Deflate does not return error in this case and does not needs such\n    // logic.\n    if (strm.avail_in === 0 && strm.avail_out === 0) {\n      allowBufError = true;\n    }\n\n  } while ((strm.avail_in > 0 || strm.avail_out === 0) && status !== c.Z_STREAM_END);\n\n  if (status === c.Z_STREAM_END) {\n    _mode = c.Z_FINISH;\n  }\n\n  // Finalize on the last chunk.\n  if (_mode === c.Z_FINISH) {\n    status = zlib_inflate.inflateEnd(this.strm);\n    this.onEnd(status);\n    this.ended = true;\n    return status === c.Z_OK;\n  }\n\n  // callback interim results if Z_SYNC_FLUSH.\n  if (_mode === c.Z_SYNC_FLUSH) {\n    this.onEnd(c.Z_OK);\n    strm.avail_out = 0;\n    return true;\n  }\n\n  return true;\n};\n\n\n/**\n * Inflate#onData(chunk) -> Void\n * - chunk (Uint8Array|Array|String): output data. Type of array depends\n *   on js engine support. When string output requested, each chunk\n *   will be string.\n *\n * By default, stores data blocks in `chunks[]` property and glue\n * those in `onEnd`. Override this handler, if you need another behaviour.\n **/\nInflate.prototype.onData = function (chunk) {\n  this.chunks.push(chunk);\n};\n\n\n/**\n * Inflate#onEnd(status) -> Void\n * - status (Number): inflate status. 0 (Z_OK) on success,\n *   other if not.\n *\n * Called either after you tell inflate that the input stream is\n * complete (Z_FINISH) or should be flushed (Z_SYNC_FLUSH)\n * or if an error happened. By default - join collected chunks,\n * free memory and fill `results` / `err` properties.\n **/\nInflate.prototype.onEnd = function (status) {\n  // On success - join\n  if (status === c.Z_OK) {\n    if (this.options.to === 'string') {\n      // Glue & convert here, until we teach pako to send\n      // utf8 aligned strings to onData\n      this.result = this.chunks.join('');\n    } else {\n      this.result = utils.flattenChunks(this.chunks);\n    }\n  }\n  this.chunks = [];\n  this.err = status;\n  this.msg = this.strm.msg;\n};\n\n\n/**\n * inflate(data[, options]) -> Uint8Array|Array|String\n * - data (Uint8Array|Array|String): input data to decompress.\n * - options (Object): zlib inflate options.\n *\n * Decompress `data` with inflate/ungzip and `options`. Autodetect\n * format via wrapper header by default. That's why we don't provide\n * separate `ungzip` method.\n *\n * Supported options are:\n *\n * - windowBits\n *\n * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)\n * for more information.\n *\n * Sugar (options):\n *\n * - `raw` (Boolean) - say that we work with raw stream, if you don't wish to specify\n *   negative windowBits implicitly.\n * - `to` (String) - if equal to 'string', then result will be converted\n *   from utf8 to utf16 (javascript) string. When string output requested,\n *   chunk length can differ from `chunkSize`, depending on content.\n *\n *\n * ##### Example:\n *\n * ```javascript\n * var pako = require('pako')\n *   , input = pako.deflate([1,2,3,4,5,6,7,8,9])\n *   , output;\n *\n * try {\n *   output = pako.inflate(input);\n * } catch (err)\n *   console.log(err);\n * }\n * ```\n **/\nfunction inflate(input, options) {\n  var inflator = new Inflate(options);\n\n  inflator.push(input, true);\n\n  // That will never happens, if you don't cheat with options :)\n  if (inflator.err) { throw inflator.msg || msg[inflator.err]; }\n\n  return inflator.result;\n}\n\n\n/**\n * inflateRaw(data[, options]) -> Uint8Array|Array|String\n * - data (Uint8Array|Array|String): input data to decompress.\n * - options (Object): zlib inflate options.\n *\n * The same as [[inflate]], but creates raw data, without wrapper\n * (header and adler32 crc).\n **/\nfunction inflateRaw(input, options) {\n  options = options || {};\n  options.raw = true;\n  return inflate(input, options);\n}\n\n\n/**\n * ungzip(data[, options]) -> Uint8Array|Array|String\n * - data (Uint8Array|Array|String): input data to decompress.\n * - options (Object): zlib inflate options.\n *\n * Just shortcut to [[inflate]], because it autodetects format\n * by header.content. Done for convenience.\n **/\n\n\nexports.Inflate = Inflate;\nexports.inflate = inflate;\nexports.inflateRaw = inflateRaw;\nexports.ungzip  = inflate;\n","'use strict';\n\n// (C) 1995-2013 Jean-loup Gailly and Mark Adler\n// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//   claim that you wrote the original software. If you use this software\n//   in a product, an acknowledgment in the product documentation would be\n//   appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//   misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n\nvar utils         = require('../utils/common');\nvar adler32       = require('./adler32');\nvar crc32         = require('./crc32');\nvar inflate_fast  = require('./inffast');\nvar inflate_table = require('./inftrees');\n\nvar CODES = 0;\nvar LENS = 1;\nvar DISTS = 2;\n\n/* Public constants ==========================================================*/\n/* ===========================================================================*/\n\n\n/* Allowed flush values; see deflate() and inflate() below for details */\n//var Z_NO_FLUSH      = 0;\n//var Z_PARTIAL_FLUSH = 1;\n//var Z_SYNC_FLUSH    = 2;\n//var Z_FULL_FLUSH    = 3;\nvar Z_FINISH        = 4;\nvar Z_BLOCK         = 5;\nvar Z_TREES         = 6;\n\n\n/* Return codes for the compression/decompression functions. Negative values\n * are errors, positive values are used for special but normal events.\n */\nvar Z_OK            = 0;\nvar Z_STREAM_END    = 1;\nvar Z_NEED_DICT     = 2;\n//var Z_ERRNO         = -1;\nvar Z_STREAM_ERROR  = -2;\nvar Z_DATA_ERROR    = -3;\nvar Z_MEM_ERROR     = -4;\nvar Z_BUF_ERROR     = -5;\n//var Z_VERSION_ERROR = -6;\n\n/* The deflate compression method */\nvar Z_DEFLATED  = 8;\n\n\n/* STATES ====================================================================*/\n/* ===========================================================================*/\n\n\nvar    HEAD = 1;       /* i: waiting for magic header */\nvar    FLAGS = 2;      /* i: waiting for method and flags (gzip) */\nvar    TIME = 3;       /* i: waiting for modification time (gzip) */\nvar    OS = 4;         /* i: waiting for extra flags and operating system (gzip) */\nvar    EXLEN = 5;      /* i: waiting for extra length (gzip) */\nvar    EXTRA = 6;      /* i: waiting for extra bytes (gzip) */\nvar    NAME = 7;       /* i: waiting for end of file name (gzip) */\nvar    COMMENT = 8;    /* i: waiting for end of comment (gzip) */\nvar    HCRC = 9;       /* i: waiting for header crc (gzip) */\nvar    DICTID = 10;    /* i: waiting for dictionary check value */\nvar    DICT = 11;      /* waiting for inflateSetDictionary() call */\nvar        TYPE = 12;      /* i: waiting for type bits, including last-flag bit */\nvar        TYPEDO = 13;    /* i: same, but skip check to exit inflate on new block */\nvar        STORED = 14;    /* i: waiting for stored size (length and complement) */\nvar        COPY_ = 15;     /* i/o: same as COPY below, but only first time in */\nvar        COPY = 16;      /* i/o: waiting for input or output to copy stored block */\nvar        TABLE = 17;     /* i: waiting for dynamic block table lengths */\nvar        LENLENS = 18;   /* i: waiting for code length code lengths */\nvar        CODELENS = 19;  /* i: waiting for length/lit and distance code lengths */\nvar            LEN_ = 20;      /* i: same as LEN below, but only first time in */\nvar            LEN = 21;       /* i: waiting for length/lit/eob code */\nvar            LENEXT = 22;    /* i: waiting for length extra bits */\nvar            DIST = 23;      /* i: waiting for distance code */\nvar            DISTEXT = 24;   /* i: waiting for distance extra bits */\nvar            MATCH = 25;     /* o: waiting for output space to copy string */\nvar            LIT = 26;       /* o: waiting for output space to write literal */\nvar    CHECK = 27;     /* i: waiting for 32-bit check value */\nvar    LENGTH = 28;    /* i: waiting for 32-bit length (gzip) */\nvar    DONE = 29;      /* finished check, done -- remain here until reset */\nvar    BAD = 30;       /* got a data error -- remain here until reset */\nvar    MEM = 31;       /* got an inflate() memory error -- remain here until reset */\nvar    SYNC = 32;      /* looking for synchronization bytes to restart inflate() */\n\n/* ===========================================================================*/\n\n\n\nvar ENOUGH_LENS = 852;\nvar ENOUGH_DISTS = 592;\n//var ENOUGH =  (ENOUGH_LENS+ENOUGH_DISTS);\n\nvar MAX_WBITS = 15;\n/* 32K LZ77 window */\nvar DEF_WBITS = MAX_WBITS;\n\n\nfunction zswap32(q) {\n  return  (((q >>> 24) & 0xff) +\n          ((q >>> 8) & 0xff00) +\n          ((q & 0xff00) << 8) +\n          ((q & 0xff) << 24));\n}\n\n\nfunction InflateState() {\n  this.mode = 0;             /* current inflate mode */\n  this.last = false;          /* true if processing last block */\n  this.wrap = 0;              /* bit 0 true for zlib, bit 1 true for gzip */\n  this.havedict = false;      /* true if dictionary provided */\n  this.flags = 0;             /* gzip header method and flags (0 if zlib) */\n  this.dmax = 0;              /* zlib header max distance (INFLATE_STRICT) */\n  this.check = 0;             /* protected copy of check value */\n  this.total = 0;             /* protected copy of output count */\n  // TODO: may be {}\n  this.head = null;           /* where to save gzip header information */\n\n  /* sliding window */\n  this.wbits = 0;             /* log base 2 of requested window size */\n  this.wsize = 0;             /* window size or zero if not using window */\n  this.whave = 0;             /* valid bytes in the window */\n  this.wnext = 0;             /* window write index */\n  this.window = null;         /* allocated sliding window, if needed */\n\n  /* bit accumulator */\n  this.hold = 0;              /* input bit accumulator */\n  this.bits = 0;              /* number of bits in \"in\" */\n\n  /* for string and stored block copying */\n  this.length = 0;            /* literal or length of data to copy */\n  this.offset = 0;            /* distance back to copy string from */\n\n  /* for table and code decoding */\n  this.extra = 0;             /* extra bits needed */\n\n  /* fixed and dynamic code tables */\n  this.lencode = null;          /* starting table for length/literal codes */\n  this.distcode = null;         /* starting table for distance codes */\n  this.lenbits = 0;           /* index bits for lencode */\n  this.distbits = 0;          /* index bits for distcode */\n\n  /* dynamic table building */\n  this.ncode = 0;             /* number of code length code lengths */\n  this.nlen = 0;              /* number of length code lengths */\n  this.ndist = 0;             /* number of distance code lengths */\n  this.have = 0;              /* number of code lengths in lens[] */\n  this.next = null;              /* next available space in codes[] */\n\n  this.lens = new utils.Buf16(320); /* temporary storage for code lengths */\n  this.work = new utils.Buf16(288); /* work area for code table building */\n\n  /*\n   because we don't have pointers in js, we use lencode and distcode directly\n   as buffers so we don't need codes\n  */\n  //this.codes = new utils.Buf32(ENOUGH);       /* space for code tables */\n  this.lendyn = null;              /* dynamic table for length/literal codes (JS specific) */\n  this.distdyn = null;             /* dynamic table for distance codes (JS specific) */\n  this.sane = 0;                   /* if false, allow invalid distance too far */\n  this.back = 0;                   /* bits back of last unprocessed length/lit */\n  this.was = 0;                    /* initial length of match */\n}\n\nfunction inflateResetKeep(strm) {\n  var state;\n\n  if (!strm || !strm.state) { return Z_STREAM_ERROR; }\n  state = strm.state;\n  strm.total_in = strm.total_out = state.total = 0;\n  strm.msg = ''; /*Z_NULL*/\n  if (state.wrap) {       /* to support ill-conceived Java test suite */\n    strm.adler = state.wrap & 1;\n  }\n  state.mode = HEAD;\n  state.last = 0;\n  state.havedict = 0;\n  state.dmax = 32768;\n  state.head = null/*Z_NULL*/;\n  state.hold = 0;\n  state.bits = 0;\n  //state.lencode = state.distcode = state.next = state.codes;\n  state.lencode = state.lendyn = new utils.Buf32(ENOUGH_LENS);\n  state.distcode = state.distdyn = new utils.Buf32(ENOUGH_DISTS);\n\n  state.sane = 1;\n  state.back = -1;\n  //Tracev((stderr, \"inflate: reset\\n\"));\n  return Z_OK;\n}\n\nfunction inflateReset(strm) {\n  var state;\n\n  if (!strm || !strm.state) { return Z_STREAM_ERROR; }\n  state = strm.state;\n  state.wsize = 0;\n  state.whave = 0;\n  state.wnext = 0;\n  return inflateResetKeep(strm);\n\n}\n\nfunction inflateReset2(strm, windowBits) {\n  var wrap;\n  var state;\n\n  /* get the state */\n  if (!strm || !strm.state) { return Z_STREAM_ERROR; }\n  state = strm.state;\n\n  /* extract wrap request from windowBits parameter */\n  if (windowBits < 0) {\n    wrap = 0;\n    windowBits = -windowBits;\n  }\n  else {\n    wrap = (windowBits >> 4) + 1;\n    if (windowBits < 48) {\n      windowBits &= 15;\n    }\n  }\n\n  /* set number of window bits, free window if different */\n  if (windowBits && (windowBits < 8 || windowBits > 15)) {\n    return Z_STREAM_ERROR;\n  }\n  if (state.window !== null && state.wbits !== windowBits) {\n    state.window = null;\n  }\n\n  /* update state and reset the rest of it */\n  state.wrap = wrap;\n  state.wbits = windowBits;\n  return inflateReset(strm);\n}\n\nfunction inflateInit2(strm, windowBits) {\n  var ret;\n  var state;\n\n  if (!strm) { return Z_STREAM_ERROR; }\n  //strm.msg = Z_NULL;                 /* in case we return an error */\n\n  state = new InflateState();\n\n  //if (state === Z_NULL) return Z_MEM_ERROR;\n  //Tracev((stderr, \"inflate: allocated\\n\"));\n  strm.state = state;\n  state.window = null/*Z_NULL*/;\n  ret = inflateReset2(strm, windowBits);\n  if (ret !== Z_OK) {\n    strm.state = null/*Z_NULL*/;\n  }\n  return ret;\n}\n\nfunction inflateInit(strm) {\n  return inflateInit2(strm, DEF_WBITS);\n}\n\n\n/*\n Return state with length and distance decoding tables and index sizes set to\n fixed code decoding.  Normally this returns fixed tables from inffixed.h.\n If BUILDFIXED is defined, then instead this routine builds the tables the\n first time it's called, and returns those tables the first time and\n thereafter.  This reduces the size of the code by about 2K bytes, in\n exchange for a little execution time.  However, BUILDFIXED should not be\n used for threaded applications, since the rewriting of the tables and virgin\n may not be thread-safe.\n */\nvar virgin = true;\n\nvar lenfix, distfix; // We have no pointers in JS, so keep tables separate\n\nfunction fixedtables(state) {\n  /* build fixed huffman tables if first call (may not be thread safe) */\n  if (virgin) {\n    var sym;\n\n    lenfix = new utils.Buf32(512);\n    distfix = new utils.Buf32(32);\n\n    /* literal/length table */\n    sym = 0;\n    while (sym < 144) { state.lens[sym++] = 8; }\n    while (sym < 256) { state.lens[sym++] = 9; }\n    while (sym < 280) { state.lens[sym++] = 7; }\n    while (sym < 288) { state.lens[sym++] = 8; }\n\n    inflate_table(LENS,  state.lens, 0, 288, lenfix,   0, state.work, { bits: 9 });\n\n    /* distance table */\n    sym = 0;\n    while (sym < 32) { state.lens[sym++] = 5; }\n\n    inflate_table(DISTS, state.lens, 0, 32,   distfix, 0, state.work, { bits: 5 });\n\n    /* do this just once */\n    virgin = false;\n  }\n\n  state.lencode = lenfix;\n  state.lenbits = 9;\n  state.distcode = distfix;\n  state.distbits = 5;\n}\n\n\n/*\n Update the window with the last wsize (normally 32K) bytes written before\n returning.  If window does not exist yet, create it.  This is only called\n when a window is already in use, or when output has been written during this\n inflate call, but the end of the deflate stream has not been reached yet.\n It is also called to create a window for dictionary data when a dictionary\n is loaded.\n\n Providing output buffers larger than 32K to inflate() should provide a speed\n advantage, since only the last 32K of output is copied to the sliding window\n upon return from inflate(), and since all distances after the first 32K of\n output will fall in the output data, making match copies simpler and faster.\n The advantage may be dependent on the size of the processor's data caches.\n */\nfunction updatewindow(strm, src, end, copy) {\n  var dist;\n  var state = strm.state;\n\n  /* if it hasn't been done already, allocate space for the window */\n  if (state.window === null) {\n    state.wsize = 1 << state.wbits;\n    state.wnext = 0;\n    state.whave = 0;\n\n    state.window = new utils.Buf8(state.wsize);\n  }\n\n  /* copy state->wsize or less output bytes into the circular window */\n  if (copy >= state.wsize) {\n    utils.arraySet(state.window, src, end - state.wsize, state.wsize, 0);\n    state.wnext = 0;\n    state.whave = state.wsize;\n  }\n  else {\n    dist = state.wsize - state.wnext;\n    if (dist > copy) {\n      dist = copy;\n    }\n    //zmemcpy(state->window + state->wnext, end - copy, dist);\n    utils.arraySet(state.window, src, end - copy, dist, state.wnext);\n    copy -= dist;\n    if (copy) {\n      //zmemcpy(state->window, end - copy, copy);\n      utils.arraySet(state.window, src, end - copy, copy, 0);\n      state.wnext = copy;\n      state.whave = state.wsize;\n    }\n    else {\n      state.wnext += dist;\n      if (state.wnext === state.wsize) { state.wnext = 0; }\n      if (state.whave < state.wsize) { state.whave += dist; }\n    }\n  }\n  return 0;\n}\n\nfunction inflate(strm, flush) {\n  var state;\n  var input, output;          // input/output buffers\n  var next;                   /* next input INDEX */\n  var put;                    /* next output INDEX */\n  var have, left;             /* available input and output */\n  var hold;                   /* bit buffer */\n  var bits;                   /* bits in bit buffer */\n  var _in, _out;              /* save starting available input and output */\n  var copy;                   /* number of stored or match bytes to copy */\n  var from;                   /* where to copy match bytes from */\n  var from_source;\n  var here = 0;               /* current decoding table entry */\n  var here_bits, here_op, here_val; // paked \"here\" denormalized (JS specific)\n  //var last;                   /* parent table entry */\n  var last_bits, last_op, last_val; // paked \"last\" denormalized (JS specific)\n  var len;                    /* length to copy for repeats, bits to drop */\n  var ret;                    /* return code */\n  var hbuf = new utils.Buf8(4);    /* buffer for gzip header crc calculation */\n  var opts;\n\n  var n; // temporary var for NEED_BITS\n\n  var order = /* permutation of code lengths */\n    [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ];\n\n\n  if (!strm || !strm.state || !strm.output ||\n      (!strm.input && strm.avail_in !== 0)) {\n    return Z_STREAM_ERROR;\n  }\n\n  state = strm.state;\n  if (state.mode === TYPE) { state.mode = TYPEDO; }    /* skip check */\n\n\n  //--- LOAD() ---\n  put = strm.next_out;\n  output = strm.output;\n  left = strm.avail_out;\n  next = strm.next_in;\n  input = strm.input;\n  have = strm.avail_in;\n  hold = state.hold;\n  bits = state.bits;\n  //---\n\n  _in = have;\n  _out = left;\n  ret = Z_OK;\n\n  inf_leave: // goto emulation\n  for (;;) {\n    switch (state.mode) {\n      case HEAD:\n        if (state.wrap === 0) {\n          state.mode = TYPEDO;\n          break;\n        }\n        //=== NEEDBITS(16);\n        while (bits < 16) {\n          if (have === 0) { break inf_leave; }\n          have--;\n          hold += input[next++] << bits;\n          bits += 8;\n        }\n        //===//\n        if ((state.wrap & 2) && hold === 0x8b1f) {  /* gzip header */\n          state.check = 0/*crc32(0L, Z_NULL, 0)*/;\n          //=== CRC2(state.check, hold);\n          hbuf[0] = hold & 0xff;\n          hbuf[1] = (hold >>> 8) & 0xff;\n          state.check = crc32(state.check, hbuf, 2, 0);\n          //===//\n\n          //=== INITBITS();\n          hold = 0;\n          bits = 0;\n          //===//\n          state.mode = FLAGS;\n          break;\n        }\n        state.flags = 0;           /* expect zlib header */\n        if (state.head) {\n          state.head.done = false;\n        }\n        if (!(state.wrap & 1) ||   /* check if zlib header allowed */\n          (((hold & 0xff)/*BITS(8)*/ << 8) + (hold >> 8)) % 31) {\n          strm.msg = 'incorrect header check';\n          state.mode = BAD;\n          break;\n        }\n        if ((hold & 0x0f)/*BITS(4)*/ !== Z_DEFLATED) {\n          strm.msg = 'unknown compression method';\n          state.mode = BAD;\n          break;\n        }\n        //--- DROPBITS(4) ---//\n        hold >>>= 4;\n        bits -= 4;\n        //---//\n        len = (hold & 0x0f)/*BITS(4)*/ + 8;\n        if (state.wbits === 0) {\n          state.wbits = len;\n        }\n        else if (len > state.wbits) {\n          strm.msg = 'invalid window size';\n          state.mode = BAD;\n          break;\n        }\n        state.dmax = 1 << len;\n        //Tracev((stderr, \"inflate:   zlib header ok\\n\"));\n        strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/;\n        state.mode = hold & 0x200 ? DICTID : TYPE;\n        //=== INITBITS();\n        hold = 0;\n        bits = 0;\n        //===//\n        break;\n      case FLAGS:\n        //=== NEEDBITS(16); */\n        while (bits < 16) {\n          if (have === 0) { break inf_leave; }\n          have--;\n          hold += input[next++] << bits;\n          bits += 8;\n        }\n        //===//\n        state.flags = hold;\n        if ((state.flags & 0xff) !== Z_DEFLATED) {\n          strm.msg = 'unknown compression method';\n          state.mode = BAD;\n          break;\n        }\n        if (state.flags & 0xe000) {\n          strm.msg = 'unknown header flags set';\n          state.mode = BAD;\n          break;\n        }\n        if (state.head) {\n          state.head.text = ((hold >> 8) & 1);\n        }\n        if (state.flags & 0x0200) {\n          //=== CRC2(state.check, hold);\n          hbuf[0] = hold & 0xff;\n          hbuf[1] = (hold >>> 8) & 0xff;\n          state.check = crc32(state.check, hbuf, 2, 0);\n          //===//\n        }\n        //=== INITBITS();\n        hold = 0;\n        bits = 0;\n        //===//\n        state.mode = TIME;\n        /* falls through */\n      case TIME:\n        //=== NEEDBITS(32); */\n        while (bits < 32) {\n          if (have === 0) { break inf_leave; }\n          have--;\n          hold += input[next++] << bits;\n          bits += 8;\n        }\n        //===//\n        if (state.head) {\n          state.head.time = hold;\n        }\n        if (state.flags & 0x0200) {\n          //=== CRC4(state.check, hold)\n          hbuf[0] = hold & 0xff;\n          hbuf[1] = (hold >>> 8) & 0xff;\n          hbuf[2] = (hold >>> 16) & 0xff;\n          hbuf[3] = (hold >>> 24) & 0xff;\n          state.check = crc32(state.check, hbuf, 4, 0);\n          //===\n        }\n        //=== INITBITS();\n        hold = 0;\n        bits = 0;\n        //===//\n        state.mode = OS;\n        /* falls through */\n      case OS:\n        //=== NEEDBITS(16); */\n        while (bits < 16) {\n          if (have === 0) { break inf_leave; }\n          have--;\n          hold += input[next++] << bits;\n          bits += 8;\n        }\n        //===//\n        if (state.head) {\n          state.head.xflags = (hold & 0xff);\n          state.head.os = (hold >> 8);\n        }\n        if (state.flags & 0x0200) {\n          //=== CRC2(state.check, hold);\n          hbuf[0] = hold & 0xff;\n          hbuf[1] = (hold >>> 8) & 0xff;\n          state.check = crc32(state.check, hbuf, 2, 0);\n          //===//\n        }\n        //=== INITBITS();\n        hold = 0;\n        bits = 0;\n        //===//\n        state.mode = EXLEN;\n        /* falls through */\n      case EXLEN:\n        if (state.flags & 0x0400) {\n          //=== NEEDBITS(16); */\n          while (bits < 16) {\n            if (have === 0) { break inf_leave; }\n            have--;\n            hold += input[next++] << bits;\n            bits += 8;\n          }\n          //===//\n          state.length = hold;\n          if (state.head) {\n            state.head.extra_len = hold;\n          }\n          if (state.flags & 0x0200) {\n            //=== CRC2(state.check, hold);\n            hbuf[0] = hold & 0xff;\n            hbuf[1] = (hold >>> 8) & 0xff;\n            state.check = crc32(state.check, hbuf, 2, 0);\n            //===//\n          }\n          //=== INITBITS();\n          hold = 0;\n          bits = 0;\n          //===//\n        }\n        else if (state.head) {\n          state.head.extra = null/*Z_NULL*/;\n        }\n        state.mode = EXTRA;\n        /* falls through */\n      case EXTRA:\n        if (state.flags & 0x0400) {\n          copy = state.length;\n          if (copy > have) { copy = have; }\n          if (copy) {\n            if (state.head) {\n              len = state.head.extra_len - state.length;\n              if (!state.head.extra) {\n                // Use untyped array for more convenient processing later\n                state.head.extra = new Array(state.head.extra_len);\n              }\n              utils.arraySet(\n                state.head.extra,\n                input,\n                next,\n                // extra field is limited to 65536 bytes\n                // - no need for additional size check\n                copy,\n                /*len + copy > state.head.extra_max - len ? state.head.extra_max : copy,*/\n                len\n              );\n              //zmemcpy(state.head.extra + len, next,\n              //        len + copy > state.head.extra_max ?\n              //        state.head.extra_max - len : copy);\n            }\n            if (state.flags & 0x0200) {\n              state.check = crc32(state.check, input, copy, next);\n            }\n            have -= copy;\n            next += copy;\n            state.length -= copy;\n          }\n          if (state.length) { break inf_leave; }\n        }\n        state.length = 0;\n        state.mode = NAME;\n        /* falls through */\n      case NAME:\n        if (state.flags & 0x0800) {\n          if (have === 0) { break inf_leave; }\n          copy = 0;\n          do {\n            // TODO: 2 or 1 bytes?\n            len = input[next + copy++];\n            /* use constant limit because in js we should not preallocate memory */\n            if (state.head && len &&\n                (state.length < 65536 /*state.head.name_max*/)) {\n              state.head.name += String.fromCharCode(len);\n            }\n          } while (len && copy < have);\n\n          if (state.flags & 0x0200) {\n            state.check = crc32(state.check, input, copy, next);\n          }\n          have -= copy;\n          next += copy;\n          if (len) { break inf_leave; }\n        }\n        else if (state.head) {\n          state.head.name = null;\n        }\n        state.length = 0;\n        state.mode = COMMENT;\n        /* falls through */\n      case COMMENT:\n        if (state.flags & 0x1000) {\n          if (have === 0) { break inf_leave; }\n          copy = 0;\n          do {\n            len = input[next + copy++];\n            /* use constant limit because in js we should not preallocate memory */\n            if (state.head && len &&\n                (state.length < 65536 /*state.head.comm_max*/)) {\n              state.head.comment += String.fromCharCode(len);\n            }\n          } while (len && copy < have);\n          if (state.flags & 0x0200) {\n            state.check = crc32(state.check, input, copy, next);\n          }\n          have -= copy;\n          next += copy;\n          if (len) { break inf_leave; }\n        }\n        else if (state.head) {\n          state.head.comment = null;\n        }\n        state.mode = HCRC;\n        /* falls through */\n      case HCRC:\n        if (state.flags & 0x0200) {\n          //=== NEEDBITS(16); */\n          while (bits < 16) {\n            if (have === 0) { break inf_leave; }\n            have--;\n            hold += input[next++] << bits;\n            bits += 8;\n          }\n          //===//\n          if (hold !== (state.check & 0xffff)) {\n            strm.msg = 'header crc mismatch';\n            state.mode = BAD;\n            break;\n          }\n          //=== INITBITS();\n          hold = 0;\n          bits = 0;\n          //===//\n        }\n        if (state.head) {\n          state.head.hcrc = ((state.flags >> 9) & 1);\n          state.head.done = true;\n        }\n        strm.adler = state.check = 0;\n        state.mode = TYPE;\n        break;\n      case DICTID:\n        //=== NEEDBITS(32); */\n        while (bits < 32) {\n          if (have === 0) { break inf_leave; }\n          have--;\n          hold += input[next++] << bits;\n          bits += 8;\n        }\n        //===//\n        strm.adler = state.check = zswap32(hold);\n        //=== INITBITS();\n        hold = 0;\n        bits = 0;\n        //===//\n        state.mode = DICT;\n        /* falls through */\n      case DICT:\n        if (state.havedict === 0) {\n          //--- RESTORE() ---\n          strm.next_out = put;\n          strm.avail_out = left;\n          strm.next_in = next;\n          strm.avail_in = have;\n          state.hold = hold;\n          state.bits = bits;\n          //---\n          return Z_NEED_DICT;\n        }\n        strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/;\n        state.mode = TYPE;\n        /* falls through */\n      case TYPE:\n        if (flush === Z_BLOCK || flush === Z_TREES) { break inf_leave; }\n        /* falls through */\n      case TYPEDO:\n        if (state.last) {\n          //--- BYTEBITS() ---//\n          hold >>>= bits & 7;\n          bits -= bits & 7;\n          //---//\n          state.mode = CHECK;\n          break;\n        }\n        //=== NEEDBITS(3); */\n        while (bits < 3) {\n          if (have === 0) { break inf_leave; }\n          have--;\n          hold += input[next++] << bits;\n          bits += 8;\n        }\n        //===//\n        state.last = (hold & 0x01)/*BITS(1)*/;\n        //--- DROPBITS(1) ---//\n        hold >>>= 1;\n        bits -= 1;\n        //---//\n\n        switch ((hold & 0x03)/*BITS(2)*/) {\n          case 0:                             /* stored block */\n            //Tracev((stderr, \"inflate:     stored block%s\\n\",\n            //        state.last ? \" (last)\" : \"\"));\n            state.mode = STORED;\n            break;\n          case 1:                             /* fixed block */\n            fixedtables(state);\n            //Tracev((stderr, \"inflate:     fixed codes block%s\\n\",\n            //        state.last ? \" (last)\" : \"\"));\n            state.mode = LEN_;             /* decode codes */\n            if (flush === Z_TREES) {\n              //--- DROPBITS(2) ---//\n              hold >>>= 2;\n              bits -= 2;\n              //---//\n              break inf_leave;\n            }\n            break;\n          case 2:                             /* dynamic block */\n            //Tracev((stderr, \"inflate:     dynamic codes block%s\\n\",\n            //        state.last ? \" (last)\" : \"\"));\n            state.mode = TABLE;\n            break;\n          case 3:\n            strm.msg = 'invalid block type';\n            state.mode = BAD;\n        }\n        //--- DROPBITS(2) ---//\n        hold >>>= 2;\n        bits -= 2;\n        //---//\n        break;\n      case STORED:\n        //--- BYTEBITS() ---// /* go to byte boundary */\n        hold >>>= bits & 7;\n        bits -= bits & 7;\n        //---//\n        //=== NEEDBITS(32); */\n        while (bits < 32) {\n          if (have === 0) { break inf_leave; }\n          have--;\n          hold += input[next++] << bits;\n          bits += 8;\n        }\n        //===//\n        if ((hold & 0xffff) !== ((hold >>> 16) ^ 0xffff)) {\n          strm.msg = 'invalid stored block lengths';\n          state.mode = BAD;\n          break;\n        }\n        state.length = hold & 0xffff;\n        //Tracev((stderr, \"inflate:       stored length %u\\n\",\n        //        state.length));\n        //=== INITBITS();\n        hold = 0;\n        bits = 0;\n        //===//\n        state.mode = COPY_;\n        if (flush === Z_TREES) { break inf_leave; }\n        /* falls through */\n      case COPY_:\n        state.mode = COPY;\n        /* falls through */\n      case COPY:\n        copy = state.length;\n        if (copy) {\n          if (copy > have) { copy = have; }\n          if (copy > left) { copy = left; }\n          if (copy === 0) { break inf_leave; }\n          //--- zmemcpy(put, next, copy); ---\n          utils.arraySet(output, input, next, copy, put);\n          //---//\n          have -= copy;\n          next += copy;\n          left -= copy;\n          put += copy;\n          state.length -= copy;\n          break;\n        }\n        //Tracev((stderr, \"inflate:       stored end\\n\"));\n        state.mode = TYPE;\n        break;\n      case TABLE:\n        //=== NEEDBITS(14); */\n        while (bits < 14) {\n          if (have === 0) { break inf_leave; }\n          have--;\n          hold += input[next++] << bits;\n          bits += 8;\n        }\n        //===//\n        state.nlen = (hold & 0x1f)/*BITS(5)*/ + 257;\n        //--- DROPBITS(5) ---//\n        hold >>>= 5;\n        bits -= 5;\n        //---//\n        state.ndist = (hold & 0x1f)/*BITS(5)*/ + 1;\n        //--- DROPBITS(5) ---//\n        hold >>>= 5;\n        bits -= 5;\n        //---//\n        state.ncode = (hold & 0x0f)/*BITS(4)*/ + 4;\n        //--- DROPBITS(4) ---//\n        hold >>>= 4;\n        bits -= 4;\n        //---//\n//#ifndef PKZIP_BUG_WORKAROUND\n        if (state.nlen > 286 || state.ndist > 30) {\n          strm.msg = 'too many length or distance symbols';\n          state.mode = BAD;\n          break;\n        }\n//#endif\n        //Tracev((stderr, \"inflate:       table sizes ok\\n\"));\n        state.have = 0;\n        state.mode = LENLENS;\n        /* falls through */\n      case LENLENS:\n        while (state.have < state.ncode) {\n          //=== NEEDBITS(3);\n          while (bits < 3) {\n            if (have === 0) { break inf_leave; }\n            have--;\n            hold += input[next++] << bits;\n            bits += 8;\n          }\n          //===//\n          state.lens[order[state.have++]] = (hold & 0x07);//BITS(3);\n          //--- DROPBITS(3) ---//\n          hold >>>= 3;\n          bits -= 3;\n          //---//\n        }\n        while (state.have < 19) {\n          state.lens[order[state.have++]] = 0;\n        }\n        // We have separate tables & no pointers. 2 commented lines below not needed.\n        //state.next = state.codes;\n        //state.lencode = state.next;\n        // Switch to use dynamic table\n        state.lencode = state.lendyn;\n        state.lenbits = 7;\n\n        opts = { bits: state.lenbits };\n        ret = inflate_table(CODES, state.lens, 0, 19, state.lencode, 0, state.work, opts);\n        state.lenbits = opts.bits;\n\n        if (ret) {\n          strm.msg = 'invalid code lengths set';\n          state.mode = BAD;\n          break;\n        }\n        //Tracev((stderr, \"inflate:       code lengths ok\\n\"));\n        state.have = 0;\n        state.mode = CODELENS;\n        /* falls through */\n      case CODELENS:\n        while (state.have < state.nlen + state.ndist) {\n          for (;;) {\n            here = state.lencode[hold & ((1 << state.lenbits) - 1)];/*BITS(state.lenbits)*/\n            here_bits = here >>> 24;\n            here_op = (here >>> 16) & 0xff;\n            here_val = here & 0xffff;\n\n            if ((here_bits) <= bits) { break; }\n            //--- PULLBYTE() ---//\n            if (have === 0) { break inf_leave; }\n            have--;\n            hold += input[next++] << bits;\n            bits += 8;\n            //---//\n          }\n          if (here_val < 16) {\n            //--- DROPBITS(here.bits) ---//\n            hold >>>= here_bits;\n            bits -= here_bits;\n            //---//\n            state.lens[state.have++] = here_val;\n          }\n          else {\n            if (here_val === 16) {\n              //=== NEEDBITS(here.bits + 2);\n              n = here_bits + 2;\n              while (bits < n) {\n                if (have === 0) { break inf_leave; }\n                have--;\n                hold += input[next++] << bits;\n                bits += 8;\n              }\n              //===//\n              //--- DROPBITS(here.bits) ---//\n              hold >>>= here_bits;\n              bits -= here_bits;\n              //---//\n              if (state.have === 0) {\n                strm.msg = 'invalid bit length repeat';\n                state.mode = BAD;\n                break;\n              }\n              len = state.lens[state.have - 1];\n              copy = 3 + (hold & 0x03);//BITS(2);\n              //--- DROPBITS(2) ---//\n              hold >>>= 2;\n              bits -= 2;\n              //---//\n            }\n            else if (here_val === 17) {\n              //=== NEEDBITS(here.bits + 3);\n              n = here_bits + 3;\n              while (bits < n) {\n                if (have === 0) { break inf_leave; }\n                have--;\n                hold += input[next++] << bits;\n                bits += 8;\n              }\n              //===//\n              //--- DROPBITS(here.bits) ---//\n              hold >>>= here_bits;\n              bits -= here_bits;\n              //---//\n              len = 0;\n              copy = 3 + (hold & 0x07);//BITS(3);\n              //--- DROPBITS(3) ---//\n              hold >>>= 3;\n              bits -= 3;\n              //---//\n            }\n            else {\n              //=== NEEDBITS(here.bits + 7);\n              n = here_bits + 7;\n              while (bits < n) {\n                if (have === 0) { break inf_leave; }\n                have--;\n                hold += input[next++] << bits;\n                bits += 8;\n              }\n              //===//\n              //--- DROPBITS(here.bits) ---//\n              hold >>>= here_bits;\n              bits -= here_bits;\n              //---//\n              len = 0;\n              copy = 11 + (hold & 0x7f);//BITS(7);\n              //--- DROPBITS(7) ---//\n              hold >>>= 7;\n              bits -= 7;\n              //---//\n            }\n            if (state.have + copy > state.nlen + state.ndist) {\n              strm.msg = 'invalid bit length repeat';\n              state.mode = BAD;\n              break;\n            }\n            while (copy--) {\n              state.lens[state.have++] = len;\n            }\n          }\n        }\n\n        /* handle error breaks in while */\n        if (state.mode === BAD) { break; }\n\n        /* check for end-of-block code (better have one) */\n        if (state.lens[256] === 0) {\n          strm.msg = 'invalid code -- missing end-of-block';\n          state.mode = BAD;\n          break;\n        }\n\n        /* build code tables -- note: do not change the lenbits or distbits\n           values here (9 and 6) without reading the comments in inftrees.h\n           concerning the ENOUGH constants, which depend on those values */\n        state.lenbits = 9;\n\n        opts = { bits: state.lenbits };\n        ret = inflate_table(LENS, state.lens, 0, state.nlen, state.lencode, 0, state.work, opts);\n        // We have separate tables & no pointers. 2 commented lines below not needed.\n        // state.next_index = opts.table_index;\n        state.lenbits = opts.bits;\n        // state.lencode = state.next;\n\n        if (ret) {\n          strm.msg = 'invalid literal/lengths set';\n          state.mode = BAD;\n          break;\n        }\n\n        state.distbits = 6;\n        //state.distcode.copy(state.codes);\n        // Switch to use dynamic table\n        state.distcode = state.distdyn;\n        opts = { bits: state.distbits };\n        ret = inflate_table(DISTS, state.lens, state.nlen, state.ndist, state.distcode, 0, state.work, opts);\n        // We have separate tables & no pointers. 2 commented lines below not needed.\n        // state.next_index = opts.table_index;\n        state.distbits = opts.bits;\n        // state.distcode = state.next;\n\n        if (ret) {\n          strm.msg = 'invalid distances set';\n          state.mode = BAD;\n          break;\n        }\n        //Tracev((stderr, 'inflate:       codes ok\\n'));\n        state.mode = LEN_;\n        if (flush === Z_TREES) { break inf_leave; }\n        /* falls through */\n      case LEN_:\n        state.mode = LEN;\n        /* falls through */\n      case LEN:\n        if (have >= 6 && left >= 258) {\n          //--- RESTORE() ---\n          strm.next_out = put;\n          strm.avail_out = left;\n          strm.next_in = next;\n          strm.avail_in = have;\n          state.hold = hold;\n          state.bits = bits;\n          //---\n          inflate_fast(strm, _out);\n          //--- LOAD() ---\n          put = strm.next_out;\n          output = strm.output;\n          left = strm.avail_out;\n          next = strm.next_in;\n          input = strm.input;\n          have = strm.avail_in;\n          hold = state.hold;\n          bits = state.bits;\n          //---\n\n          if (state.mode === TYPE) {\n            state.back = -1;\n          }\n          break;\n        }\n        state.back = 0;\n        for (;;) {\n          here = state.lencode[hold & ((1 << state.lenbits) - 1)];  /*BITS(state.lenbits)*/\n          here_bits = here >>> 24;\n          here_op = (here >>> 16) & 0xff;\n          here_val = here & 0xffff;\n\n          if (here_bits <= bits) { break; }\n          //--- PULLBYTE() ---//\n          if (have === 0) { break inf_leave; }\n          have--;\n          hold += input[next++] << bits;\n          bits += 8;\n          //---//\n        }\n        if (here_op && (here_op & 0xf0) === 0) {\n          last_bits = here_bits;\n          last_op = here_op;\n          last_val = here_val;\n          for (;;) {\n            here = state.lencode[last_val +\n                    ((hold & ((1 << (last_bits + last_op)) - 1))/*BITS(last.bits + last.op)*/ >> last_bits)];\n            here_bits = here >>> 24;\n            here_op = (here >>> 16) & 0xff;\n            here_val = here & 0xffff;\n\n            if ((last_bits + here_bits) <= bits) { break; }\n            //--- PULLBYTE() ---//\n            if (have === 0) { break inf_leave; }\n            have--;\n            hold += input[next++] << bits;\n            bits += 8;\n            //---//\n          }\n          //--- DROPBITS(last.bits) ---//\n          hold >>>= last_bits;\n          bits -= last_bits;\n          //---//\n          state.back += last_bits;\n        }\n        //--- DROPBITS(here.bits) ---//\n        hold >>>= here_bits;\n        bits -= here_bits;\n        //---//\n        state.back += here_bits;\n        state.length = here_val;\n        if (here_op === 0) {\n          //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?\n          //        \"inflate:         literal '%c'\\n\" :\n          //        \"inflate:         literal 0x%02x\\n\", here.val));\n          state.mode = LIT;\n          break;\n        }\n        if (here_op & 32) {\n          //Tracevv((stderr, \"inflate:         end of block\\n\"));\n          state.back = -1;\n          state.mode = TYPE;\n          break;\n        }\n        if (here_op & 64) {\n          strm.msg = 'invalid literal/length code';\n          state.mode = BAD;\n          break;\n        }\n        state.extra = here_op & 15;\n        state.mode = LENEXT;\n        /* falls through */\n      case LENEXT:\n        if (state.extra) {\n          //=== NEEDBITS(state.extra);\n          n = state.extra;\n          while (bits < n) {\n            if (have === 0) { break inf_leave; }\n            have--;\n            hold += input[next++] << bits;\n            bits += 8;\n          }\n          //===//\n          state.length += hold & ((1 << state.extra) - 1)/*BITS(state.extra)*/;\n          //--- DROPBITS(state.extra) ---//\n          hold >>>= state.extra;\n          bits -= state.extra;\n          //---//\n          state.back += state.extra;\n        }\n        //Tracevv((stderr, \"inflate:         length %u\\n\", state.length));\n        state.was = state.length;\n        state.mode = DIST;\n        /* falls through */\n      case DIST:\n        for (;;) {\n          here = state.distcode[hold & ((1 << state.distbits) - 1)];/*BITS(state.distbits)*/\n          here_bits = here >>> 24;\n          here_op = (here >>> 16) & 0xff;\n          here_val = here & 0xffff;\n\n          if ((here_bits) <= bits) { break; }\n          //--- PULLBYTE() ---//\n          if (have === 0) { break inf_leave; }\n          have--;\n          hold += input[next++] << bits;\n          bits += 8;\n          //---//\n        }\n        if ((here_op & 0xf0) === 0) {\n          last_bits = here_bits;\n          last_op = here_op;\n          last_val = here_val;\n          for (;;) {\n            here = state.distcode[last_val +\n                    ((hold & ((1 << (last_bits + last_op)) - 1))/*BITS(last.bits + last.op)*/ >> last_bits)];\n            here_bits = here >>> 24;\n            here_op = (here >>> 16) & 0xff;\n            here_val = here & 0xffff;\n\n            if ((last_bits + here_bits) <= bits) { break; }\n            //--- PULLBYTE() ---//\n            if (have === 0) { break inf_leave; }\n            have--;\n            hold += input[next++] << bits;\n            bits += 8;\n            //---//\n          }\n          //--- DROPBITS(last.bits) ---//\n          hold >>>= last_bits;\n          bits -= last_bits;\n          //---//\n          state.back += last_bits;\n        }\n        //--- DROPBITS(here.bits) ---//\n        hold >>>= here_bits;\n        bits -= here_bits;\n        //---//\n        state.back += here_bits;\n        if (here_op & 64) {\n          strm.msg = 'invalid distance code';\n          state.mode = BAD;\n          break;\n        }\n        state.offset = here_val;\n        state.extra = (here_op) & 15;\n        state.mode = DISTEXT;\n        /* falls through */\n      case DISTEXT:\n        if (state.extra) {\n          //=== NEEDBITS(state.extra);\n          n = state.extra;\n          while (bits < n) {\n            if (have === 0) { break inf_leave; }\n            have--;\n            hold += input[next++] << bits;\n            bits += 8;\n          }\n          //===//\n          state.offset += hold & ((1 << state.extra) - 1)/*BITS(state.extra)*/;\n          //--- DROPBITS(state.extra) ---//\n          hold >>>= state.extra;\n          bits -= state.extra;\n          //---//\n          state.back += state.extra;\n        }\n//#ifdef INFLATE_STRICT\n        if (state.offset > state.dmax) {\n          strm.msg = 'invalid distance too far back';\n          state.mode = BAD;\n          break;\n        }\n//#endif\n        //Tracevv((stderr, \"inflate:         distance %u\\n\", state.offset));\n        state.mode = MATCH;\n        /* falls through */\n      case MATCH:\n        if (left === 0) { break inf_leave; }\n        copy = _out - left;\n        if (state.offset > copy) {         /* copy from window */\n          copy = state.offset - copy;\n          if (copy > state.whave) {\n            if (state.sane) {\n              strm.msg = 'invalid distance too far back';\n              state.mode = BAD;\n              break;\n            }\n// (!) This block is disabled in zlib defaults,\n// don't enable it for binary compatibility\n//#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR\n//          Trace((stderr, \"inflate.c too far\\n\"));\n//          copy -= state.whave;\n//          if (copy > state.length) { copy = state.length; }\n//          if (copy > left) { copy = left; }\n//          left -= copy;\n//          state.length -= copy;\n//          do {\n//            output[put++] = 0;\n//          } while (--copy);\n//          if (state.length === 0) { state.mode = LEN; }\n//          break;\n//#endif\n          }\n          if (copy > state.wnext) {\n            copy -= state.wnext;\n            from = state.wsize - copy;\n          }\n          else {\n            from = state.wnext - copy;\n          }\n          if (copy > state.length) { copy = state.length; }\n          from_source = state.window;\n        }\n        else {                              /* copy from output */\n          from_source = output;\n          from = put - state.offset;\n          copy = state.length;\n        }\n        if (copy > left) { copy = left; }\n        left -= copy;\n        state.length -= copy;\n        do {\n          output[put++] = from_source[from++];\n        } while (--copy);\n        if (state.length === 0) { state.mode = LEN; }\n        break;\n      case LIT:\n        if (left === 0) { break inf_leave; }\n        output[put++] = state.length;\n        left--;\n        state.mode = LEN;\n        break;\n      case CHECK:\n        if (state.wrap) {\n          //=== NEEDBITS(32);\n          while (bits < 32) {\n            if (have === 0) { break inf_leave; }\n            have--;\n            // Use '|' instead of '+' to make sure that result is signed\n            hold |= input[next++] << bits;\n            bits += 8;\n          }\n          //===//\n          _out -= left;\n          strm.total_out += _out;\n          state.total += _out;\n          if (_out) {\n            strm.adler = state.check =\n                /*UPDATE(state.check, put - _out, _out);*/\n                (state.flags ? crc32(state.check, output, _out, put - _out) : adler32(state.check, output, _out, put - _out));\n\n          }\n          _out = left;\n          // NB: crc32 stored as signed 32-bit int, zswap32 returns signed too\n          if ((state.flags ? hold : zswap32(hold)) !== state.check) {\n            strm.msg = 'incorrect data check';\n            state.mode = BAD;\n            break;\n          }\n          //=== INITBITS();\n          hold = 0;\n          bits = 0;\n          //===//\n          //Tracev((stderr, \"inflate:   check matches trailer\\n\"));\n        }\n        state.mode = LENGTH;\n        /* falls through */\n      case LENGTH:\n        if (state.wrap && state.flags) {\n          //=== NEEDBITS(32);\n          while (bits < 32) {\n            if (have === 0) { break inf_leave; }\n            have--;\n            hold += input[next++] << bits;\n            bits += 8;\n          }\n          //===//\n          if (hold !== (state.total & 0xffffffff)) {\n            strm.msg = 'incorrect length check';\n            state.mode = BAD;\n            break;\n          }\n          //=== INITBITS();\n          hold = 0;\n          bits = 0;\n          //===//\n          //Tracev((stderr, \"inflate:   length matches trailer\\n\"));\n        }\n        state.mode = DONE;\n        /* falls through */\n      case DONE:\n        ret = Z_STREAM_END;\n        break inf_leave;\n      case BAD:\n        ret = Z_DATA_ERROR;\n        break inf_leave;\n      case MEM:\n        return Z_MEM_ERROR;\n      case SYNC:\n        /* falls through */\n      default:\n        return Z_STREAM_ERROR;\n    }\n  }\n\n  // inf_leave <- here is real place for \"goto inf_leave\", emulated via \"break inf_leave\"\n\n  /*\n     Return from inflate(), updating the total counts and the check value.\n     If there was no progress during the inflate() call, return a buffer\n     error.  Call updatewindow() to create and/or update the window state.\n     Note: a memory error from inflate() is non-recoverable.\n   */\n\n  //--- RESTORE() ---\n  strm.next_out = put;\n  strm.avail_out = left;\n  strm.next_in = next;\n  strm.avail_in = have;\n  state.hold = hold;\n  state.bits = bits;\n  //---\n\n  if (state.wsize || (_out !== strm.avail_out && state.mode < BAD &&\n                      (state.mode < CHECK || flush !== Z_FINISH))) {\n    if (updatewindow(strm, strm.output, strm.next_out, _out - strm.avail_out)) {\n      state.mode = MEM;\n      return Z_MEM_ERROR;\n    }\n  }\n  _in -= strm.avail_in;\n  _out -= strm.avail_out;\n  strm.total_in += _in;\n  strm.total_out += _out;\n  state.total += _out;\n  if (state.wrap && _out) {\n    strm.adler = state.check = /*UPDATE(state.check, strm.next_out - _out, _out);*/\n      (state.flags ? crc32(state.check, output, _out, strm.next_out - _out) : adler32(state.check, output, _out, strm.next_out - _out));\n  }\n  strm.data_type = state.bits + (state.last ? 64 : 0) +\n                    (state.mode === TYPE ? 128 : 0) +\n                    (state.mode === LEN_ || state.mode === COPY_ ? 256 : 0);\n  if (((_in === 0 && _out === 0) || flush === Z_FINISH) && ret === Z_OK) {\n    ret = Z_BUF_ERROR;\n  }\n  return ret;\n}\n\nfunction inflateEnd(strm) {\n\n  if (!strm || !strm.state /*|| strm->zfree == (free_func)0*/) {\n    return Z_STREAM_ERROR;\n  }\n\n  var state = strm.state;\n  if (state.window) {\n    state.window = null;\n  }\n  strm.state = null;\n  return Z_OK;\n}\n\nfunction inflateGetHeader(strm, head) {\n  var state;\n\n  /* check state */\n  if (!strm || !strm.state) { return Z_STREAM_ERROR; }\n  state = strm.state;\n  if ((state.wrap & 2) === 0) { return Z_STREAM_ERROR; }\n\n  /* save header structure */\n  state.head = head;\n  head.done = false;\n  return Z_OK;\n}\n\nfunction inflateSetDictionary(strm, dictionary) {\n  var dictLength = dictionary.length;\n\n  var state;\n  var dictid;\n  var ret;\n\n  /* check state */\n  if (!strm /* == Z_NULL */ || !strm.state /* == Z_NULL */) { return Z_STREAM_ERROR; }\n  state = strm.state;\n\n  if (state.wrap !== 0 && state.mode !== DICT) {\n    return Z_STREAM_ERROR;\n  }\n\n  /* check for correct dictionary identifier */\n  if (state.mode === DICT) {\n    dictid = 1; /* adler32(0, null, 0)*/\n    /* dictid = adler32(dictid, dictionary, dictLength); */\n    dictid = adler32(dictid, dictionary, dictLength, 0);\n    if (dictid !== state.check) {\n      return Z_DATA_ERROR;\n    }\n  }\n  /* copy dictionary to window using updatewindow(), which will amend the\n   existing dictionary if appropriate */\n  ret = updatewindow(strm, dictionary, dictLength, dictLength);\n  if (ret) {\n    state.mode = MEM;\n    return Z_MEM_ERROR;\n  }\n  state.havedict = 1;\n  // Tracev((stderr, \"inflate:   dictionary set\\n\"));\n  return Z_OK;\n}\n\nexports.inflateReset = inflateReset;\nexports.inflateReset2 = inflateReset2;\nexports.inflateResetKeep = inflateResetKeep;\nexports.inflateInit = inflateInit;\nexports.inflateInit2 = inflateInit2;\nexports.inflate = inflate;\nexports.inflateEnd = inflateEnd;\nexports.inflateGetHeader = inflateGetHeader;\nexports.inflateSetDictionary = inflateSetDictionary;\nexports.inflateInfo = 'pako inflate (from Nodeca project)';\n\n/* Not implemented\nexports.inflateCopy = inflateCopy;\nexports.inflateGetDictionary = inflateGetDictionary;\nexports.inflateMark = inflateMark;\nexports.inflatePrime = inflatePrime;\nexports.inflateSync = inflateSync;\nexports.inflateSyncPoint = inflateSyncPoint;\nexports.inflateUndermine = inflateUndermine;\n*/\n","'use strict';\n\n// (C) 1995-2013 Jean-loup Gailly and Mark Adler\n// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//   claim that you wrote the original software. If you use this software\n//   in a product, an acknowledgment in the product documentation would be\n//   appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//   misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n\n// See state defs from inflate.js\nvar BAD = 30;       /* got a data error -- remain here until reset */\nvar TYPE = 12;      /* i: waiting for type bits, including last-flag bit */\n\n/*\n   Decode literal, length, and distance codes and write out the resulting\n   literal and match bytes until either not enough input or output is\n   available, an end-of-block is encountered, or a data error is encountered.\n   When large enough input and output buffers are supplied to inflate(), for\n   example, a 16K input buffer and a 64K output buffer, more than 95% of the\n   inflate execution time is spent in this routine.\n\n   Entry assumptions:\n\n        state.mode === LEN\n        strm.avail_in >= 6\n        strm.avail_out >= 258\n        start >= strm.avail_out\n        state.bits < 8\n\n   On return, state.mode is one of:\n\n        LEN -- ran out of enough output space or enough available input\n        TYPE -- reached end of block code, inflate() to interpret next block\n        BAD -- error in block data\n\n   Notes:\n\n    - The maximum input bits used by a length/distance pair is 15 bits for the\n      length code, 5 bits for the length extra, 15 bits for the distance code,\n      and 13 bits for the distance extra.  This totals 48 bits, or six bytes.\n      Therefore if strm.avail_in >= 6, then there is enough input to avoid\n      checking for available input while decoding.\n\n    - The maximum bytes that a single length/distance pair can output is 258\n      bytes, which is the maximum length that can be coded.  inflate_fast()\n      requires strm.avail_out >= 258 for each loop to avoid checking for\n      output space.\n */\nmodule.exports = function inflate_fast(strm, start) {\n  var state;\n  var _in;                    /* local strm.input */\n  var last;                   /* have enough input while in < last */\n  var _out;                   /* local strm.output */\n  var beg;                    /* inflate()'s initial strm.output */\n  var end;                    /* while out < end, enough space available */\n//#ifdef INFLATE_STRICT\n  var dmax;                   /* maximum distance from zlib header */\n//#endif\n  var wsize;                  /* window size or zero if not using window */\n  var whave;                  /* valid bytes in the window */\n  var wnext;                  /* window write index */\n  // Use `s_window` instead `window`, avoid conflict with instrumentation tools\n  var s_window;               /* allocated sliding window, if wsize != 0 */\n  var hold;                   /* local strm.hold */\n  var bits;                   /* local strm.bits */\n  var lcode;                  /* local strm.lencode */\n  var dcode;                  /* local strm.distcode */\n  var lmask;                  /* mask for first level of length codes */\n  var dmask;                  /* mask for first level of distance codes */\n  var here;                   /* retrieved table entry */\n  var op;                     /* code bits, operation, extra bits, or */\n                              /*  window position, window bytes to copy */\n  var len;                    /* match length, unused bytes */\n  var dist;                   /* match distance */\n  var from;                   /* where to copy match from */\n  var from_source;\n\n\n  var input, output; // JS specific, because we have no pointers\n\n  /* copy state to local variables */\n  state = strm.state;\n  //here = state.here;\n  _in = strm.next_in;\n  input = strm.input;\n  last = _in + (strm.avail_in - 5);\n  _out = strm.next_out;\n  output = strm.output;\n  beg = _out - (start - strm.avail_out);\n  end = _out + (strm.avail_out - 257);\n//#ifdef INFLATE_STRICT\n  dmax = state.dmax;\n//#endif\n  wsize = state.wsize;\n  whave = state.whave;\n  wnext = state.wnext;\n  s_window = state.window;\n  hold = state.hold;\n  bits = state.bits;\n  lcode = state.lencode;\n  dcode = state.distcode;\n  lmask = (1 << state.lenbits) - 1;\n  dmask = (1 << state.distbits) - 1;\n\n\n  /* decode literals and length/distances until end-of-block or not enough\n     input data or output space */\n\n  top:\n  do {\n    if (bits < 15) {\n      hold += input[_in++] << bits;\n      bits += 8;\n      hold += input[_in++] << bits;\n      bits += 8;\n    }\n\n    here = lcode[hold & lmask];\n\n    dolen:\n    for (;;) { // Goto emulation\n      op = here >>> 24/*here.bits*/;\n      hold >>>= op;\n      bits -= op;\n      op = (here >>> 16) & 0xff/*here.op*/;\n      if (op === 0) {                          /* literal */\n        //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?\n        //        \"inflate:         literal '%c'\\n\" :\n        //        \"inflate:         literal 0x%02x\\n\", here.val));\n        output[_out++] = here & 0xffff/*here.val*/;\n      }\n      else if (op & 16) {                     /* length base */\n        len = here & 0xffff/*here.val*/;\n        op &= 15;                           /* number of extra bits */\n        if (op) {\n          if (bits < op) {\n            hold += input[_in++] << bits;\n            bits += 8;\n          }\n          len += hold & ((1 << op) - 1);\n          hold >>>= op;\n          bits -= op;\n        }\n        //Tracevv((stderr, \"inflate:         length %u\\n\", len));\n        if (bits < 15) {\n          hold += input[_in++] << bits;\n          bits += 8;\n          hold += input[_in++] << bits;\n          bits += 8;\n        }\n        here = dcode[hold & dmask];\n\n        dodist:\n        for (;;) { // goto emulation\n          op = here >>> 24/*here.bits*/;\n          hold >>>= op;\n          bits -= op;\n          op = (here >>> 16) & 0xff/*here.op*/;\n\n          if (op & 16) {                      /* distance base */\n            dist = here & 0xffff/*here.val*/;\n            op &= 15;                       /* number of extra bits */\n            if (bits < op) {\n              hold += input[_in++] << bits;\n              bits += 8;\n              if (bits < op) {\n                hold += input[_in++] << bits;\n                bits += 8;\n              }\n            }\n            dist += hold & ((1 << op) - 1);\n//#ifdef INFLATE_STRICT\n            if (dist > dmax) {\n              strm.msg = 'invalid distance too far back';\n              state.mode = BAD;\n              break top;\n            }\n//#endif\n            hold >>>= op;\n            bits -= op;\n            //Tracevv((stderr, \"inflate:         distance %u\\n\", dist));\n            op = _out - beg;                /* max distance in output */\n            if (dist > op) {                /* see if copy from window */\n              op = dist - op;               /* distance back in window */\n              if (op > whave) {\n                if (state.sane) {\n                  strm.msg = 'invalid distance too far back';\n                  state.mode = BAD;\n                  break top;\n                }\n\n// (!) This block is disabled in zlib defaults,\n// don't enable it for binary compatibility\n//#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR\n//                if (len <= op - whave) {\n//                  do {\n//                    output[_out++] = 0;\n//                  } while (--len);\n//                  continue top;\n//                }\n//                len -= op - whave;\n//                do {\n//                  output[_out++] = 0;\n//                } while (--op > whave);\n//                if (op === 0) {\n//                  from = _out - dist;\n//                  do {\n//                    output[_out++] = output[from++];\n//                  } while (--len);\n//                  continue top;\n//                }\n//#endif\n              }\n              from = 0; // window index\n              from_source = s_window;\n              if (wnext === 0) {           /* very common case */\n                from += wsize - op;\n                if (op < len) {         /* some from window */\n                  len -= op;\n                  do {\n                    output[_out++] = s_window[from++];\n                  } while (--op);\n                  from = _out - dist;  /* rest from output */\n                  from_source = output;\n                }\n              }\n              else if (wnext < op) {      /* wrap around window */\n                from += wsize + wnext - op;\n                op -= wnext;\n                if (op < len) {         /* some from end of window */\n                  len -= op;\n                  do {\n                    output[_out++] = s_window[from++];\n                  } while (--op);\n                  from = 0;\n                  if (wnext < len) {  /* some from start of window */\n                    op = wnext;\n                    len -= op;\n                    do {\n                      output[_out++] = s_window[from++];\n                    } while (--op);\n                    from = _out - dist;      /* rest from output */\n                    from_source = output;\n                  }\n                }\n              }\n              else {                      /* contiguous in window */\n                from += wnext - op;\n                if (op < len) {         /* some from window */\n                  len -= op;\n                  do {\n                    output[_out++] = s_window[from++];\n                  } while (--op);\n                  from = _out - dist;  /* rest from output */\n                  from_source = output;\n                }\n              }\n              while (len > 2) {\n                output[_out++] = from_source[from++];\n                output[_out++] = from_source[from++];\n                output[_out++] = from_source[from++];\n                len -= 3;\n              }\n              if (len) {\n                output[_out++] = from_source[from++];\n                if (len > 1) {\n                  output[_out++] = from_source[from++];\n                }\n              }\n            }\n            else {\n              from = _out - dist;          /* copy direct from output */\n              do {                        /* minimum length is three */\n                output[_out++] = output[from++];\n                output[_out++] = output[from++];\n                output[_out++] = output[from++];\n                len -= 3;\n              } while (len > 2);\n              if (len) {\n                output[_out++] = output[from++];\n                if (len > 1) {\n                  output[_out++] = output[from++];\n                }\n              }\n            }\n          }\n          else if ((op & 64) === 0) {          /* 2nd level distance code */\n            here = dcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))];\n            continue dodist;\n          }\n          else {\n            strm.msg = 'invalid distance code';\n            state.mode = BAD;\n            break top;\n          }\n\n          break; // need to emulate goto via \"continue\"\n        }\n      }\n      else if ((op & 64) === 0) {              /* 2nd level length code */\n        here = lcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))];\n        continue dolen;\n      }\n      else if (op & 32) {                     /* end-of-block */\n        //Tracevv((stderr, \"inflate:         end of block\\n\"));\n        state.mode = TYPE;\n        break top;\n      }\n      else {\n        strm.msg = 'invalid literal/length code';\n        state.mode = BAD;\n        break top;\n      }\n\n      break; // need to emulate goto via \"continue\"\n    }\n  } while (_in < last && _out < end);\n\n  /* return unused bytes (on entry, bits < 8, so in won't go too far back) */\n  len = bits >> 3;\n  _in -= len;\n  bits -= len << 3;\n  hold &= (1 << bits) - 1;\n\n  /* update state and return */\n  strm.next_in = _in;\n  strm.next_out = _out;\n  strm.avail_in = (_in < last ? 5 + (last - _in) : 5 - (_in - last));\n  strm.avail_out = (_out < end ? 257 + (end - _out) : 257 - (_out - end));\n  state.hold = hold;\n  state.bits = bits;\n  return;\n};\n","'use strict';\n\n// (C) 1995-2013 Jean-loup Gailly and Mark Adler\n// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//   claim that you wrote the original software. If you use this software\n//   in a product, an acknowledgment in the product documentation would be\n//   appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//   misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n\nvar utils = require('../utils/common');\n\nvar MAXBITS = 15;\nvar ENOUGH_LENS = 852;\nvar ENOUGH_DISTS = 592;\n//var ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS);\n\nvar CODES = 0;\nvar LENS = 1;\nvar DISTS = 2;\n\nvar lbase = [ /* Length codes 257..285 base */\n  3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,\n  35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0\n];\n\nvar lext = [ /* Length codes 257..285 extra */\n  16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18,\n  19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78\n];\n\nvar dbase = [ /* Distance codes 0..29 base */\n  1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,\n  257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,\n  8193, 12289, 16385, 24577, 0, 0\n];\n\nvar dext = [ /* Distance codes 0..29 extra */\n  16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22,\n  23, 23, 24, 24, 25, 25, 26, 26, 27, 27,\n  28, 28, 29, 29, 64, 64\n];\n\nmodule.exports = function inflate_table(type, lens, lens_index, codes, table, table_index, work, opts)\n{\n  var bits = opts.bits;\n      //here = opts.here; /* table entry for duplication */\n\n  var len = 0;               /* a code's length in bits */\n  var sym = 0;               /* index of code symbols */\n  var min = 0, max = 0;          /* minimum and maximum code lengths */\n  var root = 0;              /* number of index bits for root table */\n  var curr = 0;              /* number of index bits for current table */\n  var drop = 0;              /* code bits to drop for sub-table */\n  var left = 0;                   /* number of prefix codes available */\n  var used = 0;              /* code entries in table used */\n  var huff = 0;              /* Huffman code */\n  var incr;              /* for incrementing code, index */\n  var fill;              /* index for replicating entries */\n  var low;               /* low bits for current root entry */\n  var mask;              /* mask for low root bits */\n  var next;             /* next available space in table */\n  var base = null;     /* base value table to use */\n  var base_index = 0;\n//  var shoextra;    /* extra bits table to use */\n  var end;                    /* use base and extra for symbol > end */\n  var count = new utils.Buf16(MAXBITS + 1); //[MAXBITS+1];    /* number of codes of each length */\n  var offs = new utils.Buf16(MAXBITS + 1); //[MAXBITS+1];     /* offsets in table for each length */\n  var extra = null;\n  var extra_index = 0;\n\n  var here_bits, here_op, here_val;\n\n  /*\n   Process a set of code lengths to create a canonical Huffman code.  The\n   code lengths are lens[0..codes-1].  Each length corresponds to the\n   symbols 0..codes-1.  The Huffman code is generated by first sorting the\n   symbols by length from short to long, and retaining the symbol order\n   for codes with equal lengths.  Then the code starts with all zero bits\n   for the first code of the shortest length, and the codes are integer\n   increments for the same length, and zeros are appended as the length\n   increases.  For the deflate format, these bits are stored backwards\n   from their more natural integer increment ordering, and so when the\n   decoding tables are built in the large loop below, the integer codes\n   are incremented backwards.\n\n   This routine assumes, but does not check, that all of the entries in\n   lens[] are in the range 0..MAXBITS.  The caller must assure this.\n   1..MAXBITS is interpreted as that code length.  zero means that that\n   symbol does not occur in this code.\n\n   The codes are sorted by computing a count of codes for each length,\n   creating from that a table of starting indices for each length in the\n   sorted table, and then entering the symbols in order in the sorted\n   table.  The sorted table is work[], with that space being provided by\n   the caller.\n\n   The length counts are used for other purposes as well, i.e. finding\n   the minimum and maximum length codes, determining if there are any\n   codes at all, checking for a valid set of lengths, and looking ahead\n   at length counts to determine sub-table sizes when building the\n   decoding tables.\n   */\n\n  /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */\n  for (len = 0; len <= MAXBITS; len++) {\n    count[len] = 0;\n  }\n  for (sym = 0; sym < codes; sym++) {\n    count[lens[lens_index + sym]]++;\n  }\n\n  /* bound code lengths, force root to be within code lengths */\n  root = bits;\n  for (max = MAXBITS; max >= 1; max--) {\n    if (count[max] !== 0) { break; }\n  }\n  if (root > max) {\n    root = max;\n  }\n  if (max === 0) {                     /* no symbols to code at all */\n    //table.op[opts.table_index] = 64;  //here.op = (var char)64;    /* invalid code marker */\n    //table.bits[opts.table_index] = 1;   //here.bits = (var char)1;\n    //table.val[opts.table_index++] = 0;   //here.val = (var short)0;\n    table[table_index++] = (1 << 24) | (64 << 16) | 0;\n\n\n    //table.op[opts.table_index] = 64;\n    //table.bits[opts.table_index] = 1;\n    //table.val[opts.table_index++] = 0;\n    table[table_index++] = (1 << 24) | (64 << 16) | 0;\n\n    opts.bits = 1;\n    return 0;     /* no symbols, but wait for decoding to report error */\n  }\n  for (min = 1; min < max; min++) {\n    if (count[min] !== 0) { break; }\n  }\n  if (root < min) {\n    root = min;\n  }\n\n  /* check for an over-subscribed or incomplete set of lengths */\n  left = 1;\n  for (len = 1; len <= MAXBITS; len++) {\n    left <<= 1;\n    left -= count[len];\n    if (left < 0) {\n      return -1;\n    }        /* over-subscribed */\n  }\n  if (left > 0 && (type === CODES || max !== 1)) {\n    return -1;                      /* incomplete set */\n  }\n\n  /* generate offsets into symbol table for each length for sorting */\n  offs[1] = 0;\n  for (len = 1; len < MAXBITS; len++) {\n    offs[len + 1] = offs[len] + count[len];\n  }\n\n  /* sort symbols by length, by symbol order within each length */\n  for (sym = 0; sym < codes; sym++) {\n    if (lens[lens_index + sym] !== 0) {\n      work[offs[lens[lens_index + sym]]++] = sym;\n    }\n  }\n\n  /*\n   Create and fill in decoding tables.  In this loop, the table being\n   filled is at next and has curr index bits.  The code being used is huff\n   with length len.  That code is converted to an index by dropping drop\n   bits off of the bottom.  For codes where len is less than drop + curr,\n   those top drop + curr - len bits are incremented through all values to\n   fill the table with replicated entries.\n\n   root is the number of index bits for the root table.  When len exceeds\n   root, sub-tables are created pointed to by the root entry with an index\n   of the low root bits of huff.  This is saved in low to check for when a\n   new sub-table should be started.  drop is zero when the root table is\n   being filled, and drop is root when sub-tables are being filled.\n\n   When a new sub-table is needed, it is necessary to look ahead in the\n   code lengths to determine what size sub-table is needed.  The length\n   counts are used for this, and so count[] is decremented as codes are\n   entered in the tables.\n\n   used keeps track of how many table entries have been allocated from the\n   provided *table space.  It is checked for LENS and DIST tables against\n   the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in\n   the initial root table size constants.  See the comments in inftrees.h\n   for more information.\n\n   sym increments through all symbols, and the loop terminates when\n   all codes of length max, i.e. all codes, have been processed.  This\n   routine permits incomplete codes, so another loop after this one fills\n   in the rest of the decoding tables with invalid code markers.\n   */\n\n  /* set up for code type */\n  // poor man optimization - use if-else instead of switch,\n  // to avoid deopts in old v8\n  if (type === CODES) {\n    base = extra = work;    /* dummy value--not used */\n    end = 19;\n\n  } else if (type === LENS) {\n    base = lbase;\n    base_index -= 257;\n    extra = lext;\n    extra_index -= 257;\n    end = 256;\n\n  } else {                    /* DISTS */\n    base = dbase;\n    extra = dext;\n    end = -1;\n  }\n\n  /* initialize opts for loop */\n  huff = 0;                   /* starting code */\n  sym = 0;                    /* starting code symbol */\n  len = min;                  /* starting code length */\n  next = table_index;              /* current table to fill in */\n  curr = root;                /* current table index bits */\n  drop = 0;                   /* current bits to drop from code for index */\n  low = -1;                   /* trigger new sub-table when len > root */\n  used = 1 << root;          /* use root table entries */\n  mask = used - 1;            /* mask for comparing low */\n\n  /* check available table space */\n  if ((type === LENS && used > ENOUGH_LENS) ||\n    (type === DISTS && used > ENOUGH_DISTS)) {\n    return 1;\n  }\n\n  /* process all codes and make table entries */\n  for (;;) {\n    /* create table entry */\n    here_bits = len - drop;\n    if (work[sym] < end) {\n      here_op = 0;\n      here_val = work[sym];\n    }\n    else if (work[sym] > end) {\n      here_op = extra[extra_index + work[sym]];\n      here_val = base[base_index + work[sym]];\n    }\n    else {\n      here_op = 32 + 64;         /* end of block */\n      here_val = 0;\n    }\n\n    /* replicate for those indices with low len bits equal to huff */\n    incr = 1 << (len - drop);\n    fill = 1 << curr;\n    min = fill;                 /* save offset to next table */\n    do {\n      fill -= incr;\n      table[next + (huff >> drop) + fill] = (here_bits << 24) | (here_op << 16) | here_val |0;\n    } while (fill !== 0);\n\n    /* backwards increment the len-bit code huff */\n    incr = 1 << (len - 1);\n    while (huff & incr) {\n      incr >>= 1;\n    }\n    if (incr !== 0) {\n      huff &= incr - 1;\n      huff += incr;\n    } else {\n      huff = 0;\n    }\n\n    /* go to next symbol, update count, len */\n    sym++;\n    if (--count[len] === 0) {\n      if (len === max) { break; }\n      len = lens[lens_index + work[sym]];\n    }\n\n    /* create new sub-table if needed */\n    if (len > root && (huff & mask) !== low) {\n      /* if first time, transition to sub-tables */\n      if (drop === 0) {\n        drop = root;\n      }\n\n      /* increment past last table */\n      next += min;            /* here min is 1 << curr */\n\n      /* determine length of next table */\n      curr = len - drop;\n      left = 1 << curr;\n      while (curr + drop < max) {\n        left -= count[curr + drop];\n        if (left <= 0) { break; }\n        curr++;\n        left <<= 1;\n      }\n\n      /* check for enough space */\n      used += 1 << curr;\n      if ((type === LENS && used > ENOUGH_LENS) ||\n        (type === DISTS && used > ENOUGH_DISTS)) {\n        return 1;\n      }\n\n      /* point entry in root table to sub-table */\n      low = huff & mask;\n      /*table.op[low] = curr;\n      table.bits[low] = root;\n      table.val[low] = next - opts.table_index;*/\n      table[low] = (root << 24) | (curr << 16) | (next - table_index) |0;\n    }\n  }\n\n  /* fill in remaining table entry if code is incomplete (guaranteed to have\n   at most one remaining entry, since if the code is incomplete, the\n   maximum code length that was allowed to get this far is one bit) */\n  if (huff !== 0) {\n    //table.op[next + huff] = 64;            /* invalid code marker */\n    //table.bits[next + huff] = len - drop;\n    //table.val[next + huff] = 0;\n    table[next + huff] = ((len - drop) << 24) | (64 << 16) |0;\n  }\n\n  /* set return parameters */\n  //opts.table_index += used;\n  opts.bits = root;\n  return 0;\n};\n","'use strict';\n\n// (C) 1995-2013 Jean-loup Gailly and Mark Adler\n// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//   claim that you wrote the original software. If you use this software\n//   in a product, an acknowledgment in the product documentation would be\n//   appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//   misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n\nfunction GZheader() {\n  /* true if compressed data believed to be text */\n  this.text       = 0;\n  /* modification time */\n  this.time       = 0;\n  /* extra flags (not used when writing a gzip file) */\n  this.xflags     = 0;\n  /* operating system */\n  this.os         = 0;\n  /* pointer to extra field or Z_NULL if none */\n  this.extra      = null;\n  /* extra field length (valid if extra != Z_NULL) */\n  this.extra_len  = 0; // Actually, we don't need it in JS,\n                       // but leave for few code modifications\n\n  //\n  // Setup limits is not necessary because in js we should not preallocate memory\n  // for inflate use constant limit in 65536 bytes\n  //\n\n  /* space at extra (only when reading header) */\n  // this.extra_max  = 0;\n  /* pointer to zero-terminated file name or Z_NULL */\n  this.name       = '';\n  /* space at name (only when reading header) */\n  // this.name_max   = 0;\n  /* pointer to zero-terminated comment or Z_NULL */\n  this.comment    = '';\n  /* space at comment (only when reading header) */\n  // this.comm_max   = 0;\n  /* true if there was or will be a header crc */\n  this.hcrc       = 0;\n  /* true when done reading gzip header (not used when writing a gzip file) */\n  this.done       = false;\n}\n\nmodule.exports = GZheader;\n"],"sourceRoot":""}                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               /*
 * Copyright 2020-2021 The OpenSSL Project Authors. All Rights Reserved.
 *
 * Licensed under the Apache License 2.0 (the "License").  You may not use
 * this file except in compliance with the License.  You can obtain a copy
 * in the file LICENSE in the source distribution or at
 * https://www.openssl.org/source/license.html
 */

/*
 * This header file preserves symbols from pre-3.0 OpenSSL.
 * It should never be included directly, as it's already included
 * by the public {lib}err.h headers, and since it will go away some
 * time in the future.
 */

#ifndef OPENSSL_CRYPTOERR_LEGACY_H
# define OPENSSL_CRYPTOERR_LEGACY_H
# pragma once

# include <openssl/macros.h>
# include <openssl/symhacks.h>

# ifdef  __cplusplus
extern "C" {
# endif

# ifndef OPENSSL_NO_DEPRECATED_3_0
OSSL_DEPRECATEDIN_3_0 int ERR_load_ASN1_strings(void);
OSSL_DEPRECATEDIN_3_0 int ERR_load_ASYNC_strings(void);
OSSL_DEPRECATEDIN_3_0 int ERR_load_BIO_strings(void);
OSSL_DEPRECATEDIN_3_0 int ERR_load_BN_strings(void);
OSSL_DEPRECATEDIN_3_0 int ERR_load_BUF_strings(void);
#  ifndef OPENSSL_NO_CMS
OSSL_DEPRECATEDIN_3_0 int ERR_load_CMS_strings(void);
#  endif
#  ifndef OPENSSL_NO_COMP
OSSL_DEPRECATEDIN_3_0 int ERR_load_COMP_strings(void);
#  endif
OSSL_DEPRECATEDIN_3_0 int ERR_load_CONF_strings(void);
OSSL_DEPRECATEDIN_3_0 int ERR_load_CRYPTO_strings(void);
#  ifndef OPENSSL_NO_CT
OSSL_DEPRECATEDIN_3_0 int ERR_load_CT_strings(void);
#  endif
#  ifndef OPENSSL_NO_DH
OSSL_DEPRECATEDIN_3_0 int ERR_load_DH_strings(void);
#  endif
#  ifndef OPENSSL_NO_DSA
OSSL_DEPRECATEDIN_3_0 int ERR_load_DSA_strings(void);
#  endif
#  ifndef OPENSSL_NO_EC
OSSL_DEPRECATEDIN_3_0 int ERR_load_EC_strings(void);
#  endif
#  ifndef OPENSSL_NO_ENGINE
OSSL_DEPRECATEDIN_3_0 int ERR_load_ENGINE_strings(void);
#  endif
OSSL_DEPRECATEDIN_3_0 int ERR_load_ERR_strings(void);
OSSL_DEPRECATEDIN_3_0 int ERR_load_EVP_strings(void);
OSSL_DEPRECATEDIN_3_0 int ERR_load_KDF_strings(void);
OSSL_DEPRECATEDIN_3_0 int ERR_load_OBJ_strings(void);
#  ifndef OPENSSL_NO_OCSP
OSSL_DEPRECATEDIN_3_0 int ERR_load_OCSP_strings(void);
#  endif
OSSL_DEPRECATEDIN_3_0 int ERR_load_PEM_strings(void);
OSSL_DEPRECATEDIN_3_0 int ERR_load_PKCS12_strings(void);
OSSL_DEPRECATEDIN_3_0 int ERR_load_PKCS7_strings(void);
OSSL_DEPRECATEDIN_3_0 int ERR_load_RAND_strings(void);
OSSL_DEPRECATEDIN_3_0 int ERR_load_RSA_strings(void);
OSSL_DEPRECATEDIN_3_0 int ERR_load_OSSL_STORE_strings(void);
#  ifndef OPENSSL_NO_TS
OSSL_DEPRECATEDIN_3_0 int ERR_load_TS_strings(void);
#  endif
OSSL_DEPRECATEDIN_3_0 int ERR_load_UI_strings(void);
OSSL_DEPRECATEDIN_3_0 int ERR_load_X509_strings(void);
OSSL_DEPRECATEDIN_3_0 int ERR_load_X509V3_strings(void);

/* Collected _F_ macros from OpenSSL 1.1.1 */

/*
 * ASN1 function codes.
 */
#  define ASN1_F_A2D_ASN1_OBJECT                           0
#  define ASN1_F_A2I_ASN1_INTEGER                          0
#  define ASN1_F_A2I_ASN1_STRING                           0
#  define ASN1_F_APPEND_EXP                                0
#  define ASN1_F_ASN1_BIO_INIT                             0
#  define ASN1_F_ASN1_BIT_STRING_SET_BIT                   0
#  define ASN1_F_ASN1_CB                                   0
#  define ASN1_F_ASN1_CHECK_TLEN                           0
#  define ASN1_F_ASN1_COLLECT                              0
#  define ASN1_F_ASN1_D2I_EX_PRIMITIVE                     0
#  define ASN1_F_ASN1_D2I_FP                               0
#  define ASN1_F_ASN1_D2I_READ_BIO                         0
#  define ASN1_F_ASN1_DIGEST                               0
#  define ASN1_F_ASN1_DO_ADB                               0
#  define ASN1_F_ASN1_DO_LOCK                              0
#  define ASN1_F_ASN1_DUP                                  0
#  define ASN1_F_ASN1_ENC_SAVE                             0
#  define ASN1_F_ASN1_EX_C2I                               0
#  define ASN1_F_ASN1_FIND_END                             0
#  define ASN1_F_ASN1_GENERALIZEDTIME_ADJ                  0
#  define ASN1_F_ASN1_GENERATE_V3                          0
#  define ASN1_F_ASN1_GET_INT64                            0
#  define ASN1_F_ASN1_GET_OBJECT                           0
#  define ASN1_F_ASN1_GET_UINT64                           0
#  define ASN1_F_ASN1_I2D_BIO                              0
#  define ASN1_F_ASN1_I2D_FP                               0
#  define ASN1_F_ASN1_ITEM_D2I_FP                          0
#  define ASN1_F_ASN1_ITEM_DUP                             0
#  define ASN1_F_ASN1_ITEM_EMBED_D2I                       0
#  define ASN1_F_ASN1_ITEM_EMBED_NEW                       0
#  define ASN1_F_ASN1_ITEM_FLAGS_I2D                       0
#  define ASN1_F_ASN1_ITEM_I2D_BIO                         0
#  define ASN1_F_ASN1_ITEM_I2D_FP                          0
#  define ASN1_F_ASN1_ITEM_PACK                            0
#  define ASN1_F_ASN1_ITEM_SIGN                            0
#  define ASN1_F_ASN1_ITEM_SIGN_CTX                        0
#  define ASN1_F_ASN1_ITEM_UNPACK                          0
#  define ASN1_F_ASN1_ITEM_VERIFY                          0
#  define ASN1_F_ASN1_MBSTRING_NCOPY                       0
#  define ASN1_F_ASN1_OBJECT_NEW                           0
#  define ASN1_F_ASN1_OUTPUT_DATA                          0
#  define ASN1_F_ASN1_PCTX_NEW                             0
#  define ASN1_F_ASN1_PRIMITIVE_NEW                        0
#  define ASN1_F_ASN1_SCTX_NEW                             0
#  define ASN1_F_ASN1_SIGN                                 0
#  define ASN1_F_ASN1_STR2TYPE                             0
#  define ASN1_F_ASN1_STRING_GET_INT64                     0
#  define ASN1_F_ASN1_STRING_GET_UINT64                    0
#  define ASN1_F_ASN1_STRING_SET                           0
#  define ASN1_F_ASN1_STRING_TABLE_ADD                     0
#  define ASN1_F_ASN1_STRING_TO_BN                         0
#  define ASN1_F_ASN1_STRING_TYPE_NEW                      0
#  define ASN1_F_ASN1_TEMPLATE_EX_D2I                      0
#  define ASN1_F_ASN1_TEMPLATE_NEW                         0
#  define ASN1_F_ASN1_TEMPLATE_NOEXP_D2I                   0
#  define ASN1_F_ASN1_TIME_ADJ                             0
#  define ASN1_F_ASN1_TYPE_GET_INT_OCTETSTRING             0
#  define ASN1_F_ASN1_TYPE_GET_OCTETSTRING                 0
#  define ASN1_F_ASN1_UTCTIME_ADJ                          0
#  define ASN1_F_ASN1_VERIFY                               0
#  define ASN1_F_B64_READ_ASN1                             0
#  define ASN1_F_B64_WRITE_ASN1                            0
#  define ASN1_F_BIO_NEW_NDEF                              0
#  define ASN1_F_BITSTR_CB                                 0
#  define ASN1_F_BN_TO_ASN1_STRING                         0
#  define ASN1_F_C2I_ASN1_BIT_STRING                       0
#  define ASN1_F_C2I_ASN1_INTEGER                          0
#  define ASN1_F_C2I_ASN1_OBJECT                           0
#  define ASN1_F_C2I_IBUF                                  0
#  define ASN1_F_C2I_UINT64_INT                            0
#  define ASN1_F_COLLECT_DATA                              0
#  define ASN1_F_D2I_ASN1_OBJECT                           0
#  define ASN1_F_D2I_ASN1_UINTEGER                         0
#  define ASN1_F_D2I_AUTOPRIVATEKEY                        0
#  define ASN1_F_D2I_PRIVATEKEY                            0
#  define ASN1_F_D2I_PUBLICKEY                             0
#  define ASN1_F_DO_BUF                                    0
#  define ASN1_F_DO_CREATE                                 0
#  define ASN1_F_DO_DUMP                                   0
#  define ASN1_F_DO_TCREATE                                0
#  define ASN1_F_I2A_ASN1_OBJECT                           0
#  define ASN1_F_I2D_ASN1_BIO_STREAM                       0
#  define ASN1_F_I2D_ASN1_OBJECT                           0
#  define ASN1_F_I2D_DSA_PUBKEY                            0
#  define ASN1_F_I2D_EC_PUBKEY                             0
#  define ASN1_F_I2D_PRIVATEKEY                            0
#  define ASN1_F_I2D_PUBLICKEY                             0
#  define ASN1_F_I2D_RSA_PUBKEY                            0
#  define ASN1_F_LONG_C2I                                  0
#  define ASN1_F_NDEF_PREFIX                               0
#  define ASN1_F_NDEF_SUFFIX                               0
#  define ASN1_F_OID_MODULE_INIT                           0
#  define ASN1_F_PARSE_TAGGING                             0
#  define ASN1_F_PKCS5_PBE2_SET_IV                         0
#  define ASN1_F_PKCS5_PBE2_SET_SCRYPT                     0
#  define ASN1_F_PKCS5_PBE_SET                             0
#  define ASN1_F_PKCS5_PBE_SET0_ALGOR                      0
#  define ASN1_F_PKCS5_PBKDF2_SET                          0
#  define ASN1_F_PKCS5_SCRYPT_SET                          0
#  define ASN1_F_SMIME_READ_ASN1                           0
#  define ASN1_F_SMIME_TEXT                                0
#  define ASN1_F_STABLE_GET                                0
#  define ASN1_F_STBL_MODULE_INIT                          0
#  define ASN1_F_UINT32_C2I                                0
#  define ASN1_F_UINT32_NEW                                0
#  define ASN1_F_UINT64_C2I                                0
#  define ASN1_F_UINT64_NEW                                0
#  define ASN1_F_X509_CRL_ADD0_REVOKED                     0
#  define ASN1_F_X509_INFO_NEW                             0
#  define ASN1_F_X509_NAME_ENCODE                          0
#  define ASN1_F_X509_NAME_EX_D2I                          0
#  define ASN1_F_X509_NAME_EX_NEW                          0
#  define ASN1_F_X509_PKEY_NEW                             0

/*
 * ASYNC function codes.
 */
#  define ASYNC_F_ASYNC_CTX_NEW                            0
#  define ASYNC_F_ASYNC_INIT_THREAD                        0
#  define ASYNC_F_ASYNC_JOB_NEW                            0
#  define ASYNC_F_ASYNC_PAUSE_JOB                          0
#  define ASYNC_F_ASYNC_START_FUNC                         0
#  define ASYNC_F_ASYNC_START_JOB                          0
#  define ASYNC_F_ASYNC_WAIT_CTX_SET_WAIT_FD               0

/*
 * BIO function codes.
 */
#  define BIO_F_ACPT_STATE                                 0
#  define BIO_F_ADDRINFO_WRAP                              0
#  define BIO_F_ADDR_STRINGS                               0
#  define BIO_F_BIO_ACCEPT                                 0
#  define BIO_F_BIO_ACCEPT_EX                              0
#  define BIO_F_BIO_ACCEPT_NEW                             0
#  define BIO_F_BIO_ADDR_NEW                               0
#  define BIO_F_BIO_BIND                                   0
#  define BIO_F_BIO_CALLBACK_CTRL                          0
#  define BIO_F_BIO_CONNECT                                0
#  define BIO_F_BIO_CONNECT_NEW                            0
#  define BIO_F_BIO_CTRL                                   0
#  define BIO_F_BIO_GETS                                   0
#  define BIO_F_BIO_GET_HOST_IP                            0
#  define BIO_F_BIO_GET_NEW_INDEX                          0
#  define BIO_F_BIO_GET_PORT                               0
#  define BIO_F_BIO_LISTEN                                 0
#  define BIO_F_BIO_LOOKUP                                 0
#  define BIO_F_BIO_LOOKUP_EX                              0
#  define BIO_F_BIO_MAKE_PAIR                              0
#  define BIO_F_BIO_METH_NEW                               0
#  define BIO_F_BIO_NEW                                    0
#  define BIO_F_BIO_NEW_DGRAM_SCTP                         0
#  define BIO_F_BIO_NEW_FILE                               0
#  define BIO_F_BIO_NEW_MEM_BUF                            0
#  define BIO_F_BIO_NREAD                                  0
#  define BIO_F_BIO_NREAD0                                 0
#  define BIO_F_BIO_NWRITE                                 0
#  define BIO_F_BIO_NWRITE0                                0
#  define BIO_F_BIO_PARSE_HOSTSERV                         0
#  define BIO_F_BIO_PUTS                                   0
#  define BIO_F_BIO_READ                                   0
#  define BIO_F_BIO_READ_EX                                0
#  define BIO_F_BIO_READ_INTERN                            0
#  define BIO_F_BIO_SOCKET                                 0
#  define BIO_F_BIO_SOCKET_NBIO                            0
#  define BIO_F_BIO_SOCK_INFO                              0
#  define BIO_F_BIO_SOCK_INIT                              0
#  define BIO_F_BIO_WRITE                                  0
#  define BIO_F_BIO_WRITE_EX                               0
#  define BIO_F_BIO_WRITE_INTERN                           0
#  define BIO_F_BUFFER_CTRL                                0
#  define BIO_F_CONN_CTRL                                  0
#  define BIO_F_CONN_STATE                                 0
#  define BIO_F_DGRAM_SCTP_NEW                             0
#  define BIO_F_DGRAM_SCTP_READ                            0
#  define BIO_F_DGRAM_SCTP_WRITE                           0
#  define BIO_F_DOAPR_OUTCH                                0
#  define BIO_F_FILE_CTRL                                  0
#  define BIO_F_FILE_READ                                  0
#  define BIO_F_LINEBUFFER_CTRL                            0
#  define BIO_F_LINEBUFFER_NEW                             0
#  define BIO_F_MEM_WRITE                                  0
#  define BIO_F_NBIOF_NEW                                  0
#  define BIO_F_SLG_WRITE                                  0
#  define BIO_F_SSL_NEW                                    0

/*
 * BN function codes.
 */
#  define BN_F_BNRAND                                      0
#  define BN_F_BNRAND_RANGE                                0
#  define BN_F_BN_BLINDING_CONVERT_EX                      0
#  define BN_F_BN_BLINDING_CREATE_PARAM                    0
#  define BN_F_BN_BLINDING_INVERT_EX                       0
#  define BN_F_BN_BLINDING_NEW                             0
#  define BN_F_BN_BLINDING_UPDATE                          0
#  define BN_F_BN_BN2DEC                                   0
#  define BN_F_BN_BN2HEX                                   0
#  define BN_F_BN_COMPUTE_WNAF                             0
#  define BN_F_BN_CTX_GET                                  0
#  define BN_F_BN_CTX_NEW                                  0
#  define BN_F_BN_CTX_START                                0
#  define BN_F_BN_DIV                                      0
#  define BN_F_BN_DIV_RECP                                 0
#  define BN_F_BN_EXP                                      0
#  define BN_F_BN_EXPAND_INTERNAL                          0
#  define BN_F_BN_GENCB_NEW                                0
#  define BN_F_BN_GENERATE_DSA_NONCE                       0
#  define BN_F_BN_GENERATE_PRIME_EX                        0
#  define BN_F_BN_GF2M_MOD                                 0
#  define BN_F_BN_GF2M_MOD_EXP                             0
#  define BN_F_BN_GF2M_MOD_MUL                             0
#  define BN_F_BN_GF2M_MOD_SOLVE_QUAD                      0
#  define BN_F_BN_GF2M_MOD_SOLVE_QUAD_ARR                  0
#  define BN_F_BN_GF2M_MOD_SQR                             0
#  define BN_F_BN_GF2M_MOD_SQRT                            0
#  define BN_F_BN_LSHIFT                                   0
#  define BN_F_BN_MOD_EXP2_MONT                            0
#  define BN_F_BN_MOD_EXP_MONT                             0
#  define BN_F_BN_MOD_EXP_MONT_CONSTTIME                   0
#  define BN_F_BN_MOD_EXP_MONT_WORD                        0
#  define BN_F_BN_MOD_EXP_RECP                             0
#  define BN_F_BN_MOD_EXP_SIMPLE                           0
#  define BN_F_BN_MOD_INVERSE                              0
#  define BN_F_BN_MOD_INVERSE_NO_BRANCH                    0
#  define BN_F_BN_MOD_LSHIFT_QUICK                         0
#  define BN_F_BN_MOD_SQRT                                 0
#  define BN_F_BN_MONT_CTX_NEW                             0
#  define BN_F_BN_MPI2BN                                   0
#  define BN_F_BN_NEW                                      0
#  define BN_F_BN_POOL_GET                                 0
#  define BN_F_BN_RAND                                     0
#  define BN_F_BN_RAND_RANGE                               0
#  define BN_F_BN_RECP_CTX_NEW                             0
#  define BN_F_BN_RSHIFT                                   0
#  define BN_F_BN_SET_WORDS                                0
#  define BN_F_BN_STACK_PUSH                               0
#  define BN_F_BN_USUB                                     0

/*
 * BUF function codes.
 */
#  define BUF_F_BUF_MEM_GROW                               0
#  define BUF_F_BUF_MEM_GROW_CLEAN                         0
#  define BUF_F_BUF_MEM_NEW                                0

#  ifndef OPENSSL_NO_CMS
/*
 * CMS function codes.
 */
#   define CMS_F_CHECK_CONTENT                              0
#   define CMS_F_CMS_ADD0_CERT                              0
#   define CMS_F_CMS_ADD0_RECIPIENT_KEY                     0
#   define CMS_F_CMS_ADD0_RECIPIENT_PASSWORD                0
#   define CMS_F_CMS_ADD1_RECEIPTREQUEST                    0
#   define CMS_F_CMS_ADD1_RECIPIENT_CERT                    0
#   define CMS_F_CMS_ADD1_SIGNER                            0
#   define CMS_F_CMS_ADD1_SIGNINGTIME                       0
#   define CMS_F_CMS_COMPRESS                               0
#   define CMS_F_CMS_COMPRESSEDDATA_CREATE                  0
#   define CMS_F_CMS_COMPRESSEDDATA_INIT_BIO                0
#   define CMS_F_CMS_COPY_CONTENT                           0
#   define CMS_F_CMS_COPY_MESSAGEDIGEST                     0
#   define CMS_F_CMS_DATA                                   0
#   define CMS_F_CMS_DATAFINAL                              0
#   define CMS_F_CMS_DATAINIT                               0
#   define CMS_F_CMS_DECRYPT                                0
#   define CMS_F_CMS_DECRYPT_SET1_KEY                       0
#   define CMS_F_CMS_DECRYPT_SET1_PASSWORD                  0
#   define CMS_F_CMS_DECRYPT_SET1_PKEY                      0
#   define CMS_F_CMS_DIGESTALGORITHM_FIND_CTX               0
#   define CMS_F_CMS_DIGESTALGORITHM_INIT_BIO               0
#   define CMS_F_CMS_DIGESTEDDATA_DO_FINAL                  0
#   define CMS_F_CMS_DIGEST_VERIFY                          0
#   define CMS_F_CMS_ENCODE_RECEIPT                         0
#   define CMS_F_CMS_ENCRYPT                                0
#   define CMS_F_CMS_ENCRYPTEDCONTENT_INIT                  0
#   define CMS_F_CMS_ENCRYPTEDCONTENT_INIT_BIO              0
#   define CMS_F_CMS_ENCRYPTEDDATA_DECRYPT                  0
#   define CMS_F_CMS_ENCRYPTEDDATA_ENCRYPT                  0
#   define CMS_F_CMS_ENCRYPTEDDATA_SET1_KEY                 0
#   define CMS_F_CMS_ENVELOPEDDATA_CREATE                   0
#   define CMS_F_CMS_ENVELOPEDDATA_INIT_BIO                 0
#   define CMS_F_CMS_ENVELOPED_DATA_INIT                    0
#   define CMS_F_CMS_ENV_ASN1_CTRL                          0
#   define CMS_F_CMS_FINAL                                  0
#   define CMS_F_CMS_GET0_CERTIFICATE_CHOICES               0
#   define CMS_F_CMS_GET0_CONTENT                           0
#   define CMS_F_CMS_GET0_ECONTENT_TYPE                     0
#   define CMS_F_CMS_GET0_ENVELOPED                         0
#   define CMS_F_CMS_GET0_REVOCATION_CHOICES                0
#   define CMS_F_CMS_GET0_SIGNED                            0
#   define CMS_F_CMS_MSGSIGDIGEST_ADD1                      0
#   define CMS_F_CMS_RECEIPTREQUEST_CREATE0                 0
#   define CMS_F_CMS_RECEIPT_VERIFY                         0
#   define CMS_F_CMS_RECIPIENTINFO_DECRYPT                  0
#   define CMS_F_CMS_RECIPIENTINFO_ENCRYPT                  0
#   define CMS_F_CMS_RECIPIENTINFO_KARI_ENCRYPT             0
#   define CMS_F_CMS_RECIPIENTINFO_KARI_GET0_ALG            0
#   define CMS_F_CMS_RECIPIENTINFO_KARI_GET0_ORIG_ID        0
#   define CMS_F_CMS_RECIPIENTINFO_KARI_GET0_REKS           0
#   define CMS_F_CMS_RECIPIENTINFO_KARI_ORIG_ID_CMP         0
#   define CMS_F_CMS_RECIPIENTINFO_KEKRI_DECRYPT            0
#   define CMS_F_CMS_RECIPIENTINFO_KEKRI_ENCRYPT            0
#   define CMS_F_CMS_RECIPIENTINFO_KEKRI_GET0_ID            0
#   define CMS_F_CMS_RECIPIENTINFO_KEKRI_ID_CMP             0
#   define CMS_F_CMS_RECIPIENTINFO_KTRI_CERT_CMP            0
#   define CMS_F_CMS_RECIPIENTINFO_KTRI_DECRYPT             0
#   define CMS_F_CMS_RECIPIENTINFO_KTRI_ENCRYPT             0
#   define CMS_F_CMS_RECIPIENTINFO_KTRI_GET0_ALGS           0
#   define CMS_F_CMS_RECIPIENTINFO_KTRI_GET0_SIGNER_ID      0
#   define CMS_F_CMS_RECIPIENTINFO_PWRI_CRYPT               0
#   define CMS_F_CMS_RECIPIENTINFO_SET0_KEY                 0
#   define CMS_F_CMS_RECIPIENTINFO_SET0_PASSWORD            0
#   define CMS_F_CMS_RECIPIENTINFO_SET0_PKEY                0
#   define CMS_F_CMS_SD_ASN1_CTRL                           0
#   define CMS_F_CMS_SET1_IAS                               0
#   define CMS_F_CMS_SET1_KEYID                             0
#   define CMS_F_CMS_SET1_SIGNERIDENTIFIER                  0
#   define CMS_F_CMS_SET_DETACHED                           0
#   define CMS_F_CMS_SIGN                                   0
#   define CMS_F_CMS_SIGNED_DATA_INIT                       0
#   define CMS_F_CMS_SIGNERINFO_CONTENT_SIGN                0
#   define CMS_F_CMS_SIGNERINFO_SIGN                        0
#   define CMS_F_CMS_SIGNERINFO_VERIFY                      0
#   define CMS_F_CMS_SIGNERINFO_VERIFY_CERT                 0
#   define CMS_F_CMS_SIGNERINFO_VERIFY_CONTENT              0
#   define CMS_F_CMS_SIGN_RECEIPT                           0
#   define CMS_F_CMS_SI_CHECK_ATTRIBUTES                    0
#   define CMS_F_CMS_STREAM                                 0
#   define CMS_F_CMS_UNCOMPRESS                             0
#   define CMS_F_CMS_VERIFY                                 0
#   define CMS_F_KEK_UNWRAP_KEY                             0
#  endif

#  ifndef OPENSSL_NO_COMP
/*
 * COMP function codes.
 */
#   define COMP_F_BIO_ZLIB_FLUSH                            0
#   define COMP_F_BIO_ZLIB_NEW                              0
#   define COMP_F_BIO_ZLIB_READ                             0
#   define COMP_F_BIO_ZLIB_WRITE                            0
#   define COMP_F_COMP_CTX_NEW                              0
#  endif

/*
 * CONF function codes.
 */
#  define CONF_F_CONF_DUMP_FP                              0
#  define CONF_F_CONF_LOAD                                 0
#  define CONF_F_CONF_LOAD_FP                              0
#  define CONF_F_CONF_PARSE_LIST                           0
#  define CONF_F_DEF_LOAD                                  0
#  define CONF_F_DEF_LOAD_BIO                              0
#  define CONF_F_GET_NEXT_FILE                             0
#  define CONF_F_MODULE_ADD                                0
#  define CONF_F_MODULE_INIT                               0
#  define CONF_F_MODULE_LOAD_DSO                           0
#  define CONF_F_MODULE_RUN                                0
#  define CONF_F_NCONF_DUMP_BIO                            0
#  define CONF_F_NCONF_DUMP_FP                             0
#  define CONF_F_NCONF_GET_NUMBER_E                        0
#  define CONF_F_NCONF_GET_SECTION                         0
#  define CONF_F_NCONF_GET_STRING                          0
#  define CONF_F_NCONF_LOAD                                0
#  define CONF_F_NCONF_LOAD_BIO                            0
#  define CONF_F_NCONF_LOAD_FP                             0
#  define CONF_F_NCONF_NEW                                 0
#  define CONF_F_PROCESS_INCLUDE                           0
#  define CONF_F_SSL_MODULE_INIT                           0
#  define CONF_F_STR_COPY                                  0

/*
 * CRYPTO function codes.
 */
#  define CRYPTO_F_CMAC_CTX_NEW                            0
#  define CRYPTO_F_CRYPTO_DUP_EX_DATA                      0
#  define CRYPTO_F_CRYPTO_FREE_EX_DATA                     0
#  define CRYPTO_F_CRYPTO_GET_EX_NEW_INDEX                 0
#  define CRYPTO_F_CRYPTO_MEMDUP                           0
#  define CRYPTO_F_CRYPTO_NEW_EX_DATA                      0
#  define CRYPTO_F_CRYPTO_OCB128_COPY_CTX                  0
#  define CRYPTO_F_CRYPTO_OCB128_INIT                      0
#  define CRYPTO_F_CRYPTO_SET_EX_DATA                      0
#  define CRYPTO_F_GET_AND_LOCK                            0
#  define CRYPTO_F_OPENSSL_ATEXIT                          0
#  define CRYPTO_F_OPENSSL_BUF2HEXSTR                      0
#  define CRYPTO_F_OPENSSL_FOPEN                           0
#  define CRYPTO_F_OPENSSL_HEXSTR2BUF                      0
#  define CRYPTO_F_OPENSSL_INIT_CRYPTO                     0
#  define CRYPTO_F_OPENSSL_LH_NEW                          0
#  define CRYPTO_F_OPENSSL_SK_DEEP_COPY                    0
#  define CRYPTO_F_OPENSSL_SK_DUP                          0
#  define CRYPTO_F_PKEY_HMAC_INIT                          0
#  define CRYPTO_F_PKEY_POLY1305_INIT                      0
#  define CRYPTO_F_PKEY_SIPHASH_INIT                       0
#  define CRYPTO_F_SK_RESERVE                              0

#  ifndef OPENSSL_NO_CT
/*
 * CT function codes.
 */
#   define CT_F_CTLOG_NEW                                   0
#   define CT_F_CTLOG_NEW_FROM_BASE64                       0
#   define CT_F_CTLOG_NEW_FROM_CONF                         0
#   define CT_F_CTLOG_STORE_LOAD_CTX_NEW                    0
#   define CT_F_CTLOG_STORE_LOAD_FILE                       0
#   define CT_F_CTLOG_STORE_LOAD_LOG                        0
#   define CT_F_CTLOG_STORE_NEW                             0
#   define CT_F_CT_BASE64_DECODE                            0
#   define CT_F_CT_POLICY_EVAL_CTX_NEW                      0
#   define CT_F_CT_V1_LOG_ID_FROM_PKEY                      0
#   define CT_F_I2O_SCT                                     0
#   define CT_F_I2O_SCT_LIST                                0
#   define CT_F_I2O_SCT_SIGNATURE                           0
#   define CT_F_O2I_SCT                                     0
#   define CT_F_O2I_SCT_LIST                                0
#   define CT_F_O2I_SCT_SIGNATURE                           0
#   define CT_F_SCT_CTX_NEW                                 0
#   define CT_F_SCT_CTX_VERIFY                              0
#   define CT_F_SCT_NEW                                     0
#   define CT_F_SCT_NEW_FROM_BASE64                         0
#   define CT_F_SCT_SET0_LOG_ID                             0
#   define CT_F_SCT_SET1_EXTENSIONS                         0
#   define CT_F_SCT_SET1_LOG_ID                             0
#   define CT_F_SCT_SET1_SIGNATURE                          0
#   define CT_F_SCT_SET_LOG_ENTRY_TYPE                      0
#   define CT_F_SCT_SET_SIGNATURE_NID                       0
#   define CT_F_SCT_SET_VERSION                             0
#  endif

#  ifndef OPENSSL_NO_DH
/*
 * DH function codes.
 */
#   define DH_F_COMPUTE_KEY                                 0
#   define DH_F_DHPARAMS_PRINT_FP                           0
#   define DH_F_DH_BUILTIN_GENPARAMS                        0
#   define DH_F_DH_CHECK_EX                                 0
#   define DH_F_DH_CHECK_PARAMS_EX                          0
#   define DH_F_DH_CHECK_PUB_KEY_EX                         0
#   define DH_F_DH_CMS_DECRYPT                              0
#   define DH_F_DH_CMS_SET_PEERKEY                          0
#   define DH_F_DH_CMS_SET_SHARED_INFO                      0
#   define DH_F_DH_METH_DUP                                 0
#   define DH_F_DH_METH_NEW                                 0
#   define DH_F_DH_METH_SET1_NAME                           0
#   define DH_F_DH_NEW_BY_NID                               0
#   define DH_F_DH_NEW_METHOD                               0
#   define DH_F_DH_PARAM_DECODE                             0
#   define DH_F_DH_PKEY_PUBLIC_CHECK                        0
#   define DH_F_DH_PRIV_DECODE                              0
#   define DH_F_DH_PRIV_ENCODE                              0
#   define DH_F_DH_PUB_DECODE                               0
#   define DH_F_DH_PUB_ENCODE                               0
#   define DH_F_DO_DH_PRINT                                 0
#   define DH_F_GENERATE_KEY                                0
#   define DH_F_PKEY_DH_CTRL_STR                            0
#   define DH_F_PKEY_DH_DERIVE                              0
#   define DH_F_PKEY_DH_INIT                                0
#   define DH_F_PKEY_DH_KEYGEN                              0
#  endif

#  ifndef OPENSSL_NO_DSA
/*
 * DSA function codes.
 */
#   define DSA_F_DSAPARAMS_PRINT                            0
#   define DSA_F_DSAPARAMS_PRINT_FP                         0
#   define DSA_F_DSA_BUILTIN_PARAMGEN                       0
#   define DSA_F_DSA_BUILTIN_PARAMGEN2                      0
#   define DSA_F_DSA_DO_SIGN                                0
#   define DSA_F_DSA_DO_VERIFY                              0
#   define DSA_F_DSA_METH_DUP                               0
#   define DSA_F_DSA_METH_NEW                               0
#   define DSA_F_DSA_METH_SET1_NAME                         0
#   define DSA_F_DSA_NEW_METHOD                             0
#   define DSA_F_DSA_PARAM_DECODE                           0
#   define DSA_F_DSA_PRINT_FP                               0
#   define DSA_F_DSA_PRIV_DECODE                            0
#   define DSA_F_DSA_PRIV_ENCODE                            0
#   define DSA_F_DSA_PUB_DECODE                             0
#   define DSA_F_DSA_PUB_ENCODE                             0
#   define DSA_F_DSA_SIGN                                   0
#   define DSA_F_DSA_SIGN_SETUP                             0
#   define DSA_F_DSA_SIG_NEW                                0
#   define DSA_F_OLD_DSA_PRIV_DECODE                        0
#   define DSA_F_PKEY_DSA_CTRL                              0
#   define DSA_F_PKEY_DSA_CTRL_STR                          0
#   define DSA_F_PKEY_DSA_KEYGEN                            0
#  endif

#  ifndef OPENSSL_NO_EC
/*
 * EC function codes.
 */
#   define EC_F_BN_TO_FELEM                                 0
#   define EC_F_D2I_ECPARAMETERS                            0
#   define EC_F_D2I_ECPKPARAMETERS                          0
#   define EC_F_D2I_ECPRIVATEKEY                            0
#   define EC_F_DO_EC_KEY_PRINT                             0
#   define EC_F_ECDH_CMS_DECRYPT                            0
#   define EC_F_ECDH_CMS_SET_SHARED_INFO                    0
#   define EC_F_ECDH_COMPUTE_KEY                            0
#   define EC_F_ECDH_SIMPLE_COMPUTE_KEY                     0
#   define EC_F_ECDSA_DO_SIGN_EX                            0
#   define EC_F_ECDSA_DO_VERIFY                             0
#   define EC_F_ECDSA_SIGN_EX                               0
#   define EC_F_ECDSA_SIGN_SETUP                            0
#   define EC_F_ECDSA_SIG_NEW                               0
#   define EC_F_ECDSA_VERIFY                                0
#   define EC_F_ECD_ITEM_VERIFY                             0
#   define EC_F_ECKEY_PARAM2TYPE                            0
#   define EC_F_ECKEY_PARAM_DECODE                          0
#   define EC_F_ECKEY_PRIV_DECODE                           0
#   define EC_F_ECKEY_PRIV_ENCODE                           0
#   define EC_F_ECKEY_PUB_DECODE                            0
#   define EC_F_ECKEY_PUB_ENCODE                            0
#   define EC_F_ECKEY_TYPE2PARAM                            0
#   define EC_F_ECPARAMETERS_PRINT                          0
#   define EC_F_ECPARAMETERS_PRINT_FP                       0
#   define EC_F_ECPKPARAMETERS_PRINT                        0
#   define EC_F_ECPKPARAMETERS_PRINT_FP                     0
#   define EC_F_ECP_NISTZ256_GET_AFFINE                     0
#   define EC_F_ECP_NISTZ256_INV_MOD_ORD                    0
#   define EC_F_ECP_NISTZ256_MULT_PRECOMPUTE                0
#   define EC_F_ECP_NISTZ256_POINTS_MUL                     0
#   define EC_F_ECP_NISTZ256_PRE_COMP_NEW                   0
#   define EC_F_ECP_NISTZ256_WINDOWED_MUL                   0
#   define EC_F_ECX_KEY_OP                                  0
#   define EC_F_ECX_PRIV_ENCODE                             0
#   define EC_F_ECX_PUB_ENCODE                              0
#   define EC_F_EC_ASN1_GROUP2CURVE                         0
#   define EC_F_EC_ASN1_GROUP2FIELDID                       0
#   define EC_F_EC_GF2M_MONTGOMERY_POINT_MULTIPLY           0
#   define EC_F_EC_GF2M_SIMPLE_FIELD_INV                    0
#   define EC_F_EC_GF2M_SIMPLE_GROUP_CHECK_DISCRIMINANT     0
#   define EC_F_EC_GF2M_SIMPLE_GROUP_SET_CURVE              0
#   define EC_F_EC_GF2M_SIMPLE_LADDER_POST                  0
#   define EC_F_EC_GF2M_SIMPLE_LADDER_PRE                   0
#   define EC_F_EC_GF2M_SIMPLE_OCT2POINT                    0
#   define EC_F_EC_GF2M_SIMPLE_POINT2OCT                    0
#   define EC_F_EC_GF2M_SIMPLE_POINTS_MUL                   0
#   define EC_F_EC_GF2M_SIMPLE_POINT_GET_AFFINE_COORDINATES 0
#   define EC_F_EC_GF2M_SIMPLE_POINT_SET_AFFINE_COORDINATES 0
#   define EC_F_EC_GF2M_SIMPLE_SET_COMPRESSED_COORDINATES   0
#   define EC_F_EC_GFP_MONT_FIELD_DECODE                    0
#   define EC_F_EC_GFP_MONT_FIELD_ENCODE                    0
#   define EC_F_EC_GFP_MONT_FIELD_INV                       0
#   define EC_F_EC_GFP_MONT_FIELD_MUL                       0
#   define EC_F_EC_GFP_MONT_FIELD_SET_TO_ONE                0
#   define EC_F_EC_GFP_MONT_FIELD_SQR                       0
#   define EC_F_EC_GFP_MONT_GROUP_SET_CURVE                 0
#   define EC_F_EC_GFP_NISTP224_GROUP_SET_CURVE             0
#   define EC_F_EC_GFP_NISTP224_POINTS_MUL                  0
#   define EC_F_EC_GFP_NISTP224_POINT_GET_AFFINE_COORDINATES 0
#   define EC_F_EC_GFP_NISTP256_GROUP_SET_CURVE             0
#   define EC_F_EC_GFP_NISTP256_POINTS_MUL                  0
#   define EC_F_EC_GFP_NISTP256_POINT_GET_AFFINE_COORDINATES 0
#   define EC_F_EC_GFP_NISTP521_GROUP_SET_CURVE             0
#   define EC_F_EC_GFP_NISTP521_POINTS_MUL                  0
#   define EC_F_EC_GFP_NISTP521_POINT_GET_AFFINE_COORDINATES 0
#   define EC_F_EC_GFP_NIST_FIELD_MUL                       0
#   define EC_F_EC_GFP_NIST_FIELD_SQR                       0
#   define EC_F_EC_GFP_NIST_GROUP_SET_CURVE                 0
#   define EC_F_EC_GFP_SIMPLE_BLIND_COORDINATES             0
#   define EC_F_EC_GFP_SIMPLE_FIELD_INV                     0
#   define EC_F_EC_GFP_SIMPLE_GROUP_CHECK_DISCRIMINANT      0
#   define EC_F_EC_GFP_SIMPLE_GROUP_SET_CURVE               0
#   define EC_F_EC_GFP_SIMPLE_MAKE_AFFINE                   0
#   define EC_F_EC_GFP_SIMPLE_OCT2POINT                     0
#   define EC_F_EC_GFP_SIMPLE_POINT2OCT                     0
#   define EC_F_EC_GFP_SIMPLE_POINTS_MAKE_AFFINE            0
#   define EC_F_EC_GFP_SIMPLE_POINT_GET_AFFINE_COORDINATES  0
#   define EC_F_EC_GFP_SIMPLE_POINT_SET_AFFINE_COORDINATES  0
#   define EC_F_EC_GFP_SIMPLE_SET_COMPRESSED_COORDINATES    0
#   define EC_F_EC_GROUP_CHECK                              0
#   define EC_F_EC_GROUP_CHECK_DISCRIMINANT                 0
#   define EC_F_EC_GROUP_COPY                               0
#   define EC_F_EC_GROUP_GET_CURVE                          0
#   define EC_F_EC_GROUP_GET_CURVE_GF2M                     0
#   define EC_F_EC_GROUP_GET_CURVE_GFP                      0
#   define EC_F_EC_GROUP_GET_DEGREE                         0
#   define EC_F_EC_GROUP_GET_ECPARAMETERS                   0
#   define EC_F_EC_GROUP_GET_ECPKPARAMETERS                 0
#   define EC_F_EC_GROUP_GET_PENTANOMIAL_BASIS              0
#   define EC_F_EC_GROUP_GET_TRINOMIAL_BASIS                0
#   define EC_F_EC_GROUP_NEW                                0
#   define EC_F_EC_GROUP_NEW_BY_CURVE_NAME                  0
#   define EC_F_EC_GROUP_NEW_FROM_DATA                      0
#   define EC_F_EC_GROUP_NEW_FROM_ECPARAMETERS              0
#   define EC_F_EC_GROUP_NEW_FROM_ECPKPARAMETERS            0
#   define EC_F_EC_GROUP_SET_CURVE                          0
#   define EC_F_EC_GROUP_SET_CURVE_GF2M                     0
#   define EC_F_EC_GROUP_SET_CURVE_GFP                      0
#   define EC_F_EC_GROUP_SET_GENERATOR                      0
#   define EC_F_EC_GROUP_SET_SEED                           0
#   define EC_F_EC_KEY_CHECK_KEY                            0
#   define EC_F_EC_KEY_COPY                                 0
#   define EC_F_EC_KEY_GENERATE_KEY                         0
#   define EC_F_EC_KEY_NEW                                  0
#   define EC_F_EC_KEY_NEW_METHOD                           0
#   define EC_F_EC_KEY_OCT2PRIV                             0
#   define EC_F_EC_KEY_PRINT                                0
#   define EC_F_EC_KEY_PRINT_FP                             0
#   define EC_F_EC_KEY_PRIV2BUF                             0
#   define EC_F_EC_KEY_PRIV2OCT                             0
#   define EC_F_EC_KEY_SET_PUBLIC_KEY_AFFINE_COORDINATES    0
#   define EC_F_EC_KEY_SIMPLE_CHECK_KEY                     0
#   define EC_F_EC_KEY_SIMPLE_OCT2PRIV                      0
#   define EC_F_EC_KEY_SIMPLE_PRIV2OCT                      0
#   define EC_F_EC_PKEY_CHECK                               0
#   define EC_F_EC_PKEY_PARAM_CHECK                         0
#   define EC_F_EC_POINTS_MAKE_AFFINE                       0
#   define EC_F_EC_POINTS_MUL                               0
#   define EC_F_EC_POINT_ADD                                0
#   define EC_F_EC_POINT_BN2POINT                           0
#   define EC_F_EC_POINT_CMP                                0
#   define EC_F_EC_POINT_COPY                               0
#   define EC_F_EC_POINT_DBL                                0
#   define EC_F_EC_POINT_GET_AFFINE_COORDINATES             0
#   define EC_F_EC_POINT_GET_AFFINE_COORDINATES_GF2M        0
#   define EC_F_EC_POINT_GET_AFFINE_COORDINATES_GFP         0
#   define EC_F_EC_POINT_GET_JPROJECTIVE_COORDINATES_GFP    0
#   define EC_F_EC_POINT_INVERT                             0
#   define EC_F_EC_POINT_IS_AT_INFINITY                     0
#   define EC_F_EC_POINT_IS_ON_CURVE                        0
#   define EC_F_EC_POINT_MAKE_AFFINE                        0
#   define EC_F_EC_POINT_NEW                                0
#   define EC_F_EC_POINT_OCT2POINT                          0
#   define EC_F_EC_POINT_POINT2BUF                          0
#   define EC_F_EC_POINT_POINT2OCT                          0
#   define EC_F_EC_POINT_SET_AFFINE_COORDINATES             0
#   define EC_F_EC_POINT_SET_AFFINE_COORDINATES_GF2M        0
#   define EC_F_EC_POINT_SET_AFFINE_COORDINATES_GFP         0
#   define EC_F_EC_POINT_SET_COMPRESSED_COORDINATES         0
#   define EC_F_EC_POINT_SET_COMPRESSED_COORDINATES_GF2M    0
#   define EC_F_EC_POINT_SET_COMPRESSED_COORDINATES_GFP     0
#   define EC_F_EC_POINT_SET_JPROJECTIVE_COORDINATES_GFP    0
#   define EC_F_EC_POINT_SET_TO_INFINITY                    0
#   define EC_F_EC_PRE_COMP_NEW                             0
#   define EC_F_EC_SCALAR_MUL_LADDER                        0
#   define EC_F_EC_WNAF_MUL                                 0
#   define EC_F_EC_WNAF_PRECOMPUTE_MULT                     0
#   define EC_F_I2D_ECPARAMETERS                            0
#   define EC_F_I2D_ECPKPARAMETERS                          0
#   define EC_F_I2D_ECPRIVATEKEY                            0
#   define EC_F_I2O_ECPUBLICKEY                             0
#   define EC_F_NISTP224_PRE_COMP_NEW                       0
#   define EC_F_NISTP256_PRE_COMP_NEW                       0
#   define EC_F_NISTP521_PRE_COMP_NEW                       0
#   define EC_F_O2I_ECPUBLICKEY                             0
#   define EC_F_OLD_EC_PRIV_DECODE                          0
#   define EC_F_OSSL_ECDH_COMPUTE_KEY                       0
#   define EC_F_OSSL_ECDSA_SIGN_SIG                         0
#   define EC_F_OSSL_ECDSA_VERIFY_SIG                       0
#   define EC_F_PKEY_ECD_CTRL                               0
#   define EC_F_PKEY_ECD_DIGESTSIGN                         0
#   define EC_F_PKEY_ECD_DIGESTSIGN25519                    0
#   define EC_F_PKEY_ECD_DIGESTSIGN448                      0
#   define EC_F_PKEY_ECX_DERIVE                             0
#   define EC_F_PKEY_EC_CTRL                                0
#   define EC_F_PKEY_EC_CTRL_STR                            0
#   define EC_F_PKEY_EC_DERIVE                              0
#   define EC_F_PKEY_EC_INIT                                0
#   define EC_F_PKEY_EC_KDF_DERIVE                          0
#   define EC_F_PKEY_EC_KEYGEN                              0
#   define EC_F_PKEY_EC_PARAMGEN                            0
#   define EC_F_PKEY_EC_SIGN                                0
#   define EC_F_VALIDATE_ECX_DERIVE                         0
#  endif

#  ifndef OPENSSL_NO_ENGINE
/*
 * ENGINE function codes.
 */
#   define ENGINE_F_DIGEST_UPDATE                           0
#   define ENGINE_F_DYNAMIC_CTRL                            0
#   define ENGINE_F_DYNAMIC_GET_DATA_CTX                    0
#   define ENGINE_F_DYNAMIC_LOAD                            0
#   define ENGINE_F_DYNAMIC_SET_DATA_CTX                    0
#   define ENGINE_F_ENGINE_ADD                              0
#   define ENGINE_F_ENGINE_BY_ID                            0
#   define ENGINE_F_ENGINE_CMD_IS_EXECUTABLE                0
#   define ENGINE_F_ENGINE_CTRL                             0
#   define ENGINE_F_ENGINE_CTRL_CMD                         0
#   define ENGINE_F_ENGINE_CTRL_CMD_STRING                  0
#   define ENGINE_F_ENGINE_FINISH                           0
#   define ENGINE_F_ENGINE_GET_CIPHER                       0
#   define ENGINE_F_ENGINE_GET_DIGEST                       0
#   define ENGINE_F_ENGINE_GET_FIRST                        0
#   define ENGINE_F_ENGINE_GET_LAST                         0
#   define ENGINE_F_ENGINE_GET_NEXT                         0
#   define ENGINE_F_ENGINE_GET_PKEY_ASN1_METH               0
#   define ENGINE_F_ENGINE_GET_PKEY_METH                    0
#   define ENGINE_F_ENGINE_GET_PREV                         0
#   define ENGINE_F_ENGINE_INIT                             0
#   define ENGINE_F_ENGINE_LIST_ADD                         0
#   define ENGINE_F_ENGINE_LIST_REMOVE                      0
#   define ENGINE_F_ENGINE_LOAD_PRIVATE_KEY                 0
#   define ENGINE_F_ENGINE_LOAD_PUBLIC_KEY                  0
#   define ENGINE_F_ENGINE_LOAD_SSL_CLIENT_CERT             0
#   define ENGINE_F_ENGINE_NEW                              0
#   define ENGINE_F_ENGINE_PKEY_ASN1_FIND_STR               0
#   define ENGINE_F_ENGINE_REMOVE                           0
#   define ENGINE_F_ENGINE_SET_DEFAULT_STRING               0
#   define ENGINE_F_ENGINE_SET_ID                           0
#   define ENGINE_F_ENGINE_SET_NAME                         0
#   define ENGINE_F_ENGINE_TABLE_REGISTER                   0
#   define ENGINE_F_ENGINE_UNLOCKED_FINISH                  0
#   define ENGINE_F_ENGINE_UP_REF                           0
#   define ENGINE_F_INT_CLEANUP_ITEM                        0
#   define ENGINE_F_INT_CTRL_HELPER                         0
#   define ENGINE_F_INT_ENGINE_CONFIGURE                    0
#   define ENGINE_F_INT_ENGINE_MODULE_INIT                  0
#   define ENGINE_F_OSSL_HMAC_INIT                          0
#  endif

/*
 * EVP function codes.
 */
#  define EVP_F_AESNI_INIT_KEY                             0
#  define EVP_F_AESNI_XTS_INIT_KEY                         0
#  define EVP_F_AES_GCM_CTRL                               0
#  define EVP_F_AES_INIT_KEY                               0
#  define EVP_F_AES_OCB_CIPHER                             0
#  define EVP_F_AES_T4_INIT_KEY                            0
#  define EVP_F_AES_T4_XTS_INIT_KEY                        0
#  define EVP_F_AES_WRAP_CIPHER                            0
#  define EVP_F_AES_XTS_INIT_KEY                           0
#  define EVP_F_ALG_MODULE_INIT                            0
#  define EVP_F_ARIA_CCM_INIT_KEY                          0
#  define EVP_F_ARIA_GCM_CTRL                              0
#  define EVP_F_ARIA_GCM_INIT_KEY                          0
#  define EVP_F_ARIA_INIT_KEY                              0
#  define EVP_F_B64_NEW                                    0
#  define EVP_F_CAMELLIA_INIT_KEY                          0
#  define EVP_F_CHACHA20_POLY1305_CTRL                     0
#  define EVP_F_CMLL_T4_INIT_KEY                           0
#  define EVP_F_DES_EDE3_WRAP_CIPHER                       0
#  define EVP_F_DO_SIGVER_INIT                             0
#  define EVP_F_ENC_NEW                                    0
#  define EVP_F_EVP_CIPHERINIT_EX                          0
#  define EVP_F_EVP_CIPHER_ASN1_TO_PARAM                   0
#  define EVP_F_EVP_CIPHER_CTX_COPY                        0
#  define EVP_F_EVP_CIPHER_CTX_CTRL                        0
#  define EVP_F_EVP_CIPHER_CTX_SET_KEY_LENGTH              0
#  define EVP_F_EVP_CIPHER_PARAM_TO_ASN1                   0
#  define EVP_F_EVP_DECRYPTFINAL_EX                        0
#  define EVP_F_EVP_DECRYPTUPDATE                          0
#  define EVP_F_EVP_DIGESTFINALXOF                         0
#  define EVP_F_EVP_DIGESTINIT_EX                          0
#  define EVP_F_EVP_ENCRYPTDECRYPTUPDATE                   0
#  define EVP_F_EVP_ENCRYPTFINAL_EX                        0
#  define EVP_F_EVP_ENCRYPTUPDATE                          0
#  define EVP_F_EVP_MD_CTX_COPY_EX                         0
#  define EVP_F_EVP_MD_SIZE                                0
#  define EVP_F_EVP_OPENINIT                               0
#  define EVP_F_EVP_PBE_ALG_ADD                            0
#  define EVP_F_EVP_PBE_ALG_ADD_TYPE                       0
#  define EVP_F_EVP_PBE_CIPHERINIT                         0
#  define EVP_F_EVP_PBE_SCRYPT                             0
#  define EVP_F_EVP_PKCS82PKEY                             0
#  define EVP_F_EVP_PKEY2PKCS8                             0
#  define EVP_F_EVP_PKEY_ASN1_ADD0                         0
#  define EVP_F_EVP_PKEY_CHECK                             0
#  define EVP_F_EVP_PKEY_COPY_PARAMETERS                   0
#  define EVP_F_EVP_PKEY_CTX_CTRL                          0
#  define EVP_F_EVP_PKEY_CTX_CTRL_STR                      0
#  define EVP_F_EVP_PKEY_CTX_DUP                           0
#  define EVP_F_EVP_PKEY_CTX_MD                            0
#  define EVP_F_EVP_PKEY_DECRYPT                           0
#  define EVP_F_EVP_PKEY_DECRYPT_INIT                      0
#  define EVP_F_EVP_PKEY_DECRYPT_OLD                       0
#  define EVP_F_EVP_PKEY_DERIVE                            0
#  define EVP_F_EVP_PKEY_DERIVE_INIT                       0
#  define EVP_F_EVP_PKEY_DERIVE_SET_PEER                   0
#  define EVP_F_EVP_PKEY_ENCRYPT                           0
#  define EVP_F_EVP_PKEY_ENCRYPT_INIT                      0
#  define EVP_F_EVP_PKEY_ENCRYPT_OLD                       0
#  define EVP_F_EVP_PKEY_GET0_DH                           0
#  define EVP_F_EVP_PKEY_GET0_DSA                          0
#  define EVP_F_EVP_PKEY_GET0_EC_KEY                       0
#  define EVP_F_EVP_PKEY_GET0_HMAC                         0
#  define EVP_F_EVP_PKEY_GET0_POLY1305                     0
#  define EVP_F_EVP_PKEY_GET0_RSA                          0
#  define EVP_F_EVP_PKEY_GET0_SIPHASH                      0
#  define EVP_F_EVP_PKEY_GET_RAW_PRIVATE_KEY               0
#  define EVP_F_EVP_PKEY_GET_RAW_PUBLIC_KEY                0
#  define EVP_F_EVP_PKEY_KEYGEN                            0
#  define EVP_F_EVP_PKEY_KEYGEN_INIT                       0
#  define EVP_F_EVP_PKEY_METH_ADD0                         0
#  define EVP_F_EVP_PKEY_METH_NEW                          0
#  define EVP_F_EVP_PKEY_NEW                               0
#  define EVP_F_EVP_PKEY_NEW_CMAC_KEY                      0
#  define EVP_F_EVP_PKEY_NEW_RAW_PRIVATE_KEY               0
#  define EVP_F_EVP_PKEY_NEW_RAW_PUBLIC_KEY                0
#  define EVP_F_EVP_PKEY_PARAMGEN                          0
#  define EVP_F_EVP_PKEY_PARAMGEN_INIT                     0
#  define EVP_F_EVP_PKEY_PARAM_CHECK                       0
#  define EVP_F_EVP_PKEY_PUBLIC_CHECK                      0
#  define EVP_F_EVP_PKEY_SET1_ENGINE                       0
#  define EVP_F_EVP_PKEY_SET_ALIAS_TYPE                    0
#  define EVP_F_EVP_PKEY_SIGN                              0
#  define EVP_F_EVP_PKEY_SIGN_INIT                         0
#  define EVP_F_EVP_PKEY_VERIFY                            0
#  define EVP_F_EVP_PKEY_VERIFY_INIT                       0
#  define EVP_F_EVP_PKEY_VERIFY_RECOVER                    0
#  define EVP_F_EVP_PKEY_VERIFY_RECOVER_INIT               0
#  define EVP_F_EVP_SIGNFINAL                              0
#  define EVP_F_EVP_VERIFYFINAL                            0
#  define EVP_F_INT_CTX_NEW                                0
#  define EVP_F_OK_NEW                                     0
#  define EVP_F_PKCS5_PBE_KEYIVGEN                         0
#  define EVP_F_PKCS5_V2_PBE_KEYIVGEN                      0
#  define EVP_F_PKCS5_V2_PBKDF2_KEYIVGEN                   0
#  define EVP_F_PKCS5_V2_SCRYPT_KEYIVGEN                   0
#  define EVP_F_PKEY_SET_TYPE                              0
#  define EVP_F_RC2_MAGIC_TO_METH                          0
#  define EVP_F_RC5_CTRL                                   0
#  define EVP_F_R_32_12_16_INIT_KEY                        0
#  define EVP_F_S390X_AES_GCM_CTRL                         0
#  define EVP_F_UPDATE                                     0

/*
 * KDF function codes.
 */
#  define KDF_F_PKEY_HKDF_CTRL_STR                         0
#  define KDF_F_PKEY_HKDF_DERIVE                           0
#  define KDF_F_PKEY_HKDF_INIT                             0
#  define KDF_F_PKEY_SCRYPT_CTRL_STR                       0
#  define KDF_F_PKEY_SCRYPT_CTRL_UINT64                    0
#  define KDF_F_PKEY_SCRYPT_DERIVE                         0
#  define KDF_F_PKEY_SCRYPT_INIT                           0
#  define KDF_F_PKEY_SCRYPT_SET_MEMBUF                     0
#  define KDF_F_PKEY_TLS1_PRF_CTRL_STR                     0
#  define KDF_F_PKEY_TLS1_PRF_DERIVE                       0
#  define KDF_F_PKEY_TLS1_PRF_INIT                         0
#  define KDF_F_TLS1_PRF_ALG                               0

/*
 * KDF reason codes.
 */
#  define KDF_R_INVALID_DIGEST                             0
#  define KDF_R_MISSING_ITERATION_COUNT                    0
#  define KDF_R_MISSING_KEY                                0
#  define KDF_R_MISSING_MESSAGE_DIGEST                     0
#  define KDF_R_MISSING_PARAMETER                          0
#  define KDF_R_MISSING_PASS                               0
#  define KDF_R_MISSING_SALT                               0
#  define KDF_R_MISSING_SECRET                             0
#  define KDF_R_MISSING_SEED                               0
#  define KDF_R_UNKNOWN_PARAMETER_TYPE                     0
#  define KDF_R_VALUE_ERROR                                0
#  define KDF_R_VALUE_MISSING                              0

/*
 * OBJ function codes.
 */
#  define OBJ_F_OBJ_ADD_OBJECT                             0
#  define OBJ_F_OBJ_ADD_SIGID                              0
#  define OBJ_F_OBJ_CREATE                                 0
#  define OBJ_F_OBJ_DUP                                    0
#  define OBJ_F_OBJ_NAME_NEW_INDEX                         0
#  define OBJ_F_OBJ_NID2LN                                 0
#  define OBJ_F_OBJ_NID2OBJ                                0
#  define OBJ_F_OBJ_NID2SN                                 0
#  define OBJ_F_OBJ_TXT2OBJ                                0

#  ifndef OPENSSL_NO_OCSP
/*
 * OCSP function codes.
 */
#   define OCSP_F_D2I_OCSP_NONCE                            0
#   define OCSP_F_OCSP_BASIC_ADD1_STATUS                    0
#   define OCSP_F_OCSP_BASIC_SIGN                           0
#   define OCSP_F_OCSP_BASIC_SIGN_CTX                       0
#   define OCSP_F_OCSP_BASIC_VERIFY                         0
#   define OCSP_F_OCSP_CERT_ID_NEW                          0
#   define OCSP_F_OCSP_CHECK_DELEGATED                      0
#   define OCSP_F_OCSP_CHECK_IDS                            0
#   define OCSP_F_OCSP_CHECK_ISSUER                         0
#   define OCSP_F_OCSP_CHECK_VALIDITY                       0
#   define OCSP_F_OCSP_MATCH_ISSUERID                       0
#   define OCSP_F_OCSP_PARSE_URL                            0
#   define OCSP_F_OCSP_REQUEST_SIGN                         0
#   define OCSP_F_OCSP_REQUEST_VERIFY                       0
#   define OCSP_F_OCSP_RESPONSE_GET1_BASIC                  0
#   define OCSP_F_PARSE_HTTP_LINE1                          0
#  endif

/*
 * PEM function codes.
 */
#  define PEM_F_B2I_DSS                                    0
#  define PEM_F_B2I_PVK_BIO                                0
#  define PEM_F_B2I_RSA                                    0
#  define PEM_F_CHECK_BITLEN_DSA                           0
#  define PEM_F_CHECK_BITLEN_RSA                           0
#  define PEM_F_D2I_PKCS8PRIVATEKEY_BIO                    0
#  define PEM_F_D2I_PKCS8PRIVATEKEY_FP                     0
#  define PEM_F_DO_B2I                                     0
#  define PEM_F_DO_B2I_BIO                                 0
#  define PEM_F_DO_BLOB_HEADER                             0
#  define PEM_F_DO_I2B                                     0
#  define PEM_F_DO_PK8PKEY                                 0
#  define PEM_F_DO_PK8PKEY_FP                              0
#  define PEM_F_DO_PVK_BODY                                0
#  define PEM_F_DO_PVK_HEADER                              0
#  define PEM_F_GET_HEADER_AND_DATA                        0
#  define PEM_F_GET_NAME                                   0
#  define PEM_F_I2B_PVK                                    0
#  define PEM_F_I2B_PVK_BIO                                0
#  define PEM_F_LOAD_IV                                    0
#  define PEM_F_PEM_ASN1_READ                              0
#  define PEM_F_PEM_ASN1_READ_BIO                          0
#  define PEM_F_PEM_ASN1_WRITE                             0
#  define PEM_F_PEM_ASN1_WRITE_BIO                         0
#  define PEM_F_PEM_DEF_CALLBACK                           0
#  define PEM_F_PEM_DO_HEADER                              0
#  define PEM_F_PEM_GET_EVP_CIPHER_INFO                    0
#  define PEM_F_PEM_READ                                   0
#  define PEM_F_PEM_READ_BIO                               0
#  define PEM_F_PEM_READ_BIO_DHPARAMS                      0
#  define PEM_F_PEM_READ_BIO_EX                            0
#  define PEM_F_PEM_READ_BIO_PARAMETERS                    0
#  define PEM_F_PEM_READ_BIO_PRIVATEKEY                    0
#  define PEM_F_PEM_READ_DHPARAMS                          0
#  define PEM_F_PEM_READ_PRIVATEKEY                        0
#  define PEM_F_PEM_SIGNFINAL                              0
#  define PEM_F_PEM_WRITE                                  0
#  define PEM_F_PEM_WRITE_BIO                              0
#  define PEM_F_PEM_WRITE_BIO_PRIVATEKEY_TRADITIONAL       0
#  define PEM_F_PEM_WRITE_PRIVATEKEY                       0
#  define PEM_F_PEM_X509_INFO_READ                         0
#  define PEM_F_PEM_X509_INFO_READ_BIO                     0
#  define PEM_F_PEM_X509_INFO_WRITE_BIO                    0

/*
 * PKCS12 function codes.
 */
#  define PKCS12_F_OPENSSL_ASC2UNI                         0
#  define PKCS12_F_OPENSSL_UNI2ASC                         0
#  define PKCS12_F_OPENSSL_UNI2UTF8                        0
#  define PKCS12_F_OPENSSL_UTF82UNI                        0
#  define PKCS12_F_PKCS12_CREATE                           0
#  define PKCS12_F_PKCS12_GEN_MAC                          0
#  define PKCS12_F_PKCS12_INIT                             0
#  define PKCS12_F_PKCS12_ITEM_DECRYPT_D2I                 0
#  define PKCS12_F_PKCS12_ITEM_I2D_ENCRYPT                 0
#  define PKCS12_F_PKCS12_ITEM_PACK_SAFEBAG                0
#  define PKCS12_F_PKCS12_KEY_GEN_ASC                      0
#  define PKCS12_F_PKCS12_KEY_GEN_UNI                      0
#  define PKCS12_F_PKCS12_KEY_GEN_UTF8                     0
#  define PKCS12_F_PKCS12_NEWPASS                          0
#  define PKCS12_F_PKCS12_PACK_P7DATA                      0
#  define PKCS12_F_PKCS12_PACK_P7ENCDATA                   0
#  define PKCS12_F_PKCS12_PARSE                            0
#  define PKCS12_F_PKCS12_PBE_CRYPT                        0
#  define PKCS12_F_PKCS12_PBE_KEYIVGEN                     0
#  define PKCS12_F_PKCS12_SAFEBAG_CREATE0_P8INF            0
#  define PKCS12_F_PKCS12_SAFEBAG_CREATE0_PKCS8            0
#  define PKCS12_F_PKCS12_SAFEBAG_CREATE_PKCS8_ENCRYPT     0
#  define PKCS12_F_PKCS12_SETUP_MAC                        0
#  define PKCS12_F_PKCS12_SET_MAC                          0
#  define PKCS12_F_PKCS12_UNPACK_AUTHSAFES                 0
#  define PKCS12_F_PKCS12_UNPACK_P7DATA                    0
#  define PKCS12_F_PKCS12_VERIFY_MAC                       0
#  define PKCS12_F_PKCS8_ENCRYPT                           0
#  define PKCS12_F_PKCS8_SET0_PBE                          0

/*
 * PKCS7 function codes.
 */
#  define PKCS7_F_DO_PKCS7_SIGNED_ATTRIB                   0
#  define PKCS7_F_PKCS7_ADD0_ATTRIB_SIGNING_TIME           0
#  define PKCS7_F_PKCS7_ADD_ATTRIB_SMIMECAP                0
#  define PKCS7_F_PKCS7_ADD_CERTIFICATE                    0
#  define PKCS7_F_PKCS7_ADD_CRL                            0
#  define PKCS7_F_PKCS7_ADD_RECIPIENT_INFO                 0
#  define PKCS7_F_PKCS7_ADD_SIGNATURE                      0
#  define PKCS7_F_PKCS7_ADD_SIGNER                         0
#  define PKCS7_F_PKCS7_BIO_ADD_DIGEST                     0
#  define PKCS7_F_PKCS7_COPY_EXISTING_DIGEST               0
#  define PKCS7_F_PKCS7_CTRL                               0
#  define PKCS7_F_PKCS7_DATADECODE                         0
#  define PKCS7_F_PKCS7_DATAFINAL                          0
#  define PKCS7_F_PKCS7_DATAINIT                           0
#  define PKCS7_F_PKCS7_DATAVERIFY                         0
#  define PKCS7_F_PKCS7_DECRYPT                            0
#  define PKCS7_F_PKCS7_DECRYPT_RINFO                      0
#  define PKCS7_F_PKCS7_ENCODE_RINFO                       0
#  define PKCS7_F_PKCS7_ENCRYPT                            0
#  define PKCS7_F_PKCS7_FINAL                              0
#  define PKCS7_F_PKCS7_FIND_DIGEST                        0
#  define PKCS7_F_PKCS7_GET0_SIGNERS                       0
#  define PKCS7_F_PKCS7_RECIP_INFO_SET                     0
#  define PKCS7_F_PKCS7_SET_CIPHER                         0
#  define PKCS7_F_PKCS7_SET_CONTENT                        0
#  define PKCS7_F_PKCS7_SET_DIGEST                         0
#  define PKCS7_F_PKCS7_SET_TYPE                           0
#  define PKCS7_F_PKCS7_SIGN                               0
#  define PKCS7_F_PKCS7_SIGNATUREVERIFY                    0
#  define PKCS7_F_PKCS7_SIGNER_INFO_SET                    0
#  define PKCS7_F_PKCS7_SIGNER_INFO_SIGN                   0
#  define PKCS7_F_PKCS7_SIGN_ADD_SIGNER                    0
#  define PKCS7_F_PKCS7_SIMPLE_SMIMECAP                    0
#  define PKCS7_F_PKCS7_VERIFY                             0

/*
 * RAND function codes.
 */
#  define RAND_F_DATA_COLLECT_METHOD                       0
#  define RAND_F_DRBG_BYTES                                0
#  define RAND_F_DRBG_GET_ENTROPY                          0
#  define RAND_F_DRBG_SETUP                                0
#  define RAND_F_GET_ENTROPY                               0
#  define RAND_F_RAND_BYTES                                0
#  define RAND_F_RAND_DRBG_ENABLE_LOCKING                  0
#  define RAND_F_RAND_DRBG_GENERATE                        0
#  define RAND_F_RAND_DRBG_GET_ENTROPY                     0
#  define RAND_F_RAND_DRBG_GET_NONCE                       0
#  define RAND_F_RAND_DRBG_INSTANTIATE                     0
#  define RAND_F_RAND_DRBG_NEW                             0
#  define RAND_F_RAND_DRBG_RESEED                          0
#  define RAND_F_RAND_DRBG_RESTART                         0
#  define RAND_F_RAND_DRBG_SET                             0
#  define RAND_F_RAND_DRBG_SET_DEFAULTS                    0
#  define RAND_F_RAND_DRBG_UNINSTANTIATE                   0
#  define RAND_F_RAND_LOAD_FILE                            0
#  define RAND_F_RAND_POOL_ACQUIRE_ENTROPY                 0
#  define RAND_F_RAND_POOL_ADD                             0
#  define RAND_F_RAND_POOL_ADD_BEGIN                       0
#  define RAND_F_RAND_POOL_ADD_END                         0
#  define RAND_F_RAND_POOL_ATTACH                          0
#  define RAND_F_RAND_POOL_BYTES_NEEDED                    0
#  define RAND_F_RAND_POOL_GROW                            0
#  define RAND_F_RAND_POOL_NEW                             0
#  define RAND_F_RAND_PSEUDO_BYTES                         0
#  define RAND_F_RAND_WRITE_FILE                           0

/*
 * RSA function codes.
 */
#  define RSA_F_CHECK_PADDING_MD                           0
#  define RSA_F_ENCODE_PKCS1                               0
#  define RSA_F_INT_RSA_VERIFY                             0
#  define RSA_F_OLD_RSA_PRIV_DECODE                        0
#  define RSA_F_PKEY_PSS_INIT                              0
#  define RSA_F_PKEY_RSA_CTRL                              0
#  define RSA_F_PKEY_RSA_CTRL_STR                          0
#  define RSA_F_PKEY_RSA_SIGN                              0
#  define RSA_F_PKEY_RSA_VERIFY                            0
#  define RSA_F_PKEY_RSA_VERIFYRECOVER                     0
#  define RSA_F_RSA_ALGOR_TO_MD                            0
#  define RSA_F_RSA_BUILTIN_KEYGEN                         0
#  define RSA_F_RSA_CHECK_KEY                              0
#  define RSA_F_RSA_CHECK_KEY_EX                           0
#  define RSA_F_RSA_CMS_DECRYPT                            0
#  define RSA_F_RSA_CMS_VERIFY                             0
#  define RSA_F_RSA_ITEM_VERIFY                            0
#  define RSA_F_RSA_METH_DUP                               0
#  define RSA_F_RSA_METH_NEW                               0
#  define RSA_F_RSA_METH_SET1_NAME                         0
#  define RSA_F_RSA_MGF1_TO_MD                             0
#  define RSA_F_RSA_MULTIP_INFO_NEW                        0
#  define RSA_F_RSA_NEW_METHOD                             0
#  define RSA_F_RSA_NULL                                   0
#  define RSA_F_RSA_NULL_PRIVATE_DECRYPT                   0
#  define RSA_F_RSA_NULL_PRIVATE_ENCRYPT                   0
#  define RSA_F_RSA_NULL_PUBLIC_DECRYPT                    0
#  define RSA_F_RSA_NULL_PUBLIC_ENCRYPT                    0
#  define RSA_F_RSA_OSSL_PRIVATE_DECRYPT                   0
#  define RSA_F_RSA_OSSL_PRIVATE_ENCRYPT                   0
#  define RSA_F_RSA_OSSL_PUBLIC_DECRYPT                    0
#  define RSA_F_RSA_OSSL_PUBLIC_ENCRYPT                    0
#  define RSA_F_RSA_PADDING_ADD_NONE                       0
#  define RSA_F_RSA_PADDING_ADD_PKCS1_OAEP                 0
#  define RSA_F_RSA_PADDING_ADD_PKCS1_OAEP_MGF1            0
#  define RSA_F_RSA_PADDING_ADD_PKCS1_PSS                  0
#  define RSA_F_RSA_PADDING_ADD_PKCS1_PSS_MGF1             0
#  define RSA_F_RSA_PADDING_ADD_PKCS1_TYPE_1               0
#  define RSA_F_RSA_PADDING_ADD_PKCS1_TYPE_2               0
#  define RSA_F_RSA_PADDING_ADD_SSLV23                     0
#  define RSA_F_RSA_PADDING_ADD_X931                       0
#  define RSA_F_RSA_PADDING_CHECK_NONE                     0
#  define RSA_F_RSA_PADDING_CHECK_PKCS1_OAEP               0
#  define RSA_F_RSA_PADDING_CHECK_PKCS1_OAEP_MGF1          0
#  define RSA_F_RSA_PADDING_CHECK_PKCS1_TYPE_1             0
#  define RSA_F_RSA_PADDING_CHECK_PKCS1_TYPE_2             0
#  define RSA_F_RSA_PADDING_CHECK_SSLV23                   0
#  define RSA_F_RSA_PADDING_CHECK_X931                     0
#  define RSA_F_RSA_PARAM_DECODE                           0
#  define RSA_F_RSA_PRINT                                  0
#  define RSA_F_RSA_PRINT_FP                               0
#  define RSA_F_RSA_PRIV_DECODE                            0
#  define RSA_F_RSA_PRIV_ENCODE                            0
#  define RSA_F_RSA_PSS_GET_PARAM                          0
#  define RSA_F_RSA_PSS_TO_CTX                             0
#  define RSA_F_RSA_PUB_DECODE                             0
#  define RSA_F_RSA_SETUP_BLINDING                         0
#  define RSA_F_RSA_SIGN                                   0
#  define RSA_F_RSA_SIGN_ASN1_OCTET_STRING                 0
#  define RSA_F_RSA_VERIFY                                 0
#  define RSA_F_RSA_VERIFY_ASN1_OCTET_STRING               0
#  define RSA_F_RSA_VERIFY_PKCS1_PSS_MGF1                  0
#  define RSA_F_SETUP_TBUF                                 0

/*
 * OSSL_STORE function codes.
 */
#  define OSSL_STORE_F_FILE_CTRL                           0
#  define OSSL_STORE_F_FILE_FIND                           0
#  define OSSL_STORE_F_FILE_GET_PASS                       0
#  define OSSL_STORE_F_FILE_LOAD                           0
#  define OSSL_STORE_F_FILE_LOAD_TRY_DECODE                0
#  define OSSL_STORE_F_FILE_NAME_TO_URI                    0
#  define OSSL_STORE_F_FILE_OPEN                           0
#  define OSSL_STORE_F_OSSL_STORE_ATTACH_PEM_BIO           0
#  define OSSL_STORE_F_OSSL_STORE_EXPECT                   0
#  define OSSL_STORE_F_OSSL_STORE_FILE_ATTACH_PEM_BIO_INT  0
#  define OSSL_STORE_F_OSSL_STORE_FIND                     0
#  define OSSL_STORE_F_OSSL_STORE_GET0_LOADER_INT          0
#  define OSSL_STORE_F_OSSL_STORE_INFO_GET1_CERT           0
#  define OSSL_STORE_F_OSSL_STORE_INFO_GET1_CRL            0
#  define OSSL_STORE_F_OSSL_STORE_INFO_GET1_NAME           0
#  define OSSL_STORE_F_OSSL_STORE_INFO_GET1_NAME_DESCRIPTION 0
#  define OSSL_STORE_F_OSSL_STORE_INFO_GET1_PARAMS         0
#  define OSSL_STORE_F_OSSL_STORE_INFO_GET1_PKEY           0
#  define OSSL_STORE_F_OSSL_STORE_INFO_NEW_CERT            0
#  define OSSL_STORE_F_OSSL_STORE_INFO_NEW_CRL             0
#  define OSSL_STORE_F_OSSL_STORE_INFO_NEW_EMBEDDED        0
#  define OSSL_STORE_F_OSSL_STORE_INFO_NEW_NAME            0
#  define OSSL_STORE_F_OSSL_STORE_INFO_NEW_PARAMS          0
#  define OSSL_STORE_F_OSSL_STORE_INFO_NEW_PKEY            0
#  define OSSL_STORE_F_OSSL_STORE_INFO_SET0_NAME_DESCRIPTION 0
#  define OSSL_STORE_F_OSSL_STORE_INIT_ONCE                0
#  define OSSL_STORE_F_OSSL_STORE_LOADER_NEW               0
#  define OSSL_STORE_F_OSSL_STORE_OPEN                     0
#  define OSSL_STORE_F_OSSL_STORE_OPEN_INT                 0
#  define OSSL_STORE_F_OSSL_STORE_REGISTER_LOADER_INT      0
#  define OSSL_STORE_F_OSSL_STORE_SEARCH_BY_ALIAS          0
#  define OSSL_STORE_F_OSSL_STORE_SEARCH_BY_ISSUER_SERIAL  0
#  define OSSL_STORE_F_OSSL_STORE_SEARCH_BY_KEY_FINGERPRINT 0
#  define OSSL_STORE_F_OSSL_STORE_SEARCH_BY_NAME           0
#  define OSSL_STORE_F_OSSL_STORE_UNREGISTER_LOADER_INT    0
#  define OSSL_STORE_F_TRY_DECODE_PARAMS                   0
#  define OSSL_STORE_F_TRY_DECODE_PKCS12                   0
#  define OSSL_STORE_F_TRY_DECODE_PKCS8ENCRYPTED           0

#  ifndef OPENSSL_NO_TS
/*
 * TS function codes.
 */
#   define TS_F_DEF_SERIAL_CB                               0
#   define TS_F_DEF_TIME_CB                                 0
#   define TS_F_ESS_ADD_SIGNING_CERT                        0
#   define TS_F_ESS_ADD_SIGNING_CERT_V2                     0
#   define TS_F_ESS_CERT_ID_NEW_INIT                        0
#   define TS_F_ESS_CERT_ID_V2_NEW_INIT                     0
#   define TS_F_ESS_SIGNING_CERT_NEW_INIT                   0
#   define TS_F_ESS_SIGNING_CERT_V2_NEW_INIT                0
#   define TS_F_INT_TS_RESP_VERIFY_TOKEN                    0
#   define TS_F_PKCS7_TO_TS_TST_INFO                        0
#   define TS_F_TS_ACCURACY_SET_MICROS                      0
#   define TS_F_TS_ACCURACY_SET_MILLIS                      0
#   define TS_F_TS_ACCURACY_SET_SECONDS                     0
#   define TS_F_TS_CHECK_IMPRINTS                           0
#   define TS_F_TS_CHECK_NONCES                             0
#   define TS_F_TS_CHECK_POLICY                             0
#   define TS_F_TS_CHECK_SIGNING_CERTS                      0
#   define TS_F_TS_CHECK_STATUS_INFO                        0
#   define TS_F_TS_COMPUTE_IMPRINT                          0
#   define TS_F_TS_CONF_INVALID                             0
#   define TS_F_TS_CONF_LOAD_CERT                           0
#   define TS_F_TS_CONF_LOAD_CERTS                          0
#   define TS_F_TS_CONF_LOAD_KEY                            0
#   define TS_F_TS_CONF_LOOKUP_FAIL                         0
#   define TS_F_TS_CONF_SET_DEFAULT_ENGINE                  0
#   define TS_F_TS_GET_STATUS_TEXT                          0
#   define TS_F_TS_MSG_IMPRINT_SET_ALGO                     0
#   define TS_F_TS_REQ_SET_MSG_IMPRINT                      0
#   define TS_F_TS_REQ_SET_NONCE                            0
#   define TS_F_TS_REQ_SET_POLICY_ID                        0
#   define TS_F_TS_RESP_CREATE_RESPONSE                     0
#   define TS_F_TS_RESP_CREATE_TST_INFO                     0
#   define TS_F_TS_RESP_CTX_ADD_FAILURE_INFO                0
#   define TS_F_TS_RESP_CTX_ADD_MD                          0
#   define TS_F_TS_RESP_CTX_ADD_POLICY                      0
#   define TS_F_TS_RESP_CTX_NEW                             0
#   define TS_F_TS_RESP_CTX_SET_ACCURACY                    0
#   define TS_F_TS_RESP_CTX_SET_CERTS                       0
#   define TS_F_TS_RESP_CTX_SET_DEF_POLICY                  0
#   define TS_F_TS_RESP_CTX_SET_SIGNER_CERT                 0
#   define TS_F_TS_RESP_CTX_SET_STATUS_INFO                 0
#   define TS_F_TS_RESP_GET_POLICY                          0
#   define TS_F_TS_RESP_SET_GENTIME_WITH_PRECISION          0
#   define TS_F_TS_RESP_SET_STATUS_INFO                     0
#   define TS_F_TS_RESP_SET_TST_INFO                        0
#   define TS_F_TS_RESP_SIGN                                0
#   define TS_F_TS_RESP_VERIFY_SIGNATURE                    0
#   define TS_F_TS_TST_INFO_SET_ACCURACY                    0
#   define TS_F_TS_TST_INFO_SET_MSG_IMPRINT                 0
#   define TS_F_TS_TST_INFO_SET_NONCE                       0
#   define TS_F_TS_TST_INFO_SET_POLICY_ID                   0
#   define TS_F_TS_TST_INFO_SET_SERIAL                      0
#   define TS_F_TS_TST_INFO_SET_TIME                        0
#   define TS_F_TS_TST_INFO_SET_TSA                         0
#   define TS_F_TS_VERIFY                                   0
#   define TS_F_TS_VERIFY_CERT                              0
#   define TS_F_TS_VERIFY_CTX_NEW                           0
#  endif

/*
 * UI function codes.
 */
#  define UI_F_CLOSE_CONSOLE                               0
#  define UI_F_ECHO_CONSOLE                                0
#  define UI_F_GENERAL_ALLOCATE_BOOLEAN                    0
#  define UI_F_GENERAL_ALLOCATE_PROMPT                     0
#  define UI_F_NOECHO_CONSOLE                              0
#  define UI_F_OPEN_CONSOLE                                0
#  define UI_F_UI_CONSTRUCT_PROMPT                         0
#  define UI_F_UI_CREATE_METHOD                            0
#  define UI_F_UI_CTRL                                     0
#  define UI_F_UI_DUP_ERROR_STRING                         0
#  define UI_F_UI_DUP_INFO_STRING                          0
#  define UI_F_UI_DUP_INPUT_BOOLEAN                        0
#  define UI_F_UI_DUP_INPUT_STRING                         0
#  define UI_F_UI_DUP_USER_DATA                            0
#  define UI_F_UI_DUP_VERIFY_STRING                        0
#  define UI_F_UI_GET0_RESULT                              0
#  define UI_F_UI_GET_RESULT_LENGTH                        0
#  define UI_F_UI_NEW_METHOD                               0
#  define UI_F_UI_PROCESS                                  0
#  define UI_F_UI_SET_RESULT                               0
#  define UI_F_UI_SET_RESULT_EX                            0

/*
 * X509 function codes.
 */
#  define X509_F_ADD_CERT_DIR                              0
#  define X509_F_BUILD_CHAIN                               0
#  define X509_F_BY_FILE_CTRL                              0
#  define X509_F_CHECK_NAME_CONSTRAINTS                    0
#  define X509_F_CHECK_POLICY                              0
#  define X509_F_DANE_I2D                                  0
#  define X509_F_DIR_CTRL                                  0
#  define X509_F_GET_CERT_BY_SUBJECT                       0
#  define X509_F_I2D_X509_AUX                              0
#  define X509_F_LOOKUP_CERTS_SK                           0
#  define X509_F_NETSCAPE_SPKI_B64_DECODE                  0
#  define X509_F_NETSCAPE_SPKI_B64_ENCODE                  0
#  define X509_F_NEW_DIR                                   0
#  define X509_F_X509AT_ADD1_ATTR                          0
#  define X509_F_X509V3_ADD_EXT                            0
#  define X509_F_X509_ATTRIBUTE_CREATE_BY_NID              0
#  define X509_F_X509_ATTRIBUTE_CREATE_BY_OBJ              0
#  define X509_F_X509_ATTRIBUTE_CREATE_BY_TXT              0
#  define X509_F_X509_ATTRIBUTE_GET0_DATA                  0
#  define X509_F_X509_ATTRIBUTE_SET1_DATA                  0
#  define X509_F_X509_CHECK_PRIVATE_KEY                    0
#  define X509_F_X509_CRL_DIFF                             0
#  define X509_F_X509_CRL_METHOD_NEW                       0
#  define X509_F_X509_CRL_PRINT_FP                         0
#  define X509_F_X509_EXTENSION_CREATE_BY_NID              0
#  define X509_F_X509_EXTENSION_CREATE_BY_OBJ              0
#  define X509_F_X509_GET_PUBKEY_PARAMETERS                0
#  define X509_F_X509_LOAD_CERT_CRL_FILE                   0
#  define X509_F_X509_LOAD_CERT_FILE                       0
#  define X509_F_X509_LOAD_CRL_FILE                        0
#  define X509_F_X509_LOOKUP_METH_NEW                      0
#  define X509_F_X509_LOOKUP_NEW                           0
#  define X509_F_X509_NAME_ADD_ENTRY                       0
#  define X509_F_X509_NAME_CANON                           0
#  define X509_F_X509_NAME_ENTRY_CREATE_BY_NID             0
#  define X509_F_X509_NAME_ENTRY_CREATE_BY_TXT             0
#  define X509_F_X509_NAME_ENTRY_SET_OBJECT                0
#  define X509_F_X509_NAME_ONELINE                         0
#  define X509_F_X509_NAME_PRINT                           0
#  define X509_F_X509_OBJECT_NEW                           0
#  define X509_F_X509_PRINT_EX_FP                          0
#  define X509_F_X509_PUBKEY_DECODE                        0
#  define X509_F_X509_PUBKEY_GET                           0
#  define X509_F_X509_PUBKEY_GET0                          0
#  define X509_F_X509_PUBKEY_SET                           0
#  define X509_F_X509_REQ_CHECK_PRIVATE_KEY                0
#  define X509_F_X509_REQ_PRINT_EX                         0
#  define X509_F_X509_REQ_PRINT_FP                         0
#  define X509_F_X509_REQ_TO_X509                          0
#  define X509_F_X509_STORE_ADD_CERT                       0
#  define X509_F_X509_STORE_ADD_CRL                        0
#  define X509_F_X509_STORE_ADD_LOOKUP                     0
#  define X509_F_X509_STORE_CTX_GET1_ISSUER                0
#  define X509_F_X509_STORE_CTX_INIT                       0
#  define X509_F_X509_STORE_CTX_NEW                        0
#  define X509_F_X509_STORE_CTX_PURPOSE_INHERIT            0
#  define X509_F_X509_STORE_NEW                            0
#  define X509_F_X509_TO_X509_REQ                          0
#  define X509_F_X509_TRUST_ADD                            0
#  define X509_F_X509_TRUST_SET                            0
#  define X509_F_X509_VERIFY_CERT                          0
#  define X509_F_X509_VERIFY_PARAM_NEW                     0

/*
 * X509V3 function codes.
 */
#  define X509V3_F_A2I_GENERAL_NAME                        0
#  define X509V3_F_ADDR_VALIDATE_PATH_INTERNAL             0
#  define X509V3_F_ASIDENTIFIERCHOICE_CANONIZE             0
#  define X509V3_F_ASIDENTIFIERCHOICE_IS_CANONICAL         0
#  define X509V3_F_BIGNUM_TO_STRING                        0
#  define X509V3_F_COPY_EMAIL                              0
#  define X509V3_F_COPY_ISSUER                             0
#  define X509V3_F_DO_DIRNAME                              0
#  define X509V3_F_DO_EXT_I2D                              0
#  define X509V3_F_DO_EXT_NCONF                            0
#  define X509V3_F_GNAMES_FROM_SECTNAME                    0
#  define X509V3_F_I2S_ASN1_ENUMERATED                     0
#  define X509V3_F_I2S_ASN1_IA5STRING                      0
#  define X509V3_F_I2S_ASN1_INTEGER                        0
#  define X509V3_F_I2V_AUTHORITY_INFO_ACCESS               0
#  define X509V3_F_LEVEL_ADD_NODE                          0
#  define X509V3_F_NOTICE_SECTION                          0
#  define X509V3_F_NREF_NOS                                0
#  define X509V3_F_POLICY_CACHE_CREATE                     0
#  define X509V3_F_POLICY_CACHE_NEW                        0
#  define X509V3_F_POLICY_DATA_NEW                         0
#  define X509V3_F_POLICY_SECTION                          0
#  define X509V3_F_PROCESS_PCI_VALUE                       0
#  define X509V3_F_R2I_CERTPOL                             0
#  define X509V3_F_R2I_PCI                                 0
#  define X509V3_F_S2I_ASN1_IA5STRING                      0
#  define X509V3_F_S2I_ASN1_INTEGER                        0
#  define X509V3_F_S2I_ASN1_OCTET_STRING                   0
#  define X509V3_F_S2I_SKEY_ID                             0
#  define X509V3_F_SET_DIST_POINT_NAME                     0
#  define X509V3_F_SXNET_ADD_ID_ASC                        0
#  define X509V3_F_SXNET_ADD_ID_INTEGER                    0
#  define X509V3_F_SXNET_ADD_ID_ULONG                      0
#  define X509V3_F_SXNET_GET_ID_ASC                        0
#  define X509V3_F_SXNET_GET_ID_ULONG                      0
#  define X509V3_F_TREE_INIT                               0
#  define X509V3_F_V2I_ASIDENTIFIERS                       0
#  define X509V3_F_V2I_ASN1_BIT_STRING                     0
#  define X509V3_F_V2I_AUTHORITY_INFO_ACCESS               0
#  define X509V3_F_V2I_AUTHORITY_KEYID                     0
#  define X509V3_F_V2I_BASIC_CONSTRAINTS                   0
#  define X509V3_F_V2I_CRLD                                0
#  define X509V3_F_V2I_EXTENDED_KEY_USAGE                  0
#  define X509V3_F_V2I_GENERAL_NAMES                       0
#  define X509V3_F_V2I_GENERAL_NAME_EX                     0
#  define X509V3_F_V2I_IDP                                 0
#  define X509V3_F_V2I_IPADDRBLOCKS                        0
#  define X509V3_F_V2I_ISSUER_ALT                          0
#  define X509V3_F_V2I_NAME_CONSTRAINTS                    0
#  define X509V3_F_V2I_POLICY_CONSTRAINTS                  0
#  define X509V3_F_V2I_POLICY_MAPPINGS                     0
#  define X509V3_F_V2I_SUBJECT_ALT                         0
#  define X509V3_F_V2I_TLS_FEATURE                         0
#  define X509V3_F_V3_GENERIC_EXTENSION                    0
#  define X509V3_F_X509V3_ADD1_I2D                         0
#  define X509V3_F_X509V3_ADD_VALUE                        0
#  define X509V3_F_X509V3_EXT_ADD                          0
#  define X509V3_F_X509V3_EXT_ADD_ALIAS                    0
#  define X509V3_F_X509V3_EXT_I2D                          0
#  define X509V3_F_X509V3_EXT_NCONF                        0
#  define X509V3_F_X509V3_GET_SECTION                      0
#  define X509V3_F_X509V3_GET_STRING                       0
#  define X509V3_F_X509V3_GET_VALUE_BOOL                   0
#  define X509V3_F_X509V3_PARSE_LIST                       0
#  define X509V3_F_X509_PURPOSE_ADD                        0
#  define X509V3_F_X509_PURPOSE_SET                        0

/*
 * Compatibility defines.
 */
# define EVP_R_OPERATON_NOT_INITIALIZED    EVP_R_OPERATION_NOT_INITIALIZED

# endif

# ifdef  __cplusplus
}
# endif
#endif
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    ex ioc_reset_in_progress_lock ioc_link_reset_in_progress ignore_loginfos remove_host pci_error_recovery wait_for_discovery_to_complete is_driver_loading port_enable_failed start_scan start_scan_failed msix_enable msix_vector_count cpu_msix_table cpu_msix_table_sz ioc_reset_count schedule_dead_ioc_flush_running_cmds non_operational_loop ioc_coredump_loop timestamp_update_count time_sync_interval total_io_cnt high_iops_outstanding msix_load_balance thresh_hold high_iops_queues iopoll_q_start_index drv_internal_flags drv_support_bitmap enable_sdev_max_qd use_32bit_dma io_uring_poll_queues scsi_io_cb_idx tm_cb_idx transport_cb_idx scsih_cb_idx ctl_cb_idx base_cb_idx port_enable_cb_idx config_cb_idx tm_tr_cb_idx tm_tr_volume_cb_idx tm_sas_control_cb_idx base_cmds port_enable_cmds transport_cmds scsih_cmds tm_cmds ctl_cmds config_cmds base_add_sg_single build_sg_scmd build_sg build_zero_len_sge sge_size_ieee hba_mpi_version_belonged build_sg_mpi build_zero_len_sge_mpi build_nvme_prp event_context event_log event_masks tm_custom_handling nvme_abort_timeout max_shutdown_latency max_wideport_qd max_narrowport_qd max_nvme_qd max_sata_qd facts prev_fw_facts pfacts manu_pg0 manu_pg10 manu_pg11 bios_pg2 bios_pg3 ioc_pg8 iounit_pg0 iounit_pg1 iounit_pg8 ioc_pg1_copy req_boot_device req_alt_boot_device current_boot_device sas_hba sas_expander_list enclosure_list sas_node_lock sas_device_list sas_device_init_list sas_device_lock pcie_device_list pcie_device_init_list pcie_device_lock raid_device_list raid_device_lock io_missing_delay device_missing_delay sas_id pcie_target_id blocking_handles pd_handles pd_handles_sz pend_os_device_add pend_os_device_add_sz config_page_sz config_page config_page_dma config_vaddr hba_queue_depth sge_size scsiio_depth request_sz request_dma_sz pcie_sg_lookup scsi_lookup_lock pending_io_count reset_wq io_queue_num pcie_sgl_dma_pool free_chain_list chain_dma_pool chain_pages max_sges_in_main_message max_sges_in_chain_message chains_needed_per_io chain_depth chain_segment_sz chains_per_prp_buffer hi_priority_smid hi_priority hi_priority_dma hi_priority_depth hpr_lookup hpr_free_list internal_smid internal_dma internal_depth internal_lookup internal_free_list sense_dma sense_dma_pool reply_sz reply_dma reply_dma_max_address reply_dma_min_address reply_dma_pool reply_free_queue_depth reply_free reply_free_dma reply_free_dma_pool reply_free_host_index reply_post_queue_depth reply_post rdpq_array_capable rdpq_array_enable rdpq_array_enable_assigned reply_post_free_dma_pool reply_post_free_array_dma_pool reply_post_free_array reply_post_free_array_dma reply_queue_count reply_queue_list combined_reply_queue combined_reply_index_count smp_affinity_enable replyPostRegisterIndex delayed_tr_list delayed_tr_volume_list delayed_sc_list delayed_event_ack_list temp_sensors_count pci_access_mutex diag_buffer diag_buffer_sz diag_buffer_dma diag_buffer_status product_specific diagnostic_flags ring_buffer_offset ring_buffer_sz htb_rel reset_from_user is_warpdrive is_mcpu_endpoint hide_ir_msg mfg_pg10_hide_flag hide_drives diag_trigger_lock diag_trigger_active atomic_desc_capable base_readl base_readl_ext_retry diag_trigger_master diag_trigger_event diag_trigger_scsi diag_trigger_mpi supports_trigger_pages device_remove_in_progress device_remove_in_progress_sz is_gen35_ioc is_aero_ioc ioc_dump put_smid_scsi_io put_smid_fast_path put_smid_hi_priority put_smid_default get_msix_index_for_smlio multipath_on_hba port_table_list io_uring_poll_queue reply_q MPT_ADD_SGE MPT_BUILD_SG_SCMD MPT_BUILD_SG MPT_BUILD_ZERO_LEN_SGE NVME_BUILD_PRP PUT_SMID_IO_FP_HIP PUT_SMID_DEFAULT BASE_READ_REG GET_MSIX_INDEX mpi3_version_union mpt3sas_facts MsgVersion HeaderVersion IOCNumber IOCExceptions MaxChainDepth WhoInit NumberOfPorts MaxMSIxVectors RequestCredit IOCCapabilities FWVersion IOCRequestFrameSize IOCMaxChainSegmentSize MaxInitiators MaxTargets MaxSasExpanders MaxEnclosures ProtocolFlags HighPriorityCredit MaxReplyDescriptorPostQueueDepth ReplyFrameSize MaxVolumes MaxDevHandle MinDevHandle CurrentHostPageSize mpt3sas_port_facts PortNumber Po---Implements the HTTP client protocol in a standard form that Nmap scripts can
-- take advantage of.
--
-- Because HTTP has so many uses, there are a number of interfaces to this
-- library.
--
-- The most obvious and common ones are simply <code>get</code>,
-- <code>post</code>, and <code>head</code>; or, if more control is required,
-- <code>generic_request</code> can be used. These functions take host and port
-- as their main parameters and they do what one would expect. The
-- <code>get_url</code> helper function can be used to parse and retrieve a full
-- URL.
--
-- HTTPS support is transparent. The library uses <code>comm.tryssl</code> to
-- determine whether SSL is required for a request.
--
-- These functions return a table of values, including:
-- * <code>status-line</code> - A string representing the status, such as "HTTP/1.1 200 OK", followed by a newline. In case of an error, a description will be provided in this line.
-- * <code>status</code> - The HTTP status value; for example, "200". If an error occurs during a request, then this value is going to be nil.
-- * <code>version</code> - HTTP protocol version string, as stated in the status line. Example: "1.1"
-- * <code>header</code> - An associative array representing the header. Keys are all lowercase, and standard headers, such as 'date', 'content-length', etc. will typically be present.
-- * <code>rawheader</code> - A numbered array of the headers, exactly as the server sent them. While header['content-type'] might be 'text/html', rawheader[3] might be 'Content-type: text/html'.
-- * <code>cookies</code> - A numbered array of the cookies the server sent. Each cookie is a table with the expected keys, such as <code>name</code>, <code>value</code>, <code>path</code>, <code>domain</code>, and <code>expires</code>. This table can be sent to the server in subsequent responses in the <code>options</code> table to any function (see below).
-- * <code>rawbody</code> - The full body, as returned by the server. Chunked transfer encoding is handled transparently.
-- * <code>body</code> - The full body, after processing the Content-Encoding header, if any. The Content-Encoding and Content-Length headers are adjusted to stay consistent with the processed body.
-- * <code>incomplete</code> - Partially received response object, in case of an error.
-- * <code>truncated</code> - A flag to indicate that the body has been truncated
-- * <code>decoded</code> - A list of processed named content encodings (like "identity" or "gzip")
-- * <code>undecoded</code> - A list of named content encodings that could not be processed (due to lack of support or the body being corrupted for a given encoding). A body has been successfully decoded if this list is empty (or nil, if no encodings were used in the first place).
-- * <code>location</code> - A numbered array of the locations of redirects that were followed.
--
-- Many of the functions optionally allow an "options" input table, which can
-- modify the HTTP request or its processing in many ways like adding headers or
-- setting the timeout. The following are valid keys in "options"
-- (note: not all options will necessarily affect every function):
-- * <code>timeout</code>: A timeout used for socket operations.
-- * <code>header</code>: A table containing additional headers to be used for the request. For example, <code>options['header']['Content-Type'] = 'text/xml'</code>
-- * <code>content</code>: The content of the message. This can be either a string, which will be directly added as the body of the message, or a table, which will have each key=value pair added (like a normal POST request). (A corresponding Content-Length header will be added automatically. Set header['Content-Length'] to override it).
-- * <code>cookies</code>: A list of cookies as either a string, which will be directly sent, or a table. If it's a table, the following fields are recognized: <code>name</code>, <code>value</code> and <code>path</code>. Only <code>name</code> and <code>value</code> fields are required.
-- * <code>auth</code>: A table containing the keys <code>username</code> and <code>password</code>, which will be used for HTTP Basic authentication.
--   If a server requires HTTP Digest authentication, then there must also be a key <code>digest</code>, with value <code>true</code>.
--   If a server requires NTLM authentication, then there must also be a key <code>ntlm</code>, with value <code>true</code>.
-- * <code>bypass_cache</code>: Do not perform a lookup in the local HTTP cache.
-- * <code>no_cache</code>: Do not save the result of this request to the local HTTP cache.
-- * <code>no_cache_body</code>: Do not save the body of the response to the local HTTP cache.
-- * <code>max_body_size</code>: Limit the received body to specific number of bytes. Overrides script argument <code>http.max-body-size</code>. See the script argument for details.
-- * <code>truncated_ok</code>: Do not treat oversized body as error. Overrides script argument <code>http.truncated-ok</code>.
-- * <code>any_af</code>: Allow connecting to any address family, inet or inet6. By default, these functions will only use the same AF as nmap.address_family to resolve names. (This option is a straight pass-thru to <code>comm.lua</code> functions.)
-- * <code>redirect_ok</code>: Closure that overrides the default redirect_ok used to validate whether to follow HTTP redirects or not. False, if no HTTP redirects should be followed. Alternatively, a number may be passed to change the number of redirects to follow.
--   The following example shows how to write a custom closure that follows 5 consecutive redirects, without the safety checks in the default redirect_ok:
--   <code>
--   redirect_ok = function(host,port)
--     local c = 5
--     return function(url)
--       if ( c==0 ) then return false end
--       c = c - 1
--       return true
--     end
--   end
--   </code>
--
-- If a script is planning on making a lot of requests, the pipelining functions
-- can be helpful. <code>pipeline_add</code> queues requests in a table, and
-- <code>pipeline_go</code> performs the requests, returning the results as an
-- array, with the responses in the same order as the requests were added.
-- As a simple example:
--<code>
--  -- Start by defining the 'all' variable as nil
--  local all = nil
--
--  -- Add two GET requests and one HEAD to the queue but these requests are
--  -- not performed yet. The second parameter represents the "options" table
--  -- (which we don't need in this example).
--  all = http.pipeline_add('/book',    nil, all)
--  all = http.pipeline_add('/test',    nil, all)
--  all = http.pipeline_add('/monkeys', nil, all, 'HEAD')
--
--  -- Perform all three requests as parallel as Nmap is able to
--  local results = http.pipeline_go('nmap.org', 80, all)
--</code>
--
-- At this point, <code>results</code> is an array with three elements.
-- Each element is a table containing the HTTP result, as discussed above.
--
-- One more interface provided by the HTTP library helps scripts determine
-- whether or not a page exists. The <code>identify_404</code> function will
-- try several URLs on the server to determine what the server's 404 pages look
-- like. It will attempt to identify customized 404 pages that may not return
-- the actual status code 404. If successful, the function
-- <code>page_exists</code> can then be used to determine whether or not a page
-- exists.
--
-- Some other miscellaneous functions that can come in handy are
-- <code>response_contains</code>, <code>can_use_head</code>, and
-- <code>save_path</code>. See the appropriate documentation for details.
--
-- @args http.max-cache-size The maximum memory size (in bytes) of the cache.
--
-- @args http.useragent The value of the User-Agent header field sent with
-- requests. By default it is
-- <code>"Mozilla/5.0 (compatible; Nmap Scripting Engine; https://nmap.org/book/nse.html)"</code>.
-- A value of the empty string disables sending the User-Agent header field.
--
-- @args http.pipeline If set, it represents the number of HTTP requests that'll be
-- sent on one connection. This can be set low to make debugging easier, or it
-- can be set high to test how a server reacts (its chosen max is ignored).
-- @args http.max-pipeline If set, it represents the number of outstanding
-- HTTP requests that should be sent together in a single burst. Defaults to
-- <code>http.pipeline</code> (if set), or to what function
-- <code>get_pipeline_limit</code> returns.
--
-- @args http.host The value to use in the Host header of all requests unless
-- otherwise set. By default, the Host header uses the output of
-- <code>stdnse.get_hostname()</code>.
--
-- @args http.max-body-size Limit the received body to specific number of bytes.
-- An oversized body results in an error unless script argument
-- <code>http.truncated-ok</code> or request option
-- <code>truncated_ok</code> is set to true. The default is 2097152 (2MB). Use
-- value -1 to disable the limit altogether. This argument can be overridden
-- case-by-case with request option <code>max_body_size</code>.
--
-- @args http.truncated-ok Do not treat oversized body as error. (Use response
-- object flag <code>truncated</code> to check if the returned body has been
-- truncated.) This argument can be overridden case-by-case with request option
-- <code>truncated_ok</code>.

-- TODO
-- Implement cache system for http pipelines
--


local base64 = require "base64"
local comm = require "comm"
local coroutine = require "coroutine"
local math = require "math"
local nmap = require "nmap"
local os = require "os"
local sasl = require "sasl"
local shortport = require "shortport"
local slaxml = require "slaxml"
local stdnse = require "stdnse"
local string = require "string"
local stringaux = require "stringaux"
local table = require "table"
local tableaux = require "tableaux"
local url = require "url"
local smbauth = require "smbauth"
local unicode = require "unicode"

_ENV = stdnse.module("http", stdnse.seeall)

--Use ssl if we have it
local have_ssl, openssl = pcall(require,'openssl')

--Use zlib if we have it
local have_zlib, zlib = pcall(require,'zlib')

USER_AGENT = stdnse.get_script_args('http.useragent') or "Mozilla/5.0 (compatible; Nmap Scripting Engine; https://nmap.org/book/nse.html)"
local host_header = stdnse.get_script_args('http.host')
local MAX_REDIRECT_COUNT = 5
local MAX_BODY_SIZE = tonumber(stdnse.get_script_args('http.max-body-size')) or 2*1024*1024
local TRUNCATED_OK = string.lower(stdnse.get_script_args('http.truncated-ok') or "false") ~= "false"
local ERR_OVERSIZED_BODY = "response body too large"

--- Recursively copy into a table any elements from another table whose key it
-- doesn't have.
local function table_augment(to, from)
  for k, v in pairs(from) do
    if type( to[k] ) == 'table' then
      table_augment(to[k], from[k])
    else
      to[k] = from[k]
    end
  end
end

--- Provide the default port for a given scheme.
-- The localization is necessary because functions in http.lua like to use
-- "url" as a local parameter
local get_default_port = url.get_default_port

--- Get a value suitable for the Host header field.
-- See RFC 2616 sections 14.23 and 5.2.
local function get_host_field(host, port, scheme)
  -- If the global header is set by script-arg, use that.
  if host_header then return host_header end
  -- If there's no host, we can't invent a name.
  if not host then return nil end
  -- If there's no port, just return hostname.
  if not port then return stdnse.get_hostname(host) end
  if type(port) == "string" then
    port = tonumber(port)
    assert(port, "Invalid port: not a number or table")
  end
  if type(port) == "number" then
    port = {number=port, protocol="tcp"}
  end
  local number = port.number
  if scheme then
    -- Caller provided scheme. If it's default, return just the hostname.
    if number == get_default_port(scheme) then
      return stdnse.get_hostname(host)
    end
  else
    scheme = url.get_default_scheme(port)
    if scheme then
      -- Caller did not provide scheme, and this port has a default scheme.
      local ssl_port = shortport.ssl(host, port)
      if (ssl_port and scheme == 'https') or
        (not ssl_port and scheme == 'http') then
        -- If it's SSL and https, or if it's plaintext and http, return just the hostname.
        return stdnse.get_hostname(host)
      end
    end
  end
  -- No special cases matched, so include the port number in the host header
  return stdnse.get_hostname(host) .. ":" .. number
end

-- Skip *( SP | HT ) starting at offset. See RFC 2616, section 2.2.
-- @return the first index following the spaces.
-- @return the spaces skipped over.
local function skip_space(s, offset)
  local _, i, space = s:find("^([ \t]*)", offset)
  return i + 1, space
end

-- Get a token starting at offset. See RFC 2616, section 2.2.
-- @return the first index following the token, or nil if no token was found.
-- @return the token.
local function get_token(s, offset)
  -- All characters except CTL and separators.
  local _, i, token = s:find("^([^()<>@,;:\\\"/%[%]?={} \0\001-\031\127]+)", offset)
  if i then
    return i + 1, token
  else
    return nil
  end
end

-- Get a quoted-string starting at offset. See RFC 2616, section 2.2. crlf is
-- used as the definition for CRLF in the case of LWS within the string.
-- @return the first index following the quoted-string, or nil if no
-- quoted-string was found.
-- @return the contents of the quoted-string, without quotes or backslash
-- escapes.
local function get_quoted_string(s, offset, crlf)
  local result = {}
  local i = offset
  assert(s:sub(i, i) == "\"")
  i = i + 1
  while i <= s:len() do
    local c = s:sub(i, i)
    if c == "\"" then
      -- Found the closing quote, done.
      return i + 1, table.concat(result)
    elseif c == "\\" then
      -- This is a quoted-pair ("\" CHAR).
      i = i + 1
      c = s:sub(i, i)
      if c == "" then
        -- No character following.
        error("\\ escape at end of input while parsing quoted-string.")
      end
      -- Only CHAR may follow a backslash.
      if c:byte(1) > 127 then
        error(string.format("Unexpected character with value > 127 (0x%02X) in quoted-string.", c:byte(1)))
      end
    else
      -- This is qdtext, which is TEXT except for '"'.
      -- TEXT is "any OCTET except CTLs, but including LWS," however "a CRLF is
      -- allowed in the definition of TEXT only as part of a header field
      -- continuation." So there are really two definitions of quoted-string,
      -- depending on whether it's in a header field or not. This function does
      -- not allow CRLF.
      c = s:sub(i, i)
      if c ~= "\t" and c:match("^[\0\001-\031\127]$") then
        error(string.format("Unexpected control character in quoted-string: 0x%02X.", c:byte(1)))
      end
    end
    result[#result + 1] = c
    i = i + 1
  end
  return nil
end

-- Returns the index just past the end of LWS.
local function skip_lws(s, pos)
  local _, e

  while true do
    while string.match(s, "^[ \t]", pos) do
      pos = pos + 1
    end
    _, e = string.find(s, "^\r?\n[ \t]", pos)
    if not e then
      return pos
    end
    pos = e + 1
  end
end


local digestauth_required = {"username","realm","nonce","digest-uri","response"}
---Validate an 'options' table, which is passed to a number of the HTTP functions. It is
-- often difficult to track down a mistake in the options table, and requires fiddling
-- with the http.lua source, but this should make that a lot easier.
local function validate_options(options)
  local bad = false

  if(options == nil) then
    return true
  end

  for key, value in pairs(options) do
    if(key == 'timeout') then
      if(type(tonumber(value)) ~= 'number') then
        stdnse.debug1('http: options.timeout contains a non-numeric value')
        bad = true
      end
    elseif(key == 'header') then
      if(type(value) ~= 'table') then
        stdnse.debug1("http: options.header should be a table")
        bad = true
      end
    elseif(key == 'content') then
      if(type(value) ~= 'string' and type(value) ~= 'table') then
        stdnse.debug1("http: options.content should be a string or a table")
        bad = true
      end
    elseif(key == 'cookies') then
      if(type(value) == 'table') then
        for _, cookie in ipairs(value) do
          for cookie_key, cookie_value in pairs(cookie) do
            if(cookie_key == 'name') then
              if(type(cookie_value) ~= 'string') then
                stdnse.debug1("http: options.cookies[i].name should be a string")
                bad = true
              end
            elseif(cookie_key == 'value') then
              if(type(cookie_value) ~= 'string') then
                stdnse.debug1("http: options.cookies[i].value should be a string")
                bad = true
              end
            elseif(cookie_key == 'path') then
              if(type(cookie_value) ~= 'string') then
                stdnse.debug1("http: options.cookies[i].path should be a string")
                bad = true
              end
            elseif(cookie_key == 'expires') then
              if(type(cookie_value) ~= 'string') then
                stdnse.debug1("http: options.cookies[i].expires should be a string")
                bad = true
              end
            elseif(cookie_key == 'max-age') then
              if(type(cookie_value) ~= 'string') then
                stdnse.debug1("http: options.cookies[i].max-age should be a string")
                bad = true
              end
            elseif not (cookie_key == 'httponly' or cookie_key == 'secure') then
              stdnse.debug1("http: Unknown field in cookie table: %s", cookie_key)
              -- Ignore unrecognized attributes (per RFC 6265, Section 5.2)
            end
          end
        end
      elseif(type(value) ~= 'string') then
        stdnse.debug1("http: options.cookies should be a table or a string")
        bad = true
      end
    elseif(key == 'auth') then
      if(type(value) == 'table') then
        if(value['username'] == nil or value['password'] == nil) then
          stdnse.debug1("http: options.auth should contain both a 'username' and a 'password' key")
          bad = true
        end
      else
        stdnse.debug1("http: options.auth should be a table")
        bad = true
      end
    elseif (key == 'digestauth') then
      if(type(value) == 'table') then
        for _,k in ipairs(digestauth_required) do
          if not value[k] then
            stdnse.debug1("http: options.digestauth missing key: %s",k)
            bad = true
            break
          end
        end
      else
        bad = true
        stdnse.debug1("http: options.digestauth should be a table")
      end
    elseif (key == 'ntlmauth') then
      stdnse.debug1("Proceeding with ntlm message")
    elseif(key == 'bypass_cache' or key == 'no_cache' or key == 'no_cache_body'
           or key == 'any_af' or key == "truncated_ok") then
      if(type(value) ~= 'boolean') then
        stdnse.debug1("http: options.%s must be a boolean value", key)
        bad = true
      end
    elseif(key == 'redirect_ok') then
      if(type(value)~= 'function' and type(value)~='boolean' and type(value) ~= 'number') then
        stdnse.debug1("http: options.redirect_ok must be a function or boolean or number")
        bad = true
      end
    elseif(key == 'scheme') then
      if type(value) ~= 'string' then
        stdnse.debug1("http: options.scheme must be a string")
        bad = true
      end
    elseif(key == 'max_body_size') then
      if type(value) ~= 'number' then
        stdnse.debug1("http: options.max_body_size must be a number")
        bad = true
      end
    else
      stdnse.debug1("http: Unknown key in the options table: %s", key)
    end
  end

  return not(bad)
end


-- The following recv functions, and the function <code>next_response</code>
-- follow a common pattern. They each take a <code>partial</code> argument
-- whose value is data that has been read from the socket but not yet used in
-- parsing, and they return as their second return value a new value for
-- <code>partial</code>. The idea is that, for example, in reading from the
-- socket to get the Status-Line, you will probably read too much and read part
-- of the header. That part (the "partial") has to be retained when you go to
-- parse the header. The common use pattern is this:
-- <code>
-- local partial
-- status_line, partial = recv_line(socket, partial)
-- ...
-- header, partial = recv_header(socket, partial)
-- ...
-- </code>
-- On error, the functions return <code>nil</code>, the second return value
-- is an error message, and the third value is an unfinished fragment of
-- the response body (if any):
-- <code>
-- body, partial, fragment = recv_body(socket, partial)
-- if not body then
--   stdnse.debug1("Error encountered: %s", partial)
--   stdnse.debug1("Only %d bytes of the body received", (#fragment or 0))
-- end
-- ...
-- </code>

-- Receive a single line (up to <code>\n</code>).
local function recv_line(s, partial)
  local _, e
  local status, data
  local pos

  partial = partial or ""

  pos = 1
  while true do
    _, e = string.find(partial, "\n", pos, true)
    if e then
      break
    end
    status, data = s:receive()
    if not status then
      return status, data
    end
    pos = #partial
    partial = partial .. data
  end

  return string.sub(partial, 1, e), string.sub(partial, e + 1)
end

local function line_is_empty(line)
  return line == "\r\n" or line == "\n"
end

-- Receive up to and including the first blank line, but return everything up
-- to and not including the final blank line.
local function recv_header(s, partial)
  local lines = {}

  partial = partial or ""

  while true do
    local line
    line, partial = recv_line(s, partial)
    if not line then
      return line, partial
    end
    if line_is_empty(line) then
      break
    end
    lines[#lines + 1] = line
  end

  return table.concat(lines), partial
end

-- Receive until the connection is closed.
local function recv_all(s, partial, maxlen)
  local parts = {}
  local part = partial or ""
  repeat
    if maxlen then
      maxlen = maxlen - #part
      if maxlen < 0 then
        table.insert(parts, part:sub(1, maxlen - 1))
        return nil, ERR_OVERSIZED_BODY, table.concat(parts)
      end
    end
    table.insert(parts, part)
    local status
    status, part = s:receive()
  until not status
  return table.concat(parts), ""
end

-- Receive exactly <code>length</code> bytes. Returns <code>nil</code> if that
-- many aren't available.
local function recv_length(s, recvlen, partial, maxlen)
  local parts = {}
  local part = partial or ""
  partial = ""
  repeat
    if #part > recvlen then
      partial = part:sub(recvlen + 1)
      part = part:sub(1, recvlen)
    end
    if maxlen then
      maxlen = maxlen - #part
      if maxlen < 0 then
        table.insert(parts, part:sub(1, maxlen - 1))
        return nil, ERR_OVERSIZED_BODY, table.concat(parts)
      end
    end
    table.insert(parts, part)
    recvlen = recvlen - #part
    if recvlen == 0 then
      return table.concat(parts), partial
    end
    local status
    status, part = s:receive()
  until not status
  return nil, part, table.concat(parts)
end

-- Receive until the end of a chunked message body, and return the dechunked
-- body.
local function recv_chunked(s, partial, maxlen)
  local chunks = {}
  repeat
    local line
    line, partial = recv_line(s, partial)
    if not line then
      return nil, "Chunk size not received; " .. partial, table.concat(chunks)
    end

    -- Get the chunk size.
    local pos = skip_space(line)
    local chunklen = line:match("^%x+", pos)
    if not chunklen then
      return nil,
            ("Chunked encoding didn't find hex; got %q."):format(line:sub(pos, pos + 10)),
            table.concat(chunks)
    end
    chunklen = tonumber(chunklen, 16)

    -- Ignore chunk-extensions that may follow here.
    -- RFC 2616, section 2.1 ("Implied *LWS") seems to allow *LWS between the
    -- parts of a chunk-extension, but that is ambiguous. Consider this case:
    -- "1234;a\r\n =1\r\n...". It could be an extension with a chunk-ext-name
    -- of "a" (and no value), and a chunk-data beginning with " =", or it could
    -- be a chunk-ext-name of "a" with a value of "1", and a chunk-data
    -- starting with "...". We don't allow *LWS here, only ( SP | HT ), so the
    -- first interpretation will prevail.

    local chunk, fragment
    chunk, partial, fragment = recv_length(s, chunklen, partial, maxlen)
    if not chunk then
      if partial ~= ERR_OVERSIZED_BODY then
        partial = "Incomplete chunk; " .. partial
      end
      table.insert(chunks, fragment)
      return nil, partial, table.concat(chunks)
    end
    table.insert(chunks, chunk)
    if maxlen then
      maxlen = maxlen - chunklen
    end

    line, partial = recv_line(s, partial)
    if not line then
      -- this warning message was initially an error but was adapted
      -- to support broken servers, such as the Citrix XML Service
      stdnse.debug2("Didn't find CRLF after chunk-data.")
    elseif not string.match(line, "^\r?\n") then
      return nil,
             ("Didn't find CRLF after chunk-data; got %q."):format(line),
             table.concat(chunks)
    end
  until chunklen == 0

  return table.concat(chunks), partial
end

-- Receive a message body, assuming that the header has already been read by
-- <code>recv_header</code>. The handling is sensitive to the request method
-- and the status code of the response.
local function recv_body(s, response, method, partial, maxlen)
  local connection_close, connection_keepalive

  partial = partial or ""

  -- First check for Connection: close and Connection: keep-alive. This is
  -- necessary to handle some servers that don't follow the protocol.
  connection_close = false
  connection_keepalive = false
  if response.header.connection then
    local offset, token
    offset = 0
    while true do
      offset, token = get_token(response.header.connection, offset + 1)
      if not offset then
        break
      end
      if string.lower(token) == "close" then
        connection_close = true
      elseif string.lower(token) == "keep-alive" then
        connection_keepalive = true
      end
    end
  end

  -- See RFC 2616, section 4.4 "Message Length".

  -- 1. Any response message which "MUST NOT" include a message-body (such as
  --    the 1xx, 204, and 304 responses and any response to a HEAD request) is
  --    always terminated by the first empty line after the header fields...
  --
  -- Despite the above, some servers return a body with response to a HEAD
  -- request. So if an HTTP/1.0 server returns a response without Connection:
  -- keep-alive, or any server returns a response with Connection: close, read
  -- whatever's left on the socket (should be zero bytes).
  if string.upper(method) == "HEAD"
    or (response.status >= 100 and response.status <= 199)
    or response.status == 204 or response.status == 304 then
    if connection_close or (response.version == "1.0" and not connection_keepalive) then
      return recv_all(s, partial, maxlen)
    else
      return "", partial
    end
  end

  -- 2. If a Transfer-Encoding header field (section 14.41) is present and has
  --    any value other than "identity", then the transfer-length is defined by
  --    use of the "chunked" transfer-coding (section 3.6), unless the message
  --    is terminated by closing the connection.
  if response.header["transfer-encoding"]
    and response.header["transfer-encoding"] ~= "identity" then
    return recv_chunked(s, partial, maxlen)
  end
  -- The Citrix XML Service sends a wrong "Transfer-Coding" instead of
  -- "Transfer-Encoding".
  if response.header["transfer-coding"]
    and response.header["transfer-coding"] ~= "identity" then
    return recv_chunked(s, partial, maxlen)
  end

  -- 3. If a Content-Length header field (section 14.13) is present, its decimal
  --    value in OCTETs represents both the entity-length and the
  --    transfer-length. The Content-Length header field MUST NOT be sent if
  --    these two lengths are different (i.e., if a Transfer-Encoding header
  --    field is present). If a message is received with both a
  --    Transfer-Encoding header field and a Content-Length header field, the
  --    latter MUST be ignored.
  if response.header["content-length"]  and not response.header["transfer-encoding"] then
    local content_length = tonumber(response.header["content-length"])
    if not content_length then
      return nil, string.format("Content-Length %q is non-numeric", response.header["content-length"])
    end
    return recv_length(s, content_length, partial, maxlen)
  end

  -- 4. If the message uses the media type "multipart/byteranges", and the
  --    transfer-length is not otherwise specified, then this self-delimiting
  --    media type defines the transfer-length. [sic]

  -- Case 4 is unhandled.

  -- 5. By the server closing the connection.
  return recv_all(s, partial, maxlen)
end

-- Sets response["status-line"], response.status, and response.version.
local function parse_status_line(status_line, response)
  response["status-line"] = status_line
  local version, status, reason_phrase = string.match(status_line,
    "^HTTP/(%d+%.%d+) +(%d+)%f[ \r\n] *(.-)\r?\n$")
  if not version then
    return nil, string.format("Error parsing status-line %q.", status_line)
  end
  -- We don't have a use for the reason_phrase; ignore it.
  response.version = version
  response.status = tonumber(status)
  if not response.status then
    return nil, string.format("Status code is not numeric: %s", status)
  end

  return true
end

local parse_set_cookie -- defined farther down

-- Sets response.header and response.rawheader.
local function parse_header(header, response)
  local pos
  local name, words
  local s, e

  response.header = {}
  response.rawheader = stringaux.strsplit("\r?\n", header)
  pos = 1
  while pos <= #header do
    -- Get the field name.
    e, name = get_token(header, pos)
    -- Do not bail out if the header is malformed. Consume the header line
    -- anyway, getting to the next header, but do not create a new entry in
    -- the "header" table.
    if e then
      if header:sub(e, e) ~= ":" then
        name = nil
      end
      pos = e + 1
    end

    -- Skip initial space.
    pos = skip_lws(header, pos)
    -- Get non-space words separated by LWS, then join them with a single space.
    words = {}
    while pos <= #header and not string.match(header, "^\r?\n", pos) do
      s = pos
      while not string.match(header, "^[ \t]", pos) and
        not string.match(header, "^\r?\n", pos) do
        pos = pos + 1
      end
      words[#words + 1] = string.sub(header, s, pos - 1)
      pos = skip_lws(header, pos)
    end

    if name then
      -- Set it in our table.
      name = string.lower(name)
      local value = table.concat(words, " ")
      if response.header[name] then
        -- TODO: delay concatenation until return to avoid resource exhaustion
        response.header[name] = response.header[name] .. ", " .. value
      else
        response.header[name] = value
      end

      -- Update the cookie table if this is a Set-Cookie header
      if name == "set-cookie" then
        local cookie, err = parse_set_cookie(value)
        if cookie then
          response.cookies[#response.cookies + 1] = cookie
        else
          -- Ignore any cookie parsing error
        end
      end
    end

    -- Next field, or end of string. (If not it's an error.)
    s, e = string.find(header, "^\r?\n", pos)
    if not e then
      return nil, string.format("Header field named %q didn't end with CRLF", name)
    end
    pos = e + 1
  end

  return true
end

-- Parse the contents of a Set-Cookie header field.
-- The result is a table of the form
--
-- { name = "NAME", value = "VALUE", Comment = "...", Domain = "...", ... }
--
-- Every key except "name" and "value" is optional.
--
-- This function attempts to support the header parser defined in RFC 6265,
-- Section 5.2.
--
-- This parser used to support quoted strings for cookie and attribute values
-- but this behavior was breaking interoperability.
parse_set_cookie = function (s)
  local name, value
  local _, pos

  local cookie = {}
  s = s:gsub(";", "; ")

  -- Get the NAME=VALUE part.
  _, pos, cookie.name, cookie.value = s:find("^[ \t]*(.-)[ \t]*=[ \t]*(.-)[ \t]*%f[;\0]")
  if not (cookie.name or ""):find("^[^;]+$") then
    return nil, "Can't get cookie name."
  end
  pos = pos + 1

  -- Loop over the attributes.
  while s:sub(pos, pos) == ";" do
    _, pos, name = s:find("[ \t]*(.-)[ \t]*%f[=;\0]", pos + 1)
    pos = pos + 1
    if s:sub(pos, pos) == "=" then
      _, pos, value = s:find("[ \t]*(.-)[ \t]*%f[;\0]", pos + 1)
      pos = pos + 1
    else
      value = ""
    end
    name = name:lower()
    if not (name == "" or name == "name" or name == "value") then
      cookie[name] = value
    end
  end

  return cookie
end

--- Attempt to repeatedly decode HTTP response body according to a given list
-- of named encodings.
--
-- @param body A string representing the raw, undecoded response body.
-- @param encodings A list of encodings (string or table)
-- @param maxlen A size limit for the decoded body
-- @return A decoded body
-- @return A list of encodings that were successfully applied
-- @return A list of encodings that remain to be applied to decode the body
--         completely.
-- @return Error string (if any)
-- @return Partially decoded body. For corrupted encoding, this is the body
--         still undecoded. For oversized body, this is a portion of the decoded
--         body, up to the size limit.
local decode_body = function (body, encodings, maxlen)
  if not encodings then return body end

  if type(encodings) == "string" then
    encodings = stringaux.strsplit("%W+", encodings)
  end
  assert(type(encodings) == "table", "Invalid encoding specification")

  local decoded = {}
  local undecoded = tableaux.tcopy(encodings)
  while #undecoded > 0 do
    local enc = undecoded[1]:lower()
    if enc == "identity" then
      -- do nothing
      table.insert(decoded, table.remove(undecoded, 1))
    elseif enc == "gzip" and have_zlib then
      local stream = zlib.inflate(body)
      local status, newbody = pcall(stream.read, stream,
                                   maxlen and (maxlen + 1) or "*a")
      stream:close()
      if not status then
        return nil, decoded, undecoded,
               ("Corrupted Content-Encoding: " .. enc), body
      end
      table.insert(decoded, table.remove(undecoded, 1))
      newbody = newbody or ""
      if maxlen and #newbody > maxlen then
        return nil, decoded, undecoded, ERR_OVERSIZED_BODY, newbody:sub(1, maxlen)
      end
      body = newbody
    else
      stdnse.debug1("Unsupported Content-Encoding: %s", enc)
      break
    end
  end

  return body, decoded, undecoded
end

-- Read one response from the socket <code>s</code> and return it after
-- parsing.
--
-- In case of an error, an error message and a partially received response
-- (if any) are returned as additional values.
local function next_response(s, method, partial, options)
  local response
  local status_line, header, body, fragment
  local status, err

  partial = partial or ""
  response = {
    status=nil,
    ["status-line"]=nil,
    header={},
    rawheader={},
    cookies={},
    rawbody="",
    body="",
    truncated = nil
  }

  status_line, partial = recv_line(s, partial)
  if not status_line then
    return nil, partial, response
  end

  status, err = parse_status_line(status_line, response)
  if not status then
    return nil, err, response
  end

  header, partial = recv_header(s, partial)
  if not header then
    return nil, partial, response
  end
  status, err = parse_header(header, response)
  if not status then
    return nil, err, response
  end

  options = options or {}
  local maxlen = math.floor(options.max_body_size or MAX_BODY_SIZE)
  if maxlen < 0 then
    maxlen = nil
  end
  local truncated_ok = options.truncated_ok
  if truncated_ok == nil then
    truncated_ok = TRUNCATED_OK
  end

  body, partial, fragment = recv_body(s, response, method, partial, maxlen)
  response.rawbody = body or fragment
  response.body = response.rawbody
  if not body then
    if partial ~= ERR_OVERSIZED_BODY then
      return nil, partial, response
    end
    response.truncated = true
    if not truncated_ok then
      return nil, ("Received " .. ERR_OVERSIZED_BODY), response
    end
  end

  if response.header["content-encoding"] then
    local dcd, undcd
    body, dcd, undcd, err, fragment = decode_body(body, response.header["content-encoding"], maxlen)
    response.body = body or fragment
    response.decoded = dcd
    response.undecoded = undcd
    if not body then
      if err ~= ERR_OVERSIZED_BODY then
        return nil, err, response
      end
      response.truncated = true
      if not truncated_ok then
        return nil, ("Decoded " .. ERR_OVERSIZED_BODY), response
      end
    else
      if response.header["content-length"] then
        response.header["content-length"] = tostring(#body)
      end
    end
    response.header["content-encoding"] = #undcd > 0 and table.concat(undcd, ", ") or nil
  end

  return response, partial
end

--- Tries to extract the max number of requests that should be made on
--  a keep-alive connection based on "Keep-Alive: timeout=xx,max=yy" response
--  header.
--
--  If the value is not available, an arbitrary value is used. If the connection
--  is not explicitly closed by the server, this same value is attempted.
--
--  @param response The HTTP response table
--  @return The max number of requests on a keep-alive connection
local function get_pipeline_limit(response)
  -- Allow users to override this with a script-arg
  local pipeline = tonumber(stdnse.get_script_args({'http.pipeline', 'pipeline'}))

  if pipeline then
    return pipeline
  end

  if response then
    local hdr = response.header or {}
    local opts = stringaux.strsplit("[,%s]+", (hdr.connection or ""):lower())
    if tableaux.contains(opts, "close") then return 1 end
    if response.version >= "1.1" or tableaux.contains(opts, "keep-alive") then
      return 1 + (tonumber((hdr["keep-alive"] or ""):match("max=(%d+)")) or 39)
    end
  end
  return 1
end

--- Builds a string to be added to the request mod_options table
--
--  @param cookies A cookie jar just like the table returned by parse_set_cookie.
--  @param path If the argument exists, only cookies with this path are included in the request
--  @return A string to be added to the mod_options table
local function buildCookies(cookies, path)
  if type(cookies) == 'string' then return cookies end
  local cookie = {}
  for _, ck in ipairs(cookies or {}) do
    local ckpath = ck["path"]
    if not path or not ckpath
      or ckpath == path
      or ckpath:sub(-1) == "/" and ckpath == path:sub(1, ckpath:len())
      or ckpath .. "/" == path:sub(1, ckpath:len()+1)
      then
        cookie[#cookie+1] = ck["name"] .. "=" .. ck["value"]
      end
    end
  return table.concat(cookie, "; ")
end

-- HTTP cache.
-- Cache of GET and HEAD requests. Uses <"host:port:path", record>.
-- record is in the format:
--   result: The result from http.get or http.head
--   last_used: The time the record was last accessed or made.
--   get: Was the result received from a request to get or recently wiped?
--   size: The size of the record, equal to #record.result.body.
local cache = {size = 0};

local function cmp_last_used (r1, r2)
      return (r1.last_used or 0) < (r2.last_used or 0);
end

local arg_max_cache_size = tonumber(stdnse.get_script_args({'http.max-cache-size', 'http-max-cache-size'}) or 1e6);
local function check_size (cache)

  local size = cache.size;

  if size > arg_max_cache_size then
    stdnse.debug1(
        "Current http cache size (%d bytes) exceeds max size of %d",
        size, arg_max_cache_size);
    table.sort(cache, cmp_last_used);

    for i, record in ipairs(cache) do
      if size <= arg_max_cache_size then break end
      local result = record.result;
      if type(result.body) == "string" then
        size = size - record.size;
        record.size, record.get, result.body = 0, false, "";
      end
    end
    cache.size = size;
  end
  stdnse.debug2("Final http cache size (%d bytes) of max size of %d",
      size, arg_max_cache_size);
  return size;
end

-- Unique value to signal value is being retrieved.
-- Also holds <mutex, thread> pairs, working thread is value
local WORKING = setmetatable({}, {__mode = "v"});

local function lookup_cache (method, host, port, path, options)
  if(not(validate_options(options))) then
    return nil
  end

  options = options or {};
  local bypass_cache = options.bypass_cache; -- do not lookup
  local no_cache = options.no_cache; -- do not save result
  local no_cache_body = options.no_cache_body; -- do not save body

  if type(port) == "table" then port = port.number end

  local key = stdnse.get_hostname(host)..":"..port..":"..path;
  local mutex = nmap.mutex(tostring(lookup_cache)..key);

  local state = {
    mutex = mutex,
    key = key,
    method = method,
    bypass_cache = bypass_cache,
    no_cache = no_cache,
    no_cache_body = no_cache_body,
  };

  while true do
    mutex "lock";
    local record = cache[key];
    if bypass_cache or record == nil or method ~= record.method then
      WORKING[mutex] = coroutine.running();
      cache[key], state.old_record = WORKING, record;
      return nil, state;
    elseif record == WORKING then
      local working = WORKING[mutex];
      if working == nil or coroutine.status(working) == "dead" then
        -- thread died before insert_cache could be called
        cache[key] = nil; -- reset
      end
      mutex "done";
    else
      mutex "done";
      record.last_used = os.time();
      return tableaux.tcopy(record.result), state;
    end
  end
end

local function response_is_cacheable(response)
  -- if response.status is nil, then an error must have occurred during the request
  -- and we probably don't want to cache the response
  if not response.status then
    return false
  end

  -- 206 Partial Content. RFC 2616, 1.34: "...a cache that does not support the
  -- Range and Content-Range headers MUST NOT cache 206 (Partial Content)
  -- responses."
  if response.status == 206 then
    return false
  end

  -- RFC 2616, 13.4. "A response received with any [status code other than 200,
  -- 203, 206, 300, 301 or 410] (e.g. status codes 302 and 307) MUST NOT be
  -- returned in a reply to a subsequent request unless there are cache-control
  -- directives or another header(s) that explicitly allow it."
  -- We violate the standard here and allow these other codes to be cached,
  -- with the exceptions listed below.

  -- 401 Unauthorized. Caching this would prevent us from retrieving it later
  -- with the correct credentials.
  if response.status == 401 then
    return false
  end

  -- It is not desirable to cache a truncated response because it could poison
  -- subsequent requests with different options max-body-size or truncated_ok.
  if response.truncated then
    return false
  end

  return true
end

local function insert_cache (state, response)
  local key = assert(state.key);
  local mutex = assert(state.mutex);

  if response == nil or state.no_cache or not response_is_cacheable(response) then
    cache[key] = state.old_record;
  else
    local record = {
      result = tableaux.tcopy(response),
      last_used = os.time(),
      method = state.method,
      size = type(response.body) == "string" and #response.body or 0,
    };
    response = record.result; -- only modify copy
    cache[key], cache[#cache+1] = record, record;
    if state.no_cache_body then
      response.body = "";
    end
    if type(response.body) == "string" then
      cache.size = cache.size + #response.body;
      check_size(cache);
    end
  end
  mutex "done";
end

-- Return true if the given method requires a body in the request. In case no
-- body was supplied we must send "Content-Length: 0".
local function request_method_needs_content_length(method)
  return method == "POST"
end

-- For each of the following request functions, <code>host</code> may either be
-- a string or a table, and <code>port</code> may either be a number or a
-- table.
--
-- The format of the return value is a table with the following structure:
-- {status = 200, status-line = "HTTP/1.1 200 OK", header = {}, rawheader = {}, body ="<html>...</html>"}
-- The header table has an entry for each received header with the header name
-- being the key. The table also has an entry named "status" which contains the
-- http status code of the request.
-- In case of an error, the status is nil, status-line describes the problem,
-- and member "incomplete" contains a partially received response (if any).

local function http_error(status_line, response)
  stdnse.debug2("HTTP response error: %s", status_line)
  return {
    status = nil,
    ["status-line"] = status_line,
    header = {},
    rawheader = {},
    body = nil,
    rawbody = nil,
    truncated = nil,
    incomplete = response
  }
end

--- Build an HTTP request from parameters and return it as a string.
--
-- @param host The host this request is intended for.
-- @param port The port this request is intended for.
-- @param method The method to use.
-- @param path The path for the request.
-- @param options A table of options, which may include the keys:
-- * <code>header</code>: A table containing additional headers to be used for the request.
-- * <code>content</code>: The content of the message (content-length will be added -- set header['Content-Length'] to override)
-- * <code>cookies</code>: A table of cookies in the form returned by <code>parse_set_cookie</code>.
-- * <code>auth</code>: A table containing the keys <code>username</code> and <code>password</code>.
-- @return A request string.
-- @see generic_request
local function build_request(host, port, method, path, options)
  if(not(validate_options(options))) then
    return nil
  end
  options = options or {}

  -- Private copy of the options table, used to add default header fields.
  local mod_options = {
    header = {
      Connection = "close",
      Host = get_host_field(host, port, options.scheme),
      ["User-Agent"]  = USER_AGENT
    }
  }

  if options.cookies then
    local cookies = buildCookies(options.cookies, path)
    if #cookies > 0 then
      mod_options.header["Cookie"] = cookies
    end
  end

  if options.auth and not (options.auth.digest or options.auth.ntlm) then
    local username = options.auth.username
    local password = options.auth.password
    local credentials = "Basic " .. base64.enc(username .. ":" .. password)
    mod_options.header["Authorization"] = credentials
  end

  if options.digestauth then
    local order = {"username", "realm", "nonce", "digest-uri", "algorithm", "response", "qop", "nc", "cnonce"}
    local no_quote = {algorithm=true, qop=true, nc=true}
    local creds = {}
    for _,k in ipairs(order) do
      local v = options.digestauth[k]
      if v then
        if no_quote[k] then
          table.insert(creds, ("%s=%s"):format(k,v))
        else
          if k == "digest-uri" then
            table.insert(creds, ('%s="%s"'):format("uri",v))
          else
            table.insert(creds, ('%s="%s"'):format(k,v))
          end
        end
      end
    end
    local credentials = "Digest "..table.concat(creds, ", ")
    mod_options.header["Authorization"] = credentials
  end

  if options.ntlmauth then
    mod_options.header["Authorization"] = "NTLM " .. base64.enc(options.ntlmauth)
  end


  local body
  -- Build a form submission from a table, like "k1=v1&k2=v2".
  if type(options.content) == "table" then
    local parts = {}
    for k, v in pairs(options.content) do
      parts[#parts + 1] = url.escape(k) .. "=" .. url.escape(v)
    end
    body = table.concat(parts, "&")
    mod_options.header["Content-Type"] = "application/x-www-form-urlencoded"
  elseif options.content then
    body = options.content
  elseif request_method_needs_content_length(method) then
    body = ""
  end
  if body then
    mod_options.header["Content-Length"] = #body
  end

  -- Add any other header fields into the local copy.
  table_augment(mod_options, options)
  -- We concat this string manually to allow null bytes in requests
  local request_line = method.." "..path.." HTTP/1.1"
  local header = {}
  for name, value in pairs(mod_options.header) do
    -- we concat this string manually to allow null bytes in requests
    header[#header + 1] = name..": "..value
  end

  return request_line .. "\r\n" .. table.concat(header, "\r\n") .. "\r\n\r\n" .. (body or "")
end

--- A wrapper for comm.tryssl that strictly obeys options.scheme. If it is
--  "https" then only SSL connection is attempted. If "http" then there is no
--  HTTPS fallback.
local function do_connect(host, port, data, options)
  if options.scheme == "https" or options.scheme == "http" then
    -- If the scheme is specifically requested (e.g.
    -- get_url("https://example.com")) then don't fall back.
    return comm.opencon(host, port, data, {
        timeout = options.timeout,
        any_af = options.any_af,
        proto = (options.scheme == "https" and "ssl" or "tcp"),
        })
  end
  return comm.tryssl(host, port, data, {timeout = options.timeout, any_af = options.any_af})
end

--- Send a string to a host and port and return the HTTP result. This function
-- is like <code>generic_request</code>, to be used when you have a ready-made
-- request, not a collection of request parameters.
--
-- @param host The host to connect to.
-- @param port The port to connect to.
-- @param options A table of other parameters. It may have any of these fields:
-- * <code>timeout</code>: A timeout used for socket operations.
-- * <code>header</code>: A table containing additional headers to be used for the request.
-- * <code>content</code>: The content of the message (content-length will be added -- set header['Content-Length'] to override)
-- * <code>cookies</code>: A table of cookies in the form returned by <code>parse_set_cookie</code>.
-- * <code>auth</code>: A table containing the keys <code>username</code> and <code>password</code>.
-- @return A response table, see module documentation for description.
-- @see generic_request
local function request(host, port, data, options)
  if(not(validate_options(options))) then
    return http_error("Options failed to validate.")
  end
  local method
  local response

  options = options or {}

  if type(port) == 'table' then
    if port.protocol and port.protocol ~= 'tcp' then
      stdnse.debug1("http.request() supports the TCP protocol only, your request to %s cannot be completed.", host)
      return http_error("Unsupported protocol.")
    end
  end

  method = string.match(data, "^(%S+)")

  local socket, partial, opts = do_connect(host, port, data, options)

  if not socket then
    stdnse.debug1("http.request socket error: %s", partial)
    return http_error("Error creating socket.")
  end

  repeat
    local incomplete
    response, partial, incomplete = next_response(socket, method, partial, options)
    if not response then
      return http_error("Error in next_response function; " .. partial, incomplete)
    end
    -- See RFC 2616, sections 8.2.3 and 10.1.1, for the 100 Continue status.
    -- Sometimes a server will tell us to "go ahead" with a POST body before
    -- sending the real response. If we got one of those, skip over it.
  until not (response.status >= 100 and response.status <= 199)

  socket:close()

  -- if SSL was used to retrieve the URL mark this in the response
  response.ssl = ( opts == 'ssl' )

  return response
end

---Do a single request with a given method. The response is returned as the standard
-- response table (see the module documentation).
--
-- The <code>get</code>, <code>head</code>, and <code>post</code> functions are simple
-- wrappers around <code>generic_request</code>.
--
-- Any 1XX (informational) responses are discarded.
--
-- @param host The host to connect to.
-- @param port The port to connect to.
-- @param method The method to use; for example, 'GET', 'HEAD', etc.
-- @param path The path to retrieve.
-- @param options [optional] A table that lets the caller control socket timeouts, HTTP headers, and other parameters. For full documentation, see the module documentation (above).
-- @return A response table, see module documentation for description.
-- @see request
function generic_request(host, port, method, path, options)
  if(not(validate_options(options))) then
    return http_error("Options failed to validate.")
  end

  local digest_auth = options and options.auth and options.auth.digest
  local ntlm_auth = options and options.auth and options.auth.ntlm

  if (digest_auth or ntlm_auth) and not have_ssl then
    stdnse.debug1("http: digest and ntlm auth require openssl.")
  end

  if digest_auth and have_ssl then
    -- If we want to do digest authentication, we have to make an initial
    -- request to get realm, nonce and other fields.
    local options_with_auth_removed = tableaux.tcopy(options)
    options_with_auth_removed["auth"] = nil
    local r = generic_request(host, port, method, path, options_with_auth_removed)
    local h = r.header['www-authenticate']
    if not (r.status and h and h:lower():find("digest.-realm")) then
      stdnse.debug1("http: the target doesn't support digest auth or there was an error during request.")
      return http_error("The target doesn't support digest auth or there was an error during request.")
    end
    -- Compute the response hash
    local dmd5 = sasl.DigestMD5:new(h, options.auth.username, options.auth.password, method, path)
    local _, digest_table = dmd5:calcDigest()
    options.digestauth = digest_table
  end

  if ntlm_auth and have_ssl then

    local custom_options = tableaux.tcopy(options) -- to be sent with the type 1 request
    custom_options["auth"] = nil -- removing the auth options
    -- let's check if the target supports ntlm with a simple get request.
    -- Setting a timeout here other than nil messes up the authentication if this is the first device sending
    -- a request to the server. Don't know why.
    custom_options.timeout = nil
    local response = generic_request(host, port, method, path, custom_options)
    local authentication_header = response.header['www-authenticate']
    -- get back the timeout option.
    custom_options.timeout = options.timeout
    -- cannot deal with truncated responses here
    custom_options.truncated_ok = false
    custom_options.header = options.header or {}
    custom_options.header["Connection"] = "Keep-Alive" -- Keep-Alive headers are needed for authentication.

    if (not authentication_header) or (not response.status) or (not string.find(authentication_header:lower(), "ntlm")) then
      stdnse.debug1("http: the target doesn't support NTLM or there was an error during request.")
      return http_error("The target doesn't support NTLM or there was an error during request.")
    end

    -- ntlm works with three messages. we send a request, it sends
    -- a challenge, we respond to the challenge.
    local hostname = options.auth.hostname or "localhost" -- the hostname to be sent
    local workstation_name = options.auth.workstation_name or "NMAP" -- the workstation name to be sent
    local username = options.auth.username -- the username as specified

    local auth_blob = "NTLMSSP\x00" .. -- NTLM signature
    "\x01\x00\x00\x00" .. -- NTLM Type 1 message
    string.pack("<I4", 0xa208b207) .. -- flags 56, 128, Version, Extended Security, Always Sign, Workstation supplied, Domain Supplied, NTLM Key, OEM, Unicode
    string.pack("<I2I2I4 I2I2I4",#workstation_name, #workstation_name, 40 + #hostname, #hostname, #hostname, 40) .. -- Supplied Domain and Workstation
    string.pack("BB<I2", -- OS version info
    5, 1, 2600) .. -- 5.1.2600
    "\x00\x00\x00\x0f" .. -- OS version info end (static 0x0000000f)
    hostname.. -- HOST NAME
    workstation_name --WORKSTATION name

    custom_options.ntlmauth = auth_blob

    -- check if the protocol is tcp
    if type(port) == 'table' then
      if port.protocol and port.protocol ~= 'tcp' then
        stdnse.debug1("NTLM authentication supports the TCP protocol only, your request to %s cannot be completed.", host)
        return http_error("Unsupported protocol.")
      end
    end

    -- sends the type 1 message.
    local socket, partial, opts = do_connect(host, port, build_request(host, port, method, path, custom_options), options)

    if not socket then
      return http_error("Could not create socket to send type 1 message.")
    end

    repeat
      local incomplete
      response, partial, incomplete = next_response(socket, method, partial, custom_options)
      if not response then
        return http_error("There was error in receiving response of type 1 message; " .. partial, incomplete)
      end
    until not (response.status >= 100 and response.status <= 199)

    authentication_header = response.header['www-authenticate']
    -- take out the challenge
    local type2_response = authentication_header:sub(authentication_header:find(' ')+1, -1)
    local _, message_type, _, _, _, flags_received, challenge= string.unpack("<c8 I4 I2I2I4 I4 c8", base64.dec(type2_response))
    -- check if the response is a type 2 message.
    if message_type ~= 0x02 then
      stdnse.debug1("Expected type 2 message as response.")
      return
    end

    local is_unicode  = ((flags_received & 0x00000001) == 0x00000001) -- 0x00000001 UNICODE Flag
    local is_extended = ((flags_received & 0x00080000) == 0x00080000) -- 0x00080000 Extended Security Flag
    local type_3_flags = 0xa2888206 -- flags 56, 128, Version, Target Info, Extended Security, Always Sign, NTLM Key, OEM

    local lanman, ntlm
    if is_extended then
    -- this essentially calls the new ntlmv2_session_response function in smbauth.lua and returns whatever it returns
      lanman, ntlm = smbauth.get_password_response(nil, username, "", options.auth.password, nil, "ntlmv2_session", challenge, true)
    else
      lanman, ntlm = smbauth.get_password_response(nil, username, "", options.auth.password, nil, "ntlm", challenge, false)
      type_3_flags = type_3_flags - 0x00080000 -- Removing the Extended Security Flag as server doesn't support it.
    end

    local domain = ""
    local session_key = ""

    -- if server supports unicode, then strings are sent in unicode format.
    if is_unicode then
      username = unicode.utf8to16(username)
      hostname = unicode.utf8to16(hostname)
      type_3_flags = type_3_flags - 0x00000001 -- OEM flag is 0x00000002. removing 0x00000001 results in UNICODE flag.
    end

    local BASE_OFFSET = 72 -- Version 3 -- The Session Key<empty in our case>, flags, and OS Version structure are all present.

    auth_blob = string.pack("<z I4 I2I2I4 I2I2I4 I2I2I4 I2I2I4 I2I2I4 I2I2I4 I4 BBI2",
      "NTLMSSP",
      0x00000003,
      #lanman,
      #lanman,
      BASE_OFFSET + #username + #hostname,
      ( #ntlm ),
      ( #ntlm ),
      BASE_OFFSET + #username + #hostname + #lanman,
      #domain,
      #domain,
      BASE_OFFSET,
      #username,
      #username,
      BASE_OFFSET,
      #hostname,
      #hostname,
      BASE_OFFSET + #username,
      #session_key,
      #session_key,
      BASE_OFFSET + #username + #hostname + #lanman + #ntlm,
      type_3_flags,
      5,
      1,
      2600)
    .. "\x00\x00\x00\x0f"
    .. username
    .. hostname
    .. lanman
    .. ntlm

    custom_options.ntlmauth = auth_blob
    socket:send(build_request(host, port, method, path, custom_options))

    repeat
      local incomplete
      response, partial, incomplete = next_response(socket, method, partial, options)
      if not response then
        return http_error("There was error in receiving response of type 3 message; " .. partial, incomplete)
      end
    until not (response.status >= 100 and response.status <= 199)

    socket:close()
    response.ssl = ( opts == 'ssl' )

    return response
  end

  return request(host, port, build_request(host, port, method, path, options), options)
end

---Uploads a file using the PUT method and returns a result table. This is a simple wrapper
-- around <code>generic_request</code>
--
-- @param host The host to connect to.
-- @param port The port to connect to.
-- @param path The path to retrieve.
-- @param options [optional] A table that lets the caller control socket timeouts, HTTP headers, and other parameters. For full documentation, see the module documentation (above).
-- @param putdata The contents of the file to upload
-- @return A response table, see module documentation for description.
-- @see http.generic_request
function put(host, port, path, options, putdata)
  if(not(validate_options(options))) then
    return http_error("Options failed to validate.")
  end
  if ( not(putdata) ) then
    return http_error("No file to PUT.")
  end
  local mod_options = {
    content = putdata,
  }
  table_augment(mod_options, options or {})
  return generic_request(host, port, "PUT", path, mod_options)
end

local function domain (h)
  return (h:match("%..+%..+") or h):lower()
end
-- A battery of tests a URL is subjected to in order to decide if it may be
-- redirected to.
local redirect_ok_rules = {

  -- Check if there's any credentials in the url
  function (url, host, port)
    -- bail if userinfo is present
    return not url.userinfo
  end,

  -- Check if the location is within the domain or host
  --
  -- Notes:
  -- * A domain match must be exact and at least a second-level domain
  -- * ccTLDs are not treated as such. The rule will not stop a redirect
  --   from foo.co.uk to bar.co.uk even though it logically should.
  function (url, host, port)
    local hostname = stdnse.get_hostname(host)
    if hostname == host.ip then
      return url.host == hostname
    end
    return domain(hostname) == domain(url.host)
  end,

  -- Check whether the new location has the same port number
  function (url, host, port)
    -- port fixup, adds default ports 80 and 443 in case no url.port was
    -- defined, we do this based on the url scheme
    local url_port = url.port or get_default_port(url.scheme)
    if not url_port or url_port == port.number then
      return true
    end
    return false
  end,

  -- Check whether the url.scheme matches the port.service
  function (url, host, port)
    -- if url.scheme is present then it must match the scanned port
    if url.scheme and url.port then return true end
    if url.scheme and url.scheme ~= port.service then return false end
    return true
  end,

  -- make sure we're actually being redirected somewhere and not to the same url
  function (url, host, port)
    -- url.path must be set if returning true
    -- path cannot be unchanged unless host has changed
    -- TODO: Since we do not know here what the actual old path was then
    --       the effectiveness of this code is a bit unclear.
    if not url.path then return false end
    if url.path == "/" and url.host == (host.targetname or host.ip) then return false end
    return true
  end,
}

--- Provides the default behavior for HTTP redirects.
--
-- Redirects will be followed unless they:
-- * contain credentials
-- * are on a different domain or host
-- * have a different port number or URI scheme
-- * redirect to the same URI
-- * exceed the maximum number of redirects specified
-- @param host table as received by the action function
-- @param port table as received by the action function
-- @param counter number of redirects to follow.
-- @return a default closure suitable for option "redirect_ok"
function redirect_ok(host, port, counter)
  -- convert a numeric port to a table
  if ( "number" == type(port) ) then
    port = { number = port }
  end
  return function(url)
    if ( counter == 0 ) then return false end
    counter = counter - 1
    for i, rule in ipairs( redirect_ok_rules ) do
      if ( not(rule( url, host, port )) ) then
        --stdnse.debug1("Rule failed: %d", i)
        return false
      end
    end
    return true
  end
end

--- Handles a HTTP redirect
-- @param host table as received by the script action function
-- @param port table as received by the script action function
-- @param path string
-- @param response table as returned by http.get or http.head
-- @return url table as returned by <code>url.parse</code> or nil if there's no
--         redirect taking place
function parse_redirect(host, port, path, response)
  if ( not(tostring(response.status):match("^30[01237]$")) or
       not(response.header) or
       not(response.header.location) ) then
    return nil
  end
  port = ( "number" == type(port) ) and { number = port } or port
  local u = url.parse(response.header.location)
  if ( not(u.host) ) then
    -- we're dealing with a relative url
    u.host = stdnse.get_hostname(host)
  end
  -- do port fixup
  u.port = u.port or get_default_port(u.scheme) or port.number
  if ( not(u.path) ) then
    u.path = "/"
  end
  u.path = url.absolute(path, u.path)
  if ( u.query ) then
    u.path = ("%s?%s"):format( u.path, u.query )
  end
  return u
end

local ret_false = function () return false end
-- Retrieves the correct function to use to validate HTTP redirects
-- @param host table as received by the action function
-- @param port table as received by the action function
-- @param options table as passed to http.get or http.head
-- @return redirect_ok function used to validate HTTP redirects
local function get_redirect_ok(host, port, options)
  if ( options ) then
    if ( options.redirect_ok == false ) then
      return ret_false
    elseif( "function" == type(options.redirect_ok) ) then
      return options.redirect_ok(host, port)
    elseif( type(options.redirect_ok) == "number") then
      return redirect_ok(host, port, options.redirect_ok)
    else
      return redirect_ok(host, port, MAX_REDIRECT_COUNT)
    end
  else
    return redirect_ok(host, port, MAX_REDIRECT_COUNT)
  end
end

---Fetches a resource with a GET request and returns the result as a table.
--
-- This is a simple wrapper around <code>generic_request</code>, with the added
-- benefit of having local caching and support for HTTP redirects. Redirects
-- are followed only if they pass all the validation rules of the redirect_ok
-- function. This function may be overridden by supplying a custom function in
-- the <code>redirect_ok</code> field of the options array. The default
-- function redirects the request if the destination is:
-- * Within the same host or domain
-- * Has the same port number
-- * Stays within the current scheme
-- * Does not exceed <code>MAX_REDIRECT_COUNT</code> count of redirects
--
-- Caching and redirects can be controlled in the <code>options</code> array,
-- see module documentation for more information.
--
-- @param host The host to connect to.
-- @param port The port to connect to.
-- @param path The path to retrieve.
-- @param options [optional] A table that lets the caller control socket
--                timeouts, HTTP headers, and other parameters. For full
--                documentation, see the module documentation (above).
-- @return A response table, see module documentation for description.
-- @see http.generic_request
function get(host, port, path, options)
  if(not(validate_options(options))) then
    return http_error("Options failed to validate.")
  end
  options = options or {}
  local redir_check = get_redirect_ok(host, port, options)
  local response, state, location
  local u = { host = host, port = port, path = path }
  repeat
    response, state = lookup_cache("GET", u.host, u.port, u.path, options);
    if ( response == nil ) then
      response = generic_request(u.host, u.port, "GET", u.path, options)
      insert_cache(state, response);
    end
    u = parse_redirect(host, port, path, response)
    if ( not(u) ) then
      break
    end
    -- Allow redirect to change scheme (e.g. redirect to https)
    options.scheme = u.scheme or options.scheme
    location = location or {}
    table.insert(location, response.header.location)
  until( not(redir_check(u)) )
  response.location = location
  return response
end

---Parses a URL and calls <code>http.get</code> with the result. The URL can contain
-- all the standard fields, protocol://host:port/path
--
-- @param u The URL of the host.
-- @param options [optional] A table that lets the caller control socket timeouts, HTTP headers, and other parameters. For full documentation, see the module documentation (above).
-- @return A response table, see module documentation for description.
-- @see http.get
function get_url( u, options )
  if(not(validate_options(options))) then
    return http_error("Options failed to validate.")
  end
  options = options or {}
  local parsed = url.parse( u )
  local port = {}

  port.service = parsed.scheme
  port.number = parsed.port or get_default_port(parsed.scheme) or 80
  options.scheme = options.scheme or parsed.scheme

  local path = parsed.path or "/"
  if parsed.query then
    path = path .. "?" .. parsed.query
  end

  return get( parsed.host, port, path, options )
end

---Fetches a resource with a HEAD request.
--
-- Like <code>get</code>, this is a simple wrapper around
-- <code>generic_request</code> with response caching. This function also has
-- support for HTTP redirects. Redirects are followed only if they pass all the
-- validation rules of the redirect_ok function. This function may be
-- overridden by supplying a custom function in the <code>redirect_ok</code>
-- field of the options array. The default function redirects the request if
-- the destination is:
-- * Within the same host or domain
-- * Has the same port number
-- * Stays within the current scheme
-- * Does not exceed <code>MAX_REDIRECT_COUNT</code> count of redirects
--
-- Caching and redirects can be controlled in the <code>options</code> array,
-- see module documentation for more information.
--
-- @param host The host to connect to.
-- @param port The port to connect to.
-- @param path The path to retrieve.
-- @param options [optional] A table that lets the caller control socket
--                timeouts, HTTP headers, and other parameters. For full
--                documentation, see the module documentation (above).
-- @return A response table, see module documentation for description.
-- @see http.generic_request
function head(host, port, path, options)
  if(not(validate_options(options))) then
    return http_error("Options failed to validate.")
  end
  options = options or {}
  local redir_check = get_redirect_ok(host, port, options)
  local response, state, location
  local u = { host = host, port = port, path = path }
  repeat
    response, state = lookup_cache("HEAD", u.host, u.port, u.path, options);
    if response == nil then
      response = generic_request(u.host, u.port, "HEAD", u.path, options)
      insert_cache(state, response);
    end
    u = parse_redirect(host, port, path, response)
    if ( not(u) ) then
      break
    end
    -- Allow redirect to change scheme (e.g. redirect to https)
    options.scheme = u.scheme or options.scheme
    location = location or {}
    table.insert(location, response.header.location)
  until( not(redir_check(u)) )
  response.location = location
  return response
end

---Fetches a resource with a POST request.
--
-- Like <code>get</code>, this is a simple wrapper around
-- <code>generic_request</code> except that postdata is handled properly.
--
-- @param host The host to connect to.
-- @param port The port to connect to.
-- @param path The path to retrieve.
-- @param options [optional] A table that lets the caller control socket
--                timeouts, HTTP headers, and other parameters. For full
--                documentation, see the module documentation (above).
-- @param ignored Ignored for backwards compatibility.
-- @param postdata A string or a table of data to be posted. If a table, the
--                 keys and values must be strings, and they will be encoded
--                 into an application/x-www-form-encoded form submission.
-- @return A response table, see module documentation for description.
-- @see http.generic_request
function post( host, port, path, options, ignored, postdata )
  if(not(validate_options(options))) then
    return http_error("Options failed to validate.")
  end
  local mod_options = {
    content = postdata,
  }
  table_augment(mod_options, options or {})
  return generic_request(host, port, "POST", path, mod_options)
end

-- Deprecated pipeline functions
function pGet( host, port, path, options, ignored, allReqs )
  stdnse.debug1("WARNING: pGet() is deprecated. Use pipeline_add() instead.")
  return pipeline_add(path, options, allReqs, 'GET')
end
function pHead( host, port, path, options, ignored, allReqs )
  stdnse.debug1("WARNING: pHead() is deprecated. Use pipeline_add instead.")
  return pipeline_add(path, options, allReqs, 'HEAD')
end
function addPipeline(host, port, path, options, ignored, allReqs, method)
  stdnse.debug1("WARNING: addPipeline() is deprecated! Use pipeline_add instead.")
  return pipeline_add(path, options, allReqs, method)
end
function pipeline(host, port, allReqs)
  stdnse.debug1("WARNING: pipeline() is deprecated. Use pipeline_go() instead.")
  return pipeline_go(host, port, allReqs)
end

---Adds a pending request to the HTTP pipeline.
--
-- The HTTP pipeline is a set of requests that will all be sent at the same
-- time, or as close as the server allows. This allows more efficient code,
-- since requests are automatically buffered and sent simultaneously.
--
-- The <code>all_requests</code> argument contains the current list of queued
-- requests (if this is the first time calling <code>pipeline_add</code>, it
-- should be <code>nil</code>). After adding the request to end of the queue,
-- the queue is returned and can be passed to the next
-- <code>pipeline_add</code> call.
--
-- When all requests have been queued, call <code>pipeline_go</code> with the
-- all_requests table that has been built.
--
-- @param path The path to retrieve.
-- @param options [optional] A table that lets the caller control socket
--                timeouts, HTTP headers, and other parameters. For full
--                documentation, see the module documentation (above).
-- @param all_requests [optional] The current pipeline queue (returned from a
--                     previous <code>add_pipeline</code> call), or nil if it's
--                     the first call.
-- @param method [optional] The HTTP method ('GET', 'HEAD', 'POST', etc).
--                          Default: 'GET'.
-- @return Table with the pipeline requests (plus this new one)
-- @see http.pipeline_go
function pipeline_add(path, options, all_requests, method)
  if not validate_options(options) then
    return nil
  end
  options = tableaux.tcopy(options or {})
  method = method or 'GET'
  all_requests = all_requests or {}
  table.insert(all_requests, {method=method, path=path, options=options})
  return all_requests
end

---Makes sure that a given header is set to a given value. Any existing values
-- of this header are removed.
--
-- @param headers A table of existing headers or nil.
-- @param header to set
-- @param value to set the header to
-- @return An in-place modified table of headers
local function force_header (headers, header, value)
  local headers = headers or {}
  local header_lc = header:lower()
  for h in pairs(headers) do
    if h:lower() == header_lc then
      headers[h] = nil
    end
  end
  headers[header] = value
  return headers
end

local pipeline_comm_opts = {recv_before=false, request_timeout=10000}
---Performs all queued requests in the all_requests variable (created by the
-- <code>pipeline_add</code> function).
--
-- Returns an array of responses, each of which is a table as defined in the
-- module documentation above.
--
-- @param host The host to connect to.
-- @param port The port to connect to.
-- @param all_requests A table with all the previously built pipeline requests
-- @return A list of responses, in the same order as the requests were queued.
--         Each response is a table as described in the module documentation.
--         The response list may be either nil or shorter than expected (up to
--         and including being completely empty) due to communication issues or
--         other errors.
function pipeline_go(host, port, all_requests)
  local responses = {}

  -- Check for an empty set
  if (not all_requests or #all_requests == 0) then
    stdnse.debug1("Warning: empty set of requests passed to http.pipeline_go()")
    return responses
  end
  stdnse.debug1("HTTP pipeline: Total number of requests: " .. #all_requests)

  -- We'll try a first request with keep-alive, just to check if the server
  -- supports it and how many requests we can send into one socket
  local req = all_requests[1]
  req.options.header = force_header(req.options.header, "Connection", "keep-alive")
  local reqstr = build_request(host, port, req.method, req.path, req.options)
  local socket, partial, bopt = comm.tryssl(host, port, reqstr, pipeline_comm_opts)
  if not socket then
    return nil
  end
  local resp
  resp, partial = next_response(socket, req.method, partial, req.options)
  if not resp then
    return responses
  end
  table.insert(responses, resp)
  local connsent = 1

  -- how many requests to send on one connection
  local connlimit = get_pipeline_limit(resp)
  -- how many requests should be sent in a single batch
  local batchlimit = tonumber(stdnse.get_script_args("http.max-pipeline")) or connlimit
  stdnse.debug3("HTTP pipeline: connlimit=%d, batchlimit=%d", connlimit, batchlimit)

  while #responses < #all_requests do
    -- reconnect if necessary
    if connsent >= connlimit or resp.truncated or not socket:get_info() then
      socket:close()
      stdnse.debug3("HTTP pipeline: reconnecting")
      socket:connect(host, port, bopt)
      if not socket then
        return nil
      end
      socket:set_timeout(10000)
      partial = ""
      connsent = 0
    end
    if connlimit > connsent + #all_requests - #responses then
      connlimit = connsent + #all_requests - #responses
    end

    -- determine the current batch size
    local batchsize = connlimit - connsent
    if batchsize > batchlimit then
      batchsize = batchlimit
    end
    stdnse.debug3("HTTP pipeline: batch=%d, conn=%d/%d, resp=%d/%d", batchsize, connsent, connlimit, #responses, #all_requests)

    -- build and send a batch of requests
    local requests = {}
    for i = 1, batchsize do
      local req = all_requests[#responses + i]
      local connmode = connsent + i < connlimit and "keep-alive" or "close"
      req.options.header = force_header(req.options.header, "Connection", connmode)
      table.insert(requests, build_request(host, port, req.method, req.path, req.options))
    end
    socket:send(table.concat(requests))

    -- receive batch responses
    for i = 1, batchsize do
      local req = all_requests[#responses + 1]
      resp, partial = next_response(socket, req.method, partial, req.options)
      if not resp then
        stdnse.debug3("HTTP pipeline: response[%d]: %s", #responses + 1, partial)
        connlimit = connsent + i - 1
        if connlimit == 0 then
          stdnse.debug1("HTTP pipeline: First request on a new connection failed; giving up.");
          return responses
        end
        stdnse.debug1("HTTP pipeline: Received only %d of %d batch responses.\nDecreasing connection limit to %d.", i - 1, batchsize, connlimit)
        break
      end
      table.insert(responses, resp)
      if resp.truncated then break end
    end
    connsent = connsent + batchsize
  end
  socket:close()

  stdnse.debug1("HTTP pipeline: Number of received responses: %d", #responses)
  return responses
end

-- Parsing of specific headers. skip_space and the read_* functions return the
-- byte index following whatever they have just read, or nil on error.

-- Skip whitespace (that has already been folded from LWS). See RFC 2616,
-- section 2.2, definition of LWS.
local function skip_space(s, pos)
  local _

  _, pos = string.find(s, "^[ \t]*", pos)

  return pos + 1
end

-- See RFC 2616, section 2.2.
local function read_token(s, pos)
  local _, token

  pos = skip_space(s, pos)
  -- 1*<any CHAR except CTLs or separators>. CHAR is only byte values 0-127.
  _, pos, token = string.find(s, "^([^\0\001-\031()<>@,;:\\\"/?={} \t%[%]\127-\255]+)", pos)

  if token then
    return pos + 1, token
  else
    return nil
  end
end

-- See RFC 2616, section 2.2. Here we relax the restriction that TEXT may not
-- contain CTLs.
local function read_quoted_string(s, pos)
  local chars = {}

  if string.sub(s, pos, pos) ~= "\"" then
    return nil
  end
  pos = pos + 1
  pos = skip_space(s, pos)
  while pos <= #s and string.sub(s, pos, pos) ~= "\"" do
    local c

    c = string.sub(s, pos, pos)
    if c == "\\" then
      if pos < #s then
        pos = pos + 1
        c = string.sub(s, pos, pos)
      else
        return nil
      end
    end

    chars[#chars + 1] = c
    pos = pos + 1
  end
  if pos > #s or string.sub(s, pos, pos) ~= "\"" then
    return nil
  end

  return pos + 1, table.concat(chars)
end

local function read_token_or_quoted_string(s, pos)
  pos = skip_space(s, pos)
  if string.sub(s, pos, pos) == "\"" then
    return read_quoted_string(s, pos)
  else
    return read_token(s, pos)
  end
end

--- Create a pattern to find a tag
--
-- Case-insensitive search for tags
-- @param tag The name of the tag to find
-- @param endtag Boolean true if you are looking for an end tag, otherwise it will look for a start tag
-- @return A pattern to find the tag
function tag_pattern(tag, endtag)
  if endtag then
    return "</%s*" .. stringaux.ipattern(tag) .. "%f[%s>].->"
  else
    return "<%s*" .. stringaux.ipattern(tag) .. "%f[%s/>].->"
  end
end

---
-- Finds forms in html code
--
-- returns table of found forms, in plaintext.
-- @param body A <code>response.body</code> in which to search for forms
-- @return A list of forms.
function grab_forms(body)
  local forms = {}
  if not body then return forms end
  local form_start_expr = tag_pattern("form")
  local form_end_expr = tag_pattern("form", true)

  local form_opening = string.find(body, form_start_expr)

  while form_opening do
    local form_closing = string.find(body, form_end_expr, form_opening+1)
    if form_closing == nil then --html code contains errors
      break
    end
    forms[#forms+1] = string.sub(body, form_opening, form_closing-1)
    if form_closing+1 <= #body then
      form_opening = string.find(body, form_start_expr, form_closing+1)
    else
      break
    end
  end
  return forms
end

local function get_attr (html, name)
  local lhtml = html:lower()
  local lname = name:lower()
  -- try the attribute-value syntax first
  local _, pos = lhtml:find('%s' .. lname .. '%s*=%s*[^%s]')
  if not pos then
    -- try the empty attribute syntax and, if found,
    -- return zero-length string as its value; nil otherwise
    return lhtml:match('[^%s=]%s+' .. lname .. '[%s/>]') and "" or nil
  end
  local value
  _, value = html:match('^([\'"])(.-)%1', pos)
  if not value then
    value = html:match('^[^%s<>=\'"`]+', pos)
  end
  return slaxml.parser.unescape(value)
end
---
-- Parses a form, that is, finds its action and fields.
-- @param form A plaintext representation of form
-- @return A dictionary with keys: <code>action</code>,
-- <code>method</code> if one is specified, <code>fields</code>
-- which is a list of fields found in the form each of which has a
-- <code>name</code> attribute and <code>type</code> if specified.
function parse_form(form)
  local parsed = {}
  local fields = {}
  local form_action = get_attr(form, "action")
  if form_action then
    parsed["action"] = form_action
  end

  -- determine if the form is using get or post
  local form_method = get_attr(form, "method")
  if form_method then
    parsed["method"] = string.lower(form_method)
  end

  -- get the id of the form
  local form_id = get_attr(form, "id")
  if form_id then
    parsed["id"] = string.lower(form_id)
  end

  -- now identify the fields
  local input_type
  local input_name
  local input_value

  -- first find regular inputs
  for f in string.gmatch(form, tag_pattern("input")) do
    input_type = get_attr(f, "type")
    input_name = get_attr(f, "name")
    input_value = get_attr(f, "value")
    local next_field_index = #fields+1
    if input_name then
      fields[next_field_index] = {}
      fields[next_field_index]["name"] = input_name
      if input_type then
        fields[next_field_index]["type"] = string.lower(input_type)
      end
      if input_value then
        fields[next_field_index]["value"] = input_value
      end
    end
  end

  -- now search for textareas
  for f in string.gmatch(form, tag_pattern("textarea")) do
    input_name = get_attr(f, "name")
    local next_field_index = #fields+1
    if input_name then
      fields[next_field_index] = {}
      fields[next_field_index]["name"] = input_name
      fields[next_field_index]["type"] = "textarea"
    end
  end
  parsed["fields"] = fields
  return parsed
end

local MONTH_MAP = {
  Jan = 1, Feb = 2, Mar = 3, Apr = 4, May = 5, Jun = 6,
  Jul = 7, Aug = 8, Sep = 9, Oct = 10, Nov = 11, Dec = 12
}

--- Parses an HTTP date string
--
-- Supports any of the following formats from section 3.3.1 of RFC 2616:
-- * Sun, 06 Nov 1994 08:49:37 GMT  (RFC 822, updated by RFC 1123)
-- * Sunday, 06-Nov-94 08:49:37 GMT (RFC 850, obsoleted by RFC 1036)
-- * Sun Nov  6 08:49:37 1994       (ANSI C's <code>asctime()</code> format)
-- @param s the date string.
-- @return a table with keys <code>year</code>, <code>month</code>,
-- <code>day</code>, <code>hour</code>, <code>min</code>, <code>sec</code>, and
-- <code>isdst</code>, relative to GMT, suitable for input to
-- <code>os.time</code>.
function parse_date(s)
  local day, month, year, hour, min, sec, tz, month_name

  -- Handle RFC 1123 and 1036 at once.
  day, month_name, year, hour, min, sec, tz = s:match("^%w+, (%d+)[- ](%w+)[- ](%d+) (%d+):(%d+):(%d+) (%w+)$")
  if not day then
    month_name, day, hour, min, sec, year = s:match("%w+ (%w+)  ?(%d+) (%d+):(%d+):(%d+) (%d+)")
    tz = "GMT"
  end
  if not day then
    stdnse.debug1("http.parse_date: can't parse date \"%s\": unknown format.", s)
    return nil
  end
  -- Look up the numeric code for month.
  month = MONTH_MAP[month_name]
  if not month then
    stdnse.debug1("http.parse_date: unknown month name \"%s\".", month_name)
    return nil
  end
  if tz ~= "GMT" then
    stdnse.debug1("http.parse_date: don't know time zone \"%s\", only \"GMT\".", tz)
    return nil
  end
  day = tonumber(day)
  year = tonumber(year)
  hour = tonumber(hour)
  min = tonumber(min)
  sec = tonumber(sec)

  if year < 100 then
    -- Two-digit year. Make a guess.
    if year < 70 then
      year = year + 2000
    else
      year = year + 1900
    end
  end

  return { year = year, month = month, day = day, hour = hour, min = min, sec = sec, isdst = false }
end

-- See RFC 2617, section 1.2. This function returns a table with keys "scheme"
-- and "params".
local function read_auth_challenge(s, pos)
  local scheme, params

  pos, scheme = read_token(s, pos)
  if not scheme then
    return nil
  end

  params = {}
  pos = skip_space(s, pos)
  while pos < #s do
    local name, val
    local tmp_pos

    -- We need to peek ahead at this point. It's possible that we've hit the
    -- end of one challenge and the beginning of another. Section 14.33 says
    -- that the header value can be 1#challenge, in other words several
    -- challenges separated by commas. Because the auth-params are also
    -- separated by commas, the only way we can tell is if we find a token not
    -- followed by an equals sign.
    tmp_pos = pos
    tmp_pos, name = read_token(s, tmp_pos)
    if not name then
      pos = skip_space(s, pos + 1)
      return pos, { scheme = scheme, params = nil }
    end
    tmp_pos = skip_space(s, tmp_pos)
    if string.sub(s, tmp_pos, tmp_pos) ~= "=" then
      -- No equals sign, must be the beginning of another challenge.
      break
    end
    tmp_pos = tmp_pos + 1

    pos = tmp_pos
    pos, val = read_token_or_quoted_string(s, pos)
    if not val then
      return nil
    end
    if params[name] then
      return nil
    end
    params[name] = val
    pos = skip_space(s, pos)
    if string.sub(s, pos, pos) == "," then
      pos = skip_space(s, pos + 1)
      if pos > #s then
        return nil
      end
    end
  end

  return pos, { scheme = scheme, params = params }
end

---Parses the WWW-Authenticate header as described in RFC 2616, section 14.47
-- and RFC 2617, section 1.2.
--
-- The return value is an array of challenges. Each challenge is a table with
-- the keys <code>scheme</code> and <code>params</code>.
-- @param s The header value text.
-- @return An array of challenges, or <code>nil</code> on error.
function parse_www_authenticate(s)
  local challenges = {}
  local pos

  pos = 1
  while pos <= #s do
    local challenge

    pos, challenge = read_auth_challenge(s, pos)
    if not challenge then
      return nil
    end
    challenges[#challenges + 1] = challenge
  end

  return challenges
end


---Take the data returned from a HTTP request and return the status string.
-- Useful for <code>stdnse.debug</code> messages and even advanced output.
--
-- @param data The response table from any HTTP request
-- @return The best status string we could find: either the actual status string, the status code, or <code>"<unknown status>"</code>.
function get_status_string(data)
  -- Make sure we have valid data
  if(data == nil) then
    return "<unknown status>"
  elseif(data['status-line'] == nil) then
    if(data['status'] ~= nil) then
      return data['status']
    end

    return "<unknown status>"
  end

  -- We basically want everything after the space
  local space = string.find(data['status-line'], ' ')
  if(space == nil) then
    return data['status-line']
  else
    return (string.sub(data['status-line'], space + 1)):gsub('\r?\n', '')
  end
end

---Determine whether or not the server supports HEAD.
--
-- Tests by requesting / and verifying that it returns 200, and doesn't return
-- data. We implement the check like this because can't always rely on OPTIONS
-- to tell the truth.
--
-- Note: If <code>identify_404</code> returns a 200 status, HEAD requests
-- should be disabled. Sometimes, servers use a 200 status code with a message
-- explaining that the page wasn't found. In this case, to actually identify
-- a 404 page, we need the full body that a HEAD request doesn't supply.
-- This is determined automatically if the <code>result_404</code> field is
-- set.
--
-- @param host The host object.
-- @param port The port to use.
-- @param result_404 [optional] The result when an unknown page is requested.
--                   This is returned by <code>identify_404</code>. If the 404
--                   page returns a 200 code, then we disable HEAD requests.
-- @param path The path to request; by default, / is used.
-- @return A boolean value: true if HEAD is usable, false otherwise.
-- @return If HEAD is usable, the result of the HEAD request is returned (so
--         potentially, a script can avoid an extra call to HEAD)
function can_use_head(host, port, result_404, path)
  -- If the 404 result is 200, don't use HEAD.
  if(result_404 == 200) then
    return false
  end

  -- Default path
  if(path == nil) then
    path = '/'
  end

  -- Perform a HEAD request and see what happens.
  local data = head( host, port, path )
  if data then
    if data.status and data.status == 302 and data.header and data.header.location then
      stdnse.debug1("HTTP: Warning: Host returned 302 and not 200 when performing HEAD.")
      return false
    end

    if data.status and data.status == 200 and data.header then
      -- check that a body wasn't returned
      if #data.body > 0 then
        stdnse.debug1("HTTP: Warning: Host returned data when performing HEAD.")
        return false
      end

      stdnse.debug1("HTTP: Host supports HEAD.")
      return true, data
    end

    stdnse.debug1("HTTP: Didn't receive expected response to HEAD request (got %s).", get_status_string(data))
    return false
  end

  stdnse.debug1("HTTP: HEAD request completely failed.")
  return false
end

--- Try to remove anything that might change within a 404.
--
-- For example:
-- * A file path (includes URI)
-- * A time
-- * A date
-- * An execution time (numbers in general, really)
--
-- The intention is that two 404 pages from different URIs and taken hours
-- apart should, whenever possible, look the same.
--
-- During this function, we're likely going to over-trim things. This is fine
-- -- we want enough to match on that it'll a) be unique, and b) have the best
-- chance of not changing. Even if we remove bits and pieces from the file, as
-- long as it isn't a significant amount, it'll remain unique.
--
-- One case this doesn't cover is if the server generates a random haiku for
-- the user.
--
-- @param body The body of the page.
function clean_404(body)
  if ( not(body) ) then
    return
  end

  -- Remove anything that looks like time
  body = string.gsub(body, '%d?%d:%d%d:%d%d', "")
  body = string.gsub(body, '%d%d:%d%d', "")
  body = string.gsub(body, 'AM', "")
  body = string.gsub(body, 'am', "")
  body = string.gsub(body, 'PM', "")
  body = string.gsub(body, 'pm', "")

  -- Remove anything that looks like a date (this includes 6 and 8 digit numbers)
  -- (this is probably unnecessary, but it's getting pretty close to 11:59 right now, so you never know!)
  body = string.gsub(body, '%d%d%d%d%d%d%d%d', "") -- 4-digit year (has to go first, because it overlaps 2-digit year)
  body = string.gsub(body, '%d%d%d%d%-%d%d%-%d%d', "")
  body = string.gsub(body, '%d%d%d%d/%d%d/%d%d', "")
  body = string.gsub(body, '%d%d%-%d%d%-%d%d%d%d', "")
  body = string.gsub(body, '%d%d%/%d%d%/%d%d%d%d', "")

  body = string.gsub(body, '%d%d%d%d%d%d', "") -- 2-digit year
  body = string.gsub(body, '%d%d%-%d%d%-%d%d', "")
  body = string.gsub(body, '%d%d%/%d%d%/%d%d', "")

  -- Remove anything that looks like a path (note: this will get the URI too) (note2: this interferes with the date removal above, so it can't be moved up)
  body = string.gsub(body, "/[^ ]+", "") -- Unix - remove everything from a slash till the next space
  body = string.gsub(body, "[a-zA-Z]:\\[^ ]+", "") -- Windows - remove everything from a "x:\" pattern till the next space

  -- If we have SSL available, save us a lot of memory by hashing the page (if SSL isn't available, this will work fine, but
  -- take up more memory). If we're debugging, don't hash (it makes things far harder to debug).
  if(have_ssl and nmap.debugging() == 0) then
    return openssl.md5(body)
  end

  return body
end

local function cache_404_response(host, port, response)
  if type(host) == "table" and host.registry then
    host.registry.http_404 = host.registry.http_404 or {}
    local portnum = port
    if type(port) == "table" then
      portnum = port.number
    end
    host.registry.http_404[portnum] = response
  end
  return table.unpack(response)
end

local bad_responses = { 301, 302, 400, 401, 403, 499, 501, 503 }
local identify_404_get_opts = {redirect_ok=false}
local identify_404_cache_404 = {true, 404}
local identify_404_cache_unknown = {false,
  "Two known 404 pages returned valid and different pages; unable to identify valid response."
}
local identify_404_cache_unknown_folder = {false,
  "Two known 404 pages returned valid and different pages; unable to identify valid response (happened when checking a folder)."
}
local identify_404_cache_200 = {true, 200}
---Try requesting a non-existent file to determine how the server responds to
-- unknown pages ("404 pages")
--
-- This tells us
-- * what to expect when a non-existent page is requested, and
-- * if the server will be impossible to scan.
--
-- If the server responds with a 404 status code, as it is supposed to, then
-- this function simply returns 404. If it contains one of a series of common
-- status codes, including unauthorized, moved, and others, it is returned like
-- a 404.
--
-- I (Ron Bowes) have observed one host that responds differently for three
-- scenarios:
-- * A non-existent page, all lowercase (a login page)
-- * A non-existent page, with uppercase (a weird error page that says,
--   "Filesystem is corrupt.")
-- * A page in a non-existent directory (a login page with different font
--   colours)
--
-- As a result, I've devised three different 404 tests, one to check each of
-- these conditions. They all have to match, the tests can proceed; if any of
-- them are different, we can't check 404s properly.
--
-- @param host The host object.
-- @param port The port to which we are establishing the connection.
-- @return status Did we succeed?
-- @return result If status is false, result is an error message. Otherwise,
--                it's the code to expect (typically, but not necessarily,
--                '404').
-- @return body Body is a hash of the cleaned-up body that can be used when
--              detecting a 404 page that doesn't return a 404 error code.
function identify_404(host, port)
  if type(host) == "table" and host.registry and host.registry.http_404 then
    local portnum = port
    if type(port) == "table" then
      portnum = port.number
    end
    local result = host.registry.http_404[portnum]
    if result then
      return table.unpack(result)
    end
  end
  local data

  -- The URLs used to check 404s
  local URL_404_1 = '/nmaplowercheck' .. os.time(os.date('*t'))
  local URL_404_2 = '/NmapUpperCheck' .. os.time(os.date('*t'))
  local URL_404_3 = '/Nmap/folder/check' .. os.time(os.date('*t'))

  data = get(host, port, URL_404_1, identify_404_get_opts)
  if(data == nil) then
    stdnse.debug1("HTTP: Failed while testing for 404 status code")
    -- do not cache; maybe it will work next time?
    return false, "Failed while testing for 404 error message"
  end

  if(data.status and data.status == 404) then
    stdnse.debug1("HTTP: Host returns proper 404 result.")
    return cache_404_response(host, port, identify_404_cache_404)
  end

  if(data.status and data.status == 200) then
    stdnse.debug1("HTTP: Host returns 200 instead of 404.")

    -- Clean up the body (for example, remove the URI). This makes it easier to validate later
    if(data.body) then
      -- Obtain a couple more 404 pages to test different conditions
      local data2 = get(host, port, URL_404_2)
      local data3 = get(host, port, URL_404_3)
      if(data2 == nil or data3 == nil) then
        stdnse.debug1("HTTP: Failed while testing for extra 404 error messages")
        -- do not cache; maybe it will work next time?
        return false, "Failed while testing for extra 404 error messages"
      end

      -- Check if the return code became something other than 200.
      -- Status code: -1 represents unknown.
      -- If the status is nil or the string "unknown" we switch to -1.
      if(data2.status ~= 200) then
        if(type(data2.status) ~= "number") then
          data2.status = -1
        end
        stdnse.debug1("HTTP: HTTP 404 status changed for second request (became %d).", data2.status)
        return cache_404_response(host, port, {false,
            string.format("HTTP 404 status changed for second request (became %d).", data2.status)
          })
      end

      -- Check if the return code became something other than 200
      if(data3.status ~= 200) then
        if(type(data3.status) ~= "number") then
          data3.status = -1
        end
        stdnse.debug1("HTTP: HTTP 404 status changed for third request (became %d).", data3.status)
        return cache_404_response(host, port, {false,
            string.format("HTTP 404 status changed for third request (became %d).", data3.status)
          })
      end

      -- Check if the returned bodies (once cleaned up) matches the first returned body
      local clean_body  = clean_404(data.body)
      local clean_body2 = clean_404(data2.body)
      local clean_body3 = clean_404(data3.body)
      if(clean_body ~= clean_body2) then
        stdnse.debug1("HTTP: Two known 404 pages returned valid and different pages; unable to identify valid response.")
        stdnse.debug1("HTTP: If you investigate the server and it's possible to clean up the pages, please post to nmap-dev mailing list.")
        return cache_404_response(host, port, identify_404_cache_unknown)
      end

      if(clean_body ~= clean_body3) then
        stdnse.debug1("HTTP: Two known 404 pages returned valid and different pages; unable to identify valid response (happened when checking a folder).")
        stdnse.debug1("HTTP: If you investigate the server and it's possible to clean up the pages, please post to nmap-dev mailing list.")
        return cache_404_response(host, port, identify_404_cache_unknown_folder)
      end

      cache_404_response(host, port, {true, 200, clean_body})
      return true, 200, clean_body
    end

    stdnse.debug1("HTTP: The 200 response didn't contain a body.")
    return cache_404_response(host, port, identify_404_cache_200)
  end

  -- Loop through any expected error codes
  for _,code in pairs(bad_responses) do
    if(data.status and data.status == code) then
      stdnse.debug1("HTTP: Host returns %s instead of 404 File Not Found.", get_status_string(data))
      return cache_404_response(host, port, {true, code})
    end
  end

  stdnse.debug1("Unexpected response returned for 404 check: %s", get_status_string(data))

  return cache_404_response(host, port, {true, data.status})
end

--- Determine whether or not the page that was returned is a 404 page.
--
-- This is actually a pretty simple function, but it's best to keep this logic
-- close to <code>identify_404</code>, since they will generally be used
-- together.
--
-- @param data The data returned by the HTTP request
-- @param result_404 The status code to expect for non-existent pages. This is
--                   returned by <code>identify_404</code>.
-- @param known_404 The 404 page itself, if <code>result_404</code> is 200. If
--                  <code>result_404</code> is something else, this parameter
--                  is ignored and can be set to <code>nil</code>. This is
--                  returned by <code>identify_404</code>.
-- @param page The page being requested (used in error messages).
-- @param displayall [optional] If set to true, don't exclude non-404 errors
--                   (such as 500).
-- @return A boolean value: true if the page appears to exist, and false if it
--         does not.
function page_exists(data, result_404, known_404, page, displayall)
  if(data and data.status) then
    -- Handle the most complicated case first: the "200 Ok" response
    if(data.status == 200) then
      if(result_404 == 200) then
        -- If the 404 response is also "200", deal with it (check if the body matches)
        if(#data.body == 0) then
          -- I observed one server that returned a blank string instead of an error, on some occasions
          stdnse.debug1("HTTP: Page returned a totally empty body; page likely doesn't exist")
          return false
        elseif(clean_404(data.body) ~= known_404) then
          stdnse.debug1("HTTP: Page returned a body that doesn't match known 404 body, therefore it exists (%s)", page)
          return true
        else
          return false
        end
      else
        -- If 404s return something other than 200, and we got a 200, we're good to go
        stdnse.debug1("HTTP: Page was '%s', it exists! (%s)", get_status_string(data), page)
        return true
      end
    else
      -- If the result isn't a 200, check if it's a 404 or returns the same code as a 404 returned
      if(data.status ~= 404 and data.status ~= result_404) then
        -- If this check succeeded, then the page isn't a standard 404 -- it could be a redirect, authentication request, etc. Unless the user
        -- asks for everything (with a script argument), only display 401 Authentication Required here.
        stdnse.debug1("HTTP: Page didn't match the 404 response (%s) (%s)", get_status_string(data), page)

        if(data.status == 401) then -- "Authentication Required"
          return true
        elseif(displayall) then
          return true
        end

        return false
      else
        -- Page was a 404, or looked like a 404
        return false
      end
    end
  else
    stdnse.debug1("HTTP: HTTP request failed (is the host still up?)")
    return false
  end
end

local lowercase = function (p)
  return (p or ''):lower()
end
local safe_string = function (p)
  return p or ''
end
---Check if the response variable contains the given text.
--
-- Response variable could be a return from a http.get, http.post,
-- http.pipeline_go, etc. The text can be:
-- * Part of a header ('content-type', 'text/html', '200 OK', etc)
-- * An entire header ('Content-type: text/html', 'Content-length: 123', etc)
-- * Part of the body
--
-- The search text is treated as a Lua pattern.
--
--@param response The full response table from a HTTP request.
--@param pattern The pattern we're searching for. Don't forget to escape '-',
--               for example, 'Content%-type'. The pattern can also contain
--               captures, like 'abc(.*)def', which will be returned if
--               successful.
--@param case_sensitive [optional] Set to <code>true</code> for case-sensitive
--                      searches. Default: not case sensitive.
--@return result True if the string matched, false otherwise
--@return matches An array of captures from the match, if any
function response_contains(response, pattern, case_sensitive)
  local m

  -- If they're searching for the empty string or nil, it's true
  if(pattern == '' or pattern == nil) then
    return true
  end

  -- Create a function that either lowercases everything or doesn't, depending on case sensitivity
  local case = case_sensitive and safe_string or lowercase

  -- Set the case of the pattern
  pattern = case(pattern)

  -- Check the status line (eg, 'HTTP/1.1 200 OK')
  m = {string.match(case(response['status-line']), pattern)};
  if(m and #m > 0) then
    return true, m
  end

  -- Check the headers
  for _, header in pairs(response['rawheader']) do
    m = {string.match(case(header), pattern)}
    if(m and #m > 0) then
      return true, m
    end
  end

  -- Check the body
  m = {string.match(case(response['body']), pattern)}
  if(m and #m > 0) then
    return true, m
  end

  return false
end

---This function should be called whenever a valid path (a path that doesn't
-- contain a known 404 page) is discovered.
--
-- It will add the path to the registry in several ways, allowing other scripts
-- to take advantage of it in interesting ways.
--
--@param host The host the path was discovered on (not necessarily the host
--            being scanned).
--@param port The port the path was discovered on (not necessarily the port
--            being scanned).
--@param path The path discovered. Calling this more than once with the same
--            path is okay; it'll update the data as much as possible instead
--            of adding a duplicate entry
--@param status [optional] The status code (200, 404, 500, etc). This can be
--              left off if it isn't known.
--@param links_to [optional] A table of paths that this page links to.
--@param linked_from [optional] A table of paths that link to this page.
--@param contenttype [optional] The content-type value for the path, if it's known.
function save_path(host, port, path, status, links_to, linked_from, contenttype)
  -- Make sure we have a proper hostname and port
  host = stdnse.get_hostname(host)
  if(type(port) == 'table') then
    port = port['number']
  end

  -- Parse the path
  local parsed = url.parse(path)

  -- contains both query and fragment
  parsed['raw_querystring'] = parsed['query']

  if parsed['fragment'] then
    parsed['raw_querystring'] = ( parsed['raw_querystring'] or "" ) .. '#' .. parsed['fragment']
  end

  if parsed['raw_querystring'] then
    parsed['path_query'] = parsed['path'] .. '?' .. parsed['raw_querystring']
  else
    parsed['path_query'] = parsed['path']
  end

  -- Split up the query, if necessary
  if(parsed['raw_querystring']) then
    parsed['querystring'] = {}
    local values = stringaux.strsplit('&', parsed['raw_querystring'])
    for i, v in ipairs(values) do
      local name, value = table.unpack(stringaux.strsplit('=', v))
      parsed['querystring'][name] = value
    end
  end

  -- Add to the 'all_pages' key
  stdnse.registry_add_array({parsed['host'] or host, 'www', parsed['port'] or port, 'all_pages'}, parsed['path'])

  -- Add the URL with querystring to all_pages_full_query
  stdnse.registry_add_array({parsed['host'] or host, 'www', parsed['port'] or port, 'all_pages_full_query'}, parsed['path_query'])

  -- Add the URL to a key matching the response code
  if(status) then
    stdnse.registry_add_array({parsed['host'] or host, 'www', parsed['port'] or port, 'status_codes', status}, parsed['path'])
  end

  -- If it's a directory, add it to the directories list; otherwise, add it to the files list
  if(parsed['is_folder']) then
    stdnse.registry_add_array({parsed['host'] or host, 'www', parsed['port'] or port, 'directories'}, parsed['path'])
  else
    stdnse.registry_add_array({parsed['host'] or host, 'www', parsed['port'] or port, 'files'}, parsed['path'])
  end


  -- If we have an extension, add it to the extensions key
  if(parsed['extension']) then
    stdnse.registry_add_array({parsed['host'] or host, 'www', parsed['port'] or port, 'extensions', parsed['extension']}, parsed['path'])
  end

  -- Add an entry for the page and its arguments
  if(parsed['querystring']) then
    -- Add all scripts with a querystring to the 'cgi' and 'cgi_full_query' keys
    stdnse.registry_add_array({parsed['host'] or host, 'www', parsed['port'] or port, 'cgi'}, parsed['path'])
    stdnse.registry_add_array({parsed['host'] or host, 'www', parsed['port'] or port, 'cgi_full_query'}, parsed['path_query'])

    -- Add the query string alone to the registry (probably not necessary)
    stdnse.registry_add_array({parsed['host'] or host, 'www', parsed['port'] or port, 'cgi_querystring', parsed['path'] }, parsed['raw_querystring'])

    -- Add the individual arguments for the page, along with their values
    for key, value in pairs(parsed['querystring']) do
      stdnse.registry_add_array({parsed['host'] or host, 'www', parsed['port'] or port, 'cgi_args', parsed['path']}, parsed['querystring'])
    end
  end

  -- Save the pages it links to
  if(links_to) then
    if(type(links_to) == 'string') then
      links_to = {links_to}
    end

    for _, v in ipairs(links_to) do
      stdnse.registry_add_array({parsed['host'] or host, 'www', parsed['port'] or port, 'links_to', parsed['path_query']}, v)
    end
  end

  -- Save the pages it's linked from (we save these in the 'links_to' key, reversed)
  if(linked_from) then
    if(type(linked_from) == 'string') then
      linked_from = {linked_from}
    end

    for _, v in ipairs(linked_from) do
      stdnse.registry_add_array({parsed['host'] or host, 'www', parsed['port'] or port, 'links_to', v}, parsed['path_query'])
    end
  end

  -- Save it as a content-type, if we have one
  if(contenttype) then
    stdnse.registry_add_array({parsed['host'] or host, 'www', parsed['port'] or port, 'content-type', contenttype}, parsed['path_query'])
  end
end

local unittest = require "unittest"
if not unittest.testing() then
  return _ENV
end

test_suite = unittest.TestSuite:new()

do
  local cookie_tests = {
    { name = "#1198: conflicting attribute name",
      cookie = "JSESSIONID=aaa; name=bbb; value=ccc; attr=ddd",
      parsed = {
        name = "JSESSIONID",
        value = "aaa",
        attr = "ddd",
      }
    },
    { name = "#1171: empty attribute value",
      cookie = "JSESSIONID=aaa; attr1; attr2=; attr3=",
      parsed = {
        name = "JSESSIONID",
        value = "aaa",
        attr1 = "",
        attr2 = "",
        attr3 = "",
      }
    },
    { name = "#1170: quotes present",
      cookie = "aaa=\"b\\\"bb\"; pATH = \"ddd eee\" fff",
      parsed = {
        name = "aaa",
        value = "\"b\\\"bb\"",
        path = "\"ddd eee\" fff"
      }
    },
    { name = "#1169: empty attributes",
      cookie = "JSESSIONID=aaa; ; Path=/;;Secure;",
      parsed = {
        name = "JSESSIONID",
        value = "aaa",
        path = "/",
        secure = ""
      }
    },
    { name = "#844: space in a cookie value",
      cookie = " SESSIONID = IgAAABjN8b3xxxNsLRIiSpHLPn1lE=&IgAAAxxxMT6Bw==&Huawei USG6320&langfrombrows=en-US&copyright=2014 ;secure",
      parsed = {
        name = "SESSIONID",
        value = "IgAAABjN8b3xxxNsLRIiSpHLPn1lE=&IgAAAxxxMT6Bw==&Huawei USG6320&langfrombrows=en-US&copyright=2014",
        secure = ""
      }
    },
    { name = "#866: unexpected attribute",
      cookie = " SID=c98fefa3ad659caa20b89582419bb14f; Max-Age=1200; Version=1",
      parsed = {
        name = "SID",
        value = "c98fefa3ad659caa20b89582419bb14f",
        ["max-age"] = "1200",
        version = "1"
      }
    },
    { name = "#731: trailing semicolon",
      cookie = "session_id=76ca8bc8c19;",
      parsed = {
        name = "session_id",
        value = "76ca8bc8c19"
      }
    },
    { name = "#229: comma is not a delimiter",
      cookie = "c1=aaa; path=/bbb/ccc,ddd/eee",
      parsed = {
        name = "c1",
        value = "aaa",
        path = "/bbb/ccc,ddd/eee"
      }
    },
  }

  for _, test in ipairs(cookie_tests) do
    local parsed = parse_set_cookie(test.cookie)
    test_suite:add_test(unittest.not_nil(parsed), test.name)
    if parsed then
      test_suite:add_test(unittest.keys_equal(parsed, test.parsed), test.name)
    end
  end

  local status_line_tests = {
    { name = "valid status line",
      line = "HTTP/1.0 200 OK\r\n",
      result = true,
      parsed = {
        version = "1.0",
        status = 200,
      }
    },
    { name = "malformed version in status line",
      line = "HTTP/1. 200 OK\r\n",
      result = false,
      parsed = {
        version = nil,
        status = nil,
      }
    },
    { name = "non-integer status code in status line",
      line = "HTTP/1.0 20A OK\r\n",
      result = false,
      parsed = {
        version = "1.0",
        status = nil,
      }
    },
    { name = "missing reason phrase in status line",
      line = "HTTP/1.0 200\r\n",
      result = true,
      parsed = {
        version = "1.0",
        status = 200,
      }
    },
  }

  for _, test in ipairs(status_line_tests) do
    local response = {}
    local result, error = parse_status_line(test.line, response)
    if test.result then
      test_suite:add_test(unittest.not_nil(result), test.name)
    else
      test_suite:add_test(unittest.is_nil(result), test.name)
      test_suite:add_test(unittest.not_nil(error), test.name)
    end
    test_suite:add_test(unittest.equal(response["status-line"], test.line), test.name)
    if result then
      test_suite:add_test(unittest.equal(response.status, test.parsed.status), test.name)
      test_suite:add_test(unittest.equal(response.version, test.parsed.version), test.name)
    end
  end

  local content_encoding_tests = {}
  table.insert(content_encoding_tests,
    { name = "nil encoding list",
      encoding = nil,
      source = "SomePlaintextBody",
      target = "SomePlaintextBody",
      decoded = nil,
      undecoded = nil
    })
  table.insert(content_encoding_tests,
    { name = "no encoding",
      encoding = {},
      source = "SomePlaintextBody",
      target = "SomePlaintextBody",
      decoded = {},
      undecoded = {}
    })
  table.insert(content_encoding_tests,
    { name = "identity encoding",
      encoding = "identity",
      source = "SomePlaintextBody",
      target = "SomePlaintextBody",
      decoded = {"identity"},
      undecoded = {}
    })
  if have_zlib then
    table.insert(content_encoding_tests,
      { name = "gzip encoding",
        encoding = "gzip",
        source = stdnse.fromhex("1f8b0800000000000000f348cdc9c9d75108cf2fca49510400d0c34aec0d000000"),
        target = "Hello, World!",
        decoded = {"gzip"},
        undecoded = {}
      })
    table.insert(content_encoding_tests,
      { name = "corrupted gzip encoding",
        encoding = "gzip",
        source = stdnse.fromhex("2f8b0800000000000000f348cdc9c9d75108cf2fca49510400d0c34aec0d000000"),
        target = nil,
        decoded = {},
        undecoded = {"gzip"},
        err = "Corrupted Content-Encoding: gzip",
        fragment = stdnse.fromhex("2f8b0800000000000000f348cdc9c9d75108cf2fca49510400d0c34aec0d000000")
      })
    table.insert(content_encoding_tests,
      { name = "gzip encoding with maxlen",
        encoding = "gzip",
        source = stdnse.fromhex("1f8b0800000000000000f348cdc9c9d75108cf2fca49510400d0c34aec0d000000"),
        target = "Hello, World!",
        decoded = {"gzip"},
        undecoded = {},
        maxlen = 999
      })
    table.insert(content_encoding_tests,
      { name = "gzip encoding with small maxlen",
        encoding = "gzip",
        source = stdnse.fromhex("1f8b0800000000000000f348cdc9c9d75108cf2fca49510400d0c34aec0d000000"),
        target = nil,
        decoded = {"gzip"},
        undecoded = {},
        maxlen = 4,
        err = ERR_OVERSIZED_BODY,
        fragment = "Hell"
      })
    table.insert(content_encoding_tests,
      { name = "gzip encoding with exact maxlen",
        encoding = "gzip",
        source = stdnse.fromhex("1f8b0800000000000000f348cdc9c9d75108cf2fca49510400d0c34aec0d000000"),
        target = "Hello, World!",
        decoded = {"gzip"},
        undecoded = {},
        maxlen = 13
      })
    table.insert(content_encoding_tests,
      { name = "gzip-encoded empty body",
        encoding = "gzip",
        source = "",
        target = "",
        decoded = {"gzip"},
        undecoded = {},
        maxlen = 999
      })
  end
  table.insert(content_encoding_tests,
    { name = "unknown encoding",
      encoding = "identity, mystery, miracle",
      source = stdnse.fromhex("1f8b0800000000000000f348cdc9c9d75108cf2fca49510400d0c34aec0d000000"),
      target = stdnse.fromhex("1f8b0800000000000000f348cdc9c9d75108cf2fca49510400d0c34aec0d000000"),
      decoded = {"identity"},
      undecoded = {"mystery", "miracle"}
    })
  if have_zlib then
    table.insert(content_encoding_tests,
      { name = "stacked encoding",
        encoding = "identity, gzip, identity",
        source = stdnse.fromhex("1f8b0800000000000000f348cdc9c9d75108cf2fca49510400d0c34aec0d000000"),
        target = "Hello, World!",
        decoded = {"identity", "gzip", "identity"},
        undecoded = {}
      })
  else
    table.insert(content_encoding_tests,
      { name = "stacked encoding",
        encoding = "identity, gzip, identity",
        source = stdnse.fromhex("1f8b0800000000000000f348cdc9c9d75108cf2fca49510400d0c34aec0d000000"),
        target = stdnse.fromhex("1f8b0800000000000000f348cdc9c9d75108cf2fca49510400d0c34aec0d000000"),
        decoded = {"identity"},
        undecoded = {"gzip", "identity"}
      })
  end
  for _, test in ipairs(content_encoding_tests) do
    local body, dcd, undcd, err, fragment = decode_body(test.source, test.encoding, test.maxlen)
    test_suite:add_test(unittest.equal(body, test.target), test.name .. " (body)")
    test_suite:add_test(unittest.identical(dcd, test.decoded), test.name .. " (decoded)")
    test_suite:add_test(unittest.identical(undcd, test.undecoded), test.name .. " (undecoded)")
    test_suite:add_test(unittest.equal(err, test.err), test.name .. " (err)")
    test_suite:add_test(unittest.equal(fragment, test.fragment), test.name .. " (fragment)")
  end

end

return _ENV;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              0?                             h?                             ?                             ~                  }      $         '  H           W         '  s                    '              }                          ;  +           E         A  T                    '           A             0           ?           S           g           v                                          ;                      '                      A                        X           e                               '                        &           c           w                                            I         T  Q            ~      ^            ~      ~            %               A                      '  J           u         
  }                                                                             .      f                                         V                 -           |         '                                    <                 {                V          y        '                                                                     #          @          Q                  A            ,                            Ȁ                          i        A  }        A            $                                                                       ^           ρ      }                                   c            %          l                    ,                            ,                $        A  @          H                                                                       x                              a        ~  n                                                                               c             s       	           E      	          ,	                 @	          J	                 W	           ǂ      	          *
          R
          Z
                 g
           w      
          
          ,       
          
           ;      
                     )        A  H        L  T                 Y           :      ^          q                     ׄ                 ׄ      %           ׄ      C          K                 X                 {           *                                  '  
          
          U
        '  \
          4       k
          
           ߄      
          
           O      
           q      
        '  
           j      
        '  '        A  ,          1          r        A  y        A                    A                    A            1          U        ;  g          ,       v                              A        '  R        A  a          6        '  w                  '            	          !                  A                    A          A                      #          $       (                  ;          A                      3        '  D                            '                    '  
        '             2           0      >           pB      E                 J          a           0      m           @B      t                 y                                                  !        '  L        A  _        '  g          r        '                                          2          k        '          '          A                       B                                     ?        '  H          ^          q                                      '  K        A                    A                      7          q           HC      x                                                    '  	                  \  D        A  S          m                                                       D                                 1        '  b        &          e                                                                                                  ,            2        '  @                 H        '  n                                             C                                                             ̇              u                                    /                 Y                 `                             
              
   <                                 '           C      0                 5          G          _                 f           xC      m                 u                                      '             x                        5                  I                  P                  m                       M                                     '              "                             (C                                   !           B      !                 !          -!                 ?!                 Q!        
   4      ]!          o!          !          "        L  local io = require "io"
local string = require "string"
local table = require "table"

---HTTP Fingerprint files, compiled by Ron Bowes with a special thanks to...
-- o Kevin Johnson (@secureideas) for the fingerprints that come with Yokoso
--   http://yokoso.inguardians.com
-- o Jason H. (@jhaddix) for helping out with a whole pile of fingerprints he's
--   collected
-- o Bob Dooling
-- o Robert Rowley for the awesome open source cms and README checks
--   http://www.irvineunderground.org
--
-- This file is released under the Nmap license; see:
--  https://nmap.org/book/man-legal.html
--
-- @args http-fingerprints.nikto-db-path Looks at the given path for nikto database.
--       The database is expected to be a CSV file structured as nikto "db_tests".
--       It then converts the records in nikto's database into our Lua table format
--       and adds them to our current fingerprints if they don't exist already.
--       Unfortunately, our current implementation has some limitations:
--          * It doesn't support records with more than one 'dontmatch' patterns for
--            a probe.
--          * It doesn't support logical AND for the 'match' patterns.
--          * It doesn't support sending additional headers for a probe.
--       That means, if a nikto fingerprint needs one of the above features, it
--       won't be loaded. At the time of writing this, 6546 out of the 6573 Nikto
--       fingerprints are being loaded successfully.  This runtime Nikto fingerprint integration was suggested by Nikto co-author Chris Sullo as described at http://seclists.org/nmap-dev/2013/q4/292
--
-- Although this format was originally modeled after the Nikto format, that ended
-- up being too restrictive. The current format is a simple Lua table. There are many
-- advantages to this technique; it's powerful, we don't need to write custom parsing
-- code, anybody who codes in Lua can easily add checks, and we can write converters
-- to read Nikto and other formats if we want to.
--
-- The 'fingerprints' table is the key. It's an array of checks that will be run in the
-- order they're given. Each check consists of a path, zero or more matches, output text,
-- and other optional fields. Here are all the currently defined fields:
--
-- fingerprint.probes
-- A list of one or more probes to send to the server. Each probe is either a table containing
-- the key 'path' (and potentially others), or it's a string indicating the path.
--
-- fingerprint.probes[i].path
-- The URI to check, optionally containing GET arguments. This should start with a '/'
-- and, if it's a directory, end with a '/'.
--
-- fingerprint.probes[i].method [optional; default: 'GET'}}]
-- The HTTP method to use when making requests ('GET'}}, 'POST', 'HEAD', 'PUT', 'DELETE', etc
--
-- fingerprint.probes[i].nopipeline [optional; default: false]
-- Do not use HTTP pipelining to send this request.
--
-- fingerprint.probes[i].options [optional]
-- An options table as defined in http.lua. Can be used to provide POST data or
-- override defaults. Note that when HTTP pipelining is used, not all of these
-- options will be used.
--
-- fingerprint.ignore_404 [optional; default: false]
-- If set, the automatic checks for 404 and custom 404 pages are disabled for that check.
-- Every page will be included unless fingerprint.matches.dontmatch excludes it.
--
-- fingerprint.severity [optional; default: 1]
-- Give a severity rating, if it's a vulnerability. The scale is:
-- 1 - Info
-- 2 - Low priority
-- 3 - Warning
-- 4 - Critical
--
-- fingerprint.matches
-- An array of tables, each of which contains three fields. These will be checked, starting
-- from the first, until one is matched. If there is no 'match' text, it will fire as long
-- as the result isn't a 404. This match is not case sensitive.
--
-- fingerprint.matches[i].match
-- A string (specifically, a Lua pattern) that has to be found somewhere in the output to
-- count as a match. The string can be in the status line, in a header, or in the body.
-- In addition to matching, this field can contain captures that'll be included in the
-- output. See: http://lua-users.org/wiki/PatternsTutorial
--
-- fingerprint.matches[i].dontmatch
-- A string (specifically, a lua pattern) that cannot be found somewhere in the output.
-- This takes precedence over any text matched in the 'match' field
--
-- fingerprint.matches[i].output
-- The text to output if this match happens. If the 'match' field contains captures, these
-- captures can be used with \1, \2, etc.
--
-- If you have any questions, feel free to email dev@nmap.org or contact Ron Bowes!
--
-- CHANGELOG:
-- Added 120 new signatures taken from exploit-db.com archives from July 2009 to July 2011 [Paulino Calderon]
-- Added the option to read nikto's database and make use of its fingerprints. [George Chatzisofroniou]
--

fingerprints = {};

------------------------------------------------
----           GENERAL CHECKS               ----
------------------------------------------------
-- These are checks for generic paths, like /wiki, /images, /admin, etc

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/',
        method = 'GET'
      }
    },
    matches = {
      {
        match = '<title>Index of .*(Apache.*) Server at',
        output = 'Root directory w/ listing on \'\\1\''
      },
      {
        match = '<title>Index of',
        output = 'Root directory w/ directory listing'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/blog/',
        method = 'HEAD'
      },
      {
        path = '/weblog/',
        method = 'HEAD'
      },
      {
        path = '/weblogs/',
        method = 'HEAD'
      },
      {
        path = '/wordpress/',
        method = 'HEAD'
      }
    },
    matches = {
      {
        output = 'Blog'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/wiki/',
        method = 'HEAD'
      },
      {
        path = '/mediawiki/',
        method = 'HEAD'
      },
      {
        path = '/wiki/Main_Page',
        method = 'HEAD'
      }
    },
    matches = {
      {
        output = 'Wiki'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/tikiwiki/',
        method = 'HEAD'
      }
    },
    matches = {
      {
        output = 'Tikiwiki'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/cgi-bin/mj_wwwusr',
        method = 'HEAD'
      },
      {
        path = '/majordomo/mj_wwwusr',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Majordomo2 Mailing List'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/j2ee/examples/servlets/',
        method = 'HEAD'
      },
      {
        path = '/j2ee/examples/jsp/',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Oracle j2ee examples'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/dsc/',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Trend Micro Data Loss Prevention Virtual Appliance'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/reg_1.htm',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Polycom IP phone'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/adr.htm',
        method = 'HEAD'
      },
      {
        path = '/line_login.htm?l=1',
        method = 'HEAD'
      },
      {
        path = '/tbook.csv',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Snom IP Phone'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/globalSIPsettings.html',
        method = 'HEAD'
      },
      {
        path = '/SIPsettingsLine1.html',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Aastra IP Phone'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/websvn/',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'WEBSVN Repository'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/repos/',
        method = 'GET'
      },
      {
        path = '/repo/',
        method = 'GET'
      },
      {
        path = '/svn/',
        method = 'GET'
      },
      {
        path = '/cvs/',
        method = 'GET'
      }
    },
    matches = {
      {
        match = 'realm=".-Subversion.-"',
        output = 'Subversion Repository'
      },
      {
        match = '',
        output = 'Possible code repository'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/archiva/index.action',
        method = 'GET'
      },
      {
        path = '/index.action',
        method = 'GET'
      }
    },
    matches = {
      {
        match = '.*">Apache Archiva (.-)</a>',
        output = 'Apache Archiva version \\1'
      },
      {
        match = 'Apache Archiva (%d-%..-)\n',
        output = 'Apache Archiva version \\1'
      },
      {
        match = '<title>Apache Archiva \\',
        output = 'Apache Archiva'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/login.stm',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Belkin G Wireless Router'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/tools_admin.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'D-Link DIR-300'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/bsc_lan.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'D-Link DIR-300, DIR-320, DIR-615 revD'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/Manage.tri',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Linksys WRT54G2'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/logo_t.gif',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = 'IP_SHARER WEB',
        output = 'Belkin/Arris 2307'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '//system.html',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'CMNC-200 IP Camera'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/main_configure.cgi',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Intellinet IP Camera'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/OvCgi/Toolbar.exe',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'HP OpenView Network Node Manager'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/frontend/x3/',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'CPanel'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/awstatstotals/awstatstotals.php',
        method = 'HEAD'
      },
      {
        path = '/awstats/awstatstotals.php',
        method = 'HEAD'
      },
      {
        path = '/awstatstotals.php',
        method = 'HEAD'
      },
      {
        path = '/awstats/index.php',
        method = 'HEAD'
      },
      {
        path = '/awstatstotals/index.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'AWStats Totals'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/egroupware/',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'eGroupware'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/calendar/cal_search.php',
        method = 'HEAD'
      },
      {
        path = '/cal_search.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'ExtCalendar'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/a_viewusers.php',
        method = 'HEAD'
      },
      {
        path = '/aphpkb/',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Andys PHP Knowledgebase'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/webedition/we/include/we_modules/',
        method = 'HEAD'
      },
      {
        path = '/webedition/',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Web Edition'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/Examples/',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Possible documentation files'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/LightNEasy.php?do=login',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'LightNEasy'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/channel_detail.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'DzTube'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/cgi-bin/vcs',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Mitel Audio and Web Conferencing (AWC)'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/ocsreports/',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'OCS Inventory'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/vbseo.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'vBSEO'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/forum/',
        method = 'HEAD'
      },
      {
        path = '/forums/',
        method = 'HEAD'
      },
      {
        path = '/smf/',
        method = 'HEAD'
      },
      {
        path = '/phpbb/',
        method = 'HEAD'
      }
    },
    matches = {
      {
        output = 'Forum'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/manager/',
        method = 'HEAD'
      },
      {
        path = '/admin.php',
        method = 'HEAD'
      },
      {
        path = '/admin/',
        method = 'HEAD'
      },
      {
        path = '/admin/admin/',
        method = 'HEAD'
      },
      {
        path = '/administrator/',
        method = 'HEAD'
      },
      {
        path = '/moderator/',
        method = 'HEAD'
      },
      {
        path = '/webadmin/',
        method = 'HEAD'
      },
      {
        path = '/adminarea/',
        method = 'HEAD'
      },
      {
        path = '/bb-admin/',
        method = 'HEAD'
      },
      {
        path = '/adminLogin/',
        method = 'HEAD'
      },
      {
        path = '/admin_area/',
        method = 'HEAD'
      },
      {
        path = '/panel-administracion/',
        method = 'HEAD'
      },
      {
        path = '/instadmin/',
        method = 'HEAD'
      },
      {
        path = '/memberadmin/',
        method = 'HEAD'
      },
      {
        path = '/administratorlogin/',
        method = 'HEAD'
      },
      {
        path = '/adm/',
        method = 'HEAD'
      },
      {
        path = '/admin/account.php',
        method = 'HEAD'
      },
      {
        path = '/admin/index.php',
        method = 'HEAD'
      },
      {
        path = '/admin/login.php',
        method = 'HEAD'
      },
      {
        path = '/admin/admin.php',
        method = 'HEAD'
      },
      {
        path = '/joomla/administrator',
        method = 'HEAD'
      },
      {
        path = '/login.php',
        method = 'HEAD'
      },
      {
        path = '/admin_area/admin.php',
        method = 'HEAD'
      },
      {
        path = '/admin_area/login.php',
        method = 'HEAD'
      },
      {
        path = '/siteadmin/login.php',
        method = 'HEAD'
      },
      {
        path = '/siteadmin/index.php',
        method = 'HEAD'
      },
      {
        path = '/siteadmin/login.html',
        method = 'HEAD'
      },
      {
        path = '/admin/index.html',
        method = 'HEAD'
      },
      {
        path = '/admin/login.html',
        method = 'HEAD'
      },
      {
        path = '/admin/admin.html',
        method = 'HEAD'
      },
      {
        path = '/admin_area/index.php',
        method = 'HEAD'
      },
      {
        path = '/bb-admin/index.php',
        method = 'HEAD'
      },
      {
        path = '/bb-admin/login.php',
        method = 'HEAD'
      },
      {
        path = '/bb-admin/admin.php',
        method = 'HEAD'
      },
      {
        path = '/admin/home.php',
        method = 'HEAD'
      },
      {
        path = '/admin_area/login.html',
        method = 'HEAD'
      },
      {
        path = '/admin_area/index.html',
        method = 'HEAD'
      },
      {
        path = '/admin/controlpanel.php',
        method = 'HEAD'
      },
      {
        path = '/admincp/',
        method = 'HEAD'
      },
      {
        path = '/admincp/index.asp',
        method = 'HEAD'
      },
      {
        path = '/admincp/index.html',
        method = 'HEAD'
      },
      {
        path = '/admincp/login.php',
        method = 'HEAD'
      },
      {
        path = '/admin/account.html',
        method = 'HEAD'
      },
      {
        path = '/adminpanel.html',
        method = 'HEAD'
      },
      {
        path = '/webadmin.html',
        method = 'HEAD'
      },
      {
        path = '/webadmin/index.html',
        method = 'HEAD'
      },
      {
        path = '/webadmin/admin.html',
        method = 'HEAD'
      },
      {
        path = '/webadmin/login.html',
        method = 'HEAD'
      },
      {
        path = '/admin/admin_login.html',
        method = 'HEAD'
      },
      {
        path = '/admin_login.html',
        method = 'HEAD'
      },
      {
        path = '/panel-administracion/login.html',
        method = 'HEAD'
      },
      {
        path = '/admin/cp.php',
        method = 'HEAD'
      },
      {
        path = '/cp.php',
        method = 'HEAD'
      },
      {
        path = '/administrator/index.php',
        method = 'HEAD'
      },
      {
        path = '/administrator/login.php',
        method = 'HEAD'
      },
      {
        path = '/nsw/admin/login.php',
        method = 'HEAD'
      },
      {
        path = '/webadmin/login.php',
        method = 'HEAD'
      },
      {
        path = '/admin/admin_login.php',
        method = 'HEAD'
      },
      {
        path = '/admin_login.php',
        method = 'HEAD'
      },
      {
        path = '/administrator/account.php',
        method = 'HEAD'
      },
      {
        path = '/administrator.php',
        method = 'HEAD'
      },
      {
        path = '/admin_area/admin.html',
        method = 'HEAD'
      },
      {
        path = '/pages/admin/admin-login.php',
        method = 'HEAD'
      },
      {
        path = '/admin/admin-login.php',
        method = 'HEAD'
      },
      {
        path = '/admin-login.php',
        method = 'HEAD'
      },
      {
        path = '/bb-admin/index.html',
        method = 'HEAD'
      },
      {
        path = '/bb-admin/login.html',
        method = 'HEAD'
      },
      {
        path = '/bb-admin/admin.html',
        method = 'HEAD'
      },
      {
        path = '/admin/home.html',
        method = 'HEAD'
      },
      {
        path = '/modelsearch/login.php',
        method = 'HEAD'
      },
      {
        path = '/moderator.php',
        method = 'HEAD'
      },
      {
        path = '/moderator/login.php',
        method = 'HEAD'
      },
      {
        path = '/moderator/admin.php',
        method = 'HEAD'
      },
      {
        path = '/account.php',
        method = 'HEAD'
      },
      {
        path = '/pages/admin/admin-login.html',
        method = 'HEAD'
      },
      {
        path = '/admin/admin-login.html',
        method = 'HEAD'
      },
      {
        path = '/admin-login.html',
        method = 'HEAD'
      },
      {
        path = '/controlpanel.php',
        method = 'HEAD'
      },
      {
        path = '/admincontrol.php',
        method = 'HEAD'
      },
      {
        path = '/admin/adminLogin.html',
        method = 'HEAD'
      },
      {
        path = '/adminLogin.html',
        method = 'HEAD'
      },
      {
        path = '/home.html',
        method = 'HEAD'
      },
      {
        path = '/rcjakar/admin/login.php',
        method = 'HEAD'
      },
      {
        path = '/adminarea/index.html',
        method = 'HEAD'
      },
      {
        path = '/adminarea/admin.html',
        method = 'HEAD'
      },
      {
        path = '/webadmin.php',
        method = 'HEAD'
      },
      {
        path = '/webadmin/index.php',
        method = 'HEAD'
      },
      {
        path = '/webadmin/admin.php',
        method = 'HEAD'
      },
      {
        path = '/admin/controlpanel.html',
        method = 'HEAD'
      },
      {
        path = '/admin.html',
        method = 'HEAD'
      },
      {
        path = '/admin/cp.html',
        method = 'HEAD'
      },
      {
        path = '/cp.html',
        method = 'HEAD'
      },
      {
        path = '/adminpanel.php',
        method = 'HEAD'
      },
      {
        path = '/moderator.html',
        method = 'HEAD'
      },
      {
        path = '/administrator/index.html',
        method = 'HEAD'
      },
      {
        path = '/administrator/login.html',
        method = 'HEAD'
      },
      {
        path = '/user.html',
        method = 'HEAD'
      },
      {
        path = '/administrator/account.html',
        method = 'HEAD'
      },
      {
        path = '/administrator.html',
        method = 'HEAD'
      },
      {
        path = '/login.html',
        method = 'HEAD'
      },
      {
        path = '/modelsearch/login.html',
        method = 'HEAD'
      },
      {
        path = '/moderator/login.html',
        method = 'HEAD'
      },
      {
        path = '/adminarea/login.html',
        method = 'HEAD'
      },
      {
        path = '/panel-administracion/index.html',
        method = 'HEAD'
      },
      {
        path = '/panel-administracion/admin.html',
        method = 'HEAD'
      },
      {
        path = '/modelsearch/index.html',
        method = 'HEAD'
      },
      {
        path = '/modelsearch/admin.html',
        method = 'HEAD'
      },
      {
        path = '/admincontrol/login.html',
        method = 'HEAD'
      },
      {
        path = '/adm/index.html',
        method = 'HEAD'
      },
      {
        path = '/adm.html',
        method = 'HEAD'
      },
      {
        path = '/moderator/admin.html',
        method = 'HEAD'
      },
      {
        path = '/user.php',
        method = 'HEAD'
      },
      {
        path = '/account.html',
        method = 'HEAD'
      },
      {
        path = '/controlpanel.html',
        method = 'HEAD'
      },
      {
        path = '/admincontrol.html',
        method = 'HEAD'
      },
      {
        path = '/panel-administracion/login.php',
        method = 'HEAD'
      },
      {
        path = '/wp-login.php',
        method = 'HEAD'
      },
      {
        path = '/wp-json',
        method = 'HEAD'
      },
      {
        path = '/adminLogin.php',
        method = 'HEAD'
      },
      {
        path = '/admin/adminLogin.php',
        method = 'HEAD'
      },
      {
        path = '/adminarea/index.php',
        method = 'HEAD'
      },
      {
        path = '/adminarea/admin.php',
        method = 'HEAD'
      },
      {
        path = '/adminarea/login.php',
        method = 'HEAD'
      },
      {
        path = '/panel-administracion/index.php',
        method = 'HEAD'
      },
      {
        path = '/panel-administracion/admin.php',
        method = 'HEAD'
      },
      {
        path = '/modelsearch/index.php',
        method = 'HEAD'
      },
      {
        path = '/modelsearch/admin.php',
        method = 'HEAD'
      },
      {
        path = '/admincontrol/login.php',
        method = 'HEAD'
      },
      {
        path = '/adm/admloginuser.php',
        method = 'HEAD'
      },
      {
        path = '/admloginuser.php',
        method = 'HEAD'
      },
      {
        path = '/admin2.php',
        method = 'HEAD'
      },
      {
        path = '/admin2/login.php',
        method = 'HEAD'
      },
      {
        path = '/admin2/index.php',
        method = 'HEAD'
      },
      {
        path = '/adm/index.php',
        method = 'HEAD'
      },
      {
        path = '/adm.php',
        method = 'HEAD'
      },
      {
        path = '/affiliate.php',
        method = 'HEAD'
      },
      {
        path = '/adm_auth.php',
        method = 'HEAD'
      },
      {
        path = '/memberadmin.php',
        method = 'HEAD'
      },
      {
        path = '/administratorlogin.php',
        method = 'HEAD'
      },
      {
        path = '/account.cfm',
        method = 'HEAD'
      },
      {
        path = '/admin/account.cfm',
        method = 'HEAD'
      },
      {
        path = '/admin/index.cfm',
        method = 'HEAD'
      },
      {
        path = '/admin/login.cfm',
        method = 'HEAD'
      },
      {
        path = '/admin/admin.cfm',
        method = 'HEAD'
      },
      {
        path = '/admin.cfm',
        method = 'HEAD'
      },
      {
        path = '/admin/admin_login.cfm',
        method = 'HEAD'
      },
      {
        path = '/admin_login.cfm',
        method = 'HEAD'
      },
      {
        path = '/adminpanel.cfm',
        method = 'HEAD'
      },
      {
        path = '/admin/controlpanel.cfm',
        method = 'HEAD'
      },
      {
        path = '/admincontrol.cfm',
        method = 'HEAD'
      },
      {
        path = '/panel-administracion/login.cfm',
        method = 'HEAD'
      },
      {
        path = '/admin/cp.cfm',
        method = 'HEAD'
      },
      {
        path = '/pages/admin/admin-login.cfm',
        method = 'HEAD'
      },
      {
        path = '/admincp/index.cfm',
        method = 'HEAD'
      },
      {
        path = '/admincp/login.cfm',
        method = 'HEAD'
      },
      {
        path = '/admin_area/admin.cfm',
        method = 'HEAD'
      },
      {
        path = '/admin_area/login.cfm',
        method = 'HEAD'
      },
      {
        path = '/moderator/login.cfm',
        method = 'HEAD'
      },
      {
        path = '/administrator/login.cfm',
        method = 'HEAD'
      },
      {
        path = '/moderator.cfm',
        method = 'HEAD'
      },
      {
        path = '/modelsearch/index.cfm',
        method = 'HEAD'
      },
      {
        path = '/panel-administracion/admin.cfm',
        method = 'HEAD'
      },
      {
        path = '/adm/admloginuser.cfm',
        method = 'HEAD'
      },
      {
        path = '/adm.cfm',
        method = 'HEAD'
      },
      {
        path = '/adm_auth.cfm',
        method = 'HEAD'
      },
      {
        path = '/administratorlogin.cfm',
        method = 'HEAD'
      },
      {
        path = '/webadmin.cfm',
        method = 'HEAD'
      },
      {
        path = '/webadmin/index.cfm',
        method = 'HEAD'
      },
      {
        path = '/modelsearch/login.cfm',
        method = 'HEAD'
      },
      {
        path = '/login.cfm',
        method = 'HEAD'
      },
      {
        path = '/administrator.cfm',
        method = 'HEAD'
      },
      {
        path = '/administrator/account.cfm',
        method = 'HEAD'
      },
      {
        path = '/adminLogin.cfm',
        method = 'HEAD'
      },
      {
        path = '/siteadmin/login.cfm',
        method = 'HEAD'
      },
      {
        path = '/admin2/index.cfm',
        method = 'HEAD'
      },
      {
        path = '/adm/index.cfm',
        method = 'HEAD'
      },
      {
        path = '/admin_area/index.cfm',
        method = 'HEAD'
      },
      {
        path = '/bb-admin/index.cfm',
        method = 'HEAD'
      },
      {
        path = '/bb-admin/login.cfm',
        method = 'HEAD'
      },
      {
        path = '/bb-admin/admin.cfm',
        method = 'HEAD'
      },
      {
        path = '/siteadmin/index.cfm',
        method = 'HEAD'
      },
      {
        path = '/memberadmin.cfm',
        method = 'HEAD'
      },
      {
        path = '/admin2/login.cfm',
        method = 'HEAD'
      },
      {
        path = '/admloginuser.cfm',
        method = 'HEAD'
      },
      {
        path = '/admincontrol/login.cfm',
        method = 'HEAD'
      },
      {
        path = '/administrator/index.cfm',
        method = 'HEAD'
      },
      {
        path = '/modelsearch/admin.cfm',
        method = 'HEAD'
      },
      {
        path = '/panel-administracion/index.cfm',
        method = 'HEAD'
      },
      {
        path = '/adminarea/login.cfm',
        method = 'HEAD'
      },
      {
        path = '/adminarea/admin.cfm',
        method = 'HEAD'
      },
      {
        path = '/adminarea/index.cfm',
        method = 'HEAD'
      },
      {
        path = '/admin/adminLogin.cfm',
        method = 'HEAD'
      },
      {
        path = '/webadmin/login.cfm',
        method = 'HEAD'
      },
      {
        path = '/webadmin/admin.cfm',
        method = 'HEAD'
      },
      {
        path = '/user.cfm',
        method = 'HEAD'
      },
      {
        path = '/controlpanel.cfm',
        method = 'HEAD'
      },
      {
        path = '/moderator/admin.cfm',
        method = 'HEAD'
      },
      {
        path = '/cp.cfm',
        method = 'HEAD'
      },
      {
        path = '/admin-login.cfm',
        method = 'HEAD'
      },
      {
        path = '/admin/admin-login.cfm',
        method = 'HEAD'
      },
      {
        path = '/admin/home.cfm',
        method = 'HEAD'
      },
      {
        path = '/adm1n/',
        method = 'HEAD'
      },
      {
        path = '/4dm1n/',
        method = 'HEAD'
      },
      {
        path = '/account.asp',
        method = 'HEAD'
      },
      {
        path = '/admin/account.asp',
        method = 'HEAD'
      },
      {
        path = '/admin/index.asp',
        method = 'HEAD'
      },
      {
        path = '/admin/login.asp',
        method = 'HEAD'
      },
      {
        path = '/admin/admin.asp',
        method = 'HEAD'
      },
      {
        path = '/admin_area/admin.asp',
        method = 'HEAD'
      },
      {
        path = '/admin_area/login.asp',
        method = 'HEAD'
      },
      {
        path = '/admin_area/index.asp',
        method = 'HEAD'
      },
      {
        path = '/bb-admin/index.asp',
        method = 'HEAD'
      },
      {
        path = '/bb-admin/login.asp',
        method = 'HEAD'
      },
      {
        path = '/bb-admin/admin.asp',
        method = 'HEAD'
      },
      {
        path = '/admin/home.asp',
        method = 'HEAD'
      },
      {
        path = '/admin/controlpanel.asp',
        method = 'HEAD'
      },
      {
        path = '/admin.asp',
        method = 'HEAD'
      },
      {
        path = '/pages/admin/admin-login.asp',
        method = 'HEAD'
      },
      {
        path = '/admin/admin-login.asp',
        method = 'HEAD'
      },
      {
        path = '/admin-login.asp',
        method = 'HEAD'
      },
      {
        path = '/admin/cp.asp',
        method = 'HEAD'
      },
      {
        path = '/cp.asp',
        method = 'HEAD'
      },
      {
        path = '/administrator/account.asp',
        method = 'HEAD'
      },
      {
        path = '/administrator.asp',
        method = 'HEAD'
      },
      {
        path = '/login.asp',
        method = 'HEAD'
      },
      {
        path = '/modelsearch/login.asp',
        method = 'HEAD'
      },
      {
        path = '/moderator.asp',
        method = 'HEAD'
      },
      {
        path = '/moderator/login.asp',
        method = 'HEAD'
      },
      {
        path = '/administrator/login.asp',
        method = 'HEAD'
      },
      {
        path = '/moderator/admin.asp',
        method = 'HEAD'
      },
      {
        path = '/controlpanel.asp',
        method = 'HEAD'
      },
      {
        path = '/user.asp',
        method = 'HEAD'
      },
      {
        path = '/admincp/login.asp',
        method = 'HEAD'
      },
      {
        path = '/admincontrol.asp',
        method = 'HEAD'
      },
      {
        path = '/adminpanel.asp',
        method = 'HEAD'
      },
      {
        path = '/webadmin.asp',
        method = 'HEAD'
      },
      {
        path = '/webadmin/index.asp',
        method = 'HEAD'
      },
      {
        path = '/webadmin/admin.asp',
        method = 'HEAD'
      },
      {
        path = '/webadmin/login.asp',
        method = 'HEAD'
      },
      {
        path = '/admin/admin_login.asp',
        method = 'HEAD'
      },
      {
        path = '/admin_login.asp',
        method = 'HEAD'
      },
      {
        path = '/panel-administracion/login.asp',
        method = 'HEAD'
      },
      {
        path = '/adminLogin.asp',
        method = 'HEAD'
      },
      {
        path = '/admin/adminLogin.asp',
        method = 'HEAD'
      },
      {
        path = '/home.asp',
        method = 'HEAD'
      },
      {
        path = '/adminarea/index.asp',
        method = 'HEAD'
      },
      {
        path = '/adminarea/admin.asp',
        method = 'HEAD'
      },
      {
        path = '/adminarea/login.asp',
        method = 'HEAD'
      },
      {
        path = '/panel-administracion/index.asp',
        method = 'HEAD'
      },
      {
        path = '/panel-administracion/admin.asp',
        method = 'HEAD'
      },
      {
        path = '/modelsearch/index.asp',
        method = 'HEAD'
      },
      {
        path = '/modelsearch/admin.asp',
        method = 'HEAD'
      },
      {
        path = '/administrator/index.asp',
        method = 'HEAD'
      },
      {
        path = '/admincontrol/login.asp',
        method = 'HEAD'
      },
      {
        path = '/adm/admloginuser.asp',
        method = 'HEAD'
      },
      {
        path = '/admloginuser.asp',
        method = 'HEAD'
      },
      {
        path = '/admin2.asp',
        method = 'HEAD'
      },
      {
        path = '/admin2/login.asp',
        method = 'HEAD'
      },
      {
        path = '/admin2/index.asp',
        method = 'HEAD'
      },
      {
        path = '/adm/index.asp',
        method = 'HEAD'
      },
      {
        path = '/adm.asp',
        method = 'HEAD'
      },
      {
        path = '/adm_auth.asp',
        method = 'HEAD'
      },
      {
        path = '/memberadmin.asp',
        method = 'HEAD'
      },
      {
        path = '/administratorlogin.asp',
        method = 'HEAD'
      },
      {
        path = '/siteadmin/login.asp',
        method = 'HEAD'
      },
      {
        path = '/siteadmin/index.asp',
        method = 'HEAD'
      },
      {
        path = '/account.aspx',
        method = 'HEAD'
      },
      {
        path = '/admin/account.aspx',
        method = 'HEAD'
      },
      {
        path = '/admin/index.aspx',
        method = 'HEAD'
      },
      {
        path = '/admin/login.aspx',
        method = 'HEAD'
      },
      {
        path = '/admin/admin.aspx',
        method = 'HEAD'
      },
      {
        path = '/admin_area/admin.aspx',
        method = 'HEAD'
      },
      {
        path = '/admin_area/login.aspx',
        method = 'HEAD'
      },
      {
        path = '/admin_area/index.aspx',
        method = 'HEAD'
      },
      {
        path = '/bb-admin/index.aspx',
        method = 'HEAD'
      },
      {
        path = '/bb-admin/login.aspx',
        method = 'HEAD'
      },
      {
        path = '/bb-admin/admin.aspx',
        method = 'HEAD'
      },
      {
        path = '/admin/home.aspx',
        method = 'HEAD'
      },
      {
        path = '/admin/controlpanel.aspx',
        method = 'HEAD'
      },
      {
        path = '/admin.aspx',
        method = 'HEAD'
      },
      {
        path = '/pages/admin/admin-login.aspx',
        method = 'HEAD'
      },
      {
        path = '/admin/admin-login.aspx',
        method = 'HEAD'
      },
      {
        path = '/admin-login.aspx',
        method = 'HEAD'
      },
      {
        path = '/admin/cp.aspx',
        method = 'HEAD'
      },
      {
        path = '/cp.aspx',
        method = 'HEAD'
      },
      {
        path = '/administrator/account.aspx',
        method = 'HEAD'
      },
      {
        path = '/administrator.aspx',
        method = 'HEAD'
      },
      {
        path = '/login.aspx',
        method = 'HEAD'
      },
      {
        path = '/modelsearch/login.aspx',
        method = 'HEAD'
      },
      {
        path = '/moderator.aspx',
        method = 'HEAD'
      },
      {
        path = '/moderator/login.aspx',
        method = 'HEAD'
      },
      {
        path = '/administrator/login.aspx',
        method = 'HEAD'
      },
      {
        path = '/moderator/admin.aspx',
        method = 'HEAD'
      },
      {
        path = '/controlpanel.aspx',
        method = 'HEAD'
      },
      {
        path = '/user.aspx',
        method = 'HEAD'
      },
      {
        path = '/admincp/index.aspx',
        method = 'HEAD'
      },
      {
        path = '/admincp/login.aspx',
        method = 'HEAD'
      },
      {
        path = '/admincontrol.aspx',
        method = 'HEAD'
      },
      {
        path = '/adminpanel.aspx',
        method = 'HEAD'
      },
      {
        path = '/webadmin.aspx',
        method = 'HEAD'
      },
      {
        path = '/webadmin/index.aspx',
        method = 'HEAD'
      },
      {
        path = '/webadmin/admin.aspx',
        method = 'HEAD'
      },
      {
        path = '/webadmin/login.aspx',
        method = 'HEAD'
      },
      {
        path = '/admin/admin_login.aspx',
        method = 'HEAD'
      },
      {
        path = '/admin_login.aspx',
        method = 'HEAD'
      },
      {
        path = '/panel-administracion/login.aspx',
        method = 'HEAD'
      },
      {
        path = '/adminLogin.aspx',
        method = 'HEAD'
      },
      {
        path = '/admin/adminLogin.aspx',
        method = 'HEAD'
      },
      {
        path = '/home.aspx',
        method = 'HEAD'
      },
      {
        path = '/adminarea/index.aspx',
        method = 'HEAD'
      },
      {
        path = '/adminarea/admin.aspx',
        method = 'HEAD'
      },
      {
        path = '/adminarea/login.aspx',
        method = 'HEAD'
      },
      {
        path = '/panel-administracion/index.aspx',
        method = 'HEAD'
      },
      {
        path = '/panel-administracion/admin.aspx',
        method = 'HEAD'
      },
      {
        path = '/modelsearch/index.aspx',
        method = 'HEAD'
      },
      {
        path = '/modelsearch/admin.aspx',
        method = 'HEAD'
      },
      {
        path = '/administrator/index.aspx',
        method = 'HEAD'
      },
      {
        path = '/admincontrol/login.aspx',
        method = 'HEAD'
      },
      {
        path = '/adm/admloginuser.aspx',
        method = 'HEAD'
      },
      {
        path = '/admloginuser.aspx',
        method = 'HEAD'
      },
      {
        path = '/admin2.aspx',
        method = 'HEAD'
      },
      {
        path = '/admin2/login.aspx',
        method = 'HEAD'
      },
      {
        path = '/admin2/index.aspx',
        method = 'HEAD'
      },
      {
        path = '/adm/index.aspx',
        method = 'HEAD'
      },
      {
        path = '/adm.aspx',
        method = 'HEAD'
      },
      {
        path = '/adm_auth.aspx',
        method = 'HEAD'
      },
      {
        path = '/memberadmin.aspx',
        method = 'HEAD'
      },
      {
        path = '/administratorlogin.aspx',
        method = 'HEAD'
      },
      {
        path = '/siteadmin/login.aspx',
        method = 'HEAD'
      },
      {
        path = '/siteadmin/index.aspx',
        method = 'HEAD'
      },
      {
        path = '/account.jsp',
        method = 'HEAD'
      },
      {
        path = '/admin/index.jsp',
        method = 'HEAD'
      },
      {
        path = '/admin/login.jsp',
        method = 'HEAD'
      },
      {
        path = '/admin/admin.jsp',
        method = 'HEAD'
      },
      {
        path = '/admin_area/admin.jsp',
        method = 'HEAD'
      },
      {
        path = '/admin_area/login.jsp',
        method = 'HEAD'
      },
      {
        path = '/admin_area/index.jsp',
        method = 'HEAD'
      },
      {
        path = '/bb-admin/index.jsp',
        method = 'HEAD'
      },
      {
        path = '/bb-admin/login.jsp',
        method = 'HEAD'
      },
      {
        path = '/bb-admin/admin.jsp',
        method = 'HEAD'
      },
      {
        path = '/admin/home.jsp',
        method = 'HEAD'
      },
      {
        path = '/admin/controlpanel.jsp',
        method = 'HEAD'
      },
      {
        path = '/admin.jsp',
        method = 'HEAD'
      },
      {
        path = '/pages/admin/admin-login.jsp',
        method = 'HEAD'
      },
      {
        path = '/admin/admin-login.jsp',
        method = 'HEAD'
      },
      {
        path = '/admin-login.jsp',
        method = 'HEAD'
      },
      {
        path = '/admin/cp.jsp',
        method = 'HEAD'
      },
      {
        path = '/cp.jsp',
        method = 'HEAD'
      },
      {
        path = '/administrator/account.jsp',
        method = 'HEAD'
      },
      {
        path = '/administrator.jsp',
        method = 'HEAD'
      },
      {
        path = '/login.jsp',
        method = 'HEAD'
      },
      {
        path = '/modelsearch/login.jsp',
        method = 'HEAD'
      },
      {
        path = '/moderator.jsp',
        method = 'HEAD'
      },
      {
        path = '/moderator/login.jsp',
        method = 'HEAD'
      },
      {
        path = '/administrator/login.jsp',
        method = 'HEAD'
      },
      {
        path = '/moderator/admin.jsp',
        method = 'HEAD'
      },
      {
        path = '/controlpanel.jsp',
        method = 'HEAD'
      },
      {
        path = '/user.jsp',
        method = 'HEAD'
      },
      {
        path = '/admincp/index.jsp',
        method = 'HEAD'
      },
      {
        path = '/admincp/login.jsp',
        method = 'HEAD'
      },
      {
        path = '/admincontrol.jsp',
        method = 'HEAD'
      },
      {
        path = '/admin/account.jsp',
        method = 'HEAD'
      },
      {
        path = '/adminpanel.jsp',
        method = 'HEAD'
      },
      {
        path = '/webadmin.jsp',
        method = 'HEAD'
      },
      {
        path = '/webadmin/index.jsp',
        method = 'HEAD'
      },
      {
        path = '/webadmin/admin.jsp',
        method = 'HEAD'
      },
      {
        path = '/webadmin/login.jsp',
        method = 'HEAD'
      },
      {
        path = '/admin/admin_login.jsp',
        method = 'HEAD'
      },
      {
        path = '/admin_login.jsp',
        method = 'HEAD'
      },
      {
        path = '/panel-administracion/login.jsp',
        method = 'HEAD'
      },
      {
        path = '/adminLogin.jsp',
        method = 'HEAD'
      },
      {
        path = '/admin/adminLogin.jsp',
        method = 'HEAD'
      },
      {
        path = '/home.jsp',
        method = 'HEAD'
      },
      {
        path = '/adminarea/index.jsp',
        method = 'HEAD'
      },
      {
        path = '/adminarea/admin.jsp',
        method = 'HEAD'
      },
      {
        path = '/adminarea/login.jsp',
        method = 'HEAD'
      },
      {
        path = '/panel-administracion/index.jsp',
        method = 'HEAD'
      },
      {
        path = '/panel-administracion/admin.jsp',
        method = 'HEAD'
      },
      {
        path = '/modelsearch/index.jsp',
        method = 'HEAD'
      },
      {
        path = '/modelsearch/admin.jsp',
        method = 'HEAD'
      },
      {
        path = '/administrator/index.jsp',
        method = 'HEAD'
      },
      {
        path = '/admincontrol/login.jsp',
        method = 'HEAD'
      },
      {
        path = '/adm/admloginuser.jsp',
        method = 'HEAD'
      },
      {
        path = '/admloginuser.jsp',
        method = 'HEAD'
      },
      {
        path = '/admin2.jsp',
        method = 'HEAD'
      },
      {
        path = '/admin2/login.jsp',
        method = 'HEAD'
      },
      {
        path = '/admin2/index.jsp',
        method = 'HEAD'
      },
      {
        path = '/adm/index.jsp',
        method = 'HEAD'
      },
      {
        path = '/adm.jsp',
        method = 'HEAD'
      },
      {
        path = '/adm_auth.jsp',
        method = 'HEAD'
      },
      {
        path = '/memberadmin.jsp',
        method = 'HEAD'
      },
      {
        path = '/administratorlogin.jsp',
        method = 'HEAD'
      },
      {
        path = '/siteadmin/login.jsp',
        method = 'HEAD'
      },
      {
        path = '/siteadmin/index.jsp',
        method = 'HEAD'
      },
      {
        path = '/admin1.php',
        method = 'HEAD'
      },
      {
        path = '/administr8.asp',
        method = 'HEAD'
      },
      {
        path = '/administr8.php',
        method = 'HEAD'
      },
      {
        path = '/administr8.jsp',
        method = 'HEAD'
      },
      {
        path = '/administr8.aspx',
        method = 'HEAD'
      },
      {
        path = '/administr8.cfm',
        method = 'HEAD'
      },
      {
        path = '/administr8/',
        method = 'HEAD'
      },
      {
        path = '/administer/',
        method = 'HEAD'
      },
      {
        path = '/administracao.php',
        method = 'HEAD'
      },
      {
        path = '/administracao.asp',
        method = 'HEAD'
      },
      {
        path = '/administracao.aspx',
        method = 'HEAD'
      },
      {
        path = '/administracao.cfm',
        method = 'HEAD'
      },
      {
        path = '/administracao.jsp',
        method = 'HEAD'
      },
      {
        path = '/administracion.php',
        method = 'HEAD'
      },
      {
        path = '/administracion.asp',
        method = 'HEAD'
      },
      {
        path = '/administracion.aspx',
        method = 'HEAD'
      },
      {
        path = '/administracion.jsp',
        method = 'HEAD'
      },
      {
        path = '/administracion.cfm',
        method = 'HEAD'
      },
      {
        path = '/administrators/',
        method = 'HEAD'
      },
      {
        path = '/adminpro/',
        method = 'HEAD'
      },
      {
        path = '/admins/',
        method = 'HEAD'
      },
      {
        path = '/admins.cfm',
        method = 'HEAD'
      },
      {
        path = '/admins.php',
        method = 'HEAD'
      },
      {
        path = '/admins.jsp',
        method = 'HEAD'
      },
      {
        path = '/admins.asp',
        method = 'HEAD'
      },
      {
        path = '/admins.aspx',
        method = 'HEAD'
      },
      {
        path = '/maintenance/',
        method = 'HEAD'
      },
      {
        path = '/Lotus_Domino_Admin/',
        method = 'HEAD'
      },
      {
        path = '/hpwebjetadmin/',
        method = 'HEAD'
      },
      {
        path = '/_admin/',
        method = 'HEAD'
      },
      {
        path = '/_administrator/',
        method = 'HEAD'
      },
      {
        path = '/_administrador/',
        method = 'HEAD'
      },
      {
        path = '/_admins/',
        method = 'HEAD'
      },
      {
        path = '/_administrators/',
        method = 'HEAD'
      },
      {
        path = '/_administradores/',
        method = 'HEAD'
      },
      {
        path = '/_administracion/',
        method = 'HEAD'
      },
      {
        path = '/_4dm1n/',
        method = 'HEAD'
      },
      {
        path = '/_adm1n/',
        method = 'HEAD'
      },
      {
        path = '/_Admin/',
        method = 'HEAD'
      },
      {
        path = '/system_administration/',
        method = 'HEAD'
      },
      {
        path = '/system-administration/',
        method = 'HEAD'
      },
      {
        path = '/system-admin/',
        method = 'HEAD'
      },
      {
        path = '/system-admins/',
        method = 'HEAD'
      },
      {
        path = '/system-administrators/',
        method = 'HEAD'
      },
      {
        path = '/administracion-sistema/',
        method = 'HEAD'
      },
      {
        path = '/Administracion/',
        method = 'HEAD'
      },
      {
        path = '/Admin/',
        method = 'HEAD'
      },
      {
        path = '/Administrator/',
        method = 'HEAD'
      },
      {
        path = '/Manager/',
        method = 'HEAD'
      },
      {
        path = '/Adm/',
        method = 'HEAD'
      },
      {
        path = '/systemadmin/',
        method = 'HEAD'
      },
      {
        path = '/AdminLogin.asp',
        method = 'HEAD'
      },
      {
        path = '/AdminLogin.php',
        method = 'HEAD'
      },
      {
        path = '/AdminLogin.jsp',
        method = 'HEAD'
      },
      {
        path = '/AdminLogin.aspx',
        method = 'HEAD'
      },
      {
        path = '/AdminLogin.cfm',
        method = 'HEAD'
      },
      {
        path = '/admin108/',
        method = 'HEAD'
      },
      {
        path = '/pec_admin/',
        method = 'HEAD'
      },
      {
        path = '/system/admin/',
        method = 'HEAD'
      },
      {
        path = '/plog-admin/',
        method = 'HEAD'
      },
      {
        path = '/ESAdmin/',
        method = 'HEAD'
      },
      {
        path = '/axis2-admin/',
        method = 'HEAD'
      },
      {
        path = '/_sys/',
        method = 'HEAD'
      },
      {
        path = '/admin_cp.asp',
        method = 'HEAD'
      },
      {
        path = '/sitecore/admin/',
        method = 'HEAD'
      },
      {
        path = '/sitecore/login/admin/',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '<title>Index of',
        output = 'Possible admin folder w/ directory listing'
      },
      {
        output = 'Possible admin folder'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/backup/',
        method = 'GET'
      },
      {
        path = '/backup',
        method = 'GET'
      },
      {
        path = '/backup.sql',
        method = 'GET'
      },
      {
        path = '/backup.sql.gz',
        method = 'GET'
      },
      {
        path = '/backup.sql.bz2',
        method = 'GET'
      },
      {
        path = '/backup.zip',
        method = 'GET'
      },
      {
        path = '/backups/',
        method = 'GET'
      },
      {
        path = '/bak/',
        method = 'GET'
      },
      {
        path = '/back/',
        method = 'GET'
      },
      {
        path = '/cache/backup/',
        method = 'GET'
      },
      {
        path = '/admin/backup/',
        method = 'GET'
      },
      {
        path = '/dbbackup.txt',
        method = 'GET'
      }
    },
    matches = {
      {
        match = '<title>Index of',
        output = 'Backup folder w/ directory listing'
      },
      {
        match = '',
        output = 'Possible backup'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/b.sql',
        method = 'HEAD'
      },
      {
        path = '/db.sql',
        method = 'HEAD'
      },
      {
        path = '/ddb.sql',
        method = 'HEAD'
      },
      {
        path = '/users.sql',
        method = 'HEAD'
      },
      {
        path = '/database.sql',
        method = 'HEAD'
      },
      {
        path = '/mysql.sql',
        method = 'HEAD'
      },
      {
        path = '/dump.sql',
        method = 'HEAD'
      },
      {
        path = '/respaldo.sql',
        method = 'HEAD'
      },
      {
        path = '/data.sql',
        method = 'HEAD'
      },
      {
        path = '/old.sql',
        method = 'HEAD'
      },
      {
        path = '/usuarios.sql',
        method = 'HEAD'
      },
      {
        path = '/bdb.sql',
        method = 'HEAD'
      },
      {
        path = '/1.sql',
        method = 'HEAD'
      },
      {
        path = '/admin/download/backup.sql',
        method = 'HEAD'
      }

    },
    matches = {
      {
        match = '',
        output = 'Possible database backup'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/clientaccesspolicy.xml',
        method = 'HEAD'
      },
    },
    matches = {
      {
        output = 'Microsoft Silverlight crossdomain policy'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/atom/',
        method = 'HEAD'
      },
      {
        path = '/atom.aspx',
        method = 'HEAD'
      },
      {
        path = '/atom.php',
        method = 'HEAD'
      },
      {
        path = '/atom.xml',
        method = 'HEAD'
      },
      {
        path = '/atom.jsp',
        method = 'HEAD'
      },
      {
        path = '/rss/',
        method = 'HEAD'
      },
      {
        path = '/rss.aspx',
        method = 'HEAD'
      },
      {
        path = '/rss.php',
        method = 'HEAD'
      },
      {
        path = '/rss.xml',
        method = 'HEAD'
      },
      {
        path = '/rss.jsp',
        method = 'HEAD'
      }
    },
    matches = {
      {
        output = 'RSS or Atom feed'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/etc/passwd',
        method = 'GET'
      },
      {
        path = '/boot.ini',
        method = 'GET'
      }
    },
    matches = {
      {
        match = 'root:',
        output = 'Webroot appears to be in / (Linux)'
      },
      {
        match = 'boot loader',
        output = 'Webroot appears to be in c:\\ (Windows)'
      },
      {
        match = '',
        output = 'Webroot might be in root folder'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/example/',
        method = 'GET'
      },
      {
        path = '/examples/',
        method = 'GET'
      },
      {
        path = '/iissamples/',
        method = 'GET'
      },
      {
        path = '/j2eeexamples/',
        method = 'GET'
      },
      {
        path = '/j2eeexamplesjsp/',
        method = 'GET'
      },
      {
        path = '/sample/',
        method = 'GET'
      },
      {
        path = '/ncsample/',
        method = 'GET'
      },
      {
        path = '/fpsample/',
        method = 'GET'
      },
      {
        path = '/cmsample/',
        method = 'GET'
      },
      {
        path = '/samples/',
        method = 'GET'
      },
      {
        path = '/mono/1.1/index.aspx',
        method = 'GET'
      }
    },
    matches = {
      {
        match = '<title>Index of .*(Apache.*) Server at',
        output = 'Sample scripts w/ listing on \'\\1\''
      },
      {
        match = '<title>Index of',
        output = 'Sample scripts w/ directory listing'
      },
      {
        match = '',
        output = 'Sample scripts'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/login/',
        method = 'HEAD'
      },
      {
        path = '/login.htm',
        method = 'HEAD'
      },
      {
        path = '/login.jsp',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Login page'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/test.asp',
        method = 'HEAD'
      },
      {
        path = '/test.class',
        method = 'HEAD'
      },
      {
        path = '/test/',
        method = 'HEAD'
      },
      {
        path = '/test.htm',
        method = 'HEAD'
      },
      {
        path = '/test.html',
        method = 'HEAD'
      },
      {
        path = '/test.php',
        method = 'HEAD'
      },
      {
        path = '/test.txt',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Test page'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/webmail/',
        method = 'HEAD'
      },
      {
        path = '/mail/',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Mail folder'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/log/',
        method = 'HEAD'
      },
      {
        path = '/log.htm',
        method = 'HEAD'
      },
      {
        path = '/log.php',
        method = 'HEAD'
      },
      {
        path = '/log.asp',
        method = 'HEAD'
      },
      {
        path = '/log.aspx',
        method = 'HEAD'
      },
      {
        path = '/log.jsp',
        method = 'HEAD'
      },
      {
        path = '/logs/',
        method = 'HEAD'
      },
      {
        path = '/logs.htm',
        method = 'HEAD'
      },
      {
        path = '/logs.php',
        method = 'HEAD'
      },
      {
        path = '/logs.asp',
        method = 'HEAD'
      },
      {
        path = '/logs.aspx',
        method = 'HEAD'
      },
      {
        path = '/logs.jsp',
        method = 'HEAD'
      },
      {
        path = '/wwwlog/',
        method = 'HEAD'
      },
      {
        path = '/wwwlogs/',
        method = 'HEAD'
      },
      {
        path = '/mail_log_files/',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Logs'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/images/rails.png',
        method = 'HEAD'
      },
    },
    matches = {
      {
        match = '',
        output = 'Ruby on Rails'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/mono/',
        method = 'HEAD'
      },
    },

    matches = {
      {
        match = '',
        output = 'Mono'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/robots.txt',
        method = 'HEAD'
      },
    },
    matches = {
      {
        match = '',
        output = 'Robots file'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/crossdomain.xml',
        method = 'HEAD'
      },
    },
    matches = {
      {
        output = 'Adobe Flash crossdomain policy'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/css/cake.generic.css',
        method = 'HEAD'
      },
      {
        path = '/img/cake.icon.gif',
        method = 'HEAD'
      },
      {
        path = '/img/cake.icon.png',
        method = 'HEAD'
      },
      {
        path = '/js/vendors.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'CakePHP application'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/cgi-bin/ffileman.cgi?',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Ffileman Web File Manager'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/fshow.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Horizon Web App'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/admin/upload.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Admin File Upload'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/upload_multiple_js.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'NAS Uploader'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/uploadtester.asp',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Free ASP Upload Shell'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/info.php',
        method = 'HEAD'
      },
      {
        path = '/phpinfo.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Possible information file'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/kusabax/manage_page.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Kusabax Image Board'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/plus/lurking.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'phpMyChat Plus'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/adm/barra/assetmanager/assetmanager.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = '360 Web Manager'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/eyeos/',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Possible eyeOS installation'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/NETWARE.HTM',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Planet FPS-1101'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/setup.cgi',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Linksys Cisco Wag120n or similar'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/debug.cgi',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Linksys WRT54G'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/ehcp/?op=applyforftpaccount',
        method = 'HEAD'
      },
      {
        path = '/ehcp/?op=applyforaccount',
        method = 'HEAD'
      },
      {
        path = '/ehcp/?op=applyfordomainaccount',
        method = 'HEAD'
      },
      {
        path = '/vhosts/ehcp/?op=applyforftpaccount',
        method = 'HEAD'
      },
      {
        path = '/vhosts/ehcp/?op=applyforaccount',
        method = 'HEAD'
      },
      {
        path = '/vhosts/ehcp/?op=applyfordomainaccount',
        method = 'HEAD'
      },
    },
    matches = {
      {
        match = '',
        output = 'Easy Hosting Control Panel'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/tools_admin.cgi?',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'D-Link WBR-1310'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/level/15',
        method = 'HEAD'
      },
      {
        path = '/exec/show/log/CR',
        method = 'HEAD'
      },
      {
        path = '/level/15/exec/-/configure/http',
        method = 'HEAD'
      },
      {
        path = '/level/15/exec/-',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = 'cisco-IOS',
        output = 'Cisco 2811'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/appServer/jvmReport.jsf?instanceName=server&pageTitle=JVM%20Report',
        method = 'HEAD'
      },
      {
        path = '/common/appServer/jvmReport.jsf?pageTitle=JVM%20Report',
        method = 'HEAD'
      },
      {
        path = '/common/appServer/jvmReport.jsf?reportType=summary&instanceName=server',
        method = 'HEAD'
      }
   },
    matches = {
      {
        match = '',
        output = 'Oracle GlashFish Server Information'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/console/login/loginForm.jsp',
        method = 'HEAD'
      }
   },
    matches = {
      {
        match = '',
        output = 'Oracle WebLogic Server Administration Console'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/login_img.jpg',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = 'RapidLogic',
        output = 'AIRAYA WirelessGRID'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/cirronetlogo.gif',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = 'Cirronet Wavebolt-AP',
        output = 'Cirronet Wavebolt'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/browserId/wizardForm.jhtml',
        method = 'HEAD'
      },
      {
        path = '/webline/html/forms/callback.jhtml',
        method = 'HEAD'
      },
      {
        path = '/webline/html/forms/callbackICM.jhtml',
        method = 'HEAD'
      },
      {
        path = '/webline/html/agent/AgentFrame.jhtml',
        method = 'HEAD'
      },
      {
        path = '/webline/html/agent/default/badlogin.jhtml',
        method = 'HEAD'
      },
      {
        path = '/callme/callForm.jhtml',
        method = 'HEAD'
      },
      {
        path = '/webline/html/multichatui/nowDefunctWindow.jhtml',
        method = 'HEAD'
      },
      {
        path = '/browserId/wizard.jhtml',
        method = 'HEAD'
      },
      {
        path = '/admin/CiscoAdmin.jhtml',
        method = 'HEAD'
      },
      {
        path = '/msccallme/mscCallForm.jhtml',
        method = 'HEAD'
      },
      {
        path = '/webline/html/admin/wcs/LoginPage.jhtml',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Cisco Collaboration Server'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/restoreinfo.cgi',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Sagem router'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/confirminvite.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'phpMyBitTorrent'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/sourcebans/',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'SourceBans - Steam server application'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/swfupload/index.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'SWFUpload'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/mymarket/shopping/index.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'MyMarket'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/myshop_start.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'FozzCom shopping'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/piranha/secure/passwd.php3',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'RedHat Piranha Virtual Server'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/cgi-bin/ck/mimencode',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'ContentKeeper Web Appliance'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/cgi-bin/masterCGI?',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Alcatel-Lucent OmniPCX Enterprise'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/tiny_mce/plugins/filemanager/',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Tiny MCE File Upload'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/upload/scp/ajax.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'osTicket / AJAX File Upload'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/cgi-mod/view_help.cgi',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Barracuda Networks Spam & Virus Firewall'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/cgi-mod/index.cgi',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Barracuda Web Application Firewall'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/cgi-mod/smtp_test.cgi',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Barracuda IM Firewall'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/TopToolArea.html',
        method = 'HEAD'
      },
      {
        path = '/switchSystem.html',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Alteon OS BBI (Nortell)'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/intruvert/jsp/module/Login.jsp',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'McAfee Network Security Manager'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/ajaxfilemanager/',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'AJAX File Manager'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/upload/data/settings.cdb',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'CF Image Hosting DB'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/fm.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Simple File Manager'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/nagios3/cgi-bin/statuswml.cgi',
        method = 'HEAD'
      },
      {
        path = '/nagios3/',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Nagios3'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/test/logon.html',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Jetty'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/cal_cat.php',
        method = 'HEAD'
      },
      {
        path = '/calendar/cal_cat.php',
        method = 'HEAD'
      },
      {
        path = '/cal/cal_cat.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Calendarix'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/private/sdc.tgz',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'IBM Bladecenter Management Logs'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/cacti/',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Cacti Web Monitoring'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/cgi-bin/awstats.pl',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'AWStats'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/wiki/rankings.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Bit Weaver'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/reqdetails.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'BtiTracker'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/shared/help.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'OpenBiblio/WebBiblio Subject Gateway System'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/seti.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'PHP SETI@home'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/imc/',
        method = 'HEAD'
      },
      {
        path = '/imcws/',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = '3Com Intelligent Management Center'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/partymgr/',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Apache OFBiz'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/Base/upload.php',
        method = 'HEAD'
      },
      {
        path = '/Base/example_1.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'MassMirror Uploader'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/YUI-upload/html',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'YUI Images / File Upload'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/tools/filemanager/skins/mobile/admin1.template.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'ispCP Omega'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/Uploadify/',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Uploadify'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/syssite/',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'ShopEx'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/updown.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'PHP Uploader Downloader'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/modules/docmanager/doctypetemplates/myuploadedfile',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Achievo'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/ReqWebHelp/advanced/workingSet.jsp',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'IBM Rational RequisitePro/ReqWebHelp'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/dhost/',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Novell eDirectory'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/engine/api/api.class.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'DatalifeEngine'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/jsft_resource.jsf',
        method = 'HEAD'
      },
      {
        path = '/scales_static_resource.jsf',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'JSFTemplating/Mojarra Scales/GlassFish Application Server'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/setup/password_required.html',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = '2WIRE GATEWAY'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/zp-core/',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Zen Photo'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/amember/',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'aMember'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/.hgignore',
        method = 'HEAD'
      },
      {
        path = '/.gitignore',
        method = 'HEAD'
      },
      {
        path = '/.bzrignore',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Revision control ignore file'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/debug.seam',
        method = 'GET'
      }
    },
    matches = {
      {
        match = 'JBoss Seam Debug Page',
        output = 'JBoss Seam Debug Page'
      }
    }
  });

------------------------------------------------
----         SECURITY SOFTWARE              ----
------------------------------------------------
-- These checks will find specific installed software. If possible, it will also
-- find versions, etc.

table.insert(fingerprints, {
    category = 'security',
    probes = {
    {
        path = '/CSS/Miniweb.css',
        method = 'GET'
      }
    },
    matches = {
      {
        match = 'ad_header_form_sprachauswahl',
        output = 'SCADA Siemens SIMATIC S7'
      }
    }
  });

table.insert(fingerprints, {
    category = 'security',
    probes = {
      {
        path = '/S7Web.css',
        method = 'GET'
      },
      {
        path = '/Portal0000.htm',
        method = 'GET'
      }
    },
    matches = {
      {
        match = '<td class="Title_Area_Name">(.-)</td>',
        output = 'SCADA Siemens PCS7: \\1'
      },
      {
        match = '',
        output = 'SCADA Siemens PCS7'
      }
    }
  });

table.insert(fingerprints, {
    category = 'security',
    probes = {
      {
        path = '/arcsight/',
        method = 'HEAD'
      },
      {
        path = '/arcsight/images/logo-login-arcsight.gif',
        method = 'HEAD'
      },
      {
        path = '/arcsight/images/navbar-icon-logout-on.gif',
        method = 'HEAD'
      },
      {
        path = '/images/logo-arcsight.gif',
        method = 'HEAD'
      },
      {
        path = '/logger/monitor.ftl',
        method = 'HEAD'
      },
    },
    matches = {
      {
        output = 'Arcsight'
      }
    }
  });

table.insert(fingerprints, {
    category = 'security',
    probes = {
      {
        path = '/beef/',
        method = 'HEAD'
      },
      {
        path = '/BEEF/',
        method = 'HEAD'
      },
      {
        path = '/beef/images/beef.gif',
        method = 'HEAD'
      }
    },
    matches = {
      {
        output = 'BeEF Browser Exploitation Framework'
      }
    }
  });

table.insert(fingerprints, {
    category = 'security',
    probes = {
      {
        path = '/gfx/form_top_left_corner.gif',
        method = 'HEAD'
      },
      {
        path = '/gfx/logout_24.png',
        method = 'HEAD'
      },
      {
        path = '/gfx/new_logo.gif',
        method = 'HEAD'
      },
      {
        path = '/javascript/sorttable.js',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Secunia NSI'
      }
    }
  });

table.insert(fingerprints, {
    category = 'security',
    probes = {
      {
        path = '/images/btn_help_nml.gif',
        method = 'HEAD'
      },
      {
        path = '/images/hdr_icon_homeG.gif',
        method = 'HEAD'
      },
      {
        path = '/spControl.php',
        method = 'HEAD'
      },
      {
        path = '/images/isslogo.gif',
        method = 'HEAD'
      },
      {
        path = '/deploymentmanager/',
        method = 'HEAD'
      },
    },
    matches = {
      {
        match = '',
        output = 'IBM Proventia'
      }
    }
  });

table.insert(fingerprints, {
    category = 'security',
    probes = {
      {
        path = '/i18n/EN/css/foundstone.css',
        method = 'HEAD'
      },
      {
        path = '/i18n/EN/images/external_nav_square.gif',
        method = 'HEAD'
      },
    },
    matches = {
      {
        match = '',
        output = 'Foundstone'
      }
    }
  });

table.insert(fingerprints, {
    category = 'security',
    probes = {
      {
        path = '/officescan/console/html/cgi/cgiChkMasterPwd.exe',
        method = 'HEAD'
      },
      {
        path = '/officescan/console/html/ClientInstall/officescannt.htm',
        method = 'HEAD'
      },
      {
        path = '/officescan/console/html/images/icon_refresh.gif',
        method = 'HEAD'
      },
    },
    matches = {
      {
        match = '',
        output = 'Trend Micro OfficeScan Server'
      }
    }
  });

table.insert(fingerprints, {
    category = 'security',
    probes = {
      {
        path = '/picts/BC_bwlogorev.gif',
        method = 'HEAD'
      },
      {
        path = '/picts/menu_leaf.gif',
        method = 'HEAD'
      },
    },
    matches = {
      {
        match = '',
        output = 'BlueCoat Reporter'
      }
    }
  });

table.insert(fingerprints, {
    category = 'security',
    probes = {
      {
        path = '/theme/images/en/login1.gif',
        method = 'HEAD'
      },
    },
    matches = {
      {
        match = '',
        output = 'Fortinet VPN/Firewall'
      }
    }
  });

table.insert(fingerprints, {
    category = 'security',
    probes = {
      {
        path = '/',
        method = 'GET'
      },
    },
    matches = {
      {
        match = 'id="NessusClient"',
        output = 'Nessus'
      },
      {
        match = 'NessusClient.swf',
        output = 'Nessus'
      }
    }
  });

table.insert(fingerprints, {
    category = 'security',
    probes = {
      {
        path = '/NessusClient.swf',
        method = 'HEAD'
      },
    },
    matches = {
      {
        match = '',
        output = 'Nessus'
      }
    }
  });

table.insert(fingerprints, {
    category = 'security',
    probes = {
      {
        path = '/dotDefender/',
        method = 'HEAD'
      },
    },
    matches = {
      {
        match = '',
        output = 'dotDefender Web Application Firewall'
      }
    }
  });

-- http://www.rapid7.com/db/modules/payload/windows/meterpreter/reverse_hop_http
-- "GET /hop.php?/control" will grab all pending messages, but is unreliable if
-- there are no pending messages.
table.insert(fingerprints, {
    category = 'security',
    probes = {
      {
        path = '/hop.php?/12345',
        method = 'GET'
      },
    },
    matches = {
      {
        -- TODO: this only works for Meterpreter payloads. Find a more generic means?
        match = 'METERPRETER_TRANSPORT_HTTP',
        output = 'Metasploit reverse_hop_http hop point'
      },
    }
  });

-- http://carnal0wnage.attackresearch.com/2015/02/cisco-asa-version-grabber-cve-2014-3398.html
table.insert(fingerprints, {
    category = 'security',
    probes = {
      {
        path = '/CSCOSSLC/config-auth',
        method = 'GET'
      },
    },
    matches = {
      {
        match = '<version who="sg">([^<]+)</version>',
        output = 'Cisco ASA, firmware \\1'
      },
    }
  });

------------------------------------------------
----        MANAGEMENT SOFTWARE             ----
------------------------------------------------
table.insert(fingerprints, {
    category = 'management',
    probes = {
      {
        path = '/actuator/',
        method = 'GET'
      },
      {
        path = '/auditevents/',
        method = 'GET'
      },
      {
        path = '/autoconfig/',
        method = 'GET'
      },
      {
        path = '/beans/',
        method = 'GET'
      },
      {
        path = '/configprops/',
        method = 'GET'
      },
      {
        path = '/env/',
        method = 'GET'
      },
      {
        path = '/flyway/',
        method = 'GET'
      },
      {
        path = '/health/',
        method = 'GET'
      },
      {
        path = '/healthcheck/',
        method = 'GET'
      },
      {
        path = '/healthchecks/',
        method = 'GET'
      },
      {
        path = '/loggers/',
        method = 'GET'
      },
      {
        path = '/liquibase/',
        method = 'GET'
      },
      {
        path = '/metrics/',
        method = 'GET'
      },
      {
        path = '/mappings/',
        method = 'GET'
      },
      {
        path = '/trace/',
        method = 'GET'
      }
    },
    matches = {
      {
        output = 'Spring Boot Actuator endpoint'
      }
    }
  });

table.insert(fingerprints, {
    category = 'management',
    probes = {
      {
        path = '/heapdump/',
        method = 'GET'
      },
      {
        path = '/jolokia/',
        method = 'GET'
      }
    },
    matches = {
      {
        output = 'Spring MVC Endpoint'
      }
    }
  });



table.insert(fingerprints, {
    category = 'management',
    probes = {
      {
        path = '/vmware/',
        method = 'HEAD'
      },
      {
        path = '/vmware/imx/vmware_boxes-16x16.png',
        method = 'HEAD'
      },
      {
        path = '/ui/',
        method = 'HEAD'
      },
      {
        path = '/ui/imx/vmwareLogo-16x16.png',
        method = 'HEAD'
      },
      {
        path = '/ui/imx/vmwarePaperBagLogo-16x16.png',
        method = 'HEAD'
      },
      {
        path = '/ui/vManage.do',
        method = 'HEAD'
      },
      {
        path = '/client/VMware-viclient.exe',
        method = 'HEAD'
      },
      {
        path = '/en/welcomeRes.js',
        method = 'HEAD'
      }
    },
    matches = {
      {
        output = 'VMWare'
      }
    }
  });

table.insert(fingerprints, {
    category = 'management',
    probes = {
      {
        path = '/citrix/',
        method = 'HEAD'
      },
      {
        path = '/Citrix/',
        method = 'HEAD'
      },
      {
        path = '/Citrix/MetaFrame/auth/login.aspx',
        method = 'HEAD'
      },
      {
        path = '/images/ctxHeader01.jpg',
        method = 'HEAD'
      },
      {
        path = '/images/Safeword_Token.jpg',
        method = 'HEAD'
      },
      {
        path = '/sw/auth/login.aspx',
        method = 'HEAD'
      },
      {
        path = '/vpn/images/AccessGateway.ico',
        method = 'HEAD'
      },
      {
        path = '/citrix/AccessPlatform/auth/clientscripts/',
        method = 'HEAD'
      },
      {
        path = '/AccessPlatform/auth/clientscripts/',
        method = 'HEAD'
      },
      {
        path = '/Citrix//AccessPlatform/auth/clientscripts/cookies.js',
        method = 'HEAD'
      },
      {
        path = '/Citrix/AccessPlatform/auth/clientscripts/login.js',
        method = 'HEAD'
      },
      {
        path = '/Citrix/PNAgent/config.xml',
        method = 'HEAD'
      },
    },
    matches = {
      {
        output = 'Citrix'
      }
    }
  });

table.insert(fingerprints, {
    category = 'management',
    probes = {
      {
        path = '/cgi-bin/image/shikaku2.png',
        method = 'HEAD'
      },
    },
    matches = {
      {
        match = '',
        output = 'TeraStation PRO RAID 0/1/5 Network Attached Storage'
      }
    }
  });

table.insert(fingerprints, {
    category = 'management',
    probes = {
      {
        path = '/config/public/usergrp.gif',
        method = 'HEAD'
      },
      {
        path = '/pictures/buttons/file_view_mark.gif',
        method = 'HEAD'
      },
    },
    matches = {
      {
        match = '',
        output = 'AXIS StorPoint'
      }
    }
  });

table.insert(fingerprints, {
    category = 'management',
    probes = {
      {
        path = '/cpqlogin.htm?RedirectUrl=/&RedirectQueryString=',
        method = 'HEAD'
      },
      {
        path = '/hplogo.gif',
        method = 'HEAD'
      },
    },
    matches = {
      {
        match = '',
        output = 'HP System Management Homepage'
      }
    }
  });

table.insert(fingerprints, {
    category = 'management',
    probes = {
      {
        path = '/ie_index.htm',
        method = 'HEAD'
      },
      {
        path = '/ilo.gif',
        method = 'HEAD'
      },
    },
    matches = {
      {
        match = '',
        output = 'HP Integrated Lights Out'
      }
    }
  });

table.insert(fingerprints, {
    category = 'management',
    probes = {
      {
        path = '/images/icon_server_connected.gif',
        method = 'HEAD'
      },
    },
    matches = {
      {
        match = '',
        output = 'HP Blade Enclosure'
      }
    }
  });

table.insert(fingerprints, {
    category = 'management',
    probes = {
      {
        path = '/mxhtml/images/signin_logo.gif',
        method = 'HEAD'
      },
      {
        path = '/mxhtml/images/status_critical_15.gif',
        method = 'HEAD'
      },
      {
        path = '/mxportal/home/en_US/servicetools.gif',
        method = 'HEAD'
      },
      {
        path = '/mxportal/home/MxPortalFrames.jsp',
        method = 'HEAD'
      },
    },
    matches = {
      {
        match = '',
        output = 'HP Insight Manager'
      }
    }
  });

table.insert(fingerprints, {
    category = 'management',
    probes = {
      {
        path = '/xymon/menu/menu.css',
        method = 'HEAD'
      },
    },
    matches = {
      {
        match = '',
        output = 'Xymon'
      }
    }
  });

table.insert(fingerprints, {
    category = 'management',
    probes = {
      {
        path = '/rrc.htm',
        method = 'HEAD'
      },
    },
    matches = {
      {
        match = '',
        output = 'Raritan Remote Client'
      }
    }
  });

table.insert(fingerprints, {
    category = 'management',
    probes = {
      {
        path = '/manager/html/upload',
        method = 'HEAD'
      },
      {
        path = '/manager/html',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Apache Tomcat'
      }
    }
  });

table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/axis2/axis2-web/HappyAxis.jsp',
        method = 'HEAD'
      },
      {
        path = '/axis2/',
        method = 'HEAD'
      },
      {
        path = '/happyaxis.jsp',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Apache Axis2'
      }
    }
  });

table.insert(fingerprints, {
    category = 'management',
    probes = {
      {
        path = '/web-console/ServerInfo.jsp',
        method = 'HEAD'
      },
      {
        path = '/web-console/Invoker',
        method = 'HEAD'
      },
      {
        path = '/invoker/JMXInvokerServlet',
        method = 'HEAD'
      },
      {
        path = '/invoker/',
        method = 'HEAD'
      },
      {
        path = '/jmx-console/',
        method = 'HEAD'
      },
      {
        path = '/admin-console/',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'JBoss Console'
      }
    }
  });

table.insert(fingerprints, {
    category = 'management',
    probes = {
      {
        path = '/CFIDE/administrator/enter.cfm',
        method = 'HEAD'
      },
      {
        path = '/CFIDE/administrator/entman/index.cfm',
        method = 'HEAD'
      },
      {
        path = '/cfide/install.cfm',
        method = 'HEAD'
      },
      {
        path = '/CFIDE/administrator/archives/index.cfm',
        method = 'HEAD'
      },
      {
        path = '/CFIDE/wizards/common/_logintowizard.cfm',
        method = 'HEAD'
      },
      {
        path = '/CFIDE/componentutils/login.cfm',
        method = 'HEAD'
      },
      {
        path = '/CFIDE/Administrator/startstop.html',
        method = 'HEAD'
      },
    },
    matches = {
      {
        match = '',
        output = 'ColdFusion Admin Console'
      }
    }
  });

table.insert(fingerprints, {
    category = 'management',
    probes = {
      {
        path = '/common/help/en/go/login_ts.html',
        method = 'HEAD'
      },
      {
        path = '/system/login/',
        method = 'HEAD'
      },
      {
        path = '/system/login/reset?next=%2Fsystem%2Flogin&set-lang=en',
        method = 'HEAD'
      },
      {
        path = '/common/images/logos/img_logoMain.jpg',
        method = 'HEAD'
      },
    },
    matches = {
      {
        match = 'URL=http://www.macromedia.com/go/breeze_login_help_en',
        output = 'Adobe Acrobat Connect Pro'
      },
      {
        match = '<title>Connect Pro Central Login</title>',
        output = 'Adobe Acrobat Connect Pro'
      },
      {
        match = '<title>Forgot your password?</title>',
        output = 'Adobe Acrobat Connect Pro'
      },
      {
        match = 'Server: JRun Web Server',
        output = 'Adobe Acrobat Connect Pro'
      },
    }
  });

table.insert(fingerprints, {
    category = 'management',
    probes = {
      {
        path = '/Dashboard/Dashboard.html',
        method = 'GET'
      }
    },
    matches = {
      {
        match = 'Server: Kodak-RulesBasedAutomation',
        output = 'Prinergy Dashboard Client Login'
      },
      {
        match = '<title>Dashboard</title>',
        output = 'Prinergy Dashboard Client Login'
      }
    }
  });

table.insert(fingerprints, {
    category = 'management',
    probes = {
      {
        path = '/flexfm/',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Flex File Manager'
      }
    }
  });

table.insert(fingerprints, {
    category = 'management',
    probes = {
      {
        path = '/lib/usermanagement/userInfo.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Testlink TestManagement'
      }
    }
  });

table.insert(fingerprints, {
    category = 'management',
    probes = {
      {
        path = '/security/xamppsecurity.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'XAMPP'
      }
    }
  });

table.insert(fingerprints, {
    category = 'management',
    probes = {
      {
        path = '/system/console',
        method = 'HEAD'
      },
    },
    matches = {
      {
        match = 'OSGi Management Console',
        output = 'OSGi Management Console'
      }
    }
  });

table.insert(fingerprints, {
    category = 'management',
    probes = {
      {
        path = '/dm-albums/dm-albums.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'DM FileManager'
      }
    }
  });

table.insert(fingerprints, {
    category = 'management',
    probes = {
      "/ibm/console/logon.jsp?action=OK",
      "/console/",
      "/console/portal/0/Welcome"
    },
    matches = {
      {
        match = "[Ww][Ee][Bb][Ss][Pp][Hh][Ee][Rr][Ee]",
        output = "WebSphere"
      },
      {
        match = "WSC Console Federation",
        output = "WebSphere Commerce"
      },
    }
  });

table.insert(fingerprints, {
    category = 'management',
    probes = {
      {
        path = '/rest/applinks/1.0/manifest',
        method = 'GET'
      },
      {
        path = '/jira/rest/applinks/1.0/manifest',
        method = 'GET'
      },
      {
        path = '/secure/rest/applinks/1.0/manifest',
        method = 'GET'
      },
    },
    matches = {
      {
        match = '<typeId>jira</typeId>.*<version>([^<]+)</version>',
        output = 'Atlassian Jira \\1'
      }
    }
  });

table.insert(fingerprints, {
    category = 'management',
    probes = {
      {
        path = '/rest/servicedeskapi/info',
        method = 'GET'
      },
      {
        path = '/jira/rest/servicedeskapi/info',
        method = 'GET'
      },
      {
        path = '/secure/rest/servicedeskapi/info',
        method = 'GET'
      },
    },
    matches = {
      {
        match = '"version":%s*"([^-"]+)',
        output = 'Atlassian Jira Service Desk \\1'
      }
    }
  });

table.insert(fingerprints, {
    category = 'management',
    probes = {
      {
        path = '/rest/applinks/1.0/manifest',
        method = 'GET'
      },
      {
        path = '/confluence/rest/applinks/1.0/manifest',
        method = 'GET'
      },
      {
        path = '/wiki/rest/applinks/1.0/manifest',
        method = 'GET'
      },
    },
    matches = {
      {
        match = '<typeId>confluence</typeId>.*<version>([^<]+)</version>',
        output = 'Atlassian Confluence \\1'
      }
    }
  });

table.insert(fingerprints, {
    category = 'management',
    probes = {
      {
        path = '/rest/applinks/1.0/manifest',
        method = 'GET'
      },
      {
        path = '/bitbucket/rest/applinks/1.0/manifest',
        method = 'GET'
      },
    },
    matches = {
      {
        match = '<typeId>stash</typeId>.*<version>([^<]+)</version>',
        output = 'Atlassian Bitbucket Server \\1'
      },
      {
        match = '<typeId>bitbucket</typeId>.*<version>([^<]+)</version>',
        output = 'Atlassian Bitbucket Server \\1'
      }
    }
  });

table.insert(fingerprints, {
    category = 'management',
    probes = {
      {
        path = '/rest/applinks/1.0/manifest',
        method = 'GET'
      },
      {
        path = '/bamboo/rest/applinks/1.0/manifest',
        method = 'GET'
      },
    },
    matches = {
      {
        match = '<typeId>bamboo</typeId>.*<version>([^<]+)</version>',
        output = 'Atlassian Bamboo \\1'
      }
    }
  });

table.insert(fingerprints, {
    category = 'management',
    probes = {
      {
        path = '/rest/applinks/1.0/manifest',
        method = 'GET'
      },
      {
        path = '/crowd/rest/applinks/1.0/manifest',
        method = 'GET'
      },
    },
    matches = {
      {
        match = '<typeId>crowd</typeId>.*<version>([^<]+)</version>',
        output = 'Atlassian Crowd \\1'
      }
    }
  });

------------------------------------------------
----     PRINTERS, WEBCAMS, PROJECTORS      ----
------------------------------------------------
table.insert(fingerprints, {
    category = 'printer',
    probes = {
      {
        path = '/x_logo.gif',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Xerox printer'
      }
    }
  });

table.insert(fingerprints, {
    category = 'printer',
    probes = {
      {
        path = '/gif/hp.gif',
        method = 'HEAD'
      },
      {
        path = '/gif/hp_invent_logo.gif',
        method = 'HEAD'
      },
      {
        path = '/gif/printer.gif',
        method = 'HEAD'
      },
      {
        path = '/hp/device/this.LCDispatcher',
        method = 'HEAD'
      },
      {
        path = '/hp/device/webAccess/index.htm',
        method = 'HEAD'
      },
      {
        path = '/PageSelector.class',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'HP Printer'
      }
    }
  });

table.insert(fingerprints, {
    category = 'printer',
    probes = {
      {
        path = '/images/lexbold.gif',
        method = 'HEAD'
      },
      {
        path = '/images/lexlogo.gif',
        method = 'HEAD'
      },
      {
        path = '/images/printer.gif',
        method = 'HEAD'
      },
      {
        path = '/printer/image',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Lexmark Printer'
      }
    }
  });

table.insert(fingerprints, {
    category = 'printer',
    probes = {
      {
        path = '/images/mute_alloff.gif',
        method = 'HEAD'
      },
      {
        path = '/images/pic_bri.gif',
        method = 'HEAD'
      },
    },
    matches = {
      {
        match = '',
        output = 'NEC Projector'
      }
    }
  });

table.insert(fingerprints, {
    category = 'printer',
    probes = {
      {
        path = '/scanweb/images/scanwebtm.gif',
        method = 'HEAD'
      },
    },
    matches = {
      {
        match = '',
        output = 'SCAN Web (Webcam)'
      }
    }
  });

table.insert(fingerprints, {
    category = 'printer',
    probes = {
      {
        path = '/view/index.shtml',
        method = 'HEAD'
      },
    },
    matches = {
      {
        match = '',
        output = 'Axis 212 PTZ Network Camera'
      }
    }
  });

------------------------------------------------
----              DATABASES                 ----
------------------------------------------------

--phpmyadmin db taken from http://milw0rm.com/exploits/8921
table.insert(fingerprints, {
    category = 'database',
    probes = {
      {
        path = '/phpmyadmin/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin/',
        method = 'HEAD'
      },
      {
        path = '/PHPMyAdmin/',
        method = 'HEAD'
      },
      {
        path = '/PMA/',
        method = 'HEAD'
      },
      {
        path = '/pma/',
        method = 'HEAD'
      },
      {
        path = '/dbadmin/',
        method = 'HEAD'
      },
      {
        path = '/myadmin/',
        method = 'HEAD'
      },
      {
        path = '/php-my-admin/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin2/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.2.3/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.2.6/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.5.1/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.5.4/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.5.5-rc1/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.5.5-rc2/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.5.5/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.5.5-pl1/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.5.6-rc1/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.5.6-rc2/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.5.6/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.5.7/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.5.7-pl1/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.6.0-alpha/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.6.0-alpha2/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.6.0-beta1/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.6.0-beta2/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.6.0-rc1/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.6.0-rc2/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.6.0-rc3/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.6.0/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.6.0-pl1/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.6.0-pl2/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.6.0-pl3/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.6.1-rc1/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.6.1-rc2/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.6.1/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.6.1-pl1/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.6.1-pl2/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.6.1-pl3/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.6.2-rc1/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.6.2-beta1/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.6.2/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.6.2-pl1/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.6.3/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.6.3-rc1/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.6.3-pl1/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.6.4-rc1/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.6.4-pl1/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.6.4-pl2/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.6.4-pl3/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.6.4-pl4/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.6.4/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.7.0-beta1/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.7.0-rc1/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.7.0-pl1/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.7.0-pl2/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.7.0/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.8.0-beta1/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.8.0-rc1/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.8.0-rc2/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.8.0/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.8.0.1/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.8.0.2/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.8.0.3/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.8.0.4/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.8.1-rc1/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.8.1/',
        method = 'HEAD'
      },
      {
        path = '/phpMyAdmin-2.8.2/',
        method = 'HEAD'
      },
      {
        path = '/sqlmanager/',
        method = 'HEAD'
      },
      {
        path = '/php-myadmin/',
        method = 'HEAD'
      },
      {
        path = '/phpmy-admin/',
        method = 'HEAD'
      },
      {
        path = '/mysqladmin/',
        method = 'HEAD'
      },
      {
        path = '/mysql-admin/',
        method = 'HEAD'
      },
      {
        path = '/websql/',
        method = 'HEAD'
      },
      {
        path = '/_phpmyadmin/',
        method = 'HEAD'
      }
    },
    matches = {
      {
        output = 'phpMyAdmin'
      }
    }
  });

table.insert(fingerprints, {
    category = 'database',
    probes = {
      {
        path = '/footer1.gif',
        method = 'HEAD'
      },
    },
    matches = {
      {
        match = '',
        output = '(possible) Oracle Web server'
      }
    }
  });

table.insert(fingerprints, {
    category = 'database',
    probes = {
      {
        path = '/homepage.nsf/homePage.gif?OpenImageResource',
        method = 'HEAD'
      },
      {
        path = '/icons/ecblank.gif',
        method = 'HEAD'
      },
      {
        path = '/852566C90012664F',
        method = 'HEAD'
      },
      {
        path = '/admin4.nsf',
        method = 'HEAD'
      },
      {
        path = '/admin5.nsf',
        method = 'HEAD'
      },
      {
        path = '/admin.nsf',
        method = 'HEAD'
      },
      {
        path = '/agentrunner.nsf',
        method = 'HEAD'
      },
      {
        path = '/alog.nsf',
        method = 'HEAD'
      },
      {
        path = '/a_domlog.nsf',
        method = 'HEAD'
      },
      {
        path = '/bookmark.nsf',
        method = 'HEAD'
      },
      {
        path = '/busytime.nsf',
        method = 'HEAD'
      },
      {
        path = '/catalog.nsf',
        method = 'HEAD'
      },
      {
        path = '/certa.nsf',
        method = 'HEAD'
      },
      {
        path = '/certlog.nsf',
        method = 'HEAD'
      },
      {
        path = '/certsrv.nsf',
        method = 'HEAD'
      },
      {
        path = '/chatlog.nsf',
        method = 'HEAD'
      },
      {
        path = '/clbusy.nsf',
        method = 'HEAD'
      },
      {
        path = '/cldbdir.nsf',
        method = 'HEAD'
      },
      {
        path = '/clusta4.nsf',
        method = 'HEAD'
      },
      {
        path = '/collect4.nsf',
        method = 'HEAD'
      },
      {
        path = '/da.nsf',
        method = 'HEAD'
      },
      {
        path = '/dba4.nsf',
        method = 'HEAD'
      },
      {
        path = '/dclf.nsf',
        method = 'HEAD'
      },
      {
        path = '/DEASAppDesign.nsf',
        method = 'HEAD'
      },
      {
        path = '/DEASLog01.nsf',
        method = 'HEAD'
      },
      {
        path = '/DEASLog02.nsf',
        method = 'HEAD'
      },
      {
        path = '/DEASLog03.nsf',
        method = 'HEAD'
      },
      {
        path = '/DEASLog04.nsf',
        method = 'HEAD'
      },
      {
        path = '/DEASLog05.nsf',
        method = 'HEAD'
      },
      {
        path = '/DEASLog.nsf',
        method = 'HEAD'
      },
      {
        path = '/decsadm.nsf',
        method = 'HEAD'
      },
      {
        path = '/decslog.nsf',
        method = 'HEAD'
      },
      {
        path = '/DEESAdmin.nsf',
        method = 'HEAD'
      },
      {
        path = '/dirassist.nsf',
        method = 'HEAD'
      },
      {
        path = '/doladmin.nsf',
        method = 'HEAD'
      },
      {
        path = '/domadmin.nsf',
        method = 'HEAD'
      },
      {
        path = '/domcfg.nsf',
        method = 'HEAD'
      },
      {
        path = '/domguide.nsf',
        method = 'HEAD'
      },
      {
        path = '/domlog.nsf',
        method = 'HEAD'
      },
      {
        path = '/dspug.nsf',
        method = 'HEAD'
      },
      {
        path = '/events4.nsf',
        method = 'HEAD'
      },
      {
        path = '/events5.nsf',
        method = 'HEAD'
      },
      {
        path = '/events.nsf',
        method = 'HEAD'
      },
      {
        path = '/event.nsf',
        method = 'HEAD'
      },
      {
        path = '/homepage.nsf',
        method = 'HEAD'
      },
      {
        path = '/iNotes/Forms5.nsf/$DefaultNav',
        method = 'HEAD'
      },
      {
        path = '/jotter.nsf',
        method = 'HEAD'
      },
      {
        path = '/leiadm.nsf',
        method = 'HEAD'
      },
      {
        path = '/leilog.nsf',
        method = 'HEAD'
      },
      {
        path = '/leivlt.nsf',
        method = 'HEAD'
      },
      {
        path = '/log4a.nsf',
        method = 'HEAD'
      },
      {
        path = '/log.nsf',
        method = 'HEAD'
      },
      {
        path = '/l_domlog.nsf',
        method = 'HEAD'
      },
      {
        path = '/mab.nsf',
        method = 'HEAD'
      },
      {
        path = '/mail10.box',
        method = 'HEAD'
      },
      {
        path = '/mail1.box',
        method = 'HEAD'
      },
      {
        path = '/mail2.box',
        method = 'HEAD'
      },
      {
        path = '/mail3.box',
        method = 'HEAD'
      },
      {
        path = '/mail4.box',
        method = 'HEAD'
      },
      {
        path = '/mail5.box',
        method = 'HEAD'
      },
      {
        path = '/mail6.box',
        method = 'HEAD'
      },
      {
        path = '/mail7.box',
        method = 'HEAD'
      },
      {
        path = '/mail8.box',
        method = 'HEAD'
      },
      {
        path = '/mail9.box',
        method = 'HEAD'
      },
      {
        path = '/mail.box',
        method = 'HEAD'
      },
      {
        path = '/msdwda.nsf',
        method = 'HEAD'
      },
      {
        path = '/mtatbls.nsf',
        method = 'HEAD'
      },
      {
        path = '/mtstore.nsf',
        method = 'HEAD'
      },
      {
        path = '/names.nsf',
        method = 'HEAD'
      },
      {
        path = '/nntppost.nsf',
        method = 'HEAD'
      },
      {
        path = '/nntp/nd000001.nsf',
        method = 'HEAD'
      },
      {
        path = '/nntp/nd000002.nsf',
        method = 'HEAD'
      },
      {
        path = '/nntp/nd000003.nsf',
        method = 'HEAD'
      },
      {
        path = '/ntsync45.nsf',
        method = 'HEAD'
      },
      {
        path = '/perweb.nsf',
        method = 'HEAD'
      },
      {
        path = '/qpadmin.nsf',
        method = 'HEAD'
      },
      {
        path = '/quickplace/quickplace/main.nsf',
        method = 'HEAD'
      },
      {
        path = '/reports.nsf',
        method = 'HEAD'
      },
      {
        path = '/sample/siregw46.nsf',
        method = 'HEAD'
      },
      {
        path = '/schema50.nsf',
        method = 'HEAD'
      },
      {
        path = '/setupweb.nsf',
        method = 'HEAD'
      },
      {
        path = '/setup.nsf',
        method = 'HEAD'
      },
      {
        path = '/smbcfg.nsf',
        method = 'HEAD'
      },
      {
        path = '/smconf.nsf',
        method = 'HEAD'
      },
      {
        path = '/smency.nsf',
        method = 'HEAD'
      },
      {
        path = '/smhelp.nsf',
        method = 'HEAD'
      },
      {
        path = '/smmsg.nsf',
        method = 'HEAD'
      },
      {
        path = '/smquar.nsf',
        method = 'HEAD'
      },
      {
        path = '/smsolar.nsf',
        method = 'HEAD'
      },
      {
        path = '/smtime.nsf',
        method = 'HEAD'
      },
      {
        path = '/smtpibwq.nsf',
        method = 'HEAD'
      },
      {
        path = '/smtpobwq.nsf',
        method = 'HEAD'
      },
      {
        path = '/smtp.box',
        method = 'HEAD'
      },
      {
        path = '/smtp.nsf',
        method = 'HEAD'
      },
      {
        path = '/smvlog.nsf',
        method = 'HEAD'
      },
      {
        path = '/srvnam.htm',
        method = 'HEAD'
      },
      {
        path = '/statmail.nsf',
        method = 'HEAD'
      },
      {
        path = '/statrep.nsf',
        method = 'HEAD'
      },
      {
        path = '/stauths.nsf',
        method = 'HEAD'
      },
      {
        path = '/stautht.nsf',
        method = 'HEAD'
      },
      {
        path = '/stconfig.nsf',
        method = 'HEAD'
      },
      {
        path = '/stconf.nsf',
        method = 'HEAD'
      },
      {
        path = '/stdnaset.nsf',
        method = 'HEAD'
      },
      {
        path = '/stdomino.nsf',
        method = 'HEAD'
      },
      {
        path = '/stlog.nsf',
        method = 'HEAD'
      },
      {
        path = '/streg.nsf',
        method = 'HEAD'
      },
      {
        path = '/stsrc.nsf',
        method = 'HEAD'
      },
      {
        path = '/userreg.nsf',
        method = 'HEAD'
      },
      {
        path = '/vpuserinfo.nsf',
        method = 'HEAD'
      },
      {
        path = '/webadmin.nsf',
        method = 'HEAD'
      },
      {
        path = '/web.nsf',
        method = 'HEAD'
      },
      {
        path = '/.nsf/../winnt/win.ini',
        method = 'HEAD'
      },
      {
        path = '/icons/ecblank.gif',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Lotus Domino'
      }
    }
  });

table.insert(fingerprints, {
    category = 'database',
    probes = {
      {
        path = '/_api/version',
        method = 'GET'
      }
    },
    matches = {
      {
        match = '"server":"arango",.-"version":"([^"])"',
        output = 'ArangoDB \\1'
      }
    }
  });

------------------------------------------------
----              MICROSOFT                 ----
------------------------------------------------
table.insert(fingerprints, {
    category = 'microsoft',
    probes = {
      {
        path = '/_layouts/images/helpicon.gif',
        method = 'HEAD'
      },
      {
        path = '/Pages/Default.aspx',
        method = 'HEAD'
      },
      {
        path = '/PublishingImages/NewsArticleImage.jpg',
        method = 'HEAD'
      },
      {
        path = '/_admin/operations.aspx',
        method = 'HEAD'
      },
      {
        path = '/_app_bin',
        method = 'HEAD'
      },
      {
        path = '/_controltemplates',
        method = 'HEAD'
      },
      {
        path = '/_layouts',
        method = 'HEAD'
      },
      {
        path = '/_layouts/viewlsts.aspx',
        method = 'HEAD'
      },
      {
        path = '/forms/allitems.aspx',
        method = 'HEAD'
      },
      {
        path = '/forms/webfldr.aspx',
        method = 'HEAD'
      },
      {
        path = '/forms/mod-view.aspx',
        method = 'HEAD'
      },
      {
        path = '/forms/my-sub.aspx',
        method = 'HEAD'
      },
      {
        path = '/pages/categoryresults.aspx',
        method = 'HEAD'
      },
      {
        path = '/categories/viewcategory.aspx',
        method = 'HEAD'
      },
      {
        path = '/sitedirectory',
        method = 'HEAD'
      },
      {
        path = '/editdocs.aspx',
        method = 'HEAD'
      },
      {
        path = '/workflowtasks/allitems.aspx',
        method = 'HEAD'
      },
      {
        path = '/lists/tasks/',
        method = 'HEAD'
      },
      {
        path = '/categories/allcategories.aspx',
        method = 'HEAD'
      },
      {
        path = '/categories/SOMEOTHERDIR/allcategories.aspx',
        method = 'HEAD'
      },
      {
        path = '/mycategories.aspx',
        method = 'HEAD'
      },
      {
        path = '/lists/',
        method = 'HEAD'
      },
      {
        path = '/lists/allitems.aspx',
        method = 'HEAD'
      },
      {
        path = '/lists/default.aspx',
        method = 'HEAD'
      },
      {
        path = '/lists/allposts.aspx',
        method = 'HEAD'
      },
      {
        path = '/lists/archive.aspx',
        method = 'HEAD'
      },
      {
        path = '/lists/byauthor.aspx',
        method = 'HEAD'
      },
      {
        path = '/lists/calendar.aspx',
        method = 'HEAD'
      },
      {
        path = '/lists/mod-view.aspx',
        method = 'HEAD'
      },
      {
        path = '/lists/myposts.aspx',
        method = 'HEAD'
      },
      {
        path = '/lists/my-sub.aspx',
        method = 'HEAD'
      },
      {
        path = '/lists/allcomments.aspx',
        method = 'HEAD'
      },
      {
        path = '/lists/mycomments.aspx',
        method = 'HEAD'
      },
      {
        path = '/_layouts/userdisp.aspx',
        method = 'HEAD'
      },
      {
        path = '/_layouts/help.aspx',
        method = 'HEAD'
      },
      {
        path = '/_layouts/download.aspx',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'MS Sharepoint'
      }
    }
  });

table.insert(fingerprints, {
    category = 'microsoft',
    probes = {
      {
        path = '/projectserver/Home/HomePage.asp',
        method = 'HEAD'
      },
      {
        path = '/projectserver/images/branding.gif',
        method = 'HEAD'
      },
      {
        path = '/projectserver/images/pgHome.gif',
        method = 'HEAD'
      },
      {
        path = '/projectserver/images/pgTask.gif',
        method = 'HEAD'
      },
      {
        path = '/projectserver/Tasks/Taskspage.asp',
        method = 'HEAD'
      },
    },
    matches = {
      {
        match = '',
        output = 'MS Project Server'
      }
    }
  });

table.insert(fingerprints, {
    category = 'microsoft',
    probes = {
      {
        path = '/exchweb/bin/auth/owalogon.asp',
        method = 'HEAD'
      },
      {
        path = '/images/outlook.jpg',
        method = 'HEAD'
      },
      {
        path = '/owa/8.1.375.2/themes/base/lgntopl.gif',
        method = 'HEAD'
      },
      {
        path = '/owa/',
        method = 'HEAD'
      },
    },
    matches = {
      {
        match = '',
        output = 'Outlook Web Access'
      }
    }
  });

table.insert(fingerprints, {
    category = 'microsoft',
    probes = {
      {
        path = '/tsweb/',
        method = 'HEAD'
      },
    },
    matches = {
      {
        match = '',
        output = 'Remote Desktop Web Connection'
      }
    }
  });

table.insert(fingerprints, {
    category = 'microsoft',
    probes = {
      {
        path = '/reportserver/',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Microsoft SQL Report Service'
      }
    }
  });
------------------------------------------------
----         NETWORK EQUIPMENT              ----
------------------------------------------------
-- Routers, switches, etc
table.insert(fingerprints, {
    category = 'network',
    probes = {
      {
        path = '/',
        method = 'GET'
      },
    },
    matches = {
      {
        match = 'realm="WRT54G"',
        output = 'Linksys WRT54g Wireless Router'
      }
    }
  });

table.insert(fingerprints, {
    category = 'network',
    probes = {
      {
        path = '/HW_logo.html',
        method = 'HEAD'
      },
    },
    matches = {
      {
        match = '',
        output = 'Huawei HG 530'
      }
    }
  });

table.insert(fingerprints, {
    category = 'network',
    probes = {
      {
        path = '/icons/icon_set_up_2701XX_01.gif',
        method = 'HEAD'
      },
      {
        path = '/icons/icon_homeportal_2701XX.gif',
        method = 'HEAD'
      },
      {
        path = '/es/images/nav_sl_home_network_01.gif',
        method = 'HEAD'
      },
      {
        path = '/en/images/nav_sl_home_network_01.gif',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = '2WIRE 2701HG'
      }
    }
  });

table.insert(fingerprints, {
    category = 'network',
    probes = {
      {
        path = '/images/stxx__xl.gif',
        method = 'HEAD'
      },
      {
        path = '/images/bbc__xl.gif',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Thomson TG585'
      }
    }
  });

-- HNAP Devices
table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/HNAP1/',
        method = 'GET'
      }
    },
    matches = {
      {
        match = '<ModelDescription>(.-)</ModelDescription>',
        output = '\\1'
      }
    }
  });

------------------------------------------------
----               ATTACKS                  ----
------------------------------------------------
-- These will search for and possibly exploit vulnerabilities.

table.insert(fingerprints, {
    category = 'attacks',
    probes = {
      {
        path = '/sdk/../../../../../../../etc/vmware/hostd/vmInventory.xml',
        method = 'GET',
        nopipeline = true
      },
      {
        path = '/sdk/%2E%2E/%2E%2E/%2E%2E/%2E%2E/%2E%2E/%2E%2E/%2E%2E/etc/vmware/hostd/vmInventory.xml',
        method = 'GET',
        nopipeline = true
      }
    },
    matches = {
      {
        match = '<ConfigRoot>',
        output = 'Path traversal in VMWare (CVE-2009-3733)'
      },
      {
        match = '',
        output = 'Possible path traversal in VMWare (CVE-2009-3733)'
      }
    }
  });

table.insert(fingerprints, {
    category = 'attacks',
    probes = {
      {
        path = '/../../../../../../../../../../etc/passwd',
        method = 'GET',
        nopipeline = true
      },
      {
        path = '/../../../../../../../../../../boot.ini',
        method = 'GET',
        nopipeline = true
      }
    },
    matches = {
      {
        match = 'root:',
        output = 'Simple path traversal in URI (Linux)'
      },
      {
        match = 'boot loader',
        output = 'Simple path traversal in URI (Windows)'
      },
      {
        match = '',
        output = 'Possible path traversal in URI'
      }
    }
  });

table.insert(fingerprints, {
    category = 'attacks',
    probes = {
      {
        path = '/.htaccess',
        method = 'GET'
      },
      {
        path = '/.htpasswd',
        method = 'GET'
      }
    },
    matches = {
      -- We look for a '200 OK' message on this one, because most Apache servers return an access denied
      {
        match = '200 OK',
        output = 'Incorrect permissions on .htaccess or .htpasswd files'
      }
    }
  });

table.insert(fingerprints, {
    category = 'attacks',
    probes = {
      {
        path = '/_vti_bin/',
        method = 'GET'
      },
      {
        path = '/_vti_cnf/',
        method = 'GET'
      },
      {
        path = '/_vti_log/',
        method = 'GET'
      },
      {
        path = '/_vti_pvt/',
        method = 'GET'
      },
      {
        path = '/_vti_txt/',
        method = 'GET'
      },
      {
        path = '/postinfo.html'
      },
      {
        path = '/_vti_bin/_vti_aut/author.dll'
      },
      {
        path = '/_vti_bin/_vti_aut/author.exe'
      },
      {
        path = '/_vti_bin/_vti_aut/dvwssr.dll'
      },
      {
        path = '/_vti_bin/_vti_adm/admin.dll'
      },
      {
        path = '/_vti_bin/_vti_adm/admin.exe'
      },
      {
        path = '/_vti_bin/fpcount.exe?Page=default.asp|Image=3'
      },
      {
        path = '/_vti_bin/shtml.dll'
      },
      {
        path = '/_vti_bin/shtml.exe'
      },
      {
        path = '/_vti_pvt/_x_todo.htm'
      },
      {
        path = '/_vti_pvt/_x_todoh.htm'
      },
      {
        path = '/_vti_pvt/access.cnf'
      },
      {
        path = '/_vti_pvt/administrator.pwd'
      },
      {
        path = '/_vti_pvt/administrators.pwd'
      },
      {
        path = '/_vti_pvt/authors.pwd'
      },
      {
        path = '/_vti_pvt/bots.cnf'
      },
      {
        path = '/_vti_pvt/botinfs.cnf'
      },
      {
        path = '/_vti_pvt/deptodoc.btr'
      },
      {
        path = '/_vti_pvt/doctodep.btr'
      },
      {
        path = '/_vti_pvt/frontpg.lck'
      },
      {
        path = '/_vti_pvt/linkinfo.cnf'
      },
      {
        path = '/_vti_pvt/service.cnf'
      },
      {
        path = '/_vti_pvt/service.grp'
      },
      {
        path = '/_vti_pvt/service.lck'
      },
      {
        path = '/_vti_pvt/service.pwd'
      },
      {
        path = '/_vti_pvt/Service.stp'
      },
      {
        path = '/_vti_pvt/services.cnf'
      },
      {
        path = '/_vti_pvt/services.org'
      },
      {
        path = '/_vti_pvt/structure.cnf'
      },
      {
        path = '/_vti_pvt/svcacl.cnf'
      },
      {
        path = '/_vti_pvt/users.pwd'
      },
      {
        path = '/_vti_pvt/uniqueperm.cnf'
      },
      {
        path = '/_vti_pvt/writeto.cnf'
      },
    },
    matches = {
      {
        match = '200',
        output = 'Frontpage file or folder'
      }
    }
  });

table.insert(fingerprints, {
    category = 'attacks',
    probes = {
      {
        path = '/.svn/',
        method = 'GET'
      },
      {
        path = '/.svn/text-base/.htaccess.svn-base',
        method = 'GET'
      },
      {
        path = '/.svn/text-base/.htpasswd.svn-base',
        method = 'GET'
      },
      {
        path = '/.svn/text-base/Web.config.svn-base',
        method = 'GET'
      }
    },
    matches = {
      {
        match = '200',
        output = 'Subversion folder'
      }
    }
  });

table.insert(fingerprints, {
    category = 'attacks',
    probes = {
      {
        path = '/.git/HEAD',
        method = 'GET'
      },
    },
    matches = {
      {
        match = 'ref: refs',
        output = 'Git folder'
      }
    }
  });

table.insert(fingerprints, {
    category = 'attacks',
    probes = {
      {
        path = '/.hg/requires',
        method = 'GET'
      },
    },
    matches = {
      {
        match = 'revlogv1',
        output = 'Mercurial folder'
      }
    }
  });

table.insert(fingerprints, {
    category = 'attacks',
    probes = {
      {
        path = '/.bzr/README',
        method = 'GET'
      },
    },
    matches = {
      {
        match = 'This is a Bazaar',
        output = 'Bazaar folder'
      }
    }
  });

table.insert(fingerprints, {
    category = 'attacks',
    probes = {
      {
        path = '/downloadFile.php',
        method = 'GET'
      },
      {
        path = '/BackupConfig.php',
        method = 'GET'
      }
    },
    matches = {
      {
        output = 'NETGEAR WNDAP350 2.0.1 to 2.0.9 potential file download and SSH root password disclosure'
      }
    }
  });

table.insert(fingerprints, {
    category = 'attacks',
    probes = {
      {
        path = '/cwhp/auditLog.do?file=..\\..\\..\\..\\..\\..\\..\\boot.ini',
        method = 'GET'
      },
      {
        path = '/cwhp/auditLog.do?file=..\\..\\..\\..\\..\\..\\..\\Program%20Files\\CSCOpx\\MDC\\Tomcat\\webapps\\triveni\\WEB-INF\\classes\\schedule.properties',
        method = 'GET'
      },
      {
        path = '/cwhp/auditLog.do?file=..\\..\\..\\..\\..\\..\\..\\Program%20Files\\CSCOpx\\lib\\classpath\\com\\cisco\\nm\\cmf\\dbservice2\\DBServer.properties',
        method = 'GET'
      },
      {
        path = '/cwhp/auditLog.do?file=..\\..\\..\\..\\..\\..\\..\\Program%20Files\\CSCOpx\\log\\dbpwdChange.log',
        method = 'GET'
      }
    },
    matches = {
      {
        match = 'boot loader',
        output = 'CiscoWorks (CuOM 8.0 and 8.5) Directory traversal (CVE-2011-0966) (Windows)'
      },
      {
        match = '',
        output = 'Possible CiscoWorks (CuOM 8.0 and 8.5) Directory traversal (CVE-2011-0966) (Windows)'
      }
    }
  });

table.insert(fingerprints, {
    category = 'attacks',
    probes = {
      {
        path = '..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f/var/mobile/Library/AddressBook/AddressBook.sqlitedb',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Possible iPhone/iPod/iPad generic file sharing app Directory Traversal (iOS)'
      }
    }
  });

table.insert(fingerprints, {
    category = 'attacks',
    probes = {
      {
        path = '/Info.live.htm',
        method = 'GET'
      }
    },
    matches = {
      {
        match = '200',
        output = 'Possible DD-WRT router Information Disclosure (BID 45598)'
      }
    }
  });

table.insert(fingerprints, {
    category = 'attacks',
    probes = {
      {
        path = '/CuteSoft_Client/CuteEditor/Load.ashx?type=image&file=../../../web.config',
        method = 'GET'
      }
    },
    matches = {
      {
        match = '200',
        output = 'Cute Editor ASP.NET Remote File Disclosure ( CVE 2009-4665 )'
      }
    }
  });

table.insert(fingerprints, {
    category = 'attacks',
    probes = {
      {
        path = '/plugins/PluginController.php?path=..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2fwindows%2fwin.ini%00',
        method = 'GET'
      }
    },
    matches = {
      {
        match = '200',
        output = 'OrangeHRM 2.6.3 Local File Inclusion '
      }
    }
  });

table.insert(fingerprints, {
    category = 'attacks',
    probes = {
      {
        path = '/tiki-listmovies.php?movie=../../../../../../etc/passwd%001234',
        method = 'GET'
      }
    },
    matches = {
      {
        match = '200',
        output = 'TikiWiki < 1.9.9 Directory Traversal Vulnerability'
      }
    }
  });

table.insert(fingerprints, {
    category = 'attacks',
    probes = {
      {
        path = '/index.php?option=com_jce&task=plugin&plugin=imgmanager&file=imgmanager&version=1576&cid=20',
        method = 'GET'
      }
    },
    matches = {
      {
        match = '2.0.11</title',
        output = 'Joomla JCE Extension 2.0.11 Remote Code Execution vulnerability'
      },
      {
        match = '2.0.12</title',
        output = 'Joomla JCE Extension 2.0.12 Remote Code Execution vulnerability'
      },
      {
        match = '2.0.13</title',
        output = 'Joomla JCE Extension 2.0.13 Remote Code Execution vulnerability'
      },
      {
        match = '2.0.14</title',
        output = 'Joomla JCE Extension 2.0.14 Remote Code Execution vulnerability'
      },
      {
        match = '2.0.15</title',
        output = 'Joomla JCE Extension 2.0.11 Remote Code Execution vulnerability'
      },
      {
        match = '1.5.7.10</title',
        output = 'Joomla JCE Extension 1.5.7.10 Remote Code Execution vulnerability'
      },
      {
        match = '1.5.7.10</title',
        output = 'Joomla JCE Extension 1.5.7.10 Remote Code Execution vulnerability'
      },
      {
        match = '1.5.7.11</title',
        output = 'Joomla JCE Extension 1.5.7.11 Remote Code Execution vulnerability'
      },
      {
        match = '1.5.7.12</title',
        output = 'Joomla JCE Extension 1.5.7.12 Remote Code Execution vulnerability'
      },
      {
        match = '1.5.7.13</title',
        output = 'Joomla JCE Extension 1.5.7.13 Remote Code Execution vulnerability'
      },
      {
        match = '1.5.7.14</title',
        output = 'Joomla JCE Extension 1.5.7.14 Remote Code Execution vulnerability'
      }
   }
  });

table.insert(fingerprints, {
    category = 'attacks',
    probes = {
      {
        path = '/d41d8cd98f00b204e9800998ecf8427e.php',
        method = 'GET'
      }
    },
    matches = {
      {
        match = '200',
        output = 'Seagate BlackArmorNAS 110/220/440 Administrator Password Reset Vulnerability'
      }
    }
  });

-- HNAP Authentication Bypass
table.insert(fingerprints, {
    category = 'attacks',
    probes = {
      {
        path = '/bsc_lan.php?NO_NEED_AUTH=1&AUTH_GROUP=0',
        method = 'GET'
      }
    },
    matches = {
      {
        dontmatch = '<a href="http://www%.dlink%.com"',
        match = '^HTTP/1.[01] 200 OK\r\n.*Server: Embedded HTTP Server',
        output = 'D-Link Router Vulnerable to Authentication Bypass',
      },
      {
        dontmatch = '<a href="http://www%.dlink%.com"',
        match = '^HTTP/1.[01] 200 OK\r\n.*Server: Virtual Web 0.9',
        output = 'D-Link Router Vulnerable to Authentication Bypass',
      },
    }
  });

-- Rompager info disclosure
table.insert(fingerprints, {
    category = 'attacks',
    probes = {
      {
        path = '/rom-0',
        method = 'GET'
      }
    },
    matches = {
      {
        match = 'dbgarea',
        output = 'RomPager Embedded Web Server information disclosure (CVE-2014-4019)'
      },
      {
        match = 'spt%.dat',
        output = 'RomPager Embedded Web Server information disclosure (CVE-2014-4019)'
      },
      {
        match = 'autoexec%.net',
        output = 'RomPager Embedded Web Server information disclosure (CVE-2014-4019)'
      },
    }
  });

-- Progress Telerik UI for ASP.NET CVE-2017-9248
table.insert(fingerprints, {
    category = 'attacks',
    probes = {
      {
        path = '/Telerik.Web.UI.DialogHandler.aspx?dp=////',
        method = 'GET'
      },
      {
        path = '/Telerik.Web.UI.DialogHandler.ashx?dp=////',
        method = 'GET'
      },
      {
        path = '/DesktopModules/Admin/RadEditorProvider/DialogHandler.aspx?dp=////',
        method = 'GET'
      }
    },
    matches = {
      {
        dontmatch = 'cannot be less than zero',
        match = 'Base%-64',
        output = 'Progress Telerik UI for ASP.NET Cryptographic Weakness (CVE-2017-9248)'
      }
    }
  });

table.insert(fingerprints, {
    category = 'attacks',
    probes = {
      {
        path = '/uir//etc/passwd',
        method = 'GET'
      }
    },
    matches = {
      {
        match = '200',
        output = 'Possible D-Link router directory traversal vulnerability (CVE-2018-10822)'
      },
      {
        match = 'root:',
	output = 'D-Link router directory traversal vulnerability (CVE-2018-10822)'
      }
     }
  });

table.insert(fingerprints, {
    category = 'attacks',
    probes = {
      {
        path = '/uir//tmp/csman/0',
        method = 'GET'
      }
    },
    matches = {
      {
        match = '200',
        output = 'Possible D-Link router plaintext password file exposure (CVE-2018-10824)'
      }
    }
  });

table.insert(fingerprints, {
    category = 'attacks',
    probes = {
      {
        path = '/cgi-bin/export_debug_msg.exp',
        method = 'GET'
      },
      {
        path = '/cgi-bin/config.exp',
        method = 'GET'
      }
    },
    matches = {
      {
        match = '200 OK',
        output = 'Cisco RV320/RV325 Unauthenticated Diagnostic Data & Configuration Export (CVE-2019-1653)'
      }
    }
  });

table.insert(fingerprints, {
    category = 'attacks',
    probes = {
      {
        path = '/',
        method = 'GET'
      }
    },
    matches = {
      {
        match = 'var admin_name=".*";\nvar guest_name=".*";\nvar admin_pwd=".*";',
        output = 'Cisco RV110W Wireless-N VPN Firewall Password Disclosure (CVE-2014-0683)'
      }
    }
  });
------------------------------------------------
----        Open Source CMS checks          ----
------------------------------------------------

-- Wordpress versions (Scraping readme file)
table.insert(fingerprints, {
  category = 'cms',
  probes = {
    {
      path = '/readme.html',
      method = 'GET'
    }
  },
  matches = {
    {
      match = '<img alt="WordPress" src=".+wordpress.+".+[V|v]ersion ([0-9 .]*)',
      output = 'Wordpress version: \\1'
    }
  }
});

-- Wordpress versions (Scraping metatags and wp-includes)
table.insert(fingerprints, {
  category = 'cms',
  probes = {
    {
      path = '/',
      method = 'GET'
    }
  },
  matches = {
    {
      match = '<meta name="generator" content="WordPress ([0-9 .]*)" />',
      output = 'WordPress version: \\1'
    },
    {
      match = '/wp-includes/js/wp-emoji-release.min.js?ver=([0-9 .]*)',
      output = 'WordPress version: \\1'
    }
  }
});

-- Wordpress versions (Scraping rss feed)
table.insert(fingerprints, {
  category = 'cms',
  probes = {
    {
      path = '?feed=rss',
      method = 'GET'
    },
    {
      path = '?feed=rss2',
      method = 'GET'
    },
    {
      path = '?feed=atom',
      method = 'GET'
    },
    {
      path = '/feed',
      method = 'GET'
    },
    {
      path = '/feed/',
      method = 'GET'
    },
    {
      path = '/feed/rss',
      method = 'GET'
    },
    {
      path = '/feed/rss2',
      method = 'GET'
    },
    {
      path = '/feed/atom',
      method = 'GET'
    }
  },
  matches = {
    {
      match = '[v|V]=([0-9 .]*)</generator>',
      output = 'Wordpress version: \\1'
    }
  }
});

-- Wordpress detection indentified by Version-specific files.
-- These are the 6 new web-accessible files that was added in each release.

table.insert(fingerprints, {
  category = 'cms',
  probes = {
    {
      path = "/wp-includes/images/rss.png"
    }
  },
  matches = {
    {
      output = "Wordpress version 2.2 found."
    }
  }
});

table.insert(fingerprints, {
  category = 'cms',
  probes = {
    {
      path = "/wp-includes/js/scriptaculous/sound.js"
    }
  },
  matches = {
    {
      output = "Wordpress version 2.3 found."
    }
  }
});

table.insert(fingerprints, {
  category = 'cms',
  probes = {
    {
      path = "/wp-includes/js/jquery/suggest.js"
    }
  },
  matches = {
    {
      output = "Wordpress version 2.5 found."
    }
  }
});

table.insert(fingerprints, {
  category = 'cms',
  probes = {
    {
      path = "/wp-includes/images/blank.gif"
    }
  },
  matches = {
    {
      output = "Wordpress version 2.6 found."
    }
  }
});

table.insert(fingerprints, {
  category = 'cms',
  probes = {
    {
      path = "/wp-includes/js/comment-reply.js"
    }
  },
  matches = {
    {
      output = "Wordpress version 2.7 found."
    }
  }
});

table.insert(fingerprints, {
  category = 'cms',
  probes = {
    {
      path = "/wp-includes/js/codepress/codepress.js"
    }
  },
  matches = {
    {
      output = "Wordpress version 2.8 found."
    }
  }
});


-- Broad wordpress version identification (Gives major only versions)
table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/wp-login.php'
      },
      {
        path = '/wordpress/wp-login.php'
      },
      {
        path = '/blog/wp-login.php'
      },
      {
        path = '/administrator/wp-login.php'
      },
      {
        path = '/weblog/wp-login.php'
      },
      {
        path = '/wp-admin/upgrade.php'
      }
    },
    matches = {
      {
        match = '[ver|version]=20080708',
        output = 'WordPress 2.6.x found'
      },
      {
        match = '[ver|version]=20081210',
        output = 'WordPress 2.7.x found'
      },
      {
        match = '[ver|version]=20090514',
        output = 'WordPress 2.8.x found'
      },
      {
        match = '[ver|version]=20091217',
        output = 'WordPress 2.9.x found'
      },
      {
        match = '[ver|version]=20100601',
        output = 'WordPress 3.0.x found'
      },
      {
        match = '[ver|version]=20110121',
        output = 'WordPress 3.1.x found'
      },
      {
        match = '[ver|version]=20121105',
        output = 'WordPress 3.7.x found'
      },
      {
        output = 'Wordpress login page.'
      }
    }
  });

-- ZenCart version detection
table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/docs/'
      },
      {
        path = '/store/docs/'
      },
      {
        path = '/zencart/docs/'
      },
      {
        path = '/cart/docs/'
      }
    },
    matches = {
      {
        match = '.*">Changelog for v(%d-%..-) %(changed files%)</a>',
        output = 'ZenCart, version \\1'
      }
    }
  });

-- Broad phpBB versions
table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/docs/CHANGELOG.html'
      },
      {
        path = '/forum/docs/CHANGELOG.html'
      },
      {
        path = '/forums/docs/CHANGELOG.html'
      },
      {
        path = '/board/docs/CHANGELOG.html'
      },
      {
        path = '/boards/docs/CHANGELOG.html'
      }
    },
    matches = {
      {
        match = 'Changes since (%d-%..-)</a>',
        output = 'phpBB version slightly newer than \\1'
      },
      {
        match = '<meta name="description" content="phpBB (%d-%..-) Changelog"',
        output = 'phpBB, version \\1'
      },
      {
        match = 'Changes since (%d)',
        output = 'phpBB versioning \\1'
      },
    }
  });

-- tinymce / changelog
table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/changelog.txt'
      },
      {
        path = '/tinymce/changelog.txt'
      },
    },
    matches = {
      {
        match = 'Version (.-) ',
        output = 'Version \\1'
      },
      {
        output = 'Interesting, a changelog.'
      }
    }
  });

-- interesting README  files
table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/readme.html'
      },
      {
        path = '/pligg/readme.html'
      },
      {
        path = '/digg/readme.html'
      },
      {
        path = '/news/readme.html'
      },
    },
    matches = {
      {
        match = '<h1>Pligg Content Management System</h1>%s*<h2>Version (.-)</h2>',
        output = 'Pligg version \\1'
      },
      {
        match = '<br /> Version (.-)\n',
        output = 'WordPress version \\1'
      },
      {
        output = 'Interesting, a readme.'
      }
    }
  });

-- They're kind enough to tell us in the meta tags (used for the author's stats)
table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/'
      },
      {
        path = '/forum/'
      },
      {
        path = '/site/'
      },
      {
        path = '/website/'
      },
      {
        path = '/store/'
      },
      {
        path = '/webstore/'
      },
      {
        path = '/comic/'
      },
      {
        path = '/wiki/'
      },
      {
        path = '/mediawiki/'
      },
      {
        path = '/Mediawiki/'
      },
      {
        path = '/MediaWiki/'
      },
      {
        path = '/wordpress/'
      },
      {
        path = '/blog/'
      },
      {
        path = '/cms/'
      },
      {
        path = '/comiccms/'
      },
      {
        path = '/weblog/'
      },
      {
        path = '/joomla/'
      },
      {
        path = '/administrator/'
      },
      {
        path = '/openx/www/admin/index.php'
      },
      {
        path = '/www/admin/index.php'
      },
      {
        path = '/ads/www/admin/index.php'
      },
      {
        path = '/adserver/www/admin/index.php'
      },
      {
        path = '/splashfrog/'
      },
      {
        path = '/pligg/'
      },
      {
        path = '/vanilla/'
      },
      {
        path = '/vanillaforum/'
      },
      {
        path = '/vanillaforums/'
      },
      {
        path = '/statusnet/'
      },
      {
        path = '/xoda/'
      },
      {
        path = '/trac/'
      },
      {
        path = '/lime/'
      },
      {
        path = '/survey/'
      },
      {
        path = '/limesurvey/'
      },
      {
        path = '/openvbx/'
      },
      {
        path = '/getsimple/'
      },
      {
        path = '/ecoder/'
      },
    },
    matches = {
      {
        match = '<meta name="generator" content="Bluefish 2.0.1" ',
        output = '\\1'
      },
      {
        match = '<h1>ecoder v(.-)</h1>',
        output = 'ecoder v\\1'
      },
      {
        match = '<a href="http://www.splashfrog.com" target="_blank">Splash Frog WMS v(.-)</a>',
        output = 'Splash Frog WMS v\\1'
      },
      {
        match = '<a href="http://status.net/">StatusNet</a> microblogging software, version (.-),',
        output = 'StatusNet v\\1'
      },
      {
        match = '<script src=".*/applications/vanilla/js/options.js%?v%=(.-)" type="text/javascript">',
        output = 'Vanilla Forums v\\1'
      },
      {
        match = 'about"><strong>Trac (.-)</strong></a>',
        output = 'Trac version \\1'
      },
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/cmspages.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = '2Point Solutions CMS'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/sc_webcat/ecat/cms_view.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Webcat'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/kbcat.cgi',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'ActivDesk'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/workorder/FileDownload.jsp',
        method = 'GET'
      },
      {
        path = '/sd/workorder/FileDownload.jsp',
        method = 'GET'
      }
    },
    matches = {
      {
        match = '500',
        output = 'ManageEngine Support Center Plus'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/sections/reference.inc.php',
        method = 'GET'
      }
    },
    matches = {
      {
        match = '200',
        output = 'BrewBlogger'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/store.php?action=view_cart',
        method = 'GET'
      }
    },
    matches = {
      {
        match = '200',
        output = 'AiCart'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/catalog/main.php?cat_id=',
        method = 'GET'
      }
    },
    matches = {
      {
        match = '200',
        output = 'Catalog Builder'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/index.php?cat_id=1',
        method = 'GET'
      }
    },
    matches = {
      {
        match = 'powered by CubeCart',
        output = 'CubeCart'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/forum_answer.php?que_id=1',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Guru JustAnswer'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/templates1/view_product.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'HB ECommerce'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/escort-profile.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'First Escort Marketing CMS'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/pages/indexheader.php',
        method = 'HEAD'
      },
      {
        path = '/pages/searcher.php',
        method = 'HEAD'
      },
      {
        path = '/pages/indexviewentry.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Green Pants CMS'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/tinymcpuk/filemanager/browser.html',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'CMS Lokomedia'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/admin/libraries/ajaxfilemanager/ajaxfilemanager.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Log1 CMS'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/leftmenubody.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Quicktech'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/dsp_page.cfm',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Alcassofts SOPHIA CMS'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/zikula/index.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Zikula CMS'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/system/admin/header.php',
        method = 'HEAD'
      },
      {
        path = '/system/admin/comments_items.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Habari Blog'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/scripts/fckeditor/editor/filemanager/connectors/test.html',
        method = 'HEAD'
      },
      {
        path = '/scripts/fckeditor/editor/filemanager/connectors/uploadtest.html',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Digitalus CMS/FCKEditor File upload'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/fckeditor/editor/filemanager/connectors/test.html',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'phpmotion/FCKeditor File upload'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/fckeditor/editor/filemanager/upload/test.html',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Geeklog/FCKeditor File upload'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/admin/view/javascript/fckeditor/editor/filemanager/connectors/test.html',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'OpenCart/FCKeditor File upload'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/fckeditor/editor/filemanager/connectors/php/config.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'DM File Manager/FCKeditor File upload'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/includes/FCKeditor/editor/filemanager/browser/default/connectors/php/connector.php',
        method = 'HEAD'
      },
      {
        path = '/includes/FCKeditor/editor/filemanager/browser/default/connectors/asp/connector.asp',
        method = 'HEAD'
      },
      {
        path = '/includes/FCKeditor/editor/filemanager/browser/default/connectors/aspx/connector.aspx',
        method = 'HEAD'
      },
      {
        path = '/includes/FCKeditor/editor/filemanager/browser/default/connectors/cfm/connector.cfm',
        method = 'HEAD'
      },
      {
        path = '/includes/FCKeditor/editor/filemanager/browser/default/connectors/lasso/connector.lasso',
        method = 'HEAD'
      },
      {
        path = '/includes/FCKeditor/editor/filemanager/browser/default/connectors/perl/connector.cgi',
        method = 'HEAD'
      },
      {
        path = '/includes/FCKeditor/editor/filemanager/browser/default/connectors/py/connector.py',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'PHPnuke/Remote File Download'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/FCKEditor/editor/filemanager/browser/default/connectors/test.html',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'EgO or osCMax/FCKeditor File upload'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/admin/includes/tiny_mce/plugins/tinybrowser/upload.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'CompactCMS or B-Hind CMS/FCKeditor File upload'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/Backstage/Components/FreeTextBox/ftb.imagegallery.aspx',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Luftguitar CMS/File upload'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/_plugin/fckeditor/editor/filemanager/connectors/test.html',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'SweetRice/FCKeditor File upload'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/html/news_fckeditor/editor/filemanager/upload/php/upload.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'cardinalCms/FCKeditor File upload'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/fckeditor/editor/filemanager/connectors/test.html',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'LightNEasy/FCKeditor File upload'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/admin/includes/FCKeditor/editor/filemanager/upload/test.html',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'ASP Simple Blog / FCKeditor File Upload'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/uploadsnaps.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'ZeeMatri/File upload'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/upload/includes/js/files/upload.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Digital College/File upload'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/tinybrowser/upload.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Tinybrowser Remote File Upload'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/editor/editor/filemanager/upload/test.html',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Tadbir / File Upload'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/photogallery_open.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Heaven Soft CMS'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/Providers/HtmlEditorProviders/Fck/fcklinkgallery.aspx',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'DotNetNuke / File Upload'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/assetmanager/assetmanager.asp',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Asset Manager/Remote File upload'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/Final/login/ava_upl.php',
        method = 'HEAD'
      },
      {
        path = '/Final/login/ava_upl2.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'CH-CMS'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/spaw/demo.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'SpawCMS/Remote File upload'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/admin/jscript/upload.php',
        method = 'HEAD'
      },
      {
        path = '/admin/jscript/upload.html',
        method = 'HEAD'
      },
      {
        path = '/admin/jscript/upload.pl',
        method = 'HEAD'
      },
      {
        path = '/admin/jscript/upload.asp',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Lizard Cart/Remote File upload'
      }
    }
  });

-- Apache Ambari Web UI
table.insert(fingerprints, {
    category = 'management',
    probes = {
      {
        path = '/',
        method = 'GET'
      },
    },
    matches = {
      {
        match = '<title>Ambari</title>',
        output = 'Apache Ambari WebUI'
      }
    }
  });

-- Apache Oozie Web Console
table.insert(fingerprints, {
    category = 'management',
    probes = {
      {
        path = '/oozie/',
        method = 'GET'
      },
    },
    matches = {
      {
        match = '<title>Oozie Web Console</title>',
        output = 'Apache Oozie Web Console'
      }
    }
  });

-- Apache Ranger Web UI
table.insert(fingerprints, {
    category = 'management',
    probes = {
      {
        path = '/login.jsp',
        method = 'GET'
      },
    },
    matches = {
      {
        match = '<title>%s*Ranger %- Sign In%s*</title>',
        output = 'Apache Ranger WebUI'
      }
    }
  });

-- Cloudera Hue
table.insert(fingerprints, {
    category = 'management',
    probes = {
      {
        path = '/about/',
        method = 'GET'
      },
    },
    matches = {
      {
        match = 'Hue&trade;%s(.-)%s[-]%s<a href="http://gethue%.com"',
        output = 'Cloudera Hue \\1'
      }
    }
  });

-- Cloudera Manager login page
table.insert(fingerprints, {
    category = 'management',
    probes = {
      {
        path = '/cmf/login',
        method = 'GET'
      },
    },
    matches = {
      {
        match = 'var%s+clouderaManager%s*=%s*{.-version:%s*\'(.-)\'',
        output = 'Cloudera Manager version \\1 '
      }
    }
  });

-- Hadoop MapReduce JobHistory WebUI
table.insert(fingerprints, {
    category = 'management',
    probes = {
      {
        path = '/jobhistory',
        method = 'GET'
      },
    },
    matches = {
      {
        match = '<title>%s*JobHistory%s*</title>',
        output = 'Hadoop MapReduce JobHistory WebUI'
      }
    }
  });

-- Hadoop YARN Resource Manager
table.insert(fingerprints, {
    category = 'management',
    probes = {
      {
        path = '/cluster/cluster',
        method = 'GET'
      },
    },
    matches = {
      {
        match = 'ResourceManager state:.-<td>%s*([^%s<]*)'
                .. '.-ResourceManager version:.-<td>%s*([^%s<]*)'
                .. '.-Hadoop version:.-<td>%s*([^%s<]*)',
        output = 'Hadoop YARN Resource Manager version \\2, state "\\1", Hadoop version \\3'
      },
    }
  });

-- Hadoop Node Resource Manager
table.insert(fingerprints, {
    category = 'info',
    probes = {
      {
        path = '/node',
        method = 'GET'
      },
    },
    matches = {
      {
        match = 'Node Manager Version:.-<td>%s*([^%s<]*)'
                .. '.-Hadoop Version:.-<td>%s*([^%s<]*)',
        output = 'Hadoop YARN Node Manager version \\1, Hadoop version \\2'
      },
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/databases/acidcat_3.mdb',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Acidcat CMS Database'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/mdb-database/dblog.mdb',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'dBlog Database'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/db/users.mdb',
        method = 'HEAD'
      },
      {
        path = '/db/'
      }
    },
    matches = {
      {
        match = '',
        output = 'BlogWorx Database'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/infusions/avatar_studio/avatar_studio.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'PHP-Fusion Mod avatar_studio'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/bnnr.php',
        method = 'HEAD'
      },
      {
        path = '/vb/bnnr.php',
        method = 'HEAD'
      },
      {
        path = '/forum/bnnr.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'vBulletin ads_saed'
      }
    }
  });

table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/weblink_cat_list.php',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'WHMCompleteSolution CMS'
      }
    }
  });

-- Drupal signatures
table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/',
        method = 'GET'
      }
    },
    matches = {
      {
        match = ' src="/sites/all/themes/',
        output = 'Drupal signature'
      },
      {
        match = ' src="/sites/all/modules/',
        output = 'Drupal signature'
      },
      {
        match = ' href="/sites/all/themes/',
        output = 'Drupal signature'
      },
      {
        match = 'jQuery.extend(Drupal.settings,',
        output = 'Drupal signature'
      }
    }
  });

-- Drupal files
table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/UPGRADE.txt'
      },
      {
        path = '/INSTALL.txt'
      },
      {
        path = '/MAINTENERS.txt'
      },
      {
        path = '/INSTALL.mysql.txt'
      },
      {
        path = '/INSTALL.pgsql.txt'
      },
      {
        path = '/update.php'
      }
    },
    matches = {
      {
        match = 'Drupal ',
        output = 'Drupal file'
      }
    }
  });

-- Joomla versions
table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        -- Detects versions >= 1.60
        path = '/administrator/manifests/files/joomla.xml',
        method = 'GET'
      },
      {
        -- Detects version >= 1.50 and <= 1.5.26
        path = '/language/en-GB/en-GB.xml',
        method = 'GET'
      },
      {
        -- Detects version < 1.50
        path = '/modules/custom.xml',
        method = 'GET'
      }
    },
    matches = {
      {
        match = '<version>(.-)</version>',
        output = 'Joomla version \\1'
      }
    }
  });

-- Joomla!
table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/htaccess.txt'
      },
      {
        path = '/templates/system/css/toolbar.css'
      },
      {
        path = '/templates/beez/css/template_rtl.css'
      }
    },
    matches = {
      {
        match = 'Joomla!',
        output = 'Joomla!'
      }
    }
  });

-- Drupal changelog
table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/CHANGELOG.txt'
      }
    },
    matches = {
      {
        match = 'Drupal (%d..-),',
        output = 'Drupal v1'
      }
    }
  });

-- Drupal version
table.insert(fingerprints, {
  category = 'cms',
  probes = {
    {
      -- Must be executed on both ports 80, 443 for accurate results
      path = '/',
      method = 'GET'
    }
  },
  matches = {
    {
      match = '<meta name="[G|g]enerator" content="Drupal ([0-9 .]*)',
      output = 'Drupal version \\1'
    }
  }
});

-- Moodle
table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/pix/moodlelogo.gif',
        method = 'HEAD'
      },
      {
        path = '/admin/environment.xml',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'Moodle files'
      }
    }
  });

-- typo3
table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/typo3/index.php',
        method = 'GET'
      },
      {
        path = '/typo3/README.txt',
        method = 'GET'
      },
      {
        path = '/t3lib/README.txt',
        method = 'GET'
      },
      {
        path = '/typo3/sysext/t3skin/images/login/typo3logo-white-greyback.gif',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = 'Login to the TYPO3',
        output = 'Typo3 login page'
      },
      {
        match = 'TYPO3 Backend Administration',
        output = 'Typo3 readme file'
      },
      {
        match = 'TYPO3 Library',
        output = 'Typo3 Library readme'
      },
      {
        match = '',
        output = 'Typo3 Installation'
      },
    }
  });

------------------------------------------------
----                 MAIL                   ----
------------------------------------------------

-- SquirrelMail
table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/squirrelmail/src/login.php',  -- Might return login page with version info
        method = 'GET'
      },
      {
        path = '/webmail/src/login.php',  -- Might return login page with version info
        method = 'GET'
      },
      {
        path = '/src/login.php',  -- Might return login page with version info
        method = 'GET'
      }
    },
    matches = {
      {
        match = '<small>([^<]*)<br />',  -- version extraction (squirrelMail)
        output = '\\1'
      },
      {
        match = 'squirrelmail',
        output = 'SquirrelMail'
      }
    }
  });

-- SquirrelMail files
table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/squirrelmail/images/sm_logo.png',  -- Standard logo file
        method = 'HEAD'
      },
      {
        path = '/webmail/images/sm_logo.png',   -- Standard logo file
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'SquirrelMail'
      }
    }
  });

-- RoundCube
table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/',
        method = 'GET'
      },
      {
        path = '/program/',
        method = 'GET'
      }
    },
    matches = {
      {
        match = '<title>Index of /program</title>',
        output = 'RoundCube (Directory listing)'
      },
      {
        match = 'rcube_webmail', -- RoundCube
        output = 'RoundCube'
      },
    }
  });

-- RoundCube file
table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/skins/default/images/roundcube_logo.png',  -- Standard logo file
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'RoundCube'
      }
    }
  });

-- Bitwarden Vault
table.insert(fingerprints, {
    category = 'general',
    probes = {
      {
        path = '/manifest.json',
        method = 'GET'
      }
    },
    matches = {
      {
        match = '([\'"])name%1%s*:%s*[\'"][Bb]itwarden',
        output = 'Bitwarden Vault Manifest File'
      },
      {
        match = '',
        output = 'Manifest JSON File'
      },
    }
  });

------------------------------------------------
----           UNCATEGORIZED                ----
------------------------------------------------

table.insert(fingerprints, {
    category = 'uncategorized',
    probes = {
      {
        path = '/archive/flash:home/html/images/Cisco_logo.gif',
        method = 'HEAD'
      },
    },
    matches = {
      {
        match = '',
        output = 'Cisco SDM'
      }
    }
  });

table.insert(fingerprints, {
    category = 'uncategorized',
    probes = {
      {
        path = '/Default?MAIN=DEVICE',
        method = 'HEAD'
      },
      {
        path = '/TopAccess/images/RioGrande/Rio_PPC.gif',
        method = 'HEAD'
      }
    },
    matches = {
      {
        match = '',
        output = 'TopAccess Toshiba e-Studio520'
      }
    }
  });

table.insert(fingerprints, {
    category = 'uncategorized',
    probes = {
      {
        path = '/jwsappmngr.jnlp',
        method = 'HEAD'
      },
      {
        path = '/nfdesktop.jnlp',
        method = 'HEAD'
      },
      {
        path = '/nfservlets/servlet/SPSRouterServlet/',
        method = 'HEAD'
      },
    },
    matches = {
      {
        match = '',
        output = 'netForensics'
      }
    }
  });

table.insert(fingerprints, {
    category = 'uncategorized',
    probes = {
      {
        path = '/na_admin/styles/dfm.css',
        method = 'HEAD'
      },
    },
    matches = {
      {
        match = '',
        output = 'NetworkAppliance NetApp Release 6.5.3P4'
      }
    }
  });

------------------------------------------------
----    MISCELLANEOUS ITEMS OF INTEREST     ----
------------------------------------------------

-- Moodle files
table.insert(fingerprints, {
    category = 'miscellaneous',
    probes = {
      {
        path = '/lib/db/install.xml'
      },
      {
        path = '/lib/thirdpartylibs.xml'
      },
      {
        path = '/local/readme.txt'
      }
    },
    matches = {
      {
        match = 'XMLDB file for core Moodle tables',
        output = 'Moodle db installation file'
      },
      {
        match = '<libraries>',
        output = 'Moodle thirdpartylibs.xml'
      },
      {
        match = 'This file is part of Moodle',
        output = 'Moodle local/readme.txt'
      }
    }
  });

-- interesting README  files
table.insert(fingerprints, {
    category = 'miscellaneous',
    probes = {
      {
        path = '/README'
      },
      {
        path = '/README.txt'
      },
      {
        path = '/xoda/README'
      },
      {
        path = '/docs/README'
      },
      {
        path = '/documents/README'
      },
      {
        path = '/dms/README'
      },
      {
        path = '/status/README'
      },
      {
        path = '/statusnet/README'
      },
      {
        path = '/twitter/README'
      },
    },
    matches = {
      {
        match = 'StatusNet (.-) ',
        output = 'StatusNet README version \\1'
      },
      {
        match = 'XODA.*Changelog%s---------%s(%d.-):',
        output = 'XODA \\1'
      },
      {
        output = 'Interesting, a readme.'
      }
    }
  });

table.insert(fingerprints, {
    category = 'miscellaneous',
    probes = {
      {
        path = '/0/',
        method = 'GET'
      },
      {
        path = '/1/',
        method = 'GET'
      },
      {
        path = '/2/',
        method = 'GET'
      },
      {
        path = '/3/',
        method = 'GET'
      },
      {
        path = '/4/',
        method = 'GET'
      },
      {
        path = '/5/',
        method = 'GET'
      },
      {
        path = '/6/',
        method = 'GET'
      },
      {
        path = '/7/',
        method = 'GET'
      },
      {
        path = '/8/',
        method = 'GET'
      },
      {
        path = '/9/',
        method = 'GET'
      },
      {
        path = '/10/',
        method = 'GET'
      },
      {
        path = '/a/',
        method = 'GET'
      },
      {
        path = '/b/',
        method = 'GET'
      },
      {
        path = '/c/',
        method = 'GET'
      },
      {
        path = '/d/',
        method = 'GET'
      },
      {
        path = '/e/',
        method = 'GET'
      },
      {
        path = '/f/',
        method = 'GET'
      },
      {
        path = '/g/',
        method = 'GET'
      },
      {
        path = '/h/',
        method = 'GET'
      },
      {
        path = '/i/',
        method = 'GET'
      },
      {
        path = '/j/',
        method = 'GET'
      },
      {
        path = '/k/',
        method = 'GET'
      },
      {
        path = '/l/',
        method = 'GET'
      },
      {
        path = '/m/',
        method = 'GET'
      },
      {
        path = '/n/',
        method = 'GET'
      },
      {
        path = '/o/',
        method = 'GET'
      },
      {
        path = '/p/',
        method = 'GET'
      },
      {
        path = '/q/',
        method = 'GET'
      },
      {
        path = '/r/',
        method = 'GET'
      },
      {
        path = '/s/',
        method = 'GET'
      },
      {
        path = '/t/',
        method = 'GET'
      },
      {
        path = '/u/',
        method = 'GET'
      },
      {
        path = '/v/',
        method = 'GET'
      },
      {
        path = '/w/',
        method = 'GET'
      },
      {
        path = '/x/',
        method = 'GET'
      },
      {
        path = '/y/',
        method = 'GET'
      },
      {
        path = '/z/',
        method = 'GET'
      },
      {
        path = '/acceso/',
        method = 'GET'
      },
      {
        path = '/access/',
        method = 'GET'
      },
      {
        path = '/accesswatch/',
        method = 'GET'
      },
      {
        path = '/acciones/',
        method = 'GET'
      },
      {
        path = '/account/',
        method = 'GET'
      },
      {
        path = '/accounting/',
        method = 'GET'
      },
      {
        path = '/active/',
        method = 'GET'
      },
      {
        path = '/activex/',
        method = 'GET'
      },
      {
        path = '/admcgi/',
        method = 'GET'
      },
      {
        path = '/admisapi/',
        method = 'GET'
      },
      {
        path = '/AdvWebAdmin/',
        method = 'GET'
      },
      {
        path = '/agentes/',
        method = 'GET'
      },
      {
        path = '/Agent/',
        method = 'GET'
      },
      {
        path = '/Agents/',
        method = 'GET'
      },
      {
        path = '/AlbumArt_/',
        method = 'GET'
      },
      {
        path = '/AlbumArt/',
        method = 'GET'
      },
      {
        path = '/Album/',
        method = 'GET'
      },
      {
        path = '/allow/',
        method = 'GET'
      },
      {
        path = '/analog/',
        method = 'GET'
      },
      {
        path = '/anthill/',
        method = 'GET'
      },
      {
        path = '/apache/',
        method = 'GET'
      },
      {
        path = '/api/',
        method = 'GET'
      },
      {
        path = '/api-docs/',
        method = 'GET'
      },
      {
        path = '/app/',
        method = 'GET'
      },
      {
        path = '/applets/',
        method = 'GET'
      },
      {
        path = '/appl/',
        method = 'GET'
      },
      {
        path = '/application/',
        method = 'GET'
      },
      {
        path = '/applications/',
        method = 'GET'
      },
      {
        path = '/applmgr/',
        method = 'GET'
      },
      {
        path = '/apply/',
        method = 'GET'
      },
      {
        path = '/appsec/',
        method = 'GET'
      },
      {
        path = '/apps/',
        method = 'GET'
      },
      {
        path = '/archive/',
        method = 'GET'
      },
      {
        path = '/archives/',
        method = 'GET'
      },
      {
        path = '/ar/',
        method = 'GET'
      },
      {
        path = '/asa/',
        method = 'GET'
      },
      {
        path = '/asp/',
        method = 'GET'
      },
      {
        path = '/atc/',
        method = 'GET'
      },
      {
        path = '/aut/',
        method = 'GET'
      },
      {
        path = '/authadmin/',
        method = 'GET'
      },
      {
        path = '/auth/',
        method = 'GET'
      },
      {
        path = '/author/',
        method = 'GET'
      },
      {
        path = '/authors/',
        method = 'GET'
      },
      {
        path = '/aw/',
        method = 'GET'
      },
      {
        path = '/ayuda/',
        method = 'GET'
      },
      {
        path = '/b2-include/',
        method = 'GET'
      },
      {
        path = '/backend/',
        method = 'GET'
      },
      {
        path = '/bad/',
        method = 'GET'
      },
      {
        path = '/banca/',
        method = 'GET'
      },
      {
        path = '/banco/',
        method = 'GET'
      },
      {
        path = '/bank/',
        method = 'GET'
      },
      {
        path = '/banner01/',
        method = 'GET'
      },
      {
        path = '/banner/',
        method = 'GET'
      },
      {
        path = '/banners/',
        method = 'GET'
      },
      {
        path = '/bar/',
        method = 'GET'
      },
      {
        path = '/batch/',
        method = 'GET'
      },
      {
        path = '/bb-dnbd/',
        method = 'GET'
      },
      {
        path = '/bbv/',
        method = 'GET'
      },
      {
        path = '/bdata/',
        method = 'GET'
      },
      {
        path = '/bdatos/',
        method = 'GET'
      },
      {
        path = '/beta/',
        method = 'GET'
      },
      {
        path = '/billpay/',
        method = 'GET'
      },
      {
        path = '/bin/',
        method = 'GET'
      },
      {
        path = '/binaries/',
        method = 'GET'
      },
      {
        path = '/binary/',
        method = 'GET'
      },
      {
        path = '/boadmin/',
        method = 'GET'
      },
      {
        path = '/boot/',
        method = 'GET'
      },
      {
        path = '/bottom/',
        method = 'GET'
      },
      {
        path = '/browse/',
        method = 'GET'
      },
      {
        path = '/browser/',
        method = 'GET'
      },
      {
        path = '/bsd/',
        method = 'GET'
      },
      {
        path = '/btauxdir/',
        method = 'GET'
      },
      {
        path = '/bug/',
        method = 'GET'
      },
      {
        path = '/bugs/',
        method = 'GET'
      },
      {
        path = '/bugzilla/',
        method = 'GET'
      },
      {
        path = '/buy/',
        method = 'GET'
      },
      {
        path = '/buynow/',
        method = 'GET'
      },
      {
        path = '/cached/',
        method = 'GET'
      },
      {
        path = '/cache/',
        method = 'GET'
      },
      {
        path = '/cache-stats/',
        method = 'GET'
      },
      {
        path = '/caja/',
        method = 'GET'
      },
      {
        path = '/card/',
        method = 'GET'
      },
      {
        path = '/cards/',
        method = 'GET'
      },
      {
        path = '/cart/',
        method = 'GET'
      },
      {
        path = '/cash/',
        method = 'GET'
      },
      {
        path = '/caspsamp/',
        method = 'GET'
      },
      {
        path = '/catalog/',
        method = 'GET'
      },
      {
        path = '/cbi-bin/',
        method = 'GET'
      },
      {
        path = '/ccard/',
        method = 'GET'
      },
      {
        path = '/ccards/',
        method = 'GET'
      },
      {
        path = '/cd-cgi/',
        method = 'GET'
      },
      {
        path = '/cd/',
        method = 'GET'
      },
      {
        path = '/cdrom/',
        method = 'GET'
      },
      {
        path = '/ce_html/',
        method = 'GET'
      },
      {
        path = '/cert/',
        method = 'GET'
      },
      {
        path = '/certificado/',
        method = 'GET'
      },
      {
        path = '/certificate/',
        method = 'GET'
      },
      {
        path = '/cfappman/',
        method = 'GET'
      },
      {
        path = '/cfdocs/',
        method = 'GET'
      },
      {
        path = '/cfide/',
        method = 'GET'
      },
      {
        path = '/cgi-914/',
        method = 'GET'
      },
      {
        path = '/cgi-915/',
        method = 'GET'
      },
      {
        path = '/cgi-auth/',
        method = 'GET'
      },
      {
        path = '/cgi-bin2/',
        method = 'GET'
      },
      {
        path = '/cgi-bin/',
        method = 'GET'
      },
      {
        path = '/cgibin/',
        method = 'GET'
      },
      {
        path = '/cgi.cgi/',
        method = 'GET'
      },
      {
        path = '/cgi-csc/',
        method = 'GET'
      },
      {
        path = '/cgi-exe/',
        method = 'GET'
      },
      {
        path = '/cgi/',
        method = 'GET'
      },
      {
        path = '/cgi-home/',
        method = 'GET'
      },
      {
        path = '/cgi-lib/',
        method = 'GET'
      },
      {
        path = '/cgilib/',
        method = 'GET'
      },
      {
        path = '/cgi-local/',
        method = 'GET'
      },
      {
        path = '/cgi-perl/',
        method = 'GET'
      },
      {
        path = '/cgi-scripts/',
        method = 'GET'
      },
      {
        path = '/cgiscripts/',
        method = 'GET'
      },
      {
        path = '/cgis/',
        method = 'GET'
      },
      {
        path = '/cgi-shl/',
        method = 'GET'
      },
      {
        path = '/cgi-shop/',
        method = 'GET'
      },
      {
        path = '/cgi-sys/',
        method = 'GET'
      },
      {
        path = '/cgi-weddico/',
        method = 'GET'
      },
      {
        path = '/cgi-win/',
        method = 'GET'
      },
      {
        path = '/cgiwin/',
        method = 'GET'
      },
      {
        path = '/class/',
        method = 'GET'
      },
      {
        path = '/classes/',
        method = 'GET'
      },
      {
        path = '/cliente/',
        method = 'GET'
      },
      {
        path = '/clientes/',
        method = 'GET'
      },
      {
        path = '/client/',
        method = 'GET'
      },
      {
        path = '/clients/',
        method = 'GET'
      },
      {
        path = '/cm/',
        method = 'GET'
      },
      {
        path = '/cobalt-images/',
        method = 'GET'
      },
      {
        path = '/code/',
        method = 'GET'
      },
      {
        path = '/com/',
        method = 'GET'
      },
      {
        path = '/comments/',
        method = 'GET'
      },
      {
        path = '/common/',
        method = 'GET'
      },
      {
        path = '/communicator/',
        method = 'GET'
      },
      {
        path = '/company/',
        method = 'GET'
      },
      {
        path = '/comp/',
        method = 'GET'
      },
      {
        path = '/compra/',
        method = 'GET'
      },
      {
        path = '/compras/',
        method = 'GET'
      },
      {
        path = '/compressed/',
        method = 'GET'
      },
      {
        path = '/conecta/',
        method = 'GET'
      },
      {
        path = '/conf/',
        method = 'GET'
      },
      {
        path = '/config/',
        method = 'GET'
      },
      {
        path = '/configs/',
        method = 'GET'
      },
      {
        path = '/configure/',
        method = 'GET'
      },
      {
        path = '/connect/',
        method = 'GET'
      },
      {
        path = '/console/',
        method = 'GET'
      },
      {
        path = '/contact/',
        method = 'GET'
      },
      {
        path = '/contacts/',
        method = 'GET'
      },
      {
        path = '/content/',
        method = 'GET'
      },
      {
        path = '/content.ie5/',
        method = 'GET'
      },
      {
        path = '/controlpanel/',
        method = 'GET'
      },
      {
        path = '/core/',
        method = 'GET'
      },
      {
        path = '/corp/',
        method = 'GET'
      },
      {
        path = '/correo/',
        method = 'GET'
      },
      {
        path = '/counter/',
        method = 'GET'
      },
      {
        path = '/credit/',
        method = 'GET'
      },
      {
        path = '/cron/',
        method = 'GET'
      },
      {
        path = '/crons/',
        method = 'GET'
      },
      {
        path = '/crypto/',
        method = 'GET'
      },
      {
        path = '/CS/',
        method = 'GET'
      },
      {
        path = '/csr/',
        method = 'GET'
      },
      {
        path = '/css/',
        method = 'GET'
      },
      {
        path = '/cuenta/',
        method = 'GET'
      },
      {
        path = '/cuentas/',
        method = 'GET'
      },
      {
        path = '/currency/',
        method = 'GET'
      },
      {
        path = '/cust/',
        method = 'GET'
      },
      {
        path = '/customer/',
        method = 'GET'
      },
      {
        path = '/customers/',
        method = 'GET'
      },
      {
        path = '/custom/',
        method = 'GET'
      },
      {
        path = '/CVS/',
        method = 'GET'
      },
      {
        path = '/cvsweb/',
        method = 'GET'
      },
      {
        path = '/cybercash/',
        method = 'GET'
      },
      {
        path = '/darkportal/',
        method = 'GET'
      },
      {
        path = '/database/',
        method = 'GET'
      },
      {
        path = '/databases/',
        method = 'GET'
      },
      {
        path = '/datafiles/',
        method = 'GET'
      },
      {
        path = '/dat/',
        method = 'GET'
      },
      {
        path = '/data/',
        method = 'GET'
      },
      {
        path = '/dato/',
        method = 'GET'
      },
      {
        path = '/datos/',
        method = 'GET'
      },
      {
        path = '/db/',
        method = 'GET'
      },
      {
        path = '/dbase/',
        method = 'GET'
      },
      {
        path = '/dcforum/',
        method = 'GET'
      },
      {
        path = '/ddreport/',
        method = 'GET'
      },
      {
        path = '/ddrint/',
        method = 'GET'
      },
      {
        path = '/debug/',
        method = 'GET'
      },
      {
        path = '/debugs/',
        method = 'GET'
      },
      {
        path = '/default/',
        method = 'GET'
      },
      {
        path = '/deleted/',
        method = 'GET'
      },
      {
        path = '/delete/',
        method = 'GET'
      },
      {
        path = '/demoauct/',
        method = 'GET'
      },
      {
        path = '/demomall/',
        method = 'GET'
      },
      {
        path = '/demo/',
        method = 'GET'
      },
      {
        path = '/demos/',
        method = 'GET'
      },
      {
        path = '/demouser/',
        method = 'GET'
      },
      {
        path = '/deny/',
        method = 'GET'
      },
      {
        path = '/derived/',
        method = 'GET'
      },
      {
        path = '/design/',
        method = 'GET'
      },
      {
        path = '/dev/',
        method = 'GET'
      },
      {
        path = '/devel/',
        method = 'GET'
      },
      {
        path = '/development/',
        method = 'GET'
      },
      {
        path = '/directories/',
        method = 'GET'
      },
      {
        path = '/directory/',
        method = 'GET'
      },
      {
        path = '/directorymanager/',
        method = 'GET'
      },
      {
        path = '/dir/',
        method = 'GET'
      },
      {
        path = '/dl/',
        method = 'GET'
      },
      {
        path = '/dm/',
        method = 'GET'
      },
      {
        path = '/DMR/',
        method = 'GET'
      },
      {
        path = '/dms0/',
        method = 'GET'
      },
      {
        path = '/dmsdump/',
        method = 'GET'
      },
      {
        path = '/dms/',
        method = 'GET'
      },
      {
        path = '/dnn/',
        method = 'GET'
      },
      {
        path = '/doc1/',
        method = 'GET'
      },
      {
        path = '/doc/',
        method = 'GET'
      },
      {
        path = '/doc-html/',
        method = 'GET'
      },
      {
        path = '/docs1/',
        method = 'GET'
      },
      {
        path = '/docs/',
        method = 'GET'
      },
      {
        path = '/DocuColor/',
        method = 'GET'
      },
      {
        path = '/documentation/',
        method = 'GET'
      },
      {
        path = '/document/',
        method = 'GET'
      },
      {
        path = '/documents/',
        method = 'GET'
      },
      {
        path = '/dotnetnuke/',
        method = 'GET'
      },
      {
        path = '/down/',
        method = 'GET'
      },
      {
        path = '/download/',
        method = 'GET'
      },
      {
        path = '/downloads/',
        method = 'GET'
      },
      {
        path = '/dump/',
        method = 'GET'
      },
      {
        path = '/durep/',
        method = 'GET'
      },
      {
        path = '/easylog/',
        method = 'GET'
      },
      {
        path = '/eforum/',
        method = 'GET'
      },
      {
        path = '/ejemplo/',
        method = 'GET'
      },
      {
        path = '/ejemplos/',
        method = 'GET'
      },
      {
        path = '/emailclass/',
        method = 'GET'
      },
      {
        path = '/email/',
        method = 'GET'
      },
      {
        path = '/employees/',
        method = 'GET'
      },
      {
        path = '/empoyees/',
        method = 'GET'
      },
      {
        path = '/empris/',
        method = 'GET'
      },
      {
        path = '/enter/',
        method = 'GET'
      },
      {
        path = '/envia/',
        method = 'GET'
      },
      {
        path = '/enviamail/',
        method = 'GET'
      },
      {
        path = '/error.html',
        method = 'GET'
      },
      {
        path = '/error/',
        method = 'GET'
      },
      {
        path = '/errors/',
        method = 'GET'
      },
      {
        path = '/es/',
        method = 'GET'
      },
      {
        path = '/estmt/',
        method = 'GET'
      },
      {
        path = '/etc/',
        method = 'GET'
      },
      {
        path = '/etcpasswd/',
        method = 'GET'
      },
      {
        path = '/excel/',
        method = 'GET'
      },
      {
        path = '/exc/',
        method = 'GET'
      },
      {
        path = '/exchange/',
        method = 'GET'
      },
      {
        path = '/exchweb/',
        method = 'GET'
      },
      {
        path = '/exec/',
        method = 'GET'
      },
      {
        path = '/exe/',
        method = 'GET'
      },
      {
        path = '/exit/',
        method = 'GET'
      },
      {
        path = '/export/',
        method = 'GET'
      },
      {
        path = '/external/',
        method = 'GET'
      },
      {
        path = '/extranet/',
        method = 'GET'
      },
      {
        path = '/failure/',
        method = 'GET'
      },
      {
        path = '/fbsd/',
        method = 'GET'
      },
      {
        path = '/fcgi-bin/',
        method = 'GET'
      },
      {
        path = '/fcgi/',
        method = 'GET'
      },
      {
        path = '/features/',
        method = 'GET'
      },
      {
        path = '/fileadmin/',
        method = 'GET'
      },
      {
        path = '/file/',
        method = 'GET'
      },
      {
        path = '/filemanager/',
        method = 'GET'
      },
      {
        path = '/files/',
        method = 'GET'
      },
      {
        path = '/find/',
        method = 'GET'
      },
      {
        path = '/flash/',
        method = 'GET'
      },
      {
        path = '/foldoc/',
        method = 'GET'
      },
      {
        path = '/foobar/',
        method = 'GET'
      },
      {
        path = '/foo/',
        method = 'GET'
      },
      {
        path = '/form/',
        method = 'GET'
      },
      {
        path = '/forms/',
        method = 'GET'
      },
      {
        path = '/formsmgr/',
        method = 'GET'
      },
      {
        path = '/form-totaller/',
        method = 'GET'
      },
      {
        path = '/foto/',
        method = 'GET'
      },
      {
        path = '/fotos/',
        method = 'GET'
      },
      {
        path = '/fpadmin/',
        method = 'GET'
      },
      {
        path = '/fpclass/',
        method = 'GET'
      },
      {
        path = '/fpdb/',
        method = 'GET'
      },
      {
        path = '/fpe/',
        method = 'GET'
      },
      {
        path = '/framesets/',
        method = 'GET'
      },
      {
        path = '/frames/',
        method = 'GET'
      },
      {
        path = '/frontpage/',
        method = 'GET'
      },
      {
        path = '/ftp/',
        method = 'GET'
      },
      {
        path = '/ftproot/',
        method = 'GET'
      },
      {
        path = '/func/',
        method = 'GET'
      },
      {
        path = '/function/',
        method = 'GET'
      },
      {
        path = '/functions/',
        method = 'GET'
      },
      {
        path = '/fun/',
        method = 'GET'
      },
      {
        path = '/general/',
        method = 'GET'
      },
      {
        path = '/gfx/',
        method = 'GET'
      },
      {
        path = '/gif/',
        method = 'GET'
      },
      {
        path = '/gifs/',
        method = 'GET'
      },
      {
        path = '/global/',
        method = 'GET'
      },
      {
        path = '/globals/',
        method = 'GET'
      },
      {
        path = '/good/',
        method = 'GET'
      },
      {
        path = '/graphics/',
        method = 'GET'
      },
      {
        path = '/grocery/',
        method = 'GET'
      },
      {
        path = '/guestbook/',
        method = 'GET'
      },
      {
        path = '/guest/',
        method = 'GET'
      },
      {
        path = '/guests/',
        method = 'GET'
      },
      {
        path = '/GXApp/',
        method = 'GET'
      },
      {
        path = '/HB/',
        method = 'GET'
      },
      {
        path = '/HBTemplates/',
        method = 'GET'
      },
      {
        path = '/helpdesk/',
        method = 'GET'
      },
      {
        path = '/help/',
        method = 'GET'
      },
      {
        path = '/hidden/',
        method = 'GET'
      },
      {
        path = '/hide/',
        method = 'GET'
      },
      {
        path = '/hitmatic/',
        method = 'GET'
      },
      {
        path = '/hit_tracker/',
        method = 'GET'
      },
      {
        path = '/hlstats/',
        method = 'GET'
      },
      {
        path = '/home/',
        method = 'GET'
      },
      {
        path = '/hosted/',
        method = 'GET'
      },
      {
        path = '/host/',
        method = 'GET'
      },
      {
        path = '/hostingcontroller/',
        method = 'GET'
      },
      {
        path = '/hosting/',
        method = 'GET'
      },
      {
        path = '/hp/',
        method = 'GET'
      },
      {
        path = '/htbin/',
        method = 'GET'
      },
      {
        path = '/htdocs/',
        method = 'GET'
      },
      {
        path = '/ht/',
        method = 'GET'
      },
      {
        path = '/htm/',
        method = 'GET'
      },
      {
        path = '/html/',
        method = 'GET'
      },
      {
        path = '/http/',
        method = 'GET'
      },
      {
        path = '/https/',
        method = 'GET'
      },
      {
        path = '/hyperstat/',
        method = 'GET'
      },
      {
        path = '/i18n/',
        method = 'GET'
      },
      {
        path = '/ibank/',
        method = 'GET'
      },
      {
        path = '/ibill/',
        method = 'GET'
      },
      {
        path = '/IBMWebAS/',
        method = 'GET'
      },
      {
        path = '/icons/',
        method = 'GET'
      },
      {
        path = '/idea/',
        method = 'GET'
      },
      {
        path = '/ideas/',
        method = 'GET'
      },
      {
        path = '/I/',
        method = 'GET'
      },
      {
        path = '/iisadmin/',
        method = 'GET'
      },
      {
        path = '/image/',
        method = 'GET'
      },
      {
        path = '/images/',
        method = 'GET'
      },
      {
        path = '/imagenes/',
        method = 'GET'
      },
      {
        path = '/imagery/',
        method = 'GET'
      },
      {
        path = '/img/',
        method = 'GET'
      },
      {
        path = '/imp/',
        method = 'GET'
      },
      {
        path = '/import/',
        method = 'GET'
      },
      {
        path = '/impreso/',
        method = 'GET'
      },
      {
        path = '/inc/',
        method = 'GET'
      },
      {
        path = '/include/',
        method = 'GET'
      },
      {
        path = '/includes/',
        method = 'GET'
      },
      {
        path = '/incoming/',
        method = 'GET'
      },
      {
        path = '/index/',
        method = 'GET'
      },
      {
        path = '/inet/',
        method = 'GET'
      },
      {
        path = '/inf/',
        method = 'GET'
      },
      {
        path = '/info/',
        method = 'GET'
      },
      {
        path = '/information/',
        method = 'GET'
      },
      {
        path = '/in/',
        method = 'GET'
      },
      {
        path = '/ingresa/',
        method = 'GET'
      },
      {
        path = '/ingreso/',
        method = 'GET'
      },
      {
        path = '/install/',
        method = 'GET'
      },
      {
        path = '/internal/',
        method = 'GET'
      },
      {
        path = '/internet/',
        method = 'GET'
      },
      {
        path = '/intranet/',
        method = 'GET'
      },
      {
        path = '/inventory/',
        method = 'GET'
      },
      {
        path = '/invitado/',
        method = 'GET'
      },
      {
        path = '/isapi/',
        method = 'GET'
      },
      {
        path = '/j2ee/',
        method = 'GET'
      },
      {
        path = '/japidoc/',
        method = 'GET'
      },
      {
        path = '/java/',
        method = 'GET'
      },
      {
        path = '/javascript/',
        method = 'GET'
      },
      {
        path = '/javasdk/',
        method = 'GET'
      },
      {
        path = '/javatest/',
        method = 'GET'
      },
      {
        path = '/jave/',
        method = 'GET'
      },
      {
        path = '/JBookIt/',
        method = 'GET'
      },
      {
        path = '/jdbc/',
        method = 'GET'
      },
      {
        path = '/job/',
        method = 'GET'
      },
      {
        path = '/jrun/',
        method = 'GET'
      },
      {
        path = '/jsa/',
        method = 'GET'
      },
      {
        path = '/jscript/',
        method = 'GET'
      },
      {
        path = '/jserv/',
        method = 'GET'
      },
      {
        path = '/js/',
        method = 'GET'
      },
      {
        path = '/jslib/',
        method = 'GET'
      },
      {
        path = '/jsp/',
        method = 'GET'
      },
      {
        path = '/junk/',
        method = 'GET'
      },
      {
        path = '/kiva/',
        method = 'GET'
      },
      {
        path = '/known/',
        method = 'GET'
      },
      {
        path = '/labs/',
        method = 'GET'
      },
      {
        path = '/lcgi/',
        method = 'GET'
      },
      {
        path = '/lib/',
        method = 'GET'
      },
      {
        path = '/libraries/',
        method = 'GET'
      },
      {
        path = '/library/',
        method = 'GET'
      },
      {
        path = '/libro/',
        method = 'GET'
      },
      {
        path = '/license/',
        method = 'GET'
      },
      {
        path = '/licenses/',
        method = 'GET'
      },
      {
        path = '/links/',
        method = 'GET'
      },
      {
        path = '/linux/',
        method = 'GET'
      },
      {
        path = '/loader/',
        method = 'GET'
      },
      {
        path = '/local/',
        method = 'GET'
      },
      {
        path = '/location/',
        method = 'GET'
      },
      {
        path = '/locations/',
        method = 'GET'
      },
      {
        path = '/logfile/',
        method = 'GET'
      },
      {
        path = '/logfiles/',
        method = 'GET'
      },
      {
        path = '/logger/',
        method = 'GET'
      },
      {
        path = '/logg/',
        method = 'GET'
      },
      {
        path = '/logging/',
        method = 'GET'
      },
      {
        path = '/logon/',
        method = 'GET'
      },
      {
        path = '/logout/',
        method = 'GET'
      },
      {
        path = '/lost+found/',
        method = 'GET'
      },
      {
        path = '/mailman/',
        method = 'GET'
      },
      {
        path = '/mailroot/',
        method = 'GET'
      },
      {
        path = '/makefile/',
        method = 'GET'
      },
      {
        path = '/manage/',
        method = 'GET'
      },
      {
        path = '/management/',
        method = 'GET'
      },
      {
        path = '/man/',
        method = 'GET'
      },
      {
        path = '/manual/',
        method = 'GET'
      },
      {
        path = '/map/',
        method = 'GET'
      },
      {
        path = '/maps/',
        method = 'GET'
      },
      {
        path = '/marketing/',
        method = 'GET'
      },
      {
        path = '/member/',
        method = 'GET'
      },
      {
        path = '/members/',
        method = 'GET'
      },
      {
        path = '/mem_bin/',
        method = 'GET'
      },
      {
        path = '/mem/',
        method = 'GET'
      },
      {
        path = '/message/',
        method = 'GET'
      },
      {
        path = '/messaging/',
        method = 'GET'
      },
      {
        path = '/metacart/',
        method = 'GET'
      },
      {
        path = '/microsoft/',
        method = 'GET'
      },
      {
        path = '/misc/',
        method = 'GET'
      },
      {
        path = '/mkstats/',
        method = 'GET'
      },
      {
        path = '/mod/',
        method = 'GET'
      },
      {
        path = '/module/',
        method = 'GET'
      },
      {
        path = '/modules/',
        method = 'GET'
      },
      {
        path = '/movimientos/',
        method = 'GET'
      },
      {
        path = '/mpcgi/',
        method = 'GET'
      },
      {
        path = '/mqseries/',
        method = 'GET'
      },
      {
        path = '/msfpe/',
        method = 'GET'
      },
      {
        path = '/ms/',
        method = 'GET'
      },
      {
        path = '/msql/',
        method = 'GET'
      },
      {
        path = '/Msword/',
        method = 'GET'
      },
      {
        path = '/mxhtml/',
        method = 'GET'
      },
      {
        path = '/mxportal/',
        method = 'GET'
      },
      {
        path = '/my/',
        method = 'GET'
      },
      {
        path = '/My%20Shared%20Folder/',
        method = 'GET'
      },
      {
        path = '/mysql_admin/',
        method = 'GET'
      },
      {
        path = '/mysql/',
        method = 'GET'
      },
      {
        path = '/name/',
        method = 'GET'
      },
      {
        path = '/names/',
        method = 'GET'
      },
      {
        path = '/ncadmin/',
        method = 'GET'
      },
      {
        path = '/nchelp/',
        method = 'GET'
      },
      {
        path = '/netbasic/',
        method = 'GET'
      },
      {
        path = '/netcat/',
        method = 'GET'
      },
      {
        path = '/NetDynamic/',
        method = 'GET'
      },
      {
        path = '/NetDynamics/',
        method = 'GET'
      },
      {
        path = '/net/',
        method = 'GET'
      },
      {
        path = '/netmagstats/',
        method = 'GET'
      },
      {
        path = '/netscape/',
        method = 'GET'
      },
      {
        path = '/netshare/',
        method = 'GET'
      },
      {
        path = '/nettracker/',
        method = 'GET'
      },
      {
        path = '/network/',
        method = 'GET'
      },
      {
        path = '/new/',
        method = 'GET'
      },
      {
        path = '/news/',
        method = 'GET'
      },
      {
        path = '/News/',
        method = 'GET'
      },
      {
        path = '/nextgeneration/',
        method = 'GET'
      },
      {
        path = '/nl/',
        method = 'GET'
      },
      {
        path = '/notes/',
        method = 'GET'
      },
      {
        path = '/noticias/',
        method = 'GET'
      },
      {
        path = '/NSearch/',
        method = 'GET'
      },
      {
        path = '/objects/',
        method = 'GET'
      },
      {
        path = '/odbc/',
        method = 'GET'
      },
      {
        path = '/officescan/',
        method = 'GET'
      },
      {
        path = '/ojspdemos/',
        method = 'GET'
      },
      {
        path = '/old_files/',
        method = 'GET'
      },
      {
        path = '/oldfiles/',
        method = 'GET'
      },
      {
        path = '/old/',
        method = 'GET'
      },
      {
        path = '/oprocmgr-service/',
        method = 'GET'
      },
      {
        path = '/oprocmgr-status/',
        method = 'GET'
      },
      {
        path = '/oracle/',
        method = 'GET'
      },
      {
        path = '/oradata/',
        method = 'GET'
      },
      {
        path = '/order/',
        method = 'GET'
      },
      {
        path = '/orders/',
        method = 'GET'
      },
      {
        path = '/os/',
        method = 'GET'
      },
      {
        path = '/out/',
        method = 'GET'
      },
      {
        path = '/outgoing/',
        method = 'GET'
      },
      {
        path = '/owners/',
        method = 'GET'
      },
      {
        path = '/ows-bin/',
        method = 'GET'
      },
      {
        path = '/page/',
        method = 'GET'
      },
      {
        path = '/_pages/',
        method = 'GET'
      },
      {
        path = '/pages/',
        method = 'GET'
      },
      {
        path = '/partner/',
        method = 'GET'
      },
      {
        path = '/partners/',
        method = 'GET'
      },
      {
        path = '/passport/',
        method = 'GET'
      },
      {
        path = '/password/',
        method = 'GET'
      },
      {
        path = '/passwords/',
        method = 'GET'
      },
      {
        path = '/path/',
        method = 'GET'
      },
      {
        path = '/payment/',
        method = 'GET'
      },
      {
        path = '/payments/',
        method = 'GET'
      },
      {
        path = '/pccsmysqladm/',
        method = 'GET'
      },
      {
        path = '/PDG_Cart/',
        method = 'GET'
      },
      {
        path = '/perl5/',
        method = 'GET'
      },
      {
        path = '/perl/',
        method = 'GET'
      },
      {
        path = '/personal/',
        method = 'GET'
      },
      {
        path = '/pforum/',
        method = 'GET'
      },
      {
        path = '/phorum/',
        method = 'GET'
      },
      {
        path = '/phpBB/',
        method = 'GET'
      },
      {
        path = '/php_classes/',
        method = 'GET'
      },
      {
        path = '/phpclassifieds/',
        method = 'GET'
      },
      {
        path = '/php/',
        method = 'GET'
      },
      {
        path = '/phpimageview/',
        method = 'GET'
      },
      {
        path = '/phpnuke/',
        method = 'GET'
      },
      {
        path = '/phpPhotoAlbum/',
        method = 'GET'
      },
      {
        path = '/phpprojekt/',
        method = 'GET'
      },
      {
        path = '/phpSecurePages/',
        method = 'GET'
      },
      {
        path = '/pics/',
        method = 'GET'
      },
      {
        path = '/pictures/',
        method = 'GET'
      },
      {
        path = '/pike/',
        method = 'GET'
      },
      {
        path = '/piranha/',
        method = 'GET'
      },
      {
        path = '/pls/',
        method = 'GET'
      },
      {
        path = '/plsql/',
        method = 'GET'
      },
      {
        path = '/plssampleadmin_/',
        method = 'GET'
      },
      {
        path = '/plssampleadmin/',
        method = 'GET'
      },
      {
        path = '/plssampleadmin_help/',
        method = 'GET'
      },
      {
        path = '/plssample/',
        method = 'GET'
      },
      {
        path = '/poll/',
        method = 'GET'
      },
      {
        path = '/polls/',
        method = 'GET'
      },
      {
        path = '/porn/',
        method = 'GET'
      },
      {
        path = '/portal/',
        method = 'GET'
      },
      {
        path = '/portals/',
        method = 'GET'
      },
      {
        path = '/postgres/',
        method = 'GET'
      },
      {
        path = '/postnuke/',
        method = 'GET'
      },
      {
        path = '/ppwb/',
        method = 'GET'
      },
      {
        path = '/printer/',
        method = 'GET'
      },
      {
        path = '/printers/',
        method = 'GET'
      },
      {
        path = '/privacy/',
        method = 'GET'
      },
      {
        path = '/privado/',
        method = 'GET'
      },
      {
        path = '/_private/',
        method = 'GET'
      },
      {
        path = '/private/',
        method = 'GET'
      },
      {
        path = '/priv/',
        method = 'GET'
      },
      {
        path = '/prod/',
        method = 'GET'
      },
      {
        path = '/projectserver/',
        method = 'GET'
      },
      {
        path = '/protected/',
        method = 'GET'
      },
      {
        path = '/proxy/',
        method = 'GET'
      },
      {
        path = '/prueba/',
        method = 'GET'
      },
      {
        path = '/pruebas/',
        method = 'GET'
      },
      {
        path = '/prv/',
        method = 'GET'
      },
      {
        path = '/pub/',
        method = 'GET'
      },
      {
        path = '/_public/',
        method = 'GET'
      },
      {
        path = '/public/',
        method = 'GET'
      },
      {
        path = '/publica/',
        method = 'GET'
      },
      {
        path = '/publicar/',
        method = 'GET'
      },
      {
        path = '/publico/',
        method = 'GET'
      },
      {
        path = '/publish/',
        method = 'GET'
      },
      {
        path = '/purchase/',
        method = 'GET'
      },
      {
        path = '/purchases/',
        method = 'GET'
      },
      {
        path = '/pw/',
        method = 'GET'
      },
      {
        path = '/python/',
        method = 'GET'
      },
      {
        path = '/random_banner/',
        method = 'GET'
      },
      {
        path = '/rdp/',
        method = 'GET'
      },
      {
        path = '/Readme/',
        method = 'GET'
      },
      {
        path = '/recycler/',
        method = 'GET'
      },
      {
        path = '/registered/',
        method = 'GET'
      },
      {
        path = '/register/',
        method = 'GET'
      },
      {
        path = '/registry/',
        method = 'GET'
      },
      {
        path = '/remote/',
        method = 'GET'
      },
      {
        path = '/remove/',
        method = 'GET'
      },
      {
        path = '/report/',
        method = 'GET'
      },
      {
        path = '/reports/',
        method = 'GET'
      },
      {
        path = '/reseller/',
        method = 'GET'
      },
      {
        path = '/restricted/',
        method = 'GET'
      },
      {
        path = '/retail/',
        method = 'GET'
      },
      {
        path = '/reveal/',
        method = 'GET'
      },
      {
        path = '/reviews/',
        method = 'GET'
      },
      {
        path = '/ROADS/',
        method = 'GET'
      },
      {
        path = '/robot/',
        method = 'GET'
      },
      {
        path = '/robots/',
        method = 'GET'
      },
      {
        path = '/root/',
        method = 'GET'
      },
      {
        path = '/rsrc/',
        method = 'GET'
      },
      {
        path = '/ruby/',
        method = 'GET'
      },
      {
        path = '/sales/',
        method = 'GET'
      },
      {
        path = '/save/',
        method = 'GET'
      },
      {
        path = '/script/',
        method = 'GET'
      },
      {
        path = '/ScriptLibrary/',
        method = 'GET'
      },
      {
        path = '/scripts/',
        method = 'GET'
      },
      {
        path = '/search/',
        method = 'GET'
      },
      {
        path = '/search-ui/',
        method = 'GET'
      },
      {
        path = '/sec/',
        method = 'GET'
      },
      {
        path = '/secret/',
        method = 'GET'
      },
      {
        path = '/secured/',
        method = 'GET'
      },
      {
        path = '/secure/',
        method = 'GET'
      },
      {
        path = '/security/',
        method = 'GET'
      },
      {
        path = '/sell/',
        method = 'GET'
      },
      {
        path = '/server/',
        method = 'GET'
      },
      {
        path = '/server-info/',
        method = 'GET'
      },
      {
        path = '/servers/',
        method = 'GET'
      },
      {
        path = '/server_stats/',
        method = 'GET'
      },
      {
        path = '/serverstats/',
        method = 'GET'
      },
      {
        path = '/server-status/',
        method = 'GET'
      },
      {
        path = '/service/',
        method = 'GET'
      },
      {
        path = '/services/',
        method = 'GET'
      },
      {
        path = '/servicio/',
        method = 'GET'
      },
      {
        path = '/servicios/',
        method = 'GET'
      },
      {
        path = '/servlet/',
        method = 'GET'
      },
      {
        path = '/servlets/',
        method = 'GET'
      },
      {
        path = '/session/',
        method = 'GET'
      },
      {
        path = '/setup/',
        method = 'GET'
      },
      {
        path = '/shared/',
        method = 'GET'
      },
      {
        path = '/sharedtemplates/',
        method = 'GET'
      },
      {
        path = '/share/',
        method = 'GET'
      },
      {
        path = '/shell-cgi/',
        method = 'GET'
      },
      {
        path = '/shipping/',
        method = 'GET'
      },
      {
        path = '/shop/',
        method = 'GET'
      },
      {
        path = '/shopper/',
        method = 'GET'
      },
      {
        path = '/show/',
        method = 'GET'
      },
      {
        path = '/SilverStream/',
        method = 'GET'
      },
      {
        path = '/siteadmin/',
        method = 'GET'
      },
      {
        path = '/site/',
        method = 'GET'
      },
      {
        path = '/sitemgr/',
        method = 'GET'
      },
      {
        path = '/siteminderagent/',
        method = 'GET'
      },
      {
        path = '/siteminder/',
        method = 'GET'
      },
      {
        path = '/siteserver/',
        method = 'GET'
      },
      {
        path = '/sites/',
        method = 'GET'
      },
      {
        path = '/sitestats/',
        method = 'GET'
      },
      {
        path = '/siteupdate/',
        method = 'GET'
      },
      {
        path = '/smreports/',
        method = 'GET'
      },
      {
        path = '/smreportsviewer/',
        method = 'GET'
      },
      {
        path = '/soapdocs/',
        method = 'GET'
      },
      {
        path = '/soap/',
        method = 'GET'
      },
      {
        path = '/software/',
        method = 'GET'
      },
      {
        path = '/solaris/',
        method = 'GET'
      },
      {
        path = '/source/',
        method = 'GET'
      },
      {
        path = '/sql/',
        method = 'GET'
      },
      {
        path = '/squid/',
        method = 'GET'
      },
      {
        path = '/src/',
        method = 'GET'
      },
      {
        path = '/srchadm/',
        method = 'GET'
      },
      {
        path = '/ssi/',
        method = 'GET'
      },
      {
        path = '/ssl/',
        method = 'GET'
      },
      {
        path = '/sslkeys/',
        method = 'GET'
      },
      {
        path = '/staff/',
        method = 'GET'
      },
      {
        path = '/state/',
        method = 'GET'
      },
      {
        path = '/stat/',
        method = 'GET'
      },
      {
        path = '/statistic/',
        method = 'GET'
      },
      {
        path = '/statistics/',
        method = 'GET'
      },
      {
        path = '/stats-bin-p/',
        method = 'GET'
      },
      {
        path = '/stats/',
        method = 'GET'
      },
      {
        path = '/stats_old/',
        method = 'GET'
      },
      {
        path = '/status/',
        method = 'GET'
      },
      {
        path = '/storage/',
        method = 'GET'
      },
      {
        path = '/StoreDB/',
        method = 'GET'
      },
      {
        path = '/store/',
        method = 'GET'
      },
      {
        path = '/storemgr/',
        method = 'GET'
      },
      {
        path = '/stronghold-info/',
        method = 'GET'
      },
      {
        path = '/stronghold-status/',
        method = 'GET'
      },
      {
        path = '/stuff/',
        method = 'GET'
      },
      {
        path = '/style/',
        method = 'GET'
      },
      {
        path = '/styles/',
        method = 'GET'
      },
      {
        path = '/stylesheet/',
        method = 'GET'
      },
      {
        path = '/stylesheets/',
        method = 'GET'
      },
      {
        path = '/subir/',
        method = 'GET'
      },
      {
        path = '/sun/',
        method = 'GET'
      },
      {
        path = '/super_stats/',
        method = 'GET'
      },
      {
        path = '/supplier/',
        method = 'GET'
      },
      {
        path = '/suppliers/',
        method = 'GET'
      },
      {
        path = '/supply/',
        method = 'GET'
      },
      {
        path = '/supporter/',
        method = 'GET'
      },
      {
        path = '/support/',
        method = 'GET'
      },
      {
        path = '/sysadmin/',
        method = 'GET'
      },
      {
        path = '/sysbackup/',
        method = 'GET'
      },
      {
        path = '/sys/',
        method = 'GET'
      },
      {
        path = '/system/',
        method = 'GET'
      },
      {
        path = '/systems/',
        method = 'GET'
      },
      {
        path = '/tar/',
        method = 'GET'
      },
      {
        path = '/target/',
        method = 'GET'
      },
      {
        path = '/tarjetas/',
        method = 'GET'
      },
      {
        path = '/tech/',
        method = 'GET'
      },
      {
        path = '/technote/',
        method = 'GET'
      },
      {
        path = '/te_html/',
        method = 'GET'
      },
      {
        path = '/temp/',
        method = 'GET'
      },
      {
        path = '/template/',
        method = 'GET'
      },
      {
        path = '/templates/',
        method = 'GET'
      },
      {
        path = '/temporal/',
        method = 'GET'
      },
      {
        path = '/test-cgi/',
        method = 'GET'
      },
      {
        path = '/testing/',
        method = 'GET'
      },
      {
        path = '/tests/',
        method = 'GET'
      },
      {
        path = '/testweb/',
        method = 'GET'
      },
      {
        path = '/themes/',
        method = 'GET'
      },
      {
        path = '/ticket/',
        method = 'GET'
      },
      {
        path = '/tickets/',
        method = 'GET'
      },
      {
        path = '/tip/',
        method = 'GET'
      },
      {
        path = '/tips/',
        method = 'GET'
      },
      {
        path = '/tmp/',
        method = 'GET'
      },
      {
        path = '/ToDo/',
        method = 'GET'
      },
      {
        path = '/tool/',
        method = 'GET'
      },
      {
        path = '/tools/',
        method = 'GET'
      },
      {
        path = '/TopAccess/',
        method = 'GET'
      },
      {
        path = '/top/',
        method = 'GET'
      },
      {
        path = '/tpv/',
        method = 'GET'
      },
      {
        path = '/trabajo/',
        method = 'GET'
      },
      {
        path = '/track/',
        method = 'GET'
      },
      {
        path = '/tracking/',
        method = 'GET'
      },
      {
        path = '/transfer/',
        method = 'GET'
      },
      {
        path = '/transito/',
        method = 'GET'
      },
      {
        path = '/transpolar/',
        method = 'GET'
      },
      {
        path = '/tree/',
        method = 'GET'
      },
      {
        path = '/trees/',
        method = 'GET'
      },
      {
        path = '/trick/',
        method = 'GET'
      },
      {
        path = '/tricks/',
        method = 'GET'
      },
      {
        path = '/u02/',
        method = 'GET'
      },
      {
        path = '/unix/',
        method = 'GET'
      },
      {
        path = '/unknown/',
        method = 'GET'
      },
      {
        path = '/updates/',
        method = 'GET'
      },
      {
        path = '/upload/',
        method = 'GET'
      },
      {
        path = '/uploads/',
        method = 'GET'
      },
      {
        path = '/usage/',
        method = 'GET'
      },
      {
        path = '/userdb/',
        method = 'GET'
      },
      {
        path = '/user/',
        method = 'GET'
      },
      {
        path = '/users/',
        method = 'GET'
      },
      {
        path = '/us/',
        method = 'GET'
      },
      {
        path = '/usr/',
        method = 'GET'
      },
      {
        path = '/ustats/',
        method = 'GET'
      },
      {
        path = '/usuario/',
        method = 'GET'
      },
      {
        path = '/usuarios/',
        method = 'GET'
      },
      {
        path = '/util/',
        method = 'GET'
      },
      {
        path = '/utils/',
        method = 'GET'
      },
      {
        path = '/vendor/',
        method = 'GET'
      },
      {
        path = '/vfs/',
        method = 'GET'
      },
      {
        path = '/view/',
        method = 'GET'
      },
      {
        path = '/vpn/',
        method = 'GET'
      },
      {
        path = '/vti_txt/',
        method = 'GET'
      },
      {
        path = '/w2000/',
        method = 'GET'
      },
      {
        path = '/w2k/',
        method = 'GET'
      },
      {
        path = '/w3perl/',
        method = 'GET'
      },
      {
        path = '/w-agora/',
        method = 'GET'
      },
      {
        path = '/way-board/',
        method = 'GET'
      },
      {
        path = '/web800fo/',
        method = 'GET'
      },
      {
        path = '/webaccess/',
        method = 'GET'
      },
      {
        path = '/webadmin/',
        method = 'GET'
      },
      {
        path = '/webAdmin/',
        method = 'GET'
      },
      {
        path = '/webalizer/',
        method = 'GET'
      },
      {
        path = '/webapps/',
        method = 'GET'
      },
      {
        path = '/WebBank/',
        method = 'GET'
      },
      {
        path = '/webboard/',
        method = 'GET'
      },
      {
        path = '/WebCalendar/',
        method = 'GET'
      },
      {
        path = '/webcart/',
        method = 'GET'
      },
      {
        path = '/webcart-lite/',
        method = 'GET'
      },
      {
        path = '/webcgi/',
        method = 'GET'
      },
      {
        path = '/webdata/',
        method = 'GET'
      },
      {
        path = '/webdav/',
        method = 'GET'
      },
      {
        path = '/webdb/',
        method = 'GET'
      },
      {
        path = '/webDB/',
        method = 'GET'
      },
      {
        path = '/web/',
        method = 'GET'
      },
      {
        path = '/webimages2/',
        method = 'GET'
      },
      {
        path = '/webimages/',
        method = 'GET'
      },
      {
        path = '/web-inf/',
        method = 'GET'
      },
      {
        path = '/webmaster/',
        method = 'GET'
      },
      {
        path = '/webmaster_logs/',
        method = 'GET'
      },
      {
        path = '/webMathematica/',
        method = 'GET'
      },
      {
        path = '/webpub/',
        method = 'GET'
      },
      {
        path = '/webpub-ui/',
        method = 'GET'
      },
      {
        path = '/webreports/',
        method = 'GET'
      },
      {
        path = '/webreps/',
        method = 'GET'
      },
      {
        path = '/webshare/',
        method = 'GET'
      },
      {
        path = '/WebShop/',
        method = 'GET'
      },
      {
        path = '/website/',
        method = 'GET'
      },
      {
        path = '/webstat/',
        method = 'GET'
      },
      {
        path = '/webstats/',
        method = 'GET'
      },
      {
        path = '/Web_store/',
        method = 'GET'
      },
      {
        path = '/webtrace/',
        method = 'GET'
      },
      {
        path = '/WebTrend/',
        method = 'GET'
      },
      {
        path = '/webtrends/',
        method = 'GET'
      },
      {
        path = '/web_usage/',
        method = 'GET'
      },
      {
        path = '/win2k/',
        method = 'GET'
      },
      {
        path = '/window/',
        method = 'GET'
      },
      {
        path = '/windows/',
        method = 'GET'
      },
      {
        path = '/win/',
        method = 'GET'
      },
      {
        path = '/winnt/',
        method = 'GET'
      },
      {
        path = '/word/',
        method = 'GET'
      },
      {
        path = '/work/',
        method = 'GET'
      },
      {
        path = '/world/',
        method = 'GET'
      },
      {
        path = '/wsdocs/',
        method = 'GET'
      },
      {
        path = '/WS_FTP/',
        method = 'GET'
      },
      {
        path = '/wstats/',
        method = 'GET'
      },
      {
        path = '/wusage/',
        method = 'GET'
      },
      {
        path = '/www0/',
        method = 'GET'
      },
      {
        path = '/www2/',
        method = 'GET'
      },
      {
        path = '/www3/',
        method = 'GET'
      },
      {
        path = '/www4/',
        method = 'GET'
      },
      {
        path = '/www/',
        method = 'GET'
      },
      {
        path = '/wwwjoin/',
        method = 'GET'
      },
      {
        path = '/wwwrooot/',
        method = 'GET'
      },
      {
        path = '/www-sql/',
        method = 'GET'
      },
      {
        path = '/wwwstat/',
        method = 'GET'
      },
      {
        path = '/wwwstats/',
        method = 'GET'
      },
      {
        path = '/xGB/',
        method = 'GET'
      },
      {
        path = '/xml/',
        method = 'GET'
      },
      {
        path = '/XSL/',
        method = 'GET'
      },
      {
        path = '/xtemp/',
        method = 'GET'
      },
      {
        path = '/xymon/',
        method = 'GET'
      },
      {
        path = '/zb41/',
        method = 'GET'
      },
      {
        path = '/zipfiles/',
        method = 'GET'
      },
      {
        path = '/zip/',
        method = 'GET'
      },
      {
        path = '/_docs/',
        method = 'GET'
      }
    },
    matches = {
      {
        match = '<title>Index of .*(Apache.*) Server at',
        output = 'Potentially interesting directory w/ listing on \'\\1\''
      },
      {
        match = '<title>Index of',
        output = 'Potentially interesting folder w/ directory listing'
      },
      {
        match = '',
        output = 'Potentially interesting folder'
      }
    }
  });

-- Sitecore Version
table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/sitecore/shell/sitecore.version.xml',
        method = 'GET'
      },
      {
        path = '/sitecore/login/default.aspx',
        method = 'GET'
      }
    },
    matches = {
      {
        match = '<major>([^<]*)</major>.*<minor>([^<]*)</minor>.*<build>([^<]*)</build>.*<revision>([^<]*)</revision>',
        output = '\\1.\\2.\\3 (rev. \\4)'
      },
      {
        match = 'class="SystemInformationDivider">.*Sitecore.NET ([^<]*)</div>',
        output = '\\1'
      },
      {
        match = '<hr/>.*Sitecore version ([^<]*)</div>',
        output = '\\1'
      },
      {
        match = '',
        output = 'Sitecore.NET login page'
      }
    }
  });

-- Sitecore
table.insert(fingerprints, {
    category = 'cms',
    probes = {
      {
        path = '/sitecore/admin/stats.aspx', -- Removed in version 6.3.1 (rev. 110112)
        method = 'HEAD'
      },
      {
        path = '/sitecore/admin/unlock_admin.aspx', -- disabled per default in 6.2.0 (rev.100507)
        method = 'HEAD'
      },
      {
        path = '/sitecore/shell/Applications/shell.xml',
        method = 'HEAD'
      },
      {
        path = '/sitecore/admin/ShowConfig.aspx',
        method = 'HEAD'
      },
      {
        path = '/App_Config/Security/Domains.config.xml',
        method = 'HEAD'
      },
      {
        path = '/App_Config/Security/GlobalRoles.config.xml',
        method = 'HEAD'
      },
      {
        path = '/sitecore%20modules/staging/service/api.asmx',
        method = 'HEAD'
      },
      {
        path = '/sitecore%20modules/staging/workdir',
        method = 'HEAD'
      },
      {
        path = '/sitecore/system/Settings/Security/Profiles',
        method = 'HEAD'
      },

    },
    matches = {
      {
        match = '',
        output = 'Sitecore.NET (CMS)'
      }
    },
  });

local stdnse = require "stdnse"
local nmap = require "nmap"

nikto_db_path = stdnse.get_script_args("http-fingerprints.nikto-db-path") or "db_tests"
nikto_db_path = nmap.fetchfile(nikto_db_path) or nikto_db_path
local f = io.open(nikto_db_path, "r")

if f then

  f:close()
  stdnse.debug1("Found nikto db.")

  local nikto_db = {}
  for l in io.lines(nikto_db_path) do

    -- Skip comments.
    if not string.match(l, "^#.*") then

      record = {}

      for field in string.gmatch(l, "\"(.-)\",") do

        -- Grab every attribute and create a record.
        if field then
          string.gsub(field, '%%', '%%%%')
          table.insert(record, field)
        end
      end

      -- Make sure this record doesn't exists already.
      local exists = false
      for _, f in pairs(fingerprints) do
        if f.probes then
          for __, p in pairs(f.probes) do
            if p.path then
              if p.path == record[4] then
                exists = true
                break
              end
            end
          end
        end
      end

      -- What we have right now, is the following record:
      -- record[1]: Nikto test ID
      -- record[2]: OSVDB-ID
      -- record[3]: Server Type
      -- record[4]: URI
      -- record[5]: HTTP Method
      -- record[6]: Match 1
      -- record[7]: Match 1 (Or)
      -- record[8]: Match1 (And)
      -- record[9]: Fail 1
      -- record[10]: Fail 2
      -- record[11]: Summary
      -- record[12]: HTTP Data
      -- record[13]: Headers

      -- Is this a valid record?  Atm, with our current format we need
      -- to skip some nikto records. See NSEDoc for more info.

      if not exists
        and record[4]
        and record[8] == "" and record[10] == "" and record[12] == ""
        and (tonumber(record[4]) == nil or (tonumber(record[4]) and record[4] == "200")) then

        -- Our current format does not support HTTP code matching.
        if record[6] == "200" then record[6] = "" end

        nikto_fingerprint = { category = "nikto",
        probes = {
          {
            path = record[4],
            method = record[5]
          }
        },
        matches = {
          {
            dontmatch = record[9],
            match = record[6],
            output = record[11]
          },
        },
      }

      -- If there is a second match, add it.
      if record[7] and record[7] ~= "" then
        table.insert(nikto_fingerprint.matches, { match = record[7], output = record[11] })
      end

      table.insert(fingerprints, nikto_fingerprint)

    end
  end
end
end
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           )                 )                 )                 )                 )                 )                 )                 )                 )                 )                 )            !     )            #     )            (     )                 )                 )                 )                 )                  *                 *                 *                 *                 *                 *                 *                 *                  *                 $*            	     (*            :
     ,*                 0*                 4*                  8*            "     <*            $     @*            &     D*            (     H*            -     L*            b     P*            p     T*            w     X*            y     \*            {     `*            }     d*            ~     h*                 l*                 p*                 t*                 x*                 |*                  *            "     *            $     *            &     *            +     *            0     *            }     *                 *                 *                 *                 *                 *                 *            #     *            0     *            7     *            9     *            ;     *            @     *            A     *            B     *            H     *            I     *            K     *            M     *            O     *            Q     *            V     *                 *                  *            @     *                 *                  +                 +                 +                 +                  +                 +                 +                 +                  +                 $+                 (+                  ,+                 0+                 4+                 8+                 <+                 @+                 D+                 H+                 L+                 P+                 T+                 X+                 \+                 `+                 d+                 h+            D     l+            E     p+            G     t+            I     x+            K     |+            P     +            Q     +            U     +            Z     +            \     +            ^     +            c     +            |     +            }     +                 +                 +                 +                 +                 +                 +                 +                 +                 +                 +                 +                 +                 +                 +                 +                 +                 +                 +                 +                 +                 +                 +                 +                  ,                 ,                 ,                 ,            S     ,            T     ,            U     ,            W     ,            Y      ,            [     $,            ]     (,            b     ,,            p     0,                 4,                 8,                 <,                 @,                 D,                 H,            A     L,            B     P,            D     T,            F     X,            H     \,            J     `,            O     d,                 h,                 l,                 p,                 t,                 x,                 |,                 ,                 ,                 ,                 ,                 ,                 ,                 ,                 ,                 ,            9     ,            :     ,            ;     ,            =     ,            ?     ,            A     ,            C     ,            H     ,                 ,                 ,                 ,                 ,            $     ,            :     ,            r     ,            z     ,                  ,            !     ,            !     ,            %!     ,            s!     ,            !     ,            !     ,            !      -            !     -            !     -            !     -            !     -            !     -            n$     -            p$     -            w$      -            $     $-            $     (-            $     ,-            $     0-            $     4-            $     8-            $     <-            $     @-            $     D-            $     H-            $     L-            $     P-            $     T-            $     X-            $     \-            %     `-            %     d-            %     h-            %     l-            %     p-             %     t-            8%     x-            @%     |-            G%     -            P%     -            Q%     -            X%     -            %     -            %     -            %     -            %     -            %     -            %     -            %     -            %     -            %     -            %     -            (&     -            0&     -            J&     -            K&     -            R&     -            &     -            &     -            &     -            &     -             '     -            '     -            	'     -            '     -            '     -            '     -            G'     -            H'     -            J'     -            L'      .            N'     .            S'     .            '     .            '     .            '     .            '     .            '     .            '      .            Q(     $.            `(     (.            g(     ,.            l(     0.            p(     4.            q(     8.            )     <.            )     @.            )     D.            )     H.            )     L.            )     P.            )     T.            )     X.            )     \.            ^)     `.            `)     d.            g)     h.            i)     l.            j)     p.            k)     t.            o)     x.            v*     |.            w*     .            x*     .            z*     .            |*     .            *     .            *     .            *     .            *     .            *     .            *     .            *     .            *     .            *     .            *     .            ,     .            ,     .            ,     .            ,     .            ,     .            ,     .            ,     .            ,     .            5     .            5     .            5     .             6     .            6     .            6     .            
6     .            m6     .            n6     .            p6     .            u6      /            6     /            6     /            6     /            6     /            6     /            6     /            6     /            6      /             7     $/            7     (/            	7     ,/            7     0/            7     4/            7     8/            7     </            7     @/            W9     D/            Z9     H/            [9     L/            ]9     P/            _9     T/            a9     X/            c9     \/            h9     `/            :     d/             :     h/            ':     l/            5:     p/            7:     t/            9:     x/            ::     |/            ;:     /            B:     /            V;     /            [;     /            ];     /            _;     /            {;     /             <     /            <     /            <     /            <     /            	<     /            <     /            
<     /            <     /             >     /            '>     /            )>     /            +>     /            ->     /            .>     /            />     /            6>     /            >     /            >     /            ?     /            ?     /            ?     /            ?     /            ?     /            <@     /            ?@     /            @@      0            B@     0            D@     0            F@     0            H@     0            M@     0            A     0            A     0            A      0            A     $0            A     (0            A     ,0            A     00            &A     40            0A     80            wA     <0            yA     @0            {A     D0            }A     H0            A     L0            A     P0            A     T0            A     X0            A     \0            A     `0            A     d0            A     h0            A     l0            A     p0            A     t0            A     x0            A     |0            LB     0            MB     0            OB     0            TB     0            XB     0            YB     0            [B     0            `B     0            aB     0            gB     0            iB     0            nB     0            pB     0            vB     0            B     0            B     0            B     0            B     0            B     0            B     0            B     0            B     0            B     0            B     0             C     0            GC     0            PC     0            WC     0            `C     0            bC     0            dC     0            eC     0            hC      1            oC     1            C     1            C     1            C     1            C     1            C     1            C     1            C      1            C     $1            D     (1            D     ,1            D     01            D     41            D     81            D     <1            D     @1            D     D1            E     H1            E     L1            E     P1            E     T1            E     X1            E     \1            E     `1            E     d1            E     h1            F     l1            F     p1            F     t1            F     x1            
F     |1            RF     1            VF     1            XF     1            ZF     1            \F     1            aF     1            bF     1            cF     1            eF     1            gF     1            iF     1            nF     1            F     1            F     1            F     1            F     1            F     1            F     1            F     1            F     1            F     1            F     1            F     1            F     1            F     1            F     1            pI     1            qI     1            rI     1            tI     1            vI     1            xI     1            zI      2            I     2            -J     2            0J     2            7J     2            9J     2            ?J     2            AJ     2            BJ      2            CJ     $2            MJ     (2            L     ,2            L     02            L     42            L     82            L     <2            L     @2            L     D2            L     H2            N     L2            N     P2            N     T2            N     X2            N     \2            O     `2            O     d2            $O     h2            ,O     l2            5O     p2            i     t2             i     x2            7i     |2            @i     2            Fi     2            i     2            i     2            j     2            j     2            j     2            j     2            j     2            j     2            j     2            j     2            )j     2            zj     2            {j     2            |j     2            ~j     2            j     2            j     2            j     2            j     2            t     2            t     2            t     2            t     2            t     2            t     2            t     2            t     2            t     2            u     2            u     2            u      3            u     3            u     3            u     3            u     3            u     3            "~     3            \      3            \       3            \      $3            \      (3            \      ,3            _      03            _      43            (`      83            0`      <3            9`      @3            ;`      D3            @`      H3            I`      L3            Q`      P3            S`      T3            U`      X3            Z`      \3            w`      `3            `      d3            `      h3            `      l3            "a      p3            a      t3            !b      x3            d      |3            d      3            d      3            d      3            d      3            d      3            d      3            e      3            e      3            e      3            e      3            [f      3            f      3            |g      3            g      3            g      3            g      3            g      3            g      3            g      3            g      3            g      3            g      3            g      3            g      3            g      3            Ki      3            Li      3            Ni      3            Pi      3            Ui      3            ^i      3            i      3            i       4            i      4            i      4            i      4            i      4            i      4            -j      4            .j      4            0j       4            2j      $4            4j      (4            9j      ,4            vj      04            zj      44            |j      84            ~j      <4            j      @4            j      D4            j      H4            j      L4            j      P4            j      T4            j      X4            j      \4            j      `4            j      d4            Xk      h4            ^k      l4            _k      p4            ak      t4            ck      x4            ek      |4            gk      4            lk      4            qk      4            k      4            k      4            k      4            k      4            k      4            k      4            k      4            *l      4            +l      4            ,l      4            .l      4            0l      4            5l      4            rl      4            zl      4            {l      4            }l      4            l      4            l      4            l      4            l      4            l      4            l      4            l      4            l      4            l      4            l      4            'm      4            .m      4            0m       5            <m      5            >m      5            Gm      5            Hm      5            Om      5            m      5            m      5            m       5            m      $5            m      (5            m      ,5            m      05            m      45            m      85            m      <5            m      @5            m      D5            m      H5            m      L5            m      P5            m      T5            n      X5            n      \5            n      `5            n      d5            n      h5            o      l5            ,o      p5            Zo      t5            o      x5            o      |5            o      5            o      5            o      5            o      5            o      5            p      5            p      5            p      5            p      5            cp      5            gp      5            ip      5            up      5            ~p      5            p      5            p      5            p      5            p      5            p      5            p      5            p      5            p      5            p      5            p      5            p      5            p      5            p      5            s      5            s      5            s      5            s      5            s      5            s       6            s      6            t      6            t      6            t      6            t      6            t      6            (u      6            Au       6            Bu      $6            Du      (6            Fu      ,6            Ku      06            u      46            u      86            u      <6            u      @6            u      D6            Gv      H6            v      L6            v      P6            v      T6            v      X6            v      \6            v      `6            v      d6            v      h6            v      l6            w      p6            w      t6            w      x6            w      |6            y      6            y      6            y      6            y      6            z      6            z      6            ){      6            4{      6            9{      6            B{      6            D{      6            F{      6            H{      6            M{      6            {      6            {      6            {      6            {      6            |      6            |      6            |      6            
|      6            S|      6            |      6            |      6            |      6            |      6            }      6            }      6            "~      6            &~      6            '~      6            )~       7            +~      7            -~      7            /~      7      ---
-- MSSQL Library supporting a very limited subset of operations.
--
-- The library was designed and tested against Microsoft SQL Server 2005.
-- However, it should work with versions 7.0, 2000, 2005, 2008 and 2012.
-- Only a minimal amount of parsers have been added for tokens, column types
-- and column data in order to support the first scripts.
--
-- The code has been implemented based on traffic analysis and the following
-- documentation:
-- * SSRP Protocol Specification: http://msdn.microsoft.com/en-us/library/cc219703.aspx
-- * TDS Protocol Specification: http://msdn.microsoft.com/en-us/library/dd304523.aspx
-- * TDS Protocol Documentation: http://www.freetds.org/tds.html.
-- * The JTDS source code: http://jtds.sourceforge.net/index.html.
--
-- * SSRP: Class that handles communication over the SQL Server Resolution Protocol, used for identifying instances on a host.
-- * ColumnInfo: Class containing parsers for column types which are present before the row data in all query response packets. The column information contains information relevant to the data type used to hold the data eg. precision, character sets, size etc.
-- * ColumnData: Class containing parsers for the actual column information.
-- * Token: Class containing parsers for tokens returned in all TDS responses. A server response may hold one or more tokens with information from the server. Each token has a type which has a number of type specific fields.
-- * QueryPacket: Class used to hold a query and convert it to a string suitable for transmission over a socket.
-- * LoginPacket: Class used to hold login specific data which can easily be converted to a string suitable for transmission over a socket.
-- * PreLoginPacket: Class used to (partially) implement the TDS PreLogin packet
-- * TDSStream: Class that handles communication over the Tabular Data Stream protocol used by SQL serve. It is used to transmit the the Query- and Login-packets to the server.
-- * Helper: Class which facilitates the use of the library by through action oriented functions with descriptive names.
-- * Util: A "static" class containing mostly character and type conversion functions.
--
-- The following sample code illustrates how scripts can use the Helper class
-- to interface the library:
--
-- <code>
-- local helper = mssql.Helper:new()
-- status, result = helper:Connect( host, port )
-- status, result = helper:Login( username, password, "temdpb", host.ip )
-- status, result = helper:Query( "SELECT name FROM master..syslogins" )
-- helper:Disconnect()
-- </code>
--
-- The following sample code illustrates how scripts can use the Helper class
-- with pre-discovered instances (e.g. by <code>ms-sql-discover</code> or <code>broadcast-ms-sql-discover</code>):
--
-- <code>
-- local instance = mssql.Helper.GetDiscoveredInstances( host, port )
-- if ( instance ) then
--   local helper = mssql.Helper:new()
--   status, result = helper:ConnectEx( instance )
--   status, result = helper:LoginEx( instance )
--   status, result = helper:Query( "SELECT name FROM master..syslogins" )
--   helper:Disconnect()
-- end
-- </code>
--
-- Known limitations:
-- * The library does not support SSL. The foremost reason being the awkward choice of implementation where the SSL handshake is performed within the TDS data block. By default, servers support connections over non SSL connections though.
-- * Version 7 and ONLY version 7 of the protocol is supported. This should cover Microsoft SQL Server 7.0 and later.
-- * TDS Responses contain one or more response tokens which are parsed based on their type. The supported tokens are listed in the <code>TokenType</code> table and their respective parsers can be found in the <code>Token</code> class. Note that some token parsers are not fully implemented and simply move the offset the right number of bytes to continue processing of the response.
-- * The library only supports a limited subsets of datatypes and will abort execution and return an error if it detects an unsupported type. The supported data types are listed in the <code>DataTypes</code> table. In order to add additional data types a parser function has to be added to both the <code>ColumnInfo</code> and <code>ColumnData</code> class.
-- * No functionality for languages, localization or character codepages has been considered or implemented.
-- * The library does database authentication only. No OS authentication or use of the integrated security model is supported.
-- * Queries using SELECT, INSERT, DELETE and EXEC of procedures have been tested while developing scripts.
--
-- @copyright Same as Nmap--See https://nmap.org/book/man-legal.html
--
-- @author Patrik Karlsson <patrik@cqure.net>
-- @author Chris Woodbury
--
-- @args mssql.username The username to use to connect to SQL Server instances.
--       This username is used by scripts taking actions that require
--       authentication (e.g. <code>ms-sql-query</code>) This username (and its
--       associated password) takes precedence over any credentials discovered
--       by the <code>ms-sql-brute</code> and <code>ms-sql-empty-password</code>
--       scripts.
--
-- @args mssql.password The password for <code>mssql.username</code>. If this
--       argument is not given but <code>mssql.username</code>, a blank password
--       is used.
--
-- @args mssql.instance-name In addition to instances discovered via port
--                           scanning and version detection, run scripts on
--                           these named instances (string or list of strings)
--
-- @args mssql.instance-port In addition to instances discovered via port
--                           scanning and version detection, run scripts on
--                           the instances running on these ports (number or list of numbers)
--
-- @args mssql.instance-all In addition to instances discovered via port
--                           scanning and version detection, run scripts on all
--                           discovered instances. These include named-pipe
--                           instances via SMB and those discovered via the
--                           browser service.
--
-- @args mssql.domain The domain against which to perform integrated
--       authentication. When set, the scripts assume integrated authentication
--       should be performed, rather than the default sql login.
--
-- @args mssql.protocol The protocol to use to connect to the instance. The
--       protocol may be either <code>NP</code>,<code>Named Pipes</code> or
--       <code>TCP</code>.
--
-- @args mssql.timeout How long to wait for SQL responses. This is a number
--       followed by <code>ms</code> for milliseconds, <code>s</code> for
--       seconds, <code>m</code> for minutes, or <code>h</code> for hours.
--       Default: <code>30s</code>.
--
-- @args mssql.scanned-ports-only If set, the script will only connect
--       to ports that were included in the Nmap scan. This may result in
--       instances not being discovered, particularly if UDP port 1434 is not
--       included. Additionally, instances that are found to be running on
--       ports that were not scanned (e.g. if 1434/udp is in the scan and the
--       SQL Server Browser service on that port reports an instance
--       listening on 43210/tcp, which was not scanned) will be reported but
--       will not be stored for use by other ms-sql-* scripts.

local math = require "math"
local match = require "match"
local nmap = require "nmap"
local datetime = require "datetime"
local outlib = require "outlib"
local smb = require "smb"
local smbauth = require "smbauth"
local stdnse = require "stdnse"
local strbuf = require "strbuf"
local string = require "string"
local table = require "table"
local tableaux = require "tableaux"
local unicode = require "unicode"
_ENV = stdnse.module("mssql", stdnse.seeall)

-- Created 01/17/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
-- Revised 03/28/2010 - v0.2 - fixed incorrect token types. added 30 seconds timeout
-- Revised 01/23/2011 - v0.3 - fixed parsing error in discovery code with patch
--                             from Chris Woodbury
-- Revised 02/01/2011 - v0.4 - numerous changes and additions to support new
--                             functionality in ms-sql- scripts and to be more
--                             robust in parsing and handling data. (Chris Woodbury)
-- Revised 02/19/2011 - v0.5 - numerous changes in script, library behaviour
--                             * huge improvements in version detection
--                             * added support for named pipes
--                             * added support for integrated NTLMv1 authentication
--
--                             (Patrik Karlsson, Chris Woodbury)
-- Revised 08/19/2012 - v0.6 - added multiple data types
--                             * added detection and handling of null values when processing query responses from the server
--                             * added DoneProc response token support
--
--                             (Tom Sellers)
-- Updated 10/01/2012 - v0.7 - added support for 2012 and later service packs for 2005, 2008 and 2008 R2 (Rob Nicholls)
-- Updated 02/06/2015 - v0.8 - added support for 2014 and later service packs for older versions (Rob Nicholls)

local HAVE_SSL, openssl = pcall(require, "openssl")

do
  namedpipes = smb.namedpipes
  local arg = stdnse.get_script_args( "mssql.timeout" ) or "30s"

  local timeout, err = stdnse.parse_timespec(arg)
  if not timeout then
    error(err)
  end
  MSSQL_TIMEOUT = timeout

end

-- Make args either a list or nil
local function list_of (input, transform)
  if not input then return nil end
  if type(input) ~= "table" then
    return {transform(input)}
  end
  for i, v in ipairs(input) do
    input[i] = transform(v)
  end
  return input
end

local SCANNED_PORTS_ONLY = not not stdnse.get_script_args("mssql.scanned-ports-only")
local targetInstanceNames = list_of(stdnse.get_script_args("mssql.instance-name"), string.upper)
local targetInstancePorts = list_of(stdnse.get_script_args("mssql.instance-port"), tonumber)
local targetAllInstances = not not stdnse.get_script_args("mssql.instance-all")

-- This constant is number of seconds from 1900-01-01 to 1970-01-01
local tds_offset_seconds = -2208988800 - datetime.utc_offset()

-- *************************************
-- Informational Classes
-- *************************************

--- SqlServerInstanceInfo class
SqlServerInstanceInfo =
{
  instanceName = nil,
  version = nil,
  serverName = nil,
  isClustered = nil,
  host = nil,
  port = nil,
  pipeName = nil,

  new = function(self,o)
    o = o or {}
    setmetatable(o, self)
    self.__index = self
    return o
  end,

  -- Compares two SqlServerInstanceInfo objects and determines whether they
  -- refer to the same SQL Server instance, judging by a combination of host,
  -- port, named pipe information and instance name.
  __eq = function( self, other )
    local areEqual
    if ( not (self.host and other.host) ) then
      -- if they don't both have host information, we certainly can't say
      -- whether they're the same
      areEqual = false
    else
      areEqual = (self.host.ip == other.host.ip)
    end

    if (self.port and other.port) then
      areEqual = areEqual and ( other.port.number == self.port.number and
        other.port.protocol == self.port.protocol )
    elseif (self.pipeName and other.pipeName) then
      areEqual = areEqual and (self.pipeName == other.pipeName)
    elseif (self.instanceName and other.instanceName) then
      areEqual = areEqual and (self.instanceName == other.instanceName)
    else
      -- if we have neither port nor named pipe info nor instance names,
      -- we can't say whether they're the same
      areEqual = false
    end

    return areEqual
  end,

  --- Merges the data from one SqlServerInstanceInfo object into another.
  --
  -- Each field in the first object is populated with the data from that field
  -- in second object if the first object's field is nil OR if
  -- <code>overwrite</code> is set to true. A special case is made for the
  -- <code>version</code> field, which is only overwritten in the second object
  -- has more reliable version information. The second object is not modified.
  Merge = function( self, other, overwrite )
    local mergeFields = { "host", "port", "instanceName", "version", "isClustered", "pipeName" }
    for _, fieldname in ipairs( mergeFields ) do
      -- Add values from other only if self doesn't have a value, or if overwrite is true
      if ( other[ fieldname ] ~= nil and (overwrite or self[ fieldname ] == nil) ) then
        self[ fieldname ] = other[ fieldname ]
      end
    end
    if (self.version and self.version.source == "SSRP" and
        other.version and other.version.Source == "SSNetLib") then
      self.version = other.version
    end
  end,

  --- Returns a name for the instance, based on the available information.
  --
  -- This may take one of the following forms:
  --  * HOST\INSTANCENAME
  --  * PIPENAME
  --  * HOST:PORT
  GetName = function( self )
    if (self.instanceName) then
      return string.format( "%s\\%s", self.host.ip or self.serverName or "[nil]", self.instanceName or "[nil]" )
    elseif (self.pipeName) then
      return string.format( "%s", self.pipeName )
    else
      return string.format( "%s:%s",  self.host.ip or self.serverName or "[nil]", (self.port and self.port.number) or "[nil]" )
    end
  end,

  --- Sets whether the instance is in a cluster
  --
  -- @param self
  -- @param isClustered Boolean true or the string "Yes" are interpreted as true;
  --         all other values are interpreted as false.
  SetIsClustered = function( self, isClustered )
    self.isClustered = (isClustered == true) or (isClustered == "Yes")
  end,

  --- Indicates whether this instance has networking protocols enabled, such
  --  that scripts could attempt to connect to it.
  HasNetworkProtocols = function( self )
    return (self.pipeName ~= nil) or (self.port and self.port.number)
  end,
}


--- SqlServerVersionInfo class
SqlServerVersionInfo =
{
  versionNumber = "", -- The full version string (e.g. "9.00.2047.00")
  major = nil, -- The major version (e.g. 9)
  minor = nil, -- The minor version (e.g. 0)
  build = nil, -- The build number (e.g. 2047)
  subBuild = nil, -- The sub-build number (e.g. 0)
  productName = nil, -- The product name (e.g. "SQL Server 2005")
  brandedVersion = nil, -- The branded version of the product (e.g. "2005")
  servicePackLevel = nil, -- The service pack level (e.g. "SP1")
  patched = nil, -- Whether patches have been applied since SP installation (true/false/nil)
  source = nil, -- The source of the version info (e.g. "SSRP", "SSNetLib")

  new = function(self,o)
    o = o or {}
    setmetatable(o, self)
    self.__index = self
    return o
  end,

  --- Sets the version using a version number string.
  --
  -- @param versionNumber a version number string (e.g. "9.00.1399.00")
  -- @param source a string indicating the source of the version info (e.g. "SSRP", "SSNetLib")
  SetVersionNumber = function(self, versionNumber, source)
    local parts = {versionNumber:match("^(%d+)%.(%d+)%.(%d+)")}
    if not parts[1] then
      stdnse.debug1("%s: SetVersionNumber: versionNumber is not in correct format: %s", "MSSQL", versionNumber or "nil" )
      return
    end

    -- If it doesn't match, subBuild will be nil
    parts[4] = versionNumber:match( "^%d+%.%d+%.%d+%.(%d+)" )
    parts[5] = source

    self:SetVersion( table.unpack(parts) )
  end,

  --- Sets the version using the individual numeric components of the version
  --  number.
  --
  -- @param source a string indicating the source of the version info (e.g. "SSRP", "SSNetLib")
  SetVersion = function(self, major, minor, build, subBuild, source)
    self.source = source
    -- make sure our version numbers all end up as valid numbers
    self.major, self.minor, self.build, self.subBuild =
      tonumber( major or 0 ), tonumber( minor or 0 ), tonumber( build or 0 ), tonumber( subBuild or 0 )

    self.versionNumber = string.format( "%u.%02u.%u.%02u", self.major, self.minor, self.build, self.subBuild )

    self:_ParseVersionInfo()
  end,

  --- Using the version number, determines the product version
  _InferProductVersion = function(self)

    local VERSION_LOOKUP_TABLE = {
      ["^6%.0"] = "6.0", ["^6%.5"] = "6.5", ["^7%.0"] = "7.0",
      ["^8%.0"] = "2000", ["^9%.0"] = "2005", ["^10%.0"] = "2008",
      ["^10%.50"] = "2008 R2", ["^11%.0"] = "2012", ["^12%.0"] = "2014",
      ["^13%.0"] = "2016", ["^14%.0"] = "2017", ["^15%.0"] = "2019",
      ["^16%.0"] = "2022",
    }

    local product = ""

    for m, v in pairs(VERSION_LOOKUP_TABLE) do
      if ( self.versionNumber:match(m) ) then
        product = v
        self.brandedVersion = product
        break
      end
    end

    self.productName = ("Microsoft SQL Server %s"):format(product)

  end,


  --- Returns a lookup table that maps revision numbers to service pack and
  --  cumulative update levels for the applicable SQL Server version,
  --  e.g., {{1913, "RC1"}, {2100, "RTM"}, {2316, "RTMCU1"}, ...,
  --        {3000, "SP1"}, {3321, "SP1CU1"}, ..., {3368, "SP1CU4"}, ...}
  _GetSpLookupTable = function(self)

    -- Service pack lookup tables:
    -- For instances where a revised service pack was released, e.g. 2000 SP3a,
    -- we will include the build number for the original SP and the build number
    -- for the revision. However, leaving it like this would make it appear that
    -- subsequent builds were a patched version of the revision, e.g., a patch
    -- applied to 2000 SP3 that increased the build number to 780 would get
    -- displayed as "SP3a+", when it was actually SP3+. To avoid this, we will
    -- include an additional fake build number that combines the two.
    -- Source: https://sqlserverbuilds.blogspot.com/
    local SP_LOOKUP_TABLE = {
      ["6.5"] = {
        {201, "RTM"},
        {213, "SP1"},
        {240, "SP2"},
        {258, "SP3"},
        {281, "SP4"},
        {415, "SP5"},
        {416, "SP5a"},
        {417, "SP5/SP5a"},
      },

      ["7.0"] = {
        {623, "RTM"},
        {699, "SP1"},
        {842, "SP2"},
        {961, "SP3"},
        {1063, "SP4"},
      },

      ["2000"] = {
        {194, "RTM"},
        {384, "SP1"},
        {532, "SP2"},
        {534, "SP2"},
        {760, "SP3"},
        {766, "SP3a"},
        {767, "SP3/SP3a"},
        {2039, "SP4"},
      },

      ["2005"] = {
        {1399, "RTM"},
        {2047, "SP1"},
        {3042, "SP2"},
        {4035, "SP3"},
        {5000, "SP4"},
      },

      ["2008"] = {
        {1600, "RTM"},
        {2531, "SP1"},
        {4000, "SP2"},
        {5500, "SP3"},
        {6000, "SP4"},
      },

      ["2008 R2"] = {
        {1600, "RTM"},
        {2500, "SP1"},
        {4000, "SP2"},
        {6000, "SP3"},
      },

      ["2012"] = {
        {1103, "CTP1"},
        {1440, "CTP3"},
        {1750, "RC0"},
        {1913, "RC1"},
        {2100, "RTM"},
        {2316, "RTMCU1"},
        {2325, "RTMCU2"},
        {2332, "RTMCU3"},
        {2383, "RTMCU4"},
        {2395, "RTMCU5"},
        {2401, "RTMCU6"},
        {2405, "RTMCU7"},
        {2410, "RTMCU8"},
        {2419, "RTMCU9"},
        {2420, "RTMCU10"},
        {2424, "RTMCU11"},
        {3000, "SP1"},
        {3321, "SP1CU1"},
        {3339, "SP1CU2"},
        {3349, "SP1CU3"},
        {3368, "SP1CU4"},
        {3373, "SP1CU5"},
        {3381, "SP1CU6"},
        {3393, "SP1CU7"},
        {3401, "SP1CU8"},
        {3412, "SP1CU9"},
        {3431, "SP1CU10"},
        {3449, "SP1CU11"},
        {3470, "SP1CU12"},
        {3482, "SP1CU13"},
        {3486, "SP1CU14"},
        {3487, "SP1CU15"},
        {3492, "SP1CU16"},
        {5058, "SP2"},
        {5532, "SP2CU1"},
        {5548, "SP2CU2"},
        {5556, "SP2CU3"},
        {5569, "SP2CU4"},
        {5582, "SP2CU5"},
        {5592, "SP2CU6"},
        {5623, "SP2CU7"},
        {5634, "SP2CU8"},
        {5641, "SP2CU9"},
        {5644, "SP2CU10"},
        {5646, "SP2CU11"},
        {5649, "SP2CU12"},
        {5655, "SP2CU13"},
        {5657, "SP2CU14"},
        {5676, "SP2CU15"},
        {5678, "SP2CU16"},
        {6020, "SP3"},
        {6518, "SP3CU1"},
        {6523, "SP3CU2"},
        {6537, "SP3CU3"},
        {6540, "SP3CU4"},
        {6544, "SP3CU5"},
        {6567, "SP3CU6"},
        {6579, "SP3CU7"},
        {6594, "SP3CU8"},
        {6598, "SP3CU9"},
        {6607, "SP3CU10"},
        {7001, "SP4"},
      },

      ["2014"] = {
        {1524, "CTP2"},
        {2000, "RTM"},
        {2342, "RTMCU1"},
        {2370, "RTMCU2"},
        {2402, "RTMCU3"},
        {2430, "RTMCU4"},
        {2456, "RTMCU5"},
        {2480, "RTMCU6"},
        {2495, "RTMCU7"},
        {2546, "RTMCU8"},
        {2553, "RTMCU9"},
        {2556, "RTMCU10"},
        {2560, "RTMCU11"},
        {2564, "RTMCU12"},
        {2568, "RTMCU13"},
        {2569, "RTMCU14"},
        {4100, "SP1"},
        {4416, "SP1CU1"},
        {4422, "SP1CU2"},
        {4427, "SP1CU3"},
        {4436, "SP1CU4"},
        {4439, "SP1CU5"},
        {4449, "SP1CU6"},
        {4459, "SP1CU7"},
        {4468, "SP1CU8"},
        {4474, "SP1CU9"},
        {4491, "SP1CU10"},
        {4502, "SP1CU11"},
        {4511, "SP1CU12"},
        {4522, "SP1CU13"},
        {5000, "SP2"},
        {5511, "SP2CU1"},
        {5522, "SP2CU2"},
        {5538, "SP2CU3"},
        {5540, "SP2CU4"},
        {5546, "SP2CU5"},
        {5553, "SP2CU6"},
        {5556, "SP2CU7"},
        {5557, "SP2CU8"},
        {5563, "SP2CU9"},
        {5571, "SP2CU10"},
        {5579, "SP2CU11"},
        {5589, "SP2CU12"},
        {5590, "SP2CU13"},
        {5600, "SP2CU14"},
        {5605, "SP2CU15"},
        {5626, "SP2CU16"},
        {5632, "SP2CU17"},
        {5687, "SP2CU18"},
        {6024, "SP3"},
        {6205, "SP3CU1"},
        {6214, "SP3CU2"},
        {6259, "SP3CU3"},
        {6329, "SP3CU4"},
      },

      ["2016"] = {
        { 200, "CTP2"},
        { 300, "CTP2.1"},
        { 407, "CTP2.2"},
        { 500, "CTP2.3"},
        { 600, "CTP2.4"},
        { 700, "CTP3.0"},
        { 800, "CTP3.1"},
        { 900, "CTP3.2"},
        {1000, "CTP3.3"},
        {1100, "RC0"},
        {1200, "RC1"},
        {1300, "RC2"},
        {1400, "RC3"},
        {1601, "RTM"},
        {2149, "RTMCU1"},
        {2164, "RTMCU2"},
        {2186, "RTMCU3"},
        {2193, "RTMCU4"},
        {2197, "RTMCU5"},
        {2204, "RTMCU6"},
        {2210, "RTMCU7"},
        {2213, "RTMCU8"},
        {2216, "RTMCU9"},
        {4001, "SP1"},
        {4411, "SP1CU1"},
        {4422, "SP1CU2"},
        {4435, "SP1CU3"},
        {4446, "SP1CU4"},
        {4451, "SP1CU5"},
        {4457, "SP1CU6"},
        {4466, "SP1CU7"},
        {4474, "SP1CU8"},
        {4502, "SP1CU9"},
        {4514, "SP1CU10"},
        {4528, "SP1CU11"},
        {4541, "SP1CU12"},
        {4550, "SP1CU13"},
        {4560, "SP1CU14"},
        {4574, "SP1CU15"},
        {5026, "SP2"},
        {5149, "SP2CU1"},
        {5153, "SP2CU2"},
        {5216, "SP2CU3"},
        {5233, "SP2CU4"},
        {5264, "SP2CU5"},
        {5292, "SP2CU6"},
        {5337, "SP2CU7"},
        {5426, "SP2CU8"},
        {5479, "SP2CU9"},
        {5492, "SP2CU10"},
        {5598, "SP2CU11"},
        {5698, "SP2CU12"},
        {5820, "SP2CU13"},
        {5830, "SP2CU14"},
        {5850, "SP2CU15"},
        {5882, "SP2CU16"},
        {5888, "SP2CU17"},
        {6300, "SP3"},
      },

      ["2017"] = {
        {   1, "CTP1"},
        { 100, "CTP1.1"},
        { 200, "CTP1.2"},
        { 304, "CTP1.3"},
        { 405, "CTP1.4"},
        { 500, "CTP2.0"},
        { 600, "CTP2.1"},
        { 800, "RC1"},
        { 900, "RC2"},
        {1000, "RTM"},
        {3006, "CU1"},
        {3008, "CU2"},
        {3015, "CU3"},
        {3022, "CU4"},
        {3023, "CU5"},
        {3025, "CU6"},
        {3026, "CU7"},
        {3029, "CU8"},
        {3030, "CU9"},
        {3037, "CU10"},
        {3038, "CU11"},
        {3045, "CU12"},
        {3048, "CU13"},
        {3076, "CU14"},
        {3162, "CU15"},
        {3223, "CU16"},
        {3238, "CU17"},
        {3257, "CU18"},
        {3281, "CU19"},
        {3294, "CU20"},
        {3335, "CU21"},
        {3356, "CU22"},
        {3381, "CU23"},
        {3391, "CU24"},
        {3401, "CU25"},
        {3411, "CU26"},
        {3421, "CU27"},
        {3430, "CU28"},
        {3436, "CU29"},
        {3451, "CU30"},
      },

      ["2019"] = {
        {1000, "CTP2.0"},
        {1100, "CTP2.1"},
        {1200, "CTP2.2"},
        {1300, "CTP2.3"},
        {1400, "CTP2.4"},
        {1500, "CTP2.5"},
        {1600, "CTP3.0"},
        {1700, "CTP3.1"},
        {1800, "CTP3.2"},
        {1900, "RC1"},
        {2000, "RTM"},
        {2070, "GDR1"},
        {4003, "CU1"},
        {4013, "CU2"},
        {4023, "CU3"},
        {4033, "CU4"},
        {4043, "CU5"},
        {4053, "CU6"},
        {4063, "CU7"},
        {4073, "CU8"},
        {4102, "CU9"},
        {4123, "CU10"},
        {4138, "CU11"},
        {4153, "CU12"},
        {4178, "CU13"},
        {4188, "CU14"},
        {4198, "CU15"},
        {4223, "CU16"},
        {4249, "CU17"},
      },

      ["2022"] = {
        {100, "CTP1.0"},
        {101, "CTP1.1"},
        {200, "CTP1.2"},
        {300, "CTP1.3"},
        {400, "CTP1.4"},
        {500, "CTP1.5"},
        {600, "CTP2.0"},
        {700, "CTP2.1"},
        {900, "RC0"},
      },
    }


    if ( not self.brandedVersion ) then
      self:_InferProductVersion()
    end

    local spLookupTable = SP_LOOKUP_TABLE[self.brandedVersion]
    stdnse.debug1("brandedVersion: %s, #lookup: %d", self.brandedVersion, spLookupTable and #spLookupTable or 0)

    return spLookupTable

  end,


  --- Processes version data to determine (if possible) the product version,
  --  service pack level and patch status.
  _ParseVersionInfo = function(self)

    local spLookupTable = self:_GetSpLookupTable()

    if spLookupTable then

      local spLookupItr = 0
      -- Loop through the service pack levels until we find one whose revision
      -- number is the same as or lower than our revision number.
      while spLookupItr < #spLookupTable do
        spLookupItr = spLookupItr + 1

        if (spLookupTable[ spLookupItr ][1] == self.build ) then
          spLookupItr = spLookupItr
          break
        elseif (spLookupTable[ spLookupItr ][1] > self.build ) then
          -- The target revision number is lower than the first release
          if spLookupItr == 1 then
            self.servicePackLevel = "Pre-RTM"
          else
            -- we went too far - it's the previous SP, but with patches applied
            spLookupItr = spLookupItr - 1
          end
          break
        end
      end

      -- Now that we've identified the proper service pack level:
      if self.servicePackLevel ~= "Pre-RTM" then
        self.servicePackLevel = spLookupTable[ spLookupItr ][2]

        if ( spLookupTable[ spLookupItr ][1] == self.build ) then
          self.patched = false
        else
          self.patched = true
        end
      end

      -- Clean up some of our inferences. If the source of our revision number
      -- was the SSRP (SQL Server Browser) response, we need to recognize its
      -- limitations:
      --  * Versions of SQL Server prior to 2005 are reported with the RTM build
      --    number, regardless of the actual version (e.g. SQL Server 2000 is
      --    always 8.00.194).
      --  * Versions of SQL Server starting with 2005 (and going through at least
      --    2008) do better but are still only reported with the build number as
      --    of the last service pack (e.g. SQL Server 2005 SP3 with patches is
      --    still reported as 9.00.4035.00).
      if ( self.source == "SSRP" ) then
        self.patched = nil

        if ( self.major <= 8 ) then
          self.servicePackLevel = nil
        end
      end
    end

    return true
  end,

  ---
  ToString = function(self)
    local friendlyVersion = strbuf.new()
    if self.productName then
      friendlyVersion:concatbuf( self.productName )
      if self.servicePackLevel then
        friendlyVersion:concatbuf( " " )
        friendlyVersion:concatbuf( self.servicePackLevel )
      end
      if self.patched then
        friendlyVersion:concatbuf( "+" )
      end
    end

    return friendlyVersion:dump()
  end,

  --- Uses the information in this SqlServerVersionInformation object to
  --  populate the version information in an Nmap port table for a SQL Server
  --  TCP listener.
  --
  --  @param self A SqlServerVersionInformation object
  --  @param port An Nmap port table corresponding to the instance
  PopulateNmapPortVersion = function(self, port)

    port.service = "ms-sql-s"
    port.version = port.version or {}
    port.version.name = "ms-sql-s"
    port.version.product = self.productName

    local versionString = strbuf.new()
    if self.source ~= "SSRP" then
      versionString:concatbuf( self.versionNumber )
      if self.servicePackLevel then
        versionString:concatbuf( "; " )
        versionString:concatbuf( self.servicePackLevel )
      end
      if self.patched then
        versionString:concatbuf( "+" )
      end
      port.version.version = versionString:dump()
    end

    return port
  end,
}


-- *************************************
-- SSRP (SQL Server Resolution Protocol)
-- *************************************
SSRP =
{
  PORT = { number = 1434, protocol = "udp" },
  DEBUG_ID = "MSSQL-SSRP",

  MESSAGE_TYPE =
  {
    ClientBroadcast = 0x02,
    ClientUnicast = 0x03,
    ClientUnicastInstance = 0x04,
    ClientUnicastDAC = 0x0F,
    ServerResponse = 0x05,
  },

  --- Parses an SSRP string and returns a table containing one or more
  --  SqlServerInstanceInfo objects created from the parsed string.
  _ParseSsrpString = function( host, ssrpString )
    -- It would seem easier to just capture (.-;;) repeatedly, since
    -- each instance ends with ";;", but ";;" can also occur within the
    -- data, signifying an empty field (e.g. "...bv;;@COMPNAME;;tcp;1433;;...").
    -- So, instead, we'll split up the string ahead of time.
    -- See the SSRP specification for more details.

    local instanceStrings = {}
    local firstInstanceEnd, instanceString
    repeat
      firstInstanceEnd = ssrpString:find( ";ServerName;(.-);InstanceName;(.-);IsClustered;(.-);" )
      if firstInstanceEnd then
        instanceString = ssrpString:sub( 1, firstInstanceEnd )
        ssrpString = ssrpString:sub( firstInstanceEnd + 1 )
      else
        instanceString = ssrpString
      end

      table.insert( instanceStrings, instanceString )
    until (not firstInstanceEnd)
    stdnse.debug2("%s: SSRP Substrings:\n  %s", SSRP.DEBUG_ID, table.concat(instanceStrings , "\n  ") )

    local instances = {}
    for _, instanceString in ipairs( instanceStrings ) do
      local instance = SqlServerInstanceInfo:new()
      local version = SqlServerVersionInfo:new()
      instance.version = version

      instance.host = host
      instance.serverName = instanceString:match( "ServerName;(.-);")
      instance.instanceName = instanceString:match( "InstanceName;(.-);")
      instance:SetIsClustered( instanceString:match( "IsClustered;(.-);") )
      version:SetVersionNumber( instanceString:match( "Version;(.-);"), "SSRP" )

      local tcpPort = tonumber( instanceString:match( ";tcp;(.-);") )
      if tcpPort then instance.port = {number = tcpPort, protocol = "tcp"} end

      local pipeName = instanceString:match( ";np;(.-);")
      local status, pipeSubPath = namedpipes.get_pipe_subpath( pipeName )
      if status then
        pipeName = namedpipes.make_pipe_name( host.ip, pipeSubPath )
      elseif pipeName ~= nil then
        stdnse.debug1("%s: Invalid pipe name:\n%s", SSRP.DEBUG_ID, pipeName )
      end
      instance.pipeName = pipeName

      table.insert( instances, instance )
    end

    return instances
  end,

  ---
  _ProcessResponse = function( host, responseData )
    local instances

    local pos, messageType, dataLength = 1, nil, nil
    messageType, dataLength, pos = string.unpack("<BI2", responseData, 1)
    -- extract the response data (i.e. everything after the 3-byte header)
    responseData = responseData:sub(4)
    stdnse.debug2("%s: SSRP Data: %s", SSRP.DEBUG_ID, responseData )
    if ( messageType ~= SSRP.MESSAGE_TYPE.ServerResponse or
        dataLength ~= responseData:len() ) then

      stdnse.debug2("%s: Invalid SSRP response. Type: 0x%02x, Length: %d, Actual length: %d",
        SSRP.DEBUG_ID, messageType, dataLength, responseData:len() )
    else
      instances = SSRP._ParseSsrpString( host, responseData )
    end

    return instances
  end,

  ---  Attempts to retrieve information about SQL Server instances by querying
  --  the SQL Server Browser service on a host.
  --
  --  @param host A host table for the target host
  --  @param port (Optional) A port table for the target SQL Server Browser service
  --  @return (status, result) If status is true, result is a table of
  --    SqlServerInstanceInfo objects. If status is false, result is an
  --    error message.
  DiscoverInstances = function( host, port )
    port = port or SSRP.PORT

    if ( SCANNED_PORTS_ONLY and nmap.get_port_state( host, port ) == nil ) then
      stdnse.debug2("%s: Discovery disallowed: scanned-ports-only is set and port %d was not scanned", SSRP.DEBUG_ID, port.number )
      return false, "Discovery disallowed: scanned-ports-only"
    end

    local socket = nmap.new_socket("udp")
    socket:set_timeout(5000)

    if ( port.number ~= SSRP.PORT.number ) then
      stdnse.debug1("%s: DiscoverInstances() called with non-standard port (%d)", SSRP.DEBUG_ID, port.number )
    end

    local status, err = socket:connect( host, port )
    if ( not(status) ) then return false, err end
    status, err = socket:send( string.pack( "B", SSRP.MESSAGE_TYPE.ClientUnicast ) )
    if ( not(status) ) then return false, err end

    local responseData, instances_host
    status, responseData = socket:receive()
    if ( not(status) ) then return false, responseData
    else
      instances_host = SSRP._ProcessResponse( host, responseData )
    end
    socket:close()

    return status, instances_host
  end,


  --- Attempts to retrieve information about SQL Server instances by querying
  -- the SQL Server Browser service on a broadcast domain.
  --
  -- @param host A host table for the broadcast specification
  -- @param port (Optional) A port table for the target SQL Server Browser service
  -- @return (status, result) If status is true, result is a table of
  --         tables containing SqlServerInstanceInfo objects. The top-level table
  --         is indexed by IP address. If status is false, result is an
  --         error message.
  DiscoverInstances_Broadcast = function( host, port )
    port = port or SSRP.PORT

    local socket = nmap.new_socket("udp")
    socket:set_timeout(5000)
    local instances_all = {}

    if ( port.number ~= SSRP.PORT.number ) then
      stdnse.debug1("%S: DiscoverInstances_Broadcast() called with non-standard port (%d)", SSRP.DEBUG_ID, port.number )
    end

    local status, err = socket:sendto(host, port, string.pack( "B", SSRP.MESSAGE_TYPE.ClientBroadcast ))
    if ( not(status) ) then return false, err end

    while ( status ) do
      local responseData
      status, responseData = socket:receive()
      if ( status ) then
        local remoteIp, _
        status, _, _, remoteIp, _ = socket:get_info()
        local instances_host = SSRP._ProcessResponse( {ip = remoteIp, name = ""}, responseData )
        instances_all[ remoteIp ] = instances_host
      end
    end
    socket:close()

    return true, instances_all
  end,
}



-- *************************
-- TDS (Tabular Data Stream)
-- *************************

-- TDS packet types
PacketType =
{
  Query = 0x01,
  Response = 0x04,
  Login = 0x10,
  NTAuthentication = 0x11,
  PreLogin = 0x12,
}

-- TDS response token types
TokenType =
{
  ReturnStatus         = 0x79,
  TDS7Results          = 0x81,
  ErrorMessage         = 0xAA,
  InformationMessage   = 0xAB,
  LoginAcknowledgement = 0xAD,
  Row                  = 0xD1,
  OrderBy              = 0xA9,
  EnvironmentChange    = 0xE3,
  NTLMSSP_CHALLENGE    = 0xed,
  Done                 = 0xFD,
  DoneProc             = 0xFE,
  DoneInProc           = 0xFF,
}

-- SQL Server/Sybase data types
DataTypes =
{
  SQLTEXT       = 0x23,
  GUIDTYPE      = 0x24,
  SYBINTN       = 0x26,
  SYBINT2       = 0x34,
  SYBINT4       = 0x38,
  SYBDATETIME   = 0x3D,
  NTEXTTYPE     = 0x63,
  BITNTYPE      = 0x68,
  DECIMALNTYPE  = 0x6A,
  NUMERICNTYPE  = 0x6C,
  FLTNTYPE      = 0x6D,
  MONEYNTYPE    = 0x6E,
  SYBDATETIMN   = 0x6F,
  XSYBVARBINARY = 0xA5,
  XSYBVARCHAR   = 0xA7,
  BIGBINARYTYPE = 0xAD,
  BIGCHARTYPE   = 0xAF,
  XSYBNVARCHAR  = 0xE7,
  SQLNCHAR      = 0xEF,
}

-- SQL Server login error codes
-- See http://msdn.microsoft.com/en-us/library/ms131024.aspx
LoginErrorType =
{
  AccountLockedOut = 15113,
  NotAssociatedWithTrustedConnection = 18452, -- This probably means that the server is set for Windows authentication only
  InvalidUsernameOrPassword = 18456,
  PasswordChangeFailed_PasswordNotAllowed = 18463,
  PasswordChangeFailed_PasswordTooShort = 18464,
  PasswordChangeFailed_PasswordTooLong = 18465,
  PasswordChangeFailed_PasswordNotComplex = 18466,
  PasswordChangeFailed_PasswordFilter = 18467,
  PasswordChangeFailed_UnexpectedError = 18468,
  PasswordExpired = 18487,
  PasswordMustChange = 18488,
}

LoginErrorMessage = {}
for i, v in pairs(LoginErrorType) do
  LoginErrorMessage[v] = i
end

-- "static" ColumnInfo parser class
ColumnInfo =
{

  Parse =
  {

    [DataTypes.SQLTEXT] = function( data, pos )
      local colinfo = {}
      local tmp

      colinfo.unknown, colinfo.codepage, colinfo.flags, colinfo.charset, pos = string.unpack("<I4I2I2B", data, pos )

      colinfo.tablenamelen, pos = string.unpack("<i2", data, pos )
      colinfo.tablename, pos = string.unpack("c" .. (colinfo.tablenamelen * 2), data, pos)
      colinfo.msglen, pos = string.unpack("<B", data, pos )
      tmp, pos = string.unpack("c" .. (colinfo.msglen * 2), data, pos)

      colinfo.text = unicode.utf16to8(tmp)

      return pos, colinfo
    end,

    [DataTypes.GUIDTYPE] = function( data, pos )
      return ColumnInfo.Parse[DataTypes.SYBINTN](data, pos)
    end,

    [DataTypes.SYBINTN] = function( data, pos )
      local colinfo = {}
      local tmp

      colinfo.unknown, colinfo.msglen, pos = string.unpack("<BB", data, pos)
      tmp, pos = string.unpack("c" .. (colinfo.msglen * 2), data, pos )
      colinfo.text = unicode.utf16to8(tmp)

      return pos, colinfo
    end,

    [DataTypes.SYBINT2] = function( data, pos )
      return ColumnInfo.Parse[DataTypes.SYBDATETIME](data, pos)
    end,

    [DataTypes.SYBINT4] = function( data, pos )
      return ColumnInfo.Parse[DataTypes.SYBDATETIME](data, pos)
    end,

    [DataTypes.SYBDATETIME] = function( data, pos )
      local colinfo = {}
      local tmp

      colinfo.msglen, pos = string.unpack("B", data, pos)
      tmp, pos = string.unpack("c" .. (colinfo.msglen * 2), data, pos )
      colinfo.text = unicode.utf16to8(tmp)

      return pos, colinfo
    end,

    [DataTypes.NTEXTTYPE] = function( data, pos )
      return ColumnInfo.Parse[DataTypes.SQLTEXT](data, pos)
    end,

    [DataTypes.BITNTYPE] = function( data, pos )
      return ColumnInfo.Parse[DataTypes.SYBINTN](data, pos)
    end,

    [DataTypes.DECIMALNTYPE] = function( data, pos )
      local colinfo = {}
      local tmp

      colinfo.unknown, colinfo.precision, colinfo.scale, pos = string.unpack("<BBB", data, pos)
      colinfo.msglen, pos = string.unpack("<B",data,pos)
      tmp, pos = string.unpack("c" .. (colinfo.msglen * 2), data, pos )
      colinfo.text = unicode.utf16to8(tmp)

      return pos, colinfo
    end,

    [DataTypes.NUMERICNTYPE] = function( data, pos )
      return ColumnInfo.Parse[DataTypes.DECIMALNTYPE](data, pos)
    end,

    [DataTypes.FLTNTYPE] = function( data, pos )
      return ColumnInfo.Parse[DataTypes.SYBINTN](data, pos)
    end,

    [DataTypes.MONEYNTYPE] = function( data, pos )
      return ColumnInfo.Parse[DataTypes.SYBINTN](data, pos)
    end,

    [DataTypes.SYBDATETIMN] = function( data, pos )
      return ColumnInfo.Parse[DataTypes.SYBINTN](data, pos)
    end,

    [DataTypes.XSYBVARBINARY] = function( data, pos )
      local colinfo = {}
      local tmp

      colinfo.lts, colinfo.msglen, pos = string.unpack("<I2B", data, pos)
      tmp, pos = string.unpack("c" .. (colinfo.msglen * 2), data, pos )
      colinfo.text = unicode.utf16to8(tmp)

      return pos, colinfo
    end,

    [DataTypes.XSYBVARCHAR] = function( data, pos )
      return ColumnInfo.Parse[DataTypes.XSYBNVARCHAR](data, pos)
    end,

    [DataTypes.BIGBINARYTYPE] = function( data, pos )
      return ColumnInfo.Parse[DataTypes.XSYBVARBINARY](data, pos)
    end,

    [DataTypes.BIGCHARTYPE] = function( data, pos )
      return ColumnInfo.Parse[DataTypes.XSYBNVARCHAR](data, pos)
    end,

    [DataTypes.XSYBNVARCHAR] = function( data, pos )
      local colinfo = {}
      local tmp

      colinfo.lts, colinfo.codepage, colinfo.flags, colinfo.charset,
      colinfo.msglen, pos = string.unpack("<I2I2I2BB", data, pos )
      tmp, pos = string.unpack("c" .. (colinfo.msglen * 2), data, pos)
      colinfo.text = unicode.utf16to8(tmp)

      return pos, colinfo
    end,

    [DataTypes.SQLNCHAR] = function( data, pos )
      return ColumnInfo.Parse[DataTypes.XSYBNVARCHAR](data, pos)
    end,

  }

}

-- "static" ColumnData parser class
ColumnData =
{
  Parse = {

    [DataTypes.SQLTEXT] = function( data, pos )
      local len, coldata

      -- The first len value is the size of the meta data block
      -- for non-null values this seems to be 0x10 / 16 bytes
      len, pos = string.unpack( "<B", data, pos )

      if ( len == 0 ) then
        return pos, 'Null'
      end

      -- Skip over the text update time and date values, we don't need them
      -- We may come back add parsing for this information.
      pos = pos + len

      -- skip a label, should be 'dummyTS'
      pos = pos + 8

      -- extract the actual data
      coldata, pos = string.unpack( "<s4", data, pos )

      return pos, coldata
    end,

    [DataTypes.GUIDTYPE] = function( data, pos )
      local len, coldata, index, nextdata
      local hex = {}
      len, pos = string.unpack("B", data, pos)

      if ( len == 0 ) then
        return pos, 'Null'

      elseif ( len == 16 ) then
        -- Mixed-endian; first 3 parts are little-endian, next 2 are big-endian
        local A, B, C, D, E, pos = string.unpack("<I4I2I2>c2c6", data, pos)
        coldata = ("%08x-%04x-%04x-%s-%s"):format(A, B, C, stdnse.tohex(D), stdnse.tohex(E))
      else
        stdnse.debug1("Unhandled length (%d) for GUIDTYPE", len)
        return pos + len, 'Unsupported Data'
      end

      return pos, coldata
    end,

    [DataTypes.SYBINTN] = function( data, pos )
      local len, num
      len, pos = string.unpack("B", data, pos)

      if ( len == 0 ) then
        return pos, 'Null'
      elseif ( len <= 16 ) then
        local v, pos = string.unpack("<i" .. len, data, pos)
        return pos, v
      else
        return -1, ("Unhandled length (%d) for SYBINTN"):format(len)
      end

      return -1, "Error"
    end,

    [DataTypes.SYBINT2] = function( data, pos )
      local num
      num, pos = string.unpack("<I2", data, pos)

      return pos, num
    end,

    [DataTypes.SYBINT4] = function( data, pos )
      local num
      num, pos = string.unpack("<I4", data, pos)

      return pos, num
    end,

    [DataTypes.SYBDATETIME] = function( data, pos )
      local hi, lo

      hi, lo, pos = string.unpack("<i4I4", data, pos)

      local result_seconds = (hi*24*60*60) + (lo/300)

      local result = datetime.format_timestamp(tds_offset_seconds + result_seconds)
      return pos, result
    end,

    [DataTypes.NTEXTTYPE] = function( data, pos )
      local len, coldata

      -- The first len value is the size of the meta data block
      len, pos = string.unpack( "<B", data, pos )

      if ( len == 0 ) then
        return pos, 'Null'
      end

      -- Skip over the text update time and date values, we don't need them
      -- We may come back add parsing for this information.
      pos = pos + len

      -- skip a label, should be 'dummyTS'
      pos = pos + 8

      -- extract the actual data
      coldata, pos = string.unpack( "<s4", data, pos )

      return pos, unicode.utf16to8(coldata)
    end,

    [DataTypes.BITNTYPE] = function( data, pos )
      return ColumnData.Parse[DataTypes.SYBINTN](data, pos)
    end,

    [DataTypes.DECIMALNTYPE] = function( precision, scale, data, pos )
      local len, sign, format_string, coldata

      len, pos = string.unpack("<B", data, pos)

      if ( len == 0 ) then
        return pos, 'Null'
      end

      sign, pos = string.unpack("<B", data, pos)

      -- subtract 1 from data len to account for sign byte
      len = len - 1

      if ( len > 0 and len <= 16 ) then
        coldata, pos = string.unpack("<I" .. len, data, pos)
      else
        stdnse.debug1("Unhandled length (%d) for DECIMALNTYPE", len)
        return pos + len, 'Unsupported Data'
      end

      if ( sign == 0 ) then
        coldata = coldata * -1
      end

      coldata = coldata * (10^-scale)
      -- format the return information to reduce truncation by lua
      format_string = string.format("%%.%if", scale)
      coldata = string.format(format_string,coldata)

      return pos, coldata
    end,

    [DataTypes.NUMERICNTYPE] = function( precision, scale, data, pos )
      return ColumnData.Parse[DataTypes.DECIMALNTYPE]( precision, scale, data, pos )
    end,

    [DataTypes.BITNTYPE] = function( data, pos )
      return ColumnData.Parse[DataTypes.SYBINTN](data, pos)
    end,

    [DataTypes.NTEXTTYPE] = function( data, pos )
      local len, coldata

      -- The first len value is the size of the meta data block
      len, pos = string.unpack( "<B", data, pos )

      if ( len == 0 ) then
        return pos, 'Null'
      end

      -- Skip over the text update time and date values, we don't need them
      -- We may come back add parsing for this information.
      pos = pos + len

      -- skip a label, should be 'dummyTS'
      pos = pos + 8

      -- extract the actual data
      coldata, pos = string.unpack( "<s4", data, pos )

      return pos, unicode.utf16to8(coldata)
    end,

    [DataTypes.FLTNTYPE] = function( data, pos )
      local len, coldata
      len, pos = string.unpack("<B", data, pos)

      if ( len == 0 ) then
        return pos, 'Null'
      elseif ( len == 4 ) then
        coldata, pos = string.unpack("<f", data, pos)
      elseif ( len == 8 ) then
        coldata, pos = string.unpack("<d", data, pos)
      end

      return pos, coldata
    end,

    [DataTypes.MONEYNTYPE] = function( data, pos )
      local len, value, coldata, hi, lo
      len, pos = string.unpack("B", data, pos)

      if ( len == 0 ) then
        return pos, 'Null'
      elseif ( len == 4 ) then
        --type smallmoney
        value, pos = string.unpack("<i4", data, pos)
      elseif ( len == 8 ) then
        -- type money
        hi, lo, pos = string.unpack("<I4I4", data, pos)
        value = ( hi * 0x100000000 ) + lo
      else
        return -1, ("Unhandled length (%d) for MONEYNTYPE"):format(len)
      end

      -- the datatype allows for 4 decimal places after the period to support various currency types.
      -- forcing to string to avoid truncation
      coldata = string.format("%.4f",value/10000)

      return pos, coldata
    end,

    [DataTypes.SYBDATETIMN] = function( data, pos )
      local len, coldata

      len, pos = string.unpack( "<B", data, pos )

      if ( len == 0 ) then
        return pos, 'Null'
      elseif ( len == 4 ) then
        -- format is smalldatetime
        local days, mins
        days, mins, pos = string.unpack("<I2I2", data, pos)

        local result_seconds = (days*24*60*60) + (mins*60)
        coldata = datetime.format_timestamp(tds_offset_seconds + result_seconds)

        return pos,coldata

      elseif ( len == 8 ) then
        -- format is datetime
        return ColumnData.Parse[DataTypes.SYBDATETIME](data, pos)
      else
        return -1, ("Unhandled length (%d) for SYBDATETIMN"):format(len)
      end

    end,

    [DataTypes.XSYBVARBINARY] = function( data, pos )
      local len, coldata

      len, pos = string.unpack( "<I2", data, pos )

      if ( len == 65535 ) then
        return pos, 'Null'
      else
        coldata, pos = string.unpack( "c"..len, data, pos )
        return pos, "0x" .. stdnse.tohex(coldata)
      end

      return -1, "Error"
    end,

    [DataTypes.XSYBVARCHAR] = function( data, pos )
      local len, coldata

      len, pos = string.unpack( "<I2", data, pos )
      if ( len == 65535 ) then
        return pos, 'Null'
      end

      coldata, pos = string.unpack( "c"..len, data, pos )

      return pos, coldata
    end,

    [DataTypes.BIGBINARYTYPE] = function( data, pos )
      return ColumnData.Parse[DataTypes.XSYBVARBINARY](data, pos)
    end,

    [DataTypes.BIGCHARTYPE] = function( data, pos )
      return ColumnData.Parse[DataTypes.XSYBVARCHAR](data, pos)
    end,

    [DataTypes.XSYBNVARCHAR] = function( data, pos )
      local len, coldata

      len, pos = string.unpack( "<I2", data, pos )
      if ( len == 65535 ) then
        return pos, 'Null'
      end
      coldata, pos = string.unpack( "c"..len, data, pos )

      return pos, unicode.utf16to8(coldata)
    end,

    [DataTypes.SQLNCHAR] = function( data, pos )
      return ColumnData.Parse[DataTypes.XSYBNVARCHAR](data, pos)
    end,

  }
}

-- "static" Token parser class
Token =
{

  Parse = {
    --- Parse error message tokens
    --
    -- @param data string containing "raw" data
    -- @param pos number containing offset into data
    -- @return pos number containing new offset after parse
    -- @return token table containing token specific fields
    [TokenType.ErrorMessage] = function( data, pos )
      local token = {}
      local tmp

      token.type = TokenType.ErrorMessage
      token.size, token.errno, token.state, token.severity, token.errlen, pos = string.unpack( "<I2I4BBI2", data, pos )
      tmp, pos = string.unpack("c" .. (token.errlen * 2), data, pos )
      token.error = unicode.utf16to8(tmp)
      token.srvlen, pos = string.unpack("B", data, pos)
      tmp, pos = string.unpack("c" .. (token.srvlen * 2), data, pos )
      token.server = unicode.utf16to8(tmp)
      token.proclen, pos = string.unpack("B", data, pos)
      tmp, pos = string.unpack("c" .. (token.proclen * 2), data, pos )
      token.proc = unicode.utf16to8(tmp)
      token.lineno, pos = string.unpack("<I2", data, pos)

      return pos, token
    end,

    --- Parse environment change tokens
    -- (This function is not implemented and simply moves the pos offset)
    --
    -- @param data string containing "raw" data
    -- @param pos number containing offset into data
    -- @return pos number containing new offset after parse
    -- @return token table containing token specific fields
    [TokenType.EnvironmentChange] = function( data, pos )
      local token = {}
      local tmp

      token.type = TokenType.EnvironmentChange
      token.size, pos = string.unpack("<I2", data, pos)

      return pos + token.size, token
    end,

    --- Parse information message tokens
    --
    -- @param data string containing "raw" data
    -- @param pos number containing offset into data
    -- @return pos number containing new offset after parse
    -- @return token table containing token specific fields
    [TokenType.InformationMessage] = function( data, pos )
      local pos, token = Token.Parse[TokenType.ErrorMessage]( data, pos )
      token.type = TokenType.InformationMessage
      return pos, token
    end,

    --- Parse login acknowledgment tokens
    --
    -- @param data string containing "raw" data
    -- @param pos number containing offset into data
    -- @return pos number containing new offset after parse
    -- @return token table containing token specific fields
    [TokenType.LoginAcknowledgement] = function( data, pos )
      local token = {}
      local _

      token.type = TokenType.LoginAcknowledgement
      token.size, _, _, _, _, token.textlen, pos = string.unpack( "<I2BBBI2B", data, pos )
      token.text, pos = string.unpack("c" .. token.textlen * 2, data, pos)
      token.version, pos = string.unpack("<I4", data, pos )

      return pos, token
    end,

    --- Parse done tokens
    --
    -- @param data string containing "raw" data
    -- @param pos number containing offset into data
    -- @return pos number containing new offset after parse
    -- @return token table containing token specific fields
    [TokenType.Done] = function( data, pos )
      local token = {}

      token.type = TokenType.Done
      token.flags, token.operation, token.rowcount, pos = string.unpack( "<I2I2I4", data, pos )

      return pos, token
    end,

    --- Parses a DoneProc token received after executing a SP
    --
    -- @param data string containing "raw" data
    -- @param pos number containing offset into data
    -- @return pos number containing new offset after parse
    -- @return token table containing token specific fields
    [TokenType.DoneProc] = function( data, pos )
      local token
      pos, token = Token.Parse[TokenType.Done]( data, pos )
      token.type = TokenType.DoneProc

      return pos, token
    end,


    --- Parses a DoneInProc token received after executing a SP
    --
    -- @param data string containing "raw" data
    -- @param pos number containing offset into data
    -- @return pos number containing new offset after parse
    -- @return token table containing token specific fields
    [TokenType.DoneInProc] = function( data, pos )
      local token
      pos, token = Token.Parse[TokenType.Done]( data, pos )
      token.type = TokenType.DoneInProc

      return pos, token
    end,

    --- Parses a ReturnStatus token
    --
    -- @param data string containing "raw" data
    -- @param pos number containing offset into data
    -- @return pos number containing new offset after parse
    -- @return token table containing token specific fields
    [TokenType.ReturnStatus] = function( data, pos )
      local token = {}

      token.value, pos = string.unpack("<i4", data, pos)
      token.type = TokenType.ReturnStatus
      return pos, token
    end,

    --- Parses a OrderBy token
    --
    -- @param data string containing "raw" data
    -- @param pos number containing offset into data
    -- @return pos number containing new offset after parse
    -- @return token table containing token specific fields
    [TokenType.OrderBy] = function( data, pos )
      local token = {}

      token.size, pos = string.unpack("<I2", data, pos)
      token.type = TokenType.OrderBy
      return pos + token.size, token
    end,


    --- Parse TDS result tokens
    --
    -- @param data string containing "raw" data
    -- @param pos number containing offset into data
    -- @return pos number containing new offset after parse
    -- @return token table containing token specific fields
    [TokenType.TDS7Results] = function( data, pos )
      local token = {}
      local _

      token.type = TokenType.TDS7Results
      token.count, pos = string.unpack( "<I2", data, pos )
      token.colinfo = {}

      for i=1, token.count do
        local colinfo = {}
        local usertype, flags, ttype

        usertype, flags, ttype, pos = string.unpack("<I2I2B", data, pos )
        if ( not(ColumnInfo.Parse[ttype]) ) then
          return -1, ("Unhandled data type: 0x%X"):format(ttype)
        end

        pos, colinfo = ColumnInfo.Parse[ttype]( data, pos )
        colinfo.usertype = usertype
        colinfo.flags = flags
        colinfo.type = ttype

        table.insert( token.colinfo, colinfo )
      end
      return pos, token
    end,


    [TokenType.NTLMSSP_CHALLENGE] = function(data, pos)
      local len, ntlmssp, msgtype, pos = string.unpack("<I2c8I4", data, pos)
      local NTLMSSP_CHALLENGE = 2

      if ( ntlmssp ~= "NTLMSSP\0" or msgtype ~= NTLMSSP_CHALLENGE ) then
        return -1, "Failed to process NTLMSSP Challenge"
      end

      local ntlm_challenge = {nonce=data:sub( 28, 35 ), type=TokenType.NTLMSSP_CHALLENGE}
      pos = pos + len - 13
      return pos, ntlm_challenge
    end,
  },

  --- Parses the first token at positions pos
  --
  -- @param data string containing "raw" data
  -- @param pos number containing offset into data
  -- @return pos number containing new offset after parse or -1 on error
  -- @return token table containing token specific fields or error message on error
  ParseToken = function( data, pos )
    local ttype
    ttype, pos = string.unpack("B", data, pos)
    if ( not(Token.Parse[ttype]) ) then
      stdnse.debug1("%s: No parser for token type 0x%X", "MSSQL", ttype )
      return -1, ("No parser for token type: 0x%X"):format( ttype )
    end

    return Token.Parse[ttype](data, pos)
  end,

}


--- QueryPacket class
QueryPacket =
{
  new = function(self,o)
    o = o or {}
    setmetatable(o, self)
    self.__index = self
    return o
  end,

  SetQuery = function( self, query )
    self.query = query
  end,

  --- Returns the query packet as string
  --
  -- @return string containing the authentication packet
  ToString = function( self )
    return PacketType.Query, unicode.utf8to16( self.query )
  end,

}


--- PreLoginPacket class
PreLoginPacket =
{
  -- TDS pre-login option types
  OPTION_TYPE = {
    Version = 0x00,
    Encryption = 0x01,
    InstOpt = 0x02,
    ThreadId = 0x03,
    MARS = 0x04,
    Terminator = 0xFF,
  },


  versionInfo = nil,
  _requestEncryption = 0,
  _instanceName = "",
  _threadId = 0, -- Dummy value; will be filled in later
  _requestMars = nil,

  new = function(self,o)
    o = o or {}
    setmetatable(o, self)
    self.__index = self
    return o
  end,

  --- Sets the client version (default = 9.00.1399.00)
  --
  -- @param versionInfo A SqlServerVersionInfo object with the client version information
  SetVersion = function(self, versionInfo)
    self._versionInfo = versionInfo
  end,

  --- Sets whether to request encryption (default = false)
  --
  -- @param requestEncryption A boolean indicating whether encryption will be requested
  SetRequestEncryption = function(self, requestEncryption)
    if requestEncryption then
      self._requestEncryption = 1
    else
      self._requestEncryption = 0
    end
  end,

  --- Sets whether to request MARS support (default = undefined)
  --
  -- @param requestMars A boolean indicating whether MARS support will be requested
  SetRequestMars = function(self, requestMars)
    if requestMars then
      self._requestMars = 1
    else
      self._requestMars = 0
    end
  end,

  --- Sets the instance name of the target
  --
  -- @param instanceName A string containing the name of the instance
  SetInstanceName = function(self, instanceName)
    self._instanceName = instanceName or ""
  end,

  --- Returns the pre-login packet as a byte string
  --
  -- @return byte string containing the pre-login packet
  ToBytes = function(self)
    -- Lengths for the values of TDS pre-login option fields
    local OPTION_LENGTH_CLIENT = {
      [PreLoginPacket.OPTION_TYPE.Version] = 6,
      [PreLoginPacket.OPTION_TYPE.Encryption] = 1,
      [PreLoginPacket.OPTION_TYPE.InstOpt] = -1,
      [PreLoginPacket.OPTION_TYPE.ThreadId] = 4,
      [PreLoginPacket.OPTION_TYPE.MARS] = 1,
      [PreLoginPacket.OPTION_TYPE.Terminator] = 0,
    }

    local optionLength, optionType = 0, 0
    local offset = 1 -- Terminator
    offset = offset + 5 -- Version
    offset = offset + 5 -- Encryption
    offset = offset + 5 -- InstOpt
    offset = offset + 5 -- ThreadId
    if self._requestMars then offset = offset + 3 end -- MARS

    if not self.versionInfo then
      self.versionInfo = SqlServerVersionInfo:new()
      self.versionInfo:SetVersionNumber( "9.00.1399.00" )
    end

    optionType = PreLoginPacket.OPTION_TYPE.Version
    optionLength = OPTION_LENGTH_CLIENT[ optionType ]
    local data = { string.pack( ">BI2I2", optionType, offset, optionLength ) }
    offset = offset + optionLength

    optionType = PreLoginPacket.OPTION_TYPE.Encryption
    optionLength = OPTION_LENGTH_CLIENT[ optionType ]
    data[#data+1] = string.pack( ">BI2I2", optionType, offset, optionLength )
    offset = offset + optionLength

    optionType = PreLoginPacket.OPTION_TYPE.InstOpt
    optionLength = #self._instanceName + 1 --(string length + null-terminator)
    data[#data+1] = string.pack( ">BI2I2", optionType, offset, optionLength )
    offset = offset + optionLength

    optionType = PreLoginPacket.OPTION_TYPE.ThreadId
    optionLength = OPTION_LENGTH_CLIENT[ optionType ]
    data[#data+1] = string.pack( ">BI2I2", optionType, offset, optionLength )
    offset = offset + optionLength

    if self.requestMars then
      optionType = PreLoginPacket.OPTION_TYPE.MARS
      optionLength = OPTION_LENGTH_CLIENT[ optionType ]
      data[#data+1] = string.pack( ">BI2I2", optionType, offset, optionLength )
      offset = offset + optionLength
    end

    data[#data+1] = string.pack( "B", PreLoginPacket.OPTION_TYPE.Terminator )

    -- Now that the pre-login headers are done, write the data
    data[#data+1] = string.pack( ">BBI2I2", self.versionInfo.major, self.versionInfo.minor,
    self.versionInfo.build, self.versionInfo.subBuild )
    data[#data+1] = string.pack( "<BzI4", self._requestEncryption, self._instanceName, self._threadId )
    if self.requestMars then
      data[#data+1] = string.pack( "B", self._requestMars )
    end

    return PacketType.PreLogin, table.concat(data)
  end,

  --- Reads a byte-string and creates a PreLoginPacket object from it. This is
  -- intended to handle the server's response to a pre-login request.
  FromBytes = function( bytes )
    local OPTION_LENGTH_SERVER = {
      [PreLoginPacket.OPTION_TYPE.Version] = 6,
      [PreLoginPacket.OPTION_TYPE.Encryption] = 1,
      [PreLoginPacket.OPTION_TYPE.InstOpt] = -1,
      [PreLoginPacket.OPTION_TYPE.ThreadId] = 0, -- According to the TDS spec, this value should be empty from the server
      [PreLoginPacket.OPTION_TYPE.MARS] = 1,
      [PreLoginPacket.OPTION_TYPE.Terminator] = 0,
    }


    local status, pos = false, 1
    local preLoginPacket = PreLoginPacket:new()

    while true do

      local optionType, optionPos, optionLength, optionData, expectedOptionLength, _
      if pos > #bytes then
        stdnse.debug2("%s: Could not extract optionType.", "MSSQL" )
        return false, "Invalid pre-login response"
      end
      optionType, pos = ("B"):unpack(bytes, pos)
      if ( optionType == PreLoginPacket.OPTION_TYPE.Terminator ) then
        status = true
        break
      end
      expectedOptionLength = OPTION_LENGTH_SERVER[ optionType ]
      if ( not expectedOptionLength ) then
        stdnse.debug2("%s: Unrecognized pre-login option type: %s", "MSSQL", optionType )
        expectedOptionLength = -1
      end

      if pos + 4 > #bytes + 1 then
        stdnse.debug2("%s: Could not unpack optionPos and optionLength.", "MSSQL" )
        return false, "Invalid pre-login response"
      end
      optionPos, optionLength, pos = (">I2I2"):unpack(bytes, pos)

      optionPos = optionPos + 1 -- convert from 0-based index to 1-based index

      if ( optionLength ~= expectedOptionLength and expectedOptionLength ~= -1 ) then
        stdnse.debug2("%s: Option data is incorrect size in pre-login response. ", "MSSQL" )
        stdnse.debug2("%s:   (optionType: %s) (optionLength: %s)", "MSSQL", optionType, optionLength )
        return false, "Invalid pre-login response"
      end
      optionData = bytes:sub( optionPos, optionPos + optionLength - 1 )
      if #optionData ~= optionLength then
        stdnse.debug2("%s: Could not read sufficient bytes from version data.", "MSSQL" )
        return false, "Invalid pre-login response"
      end

      if ( optionType == PreLoginPacket.OPTION_TYPE.Version ) then
        local major, minor, build, subBuild = (">BBI2I2"):unpack(optionData)
        local version = SqlServerVersionInfo:new()
        version:SetVersion( major, minor, build, subBuild, "SSNetLib" )
        preLoginPacket.versionInfo = version
      elseif ( optionType == PreLoginPacket.OPTION_TYPE.Encryption ) then
        preLoginPacket:SetRequestEncryption( ("B"):unpack(optionData) )
      elseif ( optionType == PreLoginPacket.OPTION_TYPE.InstOpt ) then
        preLoginPacket:SetInstanceName( ("z"):unpack(optionData) )
      elseif ( optionType == PreLoginPacket.OPTION_TYPE.ThreadId ) then
        -- Do nothing. According to the TDS spec, this option is empty when sent from the server
      elseif ( optionType == PreLoginPacket.OPTION_TYPE.MARS ) then
        preLoginPacket:SetRequestMars( ("B"):unpack(optionData) )
      end
    end

    return status, preLoginPacket
  end,
}


--- LoginPacket class
LoginPacket =
{

  -- options_1 possible values
  -- 0x80 enable warning messages if SET LANGUAGE issued
  -- 0x40 change to initial database must succeed
  -- 0x20 enable warning messages if USE <database> issued
  -- 0x10 enable BCP

  -- options_2 possible values
  -- 0x80 enable domain login security
  -- 0x40 "USER_SERVER - reserved"
  -- 0x20 user type is "DQ login"
  -- 0x10 user type is "replication login"
  -- 0x08 "fCacheConnect"
  -- 0x04 "fTranBoundary"
  -- 0x02 client is an ODBC driver
  -- 0x01 change to initial language must succeed
  length = 0,
  version = 0x71000001, -- Version 7.1
  size = 0,
  cli_version = 7, -- From jTDS JDBC driver
  cli_pid = 0, -- Dummy value
  conn_id = 0,
  options_1 = 0xa0,
  options_2 = 0x03,
  sqltype_flag = 0,
  reserved_flag= 0,
  time_zone = 0,
  collation = 0,

  -- Strings
  client = "Nmap",
  username = nil,
  password = nil,
  app = "Nmap NSE",
  server = nil,
  library = "mssql.lua",
  locale = "",
  database = "master", --nil,
  MAC = "\x00\x00\x00\x00\x00\x00", -- should contain client MAC, jTDS uses all zeroes

  new = function(self,o)
    o = o or {}
    setmetatable(o, self)
    self.__index = self
    return o
  end,

  --- Sets the username used for authentication
  --
  -- @param username string containing the username to user for authentication
  SetUsername = function(self, username)
    self.username = username
  end,

  --- Sets the password used for authentication
  --
  -- @param password string containing the password to user for authentication
  SetPassword = function(self, password)
    self.password = password
  end,

  --- Sets the database used in authentication
  --
  -- @param database string containing the database name
  SetDatabase = function(self, database)
    self.database = database
  end,

  --- Sets the server's name used in authentication
  --
  -- @param server string containing the name or ip of the server
  SetServer = function(self, server)
    self.server = server
  end,

  SetDomain = function(self, domain)
    self.domain = domain
  end,

  --- Returns the authentication packet as string
  --
  -- @return string containing the authentication packet
  ToString = function(self)
    local data
    local offset = 86
    local ntlmAuth = not(not(self.domain))
    local authLen = 0

    self.cli_pid = math.random(100000)
    local u_client = unicode.utf8to16(self.client)
    local u_app = unicode.utf8to16(self.app)
    local u_server = unicode.utf8to16(self.server)
    local u_library = unicode.utf8to16(self.library)
    local u_locale = unicode.utf8to16(self.locale)
    local u_database = unicode.utf8to16(self.database)
    local u_username, uc_password

    self.length = offset + #u_client + #u_app + #u_server + #u_library + #u_database

    if ( ntlmAuth ) then
      authLen = 32 + #self.domain
      self.length = self.length + authLen
      self.options_2 = self.options_2 + 0x80
    else
      u_username = unicode.utf8to16(self.username)
      uc_password = Auth.TDS7CryptPass(self.password, unicode.utf8_dec)
      self.length = self.length + #u_username + #uc_password
    end

    data = {
      string.pack("<I4I4I4I4I4I4", self.length, self.version, self.size, self.cli_version, self.cli_pid, self.conn_id ),
      string.pack("BBBB", self.options_1, self.options_2, self.sqltype_flag, self.reserved_flag ),
      string.pack("<I4I4", self.time_zone, self.collation ),

      -- offsets begin
      string.pack("<I2I2", offset, #u_client/2 ),
    }
    offset = offset + #u_client

    if ( not(ntlmAuth) ) then
      data[#data+1] = string.pack("<I2I2", offset, #u_username/2 )

      offset = offset + #u_username
      data[#data+1] = string.pack("<I2I2", offset, #uc_password/2 )
      offset = offset + #uc_password
    else
      data[#data+1] = string.pack("<I2I2", offset, 0 )
      data[#data+1] = string.pack("<I2I2", offset, 0 )
    end

    data[#data+1] = string.pack("<I2I2", offset, #u_app/2 )
    offset = offset + #u_app

    data[#data+1] = string.pack("<I2I2", offset, #u_server/2 )
    offset = offset + #u_server

    -- Offset to unused placeholder (reserved for future use in TDS spec)
    data[#data+1] = string.pack("<I2I2", 0, 0 )

    data[#data+1] = string.pack("<I2I2", offset, #u_library/2 )
    offset = offset + #u_library

    data[#data+1] = string.pack("<I2I2", offset, #u_locale/2 )
    offset = offset + #u_locale

    data[#data+1] = string.pack("<I2I2", offset, #u_database/2 )
    offset = offset + #u_database

    -- client MAC address, hardcoded to 00:00:00:00:00:00
    data[#data+1] = self.MAC

    -- offset to auth info
    data[#data+1] = string.pack("<I2", offset)
    -- length of nt auth (should be 0 for sql auth)
    data[#data+1] = string.pack("<I2", authLen)
    -- next position (same as total packet length)
    data[#data+1] = string.pack("<I2", self.length)
    -- zero pad
    data[#data+1] = string.pack("<I2", 0)

    -- Auth info wide strings
    data[#data+1] = u_client
    if ( not(ntlmAuth) ) then
      data[#data+1] = u_username
      data[#data+1] = uc_password
    end
    data[#data+1] = u_app
    data[#data+1] = u_server
    data[#data+1] = u_library
    data[#data+1] = u_locale
    data[#data+1] = u_database

    if ( ntlmAuth ) then
      local NTLMSSP_NEGOTIATE = 1
      local flags = 0x0000b201
      local workstation = ""

      data[#data+1] = "NTLMSSP\0"
      data[#data+1] = string.pack("<I4I4", NTLMSSP_NEGOTIATE, flags)
      data[#data+1] = string.pack("<I2I2I4", #self.domain, #self.domain, 32)
      data[#data+1] = string.pack("<I2I2I4", #workstation, #workstation, 32)
      data[#data+1] = self.domain:upper()
    end

    return PacketType.Login, table.concat(data)
  end,

}

NTAuthenticationPacket = {

  new = function(self, username, password, domain, nonce)
    local o = {}
    setmetatable(o, self)
    o.username = username
    o.domain = domain
    o.nonce = nonce
    o.password = password
    self.__index = self
    return o
  end,

  ToString = function(self)
    local ntlmssp = "NTLMSSP\0"
    local NTLMSSP_AUTH = 3
    local domain = unicode.utf8to16(self.domain:upper())
    local user = unicode.utf8to16(self.username)
    local hostname, sessionkey = "", ""
    local flags = 0x00008201
    local ntlm_response = Auth.NtlmResponse(self.password, self.nonce)
    local lm_response = Auth.LmResponse(self.password, self.nonce)

    local domain_offset = 64
    local username_offset = domain_offset + #domain
    local lm_response_offset = username_offset + #user
    local ntlm_response_offset = lm_response_offset + #lm_response
    local hostname_offset = ntlm_response_offset + #ntlm_response
    local sessionkey_offset = hostname_offset + #hostname

    local data = ntlmssp .. string.pack("<I4I2I2I4", NTLMSSP_AUTH, #lm_response, #lm_response, lm_response_offset)
    .. string.pack("<I2I2I4", #ntlm_response, #ntlm_response, ntlm_response_offset)
    .. string.pack("<I2I2I4", #domain, #domain, domain_offset)
    .. string.pack("<I2I2I4", #user, #user, username_offset)
    .. string.pack("<I2I2I4", #hostname, #hostname, hostname_offset)
    .. string.pack("<I2I2I4", #sessionkey, #sessionkey, sessionkey_offset)
    .. string.pack("<I4", flags)
    .. domain
    .. user
    .. lm_response .. ntlm_response

    return PacketType.NTAuthentication, data
  end,

}

-- Handles communication with SQL Server
TDSStream = {

  -- Status flag constants
  MESSAGE_STATUS_FLAGS = {
    Normal = 0x0,
    EndOfMessage = 0x1,
    IgnoreThisEvent = 0x2,
    ResetConnection = 0x4,
    ResetConnectionSkipTran = 0x8,
  },

  _packetId = 0,
  _pipe = nil,
  _socket = nil,
  _name = nil,

  new = function(self,o)
    o = o or {}
    setmetatable(o, self)
    self.__index = self
    return o
  end,

  --- Establishes a connection to the SQL server.
  --
  --  @param self A mssql.Helper object
  --  @param instanceInfo  A SqlServerInstanceInfo object for the instance to
  --    connect to.
  --  @param connectionPreference (Optional) A list containing one or both of
  --    the strings "TCP" and "Named Pipes", indicating which transport
  --    methods to try and in what order.
  --  @param smbOverrides (Optional) An overrides table for calls to the <code>smb</code>
  --    library (for use with named pipes).
  ConnectEx = function( self, instanceInfo, connectionPreference, smbOverrides )
    if ( self._socket ) then return false, "Already connected via TCP" end
    if ( self._pipe ) then return false, "Already connected via named pipes" end
    connectionPreference = connectionPreference or stdnse.get_script_args('mssql.protocol') or { "TCP", "Named Pipes" }
    if ( connectionPreference and 'string' == type(connectionPreference) ) then
      connectionPreference = { connectionPreference }
    end

    local status, result, connectionType, errorMessage
    stdnse.debug3("%s: Connection preferences for %s: %s",
    "MSSQL", instanceInfo:GetName(), table.concat(connectionPreference, ", ") )

    for _, connectionType in ipairs( connectionPreference ) do
      if connectionType == "TCP" then

        if not ( instanceInfo.port ) then
          stdnse.debug3("%s: Cannot connect to %s via TCP because port table is not set.",
          "MSSQL", instanceInfo:GetName() )
          result = "No TCP port for this instance"
        else
          status, result = self:Connect( instanceInfo.host, instanceInfo.port )
          if status then return true end
        end

      elseif connectionType == "Named Pipes" or connectionType == "NP" then

        if not ( instanceInfo.pipeName ) then
          stdnse.debug3("%s: Cannot connect to %s via named pipes because pipe name is not set.",
          "MSSQL", instanceInfo:GetName() )
          result = "No named pipe for this instance"
        else
          status, result = self:ConnectToNamedPipe( instanceInfo.host, instanceInfo.pipeName, smbOverrides )
          if status then return true end
        end

      else
        stdnse.debug1("%s: Unknown connection preference: %s", "MSSQL", connectionType )
        return false, ("ERROR: Unknown connection preference: %s"):format(connectionType)
      end

      -- Handle any error messages
      if not status then
        if errorMessage then
          errorMessage = string.format( "%s, %s: %s", errorMessage, connectionType, result or "nil" )
        else
          errorMessage = string.format( "%s: %s", connectionType, result or "nil" )
        end
      end
    end

    if not errorMessage then
      errorMessage = string.format( "%s: None of the preferred connection types are available for %s\\%s",
      "MSSQL", instanceInfo:GetName() )
    end

    return false, errorMessage
  end,

  --- Establishes a connection to the SQL server
  --
  -- @param host A host table for the target host
  -- @param pipePath The path to the named pipe of the target SQL Server
  --         (e.g. "\MSSQL$SQLEXPRESS\sql\query"). If nil, "\sql\query\" is used.
  -- @param smbOverrides (Optional) An overrides table for calls to the <code>smb</code>
  --        library (for use with named pipes).
  -- @return status: true on success, false on failure
  -- @return error_message: an error message, or nil
  ConnectToNamedPipe = function( self, host, pipePath, overrides )
    if ( self._socket ) then return false, "Already connected via TCP" end

    if ( SCANNED_PORTS_ONLY and smb.get_port( host ) == nil ) then
      stdnse.debug2("%s: Connection disallowed: scanned-ports-only is set and no SMB port is available", "MSSQL" )
      return false, "Connection disallowed: scanned-ports-only"
    end

    pipePath = pipePath or "\\sql\\query"

    self._pipe = namedpipes.named_pipe:new()
    local status, result = self._pipe:connect( host, pipePath, overrides )
    if ( status ) then
      self._name = self._pipe.pipe
    else
      self._pipe = nil
    end

    return status, result
  end,

  --- Establishes a connection to the SQL server
  --
  -- @param host table containing host information
  -- @param port table containing port information
  -- @return status true on success, false on failure
  -- @return result containing error message on failure
  Connect = function( self, host, port )
    if ( self._pipe ) then return false, "Already connected via named pipes" end

    if ( SCANNED_PORTS_ONLY and nmap.get_port_state( host, port ) == nil ) then
      stdnse.debug2("%s: Connection disallowed: scanned-ports-only is set and port %d was not scanned", "MSSQL", port.number )
      return false, "Connection disallowed: scanned-ports-only"
    end

    local status, result, lport, _

    self._socket = nmap.new_socket()

    -- Set the timeout to something realistic for connects
    self._socket:set_timeout( 5000 )
    status, result = self._socket:connect(host, port)

    if ( status ) then
      -- Sometimes a Query can take a long time to respond, so we set
      -- the timeout to 30 seconds. This shouldn't be a problem as the
      -- library attempt to decode the protocol and avoid reading past
      -- the end of the input buffer. So the only time the timeout is
      -- triggered is when waiting for a response to a query.
      self._socket:set_timeout( MSSQL_TIMEOUT * 1000 )

      status, _, lport, _, _ = self._socket:get_info()
    end

    if ( not(status) ) then
      self._socket = nil
      stdnse.debug2("%s: Socket connection failed on %s:%s", "MSSQL", host.ip, port.number )
      return false, "Socket connection failed"
    end
    self._name = string.format( "%s:%s", host.ip, port.number )

    return status, result
  end,

  --- Disconnects from the SQL Server
  --
  -- @return status true on success, false on failure
  -- @return result containing error message on failure
  Disconnect = function( self )
    if ( self._socket ) then
      local status, result = self._socket:close()
      self._socket = nil
      return status, result
    elseif ( self._pipe ) then
      local status, result = self._pipe:disconnect()
      self._pipe = nil
      return status, result
    else
      return false, "Not connected"
    end
  end,

  --- Sets the timeout for communication over the socket
  --
  -- @param timeout number containing the new socket timeout in ms
  SetTimeout = function( self, timeout )
    if ( self._socket ) then
      self._socket:set_timeout(timeout)
    else
      return false, "Not connected"
    end
  end,

  --- Gets the name of the name pipe, or nil
  GetNamedPipeName = function( self )
    if ( self._pipe ) then
      return self._pipe.name
    else
      return nil
    end
  end,

  --- Send a TDS request to the server
  --
  -- @param packetType A <code>PacketType</code>, indicating the type of TDS
  --                   packet being sent.
  -- @param packetData A string containing the raw data to send to the server
  -- @return status true on success, false on failure
  -- @return result containing error message on failure
  Send = function( self, packetType, packetData )
    local packetLength = packetData:len() + 8 -- +8 for TDS header
    local messageStatus, spid, window = 1, 0, 0


    if ( packetType ~= PacketType.NTAuthentication ) then self._packetId = self._packetId + 1 end
    local assembledPacket = string.pack(">BBI2I2BB", packetType, messageStatus, packetLength, spid, self._packetId, window) .. packetData

    if ( self._socket ) then
      return self._socket:send( assembledPacket )
    elseif ( self._pipe ) then
      return self._pipe:send( assembledPacket )
    else
      return false, "Not connected"
    end
  end,

  --- Receives responses from SQL Server
  --
  -- The function continues to read and assemble a response until the server
  -- responds with the last response flag set
  --
  -- @return status true on success, false on failure
  -- @return result containing raw data contents or error message on failure
  -- @return errorDetail nil, or additional information about an error. In
  --         the case of named pipes, this will be an SMB error name (e.g. NT_STATUS_PIPE_DISCONNECTED)
  Receive = function( self )
    local status, result, errorDetail
    local combinedData, readBuffer = "", "" -- the buffer is solely for the benefit of TCP connections
    local tdsPacketAvailable = true

    if not ( self._socket or self._pipe ) then
      return false, "Not connected"
    end

    -- Large messages (e.g. result sets) can be split across multiple TDS
    -- packets from the server (which could themselves each be split across
    -- multiple TCP packets or SMB messages).
    while ( tdsPacketAvailable ) do
      local packetType, messageStatus, packetLength, spid, window
      local pos = 1

      if ( self._socket ) then
        -- If there is existing data in the readBuffer, see if there's
        -- enough to read the TDS headers for the next packet. If not,
        -- do another read so we have something to work with.
        if ( readBuffer:len() < 8 ) then
          status, result = self._socket:receive_bytes(8 - readBuffer:len())
          readBuffer = readBuffer .. result
        end
      elseif ( self._pipe ) then
        -- The named pipe takes care of all of its reassembly. We don't
        -- have to mess with buffers and repeatedly reading until we get
        -- the whole packet. We'll still write to readBuffer, though, so
        -- that the common logic can be reused.
        status, result, errorDetail = self._pipe:receive()
        readBuffer = result
      end

      if not ( status and readBuffer ) then return false, result, errorDetail end

      -- TDS packet validity check: packet at least as long as the TDS header
      if ( readBuffer:len() < 8 ) then
        stdnse.debug2("%s: Receiving (%s): packet is invalid length", "MSSQL", self._name )
        return false, "Server returned invalid packet"
      end

      -- read in the TDS headers
      packetType, messageStatus, packetLength, pos = string.unpack(">BBI2", readBuffer, pos )
      spid, self._packetId, window, pos = string.unpack(">I2BB", readBuffer, pos )

      -- TDS packet validity check: packet type is Response (0x4)
      if ( packetType ~= PacketType.Response ) then
        stdnse.debug2("%s: Receiving (%s): Expected type 0x4 (response), but received type 0x%x",
          "MSSQL", self._name, packetType )
        return false, "Server returned invalid packet"
      end

      if ( self._socket ) then
        -- If we didn't previously read in enough data to complete this
        -- TDS packet, let's do so.
        while ( packetLength - readBuffer:len() > 0 ) do
          status, result = self._socket:receive()
          if not ( status and result ) then return false, result end
          readBuffer = readBuffer .. result
        end
      end

      -- We've read in an apparently valid TDS packet
      local thisPacketData = readBuffer:sub( pos, packetLength )
      -- Append its data to that of any previous TDS packets
      combinedData = combinedData .. thisPacketData
      if ( self._socket ) then
        -- If we read in data beyond the end of this TDS packet, save it
        -- so that we can use it in the next loop.
        readBuffer = readBuffer:sub( packetLength + 1 )
      end

      -- TDS packet validity check: packet length matches length from header
      if ( packetLength ~= (thisPacketData:len() + 8) ) then
        stdnse.debug2("%s: Receiving (%s): Header reports length %d, actual length is %d",
          "MSSQL", self._name, packetLength, thisPacketData:len()  )
        return false, "Server returned invalid packet"
      end

      -- Check the status flags in the TDS packet to see if the message is
      -- continued in another TDS packet.
      tdsPacketAvailable = (( messageStatus & TDSStream.MESSAGE_STATUS_FLAGS.EndOfMessage) ~=
        TDSStream.MESSAGE_STATUS_FLAGS.EndOfMessage)
    end

    -- return only the data section ie. without the headers
    return status, combinedData
  end,

}

--- Helper class
Helper =
{
  new = function(self,o)
    o = o or {}
    setmetatable(o, self)
    self.__index = self
    return o
  end,

  --- Establishes a connection to the SQL server
  --
  -- @param host table containing host information
  -- @param port table containing port information
  -- @return status true on success, false on failure
  -- @return result containing error message on failure
  ConnectEx = function( self, instanceInfo )
    local status, result
    self.stream = TDSStream:new()
    status, result = self.stream:ConnectEx( instanceInfo )
    if ( not(status) ) then
      return false, result
    end

    return true
  end,

  --- Establishes a connection to the SQL server
  --
  -- @param host table containing host information
  -- @param port table containing port information
  -- @return status true on success, false on failure
  -- @return result containing error message on failure
  Connect = function( self, host, port )
    local status, result
    self.stream = TDSStream:new()
    status, result = self.stream:Connect(host, port)
    if ( not(status) ) then
      return false, result
    end

    return true
  end,

  --- Returns true if discovery has been performed to detect
  -- SQL Server instances on the given host
  WasDiscoveryPerformed = function( host )
    local mutex = nmap.mutex( "discovery_performed for " .. host.ip )
    mutex( "lock" )
    nmap.registry.mssql = nmap.registry.mssql or {}
    nmap.registry.mssql.discovery_performed = nmap.registry.mssql.discovery_performed or {}

    local wasPerformed = nmap.registry.mssql.discovery_performed[ host.ip ] or false
    mutex( "done" )

    return wasPerformed
  end,

  --- Adds an instance to the list of instances kept in the Nmap registry for
  --  shared use by SQL Server scripts.
  --
  --  If the registry already contains the instance, any new information is
  --  merged into the existing instance info.  This may happen, for example,
  --  when an instance is discovered via named pipes, but the same instance has
  --  already been discovered via SSRP; this will prevent duplicates, where
  --  possible.
  AddOrMergeInstance = function( newInstance )
    local instanceExists

    nmap.registry.mssql = nmap.registry.mssql or {}
    nmap.registry.mssql.instances = nmap.registry.mssql.instances or {}
    nmap.registry.mssql.instances[ newInstance.host.ip ] = nmap.registry.mssql.instances[ newInstance.host.ip ] or {}

    for _, existingInstance in ipairs( nmap.registry.mssql.instances[ newInstance.host.ip ] ) do
      if existingInstance == newInstance then
        existingInstance:Merge( newInstance )
        instanceExists = true
        break
      end
    end

    if not instanceExists then
      table.insert( nmap.registry.mssql.instances[ newInstance.host.ip ], newInstance )
    end
  end,

  --- Gets a table containing SqlServerInstanceInfo objects discovered on
  --  the specified host (and port, if specified).
  --
  --  This table is the NSE registry table itself, not a copy, so do not alter
  --  it unintentionally.
  --
  --  @param host A host table for the target host
  --  @param port (Optional) If omitted, all of the instances for the host
  --    will be returned.
  --  @return A table containing SqlServerInstanceInfo objects, or nil
  GetDiscoveredInstances = function( host, port )
    nmap.registry.mssql = nmap.registry.mssql or {}
    nmap.registry.mssql.instances = nmap.registry.mssql.instances or {}
    nmap.registry.mssql.instances[ host.ip ] = nmap.registry.mssql.instances[ host.ip ] or {}

    local instances = nmap.registry.mssql.instances[ host.ip ]
    if ( not port ) then
      if ( instances and #instances == 0 ) then instances = nil end
      return instances
    else
      for _, instance in ipairs(instances) do
        if ( instance.port and instance.port.number == port.number and
          instance.port.protocol == port.protocol ) then
          return { instance }
        end
      end

      return nil
    end
  end,

  --- Attempts to discover SQL Server instances using SSRP to query one or
  --  more (if <code>broadcast</code> is used) SQL Server Browser services.
  --
  --  Any discovered instances are returned, as well as being stored for use
  --  by other scripts (see <code>mssql.Helper.GetDiscoveredInstances()</code>).
  --
  --  @param host A host table for the target.
  --  @param port (Optional) A port table for the target port. If this is nil,
  --    the default SSRP port (UDP 1434) is used.
  --  @param broadcast If true, this will be done with an SSRP broadcast, and
  --    <code>host</code> should contain the broadcast specification (e.g.
  --    ip = "255.255.255.255").
  --  @return (status, result) If status is true, result is a table of
  --    tables containing SqlServerInstanceInfo objects. The top-level table
  --    is indexed by IP address. If status is false, result is an
  --    error message.
  DiscoverBySsrp = function( host, port, broadcast )

    if broadcast then
      local status, result = SSRP.DiscoverInstances_Broadcast( host, port )

      if not status then
        return status, result
      else
        for ipAddress, host in pairs( result ) do
          for _, instance in ipairs( host ) do
            Helper.AddOrMergeInstance( instance )
            -- Give some version info back to Nmap
            if ( instance.port and instance.version ) then
              instance.version:PopulateNmapPortVersion( instance.port )
              --nmap.set_port_version( instance.host, instance.port)
            end
          end
        end

        return true, result
      end
    else
      local status, result = SSRP.DiscoverInstances( host, port )

      if not status then
        return status, result
      else
        for _, instance in ipairs( result ) do
          Helper.AddOrMergeInstance( instance )
          -- Give some version info back to Nmap
          if ( instance.port and instance.version ) then
            instance.version:PopulateNmapPortVersion( instance.port )
            nmap.set_port_version( host, instance.port)
          end
        end

        local instances_all = {}
        instances_all[ host.ip ] = result
        return true, instances_all
      end
    end
  end,

  --- Attempts to discover a SQL Server instance listening on the specified
  --  port.
  --
  --  If an instance is discovered, it is returned, as well as being stored for
  --  use by other scripts (see
  --  <code>mssql.Helper.GetDiscoveredInstances()</code>).
  --
  --  @param host A host table for the target.
  --  @param port A port table for the target port.
  --  @return (status, result) If status is true, result is a table of
  --    SqlServerInstanceInfo objects. If status is false, result is an
  --    error message or nil.
  DiscoverByTcp = function( host, port )
    local version, instance, status
    -- Check to see if we've already discovered an instance on this port
    local instance = Helper.GetDiscoveredInstances(host, port)
    if instance then
      return true, {instance}
    end
    instance =  SqlServerInstanceInfo:new()
    instance.host = host
    instance.port = port

    -- -sV may have gotten a version, but for now, it doesn't extract subBuild.
    status, version = Helper.GetInstanceVersion( instance )
    if not status then
      return false, version
    end

    Helper.AddOrMergeInstance( instance )
    -- The point of this wasn't to get the version, just to use the
    -- pre-login packet to determine whether there was a SQL Server on
    -- the port. However, since we have the version now, we'll store it.
    instance.version = version
    -- Give some version info back to Nmap
    if ( instance.port and instance.version ) then
      instance.version:PopulateNmapPortVersion( instance.port )
      nmap.set_port_version( host, instance.port)
    end

    return true, { instance }
  end,

  ---  Attempts to discover SQL Server instances listening on default named
  --  pipes.
  --
  --  Any discovered instances are returned, as well as being stored for use by
  --  other scripts (see <code>mssql.Helper.GetDiscoveredInstances()</code>).
  --
  --  @param host A host table for the target.
  --  @param port A port table for the port to connect on for SMB
  --  @return (status, result) If status is true, result is a table of
  --    SqlServerInstanceInfo objects. If status is false, result is an
  --    error message or nil.
  DiscoverBySmb = function( host, port )
    local defaultPipes = {
      "\\sql\\query",
      "\\MSSQL$SQLEXPRESS\\sql\\query",
      "\\MSSQL$SQLSERVER\\sql\\query",
    }
    local tdsStream = TDSStream:new()
    local status, result, instances_host

    for _, pipeSubPath in ipairs( defaultPipes ) do
      status, result = tdsStream:ConnectToNamedPipe( host, pipeSubPath, nil )

      if status then
        instances_host = {}
        local instance = SqlServerInstanceInfo:new()
        instance.pipeName = tdsStream:GetNamedPipeName()
        tdsStream:Disconnect()
        instance.host = host

        Helper.AddOrMergeInstance( instance )
        table.insert( instances_host, instance )
      else
        stdnse.debug3("DiscoverBySmb \n pipe: %s\n result: %s", pipeSubPath, tostring( result ) )
      end
    end

    return (instances_host ~= nil), instances_host
  end,

  --- Attempts to discover SQL Server instances by a variety of means.
  --
  --  This function calls the three DiscoverBy functions, which perform the
  --  actual discovery. Any discovered instances can be retrieved using
  --  <code>mssql.Helper.GetDiscoveredInstances()</code>.
  --
  --  @param host Host table as received by the script action function
  Discover = function( host )
    local mutex = nmap.mutex( "discovery_performed for " .. host.ip )
    mutex( "lock" )
    nmap.registry.mssql = nmap.registry.mssql or {}
    nmap.registry.mssql.discovery_performed = nmap.registry.mssql.discovery_performed or {}
    if nmap.registry.mssql.discovery_performed[ host.ip ] then
      mutex "done"
      return
    end
    nmap.registry.mssql.discovery_performed[ host.ip ] = false

    -- First, do SSRP discovery. Check any open (got response) ports first:
    local port = nmap.get_ports(host, nil, "udp", "open")
    while port do
      if port.version and port.version.name == "ms-sql-m" then
        Helper.DiscoverBySsrp(host, port)
      end
      port = nmap.get_ports(host, port, "udp", "open")
    end
    -- Then check if default SSRP port hasn't been done yet.
    port = nmap.get_port_state(host, SSRP.PORT)
    if not port or port.state == "open|filtered" then
      -- Either it wasn't scanned or it wasn't strictly "open" so we missed it above
        Helper.DiscoverBySsrp(host, port)
    end

    -- Next, do TCP discovery. Check any ports with an appropriate service name
    port = nmap.get_ports(host, nil, "tcp", "open")
    while port do
      if port.version and port.version.name == "ms-sql-s" then
        Helper.DiscoverByTcp(host, port)
      end
      port = nmap.get_ports(host, port, "tcp", "open")
    end

    -- smb.get_port() will return nil if no SMB port was scanned OR if SMB ports were scanned but none was open
    if smb.get_port(host) then
      Helper.DiscoverBySmb( host )
    end

    -- if the user has specified ports, we'll check those too
    if ( targetInstancePorts ) then
      for _, portNumber in ipairs( targetInstancePorts ) do
        portNumber = tonumber( portNumber )
        Helper.DiscoverByTcp( host, {number = portNumber, protocol = "tcp"} )
      end
    end

    nmap.registry.mssql.discovery_performed[ host.ip ] = true
    mutex( "done" )
  end,

  --- Returns all of the credentials available for the target instance,
  --  including any set by the <code>mssql.username</code> and <code>mssql.password</code>
  --  script arguments.
  --
  --  @param instanceInfo A SqlServerInstanceInfo object for the target instance
  --  @return A table of usernames mapped to passwords (i.e. <code>creds[ username ] = password</code>)
  GetLoginCredentials_All = function( instanceInfo )
    local credentials = instanceInfo.credentials or {}
    local credsExist = false
    for _, _ in pairs( credentials ) do
      credsExist = true
      break
    end
    if ( not credsExist ) then credentials = nil end

    if ( stdnse.get_script_args( "mssql.username" ) ) then
      credentials = credentials or {}
      local usernameArg = stdnse.get_script_args( "mssql.username" )
      local passwordArg = stdnse.get_script_args( "mssql.password" ) or ""
      credentials[ usernameArg ] = passwordArg
    end

    return credentials
  end,

  ---  Returns a username-password set according to the following rules of
  --  precedence:
  --
  --  * If the <code>mssql.username</code> and <code>mssql.password</code>
  --    script arguments were set, their values are used. (If the username
  --    argument was specified without the password argument, a blank
  --    password is used.)
  --  * If the password for the "sa" account has been discovered (e.g. by the
  --    <code>ms-sql-empty-password</code> or <code>ms-sql-brute</code>
  --    scripts), these credentials are used.
  --  * If other credentials have been discovered, the first of these in the
  --    table are used.
  --  * Otherwise, nil is returned.
  --
  --  @param instanceInfo A SqlServerInstanceInfo object for the target instance
  --  @return (username, password)
  GetLoginCredentials = function( instanceInfo )

    -- First preference goes to any user-specified credentials
    local username = stdnse.get_script_args( "mssql.username" )
    local password = stdnse.get_script_args( "mssql.password" ) or ""

    -- Otherwise, use any valid credentials that have been discovered (e.g. by ms-sql-brute)
    if ( not(username) and instanceInfo.credentials ) then
      -- Second preference goes to the "sa" account
      if ( instanceInfo.credentials.sa ) then
        username = "sa"
        password = instanceInfo.credentials.sa
      else
        -- ok were stuck with some n00b account, just get the first one
        for user, pass in pairs( instanceInfo.credentials ) do
          username = user
          password = pass
          break
        end
      end
    end

    return username, password
  end,

  --- Disconnects from the SQL Server
  --
  -- @return status true on success, false on failure
  -- @return result containing error message on failure
  Disconnect = function( self )
    if ( not(self.stream) ) then
      return false, "Not connected to server"
    end

    self.stream:Disconnect()
    self.stream = nil

    return true
  end,

  --- Authenticates to SQL Server.
  --
  -- If login fails, one of the following error messages will be returned:
  --  * "Password is expired"
  --  * "Must change password at next logon"
  --  * "Account is locked out"
  --  * "Login Failed"
  --
  -- @param username string containing the username for authentication
  -- @param password string containing the password for authentication
  -- @param database string containing the database to access
  -- @param servername string containing the name or ip of the remote server
  -- @return status true on success, false on failure
  -- @return result containing error message on failure
  -- @return errorDetail nil or a <code>LoginErrorType</code> value, if available
  Login = function( self, username, password, database, servername )
    local loginPacket = LoginPacket:new()
    local status, result, data, errorDetail, token
    local servername = servername or "DUMMY"
    local pos = 1
    local ntlmAuth = false

    if ( not self.stream ) then
      return false, "Not connected to server"
    end

    loginPacket:SetUsername(username)
    loginPacket:SetPassword(password)
    loginPacket:SetDatabase(database)
    loginPacket:SetServer(servername)

    local domain = stdnse.get_script_args("mssql.domain")
    if (domain) then
      if ( not(HAVE_SSL) ) then return false, "mssql: OpenSSL not present" end
      ntlmAuth = true
      -- if the domain was specified without an argument, set a default domain of "."
      if (domain == 1 or domain == true ) then
        domain = "."
      end
      loginPacket:SetDomain(domain)
    end

    status, result = self.stream:Send( loginPacket:ToString() )
    if ( not(status) ) then
      return false, result
    end

    status, data, errorDetail = self.stream:Receive()
    if ( not(status) ) then
      -- When logging in via named pipes, SQL Server will sometimes
      -- disconnect the pipe if the login attempt failed (this only seems
      -- to happen with non-"sa") accounts. At this point, having
      -- successfully connected and sent a message, we can be reasonably
      -- comfortable that a disconnected pipe indicates a failed login.
      if ( errorDetail == "NT_STATUS_PIPE_DISCONNECTED" ) then
        return false, "Bad username or password", LoginErrorType.InvalidUsernameOrPassword
      end
      return false, data
    end

    local doNTLM = ntlmAuth

    while( pos < data:len() ) do
      pos, token = Token.ParseToken( data, pos )
      if ( -1 == pos ) then
        return false, token
      end

      if ( token.type == TokenType.ErrorMessage ) then
        local errorMessageLookup = {
          [LoginErrorType.AccountLockedOut] = "Account is locked out",
          [LoginErrorType.NotAssociatedWithTrustedConnection] = "User is not associated with a trusted connection (instance may allow Windows authentication only)",
          [LoginErrorType.InvalidUsernameOrPassword] = "Bad username or password",
          [LoginErrorType.PasswordExpired] = "Password is expired",
          [LoginErrorType.PasswordMustChange] = "Must change password at next logon",
        }
        local errorMessage = errorMessageLookup[ token.errno ] or string.format( "Login Failed (%s)", tostring(token.errno) )

        return false, errorMessage, token.errno
      elseif ( token.type == TokenType.LoginAcknowledgement ) then
        return true, "Login Success"
      elseif doNTLM and token.type == TokenType.NTLMSSP_CHALLENGE then
        local authpacket = NTAuthenticationPacket:new( username, password, domain, token.nonce )
        status, result = self.stream:Send( authpacket:ToString() )
        status, data = self.stream:Receive()
        if not status then
          return false, data
        end
        doNTLM = false -- don't try again.
      else
        local found, ttype = tableaux.contains(TokenType, token.type)
        if found then
          stdnse.debug2("Unexpected token type: %s", ttype)
        else
          stdnse.debug2("Unknown token type: 0x%02x", token.type)
        end
      end
    end

    return false, "Failed to process login response"
  end,

  --- Authenticates to SQL Server, using the credentials returned by
  --  Helper.GetLoginCredentials().
  --
  --  If the login is rejected by the server, the error code will be returned,
  --  as a number in the form of a <code>mssql.LoginErrorType</code> (for which
  --  error messages can be looked up in <code>mssql.LoginErrorMessage</code>).
  --
  -- @param instanceInfo a SqlServerInstanceInfo object for the instance to log into
  -- @param database string containing the database to access
  -- @param servername string containing the name or ip of the remote server
  -- @return status true on success, false on failure
  -- @return result containing error code or error message
  LoginEx = function( self, instanceInfo, database, servername )
    local servername = servername or instanceInfo.host.ip
    local username, password = Helper.GetLoginCredentials( instanceInfo )
    if ( not username ) then
      return false, "No login credentials"
    end

    return self:Login( username, password, database, servername )
  end,

  --- Performs a SQL query and parses the response
  --
  -- @param query string containing the SQL query
  -- @return status true on success, false on failure
  -- @return table containing a table of columns for each row
  --         or error message on failure
  Query = function( self, query )

    local queryPacket = QueryPacket:new()
    local status, result, data, token, colinfo, rows
    local pos = 1

    if ( nil == self.stream ) then
      return false, "Not connected to server"
    end

    queryPacket:SetQuery( query )
    status, result = self.stream:Send( queryPacket:ToString() )
    if ( not(status) ) then
      return false, result
    end

    status, data = self.stream:Receive()
    if ( not(status) ) then
      return false, data
    end

    -- Iterate over tokens until we get to a rowtag
    while( pos < data:len() ) do
      local rowtag = string.unpack("B", data, pos)

      if ( rowtag == TokenType.Row ) then
        break
      end

      pos, token = Token.ParseToken( data, pos )
      if ( -1 == pos ) then
        return false, token
      end
      if ( token.type == TokenType.ErrorMessage ) then
        return false, token.error
      elseif ( token.type == TokenType.TDS7Results ) then
        colinfo = token.colinfo
      end
    end


    rows = {}

    while(true) do
      local rowtag
      rowtag, pos = string.unpack("B", data, pos )

      if ( rowtag ~= TokenType.Row ) then
        break
      end

      if ( rowtag == TokenType.Row and colinfo and #colinfo > 0 ) then
        local columns = {}

        for i=1, #colinfo do
          local val

          if ( ColumnData.Parse[colinfo[i].type] ) then
            if not ( colinfo[i].type == 106 or colinfo[i].type == 108) then
              pos, val = ColumnData.Parse[colinfo[i].type](data, pos)
            else
              -- decimal / numeric types need precision and scale passed.
              pos, val = ColumnData.Parse[colinfo[i].type]( colinfo[i].precision,  colinfo[i].scale, data, pos)
            end

            if ( -1 == pos ) then
              return false, val
            end
            table.insert(columns, val)
          else
            return false, ("unknown datatype=0x%X"):format(colinfo[i].type)
          end
        end
        table.insert(rows, columns)
      end
    end

    result = {}
    result.rows = rows
    result.colinfo = colinfo

    return true, result
  end,

  --- Attempts to connect to a SQL Server instance listening on a TCP port in
  --  order to determine the version of the SSNetLib DLL, which is an
  --  authoritative version number for the SQL Server instance itself.
  --
  -- @param instanceInfo An instance of SqlServerInstanceInfo
  -- @return status true on success, false on failure
  -- @return versionInfo an instance of mssql.SqlServerVersionInfo, or nil
  GetInstanceVersion = function( instanceInfo )

    if ( not instanceInfo.host or not (instanceInfo:HasNetworkProtocols()) ) then return false, nil end

    local status, response, version
    local tdsStream = TDSStream:new()

    status, response = tdsStream:ConnectEx( instanceInfo )

    if ( not status ) then
      stdnse.debug2("%s: Connection to %s failed: %s", "MSSQL", instanceInfo:GetName(), response or "" )
      return false, "Connect failed"
    end

    local preLoginRequest = PreLoginPacket:new()
    preLoginRequest:SetInstanceName( instanceInfo.instanceName )

    tdsStream:SetTimeout( 5000 )
    tdsStream:Send( preLoginRequest:ToBytes() )

    -- read in any response we might get
    status, response = tdsStream:Receive()
    tdsStream:Disconnect()

    if status then
      local preLoginResponse
      status, preLoginResponse = PreLoginPacket.FromBytes( response )
      if status then
        version = preLoginResponse.versionInfo
      else
        stdnse.debug2("%s: Parsing of pre-login packet from %s failed: %s",
          "MSSQL", instanceInfo:GetName(), preLoginResponse or "" )
        return false, "Parsing failed"
      end
    else
      stdnse.debug2("%s: Receive for %s failed: %s", "MSSQL", instanceInfo:GetName(), response or "" )
      return false, "Receive failed"
    end

    return status, version
  end,

  --- Gets a table containing SqlServerInstanceInfo objects for the instances
  --  that should be run against, based on the script-args (e.g. <code>mssql.instance</code>)
  --
  --  @param host Host table as received by the script action function
  --  @param port (Optional) Port table as received by the script action function
  --  @return status True on success, false on failure
  --  @return instances If status is true, this will be a table with one or
  --    more SqlServerInstanceInfo objects. If status is false, this will be
  --    an error message.
  GetTargetInstances = function( host, port )
    -- Perform discovery. This won't do anything if it's already been done.
    -- It's important because otherwise we might miss some ports when not using -sV
    Helper.Discover( host )

    if ( port ) then
      local status, instances = Helper.GetDiscoveredInstances(host, port)
      if status then
        return true, instances
      else
        return false, "No SQL Server instance detected on this port"
      end
    else
      if ( targetAllInstances and ( targetInstanceNames or targetInstancePorts ) ) then
        return false, "All instances cannot be specified together with an instance name or port."
      end

      if ( not (targetInstanceNames or targetInstancePorts or targetAllInstances) ) then
        return false, "No instance(s) specified."
      end

      local instanceList = Helper.GetDiscoveredInstances( host )
      if ( not instanceList ) then
        return false, "No instances found on target host"
      end

      local targetInstances = {}

      for _, instance in ipairs( instanceList ) do
        repeat -- just so we can use break
          if instance.port then
            local scanport = nmap.get_port_state(host, instance.port)
            -- If scanned-ports-only and it's on a non-scanned port
            if (SCANNED_PORTS_ONLY and not scanport)
              -- or if a portrule script will run on it
              or (scanport and scanport.state == "open") then
              break -- not interested
            end
            -- If they want everything
            if targetAllInstances or
              -- or if it's in the instance-port arg
              (targetInstancePorts and
                tableaux.contains(targetInstancePorts, instance.port.number)) then
              -- keep it and move on
              targetInstances[#targetInstances+1] = instance
              break
            end
          end
          -- If they want everything
          if targetAllInstances or
            -- or if it's in the instance-name arg
            (instance.instanceName and targetInstanceNames and
              tableaux.contains(targetInstanceNames, string.upper(instance.instanceName))) then
            --keep it and move on
            targetInstances[#targetInstances+1] = instance
            break
          end
        until false
      end

      if ( #targetInstances > 0 ) then
        return true, targetInstances
      else
        return false, "Specified instance(s) not found on target host"
      end
    end
  end,

  --- Queries the SQL Browser service for the DAC port of the specified instance
  --
  --  The DAC (Dedicated Admin Connection) port allows DBA's to connect to
  --  the database when normal connection attempts fail, for example, when
  --  the server is hanging, out of memory or other bad states.
  --
  --  @param instance the <code>SqlServerInstanceInfo</code> object to probe for a DAC port
  --  @return number containing the DAC port on success or nil on failure
  DiscoverDACPort = function(instance)
    local instanceName = instance.instanceName or instance.pipeName
    if not instanceName then
      return nil
    end
    local socket = nmap.new_socket("udp")
    socket:set_timeout(5000)

    if ( not(socket:connect(instance.host, 1434, "udp")) ) then
      return false, "Failed to connect to sqlbrowser service"
    end

    if ( not(socket:send(string.pack("c2z", "\x0F\x01", instanceName))) ) then
      socket:close()
      return false, "Failed to send request to sqlbrowser service"
    end

    local status, data = socket:receive_buf(match.numbytes(6), true)
    socket:close()
    if ( not(status) ) then
      return nil
    end

    if ( #data < 6 ) then
      return nil
    end
    return string.unpack("<I2", data, 5)
  end,

  ---  Returns an action, portrule, and hostrule for standard SQL Server scripts
  --
  -- The action function performs discovery if necessary and dispatches the
  -- process_instance function on all discovered instances.
  --
  -- The portrule returns true if the port has been identified as "ms-sql-s" or
  -- discovery has found an instance on that port.
  --
  -- The hostrule returns true if any of the <code>mssql.instance-*</code>
  -- script-args has been set and either a matching instance exists or
  -- discovery has not yet been done.
  -- @usage action, portrule, hostrule = mssql.Helper.InitScript(do_something)
  --
  -- @param process_instance A function that takes a single parameter, a
  --                         <code>SqlServerInstanceInfo</code> object, and
  --                         returns output suitable for an action function to
  --                         return.
  --
  -- @return An action function
  -- @return A portrule function
  -- @return A hostrule function
  InitScript = function(process_instance)
    local action = function(host, port)
      local status, instances = Helper.GetTargetInstances(host, port)
      if not status then
        stdnse.debug1("GetTargetInstances: %s", instances)
        return nil
      end
      local output = {}
      for _, instance in ipairs(instances) do
        output[instance:GetName()] = process_instance(instance)
      end
      if #output > 0 then
        return outlib.sorted_by_key(output)
      end
      return nil
    end

    -- GetTargetInstances does the right thing depending on whether port is
    -- provided, which corresponds to portrule vs hostrule.
    return action, Helper.GetTargetInstances, Helper.GetTargetInstances
  end,
}

local TDS7Crypt_enc = function (cp)
      local c = cp ~ 0x5a5a
      local m1= ( c >> 4 ) & 0x0F0F
      local m2= ( c << 4 ) & 0xF0F0
      return string.pack("<I2", m1 | m2 )
end

Auth = {

  --- Encrypts a password using the TDS7 *ultra secure* XOR encryption
  --
  -- @param password string containing the password to encrypt
  -- @param decoder a unicode.lua decoder function to convert password to code points
  -- @return string containing the encrypted password
  TDS7CryptPass = function(password, decoder)
    return unicode.transcode(password, decoder, TDS7Crypt_enc)
  end,

  LmResponse = function( password, nonce )

    if ( not(HAVE_SSL) ) then
      stdnse.debug1("ERROR: Nmap is missing OpenSSL")
      return
    end

    password = password .. string.rep('\0', 14 - #password)

    password = password:upper()

    -- Take the first and second half of the password (note that if it's longer than 14 characters, it's truncated)
    local str1 = string.sub(password, 1, 7)
    local str2 = string.sub(password, 8, 14)

    -- Generate the keys
    local key1 = openssl.DES_string_to_key(str1)
    local key2 = openssl.DES_string_to_key(str2)

    local result = openssl.encrypt("DES", key1, nil, nonce) .. openssl.encrypt("DES", key2, nil, nonce)

    result = result .. string.rep('\0', 21 - #result)

    str1 = string.sub(result, 1, 7)
    str2 = string.sub(result, 8, 14)
    local str3 = string.sub(result, 15, 21)

    key1 = openssl.DES_string_to_key(str1)
    key2 = openssl.DES_string_to_key(str2)
    local key3 = openssl.DES_string_to_key(str3)

    result = openssl.encrypt("DES", key1, nil, nonce) .. openssl.encrypt("DES", key2, nil, nonce) .. openssl.encrypt("DES", key3, nil, nonce)
    return result
  end,

  NtlmResponse = function( password, nonce )
    local lm_response, ntlm_response, mac_key = smbauth.get_password_response(nil,
      nil,
      nil,
      password,
      nil,
      "v1",
      nonce,
      false
    )
    return ntlm_response
  end,
}

--- "static" Utility class containing mostly conversion functions
Util =
{
  --- Takes a table as returned by Query and does some fancy formatting
  --  better suitable for <code>stdnse.format_output</code>
  --
  -- @param tbl as received by <code>Helper.Query</code>
  -- @param with_headers boolean true if output should contain column headers
  -- @return table suitable for <code>stdnse.format_output</code>
  FormatOutputTable = function ( tbl, with_headers )
    local new_tbl = {}
    local col_names = {}

    if ( not(tbl) ) then
      return
    end

    if ( with_headers and tbl.rows and #tbl.rows > 0 ) then
      local headers
      for k, v in pairs( tbl.colinfo ) do
        table.insert( col_names, v.text)
      end
      headers = table.concat(col_names, "\t")
      table.insert( new_tbl, headers)
      headers = headers:gsub("[^%s]", "=")
      table.insert( new_tbl, headers )
    end

    for _, v in ipairs( tbl.rows ) do
      table.insert( new_tbl, table.concat(v, "\t") )
    end

    return new_tbl
  end,
}

local unittest = require "unittest"
if not unittest.testing() then
  return _ENV
end

local tests = {
  {"host", "\x23\xa5\x53\xa5\x92\xa5\xe2\xa5", unicode.utf8_dec},
  {"p@ssword12-", "\xa2\xa5\xa1\xa5\x92\xa5\x92\xa5\xd2\xa5\x53\xa5\x82\xa5\xe3\xa5\xb6\xa5\x86\xa5\x77\xa5", unicode.utf8_dec},
}
test_suite = unittest.TestSuite:new()

for _, test in ipairs(tests) do
  test_suite:add_test(unittest.equal(Auth.TDS7CryptPass(test[1], test[3]), test[2]), ("TDS7 crypt %s"):format(test[1]))
end

return _ENV
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   wOFF    ~ 
                         FFTM  0      kGGDEF  L        OS/2  l   >   `2z@cmap    i  
:gasp         glyf    _y LMhead b   3   6-hhea b      $
hmtx b    
Eyloca e    \maxp l       ,name m  D  㗋post o`    u       =    O<0    1hxc`d``b	`b`d`d:$Y<   xc`fdbʢbl|6F0#F n  x͒Jqgje>"D>{EO >,"u^[[[jos_M%:0g80B.Lszðפ 1YlKWvest)Mk^Zֵ֪m׉Θbk̳26>'YҖjukZۺgm2 (4-iEkЖv}XBY``c9ZJV5eY߆6G΂`3|6[uIpn-[pL0Lp;׸%8o>F8	G8`Wί"E^_=(K,FK+yb       x	TՕ0o}{uuuwUWիnnjmz-nvEEAAJ!*(hD2c%FʦEbb6$&7߹UUW7
tw{98m8bI	ڃ݌7SEG!3j㔐=w;P^IA;RRnkLS.)o8G([)9O,,AtS
h
yujZupPGxNon{ho2AD-r]u5e^dMX8=r5ͻ^Q\~2V0 o0kC qA跍G<
9 v`|NXWI:"'aW޺O=}k#"7e	%Vs~-y$ŵXw&'q.n.EK#JDڝn봽7=|wL:Ӎ2vmrRv:=0P@DۓVZ7eOd7HMSY|[of'BL}ƷҗV^+{W=uҤ֦='j,|;vAo=0q8"I³8yZ6Ǵo9q<i3k1%&
uk{H}@΁W^qԷ4;gg7Ny/
qP O ЌL4q,ԇ"Sv=jL/UjC-woȍnj̮{j\
vEk

z>pn=^=ajID(෠quF;э5֮s7;QC7U[׈yZIۘػ*!$	dⵄŖ-ˇ?{mf6po~mԽwoG6Moza--m#]?]?Vkzܥܵ.>)9NH%&T/ _IAxOB]8(.v)G=HPSUP>fFE-GGs|'?~zI*R|[`-V'ݙGP3b'\RI̞#n;WٟDTѹb80^s6,rȥism15kk,}qWȝ;t seYqqC/0 q|>
3W/ըsF"sIoAHI 8Cw~@
_(]h=r9p!;H-[Ifw;%=d꯵bmH)k=o\hEi7i:-!mn:`[G]GE,;syH62ƈs՗:I@^\wOVõ<g?]Y{?qKgH[X &tdn[,Z!H6#=nݳ;OWUG4]]6ٰp7[aM5PB]?4P呂7o\!׺ߜؤ
2>8/p2h@k~ھB~a[r=Pr8SescF
ӗ S#P|0z'zS)8aFBFE	VrJ(EfDpU\'h4P 	j<t$>d3}CvfM}Zlf,.pj1tYj2lƗ,U<:zt[%Y!1vMfrc:_n"7zwvm
zuidtO.3K<yd03lLl؞Yĭ~bg#8H7JC*gY_YKin0AQPiMg-c)<9ܹJHX-owaX; <z̳@)*rw|u`lc߸m1:H2yΡؕdYנE+G ZQ
kP*.6O W=nuBdu8<74~c8(bK]4x~*x=¿1T2Gߡ}S}JXùP@z${P"h^bؙJr`R_3@|8~ v:GE8ci]5&4tَSצ	#5jQ 0즰N`v!
RyS(v
]wB}J]>u=.#Cjn(,THu_Z	6qhhP4#JH%jt3M)#zzdt1Dn~9/ȋB@NV?p'rf:;bBQHb$h3CG|#v2ydm)esvw~٬fp~DG	r0^XzˣՇcl&
 \`\8HHa IC?6:5H;lވ4C&\FjԬ,|MCݔ/f8ܮ2	.ҍl
_/AkTVΝg~T΂<`2Q&;XAW@@gj{j,	suuE
֟:A
8,&ռ
}|b0lFQ$px=4ddm7nru"N:Ou^x@񝂍CG*%F>Tm?2.opˮ1r\T١K+L؜cn:8qyN\Dvj[ܦDy/*=H	[0l8=`Dd&<qR}~|m?9[Y
{HIFPHp;@Y[D]j}*ÞhJԆ'v^6XDLVa@XFk<N.pVeup+O;F G\Eнbkfy
zs

	XkM֊PY_g#f}{ Lh.tMV((/4uX4u<k%Ņs=xfȌݐP(.(q\+i}J/[Ok<Ew{W%҂pRJ
˙$["H6#] FC֫C_c|=F2[#\eyÃ.anơzK9řeNԞeUտxUwΫm>76tOd٧,崅v2+׷ TU[NHN8W|fG{ܘlT_Z1 8j
`Ar㼌`h*b
#ռBj0s$n^7w$Gɡ;N
.A>3;My?zpͥΙ4aqp҃GFw|]֯ !ؾbvq8e+)h.,U~4]h.P4s)+kqD2uϸuE3V⭯ҟfS8/D]5ޖ*xWGj}l&klnçiPv'6#(%)>qEo6U+6ŋ8ۢlޏ>`Mn''zB-t/ꬱ3ik3
55Z	1ao|+
őm
0$YəOa1ag9up9Gת+b=H߀Q1hT]ҒQ^?s9ػ  lB|4TNYBL,
g#5A㉐=!7~=/X]WuwZW避[ꞞWd==Bm®ҏ΋v?$
E#
L!7ط!TRRI4)H#l*:#H.)pӇ
źRMB=ƅ(ǂ͵˥>A,_2%5pyn6/Mbt,L֮l+9QGb]*D;
{PZ!*U1|s{"3\gGχyG:-nQg7`ԏ3xAx%ÏUXMZ&HX9>osGa
'!lü|EW-ebbxsY06E>)VH߰}V=G~Ykh/;ۇ0{4. c\h`5
FA5Tg[4#So3yuy=<'j{	hNk6	@1c/5-T:`YX]g~ilp!e>1x06?eoAsb̪fyb3@B߂Yq?;m)h4skPUfW62c>8F(t*GC	ym
srp?ICY:ϻ&͜99TY-k%)@|FFh9*(RtKǻTXM-IP.%C"?,+ˆ=	>tUgQWw#Υ7݋[P	ޮ'j77̗9ZI
SO4YkDE͂B~`Ig;mu֢zSg)rE܉=mK9ZD]4~7߉R6Hۂ(ji! BldpӜ^zz拾gF:qꢝkWl/СuX2rTsBנͫڂt}}ƶ_5	k4	A;oHLϹ)z.quAzyxjk5F-@lҙcڗҗ\6=
O]9/5ڔ볝\tOCT3f(i
]wPiQwγ=JߌvGޮy[[,Et&QocÂyb66kMK|֋$Yz%P(^87DrK`%5.:	Ďx=mnًm]Ю&2G(-@Q7xu3%@p~нt S]=)AGAVg;*=$mz
-|_EZˢk<5U5fFIj`=H})0~F,"N6k"}ṒkT"$mZPc',ϛtzՅ];+j+NG>K#h-zp6\;yb~9.m	\=qrqü=fS
6u(؍3#0
:Nz{S M]"`R.Cr`-U{낍znq	tx
ic+Ԛ:3Y㳙N*aVP
`1Qb@fc^X9̼ܶjtҜYӂhھ3	ijs+\8Tvi|Q<
v߹c81-t\16GInJ:̇hXGr+<O|alyxuco7狿P'j{G wsʥs
??kL5>4Hjv4l!,cC54{ٱ4dR~p*;9nC%d}dA4Q8iOi	TgdulUSAq$.j6U;MǶۏێۏj9JDvAFbm LOI=`jf:>IǁJ!
6Txưqn̓S9ĀM|!ґ8X)hͅͳ( ,ӌ2+lD3Qɕp$`Pt[ DV2opo%xZ)n:p4N)F ՆtT7Mu`8P*r
>(O^tXi(M4!
t(>hcU<@ܦç$M'(J׳Q܃<8Vjj7P?Ͼ;_!Q.h|:B)Ӓ xܘs_d9aN=.WO.\|_O&tk.".Dp53͓	6`8IuKjk/wiUSusUlr
̥;ѠMe`TB&n¦\	g2pd[0OvzI'm%4 1}@:įZ/ r
@1m8_.WRlv(F5Aս~]*@Qؿ
VgM܊:MʞQZ㖵.
HfJwKIA\f7zl}5VzGƐ
u̻vߋaɰZ(S6Wz7ek[j#6[6iSڣn@d`[}i]<{bN&kG[Q`Ek$|'GOR4:	yX1dhz3TʷL-3DG%Z
b锥3I陌R^cy,3P!@ ieNq좀FS'}@4шÏ~* T(PY+=!?}>Ю+w*3Usƽ i[9a\uWeY5	+,iK\ʚe<zKC&Hdbktݩ7<Gh
fOfp+d<8YX(ϴ s>!;BTR@J	vKU8bUH^Q;Okb%[QHO9谉0r0}U>ʔV5^ܵ }ecFmۈrqLEl	"I5ڦfU2cW+O,
MJ񝁧6y?*0&Nݚxq?)>e(	@qTVx>sjAi2W@W<KP
+
i
4(ا	
xA̓	1Jz'O?<L0,;V|'[9;j:[BخRknC.иiޱTݝ&[h5V,RIN{oF|Tn_|QW>U{LГK^A'96&E[h8J*X>wyW+Vc*YP!3
^%"`ɒRcD@2ܵG5gL6}*Xl틵\"*p9B4MzA65L.2k,0^>G@@HtyZ4iepWtAh,8<{9ȽǷƶwZOYE<Z)t#/崐\F7ʔB>(&6ldi t/=n>?& s]@Ν0Z.3Ĥ9MG6XIJHXa:C}36~>D3UO>[vZ_}סqN!ʃ
-W 
SHa)Y'lg8=`z(bwvi:2E!`;x,Y ߩ
=Іj^ǻQ^_Yy`Q[&aYQ
us0{&m胑*j)TC$YQ>*P}H˥_7!n?Vا(sOGRBXbG/*󨴉bE("lrʔ$ΫdJwGp6
P/#jmtCR0}Bj̣RXvI>(j=:ECtV:O[h[5"uE3W.
f[eܫ8P)e
0Rԁd.ُ:~}t<)/Q
cOBGGp<"-G-b΢y3b#5RPCk{d˚ ح6d]LdLu鋶
LCzӮIYs;A@*nyڢKˏɩEWeMâx[*u-zҗrizH>2$=_j7{!h7Ύ|pfs%9LAQ,2WH(EEug&/
$̃cm$0^(K_C]Di+/TRhOJ?Nޛ j;쁳#ISm0Q4WՏ5_fd "0ԏ ~D}R'k GK1(_/TFȤ8>Q 8m.mstÁ-`wZaxx";ͯ2o2:h*4X-hW3snP,ɞ
"ޗ`7Nw8ɐD\	(,f鄝	IM|؟նkÿl5nv xL/LM}ݻ/Еum.umd>Nh&kԵ-h#
+qs}v.L8c|P=/2,T,\fxP!:*}uLvyj{C [	^܋lV͛CZk9~_+2_ʗ7%\~NVw|:$^fH-l6[DniD>=}4b=U{xCu:6ݨ18=Z%ܓ&?i*V߻"z,K=,5keb PÒ}aM)dŐ".Aǝ2AnK%
%7 ;QΤx9:J's9 :(w̿sltWN~+lAڏm[w77n\W<9-N߹ti?";iw[;LvP2zrgkcl;#E*b8*<~h!:Q@qӼek/#@wꪫ'	r*2_2mppm"Oގ:wFgRۜ{zh?U_3m3ؾ)[_./d
jG̨.+{7g|6w6؟>d5;{O"-<+jaW22pWagy6&BhI2%1S*[ϤF۷%nwT	QĶ!=00!dP$Oj!%l6bd [6,6`^Hfɖ3V߶[8|\MQ
lƜYxj?KO3ٲ%))JrGƼQ̼)2c"^;@Y5u!'hVGTi M9#(ן<4s{@efQ`Gy
8L"KB3+fOx_c`=C@d-TOj+Jw]f1򉠦J -L[,Əvu&}z)AԫyzX߶"MWwP-蒺Mrk
44LZvɎiZcKU/Nja,a!"Y<]K-{S&,-l5V(DSJZU+6UԤ)jȀMXju5xkOxkCf>v;oĂu)O[H<t_X4i+*dԒx7)lO=R|Oh\ؼERD*c R?ʇ﫯"bL+nwSBIZ^ģ|r#ReA>%rJrZNCQn?|x_B*kgYn3:B4WͤuQ.RMF2>8G3J<ZrVŗY~P9w;<
+iչ+5DDhp,;ʹjfƼ=䵫9 3Ƒ,@('h:Ƌ&mTkPq8󨴱!ä.#Q{==4V#mx	_)IfC#yFN
uQRPQyQ
u:]g*O<j,0?g`ON\Z\FkrIݝJ%QM	$%G/-S_hzt>U֧c'P fՅԭںo>x,uP^"yXdci+Y_'z6~(+q$U;{S<^xGn}ouvXt%&3`.:gA'%O0j@Ew:мjdqge<TCB=nҗCq+d)ӪLZ&ίYْbvsmk'mxl0k"ȓU\{ӲYzY.TYt|"cK:6.4LSzD&DLJa|+Qh_}eΞz_
b"	P8^Џ>4c&ūY3]*tI*
r6% &A R^3$p,a2GÇ}O>W476Ոn7[YNqOecu/=cm:&4Co<}iAO6ăNYm:̲f3J"MK:Ek:e-O7
6;kh}x?1/\g^y}7|4q'7o^ o.Uξ& d5v 3_P MpĹVjlU	a^vqǹ܈\?虽쪰:Oob2AL29zXvQ VUq^k%@$Ǡ#o}TscFW}$yF$y^2:l4/maԽ&oL3ѤNIq!#ĺ~N>0=ٞbDAw	OhCTѡ
֩FI.M[V#Œ3ze{EvceR]
ecsERn`{ahZ]'3W0vIxV[mQ8f64Sc%WrF.aR6aLv0n=,L	ZBU\ ]aJXL7e銛
ǉQƀcHj\}MGޛ
[X@"WdNS<+#(;<"w~omyL'DpEbY?~{{,o,RD(JbC>ܶ_dՇwffsܦk3ގ&~L	=$&Cyd"le؄ tQRʉ@*΋7JՄpC#5-Vgo!Gi
4&NpOo޴խ9k'y=JS4/;٬vY3MiB<
(Yuv<9_m@|zU
_<';^;#b})Kywno%6,i7-+v(k6ic"Ym=t#WRTmR[na<j
X)GVX,gB&blц*ϸ"^(^Bk(tǒD>fʭklW޼(IdrUU5=^Dfj}-:$rp(<MzMƯ:|%7L>%\x+>wW؄	Ou
gq/,W:˺/Ɏ+y+&Lo)	@[@exbiu;:Ykw[50x:rsS&_Xxf[bT:7ak}Yx<5r'(>q-proɴ2HU&I-Kmhɠ\YFY`|fM0]63Bw5%#'iH(8 [*k.Etc&aNmVJQKTMbX4?#4c왓Q,<v5?J	[Js'ڛiӒӇC>䶵hMz__m27b2HC'
 j
,JN؋
LuqMZW7'./^L^DL%S	n4:OW^of߷Rпlq{\PȖ叙y4*xBav kx@͗qY.3HQF|:rƔ9`P_SRL
6b|jAn~<DN"u0Q\ Wuާfn6oH玤N	N'S;)̓vGvejOXJUPsps<׷4}am}SjTYCheubm20~t'r3:_H7M笜YrN:1!-z\MaP}l &pq6*_UYIG~O_KU8FT{t(av"CBf_F;QnqӳB$MU*rg,^G D,IH:7FD	Jlk6c']u;&FbFiB"&͙MykUP\M]J~qZ JP$5K?1/,#	K:I)DoY:Mg!'S$M }ÊN~$Ū3wm6]r׊sO^
ll
6H{RvBoLg(iZhVd˂]w!r<3H/7CyYN9Y@LceY֖Y$rz2dk`8v1gI1"0k~,c$tyh2^/sv骩m{TUM~{WÏɿmkUٹ?΅s4a:ZDg;@Vם4`gلw]x/goLvw'vڟڔyK<+<f>Ǟ~NF=ΐ7.'hٖ}t)vSK4Yԉs]kWN-ЯK`~kR-^"9BF%`%5S'$^\o;NKM#_5y<C$(V*ޖZj/IVZetMk,xC_m{ۏ\ʶk@1R+ې.臬tиl=C;x|^c&a=w99pt袋71R1@e  a<3w6Lj(
~n0KM.
EaRIW1[S,9p'YPM>r֖
jKgMdn7Y
n NlݮmGYN̂09E&WKbK|ĸJﱵWr{ݷkQcZ\2R؛Oۡ_h]Ըy&܈V;~M/׭n߮>_[./m2A	qJ{>LM8Af]'vHTUOμŃ̚u\eAb~u:ynwݥIٸ$j[QV*b聇nEC*Z ɭEo?҃&k=t#=KTrfWQjJN^yٔQW/Oo^rrj;NM4I`0wϚ _ߜ!Iouz#3tzi
kjmfL'k
^9uDћVnǼ^߲rn_CSC "6Gi1#W0=p']@8z}Q/
F"̒&=lFwdF3v1FuDFYV'F`.bNu䡁 Vl|I׀ɷ*~)Z*!+uQvCM/vԂ.qcYs,wDiN6 YrL U߲[crcq5)V!c031;B0ތeG͝UaVNUe	(;;|d;_TA"?/}Mi	;]wt7WY㰛nNgh7EB7_RE=SxV5Psm`ržYazRat	k_F=dVٿgCj߇%T}[n.Z$Uq:ۛ*<ggnGh(U?.b=Ђ z3ek
4	v^QVJRT+N1EyD;YC+dNA݇n$9MAyhpJ=^蹭%[ҫ{\r8L^Rڠg8ޥ~ ad8U=gP'1.#l
=ΑѬzR6np~[EfnG+y|:fE˻~E׶Mʟ]f}jE3qMOϚ{d?]uU?#/;s~򹃫ؚǀK-6B'闘̵Lgcg&=G'
}S唩VCIsyRCM)rd7&UC͝w4Nsca7fl]tTwݵFè4ou֍2B>#o7(J~jE(EM-P<n}enpt^
^<5fͬ>3/rQQ@Wヌ(QUm)!sG7ꜜZ4	Ulڟpd:Cce's2E;u*'$]"c4}
vzyDzɨn4bTF.b4R#P*~6tjtŋdۥy1W!ןD}glْW_A4R/u|]P	Ǯ~:t[94{-.ǀyA0 x6-NMvM$c50ghQ6 1BnW_us;BEg}\"\aQ=#ͧվv1ŊSY(R.i[9JdQӜ<0@BNya)j0Vh2쬄sO eP5>I~1! -A8agjNq^76e/쾇ݳRuԢZ&UEJlpYo<2"_:979f阎 .!
hI4
RkCjGBu+btQPu/А1TZ5V:+zp8jy\ST!zru8Y۸$ՅFuFYTj
+[kj`GŦ+yl֦Y닍4R,+h")=U>yV˕!V]Z8G_
jWpH ֬Q6P8=wQ9]W809{z$5p+҃D%ꔒ-R`5CbJihEI@xQ@-Jhnא!7#םY
ѣX2MnƔi&#ix2nB~#}2n)Ͱ.woB(Yk"5nG
PTF;NQ@(奣$%l7Q?lRPfB!wҤJƝaîGٍ J vKgWOӬL_$ta[!i&M>JLBfR%ۣ6!o"$,J{l2"Qo#BQ'!"#H:.	o	<9*a$
<1ʔ/-
᪠(J&$
f^oћ<on!AE
fl 5
H<o!ͭ(pNtH¼բ.a.&3!"I:LfsZ0A:A REEb"`\`qbѦӻEAlrZg0_
X0JX	Щ91"BN,bqH/bI2&06IMU%U	lI:DY%YKxíAЛtPG$Lx70lĤ'vluۏx!"Io#ENF.`EbUo˘'\y
~ّ$(dF nd3HazI+F#&zh$Ygu.Xlb%N[/*W8 BV0f^@`^y'/Tz:hM#$<EH,0Aoa,0(i"!1_E63;xMrXvuaQ2C_yY#"/輘XHp#9x1@1@ 
6	Z m݆Q D/.T;O |`1e7J:^G	:^&#aA$è
:dz +
a(~dD[iFXVX DXF'
:Cqsjӎpq2E5!K(KWk̙gط(hP+R^Q-O˻h@&lo%<xj/ޅG6R-V|lhtiL`xlhY9U~S㨆ӵ(`*J|u(xn\T"KL?
gKl/jP[ &crnl*oEŅu̬۝dUFW
5\1vC@4Pb|Me^I]%S!W}`*_U<(cnu/Xxhw
)k0T$׻Z3
^^v1eFg'PJAR#F,+EY@'CK_}B}~:@ŏݏ. g2K.LKZy,ߍ6:&5 Fsn-Ț\%۹Idn
[ɸ@i5]iv$tW3L\	C^\L>}6,+7
g2.;H\Ұf,-JǒEw\Bwjǎ>fM..klDj.Xv}mW\:5֔jKضV3BS$l&ijDYdIO~q!rW )\3
H.iT2R
˔D'i>- (*Qoc$`g#Aꆘ0ߨn7.>x;w,yc?Ơ 36I61q	($,ǋwܴtr(yh2l{s\p@5H?]JHʽ<lnh'1PmϣSo7i$½݇a͙~}Z}gP$6MhM_:~z{dZKe:s/޵bR+ʤm.F_-mAELǭs;;\激q9:L0hֳoȰhmS!SbfD"N ((YqG"Č;Ck%mDD͙mvKa5:p5<pFi͢=Oӛw4->gIhhh{ ef
zUs|+DWxst-}" <;p>#?X;$}upȖow/&ν'dޒM-3g֛떤$yIEuR
;5ItБf<n;u->b{ g-:6ާ>k0ڹQs.A,1xBU\tBBA=
)~3.{ҍPa~OBP:sQS=:Uf s1KɗM
@PsygQ')_@\l`|N16fpp3,Y,wZ1~טOnoy'ǗlfCW?Ot=Kz
(UQCdPn.<=y]Sd2KZu{d^&P^	qhEAakFQ7><~̈^=QbyAsX Gr9Aժ`	ΕMʆ돱,,)4KݑYZ?0Jd\;|h~ki?ev宰Kv2)i9Jcj~Uivo	V޴ʍX~eCkˆƆKڰZn߹ZXkon퀭:h7ΤG+Ș}I]Sfn"u!`*ئ(E3	MN4jnRXMGs/MtbRS{i+-v	aJu3Z/WS9ZK]>Ɵյ68N^~i>v$$&x;ό/nTu
_pdR7#ƌ]Kqk^:J1)Ǥ5$2
;ʗ$X[Z(ޜhJ7*%2E叙#zg{hLK,M#ǤOkdւnnVZĦپ[ȷkV%ʂ:@S>Զ}S~.vm[kl&żVLsHuvM[2/z9ն.S<#y\6 nGfmȬ@xʃEӻeiwXDv
[#:bL_hkm[-NٌEZ~emM%Y뛮% Zbth%:9}6xn.^%,uXF>.1^x oUQO7}\1B,53V̒ׄ'Ōzw67Oi6o_rUqp,1qOi#*n;6F(Ny'+ܣcTq<eLA"qe޲SqxLPQWQWyhBfM([vL#ۛ Q};Ε˒-$glYo+s8qNer:@Bp &*АBy	RZhMKy-Gۗ̮!>333~xh4[ A=,Oc⋢rx{+=.zfGA=SMϒk߉kѥ1|ug<Dk~>\==j=$rR3,xٰU`B!"LQ Jc@({˯F/43ibM6A
>A 0Z( 	zcdI
Q& Z+8LTW& aQ<a"*FS)1^T}uМ5`-q'6nh־ڻO׬%3<h%rܿe	:b
VYzlN]6p/oyiOc5xrM{>_ؾv5>9Xruʓ3r0rdet|¶Ld_*5hct,g}￼Wi\<csp=iv6l۽N8E߹ٿ}aq̈́s+Wߚ
DٶD^؉>[DPjq\j3th d[)7rhUW]jiK97
X|/>g],pK4YW_ځ/&-.S0+0:AH4bc7o|~۶FyWub^yV{1
o8S8#(緥~w޹jҢ6ĉ"h0PT	u) $`]+E:Eq؎W7jD-7(3uŲ{Ql`Y$OCoɊ= ;h>E3g^tPeNB* ʘ!x%	֙Y}IK %epH	ZR́H+!)ʵ	*	1B1ˬB`> &)ç	&
),~)|H}ؚ"odA[aO:)禓GwLr(yļCgQ#[UN84~c!yzݰҔZ3;zss.FMؾ1FSI`A	4QByE軼a "OiPSbnByḰXKG`SVЍC/|WM߫ʪkjv!:|uQ(UϜe׷]N#h<;vU{}fjH%X&?	Vu~V~j6A'MYvM!GP۹re紳 Dk/s)kq8vI8#x
G,c?;_?!syٯ3ηw>w`||tuP~IhhnE/&jy+ٸuTS6ooOoh-Np8ޗU2$u]v$0$
c߂ST6hBڭw.ci[ҙ-:g <F=*ǫT9 r%@+2u!tޮՒ2#ލnA7AYQȺUax(Ę[6b8{`.92q+vK$	2+p*~MrVs\IΤ_!j)pjf]_^șPG>*Khq{FA
lW?} 'MR~<3.([v<QHPCc
}Ibr`\~`8{;N\wYuI-U'Ny]9
Kp;+I^^V۳dv9!Ns߁_倻l1p~G pF#::ԅ[	H˯쀿":s-@w;1n3+U&97ϳJ:Wja3,)a> 'Tgx4JA]ԧ?21:yAc4Qd8`b4Dlu*l.]&' NY	?_EJOG#yn	^TA/UB
{dȎU}xX1r_i}~8b*=^]W*s->KdfgQU(s,ZeM\]2)1
$l!?OnG'o~P]h꙾V'E6Fo/q+Zjz*S`OƁ| MUa{o03g}(骪5J8+5OOWU$#+Z	J,2Yin>ŖXp'E!4l񺻜i	S(߁TR_ʠ̈́$^ŊMOwޯ,cӊф惞\I`T)&IX3W
Sv$Fݸ{e1fHțaw(Q \9u\Ox7NЍ%hۑ\WTT۪˻UmʂjrS-kU-nE*+g]4u,}뮻mfmsMX9UuuUNGQ>+UUG7O(YA!9ې#I%y\gf</
Z-HLHP&OEZ:3.&0B}H`n(.Y2,L~]Dax Q`2:6_u>6)+{?DC<Ukmb~c|T`ᾮ&
>E7"B1;/ ʤA$vBfYtجG_))P@	p7: z3hfa2
:v(^&m胍ɛ7Mi(&+;vv& 1S	{\ر%W[7mnYm}5qoqQˊc^nBq]dZCG6\i9I/`b}ޥ75!parHٰ)
|\n@s؇Ӂfs޿jZV+m#~xd	Iq|Y;$`kG^i[يFTX
*QlN+xDՑ-ML[J ϧ},i.F,2"BGщ0~IeOÖ[咛o}Ta>ľ/oz>E}ʋ`vz%5QlҥH++l6gSÔ|Bh8ڱt}C_Ꮐ֣*=d[M{WJfw.a44Do*VVA8sP-Ҟ}A"
@"Ȥt0+||E4NŁݓ1	9)*YѶQoP@	J2::b?2Hϴ3Y_nx[b¼Y1-Mҧi.#?<eng_+w,1?Q`tt@܁
w|3OQozi/#@ :ۨDl#ww
khiSyIM@$IgQC3I/IүRОc}>\!Бck3Fʷ׌8'חed($lٷYS  hC:Sli,ɯ䝂<d)r$SIbT^Kp+Vu	iA>Fi$柌tn_=PpT
;(3V{ID{iEZLI
sҢc"3[*8#^NG# c`4cCf4q&E:r@B$=DMRI'04	'yP^?RxS^3Ԡ j"!psmhg8G41$G>LxNy8.'RԇG@"LC8S1I.uߣBG ?>sj6خ0FƆ{17qDXSJRʳR%FL!sM(~l^0av$.XV]Υt:Jt1"GЏeC7aR.#*fE|[rX\pM[\c3`Z*؇qfPW3f!u61SJrmoXQN[1c_.ʁ6a<K#QGRs7gc7P߀sޝtos02zr{V{n͕{6>]yTЊX(|'׵h%" ׫{i`./Md!]Ђ[xC9w<XcpKC abP#lmПur8/^W`Mfs(=TA{r\X݃f?8:4gd<Pm#4Vo-Y@PVp	׆91JȺCF?!i&0 I SHHo
7A?U'SC]
74OzC$=*EL@1NfYoȒ:4#}n,uN\}Zagi~@Sd&l'Yp}@&:y0o)@}HUqSsG|@S
$qOsI#KHOsYdY/R&5@ѩFfk.`G뺦Ÿ~%0iB7}y1_wlﾥq_MRuŐpt{JHE2#f,tD%Q}:0Z`1
bW6K+bd
fe+7rJLZ+S!}wP3wi-V6uo+6]
`Wdd)PL  #,{yi*+ӕђ	g,cʺ9^V'0Y2 [g?)M~09?821^:3y+|W#ܻoط
{^GǼ?]M=pKW
BK捋fljh9i\	ȜEΚΙvÿ+~긇}$93&E4 ɹDRu$c<a!;ĂȂ!ŕ1/嗋Xv`tKeK@H2؅Ѐ86TjLeˍ4T,	.7:́bx*GASt=I,"G^HPuePCnA	GWfD#OR~^e*\NYLW|i=<ѵhώ~<hoBttU]Ns5xO|2lm6hݎ]7;S.iZU\W9?[ڜUjurl!.D߄ID1
'GW<QfB*1S8)Z:!
)QH
IZu#vRoo 5\GzxdT f17eEX\9ZAmvP{ǈ	
t8/Ҩ9ӥ%}	{_<`F=2!1ʔۢn|o	v&FH/~_:$nQ$ǟ%~:٩2j2Al 0lZ3qєɢGĉk&b
i[cu<~xsEU@}MtnZ401ySZ&l^}o_l
Evk`oMM`7-҈lXdm)\ԨHAqj+o	ƥM,ZqO,eT-5ڂC$(*9lR:jj:+=ҟFk*WEpIk Yj.8J

:S5G^МFm.䜼CcT@%kKH.!%
ud)kAAT1x7*\ypg
*5UftL1ńZmIj42`WYcD1_-Dw|㟥lS24B a"
ORz#2(Klqh\X*I_-4V.7&޹kxp1*{cGI	0ݻ
qMeO>Yc
O*EuDmO[,	f<a#$K0w >s	6WX6b%֢Bۇߕ"l?YkZ &|l
!\I8
|`&11P/IK)){@'ZYhv&g
@6`	wE&yIĲ9DI=Ab̚|/Hu<R	禓̘*Y.FEvPߡ<ݓgZE=tLT"&ǣ2="ǾG
GL `D݋g9XF Me
8~ErnEF*Mlu|BWYBviJ~{^*/m*X\wt˥eR,kT$ӈ tR6j<ڭ'E6ZhPq; q >D@& 찇NQz^~y
@^,,Q`qq__X(.l{^//T8 c#*bi&OaS	l"y$&̲Ds7Pu
=j\.Qܑ?҆|rz4ʻ}ǃufůsfBQBEv^M94$?8<"<.L3jL(L5FVw߽wpf.p©Mnc^8(Uν>n.Key@{SF׆{`|737KݒpȕHdQ"p(@dYT
cTYKKJ+VOwdC$ZѧtHοn w ?&iG,
蛙|шD>yA-@K#Lҗ|sĩi@3@gM/<X6t\_ey̺q*+j/2<y? 1!Ak(+݅b	KEv_XV!{Q:_׍u{Zfu>+&Z=9s{]	FlƎp7@Ŭ7G/Ð"^9M4%?}e%Ci*fFii&8{L?pG[mXګ`dl'k&cb5ncd`A0g	-X
RY<zŽU-̞w'
v8
j BXV>גk5`YTTj,OƧ.
fء6;*;ZdNywM" 0ԈKՒ4D=#eLpEH6_-8(uwʫ%S$#0zޓd%NQoc[:@~ƹOqS>P䬕}Ǐ{"f+wm3;a8Zx
9a>n

f|}X<C;>ϓѸ?Gc"[yggYQ@z䛒K="aU5v:top
I+<I~}*2E$ĎKڿmOl(4{_ծ8L^6i4K/m9]e`T%* ~?"bH)Ԣhr9>'	/NAO٠#HzK/ ]^z 1Q80)]h" +_TaU8icm<ǥe}d@ųAc`h9NQS&ݫMXKX~JЃ͠X)=Pԯu<uLUAi>M7:u&eVb{ u+9denWjdSX	6>A8ozt+$5Fv_iN&,>V2
7>#_f
0ZҬ`>&$+H
кeH!o ڇևhN+?]¿0Ck~\,?0evgφ
cuH`s$%C_V@DbQRUͫYA$|E{Z|uaޡU_CSnn"k ǥESʇ8A<vQ #\W)WI0#F`wi~m!FQR^ȥH#|apm #gaHFA>
2}桫j>M_dd2/?(Jt5XOwNn
r>-|<+> z?=y
W~><W䯀\0gj[yc~޷CՀCC<9OE2VnK+gj2*j~y\'oޱL+0+1{iuW7*voܨUjFc=|LƦ~߮e˴P9i̫ˉ~d
9yr }uf**?8?'a"U[/͑zyU@ʙpy=K.۳H+9ې3۽RNgQ l]}g+Dd3E
d٠C|="猖D$1K/%cio&5OpFrrre +9Sn*YLID##@	fq 패a#'b}=I\̮'
Zh|,=:=(T")F`EEVj,Q|FQ_/a|2rKbIxX^bI&$Jt2(i]NEWؗ,ޥxVcmpF&+a)
z؇d=>>1F_9=!~S`;{L|cpn|U^;-.߄m";aX(Ȑ1|YYz_-^U{3u!C+Hn9d>)Ȯ˵UIͧ@E$*}*~ V9_XAW6Я5DT@BlEM+Քd0XvmRfFu%Tc^*-q)tS9岠G)AojYJ}A8I}JJe<Y s\X&Z?kUY Q2*?qC#M};x~ZT2#hno	QE^y =@'\]ce}溞zF|`ėз)芛/%g@Y@kKӟ*
E{ R"p>r(Z`Y~IrXimf)~U(0$(@z)p_\zvOw^9;]WU5c(?	z?ܶg'hNrG]ua!z"!`4yp
A72E{\G9 T2	ftBIQ
WsxnRP>#G\(:4QSR
7~F9r@ :bQ&eP3RNZD%&J ~2{@1HrX/SV18cYϷw5m4y /T4"9	|O"u(M(֍nb.e1"r%	ӆڠgt }*ݶ7DHBlg]rt9m72Z.T6kuuN^=ŒBaF_lcY@2n6J
Ea (z6id0[\IoھfЅ <jW}qG9aM\WWr!(^k=sF-멜jH NQkpè],/?nMb=Zdy׻pQ/{B5T)~+0cы[pkM[J%~uD.7Jwuw:l{ٻ<XrfqUbÆffkLv[R^UO
[>p=[amEeĉuB=\,UX簙ŀb\CӴq<a23'Z @cA"HQjH}g{;k*Sp
gY&3֚JKV~c}lw]Ohph}Rm9xqfQ4jsD,/yQeH@
ʋu_@WaJM9j12R_%Fj$lgP 1l#LщtJA8g,:Fջ-
&
|Q5Jpl兡Epd,$c ΗQ~(QOtu1WJ~ɲ1dSʨH{pTWؘ~I~|K,yxD[CK..y?ґ
} i(v
h{R@[u1)s" > 倢#ҤZa͍ta[;OgxlL l{]W&#3lwGO܏za5xsbV3wgug=N~%8wo%q1c>(G3J&iJtX2E4}	{ѯDVV"oN`4~[b1BM%CvL|"0-m}Fq$Y";(:jш-P=4]W	im+wԀvZ9Zی|d涋]v8Uzxc]NnSz묝-'<ShC5j<Ҕ	<X*]rj;sjQSp{~57Aǀf
f
|: 54=hGqA%xIlwJ`ޔPv,K7EoA瑽o)n6u,T~x.{>{=.t(F~>WZYfu3 i7QKT
h2
SF}R&U*0, 	61*ap2Հ::A/J\``AI_/qZΤoޒWz]aГ2KV@o/,hZ[8FCwЗ<O~pz7Q3;{aN
jiZC1jvWqӰ^@ubw+#!δƮ2_Y~t$ّI)s";gZAIeߔZ=FaV;vkuvfe [ ϳ}{XOV`^B5	  5յvvNNyJ>)M`h3ͮsw׈sR7mKWlXu8wNYok׬?޲<;Y(6.x&U8ǹՓ9G̯/!?C#FlndB]]yu?y;xm/1HB
D_A//Q!;tB!Ll
1q]ee%]/+
8{k:|KVUY3i$ambAl ]Vjoinݮr .xIA->9XhJf3UVa1s8ٗ7RmDC1/Th&Dc5[O`LoFE
&_ugKy%:jz%!W`׌Ot\hԆMKMgZ"
H{<ܲh䂥3BNOsimM6W˂͢oabx+@]&m
6bZؑʩ;G_^W"Z-FE/.[XGe#^eY3,1h@$NE `u:i4jAy	:
~%|8@0mLtJ<,a ZZQx7YfK'_6=i V;h
vo8?i;ZWdu.;9 _H@X~w+*&V݄0ƳG3y&|fsGjlO8vN_Z?dy1BK:87+UZf{R[$Ґ&w(T5!=.MdnEk2M=2Mt,uEFq7-_	h᢯!ZESQ=w"6xoגyyQ;aZ@dԋc?ڭ%<%]C^%=Dhtw2}Og+a9g5ԸA~ij]iXcǴXmŕc-
kU¢HQ.aQiӍ.nz
~LC}SPaa#Tf-V5K -=?QUqxl#_X,U{/~|<kJ&-\7+gCۭ֤IoMN/t[S7gqM>ijQ?iځuo'?<]~dlp@`KysMI8pj
22 A8_;ͪKpAu|Q__nNg)!(NiU~[^T	VmCg-V祯̌$eEz h΁v@bap([Ӣ~^՘)8oy#km>-<n~"5
>
`,g0}`O1k(O1FN/2 +lE Ss_*3	- D[H
|$> h^zN
R % xN!+ސ_SRCAp4Xetf+XO\7뮋/FähZ,:oEJRb[hX`l @6)?llGz  0=,El#;BcY[7?6s>9=1,	?䟃"zs`<h\Ȥ?,/gyLIhkh6ҋ;^׮}|GioH'anCҧvѻKNuu9/mBrhSڱtb9y97e4O1
ĺb.ypvY&k[j_8ӟ籺\$%i2NC;q *O<$~J>oIzwm"8#e"L
:R4pE\t#)_/9^\-}\_r9*GBpH~} >jƊOf/aAl}ع03wWrKDoSB﹄E;N#iQ"H܅ :33#^bZ=.*t7
/lN3/]#ԊYod/2'a-ra|ƙpg+}C2ٌ,KKK<]`mf kẔ&ˆ-NZhn;]-_TDךNjڢnNO]eOȽP4]}iCS]I_%VuY[	4doD:9a*XP}	3FU.
!nS`9^ik3XWG	sJAyx4͢}}4WNIk{+B6c[z=kKLw|c\k)[#^
'?'xP:̚wkyݺ^tZ&gX^Z<4\kr|UrH`4͇>pklw*iBU
~u㪗K:_m-\bl@jGC1`Y*IbQԟ X=G,=i[:[Y3
fȏgY\.۸EC铞|; FS[Z|QЁ>	Y`-tSkESI]Sq
`k:/mդ7);psk~&*.(O^ްoPTQ1j}l~e6w댂NèZU@NfIbb0SB4TVq5H`9;Xed$i8p3!3@7f% St3w(<K0Pp`3V
2zO.==pF
^NA_@Yͨ=C$QU簰0JXf'
2ܪ
ѝjg7]Y`Bّo~S+Wcy]ݬEX,NO3a^APh,|ыΖb
h3\(`	Z?J/\rh;vbzrX	+}.w}H71u+2"Itҁ(6F'Fݲ,tnʒT`u,.ZbzZp8Oè{vchiAs33+Q9yAf0*!9*y`䧮x{Tha|)r(h.775KU??+*x+1//5a_Y>7f*ojB(%&4H x*LTB<qJ7;xĒB1u9hԏ0P7@!Ov)c?pY"h#^ކV!ю@JI+h
Xjȏ3nAVpZC/LU:4qaEaa. `M18@
a)p#`DIqhފո>IP!`6N$Or[FY-aMz-JRƤsjh642@ =?4
yioO.6&@ƪ8
g/"*,vh_.@ku- X+ v&N8,s{YkUCӂv#tᬘVf(:fi 46/9-ehtGS&T#h*zDlBJ@]BZGzղ2Q\g9Fc6i,
2F  V;䝎+	(
S@VL)ݛ%NV 
:aE(B ?M'8iѪp|GA5A{z```]wxBaU&$nunw/E !ltg6tF^`r ΀vMs²=j_/ʷNS\ֶBrgUX49m_C{ 3	SjҚ=&@
h(6UCZEJ`p j&=`ZJBsŌ 	aL fɤee2[4_6{A\qڊ
% 	k^qTUJjZlpUHݖymĠWOY\jY`Bx qz0`4?1FQKnEF6Ȏz2zK g,zBy|Dk`t鳲T9
vCh
hnBӺi~l/tkck6x֮r(rXc7L)DElP{W(@*M1G<nIǲ@y]ERUlct(,PX
/ |;aP_EFVPaae+!4nsEZl^aBAF\wER^PE֯x?Фg=M׬KN9}hwO*%3&w4G=#|%gepч߶C0777}\BuJ?z5)l}

2։4~rT s'Gj={!-[;J+T84a(	E=n\4SX&wT6=ӑYvo욲ڢ?y<FsXޫp<o6,3>3Q_\UܶeIsP(p[Ym\zipG>6o|vݫȃxHwxĲQ$*c|ZBSʳr_	tB[Q́F&FDǦݵ>FF^n4ĻHdZg03LE-6tmYQy[n[uZ]k]O-\JXwP4Qg8vi"3bN~SQK.B.S(Wb
d'~LYR4@lm$/kmȕX_51
isQu Pf `>yIt/&NK4GK at=K2A≫
l6QK'?
ݛR:!+<y=CHIޔ-}P&{&Z{aV ꒡p(j쎒,7[K8KJ-UY̢̧=bWJKU3~cD/fO~ԉWaj[A
+8-$1,q'3A#	<a#ΦΧܶDـY~hyu&a?e3(A'AZqPN$n6Q#n,t:3aM7,UYutlQx\GFHmIcԡNIC|`a"3꟔_~קlA4_˗
܎)f [*,oC'o8q	M}ѵ~ʿv
o8^qg"bP`q)զ]z0soؖD\3'`Pp8T?æ"n% WbPI%bzB7%I/ĕ㓑5M)kShˍ
1)T'Iu!؀KN>t³BGw$Iz508;6
ob-b!B6 uٳϢ)
)egKY@\͍4VB}f$9zx+C#{
i<AǜJ=żTgյ4kB(gjt7Lp:d<ÈSo^,齺S
v5ku&sQ9QcsFlǜ-	EЈ`s5DrYuo{wigamj
`Ihf܄vSWzM?6YNB&Cm
@SY:hk]һ 0b_c␾_]|Ik:dMZ#kv:##^55ZO]ƬNgcD#5XJxb<VDz/qlv:Nk(>[ZBPCcHTT 9FXe*:~gbmQ(-D6n]]}o
#˧QA?W&Md8qWаcۼIS@.js1/1
Ņ9l\>$6eb/_SfŲ'{n,8>;lO00-q`@ 6m5
zԡwգ2ӝX㬞VKuycRT9|b$OmkǤ%̣bgDܣ/</_ʷ_}~PDx5(߿|omC٫gߤ俾
F~VYCN$mk/4U9'(h, 6 qpiĢU,i8hxk#9dwz-]|VٲY>rI@ڒ\0׷˷D]}JǊ9W.h,cи	H%,g5<Rآtp,G-޽c5'Z)>Px j̭fvU\hH[m\h5՘;;9i6_Q}֢c&;ڢ19-}>WAb
.c)In%UD>,/h021:AJ1{+[{q`)~jocGj1iL	b*idS!2}5ca2Zldiˊ9KqsTɴ;;afTU>%+kbGYjQ,VCj)[ePG<\x ՞[]jt=~'}6*#A8ϭT2
XbKpDZ(׷e!?x2K-_ȥ 5Ap~Uj,{??Z/go~ڒ[
"m'N:La:hx>,jQ
8;Ѡ;_+BU۴}KPkj6uO{{iI=
?s~^X@,h**#Q԰Q3aXHp)Brk$,1J=$_ߥ9$t0us0(LL>(U3')˲X|bk{.$#{b*M
3R*V.+r?Q~{3FO]j\x	_b}*JpPh=->"WT>#БZ: a^a"/9$3yɘHy❕;/)aPp-YVtEzk;KKCm?9iN_u"iS"bPɦ˿	w:W(x7(cغDdb
Q"!24:nH%Ux;R<4~:wCr\32;^q]9;ʉ4q6{;-g*{tGwGUe{{7f'3Nzhw	 ahb(Qv,(YZPς sLt??0}s9eqr>rt<gn)Ȼ=!^?TG/J鹠b{5ق&:"@vd_ҮCiIM@%})6~Zsyi
&zåUCC-F
uMΜ |:AYA)j!ffíYKldDxy8%
,̓Tj1ExB!D?AAx'?ąh≩}75[X	 ^nT?AMJYδ
rx5Ͽ9lR'5Ӹ,\0b<0J$06tϥLy+ @۷!A'+>A/;wS@ʇ*]Nr J=RҵԞguH(-]RR$l^}{n"<̩'T]Gh=:6'cğ0J1 HC1TOk0q)}F?H}wÊہ
4i؟qOm'ێj%#=k3:)%ї¾袺sql&{dܑxMJfW8O
	
%ET
O'%_IhN$tϚ" 58>sdO2~$3џ~烌VJLLLdRJjˡ\䰼N1=f21]8GЋARyã[f
jSGZ3GZ ] &D g`6Ko$XL 	ZU}xRy$fsw,J6ؐR(K |FKdUX:4ri8Je~YhO!y΢R>zVtUGVw<0v&7TG8VlƢ!;^8OW/&H#LD90((ѓ?
a)Am!L<|ئ%\ÌL4⏕`n?`VWkhb+iŚb%8ti5@/th$pK套sGXh%bɻb/u5K:`Ěcbֈ^:Mžrݹ׶gY5e\pA:K#xs"Nt;f
dBC	3vDk/U1ղ9GsX-BC<27ǽ M.EguL͋\yY6{ZbuyE5%.wAP3}Sncez52QYͫx`բ*'/ΗCi~E'`ciE*&9ҞKA#
\:+/c)q!r^={pn7\ݱdq;zkڗ,\Ր9N.N[EZ4w^/<4z29愘+GU=0R=9#}^)trgrt:".^Q~;3ʪrmNEE@~}Pf\tzMբI`/81iSNMPVv<_aO6)hNv9dyXOJA1`SNF0d
7`z$
8g0:aї
Z\f0<\oqg~1?8`|l"[nb1 MysB'F~ZbvGNu_f͉kE/˚>6D٘ HN T1P>GO6g\=WNeqot#uz:JO')%A]4QWCMR&
$%j¢
7Hl%GmPPF @9sBM\ +,u`4cNZ#,U̥.aLQ<4I&ũ1@aWN]P9h^^=T0}\$y  'ѾY!aED*nĈ\nE*eS4O pD1Kr2B}qj1Ʀ/T
78KYY&駵lWSJ9=4OG:ٝf+\*Z8Nʢg^@$|%-ϦWHMVLR:/QJh{8s*dXJ5`j[pk&UYbd`l&LSTr@tڞ){iEڲZw:0Th	&!̀\V`); ^L1C|]ߢr.-8euJ|W>R Nr 8xA#b +<SfLM6e-
!d#_ԚQ&qqPBk A(#ZqƗ!Jpl"1ײkIZVp@?-=6Ss,e:3eZ5R9+7N9InۇםXgCSٮ嫳lmu
,3m9zOPEǰB^rF&B^mc r4sͅj\g1H9T1rFBCZ0JPhwa n]bյP5ނGnWgkuʥC?■ͮ|@-^%;x>@5eyAU954mƄWbp\!, GhD"	3!
鄛HT\6H8`9LE5tV\){`{
ꔻ@`N{9瞞ݷv5ٛ:WnYu?={%14*ve\{z?gme&b+hP9B{
OQ,mճU[`l\5zHṽu=`zrX
~UӚ
gv^5y#Q(2'}CWKs륊O67Րo6kCD&PS<JN,\ՅDePZC1$ӡ *r1ѽcȅOQe4}TB%"9:v̀OHn! "B]b	PIH'h$tl$gup;0y\#0¸iIqZ!-z9$Ey(WȬi*/c[4\6Pu𹚫H53g=>㯳XNoQ5\8<On}թNh
f ft+x2mS48vו2
)ѻ$:(Z1 FbpB2kYcÐQ+Ꮏn#4wݩ/+kOT=#ʶN=;33Q
@&.֯ɗ/oD{L=aMM=I;eχ,'d<FOcJwy^@L{i׼ɥarqSY< .'\J2+]>(E5^BK1gՀbAt p7oC/Ҳj8QQޢ>YnPj.$Qlw[ǅ@>|rFR=v?$ksH
Lk꿿
N	\|D gC ]<xFL_=	gL/ۅGI^TGde!ɐ2eӺu}9qtt;GT{ZDIAIɓ'nLSh|	_D_1 FO,*4&04	aDr
gสغ7eSp W-5_ԧm0j\rM+93ZG5mj!&\9mޡxKXE{W,҂*s1\~m~e-KqޥsV7]E,/pțgKCSu߮׿{]^>ݭ~wS$cwT<б|"QDRMcjId*YN5~wQHպAk3`$0	t1B(_%ZUh*\TzR׋PyRя9h`AsdӬb ဟRX|
NjhZ; 'h0{*AZ+ehȦ`<r^PHm˄V}TWkO' #gmkOW.QZQ{p=4A6
ҘB3?#9Db%>OCxu'@<>W 8-{j>9أW9.Yz&omC}s1e5\Z<rI)u+Zǹ/M7/oԹ}蹡ѰnYV
[3ܖL\[ 
/)UC2x&#fzQJm`ݲk燚G>|犩]C-`.*
45K}_.]|[NIwzd6?rp%K끼5kqAgZ 3g!BE	RǕ>Cl)I]{km;sZ=-Cs[֯{l|~󪧭 [OVƀ#@Ik<I{wKk[V?ZE?oxtϥA E?PR>
Tk	lR"7(/CmUe@$8} ,	a[ҳxq^Q:ZRPjVut%n2f9ر]7~,Un6c6:gѫ+-.?M&fv߱s#zVwq:꙱m۫۷c$_g)O&&\@bd34n'BX̡<i !h%DĩY.St A8Mtx+8P3M3 '
F<,owRǆWd)+LӤ>1R;q"LN,`/mO䔰m8F0V\6&yhM&t3J0`g@5zzX#Ն1oԠRڮT}V*yp-"D$ן2pԓ1 8G07Oy#xh(>
MswLiw:&mH)yi*F)I$qKwN^~2I6JU`>u <I{2Yp)\֤M}$/p37`r$k㹗8AȬUPL` }QLda~TWli	fGџ0Q"쉠
EoEV-ȃǗ1I`|؁%Aݶ8CDÀHR.L4IfNHRyK3{>0P5mh9vyռ%M|Vεz0cQ[}Уcvg-3盲^Y)Vؿ娢VԳVBa\Α.ї-&<_60¡0z̈B@}
0gI=FS]+(]`\x\J
K<WRCQ4j:sۨۨT/.EzGq3h9< FvĶ7a&8P3(eӊ;8sdg$"ٔ0&FD2@lDiazsBx_o:@	B
ZIH\VJf9
J\!2ٙ/:T٠Tf6ˤvjUȡf3TF (KZN>RqbN38ʔʗ5
f	jA3]֚@ZOjM$%RN
Y[wzterZlJYV9q* N&[5L[2<2?Kl*}*g?je܏Id?r
`^1}/U߃wyE|k4~NT~WrZ@
څ_(ZVT%ZZ#X>u㲻^Eo2˽T 'v	<Ր*`c N-FK+P
WAv4?JScF'c73 SRӀ\Q>j2;ⱳIܯ3s:,([.edW=s
~=; !FKl*`DǯP 1I𿐁I
Ș,a8 pc3X)WW`:5KQy7j$uE|pM5*`lh$J6R/#4*8BݺؖWX.m)R3fa-v4+JP<g(bv#l.؄+ a攀³eGw_HXc,@u-ѫs: fp{(nX8fQ :ho6 ֏E:~D|%5V'8jKmڿ/ѐK'oBvNg!dKuK,`靿|ZhQf$v,>%F vځ'C78-6 F
@6aY9_,GoЧͳ%{#QkA6>ohͻ㥌d͟_G蓌/tk`RӍ)
|:2r	⯿s<ʖ5E躉]]Zm/xƜO	XR\roytXQ]$^Ӎiܠ*nR gf5/C7A5(1Gu@|,J$4
DIIDmx8=9="zcq2wНvȅGZ55!_u*ZmߴN3^#7$QLZu%!^A I1)91C|GDM߰A7Y݌:֨n;VBNRSq%yo|&5زgt1cL0o1Cٍe^w>½!6jf4K	GzidߴL]/y rEF~ӛUQ@߉`1qUwb\L(bY%)
ZRlҿ˪0-WiUФIS+_!y]+r=`'tv7{}1{\ǃ$
cϜZ;
;usg,kv۸U߻|ozrPQwGb
"]lɵ\{h7{{8ֻo=`#vN_2}N$sSz̙Z	6 t6@fn:6i!T$"W8=(}mZx}}5hKż{8P޾7yƾ7^:8,B7l{8O<Ĥlt	jC`)7a9Jl6C/?4gZ+q+IaɅq&gw.yEZEW~q7K&*/:; ,woܳeCk57nug͵&շ7ڱf}?uP;o>r;N}ztPu]C<֘јsUۧ.
o bo?7gW ,I$Z*!N|˲f<s&|헪m:?^Kg<CB]DSXI*᪤hs9!?+K__%9@s
NzO|jĕDAi$ڇ~>zQtc+kx>7n鸧H1L"bN65|#.hd
`/0뉚]R>[KR;tHdNkVrh*<;?Gj3 d4	ьi1;^Cg&cPSV9y8xqcn蒳ѡϷ]j<BY+<08Һu%3\Nk&,5EO>^	閪8w<:ml튵ݳGVt*魏7Ϛq0Jg!=B_Sb>7LS*J&o#'q&]+F.O	s!qLCDktK||<Q~J% UZ+Pa8<5xzyμե6d/6wXi<tۥuo[Z/w΢%EeR?W h\zSWJ}e@Vf7:xW$7){t֓Et xr֓tʓ]d̪u[)'o%CCRǌ_ރoIrL=e8=gLN;h($MjQ\19z:)t^=QZ	zpƽ9cɶ|ZbdYT j.h7DJ)2jFO^d8P
7lLč1I#n5peZ.PaӤf[[me1+ًÍ-'ŭ+!]xdskJ?{ӻKբ!őb8cHd}M-9zTg4pӹdLd5,t`V~O{Vͺ-yR%-jOMfsZ2v|u,e4OX|CGlZAzĿMV$ #C. F+&K#Z(QT.
DUΐ?8XvPs;ֆCǌvZ}I
5C<wMW4ć!'
]qJ!g]KהGJ}VV>4cLzbU[)3K!wY޶oXq¾é	[?b(\5La乖/{satq/RˀƓ/=V!疕	rR|BDPxt|߳eg)VA"#^AqF$ڻ"db&B%+ձa6U{nm0YoM}4Ғ|y|*I{6b=}
6d1yݰ=s/}qU|gFOS1
j~;q/^u 5eZXnKDkc`LSUxM֔v)#(&:!PUԤ:ˮ>eKqGe6(ABO3cC~QgTh&*F&ak[:V#UJ5.Ugp+*¢*f=c(ךW1^4٠.QK wƐetC<(a,zB0 V<[M >CwUc:y'܃i9}^< C08C\OPE^1sZR5Hvn}}n6mpb1,	P	؊A1eWv5wǽ#
h#/_]ps3:u8ifٟ>0[v۶DY4ag
"DR9KvHR]SPŷzJƛ3в 
?X§)VF1Io0O%ehyw	xA;2ބI>gvz
_ap^i5ҕp}ϛwJ9ˉlԔV4W5qH>.{C[|_B>N=^[r9^5bUΙvJڂk|߰8NgNJhJ,JA9*rDx0s{P6_WFjpm8Ϛl#)ku?!ḰГ
V{=ӓi3a3	`F`vin`n7<2n7unhC"$T /^BdG#yYl޼r U 5) 嘭C/YZ,[,rͱZhXqE~Djŗ=kqW[Y$9.v1rqj3܈m7%q\br2:.G!D8<%rըרi^`:X+r:]<cr6 yi䜂?DE;x6@KIhu϶aںqV-6uU;V3VZG>E
;B41zb_h
{b#g¼p9t(J8!RY'%saX{D_! "8dr50.&ʷӾ6ې9p:X	qw3Ϡhu8eD07D{ s&ByfthsȤ'7VT
lL./!.75^FV=.H*^WR֮,_0.iW]ee+ܸ&wo]MP{(aW80=p\qZkք΁w3V]"KfEJne*kT7*>q{-ȕ*LnwWXr. ҫ.z=b69bX`-Q
@w?qmEp_|#KWW%eB3µ{ҷe(K@ږ˃ K{[@ Ǹys0df		Q9)8{!p笯k.U}>}kk׳v@՗.q٥W&oE3C^?C?G[۷={b<}aA uip(uiW2JM_+X	^]"~ǡ@)<MN=BóM-L!mL!]}c@ж\%:%Ko`**|3*]I˰@uXK	{(|I|~_ hq% A_&A%D̠ڍޠ-hCxB>Y3=8:Y7bzS8?%,S/ҋ^$(3HݝH
$#BL*f@pO UFٳ\@ݟ e

EHquAo=SgDQ.b&.{f׋w	Z%0 .7s??~u?sȊ	'D;FFEl188:UgFͯ_6m0cYV7wU֜'706L6rh+FZ|T~8155ipMVOKZ۲s6žbD
K읁;!f
I5k%fpoZNK$p܉7&x8"~}3c@qL4GK2m
<J~))gy8s_#g{`.ڨd"J ϐD1x1"".@P9~OQOmUPhPO 
*4V}]}JV7l˸{B5寷IN].g[h`/],lrƨT˛k2ydBH㍰թrё
j[c	eЍc|IO!E# )Kx2_$ϳ}S>L5	TNy#4I <1BD,5X
ay$yRcTPYLєPZWfjzA3*SUs(go.KZ!Jڊ&A 0%Έ-B:)NゝKgu\6߸~-o_wSg+ggC.f$]HxGhc
n@dV`2]zuܸVJhsUW+w,WD}nOӤ тf}́Rj5NͧyO8<lH.6N;@{ È^x]8!Dh"=eN23x,>
I$,>扵pB]41+RKH)'!G,~%!z}< A
 &d!t2B	&Jd41Q4yAI@6d=c2/c~{V̢ 4WwvÑ@|']_41zJqKOtT)j$4+ӎ0KQ1sm|~2k<L*3{ ̟t<$E4ouఇ.Tk@/nH9 ׇ̙ـ޷`x-mK.]gàDC<'Ap-:bxJqh-,
Z̀fh7,8z	bҸorL@pG}`)B0gw fh"j2G/ܓWKhFI+Oo,WԢ!H :![lpϠ5{Qi2m^SW\׀d}ﲚ-%?I.g+A(>5oZDnHg
1,:/X9c^k4yUzK<uL?F+MKk\*JbN	fS^)P +nJƁ5jq΁ '$PoaȤ@43F0F|K1s4AsAH4/)\E%B}cĹ
y4OőZl6IQ"rc|Ւh	%PL6;I9!
%6ydyH;cEBNswW13CIpoz^tf&Ȗ
0	'p5"ϔMbĈ+̹)i;M~6N)yӜ#$7+a	(gL&^o2ypW%0}Of+љ$Ȟ;`P	G\NkFh\.qp:u6hġyPm
J*TYVqz6JU*pg:!ǤL&rʥ2>qjNo6yu4vg(tN')&]tjJC!SF4!H!C3Ą'$O={ bj6iA9CN@<Rbl\8M*AR2HY@ZA-V=oVCn3,v056h
@FQXuj΢r*{v*=
&G[|-J̥Vgn\=ؐ]m#- CA0
D\ ǳRӨyx&YrHa!Cx]9<!
)Qq-*AVeЩsB@D'K@Tм"BjJ|]jN1|ʔJW]N8v.˫Td@vqMMAn0n=9nz݋I<`v͛wV,])}nKu:&~&Z[ωVSc{V\<	=
zh$¾lJ4yڪ@]!jcfI	۱ᚢ |t9q'+,m.C]m+,Am3ҶR{|$举AL1xsé	QoxgA
FQ|4d2Z37O@<qBF_xE	`P3 C&	\Etxa4s=&LgTH^!Bys' ẸBIO6H8pbt
(AD'h!Lv<&Ap;0A+QDo@(IyD:h]9
"!Nl|XtjQ#'cD.L&on6]uɼѭpB簄,ٲu#Rixk!=7Ⱦ+Eք=~:r`6fYK>qz|jP8uMn˦{n2z$aF/K17~;D1cA2=|ɪx\T>m:Vb̗o}Yn[7}_Yj/c
7N\vu؆-5\ƭI~ĩ/,H]>|xq"vJϠ
|.(D߼*+੧R\N?hp;$OUUӁzY&7uj^c`+)4U3ұsX&:tq{,8qd>IML]Z
E M1VC9eVH꙾rJ	XEE
֣o_rUxv|0'5#GTO|x\.PިDK8ćGKgd,Xo3.A	5 $@k37_ c%ByN;IpMhZUTM6;$==<RIR5cX6IQ!3;*j
n^JCCYzAHElEz@.Y!ᩡlI%Y@Գ2+^D*ԿV"h2-0e򽻴2.tKUr]Uт@@]bҿk5ԥ-:TB
nz҈܄
n"(E.VX䫋\I^X+PM2q2$E)2(O\"DO}Q
:ZB"g[?kDQ3[]Ь,eR*7jw킗ƤwFFP^A}AA=pQdrעļڲ33<KZ5(piEUeR<YPSyEmֺفl[ոD:F]\%te=겒nEixܹ}vde"<jyԘ'VB	 +ͤ~pc2D`J[f^D^bzw' V[1:k6Q84W9ii{ts1p΁WKZ9ZْZ]v>)wgys&p߷W7z0	D{satD]3jA%<A:'b*CS?s2"7;UQ_|fڂ(JZ7<S^枮l_Ε Cw0D_	f
ėq.40:z89zAы.с p&M[Ԇ4M @A0e2e;qee#駄()	ܭe'h:]9D.PNުRO:(̺KW׽#gwjk7
'7^#~MG]iׁVfPm-~rr85-rx5*lYlg֯^@=qMx$eqRd$p r~cӪO
K\3LsS
lɾɷ?o[^
 cRdYqEh?z?	M-P>SVW-80{WtNBD[|D`-
BU0?1DɠXTFvKR8|dO2iMA<xaC<2FIϑ(
^?K&p\1mG^^	u498rlPǄBڜ'Ȑ N^;Lh]D5#472uպ'u}O/k[Z5VkֺYs$ԤqL8>9
6ز4OIwI~y~4=:"`h0* 64`F)br#!f"G#jS1s2_F8tr}]Fsu9bW&Se!n%~g!a?FD[&NתM8!
!P+:lbmVֶ̯sY[cD󂼊%tH@` u*	za-N2T_⾗+ZR>Y-{=MA<ɭ;S;xށ>\23['4'͝y6dF[Ha,rTH*OQW/JUZ<֋puBL!LHQXPu%!]Dkաm[")\0$R.w`бsZ"ebEVŸ]ӭ(8&t{+s^7{lyENK5c5*.J`sZϙmW'|/w;.Ѯx`m i3._#,9bnVw~6(b#0֟dD0Tپ0)H-^L*KlD?t0̹Ep|e,uO
=kvg8b#+6B'G|bLzpӓʜ%?ϔO3<?'R@F;
K9m8TȶMbHqS3'_b,lಹ_aR>1d~rQ|ϻ~!*LGZ<C-%<
2ɴxXnW <{;dmKQU&!h9W!sDߣ7#w_@'|Ļ _oPF>K*5D"ђb2x8@
Yx
">!~S&JZ4O>ˑ!ټ;֗ eMkd#+MO#@
*)T=/9NW
 	1 ńA)_$7">sZ̔ JSrmXē`;o]5'\G] O3`TD.ķҕ'130#nCXoa.&
aH%
&
 )!i-{`D6P	fӌxI;RRw%cÆŒN^^n[^Y
օ+p[0-XE=J0#,!1@Q8T  <OFz$ܗC5{<=dL.Bl9`iĿI}?ӟ%q9?6Eǌ#zLxC߀;w>#~!?؄~<!vCq_&`f}󆂭t~5d&{ZpNMWd]iV\WBQFID$#N$5L]qPXTMjVDIh>d]2tx9>>]rհ"0|fڜ
;
۬n-{w*EXP*sǎpj9V8jhJG;H[K·%';VW9hJ
wTOoϢ1Ҿvire/g}}?\cS[ڲڧѭ5^ sZ18x<wL+J(?
9ul^OrNp|bZ[z>3N]3L 5i'O݅$#럍8\|Տ,t'
z"`Հ4,{K};?}͍^ge5r[<4LLuB	Н/8ԭkGV$ʗ͒<pX֢c\?SP{zmZhHZx*RkjJZ;oR%UYOVV*__?M̺vvqRc =80jY3}B-Ӎa{- VTD8h{ }
e9$![N;#gV[eɲ$WȒle٘blf馛N$@BO@R)0KB
A84\KliJl}̛7o<Pف*aOiaZ6$H4xڱUQ\֭NEr/ރIKIz'bAhmX*
ĺOHFK$*BS[:7m4m[s,._㸯;K*+}pLv%}-i45c-B{

wÏv_
 _u|i$Luq(?q5D5Ssr
@AQQGԓki]!Ll.?1t8Jmv?	:b k
h"MN'@@g~΋V8&#c xF2i&9 n
{IO^ø:W NL~1e֟e{Rh5plJgO듙s[ }6dւmjܥwo/#nX@WBM?WFgoչ
iT+0HiHEdW{GX ~̺d}{YgftuaK(ǖ=<vG5>DNOŦt^'`HT.MҀF-'
=I$ݨPWشY0V3V"ར4h=sF1\U	l ?|U'EX^*ՓbhV
|(S16mZy|^v'`K,,,/_>_G_?)egΌ1(;	xϯMϯ}Bh*
!(0zOެGvJJ<{cyK1qA|^t@K9#72e|:?\}c`G0%S	вO?\0=C}%76
OuL:{gp1`]LKXcr,w'cAL /?d${mX3x9OC&~ϜbϞ/N	W
{C{m߾7[5ƼsO?ӧ,\x]!.gRښY:*doarrs3[{VEy>v[ˡoXM@Z!
+VxV4Fxanwud<,>8d7[1j:pBZ<p	"}C}7~?*LamIFP$~Sjˣ
)UJST_塈2#<MͧQ˨BoDz;{1"X$G݀L=. 	[qXiԧ"o4y^ȵ>~f3B5S~VrnV
n#~0,/x聞?^ԙ3e/]wuow$3gbj4ר7!*FyjgQ;9?2~~hўtO:)t='݃==CuY4$[:,	tBoEԘLoHMe@-5,Bo;{q^̍,f4&vphȻv)"<
'*|0Nز0[JnEE.W
:LD.D8ߵ?ODPI1Wes 烏8bavzigk6~[~΍qD>MfU^OM8Ru6.x~jTAkMgzև:j崉aU3iPRtLUxY`(@|R*Eǲgcg@
'uA`2+,vЋć/	DtUwmKbI"et'&d{bDrRINf$U`>[2ThӌNՅk-z*FO<( :sXv7b2uTt\k.7ǻt(?GC߱7N95Ct%igC̉gS`/@χU0>`;lc(|0v0:Җi#!5a
*:0,O <R|MYJ)lǉ*SnE뇀`ODokͨCb
+z%089fx1ÆiaPp_?=/!Uz2,lOZt9@`~mnCNNPf.l/IMlLX \ܗKj)Eu%u* bN c 7kg1( ;p{1-g1@\2t	7D	P4-oo')%z29L5)2<:B&):O¤T]EݶK~M[uN9\[F_)6TVpHtKu4ӬV<kz^βɎtG2y=<H"Go1oJ($gfwd;Ag`viI!;oEq-EIc
(!"PGinMv/^;1bMx	q"3&8*^|ҿi3շS^WYbiJn*M-ű]o.e_k=eo:Z
2w//ץyԥyV2s:Qb9?͖VtXJOq{̿;τvyhOÈl,oe'tALAVqҩ1ʳ?ϮZ9eM*L^w©u,m*3qlU02'z>6_WʧU;(+4%ɤfei^oH$S;C!;竭>N5)D{ʎ!K} rљ yVЌw1Hd e;N
\DFChWvπw;ty9rӹp\;>#~`)ahZb izYjq;~\lЛS+rjBkoPl
)^NA]'ޮh}f"c.!ok岭o<PB{?L'Eԗ
D	=]*.gJŶ}Bot&&
e\E^ ׭{/NK޽DX9#^4xC_
jK"wCjM{.(,ր+MsQDQcTP^/4y5@^+/'w4}
Zsũ"`W%
yGIpC0:E?kݺYɎ+	U"5U@SxW.0pKaX}:]zInN6C̦߾uQ'|䘔UVєN=?v7	9l&mONb{#pG^]<MbHd|r!q؍1a+na|)SZ6>/	SJVN\*T-@vfVO!h4RhtLaH\d,Ӏ"F'aKDPo(z
p=cwd7b]Z8p`"2X:"ŋ׃'H-2s֯{/Ǿh{ThrĐ!CT0b/b
	Ԝ[9>(^0atvav؀ńQ1So4VxE
Nln=zxϒŒ;ؼѤ$.	)_$1(}5$ӊEP۔&~F̩8ޫ`(1E(ѻ&G"T¹|b,i((18W0w#BSGXK{_gS.ф6g?{i֛뷛⥶v=vlTRadځӖȔ
\v힁UU7V͋ *5}$2uC0w҇AåήCvELSY>{4&<zqDADhB>~MjF	%ۇt_O\',}%l)hz%ۺZyIF]݂Շ_'7~U)<2N(;h-Pq]aV%?yyNM	َy[{[h1r#}B+:>̮ׅN "
	ܖ7Aq0t#I$O*}~TwDE	7^  ٝ#D(%M*6X>$@p^ 
")	zAG%b>>T^};
OǘQ;c-/
^#7wVt	s&G'*-#צ Q% ^M'pc"-W+*m9zLԎp힒{ɑ]}}(b0};ax]t[)Q@]gД vÉ7g㮆'fToJfȬ"Rۚ˫Ǆ*
S?u=95jU!9F9j.4p|P{wΔ"Nz(mW`yخ`ŰKf?~Fm(ȑX0sr6D#P2	='HBL"-0j0dNG̏rF=/tu ?"Ju*/^]2Q.Uԩ\|OYw/^p9ߡ%Ԟv%(-FʋkBeNk=vuP37g,	}QįKLZ>:MN⏆/"[I}II}{Rwu
R_KnxRFmX`HS]}Gŝ-g(KqAM"qpn8o|5Rg1:?M
N
</@U=xoZN?䞧mYqo~Z7Z\Cѝ-:O4uy	=QW\AF[%2|	BbE6RM|uB)~]T u:L*| <YR-fgg}Lbu}aLWWЈR<v3A/VK	"gԤ7vDȉonGC# &}?Gp.cFx
vnKp_w}^Ь
8
D PX@j%CH+O58}ރ,ψ!Bp=zxZmh3@|ُĉ7F^Qef^XǄ7J|6ީo.94O˲|!,
E(4
a+[Kp
^Ŋ&^jDth)b!72Ayc!$y D#4joHVp	ٖO'GoZPT1;!*7
9t/Wȩ
ZenꪞvMOLv:{\~Knjj")|ox\Wa4I3rXڍ=1] 	f"!
V@7cۙ.⃴ #❍B8xq;[/6P.]ĞC>1a%O0<;,A[w*
X'!(=i}&?#^$	^2)m4sDE|gPb2D q>n.*?W̸x(Ļ8sDSD<\"53PsA907@RFq1xodYХ&]bnʁdbzya(rj~ }@8
	>>4J.]RRŨ2*F
A6r]eH}KK۔JҡObƆL
GhN'%+Sx̒jU,V/}2D5NwY8G,JeAh*c幔wޡ.0{DxSfѢ2w$F-:WY\D,oIyךnNI	,i)m#YǪjU-3Y$v%%3ZpV򒲗.#cNf.5d$C},KSצIX$fX͊DM^uVJ0Rs0=t@kToRZ$bX*eVEWϕ5T0Tnkޑ
7&$2iyThF7ubqey #lR*[)IMk\a#u[N^3VqאnL(v\fTGQI7p=3?קw(snYISMg''gaFmL*1JJ2U,O}}]&k9-Di-%}jS*0XXWb%cRLR)$MNK,NcإUdfI$DĢ*$R fLMMuLձK7)lJehZ%V1՛
ڒS.u4elJ=RSj>rlڮb4%ǎ-Y]#,EJ؈]?Sgz-K=:b+4A|hFCR("F'ch)=
EjjR7﫧W*JoJL2lXBaar:ZcůM?'-V<C	^%y/ϻvYYL AiˤI[&mijS:{=ܠ?3)?gՠN%r |^E$$ZoIIMCͩ<4ƻxijV[{
rTZjBuT4+v4{YX;	Xڸͳ 	 ׈_lXl|ن
b q(:fjM+g:R?1TlJ@׿+&9s>xn]mPQY5eS0 Ư_?^:w.rMP	ToܞL"ʛ_b^GS7eZUd<Zi׈<Olk}VMPŇ&jY
VjdI|Q2=`H7ER("*Ez!ԽQ-m*8Н1QKOJ"R0,cW"a!(赺LnbޖN&N3:\)hVw&@ѵ6il
,>
lX>ͧAGM1	0Bǖc(B0lEguKPpl
G»vh[!A9v
qo9b\#}v@04>
B4ZQ)?ݘ:>uX vn(zHE~Jńs(7PzXx@?n;E)҃4EJACuJyc>,FuUiZ:^{P?cYոOBk3Xt5PTErׁn*~)pDM0;bMA폨p[인ւ	4]Lvky4a .YB\UE/5 lbK2#M%PJvWθnpk'`@ɴ`iʌPW8Ġl%t	%ʌSQ~Vpj*$w^#G1i6}"vw"b<nc?ͦNi&t~ؤ֭:f~Ygm-Y` ΔisV3mJAŲɹ_3YUjB$,8;DQqܓE,X6P+բR`_P̋'4Y{[*e7-nwr'PŠuw
?u:0S*{?E<yN!7PղA&16l'o5=CoJ2x
^~	[Acb-~6?u!X燚GcDqn-&hˀHp:EG+n!.<zMh9lb젮@ȑp,.Ui7eQj.`)Ƒt;hyAPIظLKq!"zFZc
Jg4F7eV(`1L^5 B+ڽa]-jlԅ:[Ų!}!b(8z)	_J|}dR*jqlϽKϽMvDg5Z5q.\jmEk6md|v4MVlqdvԵ_<r&Mל`OyE~vҙL:|Ư0g͂aG:vp	(MS<ӆ
?=&g<2jzNn߿V[0?Hlnۂ&U>zrMZښ]].?+;z##Jz~:vvۻ$31~eݹ+tJG;I	mWyؤqk*dƜ^VX_<:7''wtq}aYa#TH3:#CyVZWjU֕?;AY|.d7R]&ODh<*z@	i݉AwNA%L
@vI0c*T.39R[VJЩ,՜bM1WR
߫>EƉN,`õ>U8z/{23Yh확b^āpQ{/RX_߲d8Ȭ6e;зk	}B
rfq  HˠfŬD	ζ%,Ĭm
?sx\j\WWUqCS~mlY3M>qs3`ػoSL4.\剶jlu[I77쵥S4m323ȧꑳlg@͢؏1W%`T;ω
ExCt#8*g30Gx{!w>滢xi$plɣ`
;f7kAfyh 3>>GU4VO-HM֌oK<')m?%{[2p;>κK>e}}ڸ0D2`TIHnP(A!6Ƣ2hk}U3Yެșt#d}s|'s|\P_ ξGփ$į8;BhQ",Ƙ{5k'ZUָߚ8~)A^R--.fGWԋZGE*.FzӘP.$-J}&\VTTnv?a/'n-{4yʐ`ʡ5e9<4eU斕dT	U6?AX&튨Řf5?MA6eb$d`t%Qp3`sb3NnMSpU5G
[6CnqҀ 0y"U(tK\SR*1S$AW~gSvtQR[ 
%ZԛgXo3c(|:c(sVl`nHz*_~uz<J9L,,3XӧX 
,tRYP%$S׭)]dK nBd&n&|)ò{
K*/~4YkN_Juqh@kߟm84@
"b -M/g.,@hL`H.	}kopy\#4T 3qЎYcvh/a_INO+Ui
SV1O!Dt%CԯP4`@|&8CP_ºOV*^wvY7~EUzD4:
fbʃHBks*DT6tFYe-}e#t5}CŹΟzBZs	#C83k؁0!\M z`E!hЛ=U~VФUƤҾ=Wi0tնş4<}K񬗱āa)*[k9'nJG'Pٔ	0u
VYTJ&YcD$ϫbr<oVH%.T(O$
-ӶD\ jKZ4RI5rcѧTɜktkI)CBuPT`8M.o 0$T	0aW>P5X"ݫ~P]#jDy%K j$-v!F~32ܪQ5`.| ap>nw/y#?X##Jw5(
Nx4슩qV^=~R'Ҫe,ҧXM}jJ-)T:אw3rT'x}scFy7k
V0\SM(2@u:-YzǮS8W[4;0qƷr6SBIXqLt&t&#M
G#&t
ڠ470݆IpX2M LuwDo2`
%\ 7߳g
^mlmW)sX7ao`BfbnQ1J)?FT7ѣ;C6XV}EBq:ٗzhW*S/'W
I~F,앀 Ud
A:ɫ+z:b4'Ŵ؉szkܮ .08q/8 kYHE>QvŋgO~aժbx.쨽'T Y&7(w ^;[Ս$\0w/6p'">@'w.X HZɋ(jXyc\X{'Dy>z-zxy>xm˔ۜS^O]Ђ{E&``w) +ySL>cua=$+h)V,7RH֯a=U<35@fF9Ni@6݅LDQs-cr졂z	
W^׏~чS25$Z}݊#q~d{VF^ުԚYl&'Jk~O V{W|G&$d]8/vDj&7xҤU떦ʐ3{W(1O-T}2k@NH:e i|},Nj$}^\X,_+Vr{-sv7d/zkuxC499/%V<S[ƅٷ_:<}3^;[lzA)d }-U}sQH:z3
\D_+B3F	xh &>ϕ 4]j3=/#TQcϱͫHBw_Ee^f [џ376 N3w\"R1v/}}"O{<Z@!g (E=
5uW n&iK$j!jw%P<T<N=QZUAnŀ82+^Ra>?1	E>9|.mV
40
l<kO6ҋP$K6m
&w63dVk'Ո!o=t
4HJe\r.mOaz
*ZҩW[.sߟV"k>K҇k|2A?g`f.}W<wպ+~8U)-l}Pժ*R379~>F\[XQ:J1D~NN*(|C^&@Gj1:;kN\	0ƅfӨp?$0oGG߽0C は/zF4X~dIE[.9љwI` 샧'ab$~+/m`.- Qb'͛"+6XJ̓n+fA0H+l_sʴ!-TdؿOdɜiLjNqJɘeO;;%G'o; "),=K
][ g<Fh.~[?
u}rf2h^3sjƾ5q0f 8uĔ,'5Dk)@?\a^=MZ_1&cMͲk׏>|Mo<<
4/c遷<lٛ,v߾={{5Y{~ '=	,\k^&'0tXDl}FG*QT?.ZۂK
u-ZRhu0!$7@d~XɢŎxx+x4V^VuPif
wz9i{V<їKw#=`~ёޏ_ф3,1&W->xj~ܱja>txkla^3qniiЗ1MɎH͌ و
KQj1$ag2g#K|!yeDQLxX{i4{{VNl	Ѩr|_IG$iu,N?TW߂bt*xAutAՏ7Ѐ\84dه&I~Xsul0eZ~rsUJkG
)2S~mVyn#~chVA+c%YY Z!W1tA1y51+AE8ICo.V3['1;Sv2Q:pؽ{/fb/vܽ1<I$U
PUahTRIԴV\Uj"RkMoyӇ
9* ).:{f=ϿoQj%k1yT}[ghn44\5rd]qۇC<i̬𳦅l\EOC&Z*ZZvi-w *1t)S*%/RjJ5ey3֏WlS4j˔j04ܮղ"aDwϘ֯F8Oͦ&}6_:-HŜIE?2̓uqCgbZafJj4TL
JcXh:p{[`:N684J& nFٗ,-P2d_
'1@2'rdD*Qe?<sljI\x
+ӽĐvs.b	'YEUpCӥT)sN'Smٱ9XxmSDŽ1ONhSVe湕ET:0OapY§Ff~]8,K)7BTpK/UedmAzkT`co_ek*m>l^:fy%6?a2Gy8rmngô0.ׂ~XǌcpD1N70%p{UWܥ҄oS(آ	v-6=C=s"n"^D͐8'ݿ
ڊEBTPAEU!DwUIOep$FZo|놪'܈s!}q"TPd(le+
VW^DlYs:ahI`X kUq&HIR&
5R		
r#F <oj 25O		jvIS2_'z"el+]f(:xt䊬!^G@<~$;%"#?xmC}\x64+֢}+B6ԡ{vddN?&sTcaxiRvKf;7CU*iUवfZ4j[o`@H2,Wi PrU)L	{<\\c@sND:_Zh8))zo^RA4f[Ghml[Yoom
J&NsKŁ/e(ilXJ7x$*1p<piJ@/F'ƳvxD,)N!At!f=ΣCs^pgs ߯xcb{xTNS@`%	ISO?©7q^.3lTUf>-M>/?}D LeJ{L':y!=lgwKsC83jwV˩}.'v
cUQ)I{W-Ly}0W_훰S%YIV١gD7;;ZX4vhH;n}5>J13U!P3xd}?1mډwER`*A 36?M~hIxY=	28Lq,6h=΅Pt{k0f7?rFR8`vG<ؔkTzgL+VaLwp
#
&ɼS,Y~>o~3b!wcE. k,)O>e 1z<gT%5"<O0;J7Քc vZubo9 
|DIϧ\,.M^<{vrZ|l	GՀRE
h+h N,#Oyߛ~l}MMGm@Sa1\q r`}X$bSRReߎDK!F.ӌޥВBݧ{b/Xϐlb01v.LQ-cdX BGAWZSXw^yZ$)퀜`cQfqوa1X ^je9r$Kfd9Lhpqը`#tdOAxm~ ><aIŽRZP3Cy(Q0SrO
yI#lYeRivffT*MI$EF"}
Z2j,}2x
k:ح~(a
/P {7w3߮lgJ-8h|Wyw?Wmx@_~>	V*1 '_nFBQX
!I'P!q`3QltStb	/<;ɖ? &%yD,eOp8jb>
@Tﾶcη歿Zyw~?zE gZsq	snݴŖ'2;͹Gz,>#QQ?_ bNɆӍivǌj~w`GS^`=O3cM#!ȧtxۄ~.k:D!,茮?:At$6p9*> bi([nϠA#鰺Ih*~[Dqt珓j`my. 7e5/6u_T
BXa
?-t:Ufr4RJJoE--j#髳,*v>&$Q?㰗.;Q<aU (bt%աG1*l%:ӣ֤l&ĩd,cqku&Kn^xg#Vi؜k1n'609+l|4jcS]VjeA[)V٤OYҚs]7gxzM/]KҿTaf8gzYw b;I6@^ԲzHI4Z!Dћol}!
0'\Fō2j5 vMxKUMܻ-~Cg&
<~LvU3
 [|V\fV|r9ܐv&qoG<7kZ})+Igkʋ+ ɔeҙ9s
9hO0Rk+_6`S(X:Gi|Ko_vfs0Ca&<7(
هH2*2b64OR֍}qrdK,WS+c+YW:Ē7lVCnd\O0ƢZ|׌Z1.k?WCtEjt:dK]իG> ]'׬=f͚S'
3rxW˯f8{)VLo0床|`;&ޱ~Riqì^OMNTuG: I.AR(_Mo=pNtMj7#~s&#K(=<kwrMXwZS P{D_i5ݦvK~eh*9p=ĮΧw+zB=Ԓ\s V7ӣ}im5Uk	zG9rkA3W'Z܂|ȈC'<FBo3>q0:]pN8DG^>HY4׻]F#
÷,FhLuO'zܴ%*cvvd Elg:1hr35kgFatu~m>џz9qLI)U<gx
_ifmљ`.l8sdg鶍yXWx6ݴ
e}ư_("/[0:ӻއ6:l6%P,4
P8u,:N/6Ƿ7.Aߎgd
6{r0x؋LF"\b6(%D"`Fvpg!b`	_J*eK83|q(ԦJ>WR!&)A|r*2H8%ݠJe[|MojP?C[8ra93{cbqo5&0
4%eٳw<<` [S7߇?C Ӟ̶{"yPn) hAcWzZ*yb.urܚ[%XqᏣ605n'Ny'ND~^%s%藂]MLcBuJDO_D~_8;U\W#'soMgC=P9NWǐu0-ת׶Nnk9tz9MF̍("QIS?E@!&O">H@!}Z%?
?	qx6rD.L0"*r8"GO5E79?Е)Aֆu)~Q}@l Lrz\'I,\zӷyMڞ0`V+έxFGO_C?ҭm2h0~ |lClq槇L?dnOuD`mptGDVf롷G3H	>F`h㖋mpM6\.f/ђE8	:|12ؑ92^
ԍ5k F?pA
Иwd<	w=6J@l^}SCGmrf%[ϧgi\	[x ,ރ
u*Ժ0:
|WlrJi6}w
,i2ִi&׈y|[I0C^ymr򑯎i&"Hm$ۖOvyxt)^F(  
buroQ i7c#RsMav))fDjL(sb&[sdTb1s_7牀:U_UX/ϭXqX@	Й[FAQJq#?)ߺ|V}+-H6aGtSxYq~ㅰVjhW#r#1!w48Q{n/ i=(
U-zFnU5˖gRqw`c4gej+6C9ein33Ѭ1[wc⭽ҿˏ^.L\xK1ms\rGU5^4Z!Oѷzh3Φwyeƹ;R=}&z(6It}	|ZieݲNˇdKۊ8'sǉ	9I!R jp%p%HZ޶(hʎҾ~ߗX;;<<4kA`6KTV2^4"?K/AnyܵE!JbG*/JZX?3ҹO;OCBp`D8or[Lf5~V;>QqJD>C\K7]A-aoy@]
"
;vsHH'&!zXX5gԞNpCMN14^4xF~Fe21)^p?#fJZRԙ1]顕j3R%i5!̐?B{WJ-sva{>Z i9O?W'+ӼQJ0]zLBVQ=>J}FS*)ƉFZ5˨Vj	p4 ]!n
s Ds43Q:pӞ#
'N%;g_ = .2I_Y-,VH>{LBg6ep;kJ W"u.#|
]H(PڰFtoQ,VXSTfAápuN\[;olBMEhZة>g6	%ؑY$h0ggyX$^TDVÅ b$RrIh;,J>`i9  P*NJ}׌.GBei:㳙CB01Z[-OL|9uG̘1G\~;]kLCSYbz	ɪ:QRnNH_X> ҇BB),l}U1ƙ[	jV]Ҥ]/?ϝ8i	~%I7モl4Ub5˨5Q7Sߣ;{ȅ0N|v4-]$eq2\Ni%bd.3] @8m@n|7\9+إ29e9?G-n@@RHTlI[RV
w=bCA9MVꐗ#bPƝ&bf.A@c5Iؚ=>,/eM|ဌb7dI~ЌӦ^@5p|n`LZ AŦ*C}d.y<5PU=kR,5D«2+g/
G32
S}r.qnƬ(^*pٍ9=\<,Q?"|p)+Fkrxo>.|4߅Ad
)S:ƦI|*Έ qGs6;^O~+r.uD 뻐%WCAQTیuրW3egչ+HD))0:&pLNt~NmyFyOs[
`\ky;h_e0@.ӿx9?f`/Z^}WBHRo7z`@Q4ΆбLwl_7^=t=SUZ7HGqgEGJ}9R cjB<TMB>=)Ĝl
#=v~xqvwoDk(k.	@@ºk!}!HZ;wg_8}Vܯpt>׵>x4G;r>p<8"d4\:~FB/PGbfUޓJi8ۆݹuM5|35.axnoX0f1K 4?szRG|{GgjCB*:m6H}Wu{ˁ6֒B-yC=Jۼ;&[8ի4|rq^9pH/U`mP<=cxOAX^kC]MIh'P?LqAC`S6ħR_h fA
tL2jXBZ`͘piDlJALxfˮ ѺԘUА13CO9Ka|{۾Tz%E"˫T*7Cxvi2Vd9'a=zˣVIx F:x-
i
!p;m/Yp|x(~B%W~FA)1S~?E4=KR0j*^FR0*9GHgPR
ArX㲁xkҽ쯎 [q-E%C!PL4"zڲ\̛_L #e"քDWTSҁP)ǥ
`Uo~گ9,O`g 
^O&WK50<0Ħ]oGp
+
*HEL b5pdL_RӥJ`wDcCl <lVs'`abpH؃Y"⺽~p.|T0?(CҌYdTcؙkMC ba2xGMxؚ6HF""v Gh]~lK$n(Lbn$E-ѐpoaT3'frIal4;%ՆWEQj+i"\6u2O,G>n%-u'w8_iJqXl0kD>%K>gg^Қ(a
󬬔H΂l#*~)e,3L],.p`v:W62|]ţ^J+qXrJŰ/ab
`ݰZ<TVb;oßv	^Ї@IoCeW\c7
/-dǶ.}.GKweO?}pr60lzov>|tyֵB׭Tupm_%mzcNE(OD}˹8%ٛ	/VaMr8Ǌ
,3R,w_V^Xk a'VZ,CL{TpU"2vh{^scS*1b#OQCmxf.{@(*Fz孷A6/Vfp'wG`)gI	%[?hN}Do.ۇ̡cܴm}J'cy 
*2u=/6uX8hklleTŏP7h:xXhxQƯKh
:a׈~RF%
6.x0Fsu.VltOa.`Epv:VvqdE&;HpYs`Pk3$7LXʎ&x9ݾJR35\zMphg>0[Ġ[JNMyFYԏO
fNȼ믨Zwb!;;kԜ9_]Բ?RpD,V]Zn6yA;SkWi` @]!teKm&N̈ tpTڄ?D!~mR+u&
Z9"O
 "FBM&AJ&PDzP_ N"ce`:PK'`.
c YDDg:1JjrQU	yH"6_zH7caO2is+szDm^uK~
I\JlذSG8ӧQW}{Jޠ9Q-ry!pF}F  KA
P}%#2mW2cMK~??X͈gf63F{/CxU~hx_D0	D/(g[~=jGօFtZ.;NX8)˞93DkkpHα6A#}w{{Nޚ@gDvYv,[a%ģ5	;nPs;sZ(xpѐ+uG4߇s>=%s8Vo~Q:Ot?5'f=tgt%_4-9\GpOϒE7s0HuL cW@B T]n yKfm-1V|u+fÏ'76g#wv7
/F)ˇ/Nw'gH\Ǩ^_9]>3OPh4\Jnx IA4]:2p97i4TzYSFMa,qXKAJ9%+dDFرDBFt(LF_2du"ၝE9*D\5A5ЌoaZwmۛF^wLꛆScX6K+5gffgUߛvKsn1Qδƚ*L'S]+ ~)WOK%W'-3YP-
VhU<įV-"aO_*}3nȽ]\g=tr	?|[s*Z9	7ݶwͥp|xbhd}-P*vsӋ+I4dʢ|ciS;<|ʊ}帤F9}4d^v
dy֨ A2
-d8ߒS80DeDo[Ā=9io4gpìi5߾L^d)LX&s7tsX5KIՃ<7seajEo9'F^1#L9>kGYܝf^LMR_gSduvmySgOOgr[SFL8JFQx
u6ʆez>z7Ʊ 1ɰ]5CքяҡLؤMf)7&\
Cʓ'kyD=X!. MXuutpsر^oS*qT8l{%zT
TOmػj:D.[>*VRnBU~Q{ڞy&W(Z
ɮvk:	(R,P(
5\T:%E5k2U::fgR޳!Гd8m/St=Z
`I;BVUafte	0)/p!cUJƧ7ŀ=d!]3iu+*4ƀ3s$\(RgEmpX7yLCZQgin^Rvzi{U{|*͖::+wiEHaWq9UuOQQ =>mLi\@WicUu`̶V^eL?UITch|58rTVRmSTQ+Ř~cՎ%p"覫!VS`D/\d߄[Vy!UEd[ [Fص¨ACV<4m,i)C;wf\Nr+K\ ֊lmN}W͠޸0Ӯra#2uSǼT!z؊?n+ks~WV_Ww>ҁɅRSI?;|Tɢqj5"#kU++A14rFty+INy0MYcXpdW>q++Zbmbilˊ]m`AZ^Lޒ|Xb"ku~pt8Bfx>[&cf0{
] 3̟y~&H3P|m][`7TGYrfn,kfx/oK_
*{t@2#g=/{Lg5S?(lK?òc!_03	γ%

ɰRO׎-Smr;<ɪ)1Xɫl̊%"a 	ΘG՞v'bXZȝ܉l fm"&}GPX9{ΰ&ߐRasfW1^|q4t؍Dӻ'w'wTREdji}GU7c..}!.zsEmj1ݐ=0Z,SqK+J,q& ʹV
)A{07Ы.B,=1ydq޼΅mIƣ*?	2|*0VB'G!$hBVa{( HeRzq#.O b{o2E+RGqaaalZRJ-[~[ٗV-Tl"C",zw0gѬJƩ7+fg<ǅo*p RGoҟ&%c^~[$[⑩.wػ<Gwąu	aDZ.n&EuFC~L_3ϐv5䙾/\!̫zBkhy8! GJR^ό*_4>Sk6A\6nLz#UCر-Wwa
HII? 2Pj&%vsh1[M	ћr%݈$wHd~A7ś? WaºG~*|M^nYRo^zzj=#[ۀC^WbHRo0
sdy46~ZC7{Ɨsݳǟn8d]IU֝{6NJgnys]7,m9F7	|s湟3i/峹7fe6ʏz&1>+aK ;i
c*kپm۞Ρѕs0HzBτ	=gWVOR>#9~Vs#ynIUMR<}H$ո6K.^P}M̓XO__,!0rI]^H@Ld\LӤ)5mb<OJDF:ya/,%׿v#!oS 
؋KnbiBq}c׈丣&v龖V^p%BڹLYLLH|FN
F	;9d3Y	o#Ab玲I$^9J ^oZ*E_|D$_k562ƩLmȟxm n_ɱ;'.6~ģJ%Eg/E5E.Ìsn8ڗv	tDxr礟/j;QRnʋ$;O6^G EzYg&UuBWY{o3Ac5YY"q.SF/MegH4N^3\ m:.z69lPPi}ViDTy7 `k(\fs9H&RvPi*@h^߼N5kpWV>IV-ZP+B35p%oNਟqoD
6q+uVhYᔅёBVӊ*bKh.8̲6_^ddyԠԘ]B"ђ),i37ܿM:_i~X@,-Ѭ,}pa<28<|{ޝʰ~Ő;,j^-@d.=4cj
u
V%]8 })Ϸ$'*K	X1l8HH̛J41E!gy,U=U=M5账zGV!=G?l^3B_nevMIYdkۖg5:ñlfpl\Cl;>mJ_$\?7wj=zŊq
}Lx	{oFQ.j.ZM]ImnvQ{eW`el|cΑJJbLsIR0)-
;UM*C*.T]<
z]ʗu@VޗSޕ53J'Grd),ꁪaWwiְ]"Fs-aאbJ:Dr1I'.J	]-[|:j6"yFvju/cYx|P/Aޡ\(.]VH!O6qrqGvX?$K
q3̘&丣߹|d:dnI&.BZzb@&[1㹞~_OG>բh^Q|w4]`]w`増s^toǿLψu)VBlNux$V6}y qc<$^GVM)$Ue_y[ń$`xK)J_Sn@6zD霘1-=F]` P{7>0!Mzm)?7?yi
XyUUêVl9U5Qy,4(/5\}?o&,{w)3]:~@}.m@k&^I'%ŏqi%O(5LA١zjq ~q
U@JXg[_REJrbrֿ|v e4LECލf?_^r9 -R7~'rfna@S4S`@4z9Me`(x$[vrQ
p
AW_v.L1@!Cd/;)̡X?x{;T?Vvavՠ8mrqFߦt>_A?P5(~N{'\:o_\zʬc<%}[J5<<_yR6$kj~FLt ɦqNDrÄ{ x!E :0r D8ҡhWaY[pq.pQrFv:
:&!=QΊPXǠ&e":آ}0hԺA
oU{6:+D޷32-my,ͿH[>`PPtQZ8f	:gAQV*)Bȃ&1 ^o)*kVy,Z/XV˸EJ?mN+gjGlч|}kC_s&`4l-B!W;ZmH5ƿ+qJ(l9@gQY9O2]:jXڠUPRbTyq[T|,1%g2WZBbhuaI,{bA1٪DP놜z|$X>tBwʞNjaNn6~,
KڠuXh}y=HЂh$ATgwLa엪͏1axr
Jt<&5Q)`6/4M%gooj,
Z
cMZpLh֩gGdW a75Ł"֨VFm:jYhڴi6͛q4eMݰn1Bt\T1Ux;$1HkhbĄЏH1S[.sKګ d:I J,
~~=8pӬٻddx
&%b(Ns
ZFsE=Xx-9FTx
ʡ6usJnԬxO* (^Ffа4JH۷}wI@-mR硢',(1&^D
+1/J_i^F"5<MҍKѾ05J@c "f
jW.Z1m Ҵm^d
oJ)m[_sE
}/of+~`P]q)H׾xEgo륾ᝁ Fi <]4d+>P0c#ۜzw/]=s@+ܳ<4-#Hw4fEEixk!+T-
m5_Vq&[A)fӆ5,(>,_mW`
Ђv9t͛Eos84*O{lӧo	LjF/x^ý^&SP8>A&::ف V7C3!D6d !X|y:E_%7gk]&TmcVO#P_3k*"_/o>|1r'X>ҧ/%Hyӳ>Zj4һT@hnu/~LyCaaU4Wi@~dyGZqi$ݥ9pC@&sr<>K1ѿK; JD,~t&<gOvL;^ICJ=^FmB}dC,~PxG2?XVD~h"^?]n(52?(8wL31[HEl7?+G(6}[0)ư4
Ak߄b؝kŊuXU#)V7ŃDet[ٙ>@84 -9Z.n}:Εz #dh!
ǥkO[:!]Y)
tdOr rvP2+2*TEڄUjPBwKΘ
=|Ǥ<3n魠*ڿfMhsX>WgON'$u7tAұA qh͌̇D0'*&40<BXFFV}oq|߻Gg^äkשGNrJws`ϏUL:J^	ck@ }ߓM$?t^"YSN[yļ +]p}LFY>HCAqpyM?x	MzA
>Dm7r)y蒾V͍l1ύ"wm_\s	ɬ?=OMfR5UCԫ{GeHa[y
=sD RUW%Rd1'=uR(/_  9ܺַI
"%;0ݎb+MG`p\{?sX΁RKV7M3y>
sh)wdcyt\̌m7x5~ngl4mpѨ!k	ԣIdBG4CBs5COYbjo۰8=vMa./lnMqfJ,ias2`0:{Y),fs~vAtT12?+E1VhcO=B@U
Xy$c9h
hׂU
ׇL_CAkHq>yJ--?I'<TJ#2v$d1h0Y!}=nbJ0dN݊Tl_9V9Jkm{\n.ӡ&GTAB0fsfX
|,c:k;u>CvFގsZLW T   xc`d```a<=|Ed<Wnvb|F
``b   d# xc`d``c8"Ȁi+ {
 xVKkAy<,5VIL,E"E"'sjJU3U=ߴK>Փg_(ETu=O'{?<c|u>Law]+tw^nD.}kzՇ쯍U}ɩo9:΋;FШO;XSB[xe#2UoاC??✼	9Xz{w>	O3E*De[=픖wE:seI5oÞR݇G=SBPs|W+Ⱥ	}[0l]1V~ٴFoMr;'O^gLyhol7/ӌrq3}=vCCHF=ǡv@ilr.r4CүVldV¬L[eN0WԿoϓiosWwz:zQYY3RyK >?+#B|Jzj6]@UD-Pv>n໌u;WOMeFYг\l@*!u?'m
'18>wCÚ\fMc}~5lmo,.}Yr[Kf\yBGyoC[
|EE@
\}d<z/
|x{TgN.iBdb!3iMe$׹4M='4ri!e}Nҿ1H6dHAT8T*
HGJ%K^
2	RYHYRyr*УBTq"(*ѯTDSTuT-Iz
jpE/ N:R]ɕWgKnl 7wSGG{oxDJ=é	=Ż7,5w0@N386C&9^5;J-H~i	>j^+zO Pu//wR+=q v@GSLLgr<IRB]<1ugfO|E_P;apK\?Gǁ\ Ti5s܇}8Ap	O?ϿR KrQ's?YOw1IN 0EQ
S9?'0iOdo911ٜXŲXb9s?84Q+q>_:KຈLzK[w˘Y<r4/G
f+*$pV{"r
krZu1	nIxc7lFzmdfL-ڂ[9[L6i{[G
wo:wSo3܇j?R镊 :g&
>u:sGXK˟Qt8;<O$'|
ON2Ltgkuo\G|؋輄]書竬_×hιȆ
o2[ݢw qLg<D#r='|J~>Sg9^гo //
ͯX{]\Fel)HXLeJdJ98+eJ{Ȕ*.-ox2|@6tL%7@l^@(ceETq>%SIN-bυ!.ꎔqeϕʸȸxP!F ލ4I2^	2ެ7%wi$<_L+8;гuLX-@2A<;@c'83tAC.3=P&p(ۓ 1oO)2	yee0ڇdeNdgF{uBpoqD>*3	s3=6(T~G77L4YCٜ9񜋮z]%q	ϖ1TeWe}(=drwsoWse
\迎$H}nEc2pϘoKS
}woZ{/o?9w*z %
އaa/G|<lO0!rQ&</P3\wDpˆ
n܂-?3u>wywᘋɀu}є<m󐼇O~p)>kyg{ü,Ǜuβ<d-rJXl	Kl<ٲNXs̖'[JlOY'٪բe&ٚ ]%R6$ʺ˾C| ĺ=m0C֝5,wd=x6V\YO(J?(m,!~Sr~n S%Z@6 meюp@~et  xc`d``:$ɠ L@`> (M xjAƿݤMk`RADݴ7?MhbW6;I&avk_@+@Uo'cBMH7g<dE	,p?-QvZ^SJr	/gp}oyw/xGY:wLƜle>[.1[.bq-	uyזK輵mwfyx~bbЇ1BL IvQK^Ik&LŽD0fb`0(JfRMdDI/DK1Z`*tMƬ d.do<UڨUڴMr;gzpXmk'F}FUF]=j;௲Ki"bD.xB$dy&_jQ>º\ՒO-9"ZmWj\DI滎SidIΩ+Щ})dG»2']ZJZrl$;2VznM"L4R+_
ek=~^^8D9yWy1E&ϋx}WtȲuUb'X̔ؖ,O`ݶ5- 0̏1}̰Ls~N$ݾ}oW))L?nJ].ucԭRn4d 90
X	ư	l
l	[ְ
	`{v`gv`w`o1P	`8 `8VL¡pGp
p'ppgp
Pzj4Fj-hClX
]p}p5C !D0 ·B.KR.+J
kZF	n[V
n;N{^AxGQx'Ix
gYxEx	^WUx3
o;.x7!0	$|
>
"_/W5:|	߂ow=> ~?O39~	_o~?+
¿/0bpXaQ\qčpc7psĭpk߄v=;N3n;{^7c	XAMN~?Ax0p
qgP<#H<
cX<D<):xgxX:6
[ڸ`袇k Cpqq-x^x^Wx^x^7xތxގwxލxޏ>>O>>/f|߆ow;]n|߇Ca|
?ŏI~?E|_ƯWku~w{}?ƟOgso
?/W?_JQ2i
TaQZFihcڄ6hsڂhkچDv=@;N3Bn;A{^7CST!LM~?@At0BhifP:#H:cX:D:NST:N3L:Φs\ydQ$E-jSlZM]rG}rɣ5S@!E4G@ΧB.KR.+JkZFn[Vn;N{^AzGQz'IzgYzEz^WcAv#(ot?StZ~Ayb:
nN/vj DUϝS۫|\QHn
vr3ot<ϦjCҾk5|lIuw9ba
G10竖N^O踍nXouܾ

sTSM!ˮnSV\ShKѳn~mX=[ڡ؍bZGNXv3Y_sT+N
_L:>WGAhӲo{	NwG[VCɩrs#_e=oNgy5YVS&ufLD T^n5iY|^~Hˡg<Mp\e|8~}ЉgҝZ0nA'DAMQ},&&9#k"G8
T?ሆ%b`*ԭi;4Uv##r{"g9rpnYb)wWyFc5p@
~;~=W~o\XljUXW;GY=W*{L;b*?!+,a^CW~l_b$Cerb2}N_crߥZLmzH؉z*LdIrZ8$1%'rq~͙e΋oko9lqB~ɽbm3C=A&pc'D˛t	p~l2s6K)74RrbCBe\܊dDdEzG`$`C!HUv;ɄV
Qy3CuV87'F^Z2ٺ8BP#
YJOb^:TAΧVgvq~A]vxvg(PwTk78G;y7b@q@5T>s;'MI#I3>+7A:p}=[|y-N*y.orJqQYX;(Ck8>koqDWpd5E=qunk6t$z"cÎ|١(S	cJ)0.Geɔq:-#$Y=f
f-YVtyXKhQ]ԗH
e_`~(5TAFֱ<b=.ow
I3љwfw3ł|0˗8-	/Ona.%e/$է<0"/h܈C3e9ibį9;8$"G!HJaWkdqIf)HǶI_({ڵrvj(N2f-iMj&Pd>Qĳhr&|`DC 	{nA9YH61G&Ύm/%	iźAJcO wtCŗ^l4b&ψ8WV/g|%%Y]%Ԯ{M>ɏ63Y
8Tcx7V.M\7r8G
6CpWlЋcS\Ha/r6z#^`ޑ5,Q!^ߴ]&h#*ZL>K,GҧK\w>5]-2䖠qRs#?Xb9Vq-ˎJK!	<=
"4sύ=qWv/TKkXedI$9GM7\@&SJ5H⁚+C%)RVU)&E}Uc|8L
h,]M
hR@dVui(KQIf)EU	)4>&<и+RRb\kӵJ+	$J+	$0, ʂ(	gu!в1tmZ&akEX+V4tV
!6dZC@2dȐ0a
zhL@fϻ?PUTTPUT*4US^nHKhĄ EE|Q_TEE|QĤ &!L
bnb܊BLa)$EYU)&)K2!0XKb	C,aIIHJ3bC`1!f03bC`	_FYeA!0ʂ" DzC7DzC7DzC7*0!!!!!!! LA)S,z.sK"!UAT!"!"!"!"!"!"!"!"1)DC"JU۴41kƙ")қ:&]2XbB
3Kooooooooof)Uzu]uYzRWzB׃VzJӺlROi);y4ҼSwJNi);y4ҼSWҴּӚwZNki;y5ּӚwZNkiͫckIҌѼ3WGؒ;yg4Ѽ3wFhY;yg5ռwVΊS&5&դtVj	                                                                                                                                                                                                                                                                                                                8                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           m    __fentry__                                              9[    __x86_return_thunk                                      R1^    kmem_cache_create                                           kmem_cache_destroy                                      ]{    __SCT__might_resched                                    R+3^    __var_waitqueue                                         I]    init_wait_var_entry                                     Q     schedule                                                &    prepare_to_wait_event                                   T    finish_wait                                             V
    __stack_chk_fail                                        -    kmalloc_caches                                          wX    kmalloc_trace                                           E:#    __kmalloc                                                V    register_shrinker                                       z    kfree                                                   L3)F    __preempt_count                                         "'    kmem_cache_free                                         ,H    __SCT__preempt_schedule                                 d    _raw_spin_lock                                          UrS    __list_del_entry_valid                                  h    __list_add_valid                                        4K    _raw_spin_unlock                                        Qs    __SCT__cond_resched                                     x    kmem_cache_alloc                                        Ӆ3-    system_wq                                               6    queue_work_on                                               wake_up_var                                             NM    unregister_shrinker                                     zR    module_layout                                                   x           C           	                                                                                                                                                    mbcache                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  GCC: (Debian 12.2.0-14+deb12u1) 12.2.0  GCC: (Debian 12.2.0-14+deb12u1) 12.2.0                        A=     R=    a=   8   p= `       w=      = ]      = *      kY     @  = -     =      = k      =    @   =       = *     = `      =      =     = Q           Z        Y =       >    p         
      [ >    _       
[ )>    5>    a E>      \>    R2        
     [ }p     j>    e z>    R2        
      [ <%  \ >    h       
\   [   *   ^  -   >    j >    j       
\   [ <%  \ >    m       
\   [   *   >    o       
\   [ <%  \   *   ?    q       
    <%  \ ?    s )?    h       
     [   X     *   ^  -   ?? K   H?    v        
9             x              
y MBE_REFERENCED_B MBE_REUSABLE_B mb_cache_entry e_list e_hash_list e_refcnt e_key e_value mb_cache c_hash c_bucket_bits c_max_entries c_list_lock c_list c_entry_count c_shrink c_shrink_work mbcache_exit mbcache_init mb_cache_destroy bucket_bits mb_cache_create mb_cache_shrink_worker mb_cache_scan mb_cache_shrink mb_cache_count mb_cache_entry_touch mb_cache_entry_delete_or_get mb_cache_entry_get mb_cache_entry_find_next mb_cache_entry_find_first __entry_find mb_cache_entry_wait_unused __mb_cache_entry_free reusable mb_cache_entry_create    mbcache.ko  ilR                                                                                                                     $                                                            9      $               Z                     z                                                                                                                                  b       	       4                  A    k              Z    w       	       p           
                  <                                     $                                                    	 $                                   /     -               Q    	                 q     .                    I                   	 l                    J                    d               ,    	 <               P     e               s     ~                   	 H                                                           	 T                                   ;                    d    	 0                                                           	 `                                                            	                :                    U                    r    	                                              5          $                                                                               :                                                     7       9    C              O                   {                                            1               *           *         "                                                                                                 d                                                                                                       +                     :                     E            5                                               Q                     b                     v                                                                                                         |                 [                  n                                                            6    	                                                           0      E          p                                  &                     4                     E                     O                     d                     s                                              	      
        __crc_mb_cache_entry_create __crc___mb_cache_entry_free __crc_mb_cache_entry_wait_unused __crc_mb_cache_entry_find_first __crc_mb_cache_entry_find_next __crc_mb_cache_entry_get __crc_mb_cache_entry_delete_or_get __crc_mb_cache_entry_touch __crc_mb_cache_create __crc_mb_cache_destroy __UNIQUE_ID_depends193 ____versions __UNIQUE_ID_retpoline192 __UNIQUE_ID_intree191 __UNIQUE_ID_name190 __UNIQUE_ID_vermagic189 _note_10 _note_9 __kstrtab_mb_cache_entry_create __kstrtabns_mb_cache_entry_create __ksymtab_mb_cache_entry_create __kstrtab___mb_cache_entry_free __kstrtabns___mb_cache_entry_free __ksymtab___mb_cache_entry_free __kstrtab_mb_cache_entry_wait_unused __kstrtabns_mb_cache_entry_wait_unused __ksymtab_mb_cache_entry_wait_unused __kstrtab_mb_cache_entry_find_first __kstrtabns_mb_cache_entry_find_first __ksymtab_mb_cache_entry_find_first __kstrtab_mb_cache_entry_find_next __kstrtabns_mb_cache_entry_find_next __ksymtab_mb_cache_entry_find_next __kstrtab_mb_cache_entry_get __kstrtabns_mb_cache_entry_get __ksymtab_mb_cache_entry_get __kstrtab_mb_cache_entry_delete_or_get __kstrtabns_mb_cache_entry_delete_or_get __ksymtab_mb_cache_entry_delete_or_get __kstrtab_mb_cache_entry_touch __kstrtabns_mb_cache_entry_touch __ksymtab_mb_cache_entry_touch __kstrtab_mb_cache_create __kstrtabns_mb_cache_create __ksymtab_mb_cache_create __kstrtab_mb_cache_destroy __kstrtabns_mb_cache_destroy __ksymtab_mb_cache_destroy mb_cache_count mbcache_init mb_entry_cache mbcache_exit mb_cache_scan mb_cache_shrink_worker mb_cache_shrink __entry_find __UNIQUE_ID_license243 __UNIQUE_ID_description242 __UNIQUE_ID_author241 __UNIQUE_ID___addressable_cleanup_module240 __UNIQUE_ID___addressable_init_module239 .LC2 .LC3 __this_module queue_work_on __SCT__preempt_schedule finish_wait cleanup_module kfree prepare_to_wait_event kmem_cache_create _raw_spin_lock __fentry__ init_module __stack_chk_fail unregister_shrinker kmem_cache_alloc init_wait_var_entry __var_waitqueue __list_add_valid kmem_cache_free __list_del_entry_valid wake_up_var __x86_return_thunk __preempt_count __SCT__cond_resched kmalloc_trace _raw_spin_unlock __kmalloc __SCT__might_resched kmalloc_caches kmem_cache_destroy system_wq           Q             b             Q             b   !          Q   A          j   f          b   r          Y             X             T             M             I             U             Q             k                    g   E         i   ~                                                                 K                              b            L            L            Q             c   m         c   v            {         [            c            H            H            Q            P            _   '         Z   l         _            h            ]            f            P            h            b            Q            Q   1         Q   j            q         W            c            c                        [   .         b   Z         c   r         P            Z            h            b            ]            m            G             `   )         c   @         H   [         H   e         H            Q            V            _   *         ]   >         L   P         L   U         `   q         Q            c            c            b            H            b            c            H   !         Q   -         e   R         b   ^         P   n         _            h            ]            b            Q            c   O	         c   w	         b   	         ]   	         b   	         `   	         c   	         H   	         H   	         Q   	         Q             Q                                  O   '             1          b                          l              ]                                                               E                     0                     1                     \                     3                      4           $          d           (                     ,                     0          ^           4          *           8          +           <          a           @          !           D          "           H          n           L          $           P          %           T          e           X          '           \          (           `          S           d          -           h          .           l          N           p                     t                                                                                                                              (                    0                   8                   @                   H             0      P                   X             p      `                    h                   p             	      x             	                                        $                                      ]                                                                                                      $                   (             @      ,                   0             ;	      4             c	                    
                                        e                                                          -                                                                $             Q      (                   ,             v	      0             	      4             0                                                                                                                           '                    (                    )                     0       $             a       (             b       ,             c       0             e       4             j       8                    <                    @                    D                    H                    L                   P                   T                   X                   \                   `                    d                   h                   l                   p                   t                   x                   |                                                                                                                                                                                                                                                                                                          "                   0                   7                   <                   A                   E                   H                   &                   '                   )                   +                   -                   2                                                                                                                                    u                                                                                                                                     $            F      (            G      ,            I      0            K      4            M      8            O      <            T      @            b      D            p      H                  L                   P            '      T            (      X            ,      \            N      `            O      d            Q      h            V      l                  p                  t                  x                  |                                                v	                  {	                  	                  	                  	                  	                  	                  	                  	                                      5                                                                            C                        =                C                        ^                D                      J                      R                         @                 j                                        H                                        H                                        f                         ?      $          H           (             Z      ,          H           0             d      4          H           8                   <          H           @             
      D          H           H             	      L          H           P             	      T          H           8         R           P         J            .symtab .strtab .shstrtab .note.gnu.build-id .note.Linux .rela.text .rela.init.text .rela.exit.text .rela__ksymtab __kcrctab __ksymtab_strings .rela__mcount_loc .rela.smp_locks .rodata.str1.1 .modinfo .rela.return_sites .orc_unwind .rela.orc_unwind_ip __versions .rela__bug_table .rela.exit.data .rela.init.data .rela.static_call_sites .rela.gnu.linkonce.this_module .bss .comment .note.GNU-stack .BTF .gnu_debuglink                                                                                            @       $                              .                     d       <                              ?                            	                             :      @               6      	      )                    J                     }
      5                              E      @               x@      x       )                    Z                     
                                    U      @               @      0       )                    j                     
      x                              e      @                A            )   	                 t                     8      (                              ~      2               `                                                        J                                          @               C            )   
                                            8                                    @               pE      P      )                          2               
      >                                                  B
                                                               8                                    @               F      P      )                                         C                                                                                                 @               H      P
      )                                                                             
                           $                                   @               `R             )                    3                    D                                                         H                                         @               R             )                    .                    P                                    )     @               S             )                    >                    X      X                              9     @                S            )                     V                                        @               Q     @               0U      0       )   "                 p                    @                                    u     0               @      P                             ~                                                                                     M                                                  #                                                          #      h
      *   E                 	                      X.                                                         `U                                   0	*H
01
0	`He0	*H
1a0]080 10UDebian Secure Boot CA2(oe:B&C0	`He0
	*H
  :l1>$cQQ8X-ddԛc.iNi|
%"0_u0ƷBaJI4oT\YeMK[iҩ,o"`wjp`k/i0`bn
[Z%Xq/XNSzS4U?mKWv`lA]C}5^$$\>: 2/`~EpޚiϒG`$eJ58         ~Module signature appended~
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     ssh-server, standard
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           #!/usr/bin/perl
    eval 'exec /usr/bin/perl -S $0 ${1+"$@"}'
	if 0; # ^ Run only under a shell
#!/usr/bin/perl -w

BEGIN { pop @INC if $INC[-1] eq '.' }
use strict;
use IO::File;
use ExtUtils::Packlist;
use ExtUtils::Installed;

use vars qw($Inst @Modules);


=head1 NAME

instmodsh - A shell to examine installed modules

=head1 SYNOPSIS

    instmodsh

=head1 DESCRIPTION

A little interface to ExtUtils::Installed to examine locally* installed modules,
validate your packlists and even create a tarball from an installed module.

*On Debian system, B<core> and B<vendor> modules are managed by C<dpkg>.

=head1 SEE ALSO

ExtUtils::Installed

=cut


my $Module_Help = <<EOF;
Available commands are:
   f [all|prog|doc]   - List installed files of a given type
   d [all|prog|doc]   - List the directories used by a module
   v                  - Validate the .packlist - check for missing files
   t <tarfile>        - Create a tar archive of the module
   h                  - Display module help
   q                  - Quit the module
EOF

my %Module_Commands = (
                       f => \&list_installed,
                       d => \&list_directories,
                       v => \&validate_packlist,
                       t => \&create_archive,
                       h => \&module_help,
                      );

sub do_module($) {
    my ($module) = @_;

    print($Module_Help);
    MODULE_CMD: while (1) {
        print("$module cmd? ");

        my $reply = <STDIN>; chomp($reply);
        my($cmd) = $reply =~ /^(\w)\b/;

        last if $cmd eq 'q';

        if( $Module_Commands{$cmd} ) {
            $Module_Commands{$cmd}->($reply, $module);
        }
        elsif( $cmd eq 'q' ) {
            last MODULE_CMD;
        }
        else {
            module_help();
        }
    }
}


sub list_installed {
    my($reply, $module) = @_;

    my $class = (split(' ', $reply))[1];
    $class = 'all' unless $class;

    my @files;
    if (eval { @files = $Inst->files($module, $class); }) {
        print("$class files in $module are:\n   ",
              join("\n   ", @files), "\n");
    }
    else {
        print($@);
    }
};


sub list_directories {
    my($reply, $module) = @_;

    my $class = (split(' ', $reply))[1];
    $class = 'all' unless $class;

    my @dirs;
    if (eval { @dirs = $Inst->directories($module, $class); }) {
        print("$class directories in $module are:\n   ",
              join("\n   ", @dirs), "\n");
    }
    else {
        print($@);
    }
}


sub create_archive {
    my($reply, $module) = @_;

    my $file = (split(' ', $reply))[1];

    if( !(defined $file and length $file) ) {
        print "No tar file specified\n";
    }
    elsif( eval { require Archive::Tar } ) {
        Archive::Tar->create_archive($file, 0, $Inst->files($module));
    }
    else {
        my($first, @rest) = $Inst->files($module);
        system('tar', 'cvf', $file, $first);
        for my $f (@rest) {
            system('tar', 'rvf', $file, $f);
        }
        print "Can't use tar\n" if $?;
    }
}


sub validate_packlist {
    my($reply, $module) = @_;

    if (my @missing = $Inst->validate($module)) {
        print("Files missing from $module are:\n   ",
              join("\n   ", @missing), "\n");
    }
    else {
        print("$module has no missing files\n");
    }
}

sub module_help {
    print $Module_Help;
}



##############################################################################

sub toplevel()
{
my $help = <<EOF;
Available commands are:
   l            - List all installed modules
   m <module>   - Select a module
   q            - Quit the program
EOF
print($help);
while (1)
   {
   print("cmd? ");
   my $reply = <STDIN>; chomp($reply);
   CASE:
      {
      $reply eq 'l' and do
         {
         print("Installed modules are:\n   ", join("\n   ", @Modules), "\n");
         last CASE;
         };
      $reply =~ /^m\s+/ and do
         {
         do_module((split(' ', $reply))[1]);
         last CASE;
         };
      $reply eq 'q' and do
         {
         exit(0);
         };
      # Default
         print($help);
      }
   }
}


###############################################################################

$Inst = ExtUtils::Installed->new();
@Modules = $Inst->modules();
toplevel();

###############################################################################
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           #!/usr/bin/perl
    eval 'exec perl -S $0 "$@"'
        if 0;
#############################################################################
# podchecker -- command to invoke the podchecker function in Pod::Checker
#
# Copyright (c) 1998-2000 by Bradford Appleton. All rights reserved.
# This is free software; you can redistribute it and/or modify it under the
# same terms as Perl itself.
#############################################################################

use strict;
#use diagnostics;

=head1 NAME

podchecker - check the syntax of POD format documentation files

=head1 SYNOPSIS

B<podchecker> [B<-help>] [B<-man>] [B<-(no)warnings>] [I<file>S< >...]

=head1 OPTIONS AND ARGUMENTS

=over 8

=item B<-help>

Print a brief help message and exit.

=item B<-man>

Print the manual page and exit.

=item B<-warnings> B<-nowarnings>

Turn on/off printing of warnings. Repeating B<-warnings> increases the
warning level, i.e. more warnings are printed. Currently increasing to
level two causes flagging of unescaped "E<lt>,E<gt>" characters.

=item I<file>

The pathname of a POD file to syntax-check (defaults to standard input).

=back

=head1 DESCRIPTION

B<podchecker> will read the given input files looking for POD
syntax errors in the POD documentation and will print any errors
it find to STDERR. At the end, it will print a status message
indicating the number of errors found.

Directories are ignored, an appropriate warning message is printed.

B<podchecker> invokes the B<podchecker()> function exported by B<Pod::Checker>
Please see L<Pod::Checker/podchecker()> for more details.

=head1 RETURN VALUE

B<podchecker> returns a 0 (zero) exit status if all specified
POD files are ok.

=head1 ERRORS

B<podchecker> returns the exit status 1 if at least one of
the given POD files has syntax errors.

The status 2 indicates that at least one of the specified 
files does not contain I<any> POD commands.

Status 1 overrides status 2. If you want unambiguous
results, call B<podchecker> with one single argument only.

=head1 SEE ALSO

L<Pod::Simple> and L<Pod::Checker>

=head1 AUTHORS

Please report bugs using L<http://rt.cpan.org>.

Brad Appleton E<lt>bradapp@enteract.comE<gt>,
Marek Rouchal E<lt>marekr@cpan.orgE<gt>

Based on code for B<Pod::Text::pod2text(1)> written by
Tom Christiansen E<lt>tchrist@mox.perl.comE<gt>

=cut


use Pod::Checker;
use Pod::Usage;
use Getopt::Long;

## Define options
my %options;

## Parse options
GetOptions(\%options, qw(help man warnings+ nowarnings))  ||  pod2usage(2);
pod2usage(1)  if ($options{help});
pod2usage(-verbose => 2)  if ($options{man});

if($options{nowarnings}) {
  $options{warnings} = 0;
}
elsif(!defined $options{warnings}) {
  $options{warnings} = 1; # default is warnings on
}

## Dont default to STDIN if connected to a terminal
pod2usage(2) if ((@ARGV == 0) && (-t STDIN));

## Invoke podchecker()
my $status = 0;
@ARGV = qw(-) unless(@ARGV);
for my $podfile (@ARGV) {
    if($podfile eq '-') {
      $podfile = '<&STDIN';
    }
    elsif(-d $podfile) {
      warn "podchecker: Warning: Ignoring directory '$podfile'\n";
      next;
    }
    my $errors =
      podchecker($podfile, undef, '-warnings' => $options{warnings});
    if($errors > 0) {
        # errors occurred
        $status = 1;
        printf STDERR ("%s has %d pod syntax %s.\n",
          $podfile, $errors,
          ($errors == 1) ? 'error' : 'errors');
    }
    elsif($errors < 0) {
        # no pod found
        $status = 2 unless($status);
        print STDERR "$podfile does not contain any pod commands.\n";
    }
    else {
        print STDERR "$podfile pod syntax OK.\n";
    }
}
exit $status;

                                                                                                                                                                                                                                                                                                                                                                                                                                                      