Skip to content

Commit da17090

Browse files
author
whatsapp
committed
feat: protocol
1 parent 50f1e29 commit da17090

11 files changed

+3632
-0
lines changed

src/index.js

Whitespace-only changes.

src/lib/libsignal/index.js

Whitespace-only changes.

src/protocol/4.0/index.js

+1,268
Large diffs are not rendered by default.

src/protocol/5.2/index.js

+1,276
Large diffs are not rendered by default.

src/protocol/ProtocolTreeNode.js

+205
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
const WriteEncoder = require('./encoder');
2+
const logger = require('../logger');
3+
4+
class ProtocolTreeNode {
5+
constructor(tag, attributes = null, children = null, data = null) {
6+
this.tag = tag;
7+
this.attributes = attributes || {};
8+
this.children = children || [];
9+
this.data = data;
10+
Object.keys(this.attributes).forEach(key => {
11+
if (this.attributes[key] === undefined || this.attributes[key] === null) {
12+
delete this.attributes[key];
13+
}
14+
});
15+
this.children = this.children.map(child => {
16+
return new ProtocolTreeNode(child.tag, child.attributes, child.children, child.data);
17+
});
18+
}
19+
20+
toBuffer() {
21+
try {
22+
const bytes = new WriteEncoder().protocolTreeNodeToBytes(this);
23+
// console.log('bytes', this.tag, this.attributes, Buffer.from(bytes).toString('hex'));
24+
return Buffer.from(bytes);
25+
} catch (e) {
26+
logger.info(`Node 序列化 buffer 失败${e.message}`);
27+
logger.info(`Node 序列化 buffer 失败${e.stack}`);
28+
}
29+
}
30+
31+
toString() {
32+
let out = `<${this.tag}`;
33+
if (this.attributes) {
34+
Object.keys(this.attributes).forEach(key => {
35+
const val = this.attributes[key];
36+
out += ` ${key}="${val}"`;
37+
});
38+
}
39+
out += '>\n';
40+
if (this.data) {
41+
if (this.data instanceof Buffer) {
42+
out += this.data.toString('utf8');
43+
} else {
44+
try {
45+
out += this.data;
46+
} catch (e) {
47+
try {
48+
out += this.data.toString();
49+
} catch (err) {
50+
out += this.data.toString('hex');
51+
}
52+
}
53+
}
54+
out += `\nHEX3:${Buffer.from(this.data || '').toString('hex')}\n`;
55+
}
56+
57+
for (let i = 0; i < this.children.length; i++) {
58+
const c = this.children[i];
59+
try {
60+
out += c.toString();
61+
} catch (e) {
62+
console.error(e);
63+
out += '[ENCODED DATA]\n';
64+
}
65+
}
66+
out += `</${this.tag}>\n`;
67+
return out;
68+
}
69+
70+
toJSON() {
71+
const json = {
72+
tag: this.tag,
73+
props: this.attributes,
74+
children: [],
75+
};
76+
Object.keys(this.attributes).forEach(key => {
77+
let value = this.attributes[key];
78+
if (value instanceof Buffer) {
79+
value = value.toString('utf8');
80+
this.attributes[key] = value;
81+
}
82+
});
83+
if (this.data !== null) {
84+
json.data = this.data;
85+
// if (json.data instanceof Buffer) {
86+
// json.data = json.data.toString('utf8');
87+
// }
88+
}
89+
for (let i = 0; i < this.children.length; i++) {
90+
const c = this.children[i];
91+
try {
92+
json.children.push(c.toJSON());
93+
} catch (e) {
94+
console.error(e);
95+
json.children.push('[ENCODED DATA]');
96+
}
97+
}
98+
return json;
99+
}
100+
101+
toSimpleJSON() {
102+
const json = {};
103+
json[this.tag] = {};
104+
Object.keys(this.attributes).forEach(key => {
105+
let value = this.attributes[key];
106+
if (value instanceof Buffer) {
107+
value = value.toString('utf8');
108+
}
109+
json[this.tag][key] = value;
110+
});
111+
try {
112+
for (let i = 0; i < this.children.length; i++) {
113+
const c = this.children[i].toSimpleJSON();
114+
if (!c) continue;
115+
let tag = Object.keys(c)[0];
116+
const child = c[tag];
117+
if (typeof this.attributes[tag] !== 'undefined') {
118+
tag = `${tag}_entity`;
119+
}
120+
if (json[this.tag][tag]) {
121+
if (Array.isArray(json[this.tag][tag])) {
122+
json[this.tag][tag].push(child);
123+
} else {
124+
json[this.tag][tag] = [json[this.tag][tag], child];
125+
}
126+
} else {
127+
json[this.tag][tag] = child;
128+
}
129+
}
130+
} catch (e) {
131+
console.error(e);
132+
}
133+
if (this.data !== null && typeof this.data !== 'undefined') {
134+
json[this.tag].entity_value = this.data;
135+
if (json[this.tag].entity_value instanceof Buffer) {
136+
json[this.tag].entity_value = json[this.tag].entity_value.toString('utf8');
137+
}
138+
}
139+
return json || {};
140+
}
141+
142+
setData(data) {
143+
this.data = data;
144+
}
145+
146+
getData() {
147+
return this.data;
148+
}
149+
150+
getChild(identifier) {
151+
if (typeof identifier === 'number') {
152+
if (this.children.length > identifier) return this.children[identifier];
153+
return null;
154+
}
155+
for (let i = 0; i < this.children.length; i++) {
156+
const c = this.children[i];
157+
if (c.tag === identifier) return c;
158+
}
159+
return null;
160+
}
161+
162+
addChild(childNode) {
163+
this.children.push(childNode);
164+
}
165+
166+
addChildren(children) {
167+
children.forEach(child => {
168+
this.addChild(child);
169+
});
170+
}
171+
172+
hasChildren() {
173+
return this.children.length > 0;
174+
}
175+
176+
getAllChildren(tag = null) {
177+
if (!tag) return this.children;
178+
return this.children.filter(child => child.tag === tag);
179+
}
180+
181+
getAttributeValue(key) {
182+
return this.attributes[key];
183+
}
184+
185+
getAttr(key) {
186+
return this.getAttributeValue(key);
187+
}
188+
189+
setAttr(key, value) {
190+
this.setAttribute(key, value);
191+
}
192+
193+
removeAttr(key) {
194+
delete this.attributes[key];
195+
}
196+
197+
removeAttribute(key) {
198+
delete this.attributes[key];
199+
}
200+
201+
setAttribute(key, value) {
202+
this.attributes[key] = value;
203+
}
204+
}
205+
module.exports = ProtocolTreeNode;

