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)