| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638 | 'use strict';const Reporter = require('../base/reporter').Reporter;const EncoderBuffer = require('../base/buffer').EncoderBuffer;const DecoderBuffer = require('../base/buffer').DecoderBuffer;const assert = require('minimalistic-assert');// Supported tagsconst tags = [  'seq', 'seqof', 'set', 'setof', 'objid', 'bool',  'gentime', 'utctime', 'null_', 'enum', 'int', 'objDesc',  'bitstr', 'bmpstr', 'charstr', 'genstr', 'graphstr', 'ia5str', 'iso646str',  'numstr', 'octstr', 'printstr', 't61str', 'unistr', 'utf8str', 'videostr'];// Public methods listconst methods = [  'key', 'obj', 'use', 'optional', 'explicit', 'implicit', 'def', 'choice',  'any', 'contains'].concat(tags);// Overrided methods listconst overrided = [  '_peekTag', '_decodeTag', '_use',  '_decodeStr', '_decodeObjid', '_decodeTime',  '_decodeNull', '_decodeInt', '_decodeBool', '_decodeList',  '_encodeComposite', '_encodeStr', '_encodeObjid', '_encodeTime',  '_encodeNull', '_encodeInt', '_encodeBool'];function Node(enc, parent, name) {  const state = {};  this._baseState = state;  state.name = name;  state.enc = enc;  state.parent = parent || null;  state.children = null;  // State  state.tag = null;  state.args = null;  state.reverseArgs = null;  state.choice = null;  state.optional = false;  state.any = false;  state.obj = false;  state.use = null;  state.useDecoder = null;  state.key = null;  state['default'] = null;  state.explicit = null;  state.implicit = null;  state.contains = null;  // Should create new instance on each method  if (!state.parent) {    state.children = [];    this._wrap();  }}module.exports = Node;const stateProps = [  'enc', 'parent', 'children', 'tag', 'args', 'reverseArgs', 'choice',  'optional', 'any', 'obj', 'use', 'alteredUse', 'key', 'default', 'explicit',  'implicit', 'contains'];Node.prototype.clone = function clone() {  const state = this._baseState;  const cstate = {};  stateProps.forEach(function(prop) {    cstate[prop] = state[prop];  });  const res = new this.constructor(cstate.parent);  res._baseState = cstate;  return res;};Node.prototype._wrap = function wrap() {  const state = this._baseState;  methods.forEach(function(method) {    this[method] = function _wrappedMethod() {      const clone = new this.constructor(this);      state.children.push(clone);      return clone[method].apply(clone, arguments);    };  }, this);};Node.prototype._init = function init(body) {  const state = this._baseState;  assert(state.parent === null);  body.call(this);  // Filter children  state.children = state.children.filter(function(child) {    return child._baseState.parent === this;  }, this);  assert.equal(state.children.length, 1, 'Root node can have only one child');};Node.prototype._useArgs = function useArgs(args) {  const state = this._baseState;  // Filter children and args  const children = args.filter(function(arg) {    return arg instanceof this.constructor;  }, this);  args = args.filter(function(arg) {    return !(arg instanceof this.constructor);  }, this);  if (children.length !== 0) {    assert(state.children === null);    state.children = children;    // Replace parent to maintain backward link    children.forEach(function(child) {      child._baseState.parent = this;    }, this);  }  if (args.length !== 0) {    assert(state.args === null);    state.args = args;    state.reverseArgs = args.map(function(arg) {      if (typeof arg !== 'object' || arg.constructor !== Object)        return arg;      const res = {};      Object.keys(arg).forEach(function(key) {        if (key == (key | 0))          key |= 0;        const value = arg[key];        res[value] = key;      });      return res;    });  }};//// Overrided methods//overrided.forEach(function(method) {  Node.prototype[method] = function _overrided() {    const state = this._baseState;    throw new Error(method + ' not implemented for encoding: ' + state.enc);  };});//// Public methods//tags.forEach(function(tag) {  Node.prototype[tag] = function _tagMethod() {    const state = this._baseState;    const args = Array.prototype.slice.call(arguments);    assert(state.tag === null);    state.tag = tag;    this._useArgs(args);    return this;  };});Node.prototype.use = function use(item) {  assert(item);  const state = this._baseState;  assert(state.use === null);  state.use = item;  return this;};Node.prototype.optional = function optional() {  const state = this._baseState;  state.optional = true;  return this;};Node.prototype.def = function def(val) {  const state = this._baseState;  assert(state['default'] === null);  state['default'] = val;  state.optional = true;  return this;};Node.prototype.explicit = function explicit(num) {  const state = this._baseState;  assert(state.explicit === null && state.implicit === null);  state.explicit = num;  return this;};Node.prototype.implicit = function implicit(num) {  const state = this._baseState;  assert(state.explicit === null && state.implicit === null);  state.implicit = num;  return this;};Node.prototype.obj = function obj() {  const state = this._baseState;  const args = Array.prototype.slice.call(arguments);  state.obj = true;  if (args.length !== 0)    this._useArgs(args);  return this;};Node.prototype.key = function key(newKey) {  const state = this._baseState;  assert(state.key === null);  state.key = newKey;  return this;};Node.prototype.any = function any() {  const state = this._baseState;  state.any = true;  return this;};Node.prototype.choice = function choice(obj) {  const state = this._baseState;  assert(state.choice === null);  state.choice = obj;  this._useArgs(Object.keys(obj).map(function(key) {    return obj[key];  }));  return this;};Node.prototype.contains = function contains(item) {  const state = this._baseState;  assert(state.use === null);  state.contains = item;  return this;};//// Decoding//Node.prototype._decode = function decode(input, options) {  const state = this._baseState;  // Decode root node  if (state.parent === null)    return input.wrapResult(state.children[0]._decode(input, options));  let result = state['default'];  let present = true;  let prevKey = null;  if (state.key !== null)    prevKey = input.enterKey(state.key);  // Check if tag is there  if (state.optional) {    let tag = null;    if (state.explicit !== null)      tag = state.explicit;    else if (state.implicit !== null)      tag = state.implicit;    else if (state.tag !== null)      tag = state.tag;    if (tag === null && !state.any) {      // Trial and Error      const save = input.save();      try {        if (state.choice === null)          this._decodeGeneric(state.tag, input, options);        else          this._decodeChoice(input, options);        present = true;      } catch (e) {        present = false;      }      input.restore(save);    } else {      present = this._peekTag(input, tag, state.any);      if (input.isError(present))        return present;    }  }  // Push object on stack  let prevObj;  if (state.obj && present)    prevObj = input.enterObject();  if (present) {    // Unwrap explicit values    if (state.explicit !== null) {      const explicit = this._decodeTag(input, state.explicit);      if (input.isError(explicit))        return explicit;      input = explicit;    }    const start = input.offset;    // Unwrap implicit and normal values    if (state.use === null && state.choice === null) {      let save;      if (state.any)        save = input.save();      const body = this._decodeTag(        input,        state.implicit !== null ? state.implicit : state.tag,        state.any      );      if (input.isError(body))        return body;      if (state.any)        result = input.raw(save);      else        input = body;    }    if (options && options.track && state.tag !== null)      options.track(input.path(), start, input.length, 'tagged');    if (options && options.track && state.tag !== null)      options.track(input.path(), input.offset, input.length, 'content');    // Select proper method for tag    if (state.any) {      // no-op    } else if (state.choice === null) {      result = this._decodeGeneric(state.tag, input, options);    } else {      result = this._decodeChoice(input, options);    }    if (input.isError(result))      return result;    // Decode children    if (!state.any && state.choice === null && state.children !== null) {      state.children.forEach(function decodeChildren(child) {        // NOTE: We are ignoring errors here, to let parser continue with other        // parts of encoded data        child._decode(input, options);      });    }    // Decode contained/encoded by schema, only in bit or octet strings    if (state.contains && (state.tag === 'octstr' || state.tag === 'bitstr')) {      const data = new DecoderBuffer(result);      result = this._getUse(state.contains, input._reporterState.obj)        ._decode(data, options);    }  }  // Pop object  if (state.obj && present)    result = input.leaveObject(prevObj);  // Set key  if (state.key !== null && (result !== null || present === true))    input.leaveKey(prevKey, state.key, result);  else if (prevKey !== null)    input.exitKey(prevKey);  return result;};Node.prototype._decodeGeneric = function decodeGeneric(tag, input, options) {  const state = this._baseState;  if (tag === 'seq' || tag === 'set')    return null;  if (tag === 'seqof' || tag === 'setof')    return this._decodeList(input, tag, state.args[0], options);  else if (/str$/.test(tag))    return this._decodeStr(input, tag, options);  else if (tag === 'objid' && state.args)    return this._decodeObjid(input, state.args[0], state.args[1], options);  else if (tag === 'objid')    return this._decodeObjid(input, null, null, options);  else if (tag === 'gentime' || tag === 'utctime')    return this._decodeTime(input, tag, options);  else if (tag === 'null_')    return this._decodeNull(input, options);  else if (tag === 'bool')    return this._decodeBool(input, options);  else if (tag === 'objDesc')    return this._decodeStr(input, tag, options);  else if (tag === 'int' || tag === 'enum')    return this._decodeInt(input, state.args && state.args[0], options);  if (state.use !== null) {    return this._getUse(state.use, input._reporterState.obj)      ._decode(input, options);  } else {    return input.error('unknown tag: ' + tag);  }};Node.prototype._getUse = function _getUse(entity, obj) {  const state = this._baseState;  // Create altered use decoder if implicit is set  state.useDecoder = this._use(entity, obj);  assert(state.useDecoder._baseState.parent === null);  state.useDecoder = state.useDecoder._baseState.children[0];  if (state.implicit !== state.useDecoder._baseState.implicit) {    state.useDecoder = state.useDecoder.clone();    state.useDecoder._baseState.implicit = state.implicit;  }  return state.useDecoder;};Node.prototype._decodeChoice = function decodeChoice(input, options) {  const state = this._baseState;  let result = null;  let match = false;  Object.keys(state.choice).some(function(key) {    const save = input.save();    const node = state.choice[key];    try {      const value = node._decode(input, options);      if (input.isError(value))        return false;      result = { type: key, value: value };      match = true;    } catch (e) {      input.restore(save);      return false;    }    return true;  }, this);  if (!match)    return input.error('Choice not matched');  return result;};//// Encoding//Node.prototype._createEncoderBuffer = function createEncoderBuffer(data) {  return new EncoderBuffer(data, this.reporter);};Node.prototype._encode = function encode(data, reporter, parent) {  const state = this._baseState;  if (state['default'] !== null && state['default'] === data)    return;  const result = this._encodeValue(data, reporter, parent);  if (result === undefined)    return;  if (this._skipDefault(result, reporter, parent))    return;  return result;};Node.prototype._encodeValue = function encode(data, reporter, parent) {  const state = this._baseState;  // Decode root node  if (state.parent === null)    return state.children[0]._encode(data, reporter || new Reporter());  let result = null;  // Set reporter to share it with a child class  this.reporter = reporter;  // Check if data is there  if (state.optional && data === undefined) {    if (state['default'] !== null)      data = state['default'];    else      return;  }  // Encode children first  let content = null;  let primitive = false;  if (state.any) {    // Anything that was given is translated to buffer    result = this._createEncoderBuffer(data);  } else if (state.choice) {    result = this._encodeChoice(data, reporter);  } else if (state.contains) {    content = this._getUse(state.contains, parent)._encode(data, reporter);    primitive = true;  } else if (state.children) {    content = state.children.map(function(child) {      if (child._baseState.tag === 'null_')        return child._encode(null, reporter, data);      if (child._baseState.key === null)        return reporter.error('Child should have a key');      const prevKey = reporter.enterKey(child._baseState.key);      if (typeof data !== 'object')        return reporter.error('Child expected, but input is not object');      const res = child._encode(data[child._baseState.key], reporter, data);      reporter.leaveKey(prevKey);      return res;    }, this).filter(function(child) {      return child;    });    content = this._createEncoderBuffer(content);  } else {    if (state.tag === 'seqof' || state.tag === 'setof') {      // TODO(indutny): this should be thrown on DSL level      if (!(state.args && state.args.length === 1))        return reporter.error('Too many args for : ' + state.tag);      if (!Array.isArray(data))        return reporter.error('seqof/setof, but data is not Array');      const child = this.clone();      child._baseState.implicit = null;      content = this._createEncoderBuffer(data.map(function(item) {        const state = this._baseState;        return this._getUse(state.args[0], data)._encode(item, reporter);      }, child));    } else if (state.use !== null) {      result = this._getUse(state.use, parent)._encode(data, reporter);    } else {      content = this._encodePrimitive(state.tag, data);      primitive = true;    }  }  // Encode data itself  if (!state.any && state.choice === null) {    const tag = state.implicit !== null ? state.implicit : state.tag;    const cls = state.implicit === null ? 'universal' : 'context';    if (tag === null) {      if (state.use === null)        reporter.error('Tag could be omitted only for .use()');    } else {      if (state.use === null)        result = this._encodeComposite(tag, primitive, cls, content);    }  }  // Wrap in explicit  if (state.explicit !== null)    result = this._encodeComposite(state.explicit, false, 'context', result);  return result;};Node.prototype._encodeChoice = function encodeChoice(data, reporter) {  const state = this._baseState;  const node = state.choice[data.type];  if (!node) {    assert(      false,      data.type + ' not found in ' +            JSON.stringify(Object.keys(state.choice)));  }  return node._encode(data.value, reporter);};Node.prototype._encodePrimitive = function encodePrimitive(tag, data) {  const state = this._baseState;  if (/str$/.test(tag))    return this._encodeStr(data, tag);  else if (tag === 'objid' && state.args)    return this._encodeObjid(data, state.reverseArgs[0], state.args[1]);  else if (tag === 'objid')    return this._encodeObjid(data, null, null);  else if (tag === 'gentime' || tag === 'utctime')    return this._encodeTime(data, tag);  else if (tag === 'null_')    return this._encodeNull();  else if (tag === 'int' || tag === 'enum')    return this._encodeInt(data, state.args && state.reverseArgs[0]);  else if (tag === 'bool')    return this._encodeBool(data);  else if (tag === 'objDesc')    return this._encodeStr(data, tag);  else    throw new Error('Unsupported tag: ' + tag);};Node.prototype._isNumstr = function isNumstr(str) {  return /^[0-9 ]*$/.test(str);};Node.prototype._isPrintstr = function isPrintstr(str) {  return /^[A-Za-z0-9 '()+,-./:=?]*$/.test(str);};
 |