/**
* This class represents a list of openpgp packets.
* Take care when iterating over it - the packets themselves
* are stored as numerical indices.
* @requires enums
* @requires packet
* @requires packet/packet
* @module packet/packetlist
*/
module.exports = Packetlist;
var packetParser = require('./packet.js'),
packets = require('./all_packets.js'),
enums = require('../enums.js');
/**
* @constructor
*/
function Packetlist() {
/** The number of packets contained within the list.
* @readonly
* @type {Integer} */
this.length = 0;
}
/**
* Reads a stream of binary data and interprents it as a list of packets.
* @param {String} A binary string of bytes.
*/
Packetlist.prototype.read = function (bytes) {
var i = 0;
while (i < bytes.length) {
var parsed = packetParser.read(bytes, i, bytes.length - i);
i = parsed.offset;
var tag = enums.read(enums.packet, parsed.tag);
var packet = packets.newPacketFromTag(tag);
this.push(packet);
packet.read(parsed.packet);
}
};
/**
* Creates a binary representation of openpgp objects contained within the
* class instance.
* @returns {String} A binary string of bytes containing valid openpgp packets.
*/
Packetlist.prototype.write = function () {
var bytes = '';
for (var i = 0; i < this.length; i++) {
var packetbytes = this[i].write();
bytes += packetParser.writeHeader(this[i].tag, packetbytes.length);
bytes += packetbytes;
}
return bytes;
};
/**
* Adds a packet to the list. This is the only supported method of doing so;
* writing to packetlist[i] directly will result in an error.
*/
Packetlist.prototype.push = function (packet) {
if (!packet) return;
packet.packets = packet.packets || new Packetlist();
this[this.length] = packet;
this.length++;
};
/**
* Creates a new PacketList with all packets that pass the test implemented by the provided function.
*/
Packetlist.prototype.filter = function (callback) {
var filtered = new Packetlist();
for (var i = 0; i < this.length; i++) {
if (callback(this[i], i, this)) {
filtered.push(this[i]);
}
}
return filtered;
};
/**
* Creates a new PacketList with all packets from the given types
*/
Packetlist.prototype.filterByTag = function () {
var args = Array.prototype.slice.call(arguments);
var filtered = new Packetlist();
var that = this;
for (var i = 0; i < this.length; i++) {
if (args.some(function(packetType) {return that[i].tag == packetType;})) {
filtered.push(this[i]);
}
}
return filtered;
};
/**
* Executes the provided callback once for each element
*/
Packetlist.prototype.forEach = function (callback) {
for (var i = 0; i < this.length; i++) {
callback(this[i]);
}
};
/**
* Traverses packet tree and returns first matching packet
* @param {module:enums.packet} type The packet type
* @return {module:packet/packet|null}
*/
Packetlist.prototype.findPacket = function (type) {
var packetlist = this.filterByTag(type);
if (packetlist.length) {
return packetlist[0];
} else {
var found = null;
for (var i = 0; i < this.length; i++) {
if (this[i].packets.length) {
found = this[i].packets.findPacket(type);
if (found) return found;
}
}
}
return null;
};
/**
* Returns array of found indices by tag
*/
Packetlist.prototype.indexOfTag = function () {
var args = Array.prototype.slice.call(arguments);
var tagIndex = [];
var that = this;
for (var i = 0; i < this.length; i++) {
if (args.some(function(packetType) {return that[i].tag == packetType;})) {
tagIndex.push(i);
}
}
return tagIndex;
};
/**
* Returns slice of packetlist
*/
Packetlist.prototype.slice = function (begin, end) {
if (!end) {
end = this.length;
}
var part = new Packetlist();
for (var i = begin; i < end; i++) {
part.push(this[i]);
}
return part;
};
/**
* Concatenates packetlist or array of packets
*/
Packetlist.prototype.concat = function (packetlist) {
if (packetlist) {
for (var i = 0; i < packetlist.length; i++) {
this.push(packetlist[i]);
}
}
};