src/protocol/XMLProtocol.js

+176
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
const swig = require('swig');
2+
const path = require('path');
3+
const parser = require('fast-xml-parser');
4+
const utils = require('../utils');
5+
const ProtocolTreeNode = require('./ProtocolTreeNode');
6+
const logger = require('../logger');
7+
8+
const attrsPrefix = '@_attrs';
9+
10+
const templates = {};
11+
12+
class XMLProtocol {
13+
// constructor(tag, attrs, childre = [], data = null) {
14+
constructor(protocolName, params = {}) {
15+
const {
16+
shortId,
17+
longId,
18+
type,
19+
xmlns,
20+
md5Id,
21+
name,
22+
description,
23+
subject,
24+
from,
25+
to,
26+
code,
27+
data = null,
28+
} = params;
29+
30+
this.protocolName = protocolName;
31+
this.params = params;
32+
this.logger = logger;
33+
34+
this.tag = '';
35+
this.attrs = {};
36+
this.children = [];
37+
this.data = data;
38+
this.id = 0;
39+
40+
this.xml = null;
41+
this.json = null;
42+
this.node = null;
43+
}
44+
45+
toXML() {
46+
if (this.xml !== null) return this.xml;
47+
const template = templates[this.protocolName] || swig.compileFile(path.join(__dirname, `./protocols/${this.protocolName}.xml`));
48+
let id = this.id;
49+
50+
const params = {
51+
...this.params,
52+
get shortId() {
53+
id++;
54+
return utils.generateId(id);
55+
},
56+
get longId() {
57+
id++;
58+
return `${String(Math.round(Date.now() / 1000))}-${utils.generateId(id)}`;
59+
},
60+
get md5Id() {
61+
return utils.hash(String(Math.random()), 'md5', 'hex').toUpperCase();
62+
},
63+
get prevId() {
64+
return utils.hash(String(Math.random()), 'md5', 'hex').toUpperCase();
65+
},
66+
};
67+
this.id = id;
68+
try {
69+
const xml = template(params);
70+
this.xml = xml;
71+
return xml;
72+
} catch (e) {
73+
this.logger.errror('parse xml template failed:', e.message, e.stack);
74+
this.xml = '';
75+
return this.xml;
76+
}
77+
}
78+
79+
toJSON() {
80+
if (this.json !== null) return this.json;
81+
const xml = this.toXML();
82+
try {
83+
const json = parser.parse(
84+
xml,
85+
{
86+
attributeNamePrefix: '',
87+
attrNodeName: attrsPrefix,
88+
ignoreAttributes: false,
89+
allowBooleanAttributes: true,
90+
parseAttributeValue: true,
91+
arrayMode: false,
92+
},
93+
true
94+
);
95+
this.json = json;
96+
return json;
97+
} catch (e) {
98+
this.logger.error('parse xml to json failed:', e.message, e.stack);
99+
this.json = {};
100+
return {};
101+
}
102+
}
103+
104+
toNode(jsonObj) {
105+
if (this.node !== null) return this.node;
106+
const json = jsonObj || this.toJSON();
107+
Object.keys(json).forEach(key => {
108+
if (key === attrsPrefix) return;
109+
this.tag = key;
110+
const child = json[key];
111+
if (typeof child !== 'object') {
112+
this.data = child;
113+
} else {
114+
this.attrs = child[attrsPrefix] || {};
115+
Object.keys(child).forEach(childKey => {
116+
if (childKey === attrsPrefix) return;
117+
const childItem = child[childKey];
118+
if (Array.isArray(childItem)) {
119+
childItem.forEach(subItem => {
120+
const o = {};
121+
o[childKey] = subItem;
122+
this.children.push(new XMLProtocol().toNode(o));
123+
});
124+
} else {
125+
const o = {};
126+
o[childKey] = childItem;
127+
this.children.push(new XMLProtocol().toNode(o));
128+
}
129+
});
130+
}
131+
});
132+
this.node = new ProtocolTreeNode(this.tag, this.attrs, this.children || [], this.data);
133+
return this.node;
134+
}
135+
136+
toString() {
137+
this.toNode();
138+
let out = `<${this.tag}`;
139+
if (this.attributes) {
140+
Object.keys(this.attributes).forEach(key => {
141+
const val = this.attributes[key];
142+
out += ` ${key}="${val}"`;
143+
});
144+
}
145+
out += '>\n';
146+
if (this.data) {
147+
if (this.data instanceof Buffer) {
148+
out += this.data.toString('utf8');
149+
} else {
150+
try {
151+
out += this.data;
152+
} catch (e) {
153+
try {
154+
out += this.data.toString();
155+
} catch (err) {
156+
out += this.data.toString('hex');
157+
}
158+
}
159+
}
160+
out += `\nHEX3:${Buffer.from(this.data || '').toString('hex')}\n`;
161+
}
162+
for (let i = 0; i < this.children.length; i++) {
163+
const c = this.children[i];
164+
try {
165+
out += c.toString();
166+
} catch (e) {
167+
console.error(e);
168+
out += '[ENCODED DATA]\n';
169+
}
170+
}
171+
out += `</${this.tag}>\n`;
172+
return out;
173+
}
174+
}
175+
176+
module.exports = XMLProtocol;

0 commit comments

Comments
 (0)