// Generated by IcedCoffeeScript 1.7.1-e
(function() {
  var Encoder, Message, Parser, bufeq_fast, clearsign, compute_crc24, crc24_to_base64, crc_table, decode, encode, formatCheckSum, getCheckSum, katch, make_line, strip, trim, uint_to_buffer, verifyCheckSum, _ref;

  _ref = require('./util'), trim = _ref.trim, strip = _ref.strip, katch = _ref.katch, bufeq_fast = _ref.bufeq_fast, uint_to_buffer = _ref.uint_to_buffer;

  make_line = function(x) {
    if (x == null) {
      x = "";
    }
    return "" + x + "\n";
  };

  exports.Encoder = Encoder = (function() {
    function Encoder(C) {
      this.C = C;
    }

    Encoder.prototype.frame = function(t) {
      var dash, i;
      dash = ((function() {
        var _i, _results;
        _results = [];
        for (i = _i = 0; _i < 5; i = ++_i) {
          _results.push("-");
        }
        return _results;
      })()).join('');
      return {
        begin: make_line(dash + ("BEGIN PGP " + t) + dash),
        end: make_line(dash + ("END PGP " + t) + dash)
      };
    };

    Encoder.prototype.b64e = function(d) {
      var end, i, parts, raw, w;
      raw = d.toString('base64');
      w = 64;
      parts = (function() {
        var _i, _ref1, _results;
        _results = [];
        for (i = _i = 0, _ref1 = raw.length; w > 0 ? _i < _ref1 : _i > _ref1; i = _i += w) {
          end = Math.min(i + w, raw.length);
          _results.push(raw.slice(i, end));
        }
        return _results;
      })();
      return make_line(parts.join("\n"));
    };

    Encoder.prototype.header = function() {
      var x;
      return ((function() {
        var _i, _len, _ref1, _results;
        _ref1 = ["Version: " + this.C.header.version, "Comment: " + this.C.header.comment];
        _results = [];
        for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
          x = _ref1[_i];
          _results.push(make_line(x));
        }
        return _results;
      }).call(this)).join('');
    };

    Encoder.prototype.encode = function(type, data) {
      var f;
      f = this.frame(type);
      return f.begin.concat(this.header(), make_line(), this.b64e(data), formatCheckSum(data), f.end);
    };

    return Encoder;

  })();

  exports.clearsign_header = clearsign = function(C, data, hasher_name) {
    var enc, f;
    enc = new Encoder(C);
    f = enc.frame("SIGNED MESSAGE").begin;
    return f.concat(make_line("Hash: " + hasher_name), make_line(), data);
  };

  exports.encode = encode = function(C, type, data) {
    return (new Encoder(C)).encode(type, data);
  };

  crc_table = [0x00000000, 0x00864cfb, 0x018ad50d, 0x010c99f6, 0x0393e6e1, 0x0315aa1a, 0x021933ec, 0x029f7f17, 0x07a18139, 0x0727cdc2, 0x062b5434, 0x06ad18cf, 0x043267d8, 0x04b42b23, 0x05b8b2d5, 0x053efe2e, 0x0fc54e89, 0x0f430272, 0x0e4f9b84, 0x0ec9d77f, 0x0c56a868, 0x0cd0e493, 0x0ddc7d65, 0x0d5a319e, 0x0864cfb0, 0x08e2834b, 0x09ee1abd, 0x09685646, 0x0bf72951, 0x0b7165aa, 0x0a7dfc5c, 0x0afbb0a7, 0x1f0cd1e9, 0x1f8a9d12, 0x1e8604e4, 0x1e00481f, 0x1c9f3708, 0x1c197bf3, 0x1d15e205, 0x1d93aefe, 0x18ad50d0, 0x182b1c2b, 0x192785dd, 0x19a1c926, 0x1b3eb631, 0x1bb8faca, 0x1ab4633c, 0x1a322fc7, 0x10c99f60, 0x104fd39b, 0x11434a6d, 0x11c50696, 0x135a7981, 0x13dc357a, 0x12d0ac8c, 0x1256e077, 0x17681e59, 0x17ee52a2, 0x16e2cb54, 0x166487af, 0x14fbf8b8, 0x147db443, 0x15712db5, 0x15f7614e, 0x3e19a3d2, 0x3e9fef29, 0x3f9376df, 0x3f153a24, 0x3d8a4533, 0x3d0c09c8, 0x3c00903e, 0x3c86dcc5, 0x39b822eb, 0x393e6e10, 0x3832f7e6, 0x38b4bb1d, 0x3a2bc40a, 0x3aad88f1, 0x3ba11107, 0x3b275dfc, 0x31dced5b, 0x315aa1a0, 0x30563856, 0x30d074ad, 0x324f0bba, 0x32c94741, 0x33c5deb7, 0x3343924c, 0x367d6c62, 0x36fb2099, 0x37f7b96f, 0x3771f594, 0x35ee8a83, 0x3568c678, 0x34645f8e, 0x34e21375, 0x2115723b, 0x21933ec0, 0x209fa736, 0x2019ebcd, 0x228694da, 0x2200d821, 0x230c41d7, 0x238a0d2c, 0x26b4f302, 0x2632bff9, 0x273e260f, 0x27b86af4, 0x252715e3, 0x25a15918, 0x24adc0ee, 0x242b8c15, 0x2ed03cb2, 0x2e567049, 0x2f5ae9bf, 0x2fdca544, 0x2d43da53, 0x2dc596a8, 0x2cc90f5e, 0x2c4f43a5, 0x2971bd8b, 0x29f7f170, 0x28fb6886, 0x287d247d, 0x2ae25b6a, 0x2a641791, 0x2b688e67, 0x2beec29c, 0x7c3347a4, 0x7cb50b5f, 0x7db992a9, 0x7d3fde52, 0x7fa0a145, 0x7f26edbe, 0x7e2a7448, 0x7eac38b3, 0x7b92c69d, 0x7b148a66, 0x7a181390, 0x7a9e5f6b, 0x7801207c, 0x78876c87, 0x798bf571, 0x790db98a, 0x73f6092d, 0x737045d6, 0x727cdc20, 0x72fa90db, 0x7065efcc, 0x70e3a337, 0x71ef3ac1, 0x7169763a, 0x74578814, 0x74d1c4ef, 0x75dd5d19, 0x755b11e2, 0x77c46ef5, 0x7742220e, 0x764ebbf8, 0x76c8f703, 0x633f964d, 0x63b9dab6, 0x62b54340, 0x62330fbb, 0x60ac70ac, 0x602a3c57, 0x6126a5a1, 0x61a0e95a, 0x649e1774, 0x64185b8f, 0x6514c279, 0x65928e82, 0x670df195, 0x678bbd6e, 0x66872498, 0x66016863, 0x6cfad8c4, 0x6c7c943f, 0x6d700dc9, 0x6df64132, 0x6f693e25, 0x6fef72de, 0x6ee3eb28, 0x6e65a7d3, 0x6b5b59fd, 0x6bdd1506, 0x6ad18cf0, 0x6a57c00b, 0x68c8bf1c, 0x684ef3e7, 0x69426a11, 0x69c426ea, 0x422ae476, 0x42aca88d, 0x43a0317b, 0x43267d80, 0x41b90297, 0x413f4e6c, 0x4033d79a, 0x40b59b61, 0x458b654f, 0x450d29b4, 0x4401b042, 0x4487fcb9, 0x461883ae, 0x469ecf55, 0x479256a3, 0x47141a58, 0x4defaaff, 0x4d69e604, 0x4c657ff2, 0x4ce33309, 0x4e7c4c1e, 0x4efa00e5, 0x4ff69913, 0x4f70d5e8, 0x4a4e2bc6, 0x4ac8673d, 0x4bc4fecb, 0x4b42b230, 0x49ddcd27, 0x495b81dc, 0x4857182a, 0x48d154d1, 0x5d26359f, 0x5da07964, 0x5cace092, 0x5c2aac69, 0x5eb5d37e, 0x5e339f85, 0x5f3f0673, 0x5fb94a88, 0x5a87b4a6, 0x5a01f85d, 0x5b0d61ab, 0x5b8b2d50, 0x59145247, 0x59921ebc, 0x589e874a, 0x5818cbb1, 0x52e37b16, 0x526537ed, 0x5369ae1b, 0x53efe2e0, 0x51709df7, 0x51f6d10c, 0x50fa48fa, 0x507c0401, 0x5542fa2f, 0x55c4b6d4, 0x54c82f22, 0x544e63d9, 0x56d11cce, 0x56575035, 0x575bc9c3, 0x57dd8538];

  compute_crc24 = function(input, initval) {
    var crc, index, j, _i, _ref1;
    if (initval == null) {
      initval = 0xB704CE;
    }
    crc = initval;
    index = 0;
    while ((input.length - index) > 16) {
      crc = (crc << 8) ^ crc_table[((crc >> 16) ^ input.readUInt8(index + 0)) & 0xff];
      crc = (crc << 8) ^ crc_table[((crc >> 16) ^ input.readUInt8(index + 1)) & 0xff];
      crc = (crc << 8) ^ crc_table[((crc >> 16) ^ input.readUInt8(index + 2)) & 0xff];
      crc = (crc << 8) ^ crc_table[((crc >> 16) ^ input.readUInt8(index + 3)) & 0xff];
      crc = (crc << 8) ^ crc_table[((crc >> 16) ^ input.readUInt8(index + 4)) & 0xff];
      crc = (crc << 8) ^ crc_table[((crc >> 16) ^ input.readUInt8(index + 5)) & 0xff];
      crc = (crc << 8) ^ crc_table[((crc >> 16) ^ input.readUInt8(index + 6)) & 0xff];
      crc = (crc << 8) ^ crc_table[((crc >> 16) ^ input.readUInt8(index + 7)) & 0xff];
      crc = (crc << 8) ^ crc_table[((crc >> 16) ^ input.readUInt8(index + 8)) & 0xff];
      crc = (crc << 8) ^ crc_table[((crc >> 16) ^ input.readUInt8(index + 9)) & 0xff];
      crc = (crc << 8) ^ crc_table[((crc >> 16) ^ input.readUInt8(index + 10)) & 0xff];
      crc = (crc << 8) ^ crc_table[((crc >> 16) ^ input.readUInt8(index + 11)) & 0xff];
      crc = (crc << 8) ^ crc_table[((crc >> 16) ^ input.readUInt8(index + 12)) & 0xff];
      crc = (crc << 8) ^ crc_table[((crc >> 16) ^ input.readUInt8(index + 13)) & 0xff];
      crc = (crc << 8) ^ crc_table[((crc >> 16) ^ input.readUInt8(index + 14)) & 0xff];
      crc = (crc << 8) ^ crc_table[((crc >> 16) ^ input.readUInt8(index + 15)) & 0xff];
      index += 16;
    }
    for (j = _i = index, _ref1 = input.length; index <= _ref1 ? _i < _ref1 : _i > _ref1; j = index <= _ref1 ? ++_i : --_i) {
      crc = (crc << 8) ^ crc_table[((crc >> 16) ^ input.readUInt8(index++)) & 0xff];
    }
    return crc & 0xffffff;
  };

  getCheckSum = function(data, pad) {
    var ret;
    ret = crc24_to_base64(compute_crc24(data), pad);
    return ret;
  };

  exports.crc24_to_base64 = crc24_to_base64 = function(c, pad) {
    if (pad == null) {
      pad = true;
    }
    return (pad ? "=" : '') + uint_to_buffer(32, c).slice(1, 4).toString('base64');
  };

  formatCheckSum = function(data) {
    return make_line(getCheckSum(data, true));
  };

  verifyCheckSum = function(data, checksum) {
    return getCheckSum(data, false) === checksum;
  };

  exports.Message = Message = (function() {
    function Message(_arg) {
      this.body = _arg.body, this.type = _arg.type, this.comment = _arg.comment, this.version = _arg.version, this.pre = _arg.pre, this.post = _arg.post;
      this.lines = [];
      this.fields = {};
      this.payload = null;
    }

    Message.prototype.unsplit = function(lines) {
      return lines.join("\n");
    };

    Message.prototype.raw = function() {
      return this.unsplit(this.lines);
    };

    Message.prototype.finish_unframe = function(_arg) {
      var post, pre;
      pre = _arg.pre, post = _arg.post;
      this.pre = this.unsplit(pre);
      this.post = this.unsplit(post);
      if (this.clearsign != null) {
        return this.clearsign.body = this.unsplit(this.clearsign.lines);
      }
    };

    Message.prototype.make_clearsign = function() {
      return this.clearsign = {
        headers: {},
        lines: [],
        body: null
      };
    };

    return Message;

  })();

  exports.Parser = Parser = (function() {
    function Parser(data) {
      this.init(data);
    }

    Parser.prototype.init = function(data) {
      this.data = Buffer.isBuffer(data) ? data.toString('utf8') : data;
      this.lines = this.data.split(/\r?\n/);
      this.checksum = null;
      this.body = null;
      this.type = null;
      this.ret = null;
      return this.last_type = null;
    };

    Parser.prototype.parse = function() {
      this.ret = new Message({});
      this.unframe();
      this.pop_headers();
      this.parse_type();
      this.strip_empties_in_footer();
      this.trim_lines();
      this.find_checksum();
      this.read_body();
      this.check_checksum();
      return this.ret;
    };

    Parser.prototype.parse_type = function() {
      return this.ret.type = this.ret.fields.type = this.type;
    };

    Parser.prototype.last_line = function() {
      return this.payload.slice(-1)[0];
    };

    Parser.prototype.mparse = function() {
      var go, obj, out;
      out = [];
      go = true;
      while (go) {
        this.skip();
        if (this.lines.length) {
          obj = this.parse();
          out.push(obj);
          this.init(obj.post);
        } else {
          go = false;
        }
      }
      return out;
    };

    Parser.prototype.skip = function() {
      var _results;
      _results = [];
      while (this.lines.length) {
        if (this.lines[0].match(/\S+/)) {
          break;
        }
        _results.push(this.lines.shift());
      }
      return _results;
    };

    Parser.prototype.read_body = function() {
      var dat;
      this.ret.payload = this.payload.join("\n");
      dat = this.payload.join('');
      return this.ret.body = new Buffer(dat, 'base64');
    };

    Parser.prototype.check_checksum = function() {
      this.ret.fields.checksum = this.checksum;
      if ((this.checksum != null) && !verifyCheckSum(this.ret.body, this.checksum)) {
        throw new Error("checksum mismatch");
      }
    };

    Parser.prototype.pop_headers = function() {
      var l, m, _results;
      _results = [];
      while (this.payload.length) {
        l = this.payload.shift();
        if ((m = l.match(/Version: (.*)/))) {
          _results.push(this.ret.version = m[1]);
        } else if ((m = l.match(/Comment: (.*)/)) != null) {
          _results.push(this.ret.comment = m[1]);
        } else if ((l == null) || (l.length === 0) || (l.match(/^\s+$/))) {
          break;
        } else {
          _results.push(void 0);
        }
      }
      return _results;
    };

    Parser.prototype.strip_empties_in_footer = function() {
      var _ref1, _results;
      _results = [];
      while ((_ref1 = this.last_line()) != null ? _ref1.match(/^\s*$/) : void 0) {
        _results.push(this.payload.pop());
      }
      return _results;
    };

    Parser.prototype.trim_lines = function() {
      var p;
      return this.payload = (function() {
        var _i, _len, _ref1, _results;
        _ref1 = this.payload;
        _results = [];
        for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
          p = _ref1[_i];
          _results.push(trim(p));
        }
        return _results;
      }).call(this);
    };

    Parser.prototype.find_checksum = function() {
      var l;
      if (((l = this.last_line()) != null) && l[0] === '=') {
        return this.checksum = this.payload.pop().slice(1);
      }
    };

    Parser.prototype.v_unframe = function(pre) {
      return true;
    };

    Parser.prototype.unframe = function() {
      var found_pre, found_pre_clearsign, found_pre_std, go, line, m, payload, post, pre, ret, rxx, rxx_b, rxx_e, stage, type;
      rxx_b = /^(-{5}BEGIN PGP (.*?)-{5}.*$)/;
      rxx_e = /^(-{5}END PGP (.*?)-{5})(.*)$/m;
      rxx = rxx_b;
      payload = [];
      stage = 0;
      type = null;
      ret = null;
      go = true;
      pre = [];
      post = [];
      found_pre_std = function(l, is_last) {
        return pre.push(l);
      };
      found_pre_clearsign = (function(_this) {
        return function(l, is_last) {
          return _this.ret.clearsign.lines.push(l);
        };
      })(this);
      found_pre = found_pre_std;
      while (this.lines.length && go) {
        line = this.lines.shift();
        switch (stage) {
          case -1:
            if ((m = line.match(/^([^:]+): (.*)$/))) {
              this.ret.clearsign.headers[m[1].toLowerCase()] = m[2];
            } else if (line.match(/^\s*$/)) {
              stage++;
              found_pre = found_pre_clearsign;
            } else {
              throw new Error("Bad line in clearsign header");
            }
            this.ret.lines.push(line);
            break;
          case 0:
            if ((m = line.match(rxx_b)) != null) {
              found_pre('', true);
              this.ret.lines.push((this.ret.clearsign ? line : m[1]));
              if (this.type == null) {
                this.type = m[2];
              }
              this.last_type = m[2];
              if (m[2] === "SIGNED MESSAGE") {
                stage--;
                this.ret.make_clearsign();
              } else {
                stage++;
              }
            } else {
              if (this.ret.clearsign) {
                this.ret.lines.push(line);
              }
              found_pre(line, false);
            }
            break;
          case 1:
            if ((m = line.match(rxx_e))) {
              this.ret.lines.push(m[1]);
              if (m[2] !== this.last_type) {
                throw new Error("type mismatch -- begin " + this.last_type + " w/ end " + m[1]);
              }
              stage++;
              post = [m[3]].concat(this.lines);
              this.lines = [];
              go = false;
            } else {
              this.ret.lines.push(line);
              payload.push(line);
            }
        }
      }
      if (stage === 0) {
        throw new Error("no header found");
      } else if (stage === 1) {
        throw new Error("no tailer found");
      } else {
        this.payload = payload;
        return this.ret.finish_unframe({
          pre: pre,
          post: post
        });
      }
    };

    return Parser;

  })();

  exports.decode = decode = function(data) {
    return katch(function() {
      return (new Parser(data)).parse();
    });
  };

  exports.mdecode = decode = function(data) {
    return katch(function() {
      return (new Parser(data)).mparse();
    });
  };

  exports.compute_crc24 = compute_crc24;

}).call(this);
