Use PYONDecoder instead of eval()
This commit is contained in:
parent
ec5852b176
commit
9b619ae644
|
@ -27,8 +27,10 @@ import errno
|
|||
import time
|
||||
import sys
|
||||
import traceback
|
||||
import json
|
||||
|
||||
from fah.util import OrderedDict
|
||||
from fah.util import PYONDecoder
|
||||
|
||||
if sys.platform == 'win32':
|
||||
from ctypes import windll
|
||||
|
@ -204,7 +206,7 @@ class Connection:
|
|||
|
||||
def parse_message(self, version, type, data):
|
||||
try:
|
||||
msg = eval(data, {}, {})
|
||||
msg = json.loads(data, cls = PYONDecoder)
|
||||
#if debug: print 'MSG:', type, msg
|
||||
self.messages.append((version, type, msg))
|
||||
self.last_message = time.time()
|
||||
|
|
|
@ -0,0 +1,216 @@
|
|||
################################################################################
|
||||
# #
|
||||
# Folding@Home Client Control (FAHControl) #
|
||||
# Copyright (C) 2016-2020 foldingathome.org #
|
||||
# Copyright (C) 2010-2016 Stanford University #
|
||||
# #
|
||||
# This program is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
||||
# #
|
||||
################################################################################
|
||||
|
||||
import re
|
||||
import json
|
||||
import sys
|
||||
|
||||
|
||||
NUMBER_RE = re.compile(r'(-?(?:0|[1-9]\d*))(\.\d+)?([eE][-+]?\d+)?',
|
||||
(re.VERBOSE | re.MULTILINE | re.DOTALL))
|
||||
FLAGS = re.VERBOSE | re.MULTILINE | re.DOTALL
|
||||
STRINGCHUNK = re.compile(r'(.*?)(["\\\x00-\x1f])', FLAGS)
|
||||
BACKSLASH = {
|
||||
'"': u'"', '\\': u'\\', '/': u'/',
|
||||
'b': u'\b', 'f': u'\f', 'n': u'\n', 'r': u'\r', 't': u'\t',
|
||||
}
|
||||
|
||||
DEFAULT_ENCODING = "utf-8"
|
||||
|
||||
|
||||
def linecol(doc, pos):
|
||||
lineno = doc.count('\n', 0, pos) + 1
|
||||
if lineno == 1:
|
||||
colno = pos + 1
|
||||
else:
|
||||
colno = pos - doc.rindex('\n', 0, pos)
|
||||
return lineno, colno
|
||||
|
||||
|
||||
def errmsg(msg, doc, pos, end=None):
|
||||
# Note that this function is called from _json
|
||||
lineno, colno = linecol(doc, pos)
|
||||
if end is None:
|
||||
fmt = '{0}: line {1} column {2} (char {3})'
|
||||
return fmt.format(msg, lineno, colno, pos)
|
||||
|
||||
endlineno, endcolno = linecol(doc, end)
|
||||
fmt = '{0}: line {1} column {2} - line {3} column {4} (char {5} - {6})'
|
||||
return fmt.format(msg, lineno, colno, endlineno, endcolno, pos, end)
|
||||
|
||||
|
||||
def _decode_uXXXX(s, pos):
|
||||
esc = s[pos + 1:pos + 5]
|
||||
|
||||
if len(esc) == 4 and esc[1] not in 'xX':
|
||||
try:
|
||||
return int(esc, 16)
|
||||
except ValueError: pass
|
||||
|
||||
msg = "Invalid \\uXXXX escape"
|
||||
raise ValueError(errmsg(msg, s, pos))
|
||||
|
||||
|
||||
def pyon_scanstring(s, end, encoding = None, strict = True,
|
||||
_b = BACKSLASH, _m = STRINGCHUNK.match):
|
||||
"""Scan the string s for a JSON string. End is the index of the
|
||||
character in s after the quote that started the JSON string.
|
||||
Unescapes all valid JSON string escape sequences and raises ValueError
|
||||
on attempt to decode an invalid string. If strict is False then literal
|
||||
control characters are allowed in the string.
|
||||
Returns a tuple of the decoded string and the index of the character in s
|
||||
after the end quote."""
|
||||
if encoding is None: encoding = DEFAULT_ENCODING
|
||||
chunks = []
|
||||
_append = chunks.append
|
||||
begin = end - 1
|
||||
|
||||
while True:
|
||||
chunk = _m(s, end)
|
||||
if chunk is None:
|
||||
raise ValueError(
|
||||
errmsg("Unterminated string starting at", s, begin))
|
||||
|
||||
end = chunk.end()
|
||||
content, terminator = chunk.groups()
|
||||
|
||||
# Content is contains zero or more unescaped string characters
|
||||
if content:
|
||||
if not isinstance(content, unicode):
|
||||
content = unicode(content, encoding)
|
||||
_append(content)
|
||||
|
||||
# Terminator is the end of string, a literal control character,
|
||||
# or a backslash denoting that an escape sequence follows
|
||||
if terminator == '"': break
|
||||
elif terminator != '\\':
|
||||
if strict:
|
||||
msg = "Invalid control character {0!r} at".format(terminator)
|
||||
raise ValueError(errmsg(msg, s, end))
|
||||
else:
|
||||
_append(terminator)
|
||||
continue
|
||||
|
||||
try:
|
||||
esc = s[end]
|
||||
except IndexError:
|
||||
raise ValueError(errmsg("Unterminated string starting at", s, begin))
|
||||
|
||||
# If not a unicode escape sequence, must be in the lookup table
|
||||
if esc != 'u' and esc != 'x':
|
||||
try:
|
||||
char = _b[esc]
|
||||
except KeyError:
|
||||
msg = "Invalid \\escape: " + repr(esc)
|
||||
raise ValueError(errmsg(msg, s, end))
|
||||
end += 1
|
||||
|
||||
elif esc == 'x':
|
||||
# Hex escape sequence
|
||||
try:
|
||||
code = s[end + 1: end + 3]
|
||||
char = code.decode('hex')
|
||||
except:
|
||||
raise ValueError(errmsg('Invalid \\escape: ' + repr(code), s, end))
|
||||
|
||||
end += 3
|
||||
|
||||
else:
|
||||
# Unicode escape sequence
|
||||
uni = _decode_uXXXX(s, end)
|
||||
end += 5
|
||||
# Check for surrogate pair on UCS-4 systems
|
||||
if sys.maxunicode > 65535 and \
|
||||
0xd800 <= uni <= 0xdbff and s[end:end + 2] == '\\u':
|
||||
uni2 = _decode_uXXXX(s, end + 1)
|
||||
if 0xdc00 <= uni2 <= 0xdfff:
|
||||
uni = 0x10000 + (((uni - 0xd800) << 10) | (uni2 - 0xdc00))
|
||||
end += 6
|
||||
char = unichr(uni)
|
||||
|
||||
# Append the unescaped character
|
||||
_append(char)
|
||||
|
||||
return u''.join(chunks), end
|
||||
|
||||
|
||||
|
||||
def make_pyon_scanner(context):
|
||||
parse_object = context.parse_object
|
||||
parse_array = context.parse_array
|
||||
parse_string = context.parse_string
|
||||
match_number = NUMBER_RE.match
|
||||
strict = context.strict
|
||||
parse_float = context.parse_float
|
||||
parse_int = context.parse_int
|
||||
parse_constant = context.parse_constant
|
||||
object_hook = context.object_hook
|
||||
object_pairs_hook = context.object_pairs_hook
|
||||
|
||||
|
||||
def scan_once(string, idx):
|
||||
try:
|
||||
nextchar = string[idx]
|
||||
except IndexError:
|
||||
raise StopIteration(idx)
|
||||
|
||||
if nextchar == '"': return parse_string(string, idx + 1, 'utf-8', strict)
|
||||
elif nextchar == '{':
|
||||
return parse_object((string, idx + 1), 'utf-8', strict, scan_once,
|
||||
object_hook, object_pairs_hook)
|
||||
elif nextchar == '[':
|
||||
return parse_array((string, idx + 1), scan_once)
|
||||
elif nextchar == 'N' and string[idx:idx + 4] == 'None':
|
||||
return None, idx + 4
|
||||
elif nextchar == 'T' and string[idx:idx + 4] == 'True':
|
||||
return True, idx + 4
|
||||
elif nextchar == 'F' and string[idx:idx + 5] == 'False':
|
||||
return False, idx + 5
|
||||
|
||||
m = match_number(string, idx)
|
||||
if m is not None:
|
||||
integer, frac, exp = m.groups()
|
||||
if frac or exp:
|
||||
res = parse_float(integer + (frac or '') + (exp or ''))
|
||||
else: res = parse_int(integer)
|
||||
|
||||
return res, m.end()
|
||||
|
||||
elif nextchar == 'N' and string[idx:idx + 3] == 'NaN':
|
||||
return parse_constant('NaN'), idx + 3
|
||||
|
||||
elif nextchar == 'I' and string[idx:idx + 8] == 'Infinity':
|
||||
return parse_constant('Infinity'), idx + 8
|
||||
|
||||
elif nextchar == '-' and string[idx:idx + 9] == '-Infinity':
|
||||
return parse_constant('-Infinity'), idx + 9
|
||||
|
||||
else: raise StopIteration(idx)
|
||||
|
||||
|
||||
return scan_once
|
||||
|
||||
|
||||
class PYONDecoder(json.JSONDecoder):
|
||||
def __init__(self, *args, **kwargs):
|
||||
json.JSONDecoder.__init__(self, *args, **kwargs)
|
||||
self.parse_string = pyon_scanstring
|
||||
self.scan_once = make_pyon_scanner(self)
|
|
@ -29,6 +29,7 @@ from SingleApp import *
|
|||
from EntryValidator import *
|
||||
from PasswordValidator import *
|
||||
from OrderedDict import *
|
||||
from PYONDecoder import *
|
||||
|
||||
|
||||
def parse_bool(x):
|
||||
|
|
Loading…
Reference in New Issue