// Some configuration values are complex strings -
// so we need parsers/generators for them.
-Ext.define('PVE.Parser', { statics: {
+Ext.define('PVE.Parser', {
+ statics: {
// this class only contains static functions
+ printACME: function(value) {
+ if (Ext.isArray(value.domains)) {
+ value.domains = value.domains.join(';');
+ }
+ return PVE.Parser.printPropertyString(value);
+ },
+
+ parseACME: function(value) {
+ if (!value) {
+ return {};
+ }
+
+ var res = {};
+ var error;
+
+ Ext.Array.each(value.split(','), function(p) {
+ var kv = p.split('=', 2);
+ if (Ext.isDefined(kv[1])) {
+ res[kv[0]] = kv[1];
+ } else {
+ error = 'Failed to parse key-value pair: '+p;
+ return false;
+ }
+ });
+
+ if (error !== undefined) {
+ console.error(error);
+ return;
+ }
+
+ if (res.domains !== undefined) {
+ res.domains = res.domains.split(/;/);
+ }
+
+ return res;
+ },
+
parseBoolean: function(value, default_value) {
if (!Ext.isDefined(value)) {
return default_value;
}
value = value.toLowerCase();
- return value === 1 || value === '1' ||
+ return value === '1' ||
value === 'on' ||
value === 'yes' ||
value === 'true';
},
+ parsePropertyString: function(value, defaultKey) {
+ var res = {},
+ error;
+
+ if (typeof value !== 'string' || value === '') {
+ return res;
+ }
+
+ Ext.Array.each(value.split(','), function(p) {
+ var kv = p.split('=', 2);
+ if (Ext.isDefined(kv[1])) {
+ res[kv[0]] = kv[1];
+ } else if (Ext.isDefined(defaultKey)) {
+ if (Ext.isDefined(res[defaultKey])) {
+ error = 'defaultKey may be only defined once in propertyString';
+ return false; // break
+ }
+ res[defaultKey] = kv[0];
+ } else {
+ error = 'invalid propertyString, not a key=value pair and no defaultKey defined';
+ return false; // break
+ }
+ });
+
+ if (error !== undefined) {
+ console.error(error);
+ return;
+ }
+
+ return res;
+ },
+
+ printPropertyString: function(data, defaultKey) {
+ var stringparts = [],
+ gotDefaultKeyVal = false,
+ defaultKeyVal;
+
+ Ext.Object.each(data, function(key, value) {
+ if (defaultKey !== undefined && key === defaultKey) {
+ gotDefaultKeyVal = true;
+ defaultKeyVal = value;
+ } else if (value !== '') {
+ stringparts.push(key + '=' + value);
+ }
+ });
+
+ stringparts = stringparts.sort();
+ if (gotDefaultKeyVal) {
+ stringparts.unshift(defaultKeyVal);
+ }
+
+ return stringparts.join(',');
+ },
+
parseQemuNetwork: function(key, value) {
if (!(key && value)) {
return;
},
printQemuNetwork: function(net) {
-
var netstr = net.model;
if (net.macaddr) {
netstr += "=" + net.macaddr;
},
printQemuDrive: function(drive) {
-
var drivestr = drive.file;
Ext.Object.each(drive, function(key, value) {
return drivestr;
},
+ parseIPConfig: function(key, value) {
+ if (!(key && value)) {
+ return;
+ }
+
+ var res = {};
+
+ var errors = false;
+ Ext.Array.each(value.split(','), function(p) {
+ if (!p || p.match(/^\s*$/)) {
+ return; // continue
+ }
+
+ var match_res;
+ if ((match_res = p.match(/^ip=(\S+)$/)) !== null) {
+ res.ip = match_res[1];
+ } else if ((match_res = p.match(/^gw=(\S+)$/)) !== null) {
+ res.gw = match_res[1];
+ } else if ((match_res = p.match(/^ip6=(\S+)$/)) !== null) {
+ res.ip6 = match_res[1];
+ } else if ((match_res = p.match(/^gw6=(\S+)$/)) !== null) {
+ res.gw6 = match_res[1];
+ } else {
+ errors = true;
+ return false; // break
+ }
+ });
+
+ if (errors) {
+ return;
+ }
+
+ return res;
+ },
+
+ printIPConfig: function(cfg) {
+ var c = "";
+ var str = "";
+ if (cfg.ip) {
+ str += "ip=" + cfg.ip;
+ c = ",";
+ }
+ if (cfg.gw) {
+ str += c + "gw=" + cfg.gw;
+ c = ",";
+ }
+ if (cfg.ip6) {
+ str += c + "ip6=" + cfg.ip6;
+ c = ",";
+ }
+ if (cfg.gw6) {
+ str += c + "gw6=" + cfg.gw6;
+ c = ",";
+ }
+ return str;
+ },
+
parseOpenVZNetIf: function(value) {
if (!value) {
return;
errors = true;
return false; // break
}
- if (match_res[1] === 'bridge'){
+ if (match_res[1] === 'bridge') {
var bridgevlanf = match_res[2];
var bridge_res = bridgevlanf.match(/^(vmbr(\d+))(v(\d+))?(f)?$/);
if (!bridge_res) {
errors = true;
return false; // break
}
- data['bridge'] = bridge_res[1];
- data['tag'] = bridge_res[4];
- data['firewall'] = bridge_res[5] ? 1 : 0;
+ data.bridge = bridge_res[1];
+ data.tag = bridge_res[4];
+ data.firewall = bridge_res[5] ? 1 : 0;
} else {
data[match_res[1]] = match_res[2];
}
Ext.Object.each(netif, function(iface, data) {
var tmparray = [];
- Ext.Array.each(['ifname', 'mac', 'bridge', 'host_ifname' , 'host_mac', 'mac_filter', 'tag', 'firewall'], function(key) {
+ Ext.Array.each(['ifname', 'mac', 'bridge', 'host_ifname', 'host_mac', 'mac_filter', 'tag', 'firewall'], function(key) {
var value = data[key];
- if (key === 'bridge'){
- if(data['tag']){
- value = value + 'v' + data['tag'];
+ if (key === 'bridge') {
+ if (data.tag) {
+ value = value + 'v' + data.tag;
}
- if (data['firewall']){
+ if (data.firewall) {
value = value + 'f';
}
}
if (value) {
tmparray.push(key + '=' + value);
}
-
});
netarray.push(tmparray.join(','));
});
if (!p || p.match(/^\s*$/)) {
return; // continue
}
- var match_res = p.match(/^(bridge|hwaddr|mtu|name|ip|ip6|gw|gw6|firewall|tag|rate)=(\S+)$/);
- if (!match_res) {
+ var match_res = p.match(/^(bridge|hwaddr|mtu|name|ip|ip6|gw|gw6|tag|rate)=(\S+)$/);
+ if (match_res) {
+ data[match_res[1]] = match_res[2];
+ } else if ((match_res = p.match(/^firewall=(\d+)$/)) !== null) {
+ data.firewall = PVE.Parser.parseBoolean(match_res[1]);
+ } else {
// todo: simply ignore errors ?
return; // continue
}
- data[match_res[1]] = match_res[2];
});
return data;
if (!p || p.match(/^\s*$/)) {
return; // continue
}
- var match_res = p.match(/^([a-z_]+)=(\S+)$/);
+ var match_res = p.match(/^([a-z_]+)=(.+)$/);
if (!match_res) {
if (!p.match(/\=/)) {
res.file = p;
},
parseQemuSmbios1: function(value) {
- var res = {};
-
- Ext.Array.each(value.split(','), function(p) {
- var kva = p.split(/=/, 2);
- res[kva[0]] = kva[1];
- });
+ var res = value.split(',').reduce(function(accumulator, currentValue) {
+ var splitted = currentValue.split(new RegExp("=(.+)"));
+ accumulator[splitted[0]] = splitted[1];
+ return accumulator;
+ }, {});
+
+ if (PVE.Parser.parseBoolean(res.base64, false)) {
+ Ext.Object.each(res, function(key, value) {
+ if (key === 'uuid') { return; }
+ res[key] = Ext.util.Base64.decode(value);
+ });
+ }
return res;
},
printQemuSmbios1: function(data) {
-
var datastr = '';
-
+ var base64 = false;
Ext.Object.each(data, function(key, value) {
if (value === '') { return; }
- datastr += (datastr !== '' ? ',' : '') + key + '=' + value;
+ if (key === 'uuid') {
+ datastr += (datastr !== '' ? ',' : '') + key + '=' + value;
+ } else {
+ // values should be base64 encoded from now on, mark config strings correspondingly
+ if (!base64) {
+ base64 = true;
+ datastr += (datastr !== '' ? ',' : '') + 'base64=1';
+ }
+ datastr += (datastr !== '' ? ',' : '') + key + '=' + Ext.util.Base64.encode(value);
+ }
});
return datastr;
var res = {};
Ext.Array.each(value.split(','), function(p) {
- var kva = p.split(/=/, 2);
+ var kva = p.split('=', 2);
res[kva[0]] = kva[1];
});
return res;
},
+ parseTfaType: function(value) {
+ var match;
+ if (!value || !value.length) {
+ return undefined;
+ } else if (value === 'x!oath') {
+ return 'totp';
+ } else if (!!(match = value.match(/^x!(.+)$/))) {
+ return match[1];
+ } else {
+ return 1;
+ }
+ },
+
parseQemuCpu: function(value) {
if (!value) {
return {};
return; // continue
}
- if (!p.match(/=/)) {
- if (Ext.isDefined(res['cpu'])) {
+ if (!p.match(/\=/)) {
+ if (Ext.isDefined(res.cpu)) {
errors = true;
return false; // break
}
return cpustr + optstr;
},
-}});
+
+ parseSSHKey: function(key) {
+ // |--- options can have quotes--| type key comment
+ var keyre = /^(?:((?:[^\s"]|\"(?:\\.|[^"\\])*")+)\s+)?(\S+)\s+(\S+)(?:\s+(.*))?$/;
+ var typere = /^(?:ssh-(?:dss|rsa|ed25519)|ecdsa-sha2-nistp\d+)$/;
+
+ var m = key.match(keyre);
+ if (!m) {
+ return null;
+ }
+ if (m.length < 3 || !m[2]) { // [2] is always either type or key
+ return null;
+ }
+ if (m[1] && m[1].match(typere)) {
+ return {
+ type: m[1],
+ key: m[2],
+ comment: m[3],
+ };
+ }
+ if (m[2].match(typere)) {
+ return {
+ options: m[1],
+ type: m[2],
+ key: m[3],
+ comment: m[4],
+ };
+ }
+ return null;
+ },
+
+ parseACMEPluginData: function(data) {
+ let res = {};
+ let extradata = [];
+ data.split('\n').forEach((line) => {
+ // capture everything after the first = as value
+ let [key, value] = line.split(/=(.+)/);
+ if (value !== undefined) {
+ res[key] = value;
+ } else {
+ extradata.push(line);
+ }
+ });
+ return [res, extradata];
+ },
+}
+});