/ src / helper_startup.py.bak
helper_startup.py.bak
  1  """
  2  Startup operations.
  3  """
  4  # pylint: disable=too-many-branches,too-many-statements
  5  
  6  import ctypes
  7  import logging
  8  import os
  9  import platform
 10  import socket
 11  import sys
 12  import time
 13  from distutils.version import StrictVersion
 14  from struct import pack
 15  from six.moves import configparser
 16  
 17  try:
 18      import defaults
 19      import helper_random
 20      import paths
 21      import state
 22      from bmconfigparser import config, config_ready
 23  except ImportError:
 24      from . import defaults, helper_random, paths, state
 25      from .bmconfigparser import config, config_ready
 26  
 27  try:
 28      from plugins.plugin import get_plugin
 29  except ImportError:
 30      get_plugin = None
 31  
 32  
 33  logger = logging.getLogger('default')
 34  
 35  # The user may de-select Portable Mode in the settings if they want
 36  # the config files to stay in the application data folder.
 37  StoreConfigFilesInSameDirectoryAsProgramByDefault = False
 38  
 39  
 40  def loadConfig():
 41      """Load the config"""
 42      if state.appdata:
 43          config.read(state.appdata + 'keys.dat')
 44          # state.appdata must have been specified as a startup option.
 45          needToCreateKeysFile = config.safeGet(
 46              'bitmessagesettings', 'settingsversion') is None
 47          if not needToCreateKeysFile:
 48              logger.info(
 49                  'Loading config files from directory specified'
 50                  ' on startup: %s', state.appdata)
 51      else:
 52          config.read(paths.lookupExeFolder() + 'keys.dat')
 53  
 54          if config.safeGet('bitmessagesettings', 'settingsversion'):
 55              logger.info('Loading config files from same directory as program.')
 56              needToCreateKeysFile = False
 57              state.appdata = paths.lookupExeFolder()
 58          else:
 59              # Could not load the keys.dat file in the program directory.
 60              # Perhaps it is in the appdata directory.
 61              state.appdata = paths.lookupAppdataFolder()
 62              config.read(state.appdata + 'keys.dat')
 63              needToCreateKeysFile = config.safeGet(
 64                  'bitmessagesettings', 'settingsversion') is None
 65              if not needToCreateKeysFile:
 66                  logger.info(
 67                      'Loading existing config files from %s', state.appdata)
 68  
 69      if needToCreateKeysFile:
 70  
 71          # This appears to be the first time running the program; there is
 72          # no config file (or it cannot be accessed). Create config file.
 73          # config.add_section('bitmessagesettings')
 74          config.read()
 75          config.set('bitmessagesettings', 'settingsversion', '10')
 76          if 'linux' in sys.platform:
 77              config.set('bitmessagesettings', 'minimizetotray', 'false')
 78          # This isn't implimented yet and when True on
 79          # Ubuntu causes Bitmessage to disappear while
 80          # running when minimized.
 81          else:
 82              config.set('bitmessagesettings', 'minimizetotray', 'true')
 83          config.set(
 84              'bitmessagesettings', 'defaultnoncetrialsperbyte',
 85              str(defaults.networkDefaultProofOfWorkNonceTrialsPerByte))
 86          config.set(
 87              'bitmessagesettings', 'defaultpayloadlengthextrabytes',
 88              str(defaults.networkDefaultPayloadLengthExtraBytes))
 89          config.set('bitmessagesettings', 'dontconnect', 'true')
 90          # UI setting to stop trying to send messages after X days/months
 91          # config.set('bitmessagesettings', 'stopresendingafterxdays', '')
 92          # config.set('bitmessagesettings', 'stopresendingafterxmonths', '')
 93  
 94          # Are you hoping to add a new option to the keys.dat file? You're in
 95          # the right place for adding it to users who install the software for
 96          # the first time. But you must also add it to the keys.dat file of
 97          # existing users. To do that, search the class_sqlThread.py file
 98          # for the text: "right above this line!"
 99  
