Merge branch 'v0.6' into qt5-wip #2237

Open
kashikoibumi wants to merge 38 commits from kashikoibumi/qt5-wip into v0.6
5 changed files with 168 additions and 195 deletions
Showing only changes of commit 3202082e76 - Show all commits

View File

@ -144,11 +144,10 @@ class objectProcessor(threading.Thread):
" WHERE ackdata=?", int(time.time()), data[readPosition:])
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
data[readPosition:],
_translate(
data[readPosition:], _translate(
"MainWindow",
"Acknowledgement of the message received %1"
).arg(l10n.formatTimestamp()))
"Acknowledgement of the message received {0}"
).format(l10n.formatTimestamp()))
))
else:
logger.debug('This object is not an acknowledgement bound for me.')

View File

@ -12,6 +12,8 @@ from binascii import hexlify, unhexlify
from struct import pack
from subprocess import call # nosec
from six.moves import configparser, queue
import defaults
import helper_inbox
import helper_msgcoding
@ -24,15 +26,13 @@ import protocol
import queues
import shared
import state
import tr
from addresses import (
calculateInventoryHash, decodeAddress, decodeVarint, encodeVarint
)
calculateInventoryHash, decodeAddress, decodeVarint, encodeVarint)
from bmconfigparser import BMConfigParser
from helper_sql import sqlExecute, sqlQuery
from inventory import Inventory
from network import knownnodes, StoppableThread
from six.moves import configparser, queue
from tr import _translate
def sizeof_fmt(num, suffix='h/s'):
@ -216,9 +216,8 @@ class singleWorker(StoppableThread):
return privSigningKeyHex, privEncryptionKeyHex, \
pubSigningKey, pubEncryptionKey
def _doPOWDefaults(self, payload, TTL,
log_prefix='',
log_time=False):
def _doPOWDefaults(
self, payload, TTL, log_prefix='', log_time=False):
target = 2 ** 64 / (
defaults.networkDefaultProofOfWorkNonceTrialsPerByte * (
len(payload) + 8
@ -244,14 +243,16 @@ class singleWorker(StoppableThread):
'PoW took %.1f seconds, speed %s.',
delta, sizeof_fmt(nonce / delta)
)
except: # noqa:E722 # NameError
except NameError:
self.logger.warning("Proof of Work exception")
payload = pack('>Q', nonce) + payload
return payload
def doPOWForMyV2Pubkey(self, adressHash):
""" This function also broadcasts out the pubkey
message once it is done with the POW"""
"""
This function also broadcasts out the pubkey message once it is
done with the POW
"""
# Look up my stream number based on my address hash
myAddress = shared.myAddressesByHash[adressHash]
# status
@ -311,9 +312,10 @@ class singleWorker(StoppableThread):
def sendOutOrStoreMyV3Pubkey(self, adressHash):
"""
If this isn't a chan address, this function assembles the pubkey data, does the necessary POW and sends it out.
If it *is* a chan then it assembles the pubkey and stores is in the pubkey table so that we can send messages
to "ourselves".
If this isn't a chan address, this function assembles the pubkey
data, does the necessary POW and sends it out.
If it *is* a chan then it assembles the pubkey and stores it in
the pubkey table so that we can send messages to "ourselves".
"""
try:
myAddress = shared.myAddressesByHash[adressHash]
@ -399,9 +401,10 @@ class singleWorker(StoppableThread):
def sendOutOrStoreMyV4Pubkey(self, myAddress):
"""
It doesn't send directly anymore. It put is to a queue for another thread to send at an appropriate time,
whereas in the past it directly appended it to the outgoing buffer, I think. Same with all the other methods in
this class.
It doesn't send directly anymore. It put is to a queue for
another thread to send at an appropriate time, whereas in the
past it directly appended it to the outgoing buffer, I think.
Same with all the other methods in this class.
"""
if not BMConfigParser().has_section(myAddress):
# The address has been deleted.
@ -530,7 +533,10 @@ class singleWorker(StoppableThread):
queues.invQueue.put((streamNumber, inventoryHash))
def sendBroadcast(self):
"""Send a broadcast-type object (assemble the object, perform PoW and put it to the inv announcement queue)"""
"""
Send a broadcast-type object (assemble the object, perform PoW
and put it to the inv announcement queue)
"""
# Reset just in case
sqlExecute(
'''UPDATE sent SET status='broadcastqueued' '''
@ -562,8 +568,7 @@ class singleWorker(StoppableThread):
self.logger.warning("Section or Option did not found: %s", err)
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
ackdata, _translate(
"MainWindow",
"Error! Could not find sender address"
" (your address) in the keys.dat file."))
@ -577,7 +582,7 @@ class singleWorker(StoppableThread):
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
_translate(
"MainWindow",
"Error, can't send."))
))
@ -666,8 +671,7 @@ class singleWorker(StoppableThread):
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
ackdata, _translate(
"MainWindow",
"Doing work necessary to send broadcast..."))
))
@ -699,11 +703,9 @@ class singleWorker(StoppableThread):
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
"MainWindow",
"Broadcast sent on %1"
).arg(l10n.formatTimestamp()))
ackdata, _translate(
"MainWindow", "Broadcast sent on {0}"
).format(l10n.formatTimestamp()))
))
# Update the status of the message in the 'sent' table to have
@ -715,7 +717,10 @@ class singleWorker(StoppableThread):
)
def sendMsg(self):
"""Send a message-type object (assemble the object, perform PoW and put it to the inv announcement queue)"""
"""
Send a message-type object (assemble the object, perform PoW
and put it to the inv announcement queue)
"""
# pylint: disable=too-many-nested-blocks
# Reset just in case
sqlExecute(
@ -811,8 +816,7 @@ class singleWorker(StoppableThread):
)
queues.UISignalQueue.put((
'updateSentItemStatusByToAddress', (
toaddress,
tr._translate(
toaddress, _translate(
"MainWindow",
"Encryption key was requested earlier."))
))
@ -884,8 +888,7 @@ class singleWorker(StoppableThread):
)
queues.UISignalQueue.put((
'updateSentItemStatusByToAddress', (
toaddress,
tr._translate(
toaddress, _translate(
"MainWindow",
"Sending a request for the"
" recipient\'s encryption key."))
@ -909,8 +912,7 @@ class singleWorker(StoppableThread):
state.ackdataForWhichImWatching[ackdata] = 0
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
ackdata, _translate(
"MainWindow",
"Looking up the receiver\'s public key"))
))
@ -967,15 +969,14 @@ class singleWorker(StoppableThread):
)
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
ackdata, _translate(
"MainWindow",
"Problem: Destination is a mobile"
" device who requests that the"
" destination be included in the"
" message but this is disallowed in"
" your settings. %1"
).arg(l10n.formatTimestamp()))
" your settings. {0}"
).format(l10n.formatTimestamp()))
))
# if the human changes their setting and then
# sends another message or restarts their client,
@ -998,8 +999,7 @@ class singleWorker(StoppableThread):
defaults.networkDefaultPayloadLengthExtraBytes
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
ackdata, _translate(
"MainWindow",
"Doing work necessary to send message.\n"
"There is no required difficulty for"
@ -1031,32 +1031,19 @@ class singleWorker(StoppableThread):
requiredAverageProofOfWorkNonceTrialsPerByte,
requiredPayloadLengthExtraBytes
)
queues.UISignalQueue.put(
(
'updateSentItemStatusByAckdata',
(
ackdata,
tr._translate(
"MainWindow",
"Doing work necessary to send message.\n"
"Receiver\'s required difficulty: %1"
" and %2"
).arg(
str(
float(requiredAverageProofOfWorkNonceTrialsPerByte)
/ defaults.networkDefaultProofOfWorkNonceTrialsPerByte
)
).arg(
str(
float(requiredPayloadLengthExtraBytes)
/ defaults.networkDefaultPayloadLengthExtraBytes
)
)
)
)
)
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata, _translate(
"MainWindow",
"Doing work necessary to send message.\n"
"Receiver\'s required difficulty: {0} and {1}"
).format(
float(requiredAverageProofOfWorkNonceTrialsPerByte)
/ defaults.networkDefaultProofOfWorkNonceTrialsPerByte,
float(requiredPayloadLengthExtraBytes)
/ defaults.networkDefaultPayloadLengthExtraBytes
))
))
if status != 'forcepow':
maxacceptablenoncetrialsperbyte = BMConfigParser().getint(
'bitmessagesettings', 'maxacceptablenoncetrialsperbyte')
@ -1076,18 +1063,19 @@ class singleWorker(StoppableThread):
ackdata)
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
ackdata, _translate(
"MainWindow",
"Problem: The work demanded by"
" the recipient (%1 and %2) is"
" more difficult than you are"
" willing to do. %3"
).arg(str(float(requiredAverageProofOfWorkNonceTrialsPerByte)
/ defaults.networkDefaultProofOfWorkNonceTrialsPerByte)
).arg(str(float(requiredPayloadLengthExtraBytes)
/ defaults.networkDefaultPayloadLengthExtraBytes)
).arg(l10n.formatTimestamp()))))
"Problem: The work demanded by the"
" recipient ({0} and {1}) is more"
" difficult than you are willing"
" to do. {2}"
).format(
float(requiredAverageProofOfWorkNonceTrialsPerByte)
/ defaults.networkDefaultProofOfWorkNonceTrialsPerByte,
float(requiredPayloadLengthExtraBytes)
/ defaults.networkDefaultPayloadLengthExtraBytes,
l10n.formatTimestamp()))
))
continue
else: # if we are sending a message to ourselves or a chan..
self.logger.info('Sending a message.')
@ -1101,15 +1089,14 @@ class singleWorker(StoppableThread):
except (configparser.NoSectionError, configparser.NoOptionError) as err:
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
ackdata, _translate(
"MainWindow",
"Problem: You are trying to send a"
" message to yourself or a chan but your"
" encryption key could not be found in"
" the keys.dat file. Could not encrypt"
" message. %1"
).arg(l10n.formatTimestamp()))
" message. {0}"
).format(l10n.formatTimestamp()))
))
self.logger.error(
'Error within sendMsg. Could not read the keys'
@ -1126,8 +1113,7 @@ class singleWorker(StoppableThread):
defaults.networkDefaultPayloadLengthExtraBytes
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
ackdata, _translate(
"MainWindow",
"Doing work necessary to send message."))
))
@ -1150,8 +1136,7 @@ class singleWorker(StoppableThread):
self.logger.warning("Section or Option did not found: %s", err)
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
ackdata, _translate(
"MainWindow",
"Error! Could not find sender address"
" (your address) in the keys.dat file."))
@ -1165,7 +1150,7 @@ class singleWorker(StoppableThread):
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
_translate(
"MainWindow",
"Error, can't send."))
))
@ -1217,8 +1202,7 @@ class singleWorker(StoppableThread):
# The fullAckPayload is a normal msg protocol message
# with the proof of work already completed that the
# receiver of this message can easily send out.
fullAckPayload = self.generateFullAckMessage(
ackdata, toStreamNumber, TTL)
fullAckPayload = self.generateFullAckMessage(ackdata, TTL)
payload += encodeVarint(len(fullAckPayload))
payload += fullAckPayload
dataToSign = pack('>Q', embeddedTime) + '\x00\x00\x00\x02' + \
@ -1240,12 +1224,11 @@ class singleWorker(StoppableThread):
)
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
ackdata, _translate(
"MainWindow",
"Problem: The recipient\'s encryption key is"
" no good. Could not encrypt message. %1"
).arg(l10n.formatTimestamp()))
" no good. Could not encrypt message. {0}"
).format(l10n.formatTimestamp()))
))
continue
@ -1309,21 +1292,19 @@ class singleWorker(StoppableThread):
not protocol.checkBitfield(behaviorBitfield, protocol.BITFIELD_DOESACK):
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
"MainWindow",
"Message sent. Sent at %1"
).arg(l10n.formatTimestamp()))))
ackdata, _translate(
"MainWindow", "Message sent. Sent at {0}"
).format(l10n.formatTimestamp()))
))
else:
# not sending to a chan or one of my addresses
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
tr._translate(
ackdata, _translate(
"MainWindow",
"Message sent. Waiting for acknowledgement."
" Sent on %1"
).arg(l10n.formatTimestamp()))
" Sent on {0}"
).format(l10n.formatTimestamp()))
))
self.logger.info(
'Broadcasting inv for my msg(within sendmsg function): %s',
@ -1451,8 +1432,7 @@ class singleWorker(StoppableThread):
queues.UISignalQueue.put(('updateStatusBar', statusbar))
queues.UISignalQueue.put((
'updateSentItemStatusByToAddress', (
toAddress,
tr._translate(
toAddress, _translate(
"MainWindow",
"Doing work necessary to request encryption key."))
))
@ -1476,30 +1456,31 @@ class singleWorker(StoppableThread):
int(time.time()), retryNumber + 1, sleeptill, toAddress)
queues.UISignalQueue.put((
'updateStatusBar',
tr._translate(
'updateStatusBar', _translate(
"MainWindow",
"Broadcasting the public key request. This program will"
" auto-retry if they are offline.")
))
queues.UISignalQueue.put((
'updateSentItemStatusByToAddress', (
toAddress,
tr._translate(
toAddress, _translate(
"MainWindow",
"Sending public key request. Waiting for reply."
" Requested at %1"
).arg(l10n.formatTimestamp()))
" Requested at {0}"
).format(l10n.formatTimestamp()))
))
def generateFullAckMessage(self, ackdata, _, TTL):
"""
It might be perfectly fine to just use the same TTL for the ackdata that we use for the message. But I would
rather it be more difficult for attackers to associate ackData with the associated msg object. However, users
would want the TTL of the acknowledgement to be about the same as they set for the message itself. So let's set
the TTL of the acknowledgement to be in one of three 'buckets': 1 hour, 7 days, or 28 days, whichever is
relatively close to what the user specified.
"""
def generateFullAckMessage(self, ackdata, TTL):
"""Create ACK packet"""
# It might be perfectly fine to just use the same TTL for
# the ackdata that we use for the message. But I would rather
# it be more difficult for attackers to associate ackData with
# the associated msg object. However, users would want the TTL
# of the acknowledgement to be about the same as they set
# for the message itself. So let's set the TTL of the
# acknowledgement to be in one of three 'buckets': 1 hour, 7
# days, or 28 days, whichever is relatively close to what the
# user specified.
if TTL < 24 * 60 * 60: # 1 day
TTL = 24 * 60 * 60 # 1 day
elif TTL < 7 * 24 * 60 * 60: # 1 week

View File

@ -1,7 +1,7 @@
"""
Namecoin queries
"""
# pylint: disable=too-many-branches,protected-access
# pylint: disable=too-many-branches
import base64
import httplib
@ -14,14 +14,14 @@ import defaults
from addresses import decodeAddress
from bmconfigparser import BMConfigParser
from debug import logger
from tr import _translate # translate
from tr import _translate
configSection = "bitmessagesettings"
class RPCError(Exception):
"""Error thrown when the RPC call returns an error."""
error = None
def __init__(self, data):
@ -29,7 +29,7 @@ class RPCError(Exception):
self.error = data
def __str__(self):
return "{0}: {1}".format(type(self).__name__, self.error)
return '{0}: {1}'.format(type(self).__name__, self.error)
class namecoinConnection(object):
@ -46,20 +46,18 @@ class namecoinConnection(object):
def __init__(self, options=None):
"""
Initialise. If options are given, take the connection settings from
them instead of loading from the configs. This can be used to test
Initialise. If options are given, take the connection settings from
them instead of loading from the configs. This can be used to test
currently entered connection settings in the config dialog without
actually changing the values (yet).
"""
if options is None:
self.nmctype = BMConfigParser().get(
configSection, "namecoinrpctype")
self.host = BMConfigParser().get(
configSection, "namecoinrpchost")
self.host = BMConfigParser().get(configSection, "namecoinrpchost")
self.port = int(BMConfigParser().get(
configSection, "namecoinrpcport"))
self.user = BMConfigParser().get(
configSection, "namecoinrpcuser")
self.user = BMConfigParser().get(configSection, "namecoinrpcuser")
self.password = BMConfigParser().get(
configSection, "namecoinrpcpassword")
else:
@ -69,14 +67,14 @@ class namecoinConnection(object):
self.user = options["user"]
self.password = options["password"]
assert self.nmctype == "namecoind" or self.nmctype == "nmcontrol"
assert self.nmctype in ("namecoind", "nmcontrol")
if self.nmctype == "namecoind":
self.con = httplib.HTTPConnection(self.host, self.port, timeout=3)
def query(self, identity):
"""
Query for the bitmessage address corresponding to the given identity
string. If it doesn't contain a slash, id/ is prepended. We return
string. If it doesn't contain a slash, id/ is prepended. We return
the result as (Error, Address) pair, where the Error is an error
message to display or None in case of success.
"""
@ -96,8 +94,8 @@ class namecoinConnection(object):
res = res["reply"]
if not res:
return (_translate(
"MainWindow", "The name %1 was not found."
).arg(identity.decode("utf-8", "ignore")), None)
"MainWindow", "The name {0} was not found."
).format(identity.decode('utf-8', 'ignore')), None)
else:
assert False
except RPCError as exc:
@ -107,12 +105,12 @@ class namecoinConnection(object):
else:
errmsg = exc.error
return (_translate(
"MainWindow", "The namecoin query failed (%1)"
).arg(errmsg.decode("utf-8", "ignore")), None)
"MainWindow", "The namecoin query failed ({0})"
).format(errmsg.decode('utf-8', 'ignore')), None)
except AssertionError:
return (_translate(
"MainWindow", "Unknown namecoin interface type: %1"
).arg(self.nmctype.decode("utf-8", "ignore")), None)
"MainWindow", "Unknown namecoin interface type: {0}"
).format(self.nmctype.decode('utf-8', 'ignore')), None)
except Exception:
logger.exception("Namecoin query exception")
return (_translate(
@ -135,12 +133,12 @@ class namecoinConnection(object):
) if valid else (
_translate(
"MainWindow",
"The name %1 has no associated Bitmessage address."
).arg(identity.decode("utf-8", "ignore")), None)
"The name {0} has no associated Bitmessage address."
).format(identity.decode('utf-8', 'ignore')), None)
def test(self):
"""
Test the connection settings. This routine tries to query a "getinfo"
Test the connection settings. This routine tries to query a "getinfo"
command, and builds either an error message or a success message with
some info from it.
"""
@ -160,44 +158,36 @@ class namecoinConnection(object):
versStr = "0.%d.%d" % (v1, v2)
else:
versStr = "0.%d.%d.%d" % (v1, v2, v3)
message = (
"success",
_translate(
return (
'success', _translate(
"MainWindow",
"Success! Namecoind version %1 running.").arg(
versStr.decode("utf-8", "ignore")))
"Success! Namecoind version {0} running."
).format(versStr.decode('utf-8', 'ignore'))
)
elif self.nmctype == "nmcontrol":
res = self.callRPC("data", ["status"])
prefix = "Plugin data running"
if ("reply" in res) and res["reply"][:len(prefix)] == prefix:
return (
"success",
_translate(
'success', _translate(
"MainWindow",
"Success! NMControll is up and running."
)
"Success! NMControll is up and running.")
)
logger.error("Unexpected nmcontrol reply: %s", res)
message = (
"failed",
_translate(
"MainWindow",
"Couldn\'t understand NMControl."
)
return (
'failed', _translate(
"MainWindow", "Couldn\'t understand NMControl.")
)
else:
sys.exit("Unsupported Namecoin type")
return message
except Exception:
logger.info("Namecoin connection test failure")
return (
"failed",
_translate(
'failed', _translate(
"MainWindow", "The connection to namecoin failed.")
)
@ -245,26 +235,24 @@ class namecoinConnection(object):
"Authorization", "Basic %s" % base64.b64encode(authstr))
self.con.endheaders()
self.con.send(data)
try:
resp = self.con.getresponse()
result = resp.read()
if resp.status != 200:
raise Exception(
"Namecoin returned status %i: %s" %
(resp.status, resp.reason))
except: # noqa:E722
logger.info("HTTP receive error")
except: # noqa:E722
logger.info("HTTP connection error")
return None
try:
resp = self.con.getresponse()
result = resp.read()
if resp.status != 200:
raise Exception(
"Namecoin returned status"
" %i: %s" % (resp.status, resp.reason))
except: # noqa:E722
logger.info("HTTP receive error")
return None
return result
def queryServer(self, data):
"""Helper routine sending data to the RPC "
"server and returning the result."""
"""
Helper routine sending data to the RPC server and returning the result.
"""
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
@ -296,23 +284,24 @@ def lookupNamecoinFolder():
"""
app = "namecoin"
from os import path, environ
if sys.platform == "darwin":
if "HOME" in environ:
dataFolder = path.join(os.environ["HOME"],
"Library/Application Support/", app) + "/"
else:
try:
dataFolder = os.path.join(
os.getenv("HOME"), "Library/Application Support/", app)
except TypeError: # getenv is None
sys.exit(
"Could not find home folder, please report this message"
" and your OS X version to the BitMessage Github."
)
) # TODO: remove exits from utility modules
elif "win32" in sys.platform or "win64" in sys.platform:
dataFolder = path.join(environ["APPDATA"], app) + "\\"
else:
dataFolder = path.join(environ["HOME"], ".%s" % app) + "/"
dataFolder = (
os.path.join(os.getenv("APPDATA"), app)
if sys.platform.startswith('win') else
os.path.join(os.getenv("HOME"), ".%s" % app)
)
return dataFolder
return dataFolder + os.path.sep
def ensureNamecoinOptions():
@ -357,8 +346,8 @@ def ensureNamecoinOptions():
nmc.close()
except IOError:
logger.warning(
"%s unreadable or missing, Namecoin support deactivated",
nmcConfig)
"%s unreadable or missing, Namecoin support deactivated", nmcConfig
)
except Exception:
logger.warning("Error processing namecoin.conf", exc_info=True)
@ -370,5 +359,5 @@ def ensureNamecoinOptions():
# Set default port now, possibly to found value.
if not hasPort:
BMConfigParser().set(configSection, "namecoinrpcport",
defaults.namecoinDefaultRpcPort)
BMConfigParser().set(
configSection, "namecoinrpcport", defaults.namecoinDefaultRpcPort)

View File

@ -138,9 +138,10 @@ class TCPConnection(BMProto, TLSDispatcher):
'updateStatusBar',
_translate(
"MainWindow",
"The time on your computer, %1, may be wrong. "
"The time on your computer, {0}, may be wrong. "
"Please verify your settings."
).arg(l10n.formatTimestamp())))
).format(l10n.formatTimestamp())
))
def state_connection_fully_established(self):
"""

View File

@ -266,9 +266,12 @@ class uPnPThread(StoppableThread):
with knownnodes.knownNodesLock:
knownnodes.addKnownNode(
1, self_peer, is_self=True)
queues.UISignalQueue.put(('updateStatusBar', tr._translate(
"MainWindow", 'UPnP port mapping established on port %1'
).arg(str(self.extPort))))
queues.UISignalQueue.put((
'updateStatusBar', tr._translate(
"MainWindow",
"UPnP port mapping established on port {0}"
).format(self.extPort)
))
break
except socket.timeout:
pass