From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id A7DC4687B7 for ; Wed, 9 Mar 2022 15:16:48 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 9F063B01 for ; Wed, 9 Mar 2022 15:16:48 +0100 (CET) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [94.136.29.106]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS id F0176AF8 for ; Wed, 9 Mar 2022 15:16:47 +0100 (CET) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id C034642079 for ; Wed, 9 Mar 2022 15:16:47 +0100 (CET) From: Dominik Csapak To: pbs-devel@lists.proxmox.com Date: Wed, 9 Mar 2022 15:16:46 +0100 Message-Id: <20220309141646.1027526-1-d.csapak@proxmox.com> X-Mailer: git-send-email 2.30.2 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL 0.153 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record T_SCC_BODY_TEXT_LINE -0.01 - Subject: [pbs-devel] [RFC PATCH proxmox-backup] ui: implement quoted strings in parsePropertyString X-BeenThere: pbs-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox Backup Server development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 09 Mar 2022 14:16:48 -0000 like we do in our rust propertystring parser. code is heavily inspired by the rust code. Signed-off-by: Dominik Csapak --- if we want to go the js route and not use wasm + rust code for this i sent it as rfc for pbs (since there we have that property string implementation), but we actually want this to live in wt, and use it in pve and pbs both when pve gains support for these. @thomas, the code is my attempt to write the parser as close to the rust code as it was sensible, but i'm sure there are some js specific improvements to be done here. i'll look over it again tomorrow with fresh eyes and mind, but if you see some things, don't hold back ;) www/Utils.js | 100 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 82 insertions(+), 18 deletions(-) diff --git a/www/Utils.js b/www/Utils.js index 36a94211..87809364 100644 --- a/www/Utils.js +++ b/www/Utils.js @@ -62,32 +62,96 @@ Ext.define('PBS.Utils', { return path.indexOf(PBS.Utils.dataStorePrefix) === 0; }, + parseQuotedString: function(value) { + let data = ""; + let was_backslash = false; + if (value[0] !== '"') { + throw "not a quoted string"; + } + value = value.slice(1); + for (let i = 0; ; i++) { + if (i === value.length) { + throw "invalid quoted string"; + } + if (was_backslash) { + was_backslash = false; + switch (value[i]) { + case '"': data += '"'; break; + case '\\': data += '\\'; break; + case 'n': data += '\n'; break; + default: + throw "unsupported escape sequence"; + } + } else { + switch (value[i]) { + case '"': return [data, i+1]; + case '\\': was_backslash = true; break; + default: + data += value[i]; + } + } + } + }, + parsePropertyString: function(value, defaultKey) { - var res = {}, - error; + let res = {}; 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 + try { + while (value.length > 0) { + let key, current; + if (value[0] !== '"') { + let idx = value.search(/[,=]/); + if (idx !== -1 && value[idx] === '=') { + key = value.slice(0, idx); + value = value.slice(idx + 1); + if (Ext.isDefined(res[key])) { + throw `duplicate key ${key} found`; + } + } + + if (value[0] !== '"') { + let next_idx = value.search(/,/); + if (next_idx === -1) { + current = value; + value = ""; + } else { + current = value.slice(0, next_idx); + value = value.slice(next_idx + 1); + } + } } - res[defaultKey] = kv[0]; - } else { - error = 'invalid propertyString, not a key=value pair and no defaultKey defined'; - return false; // break - } - return true; - }); - if (error !== undefined) { + if (key === undefined) { + if (Ext.isDefined(defaultKey)) { + if (Ext.isDefined(res[defaultKey])) { + throw 'defaultKey may be only defined once in propertyString'; + } + key = defaultKey; + } else { + throw "value without key and no defaultKey"; + } + } + if (current === undefined) { + let [val, idx] = PVE.Parser.parseQuotedString(value); + current = val; + value = value.slice(idx + 1); + } + + res[key] = current; + + if (value.length > 0) { + if (value[0] === ',') { + value = value.slice(1); + } else { + throw "garbage after value"; + } + } + } + } catch (error) { console.error(error); return null; } -- 2.30.2