100          if StoreConfigFilesInSameDirectoryAsProgramByDefault:
101              # Just use the same directory as the program and forget about
102              # the appdata folder
103              state.appdata = ''
104              logger.info(
105                  'Creating new config files in same directory as program.')
106          else:
107              logger.info('Creating new config files in %s', state.appdata)
108              if not os.path.exists(state.appdata):
109                  os.makedirs(state.appdata)
110          if not sys.platform.startswith('win'):
111              os.umask(0o077)
112          config.save()
113      else:
114          updateConfig()
115      config_ready.set()
116  
117  
118  def updateConfig():
119      """Save the config"""
120      settingsversion = config.getint('bitmessagesettings', 'settingsversion')
121      if settingsversion == 1:
122          config.set('bitmessagesettings', 'socksproxytype', 'none')
123          config.set('bitmessagesettings', 'sockshostname', 'localhost')
124          config.set('bitmessagesettings', 'socksport', '9050')
125          config.set('bitmessagesettings', 'socksauthentication', 'false')
126          config.set('bitmessagesettings', 'socksusername', '')
127          config.set('bitmessagesettings', 'sockspassword', '')
128          config.set('bitmessagesettings', 'sockslisten', 'false')
129          config.set('bitmessagesettings', 'keysencrypted', 'false')
130          config.set('bitmessagesettings', 'messagesencrypted', 'false')
131          settingsversion = 2
132      # let class_sqlThread update SQL and continue
133      elif settingsversion == 4:
134          config.set(
135              'bitmessagesettings', 'defaultnoncetrialsperbyte',
136              str(defaults.networkDefaultProofOfWorkNonceTrialsPerByte))
137          config.set(
138              'bitmessagesettings', 'defaultpayloadlengthextrabytes',
139              str(defaults.networkDefaultPayloadLengthExtraBytes))
140          settingsversion = 5
141  
142      if settingsversion == 5:
143          config.set(
144              'bitmessagesettings', 'maxacceptablenoncetrialsperbyte', '0')
145          config.set(
146              'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes', '0')
147          settingsversion = 7
148  
149      if not config.has_option('bitmessagesettings', 'sockslisten'):
150          config.set('bitmessagesettings', 'sockslisten', 'false')
151  
152      if not config.has_option('bitmessagesettings', 'userlocale'):
153          config.set('bitmessagesettings', 'userlocale', 'system')
154  
155      if not config.has_option('bitmessagesettings', 'sendoutgoingconnections'):
156          config.set('bitmessagesettings', 'sendoutgoingconnections', 'True')
157  
158      if not config.has_option('bitmessagesettings', 'useidenticons'):
159          config.set('bitmessagesettings', 'useidenticons', 'True')
160      if not config.has_option('bitmessagesettings', 'identiconsuffix'):
161          # acts as a salt
162          config.set(
163              'bitmessagesettings', 'identiconsuffix', ''.join(
164                  helper_random.randomchoice(
165                      "123456789ABCDEFGHJKLMNPQRSTUVWXYZ"
166                      "abcdefghijkmnopqrstuvwxyz") for x in range(12))
167          )  # a twelve character pseudo-password to salt the identicons
168  
169      # Add settings to support no longer resending messages after
170      # a certain period of time even if we never get an ack
171      if settingsversion == 7:
172          config.set('bitmessagesettings', 'stopresendingafterxdays', '')
173          config.set('bitmessagesettings', 'stopresendingafterxmonths', '')
174          settingsversion = 8
175  
176      # With the change to protocol version 3, reset the user-settable
177      # difficulties to 1
178      if settingsversion == 8:
179          config.set(
180              'bitmessagesettings', 'defaultnoncetrialsperbyte',
181              str(defaults.networkDefaultProofOfWorkNonceTrialsPerByte))
182          config.set(
183              'bitmessagesettings', 'defaultpayloadlengthextrabytes',
184              str(defaults.networkDefaultPayloadLengthExtraBytes))
185          previousTotalDifficulty = int(
186              config.getint(
187                  'bitmessagesettings', 'maxacceptablenoncetrialsperbyte')
188          ) / 320
189          previousSmallMessageDifficulty = int(
190              config.getint(
191                  'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes')
192          ) / 14000
193          config.set(
194              'bitmessagesettings', 'maxacceptablenoncetrialsperbyte',
195              str(previousTotalDifficulty * 1000))
196          config.set(
197              'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes',
198              str(previousSmallMessageDifficulty * 1000))
199          settingsversion = 9
200  
201      # Adjust the required POW values for each of this user's addresses
202      # to conform to protocol v3 norms.
203      if settingsversion == 9:
204          for addressInKeysFile in config.addresses():
205              try:
206                  previousTotalDifficulty = float(
207                      config.getint(
208                          addressInKeysFile, 'noncetrialsperbyte')) / 320
209                  previousSmallMessageDifficulty = float(
210                      config.getint(
211                          addressInKeysFile, 'payloadlengthextrabytes')) / 14000
212                  if previousTotalDifficulty <= 2:
213                      previousTotalDifficulty = 1
214                  if previousSmallMessageDifficulty < 1:
215                      previousSmallMessageDifficulty = 1
216                  config.set(
217                      addressInKeysFile, 'noncetrialsperbyte',
218                      str(int(previousTotalDifficulty * 1000)))
219                  config.set(
220                      addressInKeysFile, 'payloadlengthextrabytes',
221                      str(int(previousSmallMessageDifficulty * 1000)))
222              except (ValueError, TypeError, configparser.NoSectionError,
223                      configparser.NoOptionError):
224                  continue
225          config.set('bitmessagesettings', 'maxdownloadrate', '0')
226          config.set('bitmessagesettings', 'maxuploadrate', '0')
227          settingsversion = 10
228  
229      # sanity check
230      if config.safeGetInt(
231              'bitmessagesettings', 'maxacceptablenoncetrialsperbyte') == 0:
232          config.set(
233              'bitmessagesettings', 'maxacceptablenoncetrialsperbyte',
234              str(defaults.ridiculousDifficulty
235                  * defaults.networkDefaultProofOfWorkNonceTrialsPerByte)
236          )
237      if config.safeGetInt(
238              'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes') == 0:
239          config.set(
240              'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes',
241              str(defaults.ridiculousDifficulty
242                  * defaults.networkDefaultPayloadLengthExtraBytes)
243          )
244  
245      if not config.has_option('bitmessagesettings', 'onionport'):
246          config.set('bitmessagesettings', 'onionport', '8444')
247      if not config.has_option('bitmessagesettings', 'onionbindip'):
248          config.set('bitmessagesettings', 'onionbindip', '127.0.0.1')
249      if not config.has_option('bitmessagesettings', 'smtpdeliver'):
250          config.set('bitmessagesettings', 'smtpdeliver', '')
251      if not config.has_option(
252              'bitmessagesettings', 'hidetrayconnectionnotifications'):
253          config.set(
254              'bitmessagesettings', 'hidetrayconnectionnotifications', 'false')
255      if config.safeGetInt('bitmessagesettings', 'maxoutboundconnections') < 1:
256          config.set('bitmessagesettings', 'maxoutboundconnections', '8')
257          logger.warning('Your maximum outbound connections must be a number.')
258  
259      # TTL is now user-specifiable. Let's add an option to save
260      # whatever the user selects.
261      if not config.has_option('bitmessagesettings', 'ttl'):
262          config.set('bitmessagesettings', 'ttl', '367200')
263  
264      config.set('bitmessagesettings', 'settingsversion', str(settingsversion))
265      config.save()
266  
267  
268  def adjustHalfOpenConnectionsLimit():
269      """Check and satisfy half-open connections limit (mainly XP and Vista)"""
270      if config.safeGet(
271              'bitmessagesettings', 'socksproxytype', 'none') != 'none':
272          state.maximumNumberOfHalfOpenConnections = 4
273          return
274  
275      is_limited = False
276      try:
277          if sys.platform[0:3] == "win":
278              # Some XP and Vista systems can only have 10 outgoing
279              # connections at a time.
280              VER_THIS = StrictVersion(platform.version())
281              is_limited = (
282                  StrictVersion("5.1.2600") <= VER_THIS
283                  and StrictVersion("6.0.6000") >= VER_THIS
284              )
285      except ValueError:
286          pass
287  
288      state.maximumNumberOfHalfOpenConnections = 9 if is_limited else 64
289  
290  
291  def fixSocket():
292      """Add missing socket options and methods mainly on Windows"""
293      if sys.platform.startswith('linux'):
294          socket.SO_BINDTODEVICE = 25
295  
296      if not sys.platform.startswith('win'):
297          return
298  
299      # Python 2 on Windows doesn't define a wrapper for
300      # socket.inet_ntop but we can make one ourselves using ctypes
301      if not hasattr(socket, 'inet_ntop'):
302          addressToString = ctypes.windll.ws2_32.WSAAddressToStringA
303  
304          def inet_ntop(family, host):
305              """Converting an IP address in packed
306              binary format to string format"""
307              if family == socket.AF_INET:
308                  if len(host) != 4:
309                      raise ValueError("invalid IPv4 host")
310                  host = pack("hH4s8s", socket.AF_INET, 0, host, "\0" * 8)
311              elif family == socket.AF_INET6:
312                  if len(host) != 16:
313                      raise ValueError("invalid IPv6 host")
314                  host = pack("hHL16sL", socket.AF_INET6, 0, 0, host, 0)
315              else:
316                  raise ValueError("invalid address family")
317              buf = "\0" * 64
318              lengthBuf = pack("I", len(buf))
319              addressToString(host, len(host), None, buf, lengthBuf)
320              return buf[0:buf.index("\0")]
321          socket.inet_ntop = inet_ntop
322  
323      # Same for inet_pton
324      if not hasattr(socket, 'inet_pton'):
325          stringToAddress = ctypes.windll.ws2_32.WSAStringToAddressA
326  
327          def inet_pton(family, host):
328              """Converting an IP address in string format
329              to a packed binary format"""
330              buf = "\0" * 28
331              lengthBuf = pack("I", len(buf))
332              if stringToAddress(str(host),
333                                 int(family),
334                                 None,
335                                 buf,
336                                 lengthBuf) != 0:
337                  raise socket.error("illegal IP address passed to inet_pton")
338              if family == socket.AF_INET:
339                  return buf[4:8]
340              elif family == socket.AF_INET6:
341                  return buf[8:24]
342              else:
343                  raise ValueError("invalid address family")
344          socket.inet_pton = inet_pton
345  
346      # These sockopts are needed on for IPv6 support
347      if not hasattr(socket, 'IPPROTO_IPV6'):
348          socket.IPPROTO_IPV6 = 41
349      if not hasattr(socket, 'IPV6_V6ONLY'):
350          socket.IPV6_V6ONLY = 27
351  
352  
353  def start_proxyconfig():
354      """Check socksproxytype and start any proxy configuration plugin"""
355      if not get_plugin:
356          return
357      config_ready.wait()
358      proxy_type = config.safeGet('bitmessagesettings', 'socksproxytype')
359      if proxy_type and proxy_type not in ('none', 'SOCKS4a', 'SOCKS5'):
360          try:
361              proxyconfig_start = time.time()
362              if not get_plugin('proxyconfig', name=proxy_type)(config):
363                  raise TypeError()
364          except TypeError:
365              # cannot import shutdown here ):
366              logger.error(
367                  'Failed to run proxy config plugin %s',
368                  proxy_type, exc_info=True)
369              config.setTemp('bitmessagesettings', 'dontconnect', 'true')
370          else:
371              logger.info(
372                  'Started proxy config plugin %s in %s sec',
373                  proxy_type, time.time() - proxyconfig_start)